/********************************************************************** | |
* 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.List; | |
import org.eclipse.objectteams.otredyn.bytecode.AbstractBoundClass; | |
import org.eclipse.objectteams.otredyn.bytecode.Binding; | |
import org.eclipse.objectteams.otredyn.bytecode.ClassRepository; | |
import org.eclipse.objectteams.otredyn.runtime.ClassIdentifierProviderFactory; | |
import org.eclipse.objectteams.otredyn.runtime.IBinding; | |
import org.eclipse.objectteams.otredyn.runtime.IClassIdentifierProvider; | |
import org.objectweb.asm.Attribute; | |
import org.objectweb.asm.ClassReader; | |
import org.objectweb.asm.Label; | |
/** | |
* This class contains all classes representing OT/J class file attributes | |
* @author Oliver Frank | |
*/ | |
abstract class Attributes { | |
protected final static String ATTRIBUTE_OT_DYN_CALLIN_BINDINGS="OTDynCallinBindings"; | |
protected final static String ATTRIBUTE_ROLE_BASE_BINDINGS = "CallinRoleBaseBindings"; | |
protected final static String ATTRIBUTE_CALLIN_PRECEDENCE = "CallinPrecedence"; | |
protected final static String ATTRIBUTE_OT_CLASS_FLAGS = "OTClassFlags"; | |
protected final static String ATTRIBUTE_OT_SPECIAL_ACCESS = "OTSpecialAccess"; | |
protected final static String ATTRIBUTE_OT_COMPILER_VERSION = "OTCompilerVersion"; | |
private static final int OTDRE_FLAG = 0x8000; // high bit in OTCompilerVersion | |
protected final static Attribute[] attributes = { | |
new CallinBindingsAttribute(0), | |
new RoleBaseBindingsAttribute(0), | |
new CallinPrecedenceAttribute(0), | |
new OTClassFlagsAttribute(0), | |
new OTSpecialAccessAttribute(), | |
new OTCompilerVersion(0) | |
}; | |
protected static class OTCompilerVersion extends Attribute { | |
protected OTCompilerVersion(int version) { | |
super(ATTRIBUTE_OT_COMPILER_VERSION); | |
} | |
@Override | |
protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) { | |
int encodedVersion = cr.readUnsignedShort(off); | |
if ((encodedVersion & OTDRE_FLAG) == 0) | |
throw new UnsupportedClassVersionError("OTDRE: Class "+cr.getClassName()+" was compiled for incompatible weaving target OTRE"); | |
return new OTCompilerVersion(encodedVersion); | |
} | |
} | |
protected static class CallinBindingsAttribute extends Attribute { | |
static final short COVARIANT_BASE_RETURN = 8; | |
/** Represents all base method bindings of one callin binding. */ | |
protected static class MultiBinding { | |
private String roleClassName; | |
private String callinLabel; | |
private String baseClassName; | |
private String[] baseMethodNames; | |
private String[] baseMethodSignatures; | |
private String[] declaringBaseClassNames; | |
private int callinModifier; | |
private int[] callinIds; | |
private int[] baseFlags; | |
private boolean isHandleCovariantReturn; | |
MultiBinding(String roleName, String callinLabel, | |
String baseClassName, | |
String[] baseMethodNames, String[] baseMethodSignatures, String[] declaringBaseClassNames, | |
int callinModifier, int[] callinIds, int[] baseFlags, boolean handleCovariantReturn) | |
{ | |
this.roleClassName = roleName; | |
this.callinLabel = callinLabel; | |
this.baseClassName = baseClassName; | |
this.baseMethodNames = baseMethodNames; | |
this.baseMethodSignatures = baseMethodSignatures; | |
this.declaringBaseClassNames = declaringBaseClassNames; | |
this.callinModifier = callinModifier; | |
this.callinIds = callinIds; | |
this.baseFlags = baseFlags; | |
this.isHandleCovariantReturn = handleCovariantReturn; | |
} | |
protected String getRoleClassName() { | |
return roleClassName; | |
} | |
protected String getBaseClassName() { | |
return baseClassName; | |
} | |
protected String[] getBaseMethodNames() { | |
return baseMethodNames; | |
} | |
protected String[] getBaseMethodSignatures() { | |
return baseMethodSignatures; | |
} | |
protected int getCallinModifier() { | |
return this.callinModifier; | |
} | |
protected int[] getCallinIds() { | |
return callinIds; | |
} | |
public int[] getBaseFlags() { | |
return baseFlags; | |
} | |
protected String getCallinLabel() { | |
return callinLabel; | |
} | |
public boolean isHandleCovariantReturn() { | |
return this. isHandleCovariantReturn; | |
} | |
public String[] getDeclaringBaseClassName() { | |
return this.declaringBaseClassNames; | |
} | |
} | |
private MultiBinding[] bindings; | |
public CallinBindingsAttribute(int bindingsCount) { | |
super(ATTRIBUTE_OT_DYN_CALLIN_BINDINGS); | |
this.bindings = new MultiBinding[bindingsCount]; | |
} | |
private void addBinding(int i, String roleName, String callinLabel, | |
String baseClassName, | |
String[] baseMethodNames, String[] baseMethodSignatures, String[] declaringBaseClassNames, | |
String callinModifierName, int[] callinIds, int[] baseFlags, boolean handleCovariantReturn) { | |
int callinModifier = 0; | |
if ("before".equals(callinModifierName)) | |
callinModifier = Binding.BEFORE; | |
else if ("after".equals(callinModifierName)) | |
callinModifier = Binding.AFTER; | |
else | |
callinModifier = Binding.REPLACE; | |
this.bindings[i] = new MultiBinding(roleName, callinLabel, | |
baseClassName, | |
baseMethodNames, baseMethodSignatures, declaringBaseClassNames, | |
callinModifier, callinIds, baseFlags, handleCovariantReturn); | |
} | |
@Override | |
protected Attribute read(ClassReader cr, int off, int len, | |
char[] buf, int codeOff, Label[] labels) | |
{ | |
int bindingsCount = cr.readShort(off); off += 2; | |
CallinBindingsAttribute attr = new CallinBindingsAttribute(bindingsCount); | |
for (int i = 0; i < bindingsCount; i++) { | |
String roleName = cr.readUTF8(off, buf); off += 2; | |
String callinLabel = cr.readUTF8(off, buf); off += 2; | |
/* skip roleSelector, roleSignature */ off += 4; | |
String callinModifier = cr.readUTF8(off, buf); off += 2; | |
int flags = cr.readByte(off); off += 1; | |
String baseClassName = cr.readUTF8(off, buf); off += 2; | |
/* skip filename & lineNumber & lineOffset */ off += 6; | |
int baseMethodsCount = cr.readShort(off); off += 2; | |
String[] baseMethodNames = new String[baseMethodsCount]; | |
String[] baseMethodSignatures = new String[baseMethodsCount]; | |
String[] declaringBaseClassNames = new String[baseMethodsCount]; | |
int[] callinIds = new int[baseMethodsCount]; | |
int[] baseFlags = new int[baseMethodsCount]; | |
for (int m = 0; m < baseMethodsCount; m++) { | |
baseMethodNames[m] = cr.readUTF8(off, buf); off += 2; | |
baseMethodSignatures[m] = cr.readUTF8(off, buf); off += 2; | |
declaringBaseClassNames[m] = cr.readUTF8(off, buf); off += 2; | |
callinIds[m] = cr.readInt(off); off += 4; | |
baseFlags[m] = cr.readByte(off); off++; | |
/* skip translationFlags */ off += 2; | |
} | |
attr.addBinding(i, roleName, callinLabel, | |
baseClassName, | |
baseMethodNames, baseMethodSignatures, declaringBaseClassNames, | |
callinModifier, callinIds, baseFlags, ((flags & COVARIANT_BASE_RETURN) != 0)); | |
} | |
return attr; | |
} | |
public MultiBinding[] getBindings() { | |
return this.bindings; | |
} | |
@Override | |
public String toString() { | |
StringBuffer buf = new StringBuffer(); | |
for (MultiBinding binding : this.bindings) { | |
buf.append(binding.getBaseClassName()); | |
int[] callinIds = binding.getCallinIds(); | |
String[] baseMethodNames = binding.getBaseMethodNames(); | |
String[] baseMethodSignatures = binding.getBaseMethodSignatures(); | |
for (int i=0; i<callinIds.length; i++) { | |
buf.append("\n\t{"); | |
buf.append(callinIds[i]); | |
buf.append("} "); | |
buf.append(baseMethodNames[i]); | |
buf.append(baseMethodSignatures[i]); | |
} | |
} | |
return buf.toString(); | |
} | |
} | |
protected static class RoleBaseBindingsAttribute extends Attribute { | |
String[] roles; | |
String[] bases; | |
protected RoleBaseBindingsAttribute(int elementCount) { | |
super(ATTRIBUTE_ROLE_BASE_BINDINGS); | |
roles = new String[elementCount]; | |
bases = new String[elementCount]; | |
} | |
@Override | |
protected Attribute read(ClassReader cr, int off, int len, | |
char[] buf, int codeOff, Label[] labels) | |
{ | |
int elementCount = cr.readShort(off); off += 2; | |
RoleBaseBindingsAttribute attr = new RoleBaseBindingsAttribute(elementCount); | |
for (int i = 0; i < elementCount; i++) { | |
attr.roles[i] = cr.readUTF8(off, buf); off += 2; | |
attr.bases[i] = cr.readUTF8(off, buf); off += 2; | |
} | |
return attr; | |
} | |
} | |
protected static class CallinPrecedenceAttribute extends Attribute { | |
String[] labels; | |
public CallinPrecedenceAttribute(int elementCount) { | |
super(ATTRIBUTE_CALLIN_PRECEDENCE); | |
this.labels = new String[elementCount]; | |
} | |
@Override | |
protected Attribute read(ClassReader cr, int off, int len, | |
char[] buf, int codeOff, Label[] labels) | |
{ | |
int elementCount = cr.readShort(off); off += 2; | |
CallinPrecedenceAttribute attr = new CallinPrecedenceAttribute(elementCount); | |
for (int i = 0; i < elementCount; i++) { | |
attr.labels[i] = cr.readUTF8(off, buf); off += 2; | |
} | |
return attr; | |
} | |
} | |
protected static class OTClassFlagsAttribute extends Attribute { | |
int flags; | |
protected OTClassFlagsAttribute(int flags) { | |
super(ATTRIBUTE_OT_CLASS_FLAGS); | |
this.flags = flags; | |
} | |
@Override | |
protected Attribute read(ClassReader cr, int off, int len, char[] buf, | |
int codeOff, Label[] labels) | |
{ | |
return new OTClassFlagsAttribute(cr.readUnsignedShort(off)); | |
} | |
} | |
protected static class OTSpecialAccessAttribute extends Attribute { | |
class DecapsField { | |
String accessMode; | |
boolean isStatic; | |
String baseclass, name, desc; | |
public int perTeamAccessId; | |
public DecapsField(String baseclass, String name, String desc, int accessId, String accessMode, boolean isStatic) { | |
this.baseclass = baseclass; | |
this.name = name; | |
this.desc = desc; | |
this.perTeamAccessId = accessId; | |
this.accessMode = accessMode; | |
this.isStatic = isStatic; | |
} | |
} | |
class DecapsMethod { | |
String[] weaveIntoClasses; | |
String declaringClass, name, desc; | |
int perTeamAccessId; | |
boolean isStatic; | |
DecapsMethod(String weaveIntoClasses, String declaringClass, String name, String desc, int id, boolean isStatic) { | |
this.weaveIntoClasses = weaveIntoClasses.split(":"); | |
this.declaringClass = declaringClass; | |
this.name = name; | |
this.desc = desc; | |
this.perTeamAccessId = id; | |
this.isStatic = isStatic; | |
} | |
} | |
private static final int DECAPSULATION_METHOD_ACCESS= 4; // kinds are disjoint from those used by the old OTRE | |
private static final int CALLOUT_FIELD_ACCESS = 5; | |
private static final int SUPER_METHOD_ACCESS = 6; | |
List<DecapsMethod> methods = new ArrayList<DecapsMethod>(); | |
List<DecapsField> fields = new ArrayList<DecapsField>(); | |
List<String> decapsulatedBaseClasses = new ArrayList<String>(); // not currently used, see AddInterfaceAdapter for brute force solution | |
protected OTSpecialAccessAttribute() { | |
super(ATTRIBUTE_OT_SPECIAL_ACCESS); | |
} | |
@Override | |
protected Attribute read(ClassReader cr, int off, int len, char[] buf, | |
int codeOff, Label[] labels) | |
{ | |
OTSpecialAccessAttribute attr = new OTSpecialAccessAttribute(); | |
int size = cr.readUnsignedShort(off); off+=2; | |
for (int i=0; i<size; i++) { | |
int kind = cr.readByte(off++); | |
switch (kind) { | |
case DECAPSULATION_METHOD_ACCESS: | |
attr.readMethodAccess(cr, off, buf); off+=8; | |
break; | |
case CALLOUT_FIELD_ACCESS: | |
attr.readFieldAccess(cr, off, buf); off+=9; | |
break; | |
case SUPER_METHOD_ACCESS: | |
throw new IllegalStateException("Not yet handled: OTSpecialAccess for SUPER_METHOD_ACCESS"); | |
// off+=8; | |
// break; | |
default: | |
throw new IllegalStateException("Unexpected kind in OTSpecialAccess attribute: "+kind); | |
} | |
} | |
size = cr.readUnsignedShort(off); off+=2; | |
for (int i = 0; i < size; i++) { | |
String baseClass = cr.readUTF8(off, buf); off+=2; | |
int flag = cr.readByte(off++); | |
if (flag == 1) | |
decapsulatedBaseClasses.add(baseClass); | |
} | |
return attr; | |
} | |
private void readMethodAccess(ClassReader cr, int off, char[] buf) { | |
String className = cr.readUTF8(off, buf); | |
String encodedName = cr.readUTF8(off+2, buf); | |
String methodDesc = cr.readUTF8(off+4, buf); | |
int accessId = cr.readUnsignedShort(off+6); | |
boolean isStatic = false; | |
String declaringClass; | |
String methodName; | |
if (encodedName.charAt(0) == '<') { | |
// constructor | |
declaringClass = className; | |
methodName = encodedName; | |
isStatic = true; // use static accessor | |
} else { | |
int pos = encodedName.indexOf('?'); | |
if (pos == -1) { | |
pos = encodedName.indexOf('!'); | |
isStatic = true; | |
} | |
declaringClass = encodedName.substring(0, pos); | |
methodName = encodedName.substring(pos+1); | |
} | |
this.methods.add(new DecapsMethod(className, declaringClass, methodName, methodDesc, accessId, isStatic)); | |
} | |
private void readFieldAccess(ClassReader cr, int off, char[] buf) { | |
int accessId = cr.readUnsignedShort(off); | |
int flags = cr.readByte(off+2); | |
String className = cr.readUTF8(off+3, buf); | |
String fieldName = cr.readUTF8(off+5, buf); | |
String fieldDesc = cr.readUTF8(off+7, buf); | |
boolean isStatic = (flags & 2) != 0; | |
String accessMode = (flags & 1) == 1 ? "set" : "get"; | |
this.fields.add(new DecapsField(className, fieldName, fieldDesc, accessId, accessMode, isStatic)); | |
} | |
public void registerAt(AsmBoundClass clazz) { | |
ClassRepository repo = ClassRepository.getInstance(); | |
IClassIdentifierProvider provider = ClassIdentifierProviderFactory.getClassIdentifierProvider(); | |
for (DecapsMethod dMethod : this.methods) { | |
// FIXME(SH): the following may need adaptation for OT/Equinox or other multi-classloader settings: | |
// bypassing the identifier provider (we don't have a Class<?> yet): | |
// String boundClassIdentifier = provider.getBoundClassIdentifier(clazz, dMethod.baseclass); | |
AbstractBoundClass baseclass = repo.getBoundClass(dMethod.declaringClass, dMethod.declaringClass.replace('.', '/'), clazz.getClassLoader()); | |
// register the target method: | |
baseclass.getMethod(dMethod.name, dMethod.desc, false/*covariantReturn*/, dMethod.isStatic); | |
clazz.recordAccessId(dMethod.perTeamAccessId); | |
clazz.addBinding(new Binding(clazz, dMethod.declaringClass, dMethod.name, dMethod.desc, dMethod.perTeamAccessId, IBinding.BindingType.METHOD_ACCESS)); | |
} | |
for (DecapsField dField: this.fields) { | |
// FIXME(SH): the following may need adaptation for OT/Equinox or other multi-classloader settings: | |
// bypassing the identifier provider (we don't have a Class<?> yet): | |
// String boundClassIdentifier = provider.getBoundClassIdentifier(clazz, dMethod.baseclass); | |
AbstractBoundClass baseclass = repo.getBoundClass(dField.baseclass, dField.baseclass.replace('.', '/'), clazz.getClassLoader()); | |
// register the target field: | |
baseclass.getField(dField.name, dField.desc); | |
clazz.recordAccessId(dField.perTeamAccessId); | |
clazz.addBinding(new Binding(clazz, dField.baseclass, dField.name, dField.desc, dField.perTeamAccessId, IBinding.BindingType.FIELD_ACCESS)); | |
} | |
} | |
} | |
} |