blob: 3998941fe0ffa5ef975d07aaad1bb29c0f7be370 [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Dynamic Runtime Environment"
*
* Copyright 2009, 2014 Oliver Frank and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Oliver Frank - Initial API and implementation
* Stephan Herrmann - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otredyn.bytecode.asm;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.eclipse.objectteams.otredyn.bytecode.AbstractBoundClass;
import org.eclipse.objectteams.otredyn.bytecode.AbstractTeam;
import org.eclipse.objectteams.otredyn.bytecode.IBytecodeProvider;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
/**
* This class implements the bytecode parsing for {@link AbstractBoundClass}.
* It parses the bytecode with ASM.
* @author Oliver Frank
*/
public abstract class AsmBoundClass extends AbstractTeam {
public static final int ASM_API = Opcodes.ASM6;
private IBytecodeProvider bytecodeProvider;
/**
* just a temporary cache for the bytecode
*/
private byte[] bytecode;
/**
* ordered lists of qualified callin labels
*/
public List<String[]> precedenceses = new ArrayList<String[]>();
/**
* Set of base classes to which the current class or one of its roles as playedBy bindings.
* Qualified class names are '.' separated.
*/
public Set<String> boundBaseClasses;
protected AsmBoundClass(String name, String id, IBytecodeProvider bytecodeProvider, ClassLoader loader) {
super(name, id, loader);
this.bytecodeProvider = bytecodeProvider;
}
/**
* Parses the bytecode of a class and uses the set/add... Methods (e.g. addMethod)
* of {@link AbstractBoundClass} to set the information
*/
@Override
public synchronized void parseBytecode() {
if (parsed) {
// Already parsed, nothing to do
return;
}
bytecode = bytecodeProvider.getBytecode(getId());
if (bytecode == null) {
//Class is not loaded yet.
return;
}
// Don't parse another time
parsed = true;
AsmClassVisitor cv = new AsmClassVisitor(this);
ClassReader cr = null;
cr = new ClassReader(bytecode);
cr.accept(cv, Attributes.attributes, 0);
//release the bytecode
bytecode = null;
}
@Override
public Collection<String> getBoundBaseClasses() {
return this.boundBaseClasses;
}
/**
* Returns the bytecode of this class and cache it temporary.
* This method is only needed, if getBytecode of the {@link IBytecodeProvider}
* is an expensive operation.
* @return
*/
protected byte[] allocateAndGetBytecode() {
if (bytecode == null) {
bytecode = getBytecode();
}
return bytecode;
}
/**
* Get the bytecode directly from the {@link IBytecodeProvider}.
* This method can be used, if getBytecode of the {@link IBytecodeProvider}
* is not an expensive operation.
* @return
*/
@Override
public byte[] getBytecode() {
return bytecodeProvider.getBytecode(getId());
}
/**
* Releases the bytecode, if it's cached, an set it in the {@link IBytecodeProvider}
*/
protected void releaseBytecode() {
bytecodeProvider.setBytecode(getId(), bytecode);
bytecode = null;
}
/**
* Returns the {@link IBytecodeProvider} used for this class
* @return
*/
protected IBytecodeProvider getBytecodeProvider() {
return bytecodeProvider;
}
/**
* Sets the bytecode.
* If the bytecode is temporary cached, the cache is used.
* Otherwise this method give the bytecode directly to the {@link IBytecodeProvider}
* @param bytecode
*/
protected void setBytecode(byte[] bytecode) {
//Is the bytecode temporary cached?
if (this.bytecode == null) {
// no, then save the bytecode directly in the bytecode provider
bytecodeProvider.setBytecode(getId(), bytecode);
} else {
// yes, then use the cache
this.bytecode = bytecode;
}
}
public int compare(String callinLabel1, String callinLabel2) {
for (String[] precedences : this.precedenceses) {
boolean label1Seen = false, label2Seen = false;
for (String label : precedences) {
if (label.equals(callinLabel1)) {
if (label2Seen)
return -1; // saw two then one: one has lower priority than two
label1Seen = true;
} else if (label.equals(callinLabel2)) {
if (label1Seen)
return 1; // saw one then two: one has higher priority than two
label2Seen = true;
}
}
}
AbstractBoundClass enclosingClass = getEnclosingClass();
if (enclosingClass != null) {
String singleName = getInternalName();
int pos = singleName.lastIndexOf('$');
singleName = singleName.substring(pos+1);
if (singleName.startsWith("__OT__"))
singleName = singleName.substring("__OT__".length());
// check for precedence at outer level:
return enclosingClass.compare(singleName+'.'+callinLabel1, singleName+'.'+callinLabel2);
}
return callinLabel1.compareTo(callinLabel2);
}
}