* The target types expression
*/
String value();
-
+
/**
* Optional class defining default implementation
* of interface members (equivalent to defining
* public methods of the interface).
*/
Class defaultImpl() default DeclareParents.class;
-
+
// note - a default of "null" is not allowed,
// hence the strange default given above.
}
@Aspect
static class TestAspect {
- @DeclareParents("ataspectj.DeclareParentsImplementsTest.Target")
- public static Introduced i = new Implementation();//see here control of instantiation
+ @DeclareParents(value="ataspectj.DeclareParentsImplementsTest.Target",
+ defaultImpl=Implementation.class)
+ public static Introduced i;
// will lead to: class Target implements Introduced {
- // void intro(args) { TestAspect.i.intro(args); }
+ // void intro(args) { delegate to some hidden field, lazy initialized here for now }
// }
@Before("execution(* ataspectj.DeclareParentsImplementsTest.Introduced.intro())")
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.DeclareImplements;
+import org.aspectj.lang.annotation.DeclareParents;
import java.util.Arrays;
@Aspect
static class TestAspect {
- @DeclareImplements("ataspectj.DeclareParentsInterfaceTest.Target")
+ @DeclareParents("ataspectj.DeclareParentsInterfaceTest.Target")
Marker introduce;
@Before("execution(* ataspectj.DeclareParentsInterfaceTest.Marker+.target())")
<weaver options="-showWeaveInfo"/>
</aspectj>
-
+
NameMangler.postIntroducedConstructor(aspectType, targetType),
UnresolvedType.insert(targetType, paramTypes));
}
-
- public static ResolvedMember interConstructor(ResolvedType targetType, ResolvedMember constructor, UnresolvedType aspectType) {
+
+ public static ResolvedMember itdAtDeclareParentsField(ResolvedType targetType, UnresolvedType itdType, UnresolvedType aspectType) {
+ return new ResolvedMemberImpl(
+ Member.FIELD,
+ targetType,
+ Modifier.PRIVATE,
+ itdType,
+ NameMangler.itdAtDeclareParentsField(aspectType, itdType),
+ null);
+ }
+
+ public static ResolvedMember interConstructor(ResolvedType targetType, ResolvedMember constructor, UnresolvedType aspectType) {
//
// ResolvedType targetType,
// UnresolvedType[] argTypes,
*/
public class MethodDelegateTypeMunger extends ResolvedTypeMunger {
+ private final UnresolvedType aspect;
+
/**
- * The field in the aspect that hosts the mixin instance
+ * The mixin impl (no arg ctor)
*/
- private final ResolvedMember aspectFieldDelegate;
+ private final String implClassName;
/**
* Type pattern this munger applies to
*
* @param signature
* @param aspect
- * @param fieldName
+ * @param implClassName
* @param typePattern
*/
- public MethodDelegateTypeMunger(ResolvedMember signature, ResolvedType aspect, String fieldName, TypePattern typePattern) {
+ public MethodDelegateTypeMunger(ResolvedMember signature, UnresolvedType aspect, String implClassName, TypePattern typePattern) {
super(MethodDelegate, signature);
+ this.aspect = aspect;
this.typePattern = typePattern;
-
- ResolvedMember[] fields = aspect.getDeclaredFields();//note: will unpack attributes
- ResolvedMember field = null;
- for (int i = 0; i < fields.length; i++) {
- if (fieldName.equals(fields[i].getName())) {
- field = fields[i];
- break;
- }
- }
- if (field == null) {
- throw new RuntimeException("Should not happen: aspect field not found for @DeclareParents delegate");
- } else {
- aspectFieldDelegate = field;
- }
+ this.implClassName = implClassName;
}
- private MethodDelegateTypeMunger(ResolvedMember signature, ResolvedMember fieldDelegate, TypePattern typePattern) {
- super(MethodDelegate, signature);
- this.aspectFieldDelegate = fieldDelegate;
- this.typePattern = typePattern;
+ public ResolvedMember getDelegate(ResolvedType targetType) {
+ return AjcMemberMaker.itdAtDeclareParentsField(
+ targetType,
+ signature.getDeclaringType(),
+ aspect
+ );
}
- public ResolvedMember getDelegate() {
- return aspectFieldDelegate;
+ public String getImplClassName() {
+ return implClassName;
}
public void write(DataOutputStream s) throws IOException {
kind.write(s);
signature.write(s);
- aspectFieldDelegate.write(s);
+ aspect.write(s);
+ s.writeUTF(implClassName);
typePattern.write(s);
}
-
-
public static ResolvedTypeMunger readMethod(VersionedDataInputStream s, ISourceContext context) throws IOException {
ResolvedMemberImpl signature = ResolvedMemberImpl.readResolvedMember(s, context);
- ResolvedMemberImpl field = ResolvedMemberImpl.readResolvedMember(s, context);
+ UnresolvedType aspect = UnresolvedType.read(s);
+ String implClassName = s.readUTF();
TypePattern tp = TypePattern.read(s, context);
- return new MethodDelegateTypeMunger(signature, field, tp);
+ return new MethodDelegateTypeMunger(signature, aspect, implClassName, tp);
}
/**
public boolean changesPublicSignature() {
return true;
}
+
+ public static class FieldHostTypeMunger extends ResolvedTypeMunger {
+
+ private UnresolvedType aspect;
+
+ /**
+ * Type pattern this munger applies to
+ */
+ private final TypePattern typePattern;
+
+ /**
+ * Construct a new type munger for @AspectJ ITD
+ *
+ * @param field
+ * @param aspect
+ * @param typePattern
+ */
+ public FieldHostTypeMunger(ResolvedMember field, UnresolvedType aspect, TypePattern typePattern) {
+ super(FieldHost, field);
+ this.aspect = aspect;
+ this.typePattern = typePattern;
+ }
+
+ public void write(DataOutputStream s) throws IOException {
+ kind.write(s);
+ signature.write(s);
+ aspect.write(s);
+ typePattern.write(s);
+ }
+
+ public static ResolvedTypeMunger readFieldHost(VersionedDataInputStream s, ISourceContext context) throws IOException {
+ ResolvedMemberImpl signature = ResolvedMemberImpl.readResolvedMember(s, context);
+ UnresolvedType aspect = UnresolvedType.read(s);
+ TypePattern tp = TypePattern.read(s, context);
+ return new FieldHostTypeMunger(signature, aspect, tp);
+ }
+
+ /**
+ * Match based on given type pattern, only classes can be matched
+ *
+ * @param matchType
+ * @param aspectType
+ * @return true if match
+ */
+ public boolean matches(ResolvedType matchType, ResolvedType aspectType) {
+ // match only on class
+ if (matchType.isEnum() || matchType.isInterface() || matchType.isAnnotation()) {
+ return false;
+ }
+
+ return typePattern.matchesStatically(matchType);
+ }
+
+ public boolean changesPublicSignature() {
+ return false;
+ }
+
+ }
}
// PTWIMPL method names that must include aspect type
public static String perTypeWithinFieldForTarget(UnresolvedType aspectType) {
- String s = makeName(aspectType.getNameAsIdentifier(), "ptwAspectInstance");
- return s;
+ return makeName(aspectType.getNameAsIdentifier(), "ptwAspectInstance");
}
-
- public static String perTypeWithinLocalAspectOf(UnresolvedType aspectType) {
+
+ public static String perTypeWithinLocalAspectOf(UnresolvedType aspectType) {
return makeName(aspectType.getNameAsIdentifier(), "localAspectOf");
}
-
-
+ public static String itdAtDeclareParentsField(UnresolvedType aspectType, UnresolvedType itdType) {
+ return makeName(aspectType.getNameAsIdentifier(), itdType.getNameAsIdentifier());
+ }
+
public static String privilegedAccessMethodForMethod(String name, UnresolvedType objectType, UnresolvedType aspectType) {
return makeName("privMethod", aspectType.getNameAsIdentifier(),
objectType.getNameAsIdentifier(), name);
return NewConstructorTypeMunger.readConstructor(s, context);
} else if (kind == MethodDelegate) {
return MethodDelegateTypeMunger.readMethod(s, context);
+ } else if (kind == FieldHost) {
+ return MethodDelegateTypeMunger.FieldHostTypeMunger.readFieldHost(s, context);
} else {
throw new RuntimeException("unimplemented");
}
case 2: return Method;
case 5: return Constructor;
case 9: return MethodDelegate;
+ case 10: return FieldHost;
}
throw new BCException("bad kind: " + key);
}
public String toString() {
// we want MethodDelegate to appear as Method in WeaveInfo messages
+ //TODO we may want something for fieldhost ?
if (MethodDelegate.getName().equals(getName())) {
return Method.toString();
} else {
public static final Kind AnnotationOnType = new Kind("AnnotationOnType",8); // not serialized
public static final Kind MethodDelegate = new Kind("MethodDelegate", 9);// serialized, @AJ ITDs
+ public static final Kind FieldHost = new Kind("FieldHost", 10);// serialized, @AJ ITDs
public static final String SUPER_DISPATCH_NAME = "superDispatch";
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
+import java.lang.reflect.Modifier;
import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.Attribute;
import org.aspectj.apache.bcel.classfile.annotation.ElementNameValuePair;
import org.aspectj.apache.bcel.classfile.annotation.RuntimeAnnotations;
import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisibleAnnotations;
+import org.aspectj.apache.bcel.classfile.annotation.ClassElementValue;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.WeaverMessages;
+import org.aspectj.weaver.NewFieldTypeMunger;
+import org.aspectj.weaver.ResolvedMemberImpl;
+import org.aspectj.weaver.Member;
import org.aspectj.weaver.patterns.AndPointcut;
import org.aspectj.weaver.patterns.DeclareErrorOrWarning;
import org.aspectj.weaver.patterns.DeclareParents;
if (acceptAttribute(fattribute)) {
RuntimeAnnotations frvs = (RuntimeAnnotations) fattribute;
if (handleDeclareErrorOrWarningAnnotation(frvs, fstruct)
- || handleDeclareImplementsAnnotation(frvs, fstruct)
|| handleDeclareParentsAnnotation(frvs, fstruct)) {
// semantic check - must be in an @Aspect [remove if previous block bypassed in advance]
if (!type.isAnnotationStyleAspect()) {
}
struct.ajAttributes.addAll(fstruct.ajAttributes);
}
-
+
return struct.ajAttributes;
}
// Note: field annotation are for ITD and DEOW - processed at class level directly
return Collections.EMPTY_LIST;
}
-
-
+
+
/**
* Read @Aspect
*
struct.context,
bindings
);
-
+
// // we can't resolve here since the perclause typically refers to pointcuts
// // defined in the aspect that we haven't told the BcelObjectType about yet.
-//
+//
// perClause.resolve(binding);
-
+
// so we prepare to do it later...
aspectAttribute.setResolutionScope(binding);
return true;
return false;
}
- /**
- * Read @DeclareImplements
- *
- * @param runtimeAnnotations
- * @param struct
- * @return true if found
- */
- private static boolean handleDeclareImplementsAnnotation(RuntimeAnnotations runtimeAnnotations, AjAttributeFieldStruct struct) {//, ResolvedPointcutDefinition preResolvedPointcut) {
- Annotation deci = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREIMPLEMENTS_ANNOTATION);
- if (deci != null) {
- ElementNameValuePair deciPatternNVP = getAnnotationElement(deci, VALUE);
- String deciPattern = deciPatternNVP.getValue().stringifyValue();
- if (deciPattern != null) {
- TypePattern typePattern = parseTypePattern(deciPattern, struct);
- ResolvedType fieldType = UnresolvedType.forSignature(struct.field.getSignature()).resolve(struct.enclosingType.getWorld());
- if (fieldType.isPrimitiveType()) {
- return false;
- } else if (fieldType.isInterface()) {
- TypePattern parent = new ExactTypePattern(UnresolvedType.forSignature(struct.field.getSignature()), false, false);
- parent.resolve(struct.enclosingType.getWorld());
- List parents = new ArrayList(1);
- parents.add(parent);
- //TODO kick ISourceLocation sl = struct.bField.getSourceLocation(); ??
- struct.ajAttributes.add(
- new AjAttribute.DeclareAttribute(
- new DeclareParents(
- typePattern,
- parents,
- false
- )
- )
- );
- return true;
- } else {
- reportError("@DeclareImplements: can only be used on field whose type is an interface", struct);
- return false;
- }
- }
- }
- return false;
- }
+// /**
+// * Read @DeclareImplements
+// *
+// * @param runtimeAnnotations
+// * @param struct
+// * @return true if found
+// */
+// private static boolean handleDeclareImplementsAnnotation(RuntimeAnnotations runtimeAnnotations, AjAttributeFieldStruct struct) {//, ResolvedPointcutDefinition preResolvedPointcut) {
+// Annotation deci = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREIMPLEMENTS_ANNOTATION);
+// if (deci != null) {
+// ElementNameValuePair deciPatternNVP = getAnnotationElement(deci, VALUE);
+// String deciPattern = deciPatternNVP.getValue().stringifyValue();
+// if (deciPattern != null) {
+// TypePattern typePattern = parseTypePattern(deciPattern, struct);
+// ResolvedType fieldType = UnresolvedType.forSignature(struct.field.getSignature()).resolve(struct.enclosingType.getWorld());
+// if (fieldType.isPrimitiveType()) {
+// return false;
+// } else if (fieldType.isInterface()) {
+// TypePattern parent = new ExactTypePattern(UnresolvedType.forSignature(struct.field.getSignature()), false, false);
+// parent.resolve(struct.enclosingType.getWorld());
+// List parents = new ArrayList(1);
+// parents.add(parent);
+// //TODO kick ISourceLocation sl = struct.bField.getSourceLocation(); ??
+// struct.ajAttributes.add(
+// new AjAttribute.DeclareAttribute(
+// new DeclareParents(
+// typePattern,
+// parents,
+// false
+// )
+// )
+// );
+// return true;
+// } else {
+// reportError("@DeclareImplements: can only be used on field whose type is an interface", struct);
+// return false;
+// }
+// }
+// }
+// return false;
+// }
/**
* Read @DeclareParents
if (decpPattern != null) {
TypePattern typePattern = parseTypePattern(decpPattern, struct);
ResolvedType fieldType = UnresolvedType.forSignature(struct.field.getSignature()).resolve(struct.enclosingType.getWorld());
- if (fieldType.isPrimitiveType()) {
- return false;
- } else if (fieldType.isInterface() && (struct.field.isPublic() && struct.field.isStatic())) {
+ if (fieldType.isInterface()) {
TypePattern parent = new ExactTypePattern(UnresolvedType.forSignature(struct.field.getSignature()), false, false);
parent.resolve(struct.enclosingType.getWorld());
//TODO kick ISourceLocation sl = struct.bField.getSourceLocation(); ??
)
)
);
+
+
+ // do we have a defaultImpl=xxx.class (ie implementation)
+ String defaultImplClassName = null;
+ ElementNameValuePair defaultImplNVP = getAnnotationElement(decp, "defaultImpl");
+ if (defaultImplNVP != null) {
+ ClassElementValue defaultImpl = (ClassElementValue) defaultImplNVP.getValue();
+ defaultImplClassName = UnresolvedType.forSignature(defaultImpl.getClassString()).getName();
+ if (defaultImplClassName.equals("org.aspectj.lang.annotation.DeclareParents")) {
+ defaultImplClassName = null;
+ }
+ //TODO check public no arg ctor
+ }
+
// then iterate on field interface hierarchy (not object)
- for (Iterator it = fieldType.getMethods(); it.hasNext();) {
- ResolvedMember method = (ResolvedMember)it.next();
+ boolean hasAtLeastOneMethod = false;
+ ResolvedMember[] methods = (ResolvedMember[])fieldType.getMethodsWithoutIterator(true, false).toArray(new ResolvedMember[0]);
+ for (int i = 0; i < methods.length; i++) {
+ ResolvedMember method = (ResolvedMember)methods[i];
if (method.isAbstract()) {
+ if (defaultImplClassName == null) {
+ // non marker interface with no default impl provided
+ reportError("@DeclareParents: used with a non marker interface and no defaultImpl=\"...\" provided", struct);
+ return false;
+ }
+ hasAtLeastOneMethod = true;
+
struct.ajAttributes.add(
new AjAttribute.TypeMunger(
new MethodDelegateTypeMunger(
method,
struct.enclosingType,
- struct.field.getName(),
+ defaultImplClassName,
typePattern
)
)
);
}
}
+ // successfull so far, we thus need a bcel type munger to have
+ // a field hosting the mixin in the target type
+ if (hasAtLeastOneMethod) {
+ struct.ajAttributes.add(
+ new AjAttribute.TypeMunger(
+ new MethodDelegateTypeMunger.FieldHostTypeMunger(
+ AjcMemberMaker.itdAtDeclareParentsField(
+ null,//prototyped
+ fieldType,
+ struct.enclosingType
+ ),
+ struct.enclosingType,
+ typePattern
+ )
+ )
+ );
+ }
+
return true;
} else {
- reportError("@DeclareParents: can only be used on a public static field whose type is an interface", struct);
+ reportError("@DeclareParents: can only be used on a field whose type is an interface", struct);
return false;
}
}
import org.aspectj.apache.bcel.generic.InstructionHandle;
import org.aspectj.apache.bcel.generic.InstructionList;
import org.aspectj.apache.bcel.generic.Type;
+import org.aspectj.apache.bcel.generic.BranchInstruction;
import org.aspectj.apache.bcel.generic.annotation.AnnotationGen;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.weaver.World;
import org.aspectj.weaver.patterns.DeclareAnnotation;
import org.aspectj.weaver.patterns.Pointcut;
-
+import org.aspectj.lang.Signature;
//XXX addLazyMethodGen is probably bad everywhere
public class BcelTypeMunger extends ConcreteTypeMunger {
changed = mungeNewMethod(weaver, (NewMethodTypeMunger)munger);
} else if (munger.getKind() == ResolvedTypeMunger.MethodDelegate) {
changed = mungeMethodDelegate(weaver, (MethodDelegateTypeMunger)munger);
+ } else if (munger.getKind() == ResolvedTypeMunger.FieldHost) {
+ changed = mungeFieldHost(weaver, (MethodDelegateTypeMunger.FieldHostTypeMunger)munger);
} else if (munger.getKind() == ResolvedTypeMunger.PerObjectInterface) {
changed = mungePerObjectInterface(weaver, (PerObjectInterfaceTypeMunger)munger);
worthReporting = false;
// reportDeclareParentsMessage(WeaveMessage.WEAVEMESSAGE_DECLAREPARENTSEXTENDS,sourceType,parent);
}
- } else {
+ } else if (munger.getKind().equals(ResolvedTypeMunger.FieldHost)) {
+ ;//hidden
+ } else {
ResolvedMember declaredSig = munger.getDeclaredSignature();
if (declaredSig==null) declaredSig= munger.getSignature();
weaver.getWorld().getMessageHandler().handleMessage(WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_ITD,
InstructionList body = new InstructionList();
InstructionFactory fact = gen.getFactory();
- // getstatic field from aspect
- body.append(Utility.createGet(fact, munger.getDelegate()));
-
+ // getfield
+ body.append(InstructionConstants.ALOAD_0);
+ body.append(Utility.createGet(fact, munger.getDelegate(weaver.getLazyClassGen().getType())));
+ BranchInstruction ifNonNull = InstructionFactory.createBranchInstruction(Constants.IFNULL, null);
+ body.append(ifNonNull);
+ InstructionHandle ifNonNullElse = body.append(InstructionConstants.ALOAD_0);
+ body.append(fact.createNew(munger.getImplClassName()));
+ body.append(InstructionConstants.DUP);
+ body.append(fact.createInvoke(munger.getImplClassName(), "<init>", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL));
+ body.append(Utility.createSet(fact, munger.getDelegate(weaver.getLazyClassGen().getType())));
+ ifNonNull.setTarget(ifNonNullElse);
+ body.append(InstructionConstants.ALOAD_0);
+ body.append(Utility.createGet(fact, munger.getDelegate(weaver.getLazyClassGen().getType())));
+
+ //args
int pos = 0;
- if (!introduced.isStatic()) { // skip 'this'
+ if (!introduced.isStatic()) { // skip 'this' (?? can this really happen)
//body.append(InstructionFactory.createThis());
pos++;
}
return false;
}
- private ResolvedMember getRealMemberForITDFromAspect(ResolvedType aspectType,ResolvedMember lookingFor,boolean isCtorRelated) {
+ private boolean mungeFieldHost(BcelClassWeaver weaver, MethodDelegateTypeMunger.FieldHostTypeMunger munger) {
+ LazyClassGen gen = weaver.getLazyClassGen();
+ if (gen.getType().isAnnotation() || gen.getType().isEnum()) {
+ // don't signal error as it could be a consequence of a wild type pattern
+ return false;
+ }
+ boolean shouldApply = munger.matches(weaver.getLazyClassGen().getType(), aspectType);
+ ResolvedMember host = AjcMemberMaker.itdAtDeclareParentsField(
+ weaver.getLazyClassGen().getType(),
+ munger.getSignature().getType(),
+ aspectType);
+ weaver.getLazyClassGen().addField(makeFieldGen(
+ weaver.getLazyClassGen(),
+ host).getField(), null);
+ return true;
+ }
+
+
+ private ResolvedMember getRealMemberForITDFromAspect(ResolvedType aspectType,ResolvedMember lookingFor,boolean isCtorRelated) {
World world = aspectType.getWorld();
boolean debug = false;
if (debug) {