From fe310a4e10cf69486f54d5cf4edd4893b0a0835c Mon Sep 17 00:00:00 2001 From: aclement Date: Tue, 18 Jan 2005 11:39:16 +0000 Subject: [PATCH] PerTypeWithin support. --- .../compiler/ast/AspectDeclaration.java | 146 +++++++++++++++ .../systemtest/ajc150/AllTestsAspectJ150.java | 7 +- .../systemtest/ajc150/PerTypeWithinTests.java | 151 +++++++++++++++ weaver/src/org/aspectj/weaver/Advice.java | 7 + weaver/src/org/aspectj/weaver/AdviceKind.java | 11 +- .../org/aspectj/weaver/AjcMemberMaker.java | 84 ++++++++- .../src/org/aspectj/weaver/NameMangler.java | 18 +- .../weaver/PerTypeWithinTargetTypeMunger.java | 52 ++++++ .../aspectj/weaver/ResolvedTypeMunger.java | 1 + weaver/src/org/aspectj/weaver/TypeX.java | 3 + .../org/aspectj/weaver/bcel/BcelAdvice.java | 3 + .../org/aspectj/weaver/bcel/BcelShadow.java | 27 +++ .../aspectj/weaver/bcel/BcelTypeMunger.java | 40 ++++ .../weaver/patterns/PatternParser.java | 10 + .../aspectj/weaver/patterns/PerClause.java | 4 + .../weaver/patterns/PerTypeWithin.java | 175 ++++++++++++++++++ 16 files changed, 728 insertions(+), 11 deletions(-) create mode 100644 tests/src/org/aspectj/systemtest/ajc150/PerTypeWithinTests.java create mode 100644 weaver/src/org/aspectj/weaver/PerTypeWithinTargetTypeMunger.java create mode 100644 weaver/src/org/aspectj/weaver/patterns/PerTypeWithin.java diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/ast/AspectDeclaration.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/ast/AspectDeclaration.java index cfb0381ce..a2f8fb22b 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/ast/AspectDeclaration.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/ast/AspectDeclaration.java @@ -27,6 +27,7 @@ import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; //import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.CodeStream; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.ExceptionLabel; import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.Label; import org.aspectj.org.eclipse.jdt.internal.compiler.env.IGenericType; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.*; @@ -194,6 +195,9 @@ public class AspectDeclaration extends TypeDeclaration { // world.makeFieldBinding( // AjcMemberMaker.perCflowField( // typeX))); + } else if (perClause.getKind() == PerClause.PERTYPEWITHIN) { + //PTWIMPL Add field for storing typename in aspect for which the aspect instance exists + binding.addField(factory.makeFieldBinding(AjcMemberMaker.perTypeWithinWithinTypeField(typeX,typeX))); } else { throw new RuntimeException("unimplemented"); } @@ -272,6 +276,14 @@ public class AspectDeclaration extends TypeDeclaration { generatePerObjectAspectOfMethod(classFile, interfaceType); generatePerObjectHasAspectMethod(classFile, interfaceType); generatePerObjectBindMethod(classFile, interfaceType); + } else if (perClause.getKind() == PerClause.PERTYPEWITHIN) { + //PTWIMPL Generate the methods required *in the aspect* + generatePerTypeWithinAspectOfMethod(classFile); // public static aspectOf(java.lang.Class) + generatePerTypeWithinGetInstanceMethod(classFile); // private static ajc$getInstance(Class c) throws Exception + generatePerTypeWithinHasAspectMethod(classFile); + generatePerTypeWithinCreateAspectInstanceMethod(classFile); // generate public static X ajc$createAspectInstance(Class forClass) { + // PTWIMPL getWithinType() would need this... + // generatePerTypeWithinGetWithinTypeMethod(classFile); // generate public Class getWithinType() { } else { throw new RuntimeException("unimplemented"); } @@ -450,6 +462,46 @@ public class AspectDeclaration extends TypeDeclaration { } + // PTWIMPL Generate aspectOf() method + private void generatePerTypeWithinAspectOfMethod(ClassFile classFile) { + final EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(this.scope); + generateMethod(classFile, aspectOfMethod, new BodyGenerator() { + public void generate(CodeStream codeStream) { + + Label instanceFound = new Label(codeStream); + + ExceptionLabel anythingGoesWrong = new ExceptionLabel(codeStream,world.makeTypeBinding(TypeX.JAVA_LANG_EXCEPTION)); + codeStream.aload_0(); + codeStream.invokestatic(world.makeMethodBindingForCall(AjcMemberMaker.perTypeWithinGetInstance(typeX))); + codeStream.astore_1(); + codeStream.aload_1(); + codeStream.ifnonnull(instanceFound); + codeStream.new_(world.makeTypeBinding(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION)); + codeStream.dup(); + + codeStream.ldc(typeX.getName()); + codeStream.aconst_null(); + + codeStream.invokespecial(world.makeMethodBindingForCall(AjcMemberMaker.noAspectBoundExceptionInit2())); + codeStream.athrow(); + instanceFound.place(); + codeStream.aload_1(); + + codeStream.areturn(); + anythingGoesWrong.placeEnd(); + anythingGoesWrong.place(); + + codeStream.astore_1(); + codeStream.new_(world.makeTypeBinding(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION)); + + codeStream.dup(); + + // Run the simple ctor for NABE + codeStream.invokespecial(world.makeMethodBindingForCall(AjcMemberMaker.noAspectBoundExceptionInit())); + codeStream.athrow(); + }}); + } + private void generatePerObjectAspectOfMethod( ClassFile classFile, final TypeBinding interfaceType) @@ -513,6 +565,32 @@ public class AspectDeclaration extends TypeDeclaration { }}); } + // PTWIMPL Generate hasAspect() method + private void generatePerTypeWithinHasAspectMethod(ClassFile classFile) { + final EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(this.scope); + generateMethod(classFile, hasAspectMethod, new BodyGenerator() { + public void generate(CodeStream codeStream) { + ExceptionLabel goneBang = new ExceptionLabel(codeStream,world.makeTypeBinding(TypeX.JAVA_LANG_EXCEPTION)); + Label noInstanceExists = new Label(codeStream); + Label leave = new Label(codeStream); + goneBang.placeStart(); + codeStream.aload_0(); + codeStream.invokestatic(world.makeMethodBinding(AjcMemberMaker.perTypeWithinGetInstance(typeX))); + codeStream.ifnull(noInstanceExists); + codeStream.iconst_1(); + codeStream.goto_(leave); + noInstanceExists.place(); + codeStream.iconst_0(); + leave.place(); + goneBang.placeEnd(); + codeStream.ireturn(); + goneBang.place(); + codeStream.astore_1(); + codeStream.iconst_0(); + codeStream.ireturn(); + }}); + } + private void generatePerObjectBindMethod( ClassFile classFile, final TypeBinding interfaceType) @@ -550,6 +628,70 @@ public class AspectDeclaration extends TypeDeclaration { }}); } + // PTWIMPL Generate getInstance method + private void generatePerTypeWithinGetInstanceMethod(ClassFile classFile) { + final EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(this.scope); + generateMethod(classFile, AjcMemberMaker.perTypeWithinGetInstance(EclipseFactory.fromBinding(binding)), + new BodyGenerator() { + public void generate(CodeStream codeStream) { + ExceptionLabel exc = new ExceptionLabel(codeStream,world.makeTypeBinding(TypeX.JAVA_LANG_EXCEPTION)); + exc.placeStart(); + codeStream.aload_0(); + codeStream.ldc(NameMangler.perTypeWithinLocalAspectOf(typeX)); + codeStream.aconst_null(); + codeStream.invokevirtual( + new MethodBinding( + 0, + "getDeclaredMethod".toCharArray(), + world.makeTypeBinding(TypeX.forSignature("Ljava/lang/reflect/Method;")), // return type + new TypeBinding[]{world.makeTypeBinding(TypeX.forSignature("Ljava/lang/String;")), + world.makeTypeBinding(TypeX.forSignature("[Ljava/lang/Class;"))}, + new ReferenceBinding[0], + (ReferenceBinding)world.makeTypeBinding(TypeX.JAVA_LANG_CLASS))); + codeStream.astore_1(); + codeStream.aload_1(); + codeStream.aconst_null(); + codeStream.aconst_null(); + codeStream.invokevirtual( + new MethodBinding( + 0, + "invoke".toCharArray(), + world.makeTypeBinding(TypeX.OBJECT), + new TypeBinding[]{world.makeTypeBinding(TypeX.OBJECT),world.makeTypeBinding(TypeX.forSignature("[Ljava/lang/Object;"))}, + new ReferenceBinding[0], + (ReferenceBinding)world.makeTypeBinding(TypeX.JAVA_LANG_REFLECT_METHOD))); + codeStream.checkcast(world.makeTypeBinding(typeX)); + codeStream.astore_2(); + codeStream.aload_2(); + exc.placeEnd(); + codeStream.areturn(); + exc.place(); + codeStream.astore_1(); + codeStream.aload_1(); + codeStream.athrow(); + }}); + } + + private void generatePerTypeWithinCreateAspectInstanceMethod(ClassFile classFile) { + final EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(this.scope); + generateMethod(classFile, AjcMemberMaker.perTypeWithinCreateAspectInstance(EclipseFactory.fromBinding(binding)), + new BodyGenerator() { + public void generate(CodeStream codeStream) { + + codeStream.new_(world.makeTypeBinding(typeX)); + codeStream.dup(); + codeStream.invokespecial(new MethodBinding(0, "".toCharArray(), + BaseTypes.VoidBinding, new TypeBinding[0], + new ReferenceBinding[0], binding)); + codeStream.astore_1(); + codeStream.aload_1(); + codeStream.aload_0(); + codeStream.putfield(world.makeFieldBinding(AjcMemberMaker.perTypeWithinWithinTypeField(typeX,typeX))); + codeStream.aload_1(); + codeStream.areturn(); + }}); + } + private void generatePerSingletonAspectOfMethod(ClassFile classFile) { @@ -785,6 +927,10 @@ public class AspectDeclaration extends TypeDeclaration { } else if (perClause.getKind() == PerClause.PEROBJECT) { aspectOfMethod = AjcMemberMaker.perObjectAspectOfMethod(typeX); hasAspectMethod = AjcMemberMaker.perObjectHasAspectMethod(typeX); + } else if (perClause.getKind() == PerClause.PERTYPEWITHIN) { + // PTWIMPL Use these variants of aspectOf()/hasAspect() + aspectOfMethod = AjcMemberMaker.perTypeWithinAspectOfMethod(typeX); + hasAspectMethod = AjcMemberMaker.perTypeWithinHasAspectMethod(typeX); } else { throw new RuntimeException("bad per clause: " + perClause); } diff --git a/tests/src/org/aspectj/systemtest/ajc150/AllTestsAspectJ150.java b/tests/src/org/aspectj/systemtest/ajc150/AllTestsAspectJ150.java index d138eb64b..fa0cff9c5 100644 --- a/tests/src/org/aspectj/systemtest/ajc150/AllTestsAspectJ150.java +++ b/tests/src/org/aspectj/systemtest/ajc150/AllTestsAspectJ150.java @@ -13,12 +13,6 @@ package org.aspectj.systemtest.ajc150; import junit.framework.Test; import junit.framework.TestSuite; -/** - * @author colyer - * - * TODO To change the template for this generated type comment go to - * Window - Preferences - Java - Code Style - Code Templates - */ public class AllTestsAspectJ150 { public static Test suite() { @@ -36,6 +30,7 @@ public class AllTestsAspectJ150 { suite.addTestSuite(AnnotationPointcutsTests.class); suite.addTestSuite(VarargsTests.class); suite.addTestSuite(AnnotationRuntimeTests.class); + suite.addTestSuite(PerTypeWithinTests.class); //$JUnit-END$ diff --git a/tests/src/org/aspectj/systemtest/ajc150/PerTypeWithinTests.java b/tests/src/org/aspectj/systemtest/ajc150/PerTypeWithinTests.java new file mode 100644 index 000000000..6c61fcad1 --- /dev/null +++ b/tests/src/org/aspectj/systemtest/ajc150/PerTypeWithinTests.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 2005 IBM + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Andy Clement - initial API and implementation + *******************************************************************************/ +package org.aspectj.systemtest.ajc150; + +import java.io.File; +import java.util.Iterator; +import java.util.List; + +import org.aspectj.bridge.IMessage; +import org.aspectj.tools.ajc.CompilationResult; + + + +public class PerTypeWithinTests extends TestUtils { + + protected void setUp() throws Exception { + super.setUp(); + baseDir = new File("../tests/java5/pertypewithin"); + } + + /** + * First few tests: + * + * Five types p.A, p.B, p.C, q.D, q.E and an aspect a.X. + * + * The aspect is pertypewithin(p..*) - this should match A,B,C but not D,E. + * + * Aspect instances should be accessible for A,B,C but not D,E. + * The aspect instances for A,B,C should be different. + * + * hasAspect(), aspectOf() should work. + * + * We test these assumptions in A,B,C,D,E. + */ + public void testDoesItWorkAtAll() { + CompilationResult cR=ajc(baseDir,new String[]{"A.java","B.java","C.java","D.java","Main.java","X.java"}); + assertTrue("Expected no errors:"+cR,!cR.hasErrorMessages()); + // if (verbose) { System.err.println(cR); System.err.println(cR.getStandardError());} + RunResult rR = run("p.A"); + // if (verbose) {System.err.println(rR.getStdErr());} + assertTrue("Expected a report from the aspect about 2 calls to sayhi():"+rR.getStdErr(), + rR.getStdErr().indexOf("callcount = 2")!=-1); + } + + public void testCheckHasAspectWorks() { + CompilationResult cR=ajc(baseDir,new String[]{"A.java","B.java","C.java","D.java","Main.java","X.java"}); + assertTrue("Expected no errors:"+cR,!cR.hasErrorMessages()); + // if (verbose) { System.err.println(cR); System.err.println(cR.getStandardError());} + RunResult rR = run("p.A"); + rR = run("p.B"); + // if (verbose) {System.err.println(rR.getStdErr());} + assertTrue("Expected a report from the aspect about 3 calls to sayhi():"+rR.getStdErr(), + rR.getStdErr().indexOf("callcount = 3")!=-1); + } + + public void testCheckAspectOfWorks() { + CompilationResult cR=ajc(baseDir,new String[]{"A.java","B.java","C.java","D.java","Main.java","X.java"}); + assertTrue("Expected no errors:"+cR,!cR.hasErrorMessages()); + // if (verbose) { System.err.println(cR); System.err.println(cR.getStandardError());} + RunResult rR = run("p.A"); + rR = run("p.C"); + // if (verbose) {System.err.println(rR.getStdErr());} + + } + + /** + * Aspects Q and R match P with a pertypewithin() - they shouldn't clash in any way + * + */ + public void testTwoAspectsHittingOneType() { + CompilationResult cR=ajc(baseDir,new String[]{"P.java","Q.java","R.java"}); + assertTrue("Expected no errors:"+cR,!cR.hasErrorMessages()); + // if (verbose) { System.err.println(cR); System.err.println(cR.getStandardError());} + RunResult rR = run("P"); + // if (verbose) {System.err.println(rR.getStdErr());} + assertTrue("Expected message about Q reporting 2: "+rR.getStdErr(), + rR.getStdErr().indexOf("Q reporting 2")!=-1); + assertTrue("Expected message about R reporting 3: "+rR.getStdErr(), + rR.getStdErr().indexOf("R reporting 3")!=-1); + } + + /** + * Checks the use of pertypewithin() doesn't result in extra join points (i.e. the + * infrastructure is properly hidden in ajc$ or synthetic members) + */ + public void testPervasivenessOfWeaving() { + CompilationResult cR = ajc(baseDir,new String[]{"U.java","-showWeaveInfo"}); + List l = cR.getInfoMessages(); + int cnt = 0; + for (Iterator iter = l.iterator(); iter.hasNext();) { + IMessage element = (IMessage) iter.next(); + if (element.getKind()==IMessage.WEAVEINFO) { + //System.err.println(element); + cnt++; + } + } + int weavingMessagesFromNormalDeploymentModel = cnt; + //System.err.println(cnt); + + cR = ajc(baseDir,new String[]{"V.java","-showWeaveInfo"}); + l = cR.getInfoMessages(); + cnt = 0; + for (Iterator iter = l.iterator(); iter.hasNext();) { + IMessage element = (IMessage) iter.next(); + if (element.getKind()==IMessage.WEAVEINFO) { + //System.err.println(element); + cnt++; + } + } + int weavingMessagesFromPerTypeWithin = cnt; + //System.err.println(cnt); + if (weavingMessagesFromNormalDeploymentModel!=weavingMessagesFromPerTypeWithin) + fail("Expected same number of messages regardless of perclause but got "+ + weavingMessagesFromNormalDeploymentModel+" and "+weavingMessagesFromPerTypeWithin); + + } + + public void testBinaryWeaving_ClassesAreBinary() { + // Compile the 'ordinary' class G.java into classes + CompilationResult cR = ajc(baseDir,new String[]{"G.java","-d","classes2"}); + setShouldEmptySandbox(false); + // Compile the aspect with G.class as input, should be binary woven correctly + cR = ajc(baseDir,new String[]{"H.java","-inpath","classes2"}); + RunResult rR = run("G"); + assertTrue("Expected aspect related message 'advice running' in output from G", + rR.getStdErr().indexOf("advice running")!=-1); + setShouldEmptySandbox(true); + } + + public void testBinaryWeaving_AspectsAreBinary() { + // Compile the aspect H.java into classes3 + CompilationResult cR = ajc(baseDir,new String[]{"H.java","-outjar","aspects.jar"}); + setShouldEmptySandbox(false); + // Compile the class with H.class as aspectpath, should be binary woven correctly + cR = ajc(baseDir,new String[]{"G.java","-aspectpath","aspects.jar"}); + RunResult rR = run("G"); + assertTrue("Expected aspect related message 'advice running' in output from G", + rR.getStdErr().indexOf("advice running")!=-1); + setShouldEmptySandbox(true); + } + + // binary weaving case ... +} \ No newline at end of file diff --git a/weaver/src/org/aspectj/weaver/Advice.java b/weaver/src/org/aspectj/weaver/Advice.java index 11b8f69c7..a6cd8f412 100644 --- a/weaver/src/org/aspectj/weaver/Advice.java +++ b/weaver/src/org/aspectj/weaver/Advice.java @@ -67,6 +67,13 @@ public abstract class Advice extends ShadowMunger { return ret; } + // PTWIMPL per type within entry advice is what initializes the aspect instance in the matched type + public static Advice makePerTypeWithinEntry(World world, Pointcut p, ResolvedTypeX inAspect) { + Advice ret = world.concreteAdvice(AdviceKind.PerTypeWithinEntry,p,null,0,p); + ret.concreteAspect = inAspect; + return ret; + } + public static Advice makeSoftener(World world, Pointcut entry, TypePattern exceptionType,ResolvedTypeX inAspect,IHasSourceLocation loc) { Advice ret = world.concreteAdvice(AdviceKind.Softener, entry, null, 0, loc); diff --git a/weaver/src/org/aspectj/weaver/AdviceKind.java b/weaver/src/org/aspectj/weaver/AdviceKind.java index abc560386..57851fd64 100644 --- a/weaver/src/org/aspectj/weaver/AdviceKind.java +++ b/weaver/src/org/aspectj/weaver/AdviceKind.java @@ -53,6 +53,8 @@ public class AdviceKind extends TypeSafeEnum { case 12: return PerTargetEntry; case 13: return Softener; + + case 14: return PerTypeWithinEntry; } throw new RuntimeException("unimplemented kind: " + key); } @@ -75,6 +77,9 @@ public class AdviceKind extends TypeSafeEnum { public static final AdviceKind PerTargetEntry = new AdviceKind("perTargetEntry", 12, 1, false, false); public static final AdviceKind Softener = new AdviceKind("softener", 13, 1, false, false); + + // PTWIMPL Advice representing when aspect should be initialized + public static final AdviceKind PerTypeWithinEntry = new AdviceKind("perTypeWithinEntry",14,1,false,false); public static AdviceKind stringToKind(String s) { @@ -99,8 +104,10 @@ public class AdviceKind extends TypeSafeEnum { } public boolean isPerEntry() { - return this == PerCflowEntry || this == PerCflowBelowEntry || - this == PerThisEntry || this == PerTargetEntry; + return + this == PerCflowEntry || this == PerCflowBelowEntry || + this == PerThisEntry || this == PerTargetEntry || + this == PerTypeWithinEntry; // PTWIMPL Allow for PTW case } public boolean isPerObjectEntry() { diff --git a/weaver/src/org/aspectj/weaver/AjcMemberMaker.java b/weaver/src/org/aspectj/weaver/AjcMemberMaker.java index f19776f05..d285a1574 100644 --- a/weaver/src/org/aspectj/weaver/AjcMemberMaker.java +++ b/weaver/src/org/aspectj/weaver/AjcMemberMaker.java @@ -68,6 +68,15 @@ public class AjcMemberMaker { "", "()V"); } + + public static Member noAspectBoundExceptionInit2() { + return new ResolvedMember( + Member.METHOD, + NO_ASPECT_BOUND_EXCEPTION, + Modifier.PUBLIC, + "", + "(Ljava/lang/String;Ljava/lang/Throwable;)V"); + } public static Member noAspectBoundExceptionInitWithCause() { return new ResolvedMember( @@ -129,6 +138,27 @@ public class AjcMemberMaker { NameMangler.perObjectInterfaceField(aspectType), TypeX.NONE); } + + // PTWIMPL ResolvedMember for aspect instance field, declared in matched type + public static ResolvedMember perTypeWithinField(TypeX declaringType, ResolvedTypeX aspectType) { + int modifiers = Modifier.PRIVATE | Modifier.STATIC; + if (!TypeX.SERIALIZABLE.isAssignableFrom(aspectType, aspectType.getWorld())) { + modifiers |= Modifier.TRANSIENT; + } + return new ResolvedMember(Member.FIELD, declaringType, modifiers, + aspectType, NameMangler.perTypeWithinFieldForTarget(aspectType), TypeX.NONE); + } + + // PTWIMPL ResolvedMember for type instance field, declared in aspect + // (holds typename for which aspect instance exists) + public static ResolvedMember perTypeWithinWithinTypeField(TypeX declaringType, ResolvedTypeX aspectType) { + int modifiers = Modifier.PRIVATE; + if (!TypeX.SERIALIZABLE.isAssignableFrom(aspectType, aspectType.getWorld())) { + modifiers |= Modifier.TRANSIENT; + } + return new ResolvedMember(Member.FIELD, declaringType, modifiers, + TypeX.forSignature("Ljava/lang/String;"), NameMangler.PERTYPEWITHIN_WITHINTYPEFIELD, TypeX.NONE); + } public static ResolvedMember perObjectBind(TypeX declaringType) { @@ -139,6 +169,34 @@ public class AjcMemberMaker { NameMangler.PEROBJECT_BIND_METHOD, "(Ljava/lang/Object;)V"); } + + // PTWIMPL ResolvedMember for getInstance() method, declared in aspect + public static ResolvedMember perTypeWithinGetInstance(TypeX declaringType) { +// private static a.X ajc$getInstance(java.lang.Class) throws java/lang/Exception + ResolvedMember rm = new ResolvedMember( + Member.METHOD, + declaringType, + PRIVATE_STATIC, + declaringType, // return value + NameMangler.PERTYPEWITHIN_GETINSTANCE_METHOD, + new TypeX[]{TypeX.JAVA_LANG_CLASS}, + new TypeX[]{TypeX.JAVA_LANG_EXCEPTION} + ); + return rm; + } + + public static ResolvedMember perTypeWithinCreateAspectInstance(TypeX declaringType) { + // public static a.X ajc$createAspectInstance(java.lang.String) + ResolvedMember rm = new ResolvedMember( + Member.METHOD, + declaringType, + PUBLIC_STATIC, + declaringType, // return value + NameMangler.PERTYPEWITHIN_CREATEASPECTINSTANCE_METHOD, + new TypeX[]{TypeX.forSignature("Ljava/lang/String;")},new TypeX[]{} + ); + return rm; + } public static TypeX perObjectInterfaceType(TypeX aspectType) { @@ -163,8 +221,16 @@ public class AjcMemberMaker { "(" + aspectType.getSignature() + ")V"); } - - + // PTWIMPL ResolvedMember for localAspectOf() method, declared in matched type + public static ResolvedMember perTypeWithinLocalAspectOf(TypeX shadowType,TypeX aspectType) { + return new ResolvedMember( + Member.METHOD, + shadowType,//perTypeWithinInterfaceType(aspectType), + Modifier.PUBLIC | Modifier.STATIC, + NameMangler.perTypeWithinLocalAspectOf(aspectType), + "()" + aspectType.getSignature()); + } + public static ResolvedMember perSingletonAspectOfMethod(TypeX declaringType) { return new ResolvedMember(Member.METHOD, @@ -198,6 +264,20 @@ public class AjcMemberMaker { "(Ljava/lang/Object;)Z"); }; + // PTWIMPL ResolvedMember for aspectOf(), declared in aspect + public static ResolvedMember perTypeWithinAspectOfMethod(TypeX declaringType) { + return new ResolvedMember(Member.METHOD, + declaringType, PUBLIC_STATIC, "aspectOf", + "(Ljava/lang/Class;)" + declaringType.getSignature()); + } + + // PTWIMPL ResolvedMember for hasAspect(), declared in aspect + public static ResolvedMember perTypeWithinHasAspectMethod(TypeX declaringType) { + return new ResolvedMember(Member.METHOD, + declaringType, PUBLIC_STATIC, "hasAspect", + "(Ljava/lang/Class;)Z"); + }; + // -- privileged accessors public static ResolvedMember privilegedAccessMethodForMethod(TypeX aspectType, ResolvedMember method) { diff --git a/weaver/src/org/aspectj/weaver/NameMangler.java b/weaver/src/org/aspectj/weaver/NameMangler.java index 6d49a45fb..1888faef8 100644 --- a/weaver/src/org/aspectj/weaver/NameMangler.java +++ b/weaver/src/org/aspectj/weaver/NameMangler.java @@ -37,7 +37,12 @@ public class NameMangler { public static final String PERCFLOW_PUSH_METHOD = PREFIX + "perCflowPush"; public static final String PEROBJECT_BIND_METHOD = PREFIX + "perObjectBind"; - + + // PTWIMPL Method and field names + public static final String PERTYPEWITHIN_GETINSTANCE_METHOD = PREFIX + "getInstance"; + public static final String PERTYPEWITHIN_CREATEASPECTINSTANCE_METHOD = PREFIX + "createAspectInstance"; + public static final String PERTYPEWITHIN_WITHINTYPEFIELD = PREFIX + "withinType"; + public static final String AJC_PRE_CLINIT_NAME = PREFIX + "preClinit"; public static final String AJC_POST_CLINIT_NAME = PREFIX + "postClinit"; @@ -57,6 +62,17 @@ public class NameMangler { } + // PTWIMPL method names that must include aspect type + public static String perTypeWithinFieldForTarget(TypeX aspectType) { + String s = makeName(aspectType.getNameAsIdentifier(), "ptwAspectInstance"); + return s; + } + + public static String perTypeWithinLocalAspectOf(TypeX aspectType) { + return makeName(aspectType.getNameAsIdentifier(), "localAspectOf"); + } + + public static String privilegedAccessMethodForMethod(String name, TypeX objectType, TypeX aspectType) { return makeName("privMethod", aspectType.getNameAsIdentifier(), diff --git a/weaver/src/org/aspectj/weaver/PerTypeWithinTargetTypeMunger.java b/weaver/src/org/aspectj/weaver/PerTypeWithinTargetTypeMunger.java new file mode 100644 index 000000000..4da7f1116 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/PerTypeWithinTargetTypeMunger.java @@ -0,0 +1,52 @@ +/* ******************************************************************* + * Copyright (c) 2005 + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Andy Clement initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.DataOutputStream; +import java.io.IOException; + +import org.aspectj.weaver.patterns.PerTypeWithin; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.TypePattern; + +// PTWIMPL Target type munger adds the localAspectOf() method +public class PerTypeWithinTargetTypeMunger extends ResolvedTypeMunger { + private ResolvedMember localAspectOfMethod; + private TypeX aspectType; + private PerTypeWithin testPointcut; + + + public PerTypeWithinTargetTypeMunger(TypeX aspectType, PerTypeWithin testPointcut) { + super(PerTypeWithinInterface, null); + this.aspectType = aspectType; + this.testPointcut = testPointcut; + } + + + public void write(DataOutputStream s) throws IOException { + throw new RuntimeException("shouldn't be serialized"); + } + + public TypeX getAspectType() { + return aspectType; + } + + public Pointcut getTestPointcut() { + return testPointcut; + } + + public boolean matches(ResolvedTypeX matchType, ResolvedTypeX aspectType) { + return testPointcut.getTypePattern().matches(matchType,TypePattern.STATIC).alwaysTrue(); + } + +} diff --git a/weaver/src/org/aspectj/weaver/ResolvedTypeMunger.java b/weaver/src/org/aspectj/weaver/ResolvedTypeMunger.java index f28e87535..17c87c9d3 100644 --- a/weaver/src/org/aspectj/weaver/ResolvedTypeMunger.java +++ b/weaver/src/org/aspectj/weaver/ResolvedTypeMunger.java @@ -224,6 +224,7 @@ public abstract class ResolvedTypeMunger { public static final Kind PrivilegedAccess = new Kind("PrivilegedAccess", 4); public static final Kind Parent = new Kind("Parent", 6); + public static final Kind PerTypeWithinInterface = new Kind("PerTypeWithinInterface",7); // PTWIMPL not serialized, used during concretization of aspects public static final String SUPER_DISPATCH_NAME = "superDispatch"; diff --git a/weaver/src/org/aspectj/weaver/TypeX.java b/weaver/src/org/aspectj/weaver/TypeX.java index c13349eb3..3dad1b8fa 100644 --- a/weaver/src/org/aspectj/weaver/TypeX.java +++ b/weaver/src/org/aspectj/weaver/TypeX.java @@ -584,6 +584,9 @@ public class TypeX implements AnnotatedElement { public static final TypeX AT_RETENTION = forSignature("Ljava/lang/annotation/Retention;"); public static final TypeX ENUM = forSignature("Ljava/lang/Enum;"); public static final TypeX ANNOTATION = forSignature("Ljava/lang/annotation/Annotation;"); + public static final TypeX JAVA_LANG_CLASS = forSignature("Ljava/lang/Class;"); + public static final TypeX JAVA_LANG_EXCEPTION = forSignature("Ljava/lang/Exception;"); + public static final TypeX JAVA_LANG_REFLECT_METHOD = forSignature("Ljava/lang/reflect/Method;"); // ---- helpers diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelAdvice.java b/weaver/src/org/aspectj/weaver/bcel/BcelAdvice.java index 76589720f..344602d97 100644 --- a/weaver/src/org/aspectj/weaver/bcel/BcelAdvice.java +++ b/weaver/src/org/aspectj/weaver/bcel/BcelAdvice.java @@ -160,6 +160,9 @@ public class BcelAdvice extends Advice { shadow.weavePerObjectEntry(this, (BcelVar)shadow.getTargetVar()); } else if (getKind() == AdviceKind.Softener) { shadow.weaveSoftener(this, ((ExactTypePattern)exceptionType).getType()); + } else if (getKind() == AdviceKind.PerTypeWithinEntry) { + // PTWIMPL Entry to ptw is the static initialization of a type that matched the ptw type pattern + shadow.weavePerTypeWithinAspectInitialization(this,shadow.getEnclosingType()); } else { throw new BCException("unimplemented kind: " + getKind()); } diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelShadow.java b/weaver/src/org/aspectj/weaver/bcel/BcelShadow.java index fe32b0aa6..aa03a51ab 100644 --- a/weaver/src/org/aspectj/weaver/bcel/BcelShadow.java +++ b/weaver/src/org/aspectj/weaver/bcel/BcelShadow.java @@ -43,6 +43,7 @@ import org.aspectj.apache.bcel.generic.LoadInstruction; import org.aspectj.apache.bcel.generic.MULTIANEWARRAY; import org.aspectj.apache.bcel.generic.NEW; import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.PUSH; import org.aspectj.apache.bcel.generic.ReturnInstruction; import org.aspectj.apache.bcel.generic.SWAP; import org.aspectj.apache.bcel.generic.StoreInstruction; @@ -1510,6 +1511,32 @@ public class BcelShadow extends Shadow { range.insert(entryInstructions, Range.InsideBefore); } + + // PTWIMPL Create static initializer to call the aspect factory + /** + * Causes the aspect instance to be *set* for later retrievable through localAspectof()/aspectOf() + */ + public void weavePerTypeWithinAspectInitialization(final BcelAdvice munger,TypeX t) { + final InstructionFactory fact = getFactory(); + + InstructionList entryInstructions = new InstructionList(); + InstructionList entrySuccessInstructions = new InstructionList(); + + BcelObjectType aspectType = BcelWorld.getBcelObjectType(munger.getConcreteAspect()); + String aspectname = munger.getConcreteAspect().getName(); + + String ptwField = NameMangler.perTypeWithinFieldForTarget(munger.getConcreteAspect()); + entrySuccessInstructions.append(new PUSH(fact.getConstantPool(),t.getName())); + + entrySuccessInstructions.append(fact.createInvoke(aspectname,"ajc$createAspectInstance",new ObjectType(aspectname), + new Type[]{new ObjectType("java.lang.String")},Constants.INVOKESTATIC)); + entrySuccessInstructions.append(fact.createPutStatic(t.getName(),ptwField, + new ObjectType(aspectname))); + + entryInstructions.append(entrySuccessInstructions); + + range.insert(entryInstructions, Range.InsideBefore); + } public void weaveCflowEntry(final BcelAdvice munger, final Member cflowField) { diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java b/weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java index 935d2f966..d8e755a8c 100644 --- a/weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java +++ b/weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java @@ -42,6 +42,7 @@ import org.aspectj.weaver.NewFieldTypeMunger; import org.aspectj.weaver.NewMethodTypeMunger; import org.aspectj.weaver.NewParentTypeMunger; import org.aspectj.weaver.PerObjectInterfaceTypeMunger; +//import org.aspectj.weaver.PerTypeWithinTargetTypeMunger; import org.aspectj.weaver.PrivilegedAccessMunger; import org.aspectj.weaver.ResolvedMember; import org.aspectj.weaver.ResolvedTypeMunger; @@ -74,6 +75,10 @@ public class BcelTypeMunger extends ConcreteTypeMunger { } 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; @@ -646,7 +651,42 @@ public class BcelTypeMunger extends ConcreteTypeMunger { 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.getField(),getSourceLocation()); + + // Add an accessor for this new field, the ajc$$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), + new Type[0], 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) { diff --git a/weaver/src/org/aspectj/weaver/patterns/PatternParser.java b/weaver/src/org/aspectj/weaver/patterns/PatternParser.java index 9b994deb8..575bcc7e1 100644 --- a/weaver/src/org/aspectj/weaver/patterns/PatternParser.java +++ b/weaver/src/org/aspectj/weaver/patterns/PatternParser.java @@ -51,6 +51,8 @@ public class PatternParser { return parsePerCflow(false); } else if (name.equals("percflowbelow")) { return parsePerCflow(true); + } else if (name.equals("pertypewithin")) { // PTWIMPL Parse the pertypewithin clause + return parsePerTypeWithin(); } else { return null; } @@ -74,6 +76,14 @@ public class PatternParser { eat(")"); return new PerObject(entry, isThis); } + + private PerClause parsePerTypeWithin() { + parseIdentifier(); + eat("("); + TypePattern withinTypePattern = parseTypePattern(); + eat(")"); + return new PerTypeWithin(withinTypePattern); + } private PerClause parsePerSingleton() { diff --git a/weaver/src/org/aspectj/weaver/patterns/PerClause.java b/weaver/src/org/aspectj/weaver/patterns/PerClause.java index 5bff424eb..8b59094d1 100644 --- a/weaver/src/org/aspectj/weaver/patterns/PerClause.java +++ b/weaver/src/org/aspectj/weaver/patterns/PerClause.java @@ -18,6 +18,7 @@ import java.io.IOException; import org.aspectj.util.TypeSafeEnum; import org.aspectj.weaver.*; +// PTWIMPL New kind added to this class, can be (de)serialized public abstract class PerClause extends Pointcut { protected ResolvedTypeX inAspect; @@ -27,6 +28,7 @@ public abstract class PerClause extends Pointcut { else if (kind == PERCFLOW) return PerCflow.readPerClause(s, context); else if (kind == PEROBJECT) return PerObject.readPerClause(s, context); else if (kind == FROMSUPER) return PerFromSuper.readPerClause(s, context); + else if (kind == PERTYPEWITHIN) return PerTypeWithin.readPerClause(s,context); throw new BCException("unknown kind: " + kind); } @@ -49,6 +51,7 @@ public abstract class PerClause extends Pointcut { case 2: return PERCFLOW; case 3: return PEROBJECT; case 4: return FROMSUPER; + case 5: return PERTYPEWITHIN; } throw new BCException("weird kind " + key); } @@ -62,4 +65,5 @@ public abstract class PerClause extends Pointcut { public static final Kind PERCFLOW = new Kind("percflow", 2); public static final Kind PEROBJECT = new Kind("perobject", 3); public static final Kind FROMSUPER = new Kind("fromsuper", 4); + public static final Kind PERTYPEWITHIN = new Kind("pertypewithin",5); } diff --git a/weaver/src/org/aspectj/weaver/patterns/PerTypeWithin.java b/weaver/src/org/aspectj/weaver/patterns/PerTypeWithin.java new file mode 100644 index 000000000..4e29e138b --- /dev/null +++ b/weaver/src/org/aspectj/weaver/patterns/PerTypeWithin.java @@ -0,0 +1,175 @@ +/* ******************************************************************* + * Copyright (c) 2005 IBM + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Andy Clement initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Member; +//import org.aspectj.weaver.PerTypeWithinTargetTypeMunger; +import org.aspectj.weaver.PerTypeWithinTargetTypeMunger; +import org.aspectj.weaver.ResolvedTypeMunger; +import org.aspectj.weaver.ResolvedTypeX; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + + +// PTWIMPL Represents a parsed pertypewithin() +public class PerTypeWithin extends PerClause { + + private TypePattern typePattern; + + // Any shadow could be considered within a pertypewithin() type pattern + private static final Set kindSet = new HashSet(Shadow.ALL_SHADOW_KINDS); + + public TypePattern getTypePattern() { + return typePattern; + } + + public PerTypeWithin(TypePattern p) { + this.typePattern = p; + } + + public Set couldMatchKinds() { + return kindSet; + } + + // ----- + public FuzzyBoolean fastMatch(FastMatchInfo info) { + if (typePattern.annotationPattern instanceof AnyAnnotationTypePattern) { + return isWithinType(info.getType()); + } + return FuzzyBoolean.MAYBE; + } + + + protected FuzzyBoolean matchInternal(Shadow shadow) { + ResolvedTypeX enclosingType = shadow.getIWorld().resolve(shadow.getEnclosingType(),true); + if (enclosingType == ResolvedTypeX.MISSING) { + //PTWIMPL ?? Add a proper message + IMessage msg = new Message( + "Cant find type pertypewithin matching...", + shadow.getSourceLocation(),true,new ISourceLocation[]{getSourceLocation()}); + shadow.getIWorld().getMessageHandler().handleMessage(msg); + } + typePattern.resolve(shadow.getIWorld()); + return isWithinType(enclosingType); + } + + public void resolveBindings(IScope scope, Bindings bindings) { + typePattern = typePattern.resolveBindings(scope, bindings, false, false); + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + Member ptwField = AjcMemberMaker.perTypeWithinField(shadow.getEnclosingType(),inAspect); + + Expr myInstance = + Expr.makeCallExpr(AjcMemberMaker.perTypeWithinLocalAspectOf(shadow.getEnclosingType(),inAspect/*shadow.getEnclosingType()*/), + Expr.NONE,inAspect); + state.setAspectInstance(myInstance); + + // this worked at one point + //Expr myInstance = Expr.makeFieldGet(ptwField,shadow.getEnclosingType().resolve(shadow.getIWorld()));//inAspect); + //state.setAspectInstance(myInstance); + + +// return Test.makeFieldGetCall(ptwField,null,Expr.NONE); + // cflowField, cflowCounterIsValidMethod, Expr.NONE + + // This is what is in the perObject variant of this ... +// Expr myInstance = +// Expr.makeCallExpr(AjcMemberMaker.perTypeWithinAspectOfMethod(inAspect), +// new Expr[] {getVar(shadow)}, inAspect); +// state.setAspectInstance(myInstance); +// return Test.makeCall(AjcMemberMaker.perTypeWithinHasAspectMethod(inAspect), +// new Expr[] { getVar(shadow) }); +// + + + return match(shadow).alwaysTrue()?Literal.TRUE:Literal.FALSE; + } + + + public PerClause concretize(ResolvedTypeX inAspect) { + PerTypeWithin ret = new PerTypeWithin(typePattern); + ret.copyLocationFrom(this); + ret.inAspect = inAspect; + if (inAspect.isAbstract()) return ret; + + + World world = inAspect.getWorld(); + + SignaturePattern sigpat = new SignaturePattern( + Member.STATIC_INITIALIZATION, + ModifiersPattern.ANY, + TypePattern.ANY, + typePattern, + NamePattern.ANY, + TypePatternList.ANY, + ThrowsPattern.ANY, + AnnotationTypePattern.ANY + ); + Pointcut testPc = new KindedPointcut(Shadow.StaticInitialization,sigpat); + Pointcut testPc2= new WithinPointcut(typePattern); + // This munger will initialize the aspect instance field in the matched type + inAspect.crosscuttingMembers.addConcreteShadowMunger(Advice.makePerTypeWithinEntry(world, testPc, inAspect)); + + ResolvedTypeMunger munger = new PerTypeWithinTargetTypeMunger(inAspect, ret); + inAspect.crosscuttingMembers.addTypeMunger(world.concreteTypeMunger(munger, inAspect)); + return ret; + + } + + public void write(DataOutputStream s) throws IOException { + PERTYPEWITHIN.write(s); + typePattern.write(s); + writeLocation(s); + } + + public static PerClause readPerClause(VersionedDataInputStream s, ISourceContext context) throws IOException { + PerClause ret = new PerTypeWithin(TypePattern.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + public PerClause.Kind getKind() { + return PERTYPEWITHIN; + } + + public String toString() { + return "pertypewithin("+typePattern+")"; + } + + private FuzzyBoolean isWithinType(ResolvedTypeX type) { + while (type != null) { + if (typePattern.matchesStatically(type)) { + return FuzzyBoolean.YES; + } + type = type.getDeclaringType(); + } + return FuzzyBoolean.NO; + } +} -- 2.39.5