/******************************************************************************* | |||||
* 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 ataspectj; | |||||
import junit.framework.TestCase; | |||||
import org.aspectj.lang.annotation.Aspect; | |||||
import org.aspectj.lang.annotation.Around; | |||||
import org.aspectj.lang.annotation.Before; | |||||
import org.aspectj.lang.ProceedingJoinPoint; | |||||
/** | |||||
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> | |||||
*/ | |||||
public class AroundInlineMungerTest extends TestCase { | |||||
public static void main(String[] args) { | |||||
TestHelper.runAndThrowOnFailure(suite()); | |||||
} | |||||
public static junit.framework.Test suite() { | |||||
return new junit.framework.TestSuite(AroundInlineMungerTest.class); | |||||
} | |||||
public void testAccessNonPublicFromAroundAdvice() { | |||||
target(); | |||||
assertEquals(3, Open.aroundCount); | |||||
assertEquals(6, Open.beforeCount); | |||||
} | |||||
public void target() {} | |||||
public static class OpenBase { | |||||
protected void superMethod() {} | |||||
} | |||||
public static class OpenSubBase extends OpenBase {} | |||||
// aspect will be prepared for inlining | |||||
@Aspect | |||||
public static class Open extends OpenSubBase { | |||||
public static int aroundCount = 0; | |||||
public static int beforeCount = 0; | |||||
private int i; | |||||
private static int I; | |||||
@Around("execution(* ataspectj.AroundInlineMungerTest.target())") | |||||
public Object around1(ProceedingJoinPoint jp) throws Throwable { | |||||
aroundCount++; | |||||
priv(1, 2L, 3); | |||||
super.superMethod(); | |||||
new Inner().priv();//fails to be wrapped so this advice will not be inlined but previous call were still prepared | |||||
return jp.proceed(); | |||||
} | |||||
// this advice to test around advice body call/get/set advising | |||||
@Before("(call(* ataspectj.AroundInlineMungerTest.Open.priv(..))" + | |||||
" || get(int ataspectj.AroundInlineMungerTest.Open.i)" + | |||||
" || set(int ataspectj.AroundInlineMungerTest.Open.i)" + | |||||
" || get(int ataspectj.AroundInlineMungerTest.Open.I)" + | |||||
" || set(int ataspectj.AroundInlineMungerTest.Open.I)" + | |||||
" )&& this(ataspectj.AroundInlineMungerTest.Open)") | |||||
public void before1() { | |||||
beforeCount++; | |||||
} | |||||
@Around("execution(* ataspectj.AroundInlineMungerTest.target())") | |||||
public Object around2(ProceedingJoinPoint jp) throws Throwable { | |||||
aroundCount++; | |||||
super.superMethod(); | |||||
new Inner().priv();//fails to be wrapped so next calls won't be prepared but previous was | |||||
priv(1, 2L, 3); | |||||
return jp.proceed(); | |||||
} | |||||
@Around("execution(* ataspectj.AroundInlineMungerTest.target())") | |||||
public Object around3(ProceedingJoinPoint jp) throws Throwable { | |||||
aroundCount++; | |||||
// all those field access will be wrapped | |||||
int li = i; | |||||
i = li; | |||||
int lI = I; | |||||
I = lI; | |||||
return jp.proceed(); | |||||
} | |||||
// -- some private member for which access will be wrapped so that around advice can be inlined | |||||
private void priv(int i, long j, int k) { | |||||
long l = i + j + k; | |||||
} | |||||
private static class Inner { | |||||
private Inner() {} | |||||
private void priv() {}; | |||||
} | |||||
} | |||||
} |
import org.aspectj.testing.XMLBasedAjcTestCase; | import org.aspectj.testing.XMLBasedAjcTestCase; | ||||
import org.aspectj.testing.AutowiredXMLBasedAjcTestCase; | import org.aspectj.testing.AutowiredXMLBasedAjcTestCase; | ||||
/** | |||||
/** | |||||
* A suite for @AspectJ aspects located in java5/ataspectj | * A suite for @AspectJ aspects located in java5/ataspectj | ||||
* | * | ||||
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> | * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> |
<suite> | <suite> | ||||
<!-- <comment>this one is ok - too simple - could be removed..</comment>--> | |||||
<!-- <ajc-test dir="java5/ataspectj"--> | |||||
<!-- pr="" title="@Aspect class extending @Aspect class">--> | |||||
<!-- <compile files="ataspectj/misuse/Test005.java" options="-1.5 -Xdev:NoAtAspectJProcessing">--> | |||||
<!-- </compile>--> | |||||
<!-- </ajc-test>--> | |||||
<!----> | |||||
<!-- <comment>just a warning - might be skept if further optimized in Aj5Attributes..</comment>--> | |||||
<!-- <ajc-test dir="java5/ataspectj"--> | |||||
<!-- pr="" title="class with @Before extending @Aspect class">--> | |||||
<!-- <compile files="ataspectj/misuse/Test006.java" options="-1.5 -Xdev:NoAtAspectJProcessing">--> | |||||
<!-- <message kind="warning" line="11" text="Found @AspectJ annotations in a non @Aspect type 'ataspectj.misuse.Test006B'"/>--> | |||||
<!-- </compile>--> | |||||
<!-- </ajc-test>--> | |||||
<comment>this one is ok - too simple - could be removed..</comment> | |||||
<ajc-test dir="java5/ataspectj" | |||||
pr="" title="@Aspect class extending @Aspect class"> | |||||
<compile files="ataspectj/misuse/Test005.java" options="-1.5 -Xdev:NoAtAspectJProcessing"> | |||||
</compile> | |||||
</ajc-test> | |||||
<comment>just a warning - might be skept if further optimized in Aj5Attributes..</comment> | |||||
<ajc-test dir="java5/ataspectj" | |||||
pr="" title="class with @Before extending @Aspect class"> | |||||
<compile files="ataspectj/misuse/Test006.java" options="-1.5 -Xdev:NoAtAspectJProcessing"> | |||||
<message kind="warning" line="11" text="Found @AspectJ annotations in a non @Aspect type 'ataspectj.misuse.Test006B'"/> | |||||
</compile> | |||||
</ajc-test> | |||||
<comment>a warning. We ignore the pointcut (TBD) - line is enclosing class (TBD Andy do better ?)</comment> | <comment>a warning. We ignore the pointcut (TBD) - line is enclosing class (TBD Andy do better ?)</comment> | ||||
<ajc-test dir="java5/ataspectj" | <ajc-test dir="java5/ataspectj" | ||||
</compile> | </compile> | ||||
</ajc-test> | </ajc-test> | ||||
<!-- <ajc-test dir="java5/ataspectj"--> | <!-- <ajc-test dir="java5/ataspectj"--> | ||||
<!-- pr="" title="@Aspect on interface">--> | <!-- pr="" title="@Aspect on interface">--> | ||||
<!-- <compile files="ataspectj/misuse/Test010.java" options="-1.5 -Xdev:NoAtAspectJProcessing">--> | <!-- <compile files="ataspectj/misuse/Test010.java" options="-1.5 -Xdev:NoAtAspectJProcessing">--> | ||||
<!-- </compile>--> | <!-- </compile>--> | ||||
<!-- </ajc-test>--> | <!-- </ajc-test>--> | ||||
<!-- <comment>line is enclosing class - TBD</comment>--> | |||||
<!-- <ajc-test dir="java5/ataspectj"--> | |||||
<!-- pr="" title="@Pointcut with garbage string">--> | |||||
<!-- <compile files="ataspectj/misuse/Test014.java" options="-1.5 -Xdev:NoAtAspectJProcessing">--> | |||||
<!-- <message kind="error" line="7" text="Cannot parse @Pointcut 'call%dddd"/>--> | |||||
<!-- </compile>--> | |||||
<!-- </ajc-test>--> | |||||
<!----> | |||||
<!-- <comment>line is enclosing class - TBD</comment>--> | |||||
<!-- <ajc-test dir="java5/ataspectj"--> | |||||
<!-- pr="" title="@Pointcut with throws clause">--> | |||||
<!-- <compile files="ataspectj/misuse/Test016.java" options="-1.5 -Xdev:NoAtAspectJProcessing">--> | |||||
<!-- <message kind="warning" line="7" text="Found @Pointcut on a method throwing exception 'someCall()V'"/>--> | |||||
<!-- </compile>--> | |||||
<!-- </ajc-test>--> | |||||
<!----> | |||||
<!-- <comment>very dirty hack - can't get this location to work properly so added match all error..</comment>--> | |||||
<!-- <ajc-test dir="java5/ataspectj"--> | |||||
<!-- pr="" title="@AfterReturning with wrong number of args">--> | |||||
<!-- <compile files="ataspectj/misuse/Test019.java" options="-1.5 -Xdev:NoAtAspectJProcessing -Xlint:ignore">--> | |||||
<!-- <message kind="error" line="1" text="the parameter x is not bound"/>--> | |||||
<!-- <message kind="error"/>--> | |||||
<!-- </compile>--> | |||||
<!-- </ajc-test>--> | |||||
<!----> | |||||
<!-- <comment>line number is enclosing type</comment>--> | |||||
<!-- <ajc-test dir="java5/ataspectj"--> | |||||
<!-- pr="" title="@Before on non-public method">--> | |||||
<!-- <compile files="ataspectj/misuse/Test020.java" options="-1.5 -Xdev:NoAtAspectJProcessing -Xlint:ignore">--> | |||||
<!-- <message kind="error" line="7" text="Found @AspectJ annotation on a non public advice 'someCall()V'"/>--> | |||||
<!-- </compile>--> | |||||
<!-- </ajc-test>--> | |||||
<!----> | |||||
<!-- <comment>line number is enclosing type</comment>--> | |||||
<!-- <ajc-test dir="java5/ataspectj"--> | |||||
<!-- pr="" title="@Before on method not returning void">--> | |||||
<!-- <compile files="ataspectj/misuse/Test021.java" options="-1.5 -Xdev:NoAtAspectJProcessing -Xlint:ignore">--> | |||||
<!-- <message kind="error" line="7" text="Found @AspectJ annotation on a non around advice not returning void 'someCall()I'"/>--> | |||||
<!-- </compile>--> | |||||
<!-- </ajc-test>--> | |||||
<comment>line is enclosing class - TBD</comment> | |||||
<ajc-test dir="java5/ataspectj" | |||||
pr="" title="@Pointcut with garbage string"> | |||||
<compile files="ataspectj/misuse/Test014.java" options="-1.5 -Xdev:NoAtAspectJProcessing"> | |||||
<message kind="error" line="7" text="Cannot parse @Pointcut 'call%dddd"/> | |||||
</compile> | |||||
</ajc-test> | |||||
<comment>line is enclosing class - TBD</comment> | |||||
<ajc-test dir="java5/ataspectj" | |||||
pr="" title="@Pointcut with throws clause"> | |||||
<compile files="ataspectj/misuse/Test016.java" options="-1.5 -Xdev:NoAtAspectJProcessing"> | |||||
<message kind="warning" line="7" text="Found @Pointcut on a method throwing exception 'someCall()V'"/> | |||||
</compile> | |||||
</ajc-test> | |||||
<comment>very dirty hack - can't get this location to work properly so added match all error..</comment> | |||||
<ajc-test dir="java5/ataspectj" | |||||
pr="" title="@AfterReturning with wrong number of args"> | |||||
<compile files="ataspectj/misuse/Test019.java" options="-1.5 -Xdev:NoAtAspectJProcessing -Xlint:ignore"> | |||||
<message kind="error" line="1" text="the parameter x is not bound"/> | |||||
<message kind="error"/> | |||||
</compile> | |||||
</ajc-test> | |||||
<comment>line number is enclosing type</comment> | |||||
<ajc-test dir="java5/ataspectj" | |||||
pr="" title="@Before on non-public method"> | |||||
<compile files="ataspectj/misuse/Test020.java" options="-1.5 -Xdev:NoAtAspectJProcessing -Xlint:ignore"> | |||||
<message kind="error" line="7" text="Found @AspectJ annotation on a non public advice 'someCall()V'"/> | |||||
</compile> | |||||
</ajc-test> | |||||
<comment>line number is enclosing type</comment> | |||||
<ajc-test dir="java5/ataspectj" | |||||
pr="" title="@Before on method not returning void"> | |||||
<compile files="ataspectj/misuse/Test021.java" options="-1.5 -Xdev:NoAtAspectJProcessing -Xlint:ignore"> | |||||
<message kind="error" line="7" text="Found @AspectJ annotation on a non around advice not returning void 'someCall()I'"/> | |||||
</compile> | |||||
</ajc-test> | |||||
<!-- | <!-- | ||||
ALEX: todo | ALEX: todo |
<run class="ataspectj.PerClauseTest"/> | <run class="ataspectj.PerClauseTest"/> | ||||
</ajc-test> | </ajc-test> | ||||
<ajc-test dir="java5/ataspectj" title="AroundInlineMunger"> | |||||
<compile files="ataspectj/AroundInlineMungerTest.java,ataspectj/TestHelper.java" options="-1.5 -XnoInline -Xdev:NoAtAspectJProcessing -Xlint:ignore"/> | |||||
<run class="ataspectj.AroundInlineMungerTest"/> | |||||
<compile files="ataspectj/AroundInlineMungerTest.java,ataspectj/TestHelper.java" options="-1.5 -Xdev:NoAtAspectJProcessing -Xlint:ignore"/> | |||||
<run class="ataspectj.AroundInlineMungerTest"/> | |||||
</ajc-test> | |||||
</suite> | </suite> |
/******************************************************************************* | |||||
* 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 org.aspectj.apache.bcel.Constants; | |||||
import org.aspectj.apache.bcel.generic.ConstantPoolGen; | |||||
import org.aspectj.apache.bcel.generic.FieldInstruction; | |||||
import org.aspectj.apache.bcel.generic.GETFIELD; | |||||
import org.aspectj.apache.bcel.generic.GETSTATIC; | |||||
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.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.ResolvedTypeX; | |||||
import org.aspectj.weaver.Shadow; | |||||
import org.aspectj.weaver.TypeX; | |||||
import java.util.HashMap; | |||||
import java.util.HashSet; | |||||
import java.util.Iterator; | |||||
import java.util.Map; | |||||
import java.util.Set; | |||||
import java.util.List; | |||||
/** | |||||
* 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. | |||||
* <p/> | |||||
* This munger is used for @AJ aspects for which inlining wrapper is not done at compile time. | |||||
* <p/> | |||||
* 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 <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> | |||||
*/ | |||||
public class BcelAccessForInlineMunger extends BcelTypeMunger { | |||||
/** | |||||
* Wrapper member cache, key is wrapper name. | |||||
* This structure is queried when regular shadow matching in the advice body (call/get/set) occurs | |||||
*/ | |||||
private Map m_inlineAccessorBcelMethods; | |||||
/** | |||||
* The aspect we act for | |||||
*/ | |||||
private LazyClassGen m_aspectGen; | |||||
/** | |||||
* The wrapper method we need to add. Those are added at the end of the munging | |||||
*/ | |||||
private Set m_inlineAccessorMethodGens; | |||||
public BcelAccessForInlineMunger(ResolvedTypeX aspectType) { | |||||
super(null, aspectType); | |||||
} | |||||
public boolean munge(BcelClassWeaver weaver) { | |||||
m_aspectGen = weaver.getLazyClassGen(); | |||||
m_inlineAccessorBcelMethods = new HashMap(0); | |||||
m_inlineAccessorMethodGens = new HashSet(); | |||||
// look for all @Around advices | |||||
for (Iterator iterator = m_aspectGen.getMethodGens().iterator(); iterator.hasNext();) { | |||||
LazyMethodGen methodGen = (LazyMethodGen) iterator.next(); | |||||
if (methodGen.hasAnnotation(TypeX.forName("org/aspectj/lang/annotation/Around"))) { | |||||
openAroundAdvice(methodGen); | |||||
} | |||||
} | |||||
// add the accessors | |||||
for (Iterator iterator = m_inlineAccessorMethodGens.iterator(); iterator.hasNext();) { | |||||
LazyMethodGen lazyMethodGen = (LazyMethodGen) iterator.next(); | |||||
m_aspectGen.addMethodGen(lazyMethodGen); | |||||
} | |||||
// flush some | |||||
m_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 | |||||
* | |||||
* @param member | |||||
* @return | |||||
*/ | |||||
public ResolvedMember getMatchingSyntheticMember(Member member) { | |||||
return (ResolvedMember) m_inlineAccessorBcelMethods.get(member.getName()); | |||||
} | |||||
public ResolvedMember getSignature() { | |||||
return null; | |||||
} | |||||
/** | |||||
* Match only the aspect for which we act | |||||
* | |||||
* @param onType | |||||
* @return | |||||
*/ | |||||
public boolean matches(ResolvedTypeX onType) { | |||||
return aspectType.equals(onType); | |||||
} | |||||
/** | |||||
* Prepare the around advice, flag it as cannot be inlined if it can't be | |||||
* | |||||
* @param aroundAdvice | |||||
*/ | |||||
private void openAroundAdvice(LazyMethodGen aroundAdvice) { | |||||
InstructionHandle curr = aroundAdvice.getBody().getStart(); | |||||
InstructionHandle end = aroundAdvice.getBody().getEnd(); | |||||
ConstantPoolGen cpg = aroundAdvice.getEnclosingClass().getConstantPoolGen(); | |||||
InstructionFactory factory = aroundAdvice.enclosingClass.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; | |||||
ResolvedTypeX callee = m_aspectGen.getWorld().resolve(TypeX.forName(invoke.getClassName(cpg))); | |||||
// look in the whole method list and not just declared for super calls and alike | |||||
List methods = callee.getMethodsWithoutIterator(); | |||||
for (Iterator iter = methods.iterator(); iter.hasNext();) { | |||||
BcelMethod resolvedMember = (BcelMethod) iter.next(); | |||||
if (invoke.getName(cpg).equals(resolvedMember.getName()) | |||||
&& invoke.getSignature(cpg).equals(resolvedMember.getSignature()) | |||||
&& !resolvedMember.isPublic()) { | |||||
if ("<init>".equals(invoke.getName(cpg))) { | |||||
// 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 | |||||
if (aspectType.getSuperclass() != null | |||||
&& aspectType.getSuperclass().getName().equals(callee.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; | |||||
ResolvedTypeX callee = m_aspectGen.getWorld().resolve(TypeX.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 instanceof GETFIELD) || (inst instanceof 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; | |||||
} | |||||
} | |||||
/** | |||||
* Add an inline wrapper for a non public method call | |||||
* | |||||
* @param resolvedMember | |||||
* @return | |||||
*/ | |||||
private ResolvedMember createOrGetInlineAccessorForMethod(ResolvedMember resolvedMember) { | |||||
String accessor = NameMangler.inlineAccessMethodForMethod( | |||||
resolvedMember.getName(), resolvedMember.getDeclaringType(), aspectType | |||||
); | |||||
ResolvedMember inlineAccessor = (ResolvedMember) m_inlineAccessorBcelMethods.get(accessor); | |||||
if (inlineAccessor == null) { | |||||
// add static method to aspect | |||||
inlineAccessor = AjcMemberMaker.inlineAccessMethodForMethod( | |||||
aspectType, | |||||
resolvedMember | |||||
); | |||||
//add new accessor method to aspect bytecode | |||||
InstructionFactory factory = m_aspectGen.getFactory(); | |||||
LazyMethodGen method = makeMethodGen(m_aspectGen, inlineAccessor); | |||||
// flag it synthetic, AjSynthetic | |||||
method.makeSynthetic(); | |||||
method.addAttribute( | |||||
BcelAttributes.bcelAttribute(new AjAttribute.AjSynthetic(), m_aspectGen.getConstantPoolGen()) | |||||
); | |||||
// flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut | |||||
method.addAttribute( | |||||
BcelAttributes.bcelAttribute( | |||||
new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.MethodCall, false), | |||||
m_aspectGen.getConstantPoolGen() | |||||
) | |||||
); | |||||
m_inlineAccessorMethodGens.add(method); | |||||
InstructionList il = method.getBody(); | |||||
int register = 0; | |||||
for (int i = 0; i < inlineAccessor.getParameterTypes().length; i++) { | |||||
TypeX typeX = inlineAccessor.getParameterTypes()[i]; | |||||
Type type = BcelWorld.makeBcelType(typeX); | |||||
il.append(InstructionFactory.createLoad(type, register)); | |||||
register += type.getSize(); | |||||
} | |||||
il.append( | |||||
Utility.createInvoke( | |||||
factory, | |||||
resolvedMember.isStatic() ? Constants.INVOKESTATIC : Constants.INVOKESPECIAL, | |||||
resolvedMember | |||||
) | |||||
); | |||||
il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(inlineAccessor.getReturnType()))); | |||||
m_inlineAccessorBcelMethods.put( | |||||
accessor, | |||||
new BcelMethod(m_aspectGen.getBcelObjectType(), method.getMethod()) | |||||
); | |||||
} | |||||
return inlineAccessor; | |||||
} | |||||
/** | |||||
* Add an inline wrapper for a non public super.method call | |||||
* | |||||
* @param resolvedMember | |||||
* @return | |||||
*/ | |||||
private ResolvedMember createOrGetInlineAccessorForSuperDispatch(ResolvedMember resolvedMember) { | |||||
String accessor = NameMangler.superDispatchMethod( | |||||
aspectType, resolvedMember.getName() | |||||
); | |||||
ResolvedMember inlineAccessor = (ResolvedMember) m_inlineAccessorBcelMethods.get(accessor); | |||||
if (inlineAccessor == null) { | |||||
// add static method to aspect | |||||
inlineAccessor = AjcMemberMaker.superAccessMethod( | |||||
aspectType, | |||||
resolvedMember | |||||
); | |||||
//add new accessor method to aspect bytecode | |||||
InstructionFactory factory = m_aspectGen.getFactory(); | |||||
LazyMethodGen method = makeMethodGen(m_aspectGen, inlineAccessor); | |||||
// flag it synthetic, AjSynthetic | |||||
method.makeSynthetic(); | |||||
method.addAttribute( | |||||
BcelAttributes.bcelAttribute(new AjAttribute.AjSynthetic(), m_aspectGen.getConstantPoolGen()) | |||||
); | |||||
// flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut | |||||
method.addAttribute( | |||||
BcelAttributes.bcelAttribute( | |||||
new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.MethodCall, false), | |||||
m_aspectGen.getConstantPoolGen() | |||||
) | |||||
); | |||||
m_inlineAccessorMethodGens.add(method); | |||||
InstructionList il = method.getBody(); | |||||
il.append(InstructionConstants.ALOAD_0); | |||||
int register = 0; | |||||
for (int i = 0; i < inlineAccessor.getParameterTypes().length; i++) { | |||||
TypeX 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()))); | |||||
m_inlineAccessorBcelMethods.put( | |||||
accessor, | |||||
new BcelMethod(m_aspectGen.getBcelObjectType(), method.getMethod()) | |||||
); | |||||
} | |||||
return inlineAccessor; | |||||
} | |||||
/** | |||||
* Add an inline wrapper for a non public field get | |||||
* | |||||
* @param resolvedMember | |||||
* @return | |||||
*/ | |||||
private ResolvedMember createOrGetInlineAccessorForFieldGet(ResolvedMember resolvedMember) { | |||||
String accessor = NameMangler.inlineAccessMethodForFieldGet( | |||||
resolvedMember.getName(), resolvedMember.getDeclaringType(), aspectType | |||||
); | |||||
ResolvedMember inlineAccessor = (ResolvedMember) m_inlineAccessorBcelMethods.get(accessor); | |||||
if (inlineAccessor == null) { | |||||
// add static method to aspect | |||||
inlineAccessor = AjcMemberMaker.inlineAccessMethodForFieldGet( | |||||
aspectType, | |||||
resolvedMember | |||||
); | |||||
//add new accessor method to aspect bytecode | |||||
InstructionFactory factory = m_aspectGen.getFactory(); | |||||
LazyMethodGen method = makeMethodGen(m_aspectGen, inlineAccessor); | |||||
// flag it synthetic, AjSynthetic | |||||
method.makeSynthetic(); | |||||
method.addAttribute( | |||||
BcelAttributes.bcelAttribute(new AjAttribute.AjSynthetic(), m_aspectGen.getConstantPoolGen()) | |||||
); | |||||
// flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut | |||||
method.addAttribute( | |||||
BcelAttributes.bcelAttribute( | |||||
new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.FieldGet, false), | |||||
m_aspectGen.getConstantPoolGen() | |||||
) | |||||
); | |||||
m_inlineAccessorMethodGens.add(method); | |||||
InstructionList il = method.getBody(); | |||||
if (resolvedMember.isStatic()) { | |||||
// 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()))); | |||||
m_inlineAccessorBcelMethods.put( | |||||
accessor, | |||||
new BcelMethod(m_aspectGen.getBcelObjectType(), method.getMethod()) | |||||
); | |||||
} | |||||
return inlineAccessor; | |||||
} | |||||
/** | |||||
* Add an inline wrapper for a non public field set | |||||
* | |||||
* @param resolvedMember | |||||
* @return | |||||
*/ | |||||
private ResolvedMember createOrGetInlineAccessorForFieldSet(ResolvedMember resolvedMember) { | |||||
String accessor = NameMangler.inlineAccessMethodForFieldSet( | |||||
resolvedMember.getName(), resolvedMember.getDeclaringType(), aspectType | |||||
); | |||||
ResolvedMember inlineAccessor = (ResolvedMember) m_inlineAccessorBcelMethods.get(accessor); | |||||
if (inlineAccessor == null) { | |||||
// add static method to aspect | |||||
inlineAccessor = AjcMemberMaker.inlineAccessMethodForFieldSet( | |||||
aspectType, | |||||
resolvedMember | |||||
); | |||||
//add new accessor method to aspect bytecode | |||||
InstructionFactory factory = m_aspectGen.getFactory(); | |||||
LazyMethodGen method = makeMethodGen(m_aspectGen, inlineAccessor); | |||||
// flag it synthetic, AjSynthetic | |||||
method.makeSynthetic(); | |||||
method.addAttribute( | |||||
BcelAttributes.bcelAttribute(new AjAttribute.AjSynthetic(), m_aspectGen.getConstantPoolGen()) | |||||
); | |||||
// flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut | |||||
method.addAttribute( | |||||
BcelAttributes.bcelAttribute( | |||||
new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.FieldSet, false), | |||||
m_aspectGen.getConstantPoolGen() | |||||
) | |||||
); | |||||
m_inlineAccessorMethodGens.add(method); | |||||
InstructionList il = method.getBody(); | |||||
if (resolvedMember.isStatic()) { | |||||
// 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); | |||||
m_inlineAccessorBcelMethods.put( | |||||
accessor, | |||||
new BcelMethod(m_aspectGen.getBcelObjectType(), method.getMethod()) | |||||
); | |||||
} | |||||
return inlineAccessor; | |||||
} | |||||
} |
annotations = resolvedDooberry.getAnnotationTypes(); | annotations = resolvedDooberry.getAnnotationTypes(); | ||||
} | } | ||||
} else if (rm.getKind()==Member.METHOD && !rm.isAbstract()) { | } else if (rm.getKind()==Member.METHOD && !rm.isAbstract()) { | ||||
if (methodName.startsWith("ajc$inlineAccessMethod")) { | |||||
if (methodName.startsWith("ajc$inlineAccessMethod") || methodName.startsWith("ajc$superDispatch")) { | |||||
ResolvedMember resolvedDooberry = world.resolve(declaredSig); | ResolvedMember resolvedDooberry = world.resolve(declaredSig); | ||||
annotations = resolvedDooberry.getAnnotationTypes(); | annotations = resolvedDooberry.getAnnotationTypes(); | ||||
} else { | } else { |
generatePerTWGetInstanceMethod(gen); | generatePerTWGetInstanceMethod(gen); | ||||
generatePerTWCreateAspectInstanceMethod(gen); | generatePerTWCreateAspectInstanceMethod(gen); | ||||
} else { | } else { | ||||
throw new RuntimeException("TODO not yet implemented perClause " + kind.getName()); | |||||
throw new Error("should not happen - not such kind " + kind.getName()); | |||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
public ResolvedMember getMatchingSyntheticMember(Member member) { | public ResolvedMember getMatchingSyntheticMember(Member member) { | ||||
//TODO is that ok ? | |||||
return null; | return null; | ||||
} | } | ||||
public ResolvedMember getSignature() { | public ResolvedMember getSignature() { | ||||
// TODO what to do here ? | |||||
return null; | return null; | ||||
//throw new RuntimeException("not implemented - BcelPerClauseAspectAdder"); | |||||
} | } | ||||
public boolean matches(ResolvedTypeX onType) { | public boolean matches(ResolvedTypeX onType) { | ||||
return true;//onType.equals(aspectType); | |||||
//we cannot return onType.equals(aspectType) | |||||
//since we need to eagerly create the nested ajcMighHaveAspect interface on LTW | |||||
return true; | |||||
} | } | ||||
private void generatePerClauseMembers(LazyClassGen classGen) { | private void generatePerClauseMembers(LazyClassGen classGen) { |
public void setCanInline(boolean canInline) { | public void setCanInline(boolean canInline) { | ||||
this.canInline = canInline; | this.canInline = canInline; | ||||
} | } | ||||
/** | |||||
* Adds an attribute to the method | |||||
* @param attr | |||||
*/ | |||||
public void addAttribute(Attribute attr) { | |||||
Attribute[] newAttributes = new Attribute[attributes.length + 1]; | |||||
System.arraycopy(attributes, 0, newAttributes, 0, attributes.length); | |||||
newAttributes[attributes.length] = attr; | |||||
attributes = newAttributes; | |||||
} | |||||
} | } |
import org.aspectj.weaver.TypeX; | import org.aspectj.weaver.TypeX; | ||||
import org.aspectj.weaver.VersionedDataInputStream; | import org.aspectj.weaver.VersionedDataInputStream; | ||||
import org.aspectj.weaver.World; | import org.aspectj.weaver.World; | ||||
import org.aspectj.weaver.bcel.BcelAccessForInlineMunger; | |||||
import org.aspectj.weaver.ataspectj.Ajc5MemberMaker; | |||||
import org.aspectj.weaver.ast.Expr; | import org.aspectj.weaver.ast.Expr; | ||||
import org.aspectj.weaver.ast.Test; | import org.aspectj.weaver.ast.Test; | ||||
); | ); | ||||
} | } | ||||
//ATAJ inline around advice support | |||||
if (Ajc5MemberMaker.isAnnotationStyleAspect(inAspect)) { | |||||
inAspect.crosscuttingMembers.addTypeMunger(new BcelAccessForInlineMunger(inAspect)); | |||||
} | |||||
return ret; | return ret; | ||||
} | } | ||||
import org.aspectj.weaver.Shadow; | import org.aspectj.weaver.Shadow; | ||||
import org.aspectj.weaver.VersionedDataInputStream; | import org.aspectj.weaver.VersionedDataInputStream; | ||||
import org.aspectj.weaver.World; | import org.aspectj.weaver.World; | ||||
import org.aspectj.weaver.bcel.BcelAccessForInlineMunger; | |||||
import org.aspectj.weaver.ataspectj.Ajc5MemberMaker; | |||||
import org.aspectj.weaver.ast.Expr; | import org.aspectj.weaver.ast.Expr; | ||||
import org.aspectj.weaver.ast.Test; | import org.aspectj.weaver.ast.Test; | ||||
import org.aspectj.weaver.ast.Var; | import org.aspectj.weaver.ast.Var; | ||||
); | ); | ||||
} | } | ||||
//ATAJ inline around advice support | |||||
if (Ajc5MemberMaker.isAnnotationStyleAspect(inAspect)) { | |||||
inAspect.crosscuttingMembers.addTypeMunger(new BcelAccessForInlineMunger(inAspect)); | |||||
} | |||||
return ret; | return ret; | ||||
} | } | ||||
import org.aspectj.weaver.ResolvedTypeX; | import org.aspectj.weaver.ResolvedTypeX; | ||||
import org.aspectj.weaver.Shadow; | import org.aspectj.weaver.Shadow; | ||||
import org.aspectj.weaver.VersionedDataInputStream; | import org.aspectj.weaver.VersionedDataInputStream; | ||||
import org.aspectj.weaver.bcel.BcelAccessForInlineMunger; | |||||
import org.aspectj.weaver.ast.Expr; | import org.aspectj.weaver.ast.Expr; | ||||
import org.aspectj.weaver.ast.Literal; | import org.aspectj.weaver.ast.Literal; | ||||
import org.aspectj.weaver.ast.Test; | import org.aspectj.weaver.ast.Test; | ||||
); | ); | ||||
} | } | ||||
//ATAJ inline around advice support | |||||
if (Ajc5MemberMaker.isAnnotationStyleAspect(inAspect)) { | |||||
inAspect.crosscuttingMembers.addTypeMunger(new BcelAccessForInlineMunger(inAspect)); | |||||
} | |||||
return ret; | return ret; | ||||
} | } | ||||
import org.aspectj.weaver.Shadow; | import org.aspectj.weaver.Shadow; | ||||
import org.aspectj.weaver.VersionedDataInputStream; | import org.aspectj.weaver.VersionedDataInputStream; | ||||
import org.aspectj.weaver.World; | import org.aspectj.weaver.World; | ||||
import org.aspectj.weaver.bcel.BcelAccessForInlineMunger; | |||||
import org.aspectj.weaver.ataspectj.Ajc5MemberMaker; | |||||
import org.aspectj.weaver.ast.Expr; | import org.aspectj.weaver.ast.Expr; | ||||
import org.aspectj.weaver.ast.Literal; | import org.aspectj.weaver.ast.Literal; | ||||
import org.aspectj.weaver.ast.Test; | import org.aspectj.weaver.ast.Test; | ||||
); | ); | ||||
} | } | ||||
//ATAJ inline around advice support | |||||
if (Ajc5MemberMaker.isAnnotationStyleAspect(inAspect)) { | |||||
inAspect.crosscuttingMembers.addTypeMunger(new BcelAccessForInlineMunger(inAspect)); | |||||
} | |||||
return ret; | return ret; | ||||
} | } |