BcelTypeMunger.java
/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* 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:
* PARC initial implementation
* Alexandre Vasseur @AspectJ ITDs
* ******************************************************************/
package org.aspectj.weaver.bcel;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.ClassFormatException;
import org.aspectj.apache.bcel.classfile.ConstantPool;
import org.aspectj.apache.bcel.classfile.Signature;
import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
import org.aspectj.apache.bcel.generic.FieldGen;
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.InvokeInstruction;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.asm.AsmManager;
import org.aspectj.asm.IProgramElement;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.Message;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.bridge.WeaveMessage;
import org.aspectj.bridge.context.CompilationAndWeavingContext;
import org.aspectj.bridge.context.ContextToken;
import org.aspectj.weaver.AjcMemberMaker;
import org.aspectj.weaver.AnnotationAJ;
import org.aspectj.weaver.AnnotationOnTypeMunger;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.ConcreteTypeMunger;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.MemberUtils;
import org.aspectj.weaver.MethodDelegateTypeMunger;
import org.aspectj.weaver.NameMangler;
import org.aspectj.weaver.NewConstructorTypeMunger;
import org.aspectj.weaver.NewFieldTypeMunger;
import org.aspectj.weaver.NewMemberClassTypeMunger;
import org.aspectj.weaver.NewMethodTypeMunger;
import org.aspectj.weaver.NewParentTypeMunger;
import org.aspectj.weaver.PerObjectInterfaceTypeMunger;
import org.aspectj.weaver.PrivilegedAccessMunger;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedMemberImpl;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.ResolvedTypeMunger;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.TypeVariableReference;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.WeaverStateInfo;
import org.aspectj.weaver.World;
import org.aspectj.weaver.model.AsmRelationshipProvider;
import org.aspectj.weaver.patterns.DeclareAnnotation;
import org.aspectj.weaver.patterns.Pointcut;
public class BcelTypeMunger extends ConcreteTypeMunger {
public BcelTypeMunger(ResolvedTypeMunger munger, ResolvedType aspectType) {
super(munger, aspectType);
}
@Override
public String toString() {
return "(BcelTypeMunger " + getMunger() + ")";
}
@Override
public boolean shouldOverwrite() {
return false;
}
public boolean munge(BcelClassWeaver weaver) {
ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.MUNGING_WITH, this);
boolean changed = false;
boolean worthReporting = true;
if (weaver.getWorld().isOverWeaving()) {
WeaverStateInfo typeWeaverState = weaver.getLazyClassGen().getType().getWeaverState();
if (typeWeaverState != null && typeWeaverState.isAspectAlreadyApplied(getAspectType())) {
return false;
}
}
if (munger.getKind() == ResolvedTypeMunger.Field) {
changed = mungeNewField(weaver, (NewFieldTypeMunger) munger);
} else if (munger.getKind() == ResolvedTypeMunger.Method) {
changed = mungeNewMethod(weaver, (NewMethodTypeMunger) munger);
} else if (munger.getKind() == ResolvedTypeMunger.InnerClass) {
changed = mungeNewMemberType(weaver, (NewMemberClassTypeMunger) munger);
} else if (munger.getKind() == ResolvedTypeMunger.MethodDelegate2) {
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;
} else if (munger.getKind() == ResolvedTypeMunger.PerTypeWithinInterface) {
// PTWIMPL Transform the target type (add the aspect instance field)
changed = mungePerTypeWithinTransformer(weaver);
worthReporting = false;
} else if (munger.getKind() == ResolvedTypeMunger.PrivilegedAccess) {
changed = mungePrivilegedAccess(weaver, (PrivilegedAccessMunger) munger);
worthReporting = false;
} else if (munger.getKind() == ResolvedTypeMunger.Constructor) {
changed = mungeNewConstructor(weaver, (NewConstructorTypeMunger) munger);
} else if (munger.getKind() == ResolvedTypeMunger.Parent) {
changed = mungeNewParent(weaver, (NewParentTypeMunger) munger);
} else if (munger.getKind() == ResolvedTypeMunger.AnnotationOnType) {
changed = mungeNewAnnotationOnType(weaver, (AnnotationOnTypeMunger) munger);
worthReporting = false;
} else {
throw new RuntimeException("unimplemented");
}
if (changed && munger.changesPublicSignature()) {
WeaverStateInfo info = weaver.getLazyClassGen().getOrCreateWeaverStateInfo(weaver.getReweavableMode());
info.addConcreteMunger(this);
}
if (changed && worthReporting) {
ResolvedType declaringAspect = null;
AsmManager model = ((BcelWorld) getWorld()).getModelAsAsmManager();
if (model != null) {
if (munger instanceof NewParentTypeMunger) {
NewParentTypeMunger nptMunger = (NewParentTypeMunger) munger;
declaringAspect = nptMunger.getDeclaringType();
if (declaringAspect.isParameterizedOrGenericType()) {
declaringAspect = declaringAspect.getRawType();
}
ResolvedType thisAspect = getAspectType();
AsmRelationshipProvider.addRelationship(model, weaver.getLazyClassGen().getType(), munger, thisAspect);
// Add a relationship on the actual declaring aspect too
if (!thisAspect.equals(declaringAspect)) {
// Might be the case the declaring aspect is generic and thisAspect is parameterizing it. In that case
// record the actual parameterizations
ResolvedType target = weaver.getLazyClassGen().getType();
ResolvedType newParent = nptMunger.getNewParent();
IProgramElement thisAspectNode = model.getHierarchy().findElementForType(thisAspect.getPackageName(),
thisAspect.getClassName());
Map<String, List<String>> declareParentsMap = thisAspectNode.getDeclareParentsMap();
if (declareParentsMap == null) {
declareParentsMap = new HashMap<>();
thisAspectNode.setDeclareParentsMap(declareParentsMap);
}
String tname = target.getName();
String pname = newParent.getName();
List<String> newparents = declareParentsMap.computeIfAbsent(tname, k -> new ArrayList<>());
newparents.add(pname);
AsmRelationshipProvider.addRelationship(model, weaver.getLazyClassGen().getType(), munger, declaringAspect);
}
} else {
declaringAspect = getAspectType();
AsmRelationshipProvider.addRelationship(model, weaver.getLazyClassGen().getType(), munger, declaringAspect);
}
}
}
// TAG: WeavingMessage
if (changed && worthReporting && munger != null && !weaver.getWorld().getMessageHandler().isIgnoring(IMessage.WEAVEINFO)) {
String tName = weaver.getLazyClassGen().getType().getSourceLocation().getSourceFile().getName();
if (tName.contains("no debug info available")) {
tName = "no debug info available";
} else {
tName = getShortname(weaver.getLazyClassGen().getType().getSourceLocation().getSourceFile().getPath());
}
String fName = getShortname(getAspectType().getSourceLocation().getSourceFile().getPath());
if (munger.getKind().equals(ResolvedTypeMunger.Parent)) {
// This message could come out of AjLookupEnvironment.addParent
// if doing parents munging at compile time only...
NewParentTypeMunger parentTM = (NewParentTypeMunger) munger;
if (parentTM.isMixin()) {
weaver.getWorld()
.getMessageHandler()
.handleMessage(
WeaveMessage.constructWeavingMessage(
WeaveMessage.WEAVEMESSAGE_MIXIN,
new String[] {
parentTM.getNewParent().getName(), fName,
weaver.getLazyClassGen().getType().getName(), tName
},
weaver.getLazyClassGen().getClassName(), getAspectType().getName(),
parentTM.getNewParent().getSourceLocation(), weaver.getLazyClassGen().getType().getSourceLocation()
)
);
}
else {
if (parentTM.getNewParent().isInterface()) {
weaver.getWorld()
.getMessageHandler()
.handleMessage(
WeaveMessage.constructWeavingMessage(
WeaveMessage.WEAVEMESSAGE_DECLAREPARENTSIMPLEMENTS,
new String[] {
weaver.getLazyClassGen().getType().getName(), tName,
parentTM.getNewParent().getName(), fName
},
weaver.getLazyClassGen().getClassName(), getAspectType().getName(),
parentTM.getNewParent().getSourceLocation(), weaver.getLazyClassGen().getType().getSourceLocation()
)
);
}
else {
weaver.getWorld()
.getMessageHandler()
.handleMessage(
WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_DECLAREPARENTSEXTENDS,
new String[] { weaver.getLazyClassGen().getType().getName(), tName,
parentTM.getNewParent().getName(), fName }));
// TAG: WeavingMessage DECLARE PARENTS: EXTENDS
// reportDeclareParentsMessage(WeaveMessage.
// WEAVEMESSAGE_DECLAREPARENTSEXTENDS,sourceType,parent);
}
}
} else if (munger.getKind().equals(ResolvedTypeMunger.FieldHost)) {
// hidden
} else {
ResolvedMember declaredSig = munger.getSignature();
String fromString = fName + ":'" + declaredSig + "'";
// if (declaredSig==null) declaredSig= munger.getSignature();
String kindString = munger.getKind().toString().toLowerCase();
if (kindString.equals("innerclass")) {
kindString = "member class";
fromString = fName;
}
weaver.getWorld()
.getMessageHandler()
.handleMessage(
WeaveMessage.constructWeavingMessage(
WeaveMessage.WEAVEMESSAGE_ITD,
new String[] {
weaver.getLazyClassGen().getType().getName(), tName,
kindString, getAspectType().getName(), fromString
},
weaver.getLazyClassGen().getClassName(), getAspectType().getName(),
weaver.getLazyClassGen().getType().getSourceLocation(), getAspectType().getSourceLocation()
)
);
}
}
CompilationAndWeavingContext.leavingPhase(tok);
return changed;
}
private String getShortname(String path) {
int takefrom = path.lastIndexOf('/');
if (takefrom == -1) {
takefrom = path.lastIndexOf('\\');
}
return path.substring(takefrom + 1);
}
private boolean mungeNewAnnotationOnType(BcelClassWeaver weaver, AnnotationOnTypeMunger munger) {
// FIXME asc this has already been done up front, need to do it here too?
try {
BcelAnnotation anno = (BcelAnnotation) munger.getNewAnnotation();
weaver.getLazyClassGen().addAnnotation(anno.getBcelAnnotation());
} catch (ClassCastException cce) {
throw new IllegalStateException("DiagnosticsFor318237: The typemunger "+munger+" contains an annotation of type "+
munger.getNewAnnotation().getClass().getName()+" when it should be a BcelAnnotation",cce);
}
return true;
}
/**
* For a long time, AspectJ did not allow binary weaving of declare parents. This restriction is now lifted but could do with
* more testing!
*/
private boolean mungeNewParent(BcelClassWeaver weaver, NewParentTypeMunger typeTransformer) {
LazyClassGen newParentTarget = weaver.getLazyClassGen();
ResolvedType newParent = typeTransformer.getNewParent();
boolean performChange = true;
performChange = enforceDecpRule1_abstractMethodsImplemented(weaver, typeTransformer.getSourceLocation(), newParentTarget,
newParent);
performChange = enforceDecpRule2_cantExtendFinalClass(weaver, typeTransformer.getSourceLocation(), newParentTarget,
newParent) && performChange;
List<ResolvedMember> methods = newParent.getMethodsWithoutIterator(false, true, false);
for (ResolvedMember method : methods) {
if (!method.getName().equals("<init>")) {
LazyMethodGen subMethod = findMatchingMethod(newParentTarget, method);
// FIXME asc is this safe for all bridge methods?
if (subMethod != null && !subMethod.isBridgeMethod()) {
if (!(subMethod.isSynthetic() && method.isSynthetic())) {
if (!(subMethod.isStatic() && subMethod.getName().startsWith("access$"))) {
// ignore generated accessors
performChange = enforceDecpRule3_visibilityChanges(weaver, newParent, method, subMethod)
&& performChange;
performChange = enforceDecpRule4_compatibleReturnTypes(weaver, method, subMethod) && performChange;
performChange = enforceDecpRule5_cantChangeFromStaticToNonstatic(weaver,
typeTransformer.getSourceLocation(), method, subMethod)
&& performChange;
}
}
}
}
}
if (!performChange) {
// A rule was violated and an error message already reported
return false;
}
if (newParent.isClass()) {
// Changing the supertype
if (!attemptToModifySuperCalls(weaver, newParentTarget, newParent)) {
return false;
}
newParentTarget.setSuperClass(newParent);
} else {
// Add a new interface
newParentTarget.addInterface(newParent, getSourceLocation());
}
return true;
}
/**
* Rule 1: For the declare parents to be allowed, the target type must override and implement inherited abstract methods (if the
* type is not declared abstract)
*/
private boolean enforceDecpRule1_abstractMethodsImplemented(BcelClassWeaver weaver, ISourceLocation mungerLoc,
LazyClassGen newParentTarget, ResolvedType newParent) {
// Ignore abstract classes or interfaces
if (newParentTarget.isAbstract() || newParentTarget.isInterface()) {
return true;
}
boolean ruleCheckingSucceeded = true;
List<ResolvedMember> newParentMethods = newParent.getMethodsWithoutIterator(false, true, false);
for (ResolvedMember newParentMethod : newParentMethods) {
String newParentMethodName = newParentMethod.getName();
// Ignore abstract ajc$interField prefixed methods
if (newParentMethod.isAbstract() && !newParentMethodName.startsWith("ajc$interField")) {
ResolvedMember discoveredImpl = null;
List<ResolvedMember> targetMethods = newParentTarget.getType().getMethodsWithoutIterator(false, true, false);
for (ResolvedMember targetMethod : targetMethods) {
if (!targetMethod.isAbstract() && targetMethod.getName().equals(newParentMethodName)) {
String newParentMethodSig = newParentMethod.getParameterSignature(); // ([TT;)
String targetMethodSignature = targetMethod.getParameterSignature(); // ([Ljava/lang/Object;)
// could be a match
if (targetMethodSignature.equals(newParentMethodSig)) {
discoveredImpl = targetMethod;
} else {
// Does the erasure match? In which case a bridge method will be created later to
// satisfy the abstract method
if (targetMethod.hasBackingGenericMember()
&& targetMethod.getBackingGenericMember().getParameterSignature().equals(newParentMethodSig)) {
discoveredImpl = targetMethod;
} else if (newParentMethod.hasBackingGenericMember()) {
if (newParentMethod.getBackingGenericMember().getParameterSignature().equals(targetMethodSignature)) { // newParentMethod.getBackingGenericMember().getParameterSignature gives: (Pjava/util/List<TI;>;) targetMethodSignature= (Ljava/util/List;)
discoveredImpl = targetMethod;
} else if (targetMethod instanceof BcelMethod) {
// BcelMethod does not have backing generic member set (need to investigate why). For now, special case here:
UnresolvedType[] targetMethodGenericParameterTypes = targetMethod.getGenericParameterTypes();
if (targetMethodGenericParameterTypes !=null) {
StringBuilder b = new StringBuilder("(");
for (UnresolvedType p: targetMethodGenericParameterTypes) {
b.append(p.getSignature());
}
b.append(')');
if (b.toString().equals(newParentMethodSig)) {
discoveredImpl = targetMethod;
}
}
}
}
}
if (discoveredImpl != null) {
break;
}
}
}
if (discoveredImpl == null) {
// didnt find a valid implementation, lets check the
// ITDs on this type to see if they satisfy it
boolean satisfiedByITD = false;
for (ConcreteTypeMunger m : newParentTarget.getType().getInterTypeMungersIncludingSupers()) {
if (m.getMunger() != null && m.getMunger().getKind() == ResolvedTypeMunger.Method) {
ResolvedMember sig = m.getSignature();
if (!Modifier.isAbstract(sig.getModifiers())) {
// If the ITD shares a type variable with some target type, we need to tailor it
// for that type
if (m.isTargetTypeParameterized()) {
ResolvedType genericOnType = getWorld().resolve(sig.getDeclaringType()).getGenericType();
ResolvedType actualOccurrence = newParent.discoverActualOccurrenceOfTypeInHierarchy(genericOnType);
if (actualOccurrence == null) {
// Handle the case where the ITD is onto the type targeted by the declare parents (PR478003)
actualOccurrence = newParentTarget.getType().discoverActualOccurrenceOfTypeInHierarchy(genericOnType);
}
m = m.parameterizedFor(actualOccurrence);
// possible sig change when type parameters filled in
sig = m.getSignature();
}
if (ResolvedType.matches(
AjcMemberMaker.interMethod(sig, m.getAspectType(),
sig.getDeclaringType().resolve(weaver.getWorld()).isInterface()), newParentMethod)) {
satisfiedByITD = true;
}
}
} else if (m.getMunger() != null && m.getMunger().getKind() == ResolvedTypeMunger.MethodDelegate2) {
// AV - that should be enough, no need to check more
satisfiedByITD = true;
}
}
if (!satisfiedByITD) {
error(weaver,
"The type " + newParentTarget.getName() + " must implement the inherited abstract method "
+ newParentMethod.getDeclaringType() + "." + newParentMethodName
+ newParentMethod.getParameterSignature(), newParentTarget.getType().getSourceLocation(),
new ISourceLocation[] { newParentMethod.getSourceLocation(), mungerLoc });
ruleCheckingSucceeded = false;
}
}
}
}
return ruleCheckingSucceeded;
}
/**
* Rule 2. Can't extend final types
*/
private boolean enforceDecpRule2_cantExtendFinalClass(BcelClassWeaver weaver, ISourceLocation transformerLoc,
LazyClassGen targetType, ResolvedType newParent) {
if (newParent.isFinal()) {
error(weaver, "Cannot make type " + targetType.getName() + " extend final class " + newParent.getName(), targetType
.getType().getSourceLocation(), new ISourceLocation[] { transformerLoc });
return false;
}
return true;
}
/**
* Rule 3. Can't narrow visibility of methods when overriding
*/
private boolean enforceDecpRule3_visibilityChanges(BcelClassWeaver weaver, ResolvedType newParent, ResolvedMember superMethod,
LazyMethodGen subMethod) {
boolean cont = true;
if (Modifier.isPublic(superMethod.getModifiers())) {
if (subMethod.isProtected() || subMethod.isDefault() || subMethod.isPrivate()) {
weaver.getWorld()
.getMessageHandler()
.handleMessage(
MessageUtil.error("Cannot reduce the visibility of the inherited method '" + superMethod
+ "' from " + newParent.getName(), superMethod.getSourceLocation()));
cont = false;
}
} else if (Modifier.isProtected(superMethod.getModifiers())) {
if (subMethod.isDefault() || subMethod.isPrivate()) {
weaver.getWorld()
.getMessageHandler()
.handleMessage(
MessageUtil.error("Cannot reduce the visibility of the inherited method '" + superMethod
+ "' from " + newParent.getName(), superMethod.getSourceLocation()));
cont = false;
}
} else if (superMethod.isDefault()) {
if (subMethod.isPrivate()) {
weaver.getWorld()
.getMessageHandler()
.handleMessage(
MessageUtil.error("Cannot reduce the visibility of the inherited method '" + superMethod
+ "' from " + newParent.getName(), superMethod.getSourceLocation()));
cont = false;
}
}
return cont;
}
/**
* Rule 4. Can't have incompatible return types
*/
private boolean enforceDecpRule4_compatibleReturnTypes(BcelClassWeaver weaver, ResolvedMember superMethod,
LazyMethodGen subMethod) {
boolean cont = true;
String superReturnTypeSig = superMethod.getGenericReturnType().getSignature(); // eg. Pjava/util/Collection<LFoo;>
String subReturnTypeSig = subMethod.getGenericReturnTypeSignature();
superReturnTypeSig = superReturnTypeSig.replace('.', '/');
subReturnTypeSig = subReturnTypeSig.replace('.', '/');
if (!superReturnTypeSig.equals(subReturnTypeSig)) {
// Check for covariance
ResolvedType subType = weaver.getWorld().resolve(subMethod.getReturnType());
ResolvedType superType = weaver.getWorld().resolve(superMethod.getReturnType());
if (!superType.isAssignableFrom(subType)) {
weaver.getWorld()
.getMessageHandler()
.handleMessage(
MessageUtil.error("The return type is incompatible with " + superMethod.getDeclaringType() + "."
+ superMethod.getName() + superMethod.getParameterSignature(),
subMethod.getSourceLocation()));
// this just might be a better error message...
// "The return type '"+subReturnTypeSig+
// "' is incompatible with the overridden method "
// +superMethod.getDeclaringType()+"."+
// superMethod.getName()+superMethod.getParameterSignature()+
// " which returns '"+superReturnTypeSig+"'",
cont = false;
}
}
return cont;
}
/**
* Rule5. Method overrides can't change the staticality (word?) - you can't override and make an instance method static or
* override and make a static method an instance method.
*/
private boolean enforceDecpRule5_cantChangeFromStaticToNonstatic(BcelClassWeaver weaver, ISourceLocation mungerLoc,
ResolvedMember superMethod, LazyMethodGen subMethod) {
boolean superMethodStatic = Modifier.isStatic(superMethod.getModifiers());
if (superMethodStatic && !subMethod.isStatic()) {
error(weaver, "This instance method " + subMethod.getName() + subMethod.getParameterSignature()
+ " cannot override the static method from " + superMethod.getDeclaringType().getName(),
subMethod.getSourceLocation(), new ISourceLocation[] { mungerLoc });
return false;
} else if (!superMethodStatic && subMethod.isStatic()) {
error(weaver, "The static method " + subMethod.getName() + subMethod.getParameterSignature()
+ " cannot hide the instance method from " + superMethod.getDeclaringType().getName(),
subMethod.getSourceLocation(), new ISourceLocation[] { mungerLoc });
return false;
}
return true;
}
public void error(BcelClassWeaver weaver, String text, ISourceLocation primaryLoc, ISourceLocation[] extraLocs) {
IMessage msg = new Message(text, primaryLoc, true, extraLocs);
weaver.getWorld().getMessageHandler().handleMessage(msg);
}
/**
* Search the specified type for a particular method - do not use the return value in the comparison as it is not considered for
* overriding.
*/
private LazyMethodGen findMatchingMethod(LazyClassGen type, ResolvedMember searchMethod) {
String searchName = searchMethod.getName();
String searchSig = searchMethod.getParameterSignature();
for (LazyMethodGen method : type.getMethodGens()) {
if (method.getName().equals(searchName) && method.getParameterSignature().equals(searchSig)) {
return method;
}
}
return null;
}
/**
* The main part of implementing declare parents extends. Modify super ctor calls to target the new type.
*/
public boolean attemptToModifySuperCalls(BcelClassWeaver weaver, LazyClassGen newParentTarget, ResolvedType newParent) {
ResolvedType currentParentType = newParentTarget.getSuperClass();
if (currentParentType.getGenericType() != null) {
currentParentType = currentParentType.getGenericType();
}
String currentParent = currentParentType.getName();
if (newParent.getGenericType() != null) {
newParent = newParent.getGenericType(); // target new super calls at
}
// the generic type if its raw or parameterized
List<LazyMethodGen> mgs = newParentTarget.getMethodGens();
// Look for ctors to modify
for (LazyMethodGen aMethod : mgs) {
if (LazyMethodGen.isConstructor(aMethod)) {
InstructionList insList = aMethod.getBody();
InstructionHandle handle = insList.getStart();
while (handle != null) {
if (handle.getInstruction().opcode == Constants.INVOKESPECIAL) {
ConstantPool cpg = newParentTarget.getConstantPool();
InvokeInstruction invokeSpecial = (InvokeInstruction) handle.getInstruction();
if (invokeSpecial.getClassName(cpg).equals(currentParent)
&& invokeSpecial.getMethodName(cpg).equals("<init>")) {
// System.err.println("Transforming super call '<init>" + invokeSpecial.getSignature(cpg) + "'");
// 1. Check there is a ctor in the new parent with
// the same signature
ResolvedMember newCtor = getConstructorWithSignature(newParent, invokeSpecial.getSignature(cpg));
if (newCtor == null) {
// 2. Check ITDCs to see if the necessary ctor is provided that way
boolean satisfiedByITDC = false;
for (Iterator<ConcreteTypeMunger> ii = newParentTarget.getType()
.getInterTypeMungersIncludingSupers().iterator(); ii.hasNext() && !satisfiedByITDC;) {
ConcreteTypeMunger m = ii.next();
if (m.getMunger() instanceof NewConstructorTypeMunger) {
if (m.getSignature().getSignature().equals(invokeSpecial.getSignature(cpg))) {
satisfiedByITDC = true;
}
}
}
if (!satisfiedByITDC) {
String csig = createReadableCtorSig(newParent, cpg, invokeSpecial);
weaver.getWorld()
.getMessageHandler()
.handleMessage(
MessageUtil.error(
"Unable to modify hierarchy for " + newParentTarget.getClassName()
+ " - the constructor " + csig + " is missing",
this.getSourceLocation()));
return false;
}
}
int idx = cpg.addMethodref(newParent.getName(), invokeSpecial.getMethodName(cpg),
invokeSpecial.getSignature(cpg));
invokeSpecial.setIndex(idx);
}
}
handle = handle.getNext();
}
}
}
return true;
}
/**
* Creates a nice signature for the ctor, something like "(int,Integer,String)"
*/
private String createReadableCtorSig(ResolvedType newParent, ConstantPool cpg, InvokeInstruction invokeSpecial) {
StringBuilder sb = new StringBuilder();
Type[] ctorArgs = invokeSpecial.getArgumentTypes(cpg);
sb.append(newParent.getClassName());
sb.append("(");
for (int i = 0; i < ctorArgs.length; i++) {
String argtype = ctorArgs[i].toString();
if (argtype.lastIndexOf(".") != -1) {
sb.append(argtype.substring(argtype.lastIndexOf(".") + 1));
} else {
sb.append(argtype);
}
if (i + 1 < ctorArgs.length) {
sb.append(",");
}
}
sb.append(")");
return sb.toString();
}
private ResolvedMember getConstructorWithSignature(ResolvedType type, String searchSig) {
for (ResolvedMember method : type.getDeclaredJavaMethods()) {
if (MemberUtils.isConstructor(method)) {
if (method.getSignature().equals(searchSig)) {
return method;
}
}
}
return null;
}
private boolean mungePrivilegedAccess(BcelClassWeaver weaver, PrivilegedAccessMunger munger) {
LazyClassGen gen = weaver.getLazyClassGen();
ResolvedMember member = munger.getMember();
ResolvedType onType = weaver.getWorld().resolve(member.getDeclaringType(), munger.getSourceLocation());
if (onType.isRawType()) {
onType = onType.getGenericType();
}
// System.out.println("munging: " + gen + " with " + member);
if (onType.equals(gen.getType())) {
if (member.getKind() == Member.FIELD) {
// System.out.println("matched: " + gen);
addFieldGetter(gen, member,
AjcMemberMaker.privilegedAccessMethodForFieldGet(aspectType, member, munger.shortSyntax));
addFieldSetter(gen, member,
AjcMemberMaker.privilegedAccessMethodForFieldSet(aspectType, member, munger.shortSyntax));
return true;
} else if (member.getKind() == Member.METHOD) {
addMethodDispatch(gen, member, AjcMemberMaker.privilegedAccessMethodForMethod(aspectType, member));
return true;
} else if (member.getKind() == Member.CONSTRUCTOR) {
for (LazyMethodGen m : gen.getMethodGens()) {
if (m.getMemberView() != null && m.getMemberView().getKind() == Member.CONSTRUCTOR) {
// m.getMemberView().equals(member)) {
m.forcePublic();
// return true;
}
}
return true;
// throw new BCException("no match for " + member + " in " +
// gen);
} else if (member.getKind() == Member.STATIC_INITIALIZATION) {
gen.forcePublic();
return true;
} else {
throw new RuntimeException("unimplemented");
}
}
return false;
}
private void addFieldGetter(LazyClassGen gen, ResolvedMember field, ResolvedMember accessMethod) {
LazyMethodGen mg = makeMethodGen(gen, accessMethod);
InstructionList il = new InstructionList();
InstructionFactory fact = gen.getFactory();
if (Modifier.isStatic(field.getModifiers())) {
il.append(fact.createFieldAccess(gen.getClassName(), field.getName(), BcelWorld.makeBcelType(field.getType()),
Constants.GETSTATIC));
} else {
il.append(InstructionConstants.ALOAD_0);
il.append(fact.createFieldAccess(gen.getClassName(), field.getName(), BcelWorld.makeBcelType(field.getType()),
Constants.GETFIELD));
}
il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(field.getType())));
mg.getBody().insert(il);
gen.addMethodGen(mg, getSignature().getSourceLocation());
}
private void addFieldSetter(LazyClassGen gen, ResolvedMember field, ResolvedMember accessMethod) {
LazyMethodGen mg = makeMethodGen(gen, accessMethod);
InstructionList il = new InstructionList();
InstructionFactory fact = gen.getFactory();
Type fieldType = BcelWorld.makeBcelType(field.getType());
if (Modifier.isStatic(field.getModifiers())) {
il.append(InstructionFactory.createLoad(fieldType, 0));
il.append(fact.createFieldAccess(gen.getClassName(), field.getName(), fieldType, Constants.PUTSTATIC));
} else {
il.append(InstructionConstants.ALOAD_0);
il.append(InstructionFactory.createLoad(fieldType, 1));
il.append(fact.createFieldAccess(gen.getClassName(), field.getName(), fieldType, Constants.PUTFIELD));
}
il.append(InstructionFactory.createReturn(Type.VOID));
mg.getBody().insert(il);
gen.addMethodGen(mg, getSignature().getSourceLocation());
}
private void addMethodDispatch(LazyClassGen gen, ResolvedMember method, ResolvedMember accessMethod) {
LazyMethodGen mg = makeMethodGen(gen, accessMethod);
InstructionList il = new InstructionList();
InstructionFactory fact = gen.getFactory();
Type[] paramTypes = BcelWorld.makeBcelTypes(method.getParameterTypes());
int pos = 0;
if (!Modifier.isStatic(method.getModifiers())) {
il.append(InstructionConstants.ALOAD_0);
pos++;
}
for (Type paramType : paramTypes) {
il.append(InstructionFactory.createLoad(paramType, pos));
pos += paramType.getSize();
}
il.append(Utility.createInvoke(fact, (BcelWorld) aspectType.getWorld(), method));
il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(method.getReturnType())));
mg.getBody().insert(il);
gen.addMethodGen(mg);
}
protected LazyMethodGen makeMethodGen(LazyClassGen gen, ResolvedMember member) {
try {
Type returnType = BcelWorld.makeBcelType(member.getReturnType());
Type[] parameterTypes = BcelWorld.makeBcelTypes(member.getParameterTypes());
LazyMethodGen ret = new LazyMethodGen(member.getModifiers(), returnType,
member.getName(), parameterTypes, UnresolvedType.getNames(member
.getExceptions()), gen);
// 43972 : Static crosscutting makes interfaces unusable for javac
// ret.makeSynthetic();
return ret;
} catch (ClassFormatException cfe) {
throw new RuntimeException("Problem with makeMethodGen for method "+member.getName()+" in type "+gen.getName()+" ret="+member.getReturnType(),cfe);
}
}
protected FieldGen makeFieldGen(LazyClassGen gen, ResolvedMember member) {
return new FieldGen(member.getModifiers(), BcelWorld.makeBcelType(member.getReturnType()), member.getName(),
gen.getConstantPool());
}
private boolean mungePerObjectInterface(BcelClassWeaver weaver, PerObjectInterfaceTypeMunger munger) {
// System.err.println("Munging perobject ["+munger+"] onto "+weaver.
// getLazyClassGen().getClassName());
LazyClassGen gen = weaver.getLazyClassGen();
if (couldMatch(gen.getBcelObjectType(), munger.getTestPointcut())) {
FieldGen fg = makeFieldGen(gen, AjcMemberMaker.perObjectField(gen.getType(), aspectType));
gen.addField(fg, getSourceLocation());
Type fieldType = BcelWorld.makeBcelType(aspectType);
LazyMethodGen mg = new LazyMethodGen(Modifier.PUBLIC, fieldType, NameMangler.perObjectInterfaceGet(aspectType),
Type.NO_ARGS, new String[0], gen);
InstructionList il = new InstructionList();
InstructionFactory fact = gen.getFactory();
il.append(InstructionConstants.ALOAD_0);
il.append(fact.createFieldAccess(gen.getClassName(), fg.getName(), fieldType, Constants.GETFIELD));
il.append(InstructionFactory.createReturn(fieldType));
mg.getBody().insert(il);
gen.addMethodGen(mg);
LazyMethodGen mg1 = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, NameMangler.perObjectInterfaceSet(aspectType),
new Type[] { fieldType, }, new String[0], gen);
InstructionList il1 = new InstructionList();
il1.append(InstructionConstants.ALOAD_0);
il1.append(InstructionFactory.createLoad(fieldType, 1));
il1.append(fact.createFieldAccess(gen.getClassName(), fg.getName(), fieldType, Constants.PUTFIELD));
il1.append(InstructionFactory.createReturn(Type.VOID));
mg1.getBody().insert(il1);
gen.addMethodGen(mg1);
gen.addInterface(munger.getInterfaceType().resolve(weaver.getWorld()), getSourceLocation());
return true;
} else {
return false;
}
}
// PTWIMPL Add field to hold aspect instance and an accessor
private boolean mungePerTypeWithinTransformer(BcelClassWeaver weaver) {
LazyClassGen gen = weaver.getLazyClassGen();
// if (couldMatch(gen.getBcelObjectType(), munger.getTestPointcut())) {
// Add (to the target type) the field that will hold the aspect instance
// e.g ajc$com_blah_SecurityAspect$ptwAspectInstance
FieldGen fg = makeFieldGen(gen, AjcMemberMaker.perTypeWithinField(gen.getType(), aspectType));
gen.addField(fg, getSourceLocation());
if (!gen.getType().canBeSeenBy(aspectType) && aspectType.isPrivilegedAspect()) {
gen.forcePublic();
}
// Add an accessor for this new field, the
// ajc$<aspectname>$localAspectOf() method
// e.g.
// "public com_blah_SecurityAspect ajc$com_blah_SecurityAspect$localAspectOf()"
Type fieldType = BcelWorld.makeBcelType(aspectType);
LazyMethodGen mg = new LazyMethodGen(Modifier.PUBLIC | Modifier.STATIC, fieldType,
NameMangler.perTypeWithinLocalAspectOf(aspectType), Type.NO_ARGS, new String[0], gen);
InstructionList il = new InstructionList();
// PTWIMPL ?? Should check if it is null and throw
// NoAspectBoundException
InstructionFactory fact = gen.getFactory();
il.append(fact.createFieldAccess(gen.getClassName(), fg.getName(), fieldType, Constants.GETSTATIC));
il.append(InstructionFactory.createReturn(fieldType));
mg.getBody().insert(il);
gen.addMethodGen(mg);
return true;
// } else {
// return false;
// }
}
// ??? Why do we have this method? I thought by now we would know if it
// matched or not
private boolean couldMatch(BcelObjectType bcelObjectType, Pointcut pointcut) {
return !bcelObjectType.isInterface();
}
private boolean mungeNewMemberType(BcelClassWeaver classWeaver, NewMemberClassTypeMunger munger) {
World world = classWeaver.getWorld();
ResolvedType onType = world.resolve(munger.getTargetType());
if (onType.isRawType()) {
onType = onType.getGenericType();
}
return onType.equals(classWeaver.getLazyClassGen().getType());
}
private boolean mungeNewMethod(BcelClassWeaver classWeaver, NewMethodTypeMunger munger) {
World world = classWeaver.getWorld();
// Resolving it will sort out the tvars
ResolvedMember unMangledInterMethod = munger.getSignature().resolve(world);
// do matching on the unMangled one, but actually add them to the mangled method
ResolvedMember interMethodBody = munger.getDeclaredInterMethodBody(aspectType, world);
ResolvedMember interMethodDispatcher = munger.getDeclaredInterMethodDispatcher(aspectType, world);
ResolvedMember memberHoldingAnyAnnotations = interMethodDispatcher;
LazyClassGen classGen = classWeaver.getLazyClassGen();
ResolvedType onType = world.resolve(unMangledInterMethod.getDeclaringType(), munger.getSourceLocation());
if (onType.isRawType()) {
onType = onType.getGenericType();
}
// Simple checks, can't ITD on annotations or enums
if (onType.isAnnotation()) {
signalError(WeaverMessages.ITDM_ON_ANNOTATION_NOT_ALLOWED, classWeaver, onType);
return false;
}
if (onType.isEnum()) {
signalError(WeaverMessages.ITDM_ON_ENUM_NOT_ALLOWED, classWeaver, onType);
return false;
}
boolean mungingInterface = classGen.isInterface();
boolean onInterface = onType.isInterface();
if (onInterface
&& classGen.getLazyMethodGen(unMangledInterMethod.getName(), unMangledInterMethod.getSignature(), true) != null) {
// this is ok, we could be providing the default implementation of a
// method
// that the target has already declared
return false;
}
// If we are processing the intended ITD target type (might be an interface)
if (onType.equals(classGen.getType())) {
ResolvedMember mangledInterMethod = AjcMemberMaker.interMethod(unMangledInterMethod, aspectType, onInterface);
LazyMethodGen newMethod = makeMethodGen(classGen, mangledInterMethod);
if (mungingInterface) {
// we want the modifiers of the ITD to be used for all *implementors* of the
// interface, but the method itself we add to the interface must be public abstract
newMethod.setAccessFlags(Modifier.PUBLIC | Modifier.ABSTRACT);
}
// pr98901
// For copying the annotations across, we have to discover the real
// member in the aspect which is holding them.
if (classWeaver.getWorld().isInJava5Mode()) {
AnnotationAJ annotationsOnRealMember[] = null;
ResolvedType toLookOn = aspectType;
if (aspectType.isRawType()) {
toLookOn = aspectType.getGenericType();
}
ResolvedMember realMember = getRealMemberForITDFromAspect(toLookOn, memberHoldingAnyAnnotations, false);
// 266602 - consider it missing to mean that the corresponding aspect had errors
if (realMember == null) {
// signalWarning("Unable to apply any annotations attached to " + munger.getSignature(), weaver);
// throw new BCException("Couldn't find ITD init member '" + interMethodBody + "' on aspect " + aspectType);
} else {
annotationsOnRealMember = realMember.getAnnotations();
}
Set<ResolvedType> addedAnnotations = new HashSet<>();
if (annotationsOnRealMember != null) {
for (AnnotationAJ anno : annotationsOnRealMember) {
AnnotationGen a = ((BcelAnnotation) anno).getBcelAnnotation();
AnnotationGen ag = new AnnotationGen(a, classGen.getConstantPool(), true);
newMethod.addAnnotation(new BcelAnnotation(ag, classWeaver.getWorld()));
addedAnnotations.add(anno.getType());
}
}
if (realMember != null) {
copyOverParameterAnnotations(newMethod, realMember);
}
// the code below was originally added to cope with the case where an aspect declares an annotation on an ITD
// declared within itself (an unusual situation). However, it also addresses the case where we may not find the
// annotation on the real representation of the ITD. This can happen in a load-time weaving situation where
// we couldn't add the annotation in time - and so here we recheck the declare annotations. Not quite ideal but
// works. pr288635
List<DeclareAnnotation> allDecams = world.getDeclareAnnotationOnMethods();
for (DeclareAnnotation declareAnnotationMC : allDecams) {
if (declareAnnotationMC.matches(unMangledInterMethod, world)) {
// && newMethod.getEnclosingClass().getType() == aspectType) {
AnnotationAJ annotation = declareAnnotationMC.getAnnotation();
if (!addedAnnotations.contains(annotation.getType())) {
newMethod.addAnnotation(annotation);
}
}
}
}
// If it doesn't target an interface and there is a body (i.e. it isnt abstract)
if (!onInterface && !Modifier.isAbstract(mangledInterMethod.getModifiers())) {
InstructionList body = newMethod.getBody();
InstructionFactory fact = classGen.getFactory();
int pos = 0;
if (!Modifier.isStatic(unMangledInterMethod.getModifiers())) {
body.append(InstructionFactory.createThis());
pos++;
}
Type[] paramTypes = BcelWorld.makeBcelTypes(mangledInterMethod.getParameterTypes());
for (Type paramType : paramTypes) {
body.append(InstructionFactory.createLoad(paramType, pos));
pos += paramType.getSize();
}
body.append(Utility.createInvoke(fact, classWeaver.getWorld(), interMethodBody));
body.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(mangledInterMethod.getReturnType())));
if (classWeaver.getWorld().isInJava5Mode()) { // Don't need bridge
// methods if not in
// 1.5 mode.
createAnyBridgeMethodsForCovariance(classWeaver, munger, unMangledInterMethod, onType, classGen, paramTypes);
}
} else {
// ??? this is okay
// if (!(mg.getBody() == null)) throw new
// RuntimeException("bas");
}
if (world.isInJava5Mode()) {
String basicSignature = mangledInterMethod.getSignature();
String genericSignature = ((ResolvedMemberImpl) mangledInterMethod).getSignatureForAttribute();
if (!basicSignature.equals(genericSignature)) {
// Add a signature attribute to it
newMethod.addAttribute(createSignatureAttribute(classGen.getConstantPool(), genericSignature));
}
}
// XXX make sure to check that we set exceptions properly on this
// guy.
classWeaver.addLazyMethodGen(newMethod);
classWeaver.getLazyClassGen().warnOnAddedMethod(newMethod.getMethod(), getSignature().getSourceLocation());
addNeededSuperCallMethods(classWeaver, onType, munger.getSuperMethodsCalled());
return true;
} else if (onInterface && !Modifier.isAbstract(unMangledInterMethod.getModifiers())) {
// This means the 'gen' should be the top most implementor
// - if it is *not* then something went wrong after we worked
// out that it was the top most implementor (see pr49657)
if (!classGen.getType().isTopmostImplementor(onType)) {
ResolvedType rtx = classGen.getType().getTopmostImplementor(onType);
if (rtx == null) {
// pr302460
// null means there is something wrong with what we are looking at
ResolvedType rt = classGen.getType();
if (rt.isInterface()) {
ISourceLocation sloc = munger.getSourceLocation();
classWeaver
.getWorld()
.getMessageHandler()
.handleMessage(
MessageUtil.error(
"ITD target "
+ rt.getName()
+ " is an interface but has been incorrectly determined to be the topmost implementor of "
+ onType.getName() + ". ITD is " + this.getSignature(), sloc));
}
if (!onType.isAssignableFrom(rt)) {
ISourceLocation sloc = munger.getSourceLocation();
classWeaver
.getWorld()
.getMessageHandler()
.handleMessage(
MessageUtil.error(
"ITD target " + rt.getName() + " doesn't appear to implement " + onType.getName()
+ " why did we consider it the top most implementor? ITD is "
+ this.getSignature(), sloc));
}
} else if (!rtx.isExposedToWeaver()) {
ISourceLocation sLoc = munger.getSourceLocation();
classWeaver
.getWorld()
.getMessageHandler()
.handleMessage(
MessageUtil.error(WeaverMessages.format(WeaverMessages.ITD_NON_EXPOSED_IMPLEMENTOR, rtx,
getAspectType().getName()), (sLoc == null ? getAspectType().getSourceLocation() : sLoc)));
} else {
// XXX what does this state mean?
// We have incorrectly identified what is the top most
// implementor and its not because
// a type wasn't exposed to the weaver
}
return false;
} else {
ResolvedMember mangledInterMethod = AjcMemberMaker.interMethod(unMangledInterMethod, aspectType, false);
LazyMethodGen mg = makeMethodGen(classGen, mangledInterMethod);
// From 98901#29 - need to copy annotations across
if (classWeaver.getWorld().isInJava5Mode()) {
AnnotationAJ annotationsOnRealMember[] = null;
ResolvedType toLookOn = aspectType;
if (aspectType.isRawType()) {
toLookOn = aspectType.getGenericType();
}
ResolvedMember realMember = getRealMemberForITDFromAspect(toLookOn, memberHoldingAnyAnnotations, false);
if (realMember == null) {
throw new BCException("Couldn't find ITD holder member '" + memberHoldingAnyAnnotations + "' on aspect "
+ aspectType);
}
annotationsOnRealMember = realMember.getAnnotations();
if (annotationsOnRealMember != null) {
for (AnnotationAJ annotationX : annotationsOnRealMember) {
AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation();
AnnotationGen ag = new AnnotationGen(a, classWeaver.getLazyClassGen().getConstantPool(), true);
mg.addAnnotation(new BcelAnnotation(ag, classWeaver.getWorld()));
}
}
copyOverParameterAnnotations(mg, realMember);
}
if (mungingInterface) {
// we want the modifiers of the ITD to be used for all
// *implementors* of the
// interface, but the method itself we add to the interface
// must be public abstract
mg.setAccessFlags(Modifier.PUBLIC | Modifier.ABSTRACT);
}
Type[] paramTypes = BcelWorld.makeBcelTypes(mangledInterMethod.getParameterTypes());
Type returnType = BcelWorld.makeBcelType(mangledInterMethod.getReturnType());
InstructionList body = mg.getBody();
InstructionFactory fact = classGen.getFactory();
int pos = 0;
if (!Modifier.isStatic(mangledInterMethod.getModifiers())) {
body.append(InstructionFactory.createThis());
pos++;
}
for (Type paramType : paramTypes) {
body.append(InstructionFactory.createLoad(paramType, pos));
pos += paramType.getSize();
}
body.append(Utility.createInvoke(fact, classWeaver.getWorld(), interMethodBody));
Type t = BcelWorld.makeBcelType(interMethodBody.getReturnType());
if (!t.equals(returnType)) {
body.append(fact.createCast(t, returnType));
}
body.append(InstructionFactory.createReturn(returnType));
mg.definingType = onType;
if (world.isInJava5Mode()) {
String basicSignature = mangledInterMethod.getSignature();
String genericSignature = ((ResolvedMemberImpl) mangledInterMethod).getSignatureForAttribute();
if (!basicSignature.equals(genericSignature)) {
// Add a signature attribute to it
mg.addAttribute(createSignatureAttribute(classGen.getConstantPool(), genericSignature));
}
}
classWeaver.addOrReplaceLazyMethodGen(mg);
addNeededSuperCallMethods(classWeaver, onType, munger.getSuperMethodsCalled());
// Work out if we need a bridge method for the new method added to the topmostimplementor.
// Check if the munger being processed is a parameterized form of the original munger
createBridgeIfNecessary(classWeaver, munger, unMangledInterMethod, classGen);
return true;
}
} else {
return false;
}
}
private void createBridgeIfNecessary(BcelClassWeaver classWeaver, NewMethodTypeMunger munger,
ResolvedMember unMangledInterMethod, LazyClassGen classGen) {
if (munger.getDeclaredSignature() != null) {
boolean needsbridging = false;
ResolvedMember mungerSignature = munger.getSignature();
ResolvedMember toBridgeTo = munger.getDeclaredSignature().parameterizedWith(null,
mungerSignature.getDeclaringType().resolve(getWorld()), false, munger.getTypeVariableAliases());
if (!toBridgeTo.getReturnType().getErasureSignature().equals(mungerSignature.getReturnType().getErasureSignature())) {
needsbridging = true;
}
UnresolvedType[] originalParams = toBridgeTo.getParameterTypes();
UnresolvedType[] newParams = mungerSignature.getParameterTypes();
for (int ii = 0; ii < originalParams.length; ii++) {
if (!originalParams[ii].getErasureSignature().equals(newParams[ii].getErasureSignature())) {
needsbridging = true;
}
}
if (needsbridging) {
createBridge(classWeaver, unMangledInterMethod, classGen, toBridgeTo);
}
}
}
private void copyOverParameterAnnotations(LazyMethodGen receiverMethod, ResolvedMember donorMethod) {
AnnotationAJ[][] pAnnos = donorMethod.getParameterAnnotations();
if (pAnnos != null) {
int offset = receiverMethod.isStatic() ? 0 : 1;
int param = 0;
for (int i = offset; i < pAnnos.length; i++) {
AnnotationAJ[] annosOnParam = pAnnos[i];
if (annosOnParam != null) {
for (AnnotationAJ anno : annosOnParam) {
receiverMethod.addParameterAnnotation(param, anno);
}
}
param++;
}
}
}
private void createBridge(BcelClassWeaver weaver, ResolvedMember unMangledInterMethod, LazyClassGen classGen,
ResolvedMember toBridgeTo) {
Type[] paramTypes;
Type returnType;
InstructionList body;
InstructionFactory fact;
int pos;
ResolvedMember bridgerMethod = AjcMemberMaker.bridgerToInterMethod(unMangledInterMethod, classGen.getType());
ResolvedMember bridgingSetter = AjcMemberMaker.interMethodBridger(toBridgeTo, aspectType, false); // pr250493
LazyMethodGen bridgeMethod = makeMethodGen(classGen, bridgingSetter);
paramTypes = BcelWorld.makeBcelTypes(bridgingSetter.getParameterTypes());
Type[] bridgingToParms = BcelWorld.makeBcelTypes(unMangledInterMethod.getParameterTypes());
returnType = BcelWorld.makeBcelType(bridgingSetter.getReturnType());
body = bridgeMethod.getBody();
fact = classGen.getFactory();
pos = 0;
if (!Modifier.isStatic(bridgingSetter.getModifiers())) {
body.append(InstructionFactory.createThis());
pos++;
}
for (int i = 0, len = paramTypes.length; i < len; i++) {
Type paramType = paramTypes[i];
body.append(InstructionFactory.createLoad(paramType, pos));
if (!bridgingSetter.getParameterTypes()[i].getErasureSignature().equals(
unMangledInterMethod.getParameterTypes()[i].getErasureSignature())) {
// System.err.println("Putting in cast from "+
// paramType+" to "+bridgingToParms[i]);
body.append(fact.createCast(paramType, bridgingToParms[i]));
}
pos += paramType.getSize();
}
body.append(Utility.createInvoke(fact, weaver.getWorld(), bridgerMethod));
body.append(InstructionFactory.createReturn(returnType));
classGen.addMethodGen(bridgeMethod);
// mg.definingType = onType;
}
/**
* Helper method to create a signature attribute based on a string signature: e.g. "Ljava/lang/Object;LI<Ljava/lang/Double;>;"
*/
private Signature createSignatureAttribute(ConstantPool cp, String signature) {
int nameIndex = cp.addUtf8("Signature");
int sigIndex = cp.addUtf8(signature);
return new Signature(nameIndex, 2, sigIndex, cp);
}
/**
* Create any bridge method required because of covariant returns being used. This method is used in the case where an ITD is
* applied to some type and it may be in an override relationship with a method from the supertype - but due to covariance there
* is a mismatch in return values. Example of when required: Super defines: Object m(String s) Sub defines: String m(String s)
* then we need a bridge method in Sub called 'Object m(String s)' that forwards to 'String m(String s)'
*/
private void createAnyBridgeMethodsForCovariance(BcelClassWeaver weaver, NewMethodTypeMunger munger,
ResolvedMember unMangledInterMethod, ResolvedType onType, LazyClassGen gen, Type[] paramTypes) {
// PERFORMANCE BOTTLENECK? Might need investigating, method analysis
// between types in a hierarchy just seems expensive...
// COVARIANCE BRIDGING
// Algorithm: Step1. Check in this type - has someone already created
// the bridge method?
// Step2. Look above us - do we 'override' a method and yet differ in
// return type (i.e. covariance)
// Step3. Create a forwarding bridge method
// ResolvedType superclass = onType.getSuperclass();
boolean quitRightNow = false;
String localMethodName = unMangledInterMethod.getName();
String erasedSig = unMangledInterMethod.getSignatureErased(); // will be something like (LSuperB;)LFoo;
String localParameterSig = erasedSig.substring(0,erasedSig.lastIndexOf(')')+1);//unMangledInterMethod.getParameterSignature();
// getParameterSignatureErased() does not include parens, which we do need.
String localReturnTypeESig = unMangledInterMethod.getReturnType().getErasureSignature();
// Step1
boolean alreadyDone = false; // Compiler might have done it
ResolvedMember[] localMethods = onType.getDeclaredMethods();
for (ResolvedMember member : localMethods) {
if (member.getName().equals(localMethodName)) {
// Check the params
if (member.getParameterSignature().equals(localParameterSig)) {
alreadyDone = true;
}
}
}
// Step2
if (!alreadyDone) {
// Use the iterator form of 'getMethods()' so we do as little work as necessary
ResolvedType supertype = onType.getSuperclass();
if (supertype != null) {
for (Iterator<ResolvedMember> iter = supertype.getMethods(true, true); iter.hasNext() && !quitRightNow;) {
ResolvedMember aMethod = iter.next();
if (aMethod.getName().equals(localMethodName) && aMethod.getParameterSignature().equals(localParameterSig)) {
// check the return types, if they are different we need a
// bridging method.
if (!aMethod.getReturnType().getErasureSignature().equals(localReturnTypeESig)
&& !Modifier.isPrivate(aMethod.getModifiers())) {
// Step3
createBridgeMethod(weaver.getWorld(), munger, unMangledInterMethod, gen, paramTypes, aMethod);
quitRightNow = true;
}
}
}
}
}
}
/**
* Create a bridge method for a particular munger.
*
* @param world
* @param munger
* @param unMangledInterMethod the method to bridge 'to' that we have already created in the 'subtype'
* @param clazz the class in which to put the bridge method
* @param paramTypes Parameter types for the bridge method, passed in as an optimization since the caller is likely to have
* already created them.
* @param theBridgeMethod
*/
private void createBridgeMethod(BcelWorld world, NewMethodTypeMunger munger, ResolvedMember unMangledInterMethod,
LazyClassGen clazz, Type[] paramTypes, ResolvedMember theBridgeMethod) {
InstructionList body;
InstructionFactory fact;
int pos = 0;
// The bridge method in this type will have the same signature as the one in the supertype
LazyMethodGen bridgeMethod = makeMethodGen(clazz, theBridgeMethod);
bridgeMethod.setAccessFlags(bridgeMethod.getAccessFlags() | 0x00000040 /* BRIDGE = 0x00000040 */);
// UnresolvedType[] newParams = munger.getSignature().getParameterTypes();
Type returnType = BcelWorld.makeBcelType(theBridgeMethod.getReturnType());
body = bridgeMethod.getBody();
fact = clazz.getFactory();
if (!Modifier.isStatic(unMangledInterMethod.getModifiers())) {
body.append(InstructionFactory.createThis());
pos++;
}
for (Type paramType : paramTypes) {
body.append(InstructionFactory.createLoad(paramType, pos));
// if (!bridgingSetter.getParameterTypes()[i].getErasureSignature().
// equals
// (unMangledInterMethod.getParameterTypes()[i].getErasureSignature
// ())) {
// System.err.println("Putting in cast from "+paramType+" to "+
// bridgingToParms[i]);
// body.append(fact.createCast(paramType,bridgingToParms[i]));
// }
pos += paramType.getSize();
}
body.append(Utility.createInvoke(fact, world, unMangledInterMethod));
body.append(InstructionFactory.createReturn(returnType));
clazz.addMethodGen(bridgeMethod);
}
// Unlike toString() on a member, this does not include the declaring type
private String stringifyMember(ResolvedMember member) {
StringBuilder buf = new StringBuilder();
buf.append(member.getReturnType().getName());
buf.append(' ');
buf.append(member.getName());
if (member.getKind() != Member.FIELD) {
buf.append("(");
UnresolvedType[] params = member.getParameterTypes();
if (params.length != 0) {
buf.append(params[0]);
for (int i = 1, len = params.length; i < len; i++) {
buf.append(", ");
buf.append(params[i].getName());
}
}
buf.append(")");
}
return buf.toString();
}
private boolean mungeMethodDelegate(BcelClassWeaver weaver, MethodDelegateTypeMunger munger) {
World world = weaver.getWorld();
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;
}
ResolvedMember introduced = munger.getSignature();
ResolvedType fromType = world.resolve(introduced.getDeclaringType(), munger.getSourceLocation());
if (fromType.isRawType()) {
fromType = fromType.getGenericType();
}
boolean shouldApply = munger.matches(weaver.getLazyClassGen().getType(), aspectType);
if (shouldApply) {
Type bcelReturnType = BcelWorld.makeBcelType(introduced.getReturnType());
// If no implementation class was specified, the intention was that
// the types matching the pattern
// already implemented the interface, let's check that now!
if (munger.getImplClassName() == null && !munger.specifiesDelegateFactoryMethod()) {
boolean isOK = false;
List<LazyMethodGen> existingMethods = gen.getMethodGens();
for (LazyMethodGen m : existingMethods) {
if (m.getName().equals(introduced.getName())
&& m.getParameterSignature().equals(introduced.getParameterSignature())
&& m.getReturnType().equals(bcelReturnType)) {
isOK = true;
}
}
if (!isOK) {
// the class does not implement this method, they needed to
// supply a default impl class
IMessage msg = new Message("@DeclareParents: No defaultImpl was specified but the type '" + gen.getName()
+ "' does not implement the method '" + stringifyMember(introduced) + "' defined on the interface '"
+ introduced.getDeclaringType() + "'", weaver.getLazyClassGen().getType().getSourceLocation(), true,
new ISourceLocation[] { munger.getSourceLocation() });
weaver.getWorld().getMessageHandler().handleMessage(msg);
return false;
}
return true;
}
LazyMethodGen mg = new LazyMethodGen(introduced.getModifiers() - Modifier.ABSTRACT, bcelReturnType,
introduced.getName(), BcelWorld.makeBcelTypes(introduced.getParameterTypes()),
BcelWorld.makeBcelTypesAsClassNames(introduced.getExceptions()), gen);
// annotation copy from annotation on ITD interface
if (weaver.getWorld().isInJava5Mode()) {
AnnotationAJ annotationsOnRealMember[] = null;
ResolvedType toLookOn = weaver.getWorld().lookupOrCreateName(introduced.getDeclaringType());
if (fromType.isRawType()) {
toLookOn = fromType.getGenericType();
}
// lookup the method
ResolvedMember[] ms = toLookOn.getDeclaredJavaMethods();
for (ResolvedMember m : ms) {
if (introduced.getName().equals(m.getName()) && introduced.getSignature().equals(m.getSignature())) {
annotationsOnRealMember = m.getAnnotations();
break;
}
}
if (annotationsOnRealMember != null) {
for (AnnotationAJ anno : annotationsOnRealMember) {
AnnotationGen a = ((BcelAnnotation) anno).getBcelAnnotation();
AnnotationGen ag = new AnnotationGen(a, weaver.getLazyClassGen().getConstantPool(), true);
mg.addAnnotation(new BcelAnnotation(ag, weaver.getWorld()));
}
}
}
InstructionList body = new InstructionList();
InstructionFactory fact = gen.getFactory();
// Create a delegate method body which basically looks as follows:
//
// public void methodOne() {
// if (this.ajc$instance$MyAspect$MyMixin == null) {
// synchronized(this) {
// if (this.ajc$instance$MyAspect$MyMixin == null) {
// this.ajc$instance$MyAspect$MyMixin = MyAspect.aspectOf().createImplementation(this);
// }
// }
// }
// this.ajc$instance$MyAspect$MyMixin.methodOne();
// }
// Before synchronising on 'this', perform a preliminary null check on the delegate field. Only if it is null,
// it makes sense to synchronise, then check for null again and initialise the delegate, if null.
body.append(InstructionConstants.ALOAD_0);
body.append(Utility.createGet(fact, munger.getDelegate(weaver.getLazyClassGen().getType())));
InstructionBranch outerIfNonNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null);
body.append(outerIfNonNull);
// Wrap delegate field initialisation in 'synchronized(this)' block - MONITORENTER (beginning of 'try')
body.append(InstructionConstants.ALOAD_0);
body.append(InstructionConstants.MONITORENTER);
// The JVM spec requires us add an exception handler ensuring MONITOREXIT in case of an exception inside the
// synchronized block, see https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-3.html#jvms-3.14.
InstructionHandle tryStart = body.append(InstructionConstants.ALOAD_0);
body.append(Utility.createGet(fact, munger.getDelegate(weaver.getLazyClassGen().getType())));
InstructionBranch innerIfNonNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null);
body.append(innerIfNonNull);
// Create and store a new instance
body.append(InstructionConstants.ALOAD_0); // 'this' is where we'll store the field value
// TODO for non-static case, call aspectOf() then call the factory method on the retval
// TODO decide whether the value can really be cached
// locate the aspect and call the static method in it
if (munger.specifiesDelegateFactoryMethod()) {
ResolvedMember rm = munger.getDelegateFactoryMethod(weaver.getWorld());
// Check the method parameter is compatible with the type of the instance to be passed
if (rm.getArity() != 0) {
ResolvedType parameterType = rm.getParameterTypes()[0].resolve(weaver.getWorld());
if (!parameterType.isAssignableFrom(weaver.getLazyClassGen().getType())) {
signalError("For mixin factory method '" + rm + "': Instance type '" + weaver.getLazyClassGen().getType()
+ "' is not compatible with factory parameter type '" + parameterType + "'", weaver);
return false;
}
}
if (Modifier.isStatic(rm.getModifiers())) {
if (rm.getArity() != 0) {
body.append(InstructionConstants.ALOAD_0);
}
body.append(fact.createInvoke(rm.getDeclaringType().getName(), rm.getName(), rm.getSignature(),
Constants.INVOKESTATIC));
body.append(Utility.createSet(fact, munger.getDelegate(weaver.getLazyClassGen().getType())));
} else {
// Need to call aspectOf() to obtain the aspect instance then call the factory method upon that
UnresolvedType theAspect = munger.getAspect();
body.append(fact.createInvoke(theAspect.getName(), "aspectOf", "()" + theAspect.getSignature(),
Constants.INVOKESTATIC));
if (rm.getArity() != 0) {
body.append(InstructionConstants.ALOAD_0);
}
body.append(fact.createInvoke(rm.getDeclaringType().getName(), rm.getName(), rm.getSignature(),
Constants.INVOKEVIRTUAL));
body.append(Utility.createSet(fact, munger.getDelegate(weaver.getLazyClassGen().getType())));
}
} else {
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())));
}
// Wrap delegate field initialisation in 'synchronized(this)' block - MONITOREXIT (end of 'try')
InstructionHandle ifNonNullElse = body.append(InstructionConstants.ALOAD_0);
innerIfNonNull.setTarget(ifNonNullElse);
body.append(InstructionConstants.MONITOREXIT);
// There was no error in the 'synchronized(this)' block -> jump to first instruction after exception handler
InstructionBranch gotoAfterTryCatch = new InstructionBranch(Constants.GOTO, null);
InstructionHandle tryEnd = body.append(gotoAfterTryCatch);
// Exception handler (logical 'catch') for the 'synchronized(this)' block ensures that MONITOREXIT is also called
// in case of an error
InstructionHandle catchStart = body.append(InstructionConstants.ALOAD_0);
body.append(InstructionConstants.MONITOREXIT);
InstructionHandle catchEnd = body.append(InstructionConstants.ATHROW);
// Add exception handler for 'synchronized(this)' block
mg.addExceptionHandler(tryStart, tryEnd.getPrev(), catchStart, null, false);
// CAVEAT: Add an extra, self-referential exception handler entry. I.e., the handler handles its own exceptions.
// According to https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-3.html#jvms-3.14, this is required, and
// Javac also creates code like this, which is why we are mimicking it.
mg.addExceptionHandler(catchStart, catchEnd.getPrev(), catchStart, null, false);
// if not null use the instance we've got
InstructionHandle afterTryCatch = body.append(InstructionConstants.ALOAD_0);
// Tell 'gotoAfterTryCatch' where to find the first statement after the exception handler
gotoAfterTryCatch.setTarget(afterTryCatch);
// The outer delegate field null check should also jump to the same place, if the delegate field is not null
outerIfNonNull.setTarget(afterTryCatch);
body.append(Utility.createGet(fact, munger.getDelegate(weaver.getLazyClassGen().getType())));
// args
int pos = 0;
if (!Modifier.isStatic(introduced.getModifiers())) { // skip 'this' (?? can this really
// happen)
// body.append(InstructionFactory.createThis());
pos++;
}
Type[] paramTypes = BcelWorld.makeBcelTypes(introduced.getParameterTypes());
for (Type paramType : paramTypes) {
body.append(InstructionFactory.createLoad(paramType, pos));
pos += paramType.getSize();
}
body.append(Utility.createInvoke(fact, Constants.INVOKEINTERFACE, introduced));
body.append(InstructionFactory.createReturn(bcelReturnType));
mg.getBody().append(body);
weaver.addLazyMethodGen(mg);
weaver.getLazyClassGen().warnOnAddedMethod(mg.getMethod(), getSignature().getSourceLocation());
return true;
}
return false;
}
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); // why
// do
// this?
ResolvedMember host = AjcMemberMaker.itdAtDeclareParentsField(weaver.getLazyClassGen().getType(), munger.getSignature()
.getType(), aspectType);
FieldGen field = makeFieldGen(weaver.getLazyClassGen(), host);
field.setModifiers(field.getModifiers() | BcelField.AccSynthetic);
weaver.getLazyClassGen().addField(field, null);
return true;
}
private ResolvedMember getRealMemberForITDFromAspect(ResolvedType aspectType, ResolvedMember lookingFor, boolean isCtorRelated) {
World world = aspectType.getWorld();
boolean debug = false;
if (debug) {
System.err.println("Searching for a member on type: " + aspectType);
System.err.println("Member we are looking for: " + lookingFor);
}
ResolvedMember[] aspectMethods = aspectType.getDeclaredMethods();
UnresolvedType[] lookingForParams = lookingFor.getParameterTypes();
ResolvedMember realMember = null;
for (int i = 0; realMember == null && i < aspectMethods.length; i++) {
ResolvedMember member = aspectMethods[i];
if (member.getName().equals(lookingFor.getName())) {
UnresolvedType[] memberParams = member.getGenericParameterTypes();
if (memberParams.length == lookingForParams.length) {
if (debug)
System.err.println("Reviewing potential candidates: " + member);
boolean matchOK = true;
// Check that all method/ctor params all match, although only the erasure
for (int j = 0; j < memberParams.length && matchOK; j++) {
ResolvedType pMember = memberParams[j].resolve(world);
ResolvedType pLookingFor = lookingForParams[j].resolve(world);
if (pMember.isTypeVariableReference())
pMember = ((TypeVariableReference) pMember).getTypeVariable().getFirstBound().resolve(world);
if (pMember.isParameterizedType() || pMember.isGenericType())
pMember = pMember.getRawType().resolve(aspectType.getWorld());
if (pLookingFor.isTypeVariableReference())
pLookingFor = ((TypeVariableReference) pLookingFor).getTypeVariable().getFirstBound().resolve(world);
if (pLookingFor.isParameterizedType() || pLookingFor.isGenericType())
pLookingFor = pLookingFor.getRawType().resolve(world);
if (debug)
System.err.println("Comparing parameter " + j + " member=" + pMember + " lookingFor=" + pLookingFor);
if (!pMember.equals(pLookingFor))
matchOK = false;
}
if (matchOK)
realMember = member;
}
}
}
if (debug && realMember == null)
System.err.println("Didn't find a match");
return realMember;
}
private void addNeededSuperCallMethods(BcelClassWeaver weaver, ResolvedType onType, Set<ResolvedMember> neededSuperCalls) {
LazyClassGen gen = weaver.getLazyClassGen();
for (ResolvedMember superMethod: neededSuperCalls) {
if (weaver.addDispatchTarget(superMethod)) {
// System.err.println("super type: " + superMethod.getDeclaringType() + ", " + gen.getType());
boolean isSuper = !superMethod.getDeclaringType().equals(gen.getType());
String dispatchName;
if (isSuper) {
dispatchName = NameMangler.superDispatchMethod(onType, superMethod.getName());
} else {
dispatchName = NameMangler.protectedDispatchMethod(onType, superMethod.getName());
}
superMethod = superMethod.resolve(weaver.getWorld());
LazyMethodGen dispatcher = makeDispatcher(gen, dispatchName, superMethod, weaver.getWorld(), isSuper);
weaver.addLazyMethodGen(dispatcher);
}
}
}
private void signalError(String msgid, BcelClassWeaver weaver, UnresolvedType onType) {
IMessage msg = MessageUtil.error(WeaverMessages.format(msgid, onType.getName()), getSourceLocation());
weaver.getWorld().getMessageHandler().handleMessage(msg);
}
// private void signalWarning(String msgString, BcelClassWeaver weaver) {
// IMessage msg = MessageUtil.warn(msgString, getSourceLocation());
// weaver.getWorld().getMessageHandler().handleMessage(msg);
// }
private void signalError(String msgString, BcelClassWeaver weaver) {
IMessage msg = MessageUtil.error(msgString, getSourceLocation());
weaver.getWorld().getMessageHandler().handleMessage(msg);
}
private boolean mungeNewConstructor(BcelClassWeaver weaver, NewConstructorTypeMunger newConstructorTypeMunger) {
final LazyClassGen currentClass = weaver.getLazyClassGen();
final InstructionFactory fact = currentClass.getFactory();
ResolvedMember newConstructorMember = newConstructorTypeMunger.getSyntheticConstructor();
ResolvedType onType = newConstructorMember.getDeclaringType().resolve(weaver.getWorld());
if (onType.isRawType()) {
onType = onType.getGenericType();
}
if (onType.isAnnotation()) {
signalError(WeaverMessages.ITDC_ON_ANNOTATION_NOT_ALLOWED, weaver, onType);
return false;
}
if (onType.isEnum()) {
signalError(WeaverMessages.ITDC_ON_ENUM_NOT_ALLOWED, weaver, onType);
return false;
}
if (!onType.equals(currentClass.getType())) {
return false;
}
ResolvedMember explicitConstructor = newConstructorTypeMunger.getExplicitConstructor();
// int declaredParameterCount =
// newConstructorTypeMunger.getDeclaredParameterCount();
LazyMethodGen mg = makeMethodGen(currentClass, newConstructorMember);
mg.setEffectiveSignature(newConstructorTypeMunger.getSignature(), Shadow.ConstructorExecution, true);
// pr98901
// For copying the annotations across, we have to discover the real
// member in the aspect
// which is holding them.
if (weaver.getWorld().isInJava5Mode()) {
ResolvedMember interMethodDispatcher = AjcMemberMaker.postIntroducedConstructor(aspectType, onType,
newConstructorTypeMunger.getSignature().getParameterTypes());
AnnotationAJ annotationsOnRealMember[] = null;
ResolvedMember realMember = getRealMemberForITDFromAspect(aspectType, interMethodDispatcher, true);
// 266602 - consider it missing to mean that the corresponding aspect had errors
if (realMember == null) {
// signalWarning("Unable to apply any annotations attached to " + munger.getSignature(), weaver);
// throw new BCException("Couldn't find ITD init member '" + interMethodBody + "' on aspect " + aspectType);
} else {
annotationsOnRealMember = realMember.getAnnotations();
}
if (annotationsOnRealMember != null) {
for (AnnotationAJ annotationX : annotationsOnRealMember) {
AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation();
AnnotationGen ag = new AnnotationGen(a, weaver.getLazyClassGen().getConstantPool(), true);
mg.addAnnotation(new BcelAnnotation(ag, weaver.getWorld()));
}
}
// the below loop fixes the very special (and very stupid)
// case where an aspect declares an annotation
// on an ITD it declared on itself.
List<DeclareAnnotation> allDecams = weaver.getWorld().getDeclareAnnotationOnMethods();
for (DeclareAnnotation decaMC : allDecams) {
if (decaMC.matches(explicitConstructor, weaver.getWorld()) && mg.getEnclosingClass().getType() == aspectType) {
mg.addAnnotation(decaMC.getAnnotation());
}
}
}
// Might have to remove the default constructor - b275032
// TODO could have tagged the type munger when the fact we needed to do this was detected earlier
if (mg.getArgumentTypes().length == 0) {
LazyMethodGen toRemove = null;
for (LazyMethodGen object : currentClass.getMethodGens()) {
if (object.getName().equals("<init>") && object.getArgumentTypes().length == 0) {
toRemove = object;
}
}
if (toRemove != null) {
currentClass.removeMethodGen(toRemove);
}
}
currentClass.addMethodGen(mg);
// weaver.addLazyMethodGen(freshConstructor);
InstructionList body = mg.getBody();
// add to body: push arts for call to pre, from actual args starting at
// 1 (skipping this), going to
// declared argcount + 1
UnresolvedType[] declaredParams = newConstructorTypeMunger.getSignature().getParameterTypes();
Type[] paramTypes = mg.getArgumentTypes();
int frameIndex = 1;
for (int i = 0, len = declaredParams.length; i < len; i++) {
body.append(InstructionFactory.createLoad(paramTypes[i], frameIndex));
frameIndex += paramTypes[i].getSize();
}
// do call to pre
Member preMethod = AjcMemberMaker.preIntroducedConstructor(aspectType, onType, declaredParams);
body.append(Utility.createInvoke(fact, null, preMethod));
// create a local, and store return pre stuff into it.
int arraySlot = mg.allocateLocal(1);
body.append(InstructionFactory.createStore(Type.OBJECT, arraySlot));
// put this on the stack
body.append(InstructionConstants.ALOAD_0);
// unpack pre args onto stack
UnresolvedType[] superParamTypes = explicitConstructor.getParameterTypes();
for (int i = 0, len = superParamTypes.length; i < len; i++) {
body.append(InstructionFactory.createLoad(Type.OBJECT, arraySlot));
body.append(Utility.createConstant(fact, i));
body.append(InstructionFactory.createArrayLoad(Type.OBJECT));
body.append(Utility.createConversion(fact, Type.OBJECT, BcelWorld.makeBcelType(superParamTypes[i])));
}
// call super/this
body.append(Utility.createInvoke(fact, null, explicitConstructor));
// put this back on the stack
body.append(InstructionConstants.ALOAD_0);
// unpack params onto stack
Member postMethod = AjcMemberMaker.postIntroducedConstructor(aspectType, onType, declaredParams);
UnresolvedType[] postParamTypes = postMethod.getParameterTypes();
for (int i = 1, len = postParamTypes.length; i < len; i++) {
body.append(InstructionFactory.createLoad(Type.OBJECT, arraySlot));
body.append(Utility.createConstant(fact, superParamTypes.length + i - 1));
body.append(InstructionFactory.createArrayLoad(Type.OBJECT));
body.append(Utility.createConversion(fact, Type.OBJECT, BcelWorld.makeBcelType(postParamTypes[i])));
}
// call post
body.append(Utility.createInvoke(fact, null, postMethod));
// don't forget to return!!
body.append(InstructionConstants.RETURN);
addNeededSuperCallMethods(weaver, onType, munger.getSuperMethodsCalled());
return true;
}
private static LazyMethodGen makeDispatcher(LazyClassGen onGen, String dispatchName, ResolvedMember superMethod,
BcelWorld world, boolean isSuper) {
Type[] paramTypes = BcelWorld.makeBcelTypes(superMethod.getParameterTypes());
Type returnType = BcelWorld.makeBcelType(superMethod.getReturnType());
int modifiers = Modifier.PUBLIC;
if (onGen.isInterface()) {
modifiers |= Modifier.ABSTRACT;
}
LazyMethodGen mg = new LazyMethodGen(modifiers, returnType, dispatchName, paramTypes, UnresolvedType.getNames(superMethod
.getExceptions()), onGen);
InstructionList body = mg.getBody();
if (onGen.isInterface()) {
return mg;
}
// assert (!superMethod.isStatic())
InstructionFactory fact = onGen.getFactory();
int pos = 0;
body.append(InstructionFactory.createThis());
pos++;
for (Type paramType : paramTypes) {
body.append(InstructionFactory.createLoad(paramType, pos));
pos += paramType.getSize();
}
if (isSuper) {
body.append(Utility.createSuperInvoke(fact, world, superMethod));
} else {
body.append(Utility.createInvoke(fact, world, superMethod));
}
body.append(InstructionFactory.createReturn(returnType));
return mg;
}
private boolean mungeNewField(BcelClassWeaver weaver, NewFieldTypeMunger munger) {
/* ResolvedMember initMethod = */munger.getInitMethod(aspectType);
LazyClassGen gen = weaver.getLazyClassGen();
ResolvedMember field = munger.getSignature();
ResolvedType onType = weaver.getWorld().resolve(field.getDeclaringType(), munger.getSourceLocation());
if (onType.isRawType()) {
onType = onType.getGenericType();
}
boolean onInterface = onType.isInterface();
if (onType.isAnnotation()) {
signalError(WeaverMessages.ITDF_ON_ANNOTATION_NOT_ALLOWED, weaver, onType);
return false;
}
if (onType.isEnum()) {
signalError(WeaverMessages.ITDF_ON_ENUM_NOT_ALLOWED, weaver, onType);
return false;
}
ResolvedMember interMethodBody = munger.getInitMethod(aspectType);
AnnotationAJ annotationsOnRealMember[] = null;
// pr98901
// For copying the annotations across, we have to discover the real
// member in the aspect
// which is holding them.
if (weaver.getWorld().isInJava5Mode()) {
// the below line just gets the method with the same name in
// aspectType.getDeclaredMethods();
ResolvedType toLookOn = aspectType;
if (aspectType.isRawType()) {
toLookOn = aspectType.getGenericType();
}
ResolvedMember realMember = getRealMemberForITDFromAspect(toLookOn, interMethodBody, false);
if (realMember == null) {
// signalWarning("Unable to apply any annotations attached to " + munger.getSignature(), weaver);
// throw new BCException("Couldn't find ITD init member '" + interMethodBody + "' on aspect " + aspectType);
} else {
annotationsOnRealMember = realMember.getAnnotations();
}
}
if (onType.equals(gen.getType())) {
if (onInterface) {
ResolvedMember itdfieldGetter = AjcMemberMaker.interFieldInterfaceGetter(field, onType, aspectType);
LazyMethodGen mg = makeMethodGen(gen, itdfieldGetter);
gen.addMethodGen(mg);
LazyMethodGen mg1 = makeMethodGen(gen, AjcMemberMaker.interFieldInterfaceSetter(field, onType, aspectType));
gen.addMethodGen(mg1);
} else {
weaver.addInitializer(this);
ResolvedMember newField = AjcMemberMaker.interFieldClassField(field, aspectType,
munger.version == NewFieldTypeMunger.VersionTwo);
FieldGen fg = makeFieldGen(gen, newField);
if (annotationsOnRealMember != null) {
for (AnnotationAJ annotationX : annotationsOnRealMember) {
AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation();
AnnotationGen ag = new AnnotationGen(a, weaver.getLazyClassGen().getConstantPool(), true);
fg.addAnnotation(ag);
}
}
if (weaver.getWorld().isInJava5Mode()) {
String basicSignature = field.getSignature();
String genericSignature = field.getReturnType().resolve(weaver.getWorld()).getSignatureForAttribute();
// String genericSignature =
// ((ResolvedMemberImpl)field).getSignatureForAttribute();
if (!basicSignature.equals(genericSignature)) {
// Add a signature attribute to it
fg.addAttribute(createSignatureAttribute(gen.getConstantPool(), genericSignature));
}
}
gen.addField(fg, getSourceLocation());
}
return true;
} else if (onInterface && gen.getType().isTopmostImplementor(onType)) {
// we know that we can't be static since we don't allow statics on interfaces
if (Modifier.isStatic(field.getModifiers())) {
throw new RuntimeException("unimplemented");
}
boolean alreadyExists = false;
// only need to check for version 2 style mungers
if (munger.version==NewFieldTypeMunger.VersionTwo) {
for (BcelField fieldgen: gen.getFieldGens()) {
if (fieldgen.getName().equals(field.getName())) {
alreadyExists=true;
break;
}
}
}
// FieldGen fg = makeFieldGen(gen, AjcMemberMaker.interFieldInterfaceField(field, onType, aspectType));
ResolvedMember newField = AjcMemberMaker.interFieldInterfaceField(field, onType, aspectType, munger.version == NewFieldTypeMunger.VersionTwo);
String fieldName = newField.getName();
Type fieldType = BcelWorld.makeBcelType(field.getType());
if (!alreadyExists) {
weaver.addInitializer(this);
FieldGen fg = makeFieldGen(gen,newField);
if (annotationsOnRealMember != null) {
for (AnnotationAJ annotationX : annotationsOnRealMember) {
AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation();
AnnotationGen ag = new AnnotationGen(a, weaver.getLazyClassGen().getConstantPool(), true);
fg.addAnnotation(ag);
}
}
if (weaver.getWorld().isInJava5Mode()) {
String basicSignature = field.getSignature();
String genericSignature = field.getReturnType().resolve(weaver.getWorld()).getSignatureForAttribute();
// String genericSignature =
// ((ResolvedMemberImpl)field).getSignatureForAttribute();
if (!basicSignature.equals(genericSignature)) {
// Add a signature attribute to it
fg.addAttribute(createSignatureAttribute(gen.getConstantPool(), genericSignature));
}
}
gen.addField(fg, getSourceLocation());
}
// this uses a shadow munger to add init method to constructors
// weaver.getShadowMungers().add(makeInitCallShadowMunger(initMethod)
// );
ResolvedMember itdfieldGetter = AjcMemberMaker.interFieldInterfaceGetter(field, gen.getType()/* onType */, aspectType);
LazyMethodGen mg = makeMethodGen(gen, itdfieldGetter);
InstructionList il = new InstructionList();
InstructionFactory fact = gen.getFactory();
if (Modifier.isStatic(field.getModifiers())) {
il.append(fact.createFieldAccess(gen.getClassName(), fieldName, fieldType, Constants.GETSTATIC));
} else {
il.append(InstructionConstants.ALOAD_0);
il.append(fact.createFieldAccess(gen.getClassName(), fieldName, fieldType, Constants.GETFIELD));
}
il.append(InstructionFactory.createReturn(fieldType));
mg.getBody().insert(il);
gen.addMethodGen(mg);
// Check if we need bridge methods for the field getter and setter
if (munger.getDeclaredSignature() != null) { // is this munger a
// parameterized
// form of some
// original munger?
ResolvedMember toBridgeTo = munger.getDeclaredSignature().parameterizedWith(null,
munger.getSignature().getDeclaringType().resolve(getWorld()), false, munger.getTypeVariableAliases());
boolean needsbridging = false;
if (!toBridgeTo.getReturnType().getErasureSignature()
.equals(munger.getSignature().getReturnType().getErasureSignature())) {
needsbridging = true;
}
if (needsbridging) {
ResolvedMember bridgingGetter = AjcMemberMaker.interFieldInterfaceGetter(toBridgeTo, gen.getType(), aspectType);
createBridgeMethodForITDF(weaver, gen, itdfieldGetter, bridgingGetter);
}
}
ResolvedMember itdfieldSetter = AjcMemberMaker.interFieldInterfaceSetter(field, gen.getType(), aspectType);
LazyMethodGen mg1 = makeMethodGen(gen, itdfieldSetter);
InstructionList il1 = new InstructionList();
if (Modifier.isStatic(field.getModifiers())) {
il1.append(InstructionFactory.createLoad(fieldType, 0));
il1.append(fact.createFieldAccess(gen.getClassName(), fieldName, fieldType, Constants.PUTSTATIC));
} else {
il1.append(InstructionConstants.ALOAD_0);
il1.append(InstructionFactory.createLoad(fieldType, 1));
il1.append(fact.createFieldAccess(gen.getClassName(), fieldName, fieldType, Constants.PUTFIELD));
}
il1.append(InstructionFactory.createReturn(Type.VOID));
mg1.getBody().insert(il1);
gen.addMethodGen(mg1);
if (munger.getDeclaredSignature() != null) {
ResolvedMember toBridgeTo = munger.getDeclaredSignature().parameterizedWith(null,
munger.getSignature().getDeclaringType().resolve(getWorld()), false, munger.getTypeVariableAliases());
boolean needsbridging = false;
if (!toBridgeTo.getReturnType().getErasureSignature()
.equals(munger.getSignature().getReturnType().getErasureSignature())) {
needsbridging = true;
}
if (needsbridging) {
ResolvedMember bridgingSetter = AjcMemberMaker.interFieldInterfaceSetter(toBridgeTo, gen.getType(), aspectType);
createBridgeMethodForITDF(weaver, gen, itdfieldSetter, bridgingSetter);
}
}
return true;
} else {
return false;
}
}
// FIXME asc combine with other createBridge.. method in this class, avoid
// the duplication...
private void createBridgeMethodForITDF(BcelClassWeaver weaver, LazyClassGen gen, ResolvedMember itdfieldSetter,
ResolvedMember bridgingSetter) {
InstructionFactory fact;
LazyMethodGen bridgeMethod = makeMethodGen(gen, bridgingSetter);
bridgeMethod.setAccessFlags(bridgeMethod.getAccessFlags() | 0x00000040); // BRIDGE = 0x00000040
Type[] paramTypes = BcelWorld.makeBcelTypes(bridgingSetter.getParameterTypes());
Type[] bridgingToParms = BcelWorld.makeBcelTypes(itdfieldSetter.getParameterTypes());
Type returnType = BcelWorld.makeBcelType(bridgingSetter.getReturnType());
InstructionList body = bridgeMethod.getBody();
fact = gen.getFactory();
int pos = 0;
if (!Modifier.isStatic(bridgingSetter.getModifiers())) {
body.append(InstructionFactory.createThis());
pos++;
}
for (int i = 0, len = paramTypes.length; i < len; i++) {
Type paramType = paramTypes[i];
body.append(InstructionFactory.createLoad(paramType, pos));
if (!bridgingSetter.getParameterTypes()[i].getErasureSignature().equals(
itdfieldSetter.getParameterTypes()[i].getErasureSignature())) {
body.append(fact.createCast(paramType, bridgingToParms[i]));
}
pos += paramType.getSize();
}
body.append(Utility.createInvoke(fact, weaver.getWorld(), itdfieldSetter));
body.append(InstructionFactory.createReturn(returnType));
gen.addMethodGen(bridgeMethod);
}
@Override
public ConcreteTypeMunger parameterizedFor(ResolvedType target) {
return new BcelTypeMunger(munger.parameterizedFor(target), aspectType);
}
@Override
public ConcreteTypeMunger parameterizeWith(Map<String, UnresolvedType> m, World w) {
return new BcelTypeMunger(munger.parameterizeWith(m, w), aspectType);
}
/**
* Returns a list of type variable aliases used in this munger. For example, if the ITD is 'int I<A,B>.m(List<A> las,List<B>
* lbs) {}' then this returns a list containing the strings "A" and "B".
*/
public List<String> getTypeVariableAliases() {
return munger.getTypeVariableAliases();
}
@Override
public boolean equals(Object other) {
if (!(other instanceof BcelTypeMunger)) {
return false;
}
BcelTypeMunger o = (BcelTypeMunger) other;
return ((o.getMunger() == null) ? (getMunger() == null) : o.getMunger().equals(getMunger()))
&& ((o.getAspectType() == null) ? (getAspectType() == null) : o.getAspectType().equals(getAspectType()));
// && (AsmManager.getDefault().getHandleProvider().dependsOnLocation() ? ((o.getSourceLocation() == null) ?
// (getSourceLocation() == null)
// : o.getSourceLocation().equals(getSourceLocation()))
// : true); // pr134471 - remove when handles are improved
// to be independent of location
}
private volatile int hashCode = 0;
@Override
public int hashCode() {
if (hashCode == 0) {
int result = 17;
result = 37 * result + ((getMunger() == null) ? 0 : getMunger().hashCode());
result = 37 * result + ((getAspectType() == null) ? 0 : getAspectType().hashCode());
hashCode = result;
}
return hashCode;
}
}