/******************************************************************************* * Copyright (c) 2005 Contributors. * All rights reserved. * This program and the accompanying materials are made available * under the terms of the Eclipse Public License v1.0 * which accompanies this distribution and is available at * http://eclipse.org/legal/epl-v10.html * * Contributors: * Alexandre Vasseur initial implementation *******************************************************************************/ 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; /** * Looks for all access to method or field that are not public within the body of the around advices and replace the invocations to * a wrapper call so that the around advice can further be inlined. *

* This munger is used for @AJ aspects for which inlining wrapper is not done at compile time. *

*

* Specific state and logic is kept in the munger ala ITD so that call/get/set pointcuts can still be matched on the wrapped member * thanks to the EffectiveSignature attribute. *

* * @author Alexandre Vasseur * @author Andy Clement */ public class BcelAccessForInlineMunger extends BcelTypeMunger { private Map inlineAccessors; private LazyClassGen aspectGen; /** * The wrapper methods representing any created inlineAccessors */ private Set 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<>(0); inlineAccessorMethodGens = new HashSet<>(); // look for all @Around advices for (LazyMethodGen methodGen : aspectGen.getMethodGens()) { if (methodGen.hasAnnotation(UnresolvedType.forName("org/aspectj/lang/annotation/Around"))) { openAroundAdvice(methodGen); } } // add the accessors for (LazyMethodGen lazyMethodGen : inlineAccessorMethodGens) { aspectGen.addMethodGen(lazyMethodGen); } // flush some inlineAccessorMethodGens = null; // we keep m_inlineAccessorsResolvedMembers for shadow matching return true; } /** * Looks in the wrapper we have added so that we can find their effective signature if needed */ @Override public ResolvedMember getMatchingSyntheticMember(Member member) { ResolvedMember rm = inlineAccessors.get(member.getName());// + member.getSignature()); // System.err.println("lookup for " + member.getName() + ":" + member.getSignature() + " = " // + (rm == null ? "" : rm.getName())); return rm; } @Override public ResolvedMember getSignature() { return null; } /** * Match only the aspect for which we act */ @Override public boolean matches(ResolvedType onType) { return aspectType.equals(onType); } /** * Prepare the around advice, flag it as cannot be inlined if it can't be */ 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) { // we know we cannot inline this advice so no need for futher handling break; } InstructionHandle next = curr.getNext(); Instruction inst = curr.getInstruction(); // open-up method call 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))); // look in the whole method list and not just declared for super calls and alike List 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 ("".equals(invoke.getName(cpg)) ) { // If ctor invocation, we care about whether it is targeting exactly the same type // (ignore non public ctors in supertype of the target) (J13 - AbstractStringBuilder has something // that trips this up in one testcase) if (invoke.getClassName(cpg).equals(resolvedMember.getDeclaringType().getPackageName()+ "."+resolvedMember.getDeclaringType().getClassName())) { // skipping open up for private constructor // can occur when aspect new a private inner type // too complex to handle new + dup + .. + invokespecial here. aroundAdvice.setCanInline(false); realizedCannotInline = true; } } else { // specific handling for super.foo() calls, where foo is non public ResolvedType memberType = aspectGen.getWorld().resolve(resolvedMember.getDeclaringType()); if (!aspectType.equals(memberType) && memberType.isAssignableFrom(aspectType)) { // old test was... // if (aspectType.getSuperclass() != null // && aspectType.getSuperclass().getName().equals(resolvedMember.getDeclaringType().getName())) { 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;// ok we found a matching callee member and swapped the instruction with the accessor } } } 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;// ok we found a matching callee member and swapped the instruction with the accessor } } } curr = next; } // no reason for not inlining this advice // since it is used for @AJ advice that cannot be inlined by defauilt // make sure we set inline to true since we have done this analysis if (!realizedCannotInline) { aroundAdvice.setCanInline(true); } } /** * Find (or add if not yet created) an inline wrapper for a non public method call */ private ResolvedMember createOrGetInlineAccessorForMethod(ResolvedMember resolvedMember) { String accessorName = NameMangler.inlineAccessMethodForMethod(resolvedMember.getName(), resolvedMember.getDeclaringType(), aspectType); String key = accessorName;// new StringBuilder(accessorName).append(resolvedMember.getSignature()).toString(); ResolvedMember inlineAccessor = inlineAccessors.get(key); // System.err.println(key + " accessor=" + inlineAccessor); if (inlineAccessor == null) { // add static method to aspect inlineAccessor = AjcMemberMaker.inlineAccessMethodForMethod(aspectType, resolvedMember); // add new accessor method to aspect bytecode InstructionFactory factory = aspectGen.getFactory(); LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor); method.makeSynthetic(); List methodAttributes = new ArrayList<>(); methodAttributes.add(new AjAttribute.AjSynthetic()); methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.MethodCall, false)); method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool())); // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut 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; } /** * Add an inline wrapper for a non public super.method call */ private ResolvedMember createOrGetInlineAccessorForSuperDispatch(ResolvedMember resolvedMember) { String accessor = NameMangler.superDispatchMethod(aspectType, resolvedMember.getName()); String key = accessor; ResolvedMember inlineAccessor = inlineAccessors.get(key); if (inlineAccessor == null) { // add super accessor method to class: inlineAccessor = AjcMemberMaker.superAccessMethod(aspectType, resolvedMember); // add new accessor method to aspect bytecode InstructionFactory factory = aspectGen.getFactory(); LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor); // flag it synthetic, AjSynthetic method.makeSynthetic(); List methodAttributes = new ArrayList<>(); methodAttributes.add(new AjAttribute.AjSynthetic()); methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.MethodCall, false)); method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool())); // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut 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; } /** * Add an inline wrapper for a non public field get */ 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) { // add static method to aspect inlineAccessor = AjcMemberMaker.inlineAccessMethodForFieldGet(aspectType, resolvedMember); // add new accessor method to aspect bytecode InstructionFactory factory = aspectGen.getFactory(); LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor); // flag it synthetic, AjSynthetic method.makeSynthetic(); List methodAttributes = new ArrayList<>(); methodAttributes.add(new AjAttribute.AjSynthetic()); methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.FieldGet, false)); // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut 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())) { // field accessed is static so no "this" as accessor sole parameter } 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; } /** * Add an inline wrapper for a non public field set */ 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) { // add static method to aspect inlineAccessor = AjcMemberMaker.inlineAccessMethodForFieldSet(aspectType, resolvedMember); // add new accessor method to aspect bytecode InstructionFactory factory = aspectGen.getFactory(); LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor); // flag it synthetic, AjSynthetic method.makeSynthetic(); List methodAttributes = new ArrayList<>(); methodAttributes.add(new AjAttribute.AjSynthetic()); methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.FieldSet, false)); method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool())); // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool())); inlineAccessorMethodGens.add(method); InstructionList il = method.getBody(); if (Modifier.isStatic(resolvedMember.getModifiers())) { // field accessed is static so sole parameter is field value to be set 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; } }