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.List;
import java.util.Map;
import java.util.Set;
import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.ConstantPool;
import org.aspectj.apache.bcel.generic.FieldInstruction;
import org.aspectj.apache.bcel.generic.Instruction;
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.InvokeDynamic;
import org.aspectj.apache.bcel.generic.InvokeInstruction;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.AjcMemberMaker;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.NameMangler;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.UnresolvedType;
public class BcelAccessForInlineMunger extends BcelTypeMunger {
private Map<String, ResolvedMember> inlineAccessors;
private LazyClassGen aspectGen;
private Set<LazyMethodGen> inlineAccessorMethodGens;
public BcelAccessForInlineMunger(ResolvedType aspectType) {
super(null, aspectType);
if (aspectType.getWorld().isXnoInline()) {
throw new Error("This should not happen");
}
}
@Override
public boolean munge(BcelClassWeaver weaver) {
aspectGen = weaver.getLazyClassGen();
inlineAccessors = new HashMap<String, ResolvedMember>(0);
inlineAccessorMethodGens = new HashSet<LazyMethodGen>();
for (LazyMethodGen methodGen : aspectGen.getMethodGens()) {
if (methodGen.hasAnnotation(UnresolvedType.forName("org/aspectj/lang/annotation/Around"))) {
openAroundAdvice(methodGen);
}
}
for (LazyMethodGen lazyMethodGen : inlineAccessorMethodGens) {
aspectGen.addMethodGen(lazyMethodGen);
}
inlineAccessorMethodGens = null;
return true;
}
@Override
public ResolvedMember getMatchingSyntheticMember(Member member) {
ResolvedMember rm = inlineAccessors.get(member.getName());
return rm;
}
@Override
public ResolvedMember getSignature() {
return null;
}
@Override
public boolean matches(ResolvedType onType) {
return aspectType.equals(onType);
}
private void openAroundAdvice(LazyMethodGen aroundAdvice) {
InstructionHandle curr = aroundAdvice.getBody().getStart();
InstructionHandle end = aroundAdvice.getBody().getEnd();
ConstantPool cpg = aroundAdvice.getEnclosingClass().getConstantPool();
InstructionFactory factory = aroundAdvice.getEnclosingClass().getFactory();
boolean realizedCannotInline = false;
while (curr != end) {
if (realizedCannotInline) {
break;
}
InstructionHandle next = curr.getNext();
Instruction inst = curr.getInstruction();
if ((inst instanceof InvokeInstruction)) {
InvokeInstruction invoke = (InvokeInstruction) inst;
if (invoke instanceof InvokeDynamic) {
realizedCannotInline = true;
break;
}
ResolvedType callee = aspectGen.getWorld().resolve(UnresolvedType.forName(invoke.getClassName(cpg)));
List<ResolvedMember> methods = callee.getMethodsWithoutIterator(false, true, false);
for (ResolvedMember resolvedMember : methods) {
if (invoke.getName(cpg).equals(resolvedMember.getName())
&& invoke.getSignature(cpg).equals(resolvedMember.getSignature()) && !resolvedMember.isPublic()) {
if ("<init>".equals(invoke.getName(cpg))
) {
if (invoke.getClassName(cpg).equals(resolvedMember.getDeclaringType().getPackageName()+
"."+resolvedMember.getDeclaringType().getClassName())) {
aroundAdvice.setCanInline(false);
realizedCannotInline = true;
}
} else {
ResolvedType memberType = aspectGen.getWorld().resolve(resolvedMember.getDeclaringType());
if (!aspectType.equals(memberType) && memberType.isAssignableFrom(aspectType)) {
ResolvedMember accessor = createOrGetInlineAccessorForSuperDispatch(resolvedMember);
InvokeInstruction newInst = factory.createInvoke(aspectType.getName(), accessor.getName(),
BcelWorld.makeBcelType(accessor.getReturnType()),
BcelWorld.makeBcelTypes(accessor.getParameterTypes()), Constants.INVOKEVIRTUAL);
curr.setInstruction(newInst);
} else {
ResolvedMember accessor = createOrGetInlineAccessorForMethod(resolvedMember);
InvokeInstruction newInst = factory.createInvoke(aspectType.getName(), accessor.getName(),
BcelWorld.makeBcelType(accessor.getReturnType()),
BcelWorld.makeBcelTypes(accessor.getParameterTypes()), Constants.INVOKESTATIC);
curr.setInstruction(newInst);
}
}
break;
}
}
} else if (inst instanceof FieldInstruction) {
FieldInstruction invoke = (FieldInstruction) inst;
ResolvedType callee = aspectGen.getWorld().resolve(UnresolvedType.forName(invoke.getClassName(cpg)));
for (int i = 0; i < callee.getDeclaredJavaFields().length; i++) {
ResolvedMember resolvedMember = callee.getDeclaredJavaFields()[i];
if (invoke.getName(cpg).equals(resolvedMember.getName())
&& invoke.getSignature(cpg).equals(resolvedMember.getSignature()) && !resolvedMember.isPublic()) {
final ResolvedMember accessor;
if ((inst.opcode == Constants.GETFIELD) || (inst.opcode == Constants.GETSTATIC)) {
accessor = createOrGetInlineAccessorForFieldGet(resolvedMember);
} else {
accessor = createOrGetInlineAccessorForFieldSet(resolvedMember);
}
InvokeInstruction newInst = factory.createInvoke(aspectType.getName(), accessor.getName(),
BcelWorld.makeBcelType(accessor.getReturnType()),
BcelWorld.makeBcelTypes(accessor.getParameterTypes()), Constants.INVOKESTATIC);
curr.setInstruction(newInst);
break;
}
}
}
curr = next;
}
if (!realizedCannotInline) {
aroundAdvice.setCanInline(true);
}
}
private ResolvedMember createOrGetInlineAccessorForMethod(ResolvedMember resolvedMember) {
String accessorName = NameMangler.inlineAccessMethodForMethod(resolvedMember.getName(), resolvedMember.getDeclaringType(),
aspectType);
String key = accessorName;
ResolvedMember inlineAccessor = inlineAccessors.get(key);
if (inlineAccessor == null) {
inlineAccessor = AjcMemberMaker.inlineAccessMethodForMethod(aspectType, resolvedMember);
InstructionFactory factory = aspectGen.getFactory();
LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor);
method.makeSynthetic();
List<AjAttribute> methodAttributes = new ArrayList<AjAttribute>();
methodAttributes.add(new AjAttribute.AjSynthetic());
methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.MethodCall, false));
method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool()));
method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool()));
inlineAccessorMethodGens.add(method);
InstructionList il = method.getBody();
int register = 0;
for (int i = 0, max = inlineAccessor.getParameterTypes().length; i < max; i++) {
UnresolvedType ptype = inlineAccessor.getParameterTypes()[i];
Type type = BcelWorld.makeBcelType(ptype);
il.append(InstructionFactory.createLoad(type, register));
register += type.getSize();
}
il.append(Utility.createInvoke(factory, Modifier.isStatic(resolvedMember.getModifiers()) ? Constants.INVOKESTATIC
: Constants.INVOKEVIRTUAL, resolvedMember));
il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(inlineAccessor.getReturnType())));
inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes));
}
return inlineAccessor;
}
private ResolvedMember createOrGetInlineAccessorForSuperDispatch(ResolvedMember resolvedMember) {
String accessor = NameMangler.superDispatchMethod(aspectType, resolvedMember.getName());
String key = accessor;
ResolvedMember inlineAccessor = inlineAccessors.get(key);
if (inlineAccessor == null) {
inlineAccessor = AjcMemberMaker.superAccessMethod(aspectType, resolvedMember);
InstructionFactory factory = aspectGen.getFactory();
LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor);
method.makeSynthetic();
List<AjAttribute> methodAttributes = new ArrayList<AjAttribute>();
methodAttributes.add(new AjAttribute.AjSynthetic());
methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.MethodCall, false));
method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool()));
method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool()));
inlineAccessorMethodGens.add(method);
InstructionList il = method.getBody();
il.append(InstructionConstants.ALOAD_0);
int register = 1;
for (int i = 0; i < inlineAccessor.getParameterTypes().length; i++) {
UnresolvedType typeX = inlineAccessor.getParameterTypes()[i];
Type type = BcelWorld.makeBcelType(typeX);
il.append(InstructionFactory.createLoad(type, register));
register += type.getSize();
}
il.append(Utility.createInvoke(factory, Constants.INVOKESPECIAL, resolvedMember));
il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(inlineAccessor.getReturnType())));
inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes));
}
return inlineAccessor;
}
private ResolvedMember createOrGetInlineAccessorForFieldGet(ResolvedMember resolvedMember) {
String accessor = NameMangler.inlineAccessMethodForFieldGet(resolvedMember.getName(), resolvedMember.getDeclaringType(),
aspectType);
String key = accessor;
ResolvedMember inlineAccessor = inlineAccessors.get(key);
if (inlineAccessor == null) {
inlineAccessor = AjcMemberMaker.inlineAccessMethodForFieldGet(aspectType, resolvedMember);
InstructionFactory factory = aspectGen.getFactory();
LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor);
method.makeSynthetic();
List<AjAttribute> methodAttributes = new ArrayList<AjAttribute>();
methodAttributes.add(new AjAttribute.AjSynthetic());
methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.FieldGet, false));
method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool()));
method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool()));
inlineAccessorMethodGens.add(method);
InstructionList il = method.getBody();
if (Modifier.isStatic(resolvedMember.getModifiers())) {
} else {
il.append(InstructionConstants.ALOAD_0);
}
il.append(Utility.createGet(factory, resolvedMember));
il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(inlineAccessor.getReturnType())));
inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes));
}
return inlineAccessor;
}
private ResolvedMember createOrGetInlineAccessorForFieldSet(ResolvedMember resolvedMember) {
String accessor = NameMangler.inlineAccessMethodForFieldSet(resolvedMember.getName(), resolvedMember.getDeclaringType(),
aspectType);
String key = accessor;
ResolvedMember inlineAccessor = inlineAccessors.get(key);
if (inlineAccessor == null) {
inlineAccessor = AjcMemberMaker.inlineAccessMethodForFieldSet(aspectType, resolvedMember);
InstructionFactory factory = aspectGen.getFactory();
LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor);
method.makeSynthetic();
List<AjAttribute> methodAttributes = new ArrayList<AjAttribute>();
methodAttributes.add(new AjAttribute.AjSynthetic());
methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.FieldSet, false));
method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool()));
method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool()));
inlineAccessorMethodGens.add(method);
InstructionList il = method.getBody();
if (Modifier.isStatic(resolvedMember.getModifiers())) {
il.append(InstructionFactory.createLoad(BcelWorld.makeBcelType(resolvedMember.getReturnType()), 0));
} else {
il.append(InstructionConstants.ALOAD_0);
il.append(InstructionFactory.createLoad(BcelWorld.makeBcelType(resolvedMember.getReturnType()), 1));
}
il.append(Utility.createSet(factory, resolvedMember));
il.append(InstructionConstants.RETURN);
inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes));
}
return inlineAccessor;
}
}