org.aspectj.weaver.bcel.AnnotationAccessVar Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.servicemix.bundles.aspectj
Show all versions of org.apache.servicemix.bundles.aspectj
This OSGi bundle wraps aspectjrt and aspectjweaver ${pkgVersion} jar files.
/* *******************************************************************
* Copyright (c) 2005-2008 Contributors
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v 2.0
* which accompanies this distribution and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
*
* Contributors:
* Andy Clement initial implementation
* ******************************************************************/
package org.aspectj.weaver.bcel;
import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.Field;
import org.aspectj.apache.bcel.generic.Instruction;
import org.aspectj.apache.bcel.generic.InstructionBranch;
import org.aspectj.apache.bcel.generic.InstructionConstants;
import org.aspectj.apache.bcel.generic.InstructionFactory;
import org.aspectj.apache.bcel.generic.InstructionHandle;
import org.aspectj.apache.bcel.generic.InstructionList;
import org.aspectj.apache.bcel.generic.ObjectType;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.Shadow.Kind;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.ast.Var;
/**
* Represents access to an annotation on an element, relating to some kinded pointcut. Depending on the kind of pointcut the element
* might be a field or a method and the code generators in here can retrieve the annotation from the element.
*/
public class AnnotationAccessVar extends BcelVar {
private BcelShadow shadow;
private Kind kind; // What kind of shadow are we at?
private UnresolvedType containingType; // The type upon which we want to ask for 'member'
private Member member; // Holds the member that has the annotations (for method/field join points)
private boolean isWithin; // implies @within() or @withincode(). If false, that implies @annotation()
public AnnotationAccessVar(BcelShadow shadow, Kind kind, ResolvedType annotationType, UnresolvedType theTargetIsStoredHere,
Member sig, boolean isWithin) {
super(annotationType, 0);
this.shadow = shadow;
this.kind = kind;
this.containingType = theTargetIsStoredHere;
this.member = sig;
this.isWithin = isWithin;
}
public Kind getKind() {
return kind;
}
@Override
public String toString() {
return "AnnotationAccessVar(" + getType() + ")";
}
@Override
public Instruction createLoad(InstructionFactory fact) {
throw new IllegalStateException("unimplemented");
}
@Override
public Instruction createStore(InstructionFactory fact) {
throw new IllegalStateException("unimplemented");
}
@Override
public InstructionList createCopyFrom(InstructionFactory fact, int oldSlot) {
throw new IllegalStateException("unimplemented");
}
@Override
public void appendLoad(InstructionList il, InstructionFactory fact) {
il.append(createLoadInstructions(getType(), fact));
}
@Override
public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) {
il.append(createLoadInstructions(toType, fact));
}
@Override
public void insertLoad(InstructionList il, InstructionFactory fact) {
il.insert(createLoadInstructions(getType(), fact));
}
private InstructionList createLoadInstructions(ResolvedType toType, InstructionFactory fact) {
InstructionList il = new InstructionList();
Type jlClass = BcelWorld.makeBcelType(UnresolvedType.JL_CLASS);
Type jlString = BcelWorld.makeBcelType(UnresolvedType.JL_STRING);
Type jlClassArray = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_CLASS_ARRAY);
Type jlaAnnotation = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_ANNOTATION);
Instruction pushConstant = fact.createConstant(new ObjectType(toType.getName()));
if (kind == Shadow.MethodCall || kind == Shadow.MethodExecution || kind == Shadow.PreInitialization
|| kind == Shadow.Initialization || kind == Shadow.ConstructorCall || kind == Shadow.ConstructorExecution
|| kind == Shadow.AdviceExecution ||
// annotations for fieldset/fieldget when an ITD is involved are stored against a METHOD
((kind == Shadow.FieldGet || kind == Shadow.FieldSet) && member.getKind() == Member.METHOD)) {
Type jlrMethod = BcelWorld.makeBcelType(UnresolvedType.forSignature("Ljava/lang/reflect/Method;"));
Type jlAnnotation = BcelWorld.makeBcelType(UnresolvedType.forSignature("Ljava/lang/annotation/Annotation;"));
Type[] paramTypes = BcelWorld.makeBcelTypes(member.getParameterTypes());
// il.append(fact.createConstant(BcelWorld.makeBcelType(containingType)));
if (kind == Shadow.MethodCall
|| kind == Shadow.MethodExecution
|| kind == Shadow.AdviceExecution
||
// annotations for fieldset/fieldget when an ITD is involved are stored against a METHOD
((kind == Shadow.FieldGet || kind == Shadow.FieldSet) && member.getKind() == Member.METHOD)
|| ((kind == Shadow.ConstructorCall || kind == Shadow.ConstructorExecution) && member.getKind() == Member.METHOD)) {
// Need to look at the cached annotation before going to fetch it again
Field annotationCachingField = shadow.getEnclosingClass().getAnnotationCachingField(shadow, toType, isWithin);
// Basic idea here is to check if the cached field is null, if it is then initialize it, otherwise use it
il.append(fact.createGetStatic(shadow.getEnclosingClass().getName(), annotationCachingField.getName(), jlAnnotation));
il.append(InstructionConstants.DUP);
InstructionBranch ifNonNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null);
il.append(ifNonNull);
il.append(InstructionConstants.POP);
il.append(fact.createConstant(BcelWorld.makeBcelType(containingType)));
il.append(fact.createConstant(member.getName()));
buildArray(il, fact, jlClass, paramTypes, 1);
// OPTIMIZE cache result of getDeclaredMethod?
il.append(fact.createInvoke("java/lang/Class", "getDeclaredMethod", jlrMethod,
new Type[] { jlString, jlClassArray }, Constants.INVOKEVIRTUAL));
il.append(pushConstant);// fact.createConstant(new ObjectType(toType.getName())));
il.append(fact.createInvoke("java/lang/reflect/Method", "getAnnotation", jlaAnnotation, new Type[] { jlClass },
Constants.INVOKEVIRTUAL));
il.append(InstructionConstants.DUP);
il.append(fact.createPutStatic(shadow.getEnclosingClass().getName(), annotationCachingField.getName(), jlAnnotation));
InstructionHandle ifNullElse = il.append(InstructionConstants.NOP);
ifNonNull.setTarget(ifNullElse);
} else { // init/preinit/ctor-call/ctor-exec
il.append(fact.createConstant(BcelWorld.makeBcelType(containingType)));
buildArray(il, fact, jlClass, paramTypes, 1);
Type jlrCtor = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_REFLECT_CONSTRUCTOR);
// OPTIMIZE cache result of getDeclaredConstructor and getAnnotation? Might be able to use it again if someone else
// needs the same annotations?
il.append(fact.createInvoke("java/lang/Class", "getDeclaredConstructor", jlrCtor, new Type[] { jlClassArray },
Constants.INVOKEVIRTUAL));
il.append(pushConstant);
il.append(fact.createInvoke("java/lang/reflect/Constructor", "getAnnotation", jlaAnnotation,
new Type[] { jlClass }, Constants.INVOKEVIRTUAL));
}
} else if (kind == Shadow.FieldSet || kind == Shadow.FieldGet) {
generateBytecodeToAccessAnnotationAtFieldGetSetShadow(toType, fact, il, pushConstant);
} else if (kind == Shadow.StaticInitialization || kind == Shadow.ExceptionHandler) {
il.append(fact.createConstant(BcelWorld.makeBcelType(containingType)));
il.append(pushConstant);
il.append(fact.createInvoke("java/lang/Class", "getAnnotation", jlaAnnotation, new Type[] { jlClass },
Constants.INVOKEVIRTUAL));
} else {
throw new RuntimeException("Don't understand this kind " + kind);
}
il.append(Utility.createConversion(fact, jlaAnnotation, BcelWorld.makeBcelType(toType)));
return il;
}
/**
* At a FieldGet or FieldSet shadow, generate the bytecode to access the annotation for that field. The annotation is cached so
* the code checks that cached value before proceeding.
*/
private void generateBytecodeToAccessAnnotationAtFieldGetSetShadow(ResolvedType toType, InstructionFactory fact,
InstructionList il, Instruction pushConstantAnnotationType) {
Type jlClass = BcelWorld.makeBcelType(UnresolvedType.JL_CLASS);
Type jlString = BcelWorld.makeBcelType(UnresolvedType.JL_STRING);
Type jlaAnnotation = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_ANNOTATION);
Type jlrField = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_REFLECT_FIELD);
LazyClassGen shadowEnclosingClass = shadow.getEnclosingClass();
// The annotation for the field of interest is cached, check cached value before fetching it
Field annotationCachingField = shadowEnclosingClass.getAnnotationCachingField(shadow, toType, isWithin);
String annotationCachingFieldName = annotationCachingField.getName();
// Basic idea here is to check if the cached field is null, if it is then initialize it, otherwise use it
il.append(fact.createGetStatic(shadowEnclosingClass.getName(), annotationCachingFieldName, jlaAnnotation));
il.appendDUP();
InstructionBranch ifNonNull = new InstructionBranch(Constants.IFNONNULL, null);
il.append(ifNonNull);
il.appendPOP();
// get the field of interest
il.append(fact.createConstant(BcelWorld.makeBcelType(containingType)));
il.append(fact.createConstant(member.getName()));
il.append(fact.createInvoke("java/lang/Class", "getDeclaredField", jlrField, new Type[] { jlString },
Constants.INVOKEVIRTUAL));
il.append(pushConstantAnnotationType);
il.append(fact.createInvoke("java/lang/reflect/Field", "getAnnotation", jlaAnnotation, new Type[] { jlClass },
Constants.INVOKEVIRTUAL));
il.appendDUP();
il.append(fact.createPutStatic(shadowEnclosingClass.getName(), annotationCachingFieldName, jlaAnnotation));
InstructionHandle ifNullElse = il.appendNOP();
ifNonNull.setTarget(ifNullElse);
}
private void buildArray(InstructionList il, InstructionFactory fact, Type arrayElementType, Type[] arrayEntries, int dim) {
il.append(fact.createConstant(arrayEntries == null ? 0 : arrayEntries.length));
il.append(fact.createNewArray(arrayElementType, (short) dim));
if (arrayEntries == null) {
return;
}
for (int i = 0; i < arrayEntries.length; i++) {
il.append(InstructionFactory.createDup(1));
il.append(fact.createConstant(i));
switch (arrayEntries[i].getType()) {
case Constants.T_ARRAY:
il.append(fact.createConstant(new ObjectType(arrayEntries[i].getSignature()))); // FIXME should be getName() and not
// getSignature()?
break;
case Constants.T_BOOLEAN:
il.append(fact.createGetStatic("java/lang/Boolean", "TYPE", arrayElementType));
break;
case Constants.T_BYTE:
il.append(fact.createGetStatic("java/lang/Byte", "TYPE", arrayElementType));
break;
case Constants.T_CHAR:
il.append(fact.createGetStatic("java/lang/Character", "TYPE", arrayElementType));
break;
case Constants.T_INT:
il.append(fact.createGetStatic("java/lang/Integer", "TYPE", arrayElementType));
break;
case Constants.T_LONG:
il.append(fact.createGetStatic("java/lang/Long", "TYPE", arrayElementType));
break;
case Constants.T_DOUBLE:
il.append(fact.createGetStatic("java/lang/Double", "TYPE", arrayElementType));
break;
case Constants.T_FLOAT:
il.append(fact.createGetStatic("java/lang/Float", "TYPE", arrayElementType));
break;
case Constants.T_SHORT:
il.append(fact.createGetStatic("java/lang/Short", "TYPE", arrayElementType));
break;
default:
il.append(fact.createConstant(arrayEntries[i]));
}
il.append(InstructionConstants.AASTORE);
}
}
public Member getMember() {
return member;
}
/**
* Return an object that can access a particular value of this annotation.
*
* @param valueType The type from the annotation that is of interest
* @param formalName the formal name expressed in the pointcut, can be used to disambiguate
* @return a variable that represents access to that annotation value
*/
@Override
public Var getAccessorForValue(ResolvedType valueType, String formalName) {
return new AnnotationAccessFieldVar(this, valueType, formalName);
}
}