summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorahaase2008-02-26 10:25:05 (EST)
committerahaase2008-02-26 10:25:05 (EST)
commit43f6f569eb50b3c4365b3a0a4f845883d6894eb1 (patch)
treed594214e344159436bd6479dedae996e46a8a9b8
parent515b96e87cc52e45afa5a85fce747f624ae9b7d9 (diff)
downloadorg.eclipse.xpand-43f6f569eb50b3c4365b3a0a4f845883d6894eb1.zip
org.eclipse.xpand-43f6f569eb50b3c4365b3a0a4f845883d6894eb1.tar.gz
org.eclipse.xpand-43f6f569eb50b3c4365b3a0a4f845883d6894eb1.tar.bz2
added tests for AOP
-rw-r--r--plugins/org.eclipse.xtend.backend/META-INF/MANIFEST.MF2
-rw-r--r--plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/AdviceParamType.java (renamed from plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/internal/AdviceParamType.java)4
-rw-r--r--plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/AdvisedFunction.java20
-rw-r--r--plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/AroundAdvice.java5
-rw-r--r--plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/ExecutionPointcut.java3
-rw-r--r--plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/ThisJoinPoint.java8
-rw-r--r--plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/internal/AdviceContextImpl.java36
-rw-r--r--plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/internal/AdviceScopeCounter.java24
-rw-r--r--plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/expr/InvocationOnObjectExpression.java2
-rw-r--r--plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/functions/internal/FunctionDefContextImpl.java3
-rw-r--r--tests/org.eclipse.xtend.backend.test/src/org/eclipse/xtend/backend/aop/AopTest.java233
-rw-r--r--tests/org.eclipse.xtend.backend.test/src/org/eclipse/xtend/backend/aop/AopTestFunctions.java41
-rw-r--r--tests/org.eclipse.xtend.backend.test/src/org/eclipse/xtend/backend/aop/ConcatAdviceFactory.java29
13 files changed, 384 insertions, 26 deletions
diff --git a/plugins/org.eclipse.xtend.backend/META-INF/MANIFEST.MF b/plugins/org.eclipse.xtend.backend/META-INF/MANIFEST.MF
index 5700d84..c56797d 100644
--- a/plugins/org.eclipse.xtend.backend/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.xtend.backend/META-INF/MANIFEST.MF
@@ -4,6 +4,8 @@ Bundle-Name: Backend Plug-in (Incubation)
Bundle-SymbolicName: org.eclipse.xtend.backend
Bundle-Version: 0.7.0
Export-Package: org.eclipse.xtend.backend;uses:="org.eclipse.xtend.backend.common",
+ org.eclipse.xtend.backend.aop,
+ org.eclipse.xtend.backend.aop.internal;x-internal:=true,
org.eclipse.xtend.backend.common,
org.eclipse.xtend.backend.expr;uses:="org.eclipse.xtend.backend.common",
org.eclipse.xtend.backend.functions;uses:="org.eclipse.xtend.backend.common",
diff --git a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/internal/AdviceParamType.java b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/AdviceParamType.java
index 9ca0282..30c444d 100644
--- a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/internal/AdviceParamType.java
+++ b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/AdviceParamType.java
@@ -8,7 +8,7 @@ http://www.eclipse.org/legal/epl-v10.html
Contributors:
Arno Haase - initial API and implementation
*/
-package org.eclipse.xtend.backend.aop.internal;
+package org.eclipse.xtend.backend.aop;
import org.eclipse.xtend.backend.common.BackendType;
@@ -26,8 +26,6 @@ public final class AdviceParamType {
_includingSubtypes = includingSubtypes;
}
- //TODO testen!
-
public boolean matches (BackendType type) {
if (_includingSubtypes)
return _type.isAssignableFrom (type);
diff --git a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/AdvisedFunction.java b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/AdvisedFunction.java
index ddab16f..732a58d 100644
--- a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/AdvisedFunction.java
+++ b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/AdvisedFunction.java
@@ -10,9 +10,9 @@ Contributors:
*/
package org.eclipse.xtend.backend.aop;
-import java.util.Arrays;
import java.util.List;
+import org.eclipse.xtend.backend.aop.internal.AdviceScopeCounter;
import org.eclipse.xtend.backend.common.ExecutionContext;
import org.eclipse.xtend.backend.common.Function;
import org.eclipse.xtend.backend.util.ObjectWrapper;
@@ -35,13 +35,15 @@ public final class AdvisedFunction {
private final Function _function;
private final List<AroundAdvice> _advice;
private final int _firstCacheableIndex;
+ private final AdviceScopeCounter _scopeCounter;
private final ThisJoinPointStaticPart _thisJoinPointStaticPart;
- public AdvisedFunction (String functionName, Function function, List<AroundAdvice> advice) {
+ public AdvisedFunction (String functionName, Function function, List<AroundAdvice> advice, AdviceScopeCounter scopeCounter) {
_function = function;
_advice = advice;
+ _scopeCounter = scopeCounter;
_thisJoinPointStaticPart = new ThisJoinPointStaticPart (functionName, _function);
@@ -56,7 +58,7 @@ public final class AdvisedFunction {
_firstCacheableIndex = advice.size();
}
- public Object evaluate (ExecutionContext ctx, Object[] params) {
+ public Object evaluate (ExecutionContext ctx, List<?> params) {
// the evaluation of the advice is performed in three stages:
// 1. all advice that is "outside" the outermost non-cacheable advice
// is always actually evaluated
@@ -68,28 +70,28 @@ public final class AdvisedFunction {
return proceedInternal (ctx, 0, params);
}
- private Object proceedInternal (final ExecutionContext ctx, final int indNextAdvice, final Object[] params) {
+ private Object proceedInternal (final ExecutionContext ctx, final int indNextAdvice, final List<?> params) {
if (indNextAdvice >= _advice.size())
- return ctx.getFunctionInvoker().invoke (ctx, _function, Arrays.asList (params));
+ return ctx.getFunctionInvoker().invoke (ctx, _function, params);
if (indNextAdvice >= _firstCacheableIndex) {
- final ObjectWrapper ow = ctx.getAdviceContext().getResultCache().get (new Triplet<Function, AroundAdvice, List<?>> (_function, _advice.get (indNextAdvice), Arrays.asList(params)));
+ final ObjectWrapper ow = ctx.getAdviceContext().getResultCache().get (new Triplet<Function, AroundAdvice, List<?>> (_function, _advice.get (indNextAdvice), params));
if (ow != null)
return ow._content;
}
final ThisJoinPoint thisJoinPoint = new ThisJoinPoint (ctx.getStacktrace(), params) {
@Override
- public Object proceed (Object[] localParams) {
+ public Object proceed (List<?> localParams) {
return proceedInternal (ctx, indNextAdvice+1, localParams);
}
};
final AroundAdvice advice = _advice.get (indNextAdvice);
- final Object result = advice.evaluate (ctx, thisJoinPoint, _thisJoinPointStaticPart);
+ final Object result = advice.evaluate (ctx, _scopeCounter, thisJoinPoint, _thisJoinPointStaticPart);
if (indNextAdvice >= _firstCacheableIndex) {
- final Triplet<Function, AroundAdvice, List<?>> key = new Triplet<Function, AroundAdvice, List<?>> (_function, _advice.get (indNextAdvice), Arrays.asList(params));
+ final Triplet<Function, AroundAdvice, List<?>> key = new Triplet<Function, AroundAdvice, List<?>> (_function, _advice.get (indNextAdvice), params);
ctx.getAdviceContext().getResultCache().put (key, new ObjectWrapper (result));
}
diff --git a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/AroundAdvice.java b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/AroundAdvice.java
index 29c9c59..8e5b6db 100644
--- a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/AroundAdvice.java
+++ b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/AroundAdvice.java
@@ -10,6 +10,7 @@ Contributors:
*/
package org.eclipse.xtend.backend.aop;
+import org.eclipse.xtend.backend.aop.internal.AdviceScopeCounter;
import org.eclipse.xtend.backend.common.ExecutionContext;
import org.eclipse.xtend.backend.common.ExpressionBase;
import org.eclipse.xtend.backend.common.SyntaxConstants;
@@ -42,7 +43,8 @@ public final class AroundAdvice {
* actually evaluates the advice, regardless of caching - that is context sensitive and must
* be taken care of by callers
*/
- public Object evaluate (ExecutionContext ctx, ThisJoinPoint thisJoinPoint, ThisJoinPointStaticPart thisJoinPointStaticPart) {
+ public Object evaluate (ExecutionContext ctx, AdviceScopeCounter scopeCounter, ThisJoinPoint thisJoinPoint, ThisJoinPointStaticPart thisJoinPointStaticPart) {
+ scopeCounter.enterAdvice();
ctx.getLocalVarContext().getLocalVars().put (SyntaxConstants.THIS_JOINPOINT, thisJoinPoint);
ctx.getLocalVarContext().getLocalVars().put (SyntaxConstants.THIS_JOINPOINT_STATICPART, thisJoinPointStaticPart);
@@ -52,6 +54,7 @@ public final class AroundAdvice {
finally {
ctx.getLocalVarContext ().getLocalVars ().remove (SyntaxConstants.THIS_JOINPOINT);
ctx.getLocalVarContext ().getLocalVars ().remove (SyntaxConstants.THIS_JOINPOINT_STATICPART);
+ scopeCounter.leaveAdvice();
}
}
diff --git a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/ExecutionPointcut.java b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/ExecutionPointcut.java
index 995e39e..6c0f75e 100644
--- a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/ExecutionPointcut.java
+++ b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/ExecutionPointcut.java
@@ -13,7 +13,6 @@ package org.eclipse.xtend.backend.aop;
import java.util.List;
import java.util.regex.Pattern;
-import org.eclipse.xtend.backend.aop.internal.AdviceParamType;
import org.eclipse.xtend.backend.common.BackendType;
import org.eclipse.xtend.backend.common.Function;
import org.eclipse.xtend.backend.util.Pair;
@@ -62,8 +61,6 @@ public final class ExecutionPointcut implements Pointcut {
_varArgsType = varArgsType;
}
- //TODO testen!!!
-
public boolean matches (String name, Function function) {
if (! matchesName (name))
return false;
diff --git a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/ThisJoinPoint.java b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/ThisJoinPoint.java
index 1d36a31..0af2959 100644
--- a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/ThisJoinPoint.java
+++ b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/ThisJoinPoint.java
@@ -22,9 +22,9 @@ import org.eclipse.xtend.backend.common.StacktraceEntry;
*/
public abstract class ThisJoinPoint {
private final List<StacktraceEntry> _stackTrace;
- private final Object[] _params;
+ private final List<?> _params;
- public ThisJoinPoint (List<StacktraceEntry> stackTrace, Object[] params) {
+ public ThisJoinPoint (List<StacktraceEntry> stackTrace, List<?> params) {
_stackTrace = stackTrace;
_params = params;
}
@@ -33,7 +33,7 @@ public abstract class ThisJoinPoint {
return _stackTrace;
}
- public Object[] getParameters () {
+ public List<?> getParameters () {
return _params;
}
@@ -41,5 +41,5 @@ public abstract class ThisJoinPoint {
return proceed (_params);
}
- public abstract Object proceed (Object[] params);
+ public abstract Object proceed (List<?> params);
}
diff --git a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/internal/AdviceContextImpl.java b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/internal/AdviceContextImpl.java
index b169826..321902c 100644
--- a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/internal/AdviceContextImpl.java
+++ b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/internal/AdviceContextImpl.java
@@ -11,6 +11,7 @@ Contributors:
package org.eclipse.xtend.backend.aop.internal;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -38,6 +39,22 @@ import org.eclipse.xtend.backend.util.Triplet;
*/
public final class AdviceContextImpl implements AdviceContext {
private final List<AroundAdvice> _advice = new ArrayList<AroundAdvice> ();
+ private int _numAdviceInScope = 0;
+
+ private final AdviceScopeCounter _scopeCounter = new AdviceScopeCounter () {
+ public void enterAdvice () {
+ _numAdviceInScope++;
+ }
+
+ public void leaveAdvice () {
+ _numAdviceInScope--;
+ }
+
+ public boolean isWithinAdvice () {
+ return _numAdviceInScope > 0;
+ }
+ };
+
private final DoubleKeyCache <String, Function, AdvisedFunction> _advisedFunctionCache = new DoubleKeyCache<String, Function, AdvisedFunction> () {
@Override
@@ -48,7 +65,7 @@ public final class AdviceContextImpl implements AdviceContext {
if (advice.getPointcut().matches (functionName, f))
applicableAdvice.add (advice);
- return new AdvisedFunction (functionName, f, applicableAdvice);
+ return new AdvisedFunction (functionName, f, applicableAdvice, _scopeCounter);
}
};
@@ -79,17 +96,28 @@ public final class AdviceContextImpl implements AdviceContext {
return result;
}
- //TODO test this (including the order in which advice is applied)!!!
-
/**
* returns the advice to be applied to this function, starting with the outermost
* advice, i.e. the advice that is to wrapped around all other advice applicable
* to a given function.
*/
public AdvisedFunction getAdvice (String functionName, Function f) {
- return _advisedFunctionCache.get (functionName, f);
+ // this distinction adds an implicit " && ! within <any advice>" to every pointcut. That is
+ // done to avoid endless recursion when a function is called from within advice that is
+ // applicable to this function
+
+ if (_scopeCounter.isWithinAdvice()) {
+ return unadvisedFunction (functionName, f);
+ }
+ else
+ return _advisedFunctionCache.get (functionName, f);
}
+ @SuppressWarnings("unchecked")
+ private AdvisedFunction unadvisedFunction (String functionName, Function f) {
+ return new AdvisedFunction (functionName, f, Collections.EMPTY_LIST, _scopeCounter);
+ }
+
public Map<Triplet<Function, AroundAdvice, List<?>>, ObjectWrapper> getResultCache () {
return _resultCache;
}
diff --git a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/internal/AdviceScopeCounter.java b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/internal/AdviceScopeCounter.java
new file mode 100644
index 0000000..56903f6
--- /dev/null
+++ b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/aop/internal/AdviceScopeCounter.java
@@ -0,0 +1,24 @@
+/*
+Copyright (c) 2008 Arno Haase.
+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://www.eclipse.org/legal/epl-v10.html
+
+Contributors:
+ Arno Haase - initial API and implementation
+*/
+package org.eclipse.xtend.backend.aop.internal;
+
+
+/**
+ * This abstraction serves to keep track of whether execution currently is within advice or not.
+ *
+ * @author Arno Haase (http://www.haase-consulting.com)
+ */
+public interface AdviceScopeCounter {
+ void enterAdvice ();
+ void leaveAdvice ();
+
+ boolean isWithinAdvice ();
+}
diff --git a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/expr/InvocationOnObjectExpression.java b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/expr/InvocationOnObjectExpression.java
index 97b5133..91314db 100644
--- a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/expr/InvocationOnObjectExpression.java
+++ b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/expr/InvocationOnObjectExpression.java
@@ -45,7 +45,7 @@ public final class InvocationOnObjectExpression extends ExpressionBase {
// shortcut the evaluation and return null
//
// CAUTION - without this shortcut, the polymorphic resolution may be ambiguous in unexpected ways
- if (_nullIfFirstParamIsNull && params.get(0) == null) {
+ if (_nullIfFirstParamIsNull && params.size() > 0 && params.get(0) == null) {
ctx.logNullDeRef (getPos());
return null;
}
diff --git a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/functions/internal/FunctionDefContextImpl.java b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/functions/internal/FunctionDefContextImpl.java
index 3716a49..f41ce6a 100644
--- a/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/functions/internal/FunctionDefContextImpl.java
+++ b/plugins/org.eclipse.xtend.backend/src/org/eclipse/xtend/backend/functions/internal/FunctionDefContextImpl.java
@@ -113,7 +113,8 @@ public final class FunctionDefContextImpl implements FunctionDefContextInternal
ErrorHandler.handle ("could not resolve function '" + functionName + "' for parameter types " + StringHelper.getTypesAsString (params) + " - candidates were " + candidates, exc);
}
- return ctx.getFunctionInvoker().invoke (ctx, f, params);
+ return ctx.getAdviceContext().getAdvice (functionName, f).evaluate(ctx, params);
+// return ctx.getFunctionInvoker().invoke (ctx, f, params);
}
/**
diff --git a/tests/org.eclipse.xtend.backend.test/src/org/eclipse/xtend/backend/aop/AopTest.java b/tests/org.eclipse.xtend.backend.test/src/org/eclipse/xtend/backend/aop/AopTest.java
new file mode 100644
index 0000000..41d3147
--- /dev/null
+++ b/tests/org.eclipse.xtend.backend.test/src/org/eclipse/xtend/backend/aop/AopTest.java
@@ -0,0 +1,233 @@
+/*
+Copyright (c) 2008 Arno Haase.
+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://www.eclipse.org/legal/epl-v10.html
+
+Contributors:
+ Arno Haase - initial API and implementation
+ */
+package org.eclipse.xtend.backend.aop;
+
+import static org.eclipse.xtend.backend.testhelpers.BackendTestHelper.createEmptyExecutionContext;
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+import org.eclipse.xtend.backend.common.AdviceContext;
+import org.eclipse.xtend.backend.common.BackendType;
+import org.eclipse.xtend.backend.common.ExecutionContext;
+import org.eclipse.xtend.backend.common.ExpressionBase;
+import org.eclipse.xtend.backend.common.Function;
+import org.eclipse.xtend.backend.common.NamedFunction;
+import org.eclipse.xtend.backend.expr.ConcatExpression;
+import org.eclipse.xtend.backend.expr.InvocationOnObjectExpression;
+import org.eclipse.xtend.backend.expr.LiteralExpression;
+import org.eclipse.xtend.backend.functions.FunctionDefContextFactory;
+import org.eclipse.xtend.backend.functions.FunctionDefContextInternal;
+import org.eclipse.xtend.backend.functions.java.JavaDefinedFunction;
+import org.eclipse.xtend.backend.testhelpers.CounterFunction;
+import org.eclipse.xtend.backend.types.builtin.CollectionType;
+import org.eclipse.xtend.backend.types.builtin.ObjectType;
+import org.eclipse.xtend.backend.util.Pair;
+import org.junit.Test;
+
+
+/**
+ *
+ * @author Arno Haase (http://www.haase-consulting.com)
+ */
+public class AopTest {
+ @Test
+ public void testUncached () {
+ final ExecutionContext ctx = createEmptyExecutionContext();
+
+ final FunctionDefContextInternal fdc = new FunctionDefContextFactory (ctx.getTypesystem()).create();
+ ctx.setFunctionDefContext (fdc);
+
+ for (JavaDefinedFunction f: JavaDefinedFunction.createForEntireClass (CounterFunction.class, ctx.getTypesystem()))
+ fdc.register (new NamedFunction (f.getName(), f));
+
+ @SuppressWarnings("unchecked")
+ final Pointcut pointCut = new ExecutionPointcut ("*", Collections.EMPTY_LIST, true, new AdviceParamType (ObjectType.INSTANCE, true));
+ registerAdvice (ctx, "pre-1", "post-1", true, pointCut, true);
+
+ assertEquals ("pre-10post-1", ctx.getFunctionDefContext().invoke (ctx, "nextCounterValue", Collections.emptyList()).toString());
+ assertEquals ("pre-11post-1", ctx.getFunctionDefContext().invoke (ctx, "nextCounterValue", Collections.emptyList()).toString());
+
+ // test wrapped advice, particularly the application order
+ final AdviceContext advCtx1 = ctx.getAdviceContext();
+ registerAdvice (ctx, "pre-2", "post-2", true, pointCut, true);
+ assertEquals ("pre-1pre-22post-2post-1", ctx.getFunctionDefContext().invoke (ctx, "nextCounterValue", Collections.emptyList()).toString());
+
+ // test without calling proceed, and at the same time reverting to an earlier AdviceContext
+ ctx.setAdviceContext (advCtx1);
+ registerAdvice (ctx, "pre-3", "post-3", false, pointCut, true);
+ assertEquals ("pre-1pre-3post-3post-1", ctx.getFunctionDefContext().invoke (ctx, "nextCounterValue", Collections.emptyList()).toString());
+ assertEquals ("pre-1pre-3post-3post-1", ctx.getFunctionDefContext().invoke (ctx, "nextCounterValue", Collections.emptyList()).toString());
+ assertEquals ("pre-1pre-3post-3post-1", ctx.getFunctionDefContext().invoke (ctx, "nextCounterValue", Collections.emptyList()).toString());
+
+ // test that the previous invocations did not reach the actual function implementation
+ ctx.setAdviceContext (advCtx1);
+ assertEquals ("pre-13post-1", ctx.getFunctionDefContext().invoke (ctx, "nextCounterValue", Collections.emptyList()).toString());
+ }
+
+ private void registerAdvice (ExecutionContext ctx, String prefix, String postfix, boolean proceed, Pointcut pointCut, boolean cached) {
+ ctx.setAdviceContext (ctx.getAdviceContext().copyWithAdvice (new AroundAdvice (ConcatAdviceFactory.createConcatExpression (prefix, postfix, proceed), pointCut, cached)));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testVarArgsParamTypeMatching () {
+ final ExecutionContext ctx = createEmptyExecutionContext();
+
+ final FunctionDefContextInternal fdc = new FunctionDefContextFactory (ctx.getTypesystem()).create();
+ ctx.setFunctionDefContext (fdc);
+
+ for (JavaDefinedFunction f: JavaDefinedFunction.createForEntireClass (AopTestFunctions.class, ctx.getTypesystem()))
+ fdc.register (new NamedFunction (f.getName(), f));
+
+ final Pointcut pointCutObject = new ExecutionPointcut ("f", Collections.EMPTY_LIST, true, new AdviceParamType (ObjectType.INSTANCE, false));
+ final Pointcut pointCutObjectPlus = new ExecutionPointcut ("f", Collections.EMPTY_LIST, true, new AdviceParamType (ObjectType.INSTANCE, true));
+ final Pointcut pointCutCollection = new ExecutionPointcut ("f", Collections.EMPTY_LIST, true, new AdviceParamType (CollectionType.INSTANCE, false));
+ final Pointcut pointCutCollectionPlus = new ExecutionPointcut ("f", Collections.EMPTY_LIST, true, new AdviceParamType (CollectionType.INSTANCE, true));
+
+ registerAdvice (ctx, "object ", "", true, pointCutObject, false);
+ registerAdvice (ctx, "object+ ", "", true, pointCutObjectPlus, false);
+ registerAdvice (ctx, "collection ", "", true, pointCutCollection, false);
+ registerAdvice (ctx, "collection+ ", "", true, pointCutCollectionPlus, false);
+
+ assertEquals ("object object+ f(Object)", ctx.getFunctionDefContext().invoke(ctx, "f", Arrays.asList (new Object ())).toString());
+ assertEquals ("object object+ f(Object)", ctx.getFunctionDefContext().invoke(ctx, "f", Arrays.asList ("")).toString());
+ assertEquals ("object+ collection collection+ f(Collection)", ctx.getFunctionDefContext().invoke(ctx, "f", Arrays.asList (new HashSet<Object> ())).toString());
+ assertEquals ("object+ collection+ f(List)", ctx.getFunctionDefContext().invoke(ctx, "f", Arrays.asList (new ArrayList<Object> ())).toString());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testExplicitParamTypeMatching () {
+ final ExecutionContext ctx = createEmptyExecutionContext();
+
+ final FunctionDefContextInternal fdc = new FunctionDefContextFactory (ctx.getTypesystem()).create();
+ ctx.setFunctionDefContext (fdc);
+
+ for (JavaDefinedFunction f: JavaDefinedFunction.createForEntireClass (AopTestFunctions.class, ctx.getTypesystem()))
+ fdc.register (new NamedFunction (f.getName(), f));
+
+ final Pointcut pointCutObject = new ExecutionPointcut ("f", Arrays.asList (new Pair<String, AdviceParamType> ("o", new AdviceParamType (ObjectType.INSTANCE, false))), false, null);
+ final Pointcut pointCutObjectPlus = new ExecutionPointcut ("f", Arrays.asList (new Pair<String, AdviceParamType> ("o", new AdviceParamType (ObjectType.INSTANCE, true))), false, null);
+ final Pointcut pointCutCollection = new ExecutionPointcut ("f", Arrays.asList (new Pair<String, AdviceParamType> ("o", new AdviceParamType (CollectionType.INSTANCE, false))), false, null);
+ final Pointcut pointCutCollectionPlus = new ExecutionPointcut ("f", Arrays.asList (new Pair<String, AdviceParamType> ("o", new AdviceParamType (CollectionType.INSTANCE, true))), false, null);
+
+ registerAdvice (ctx, "object ", "", true, pointCutObject, false);
+ registerAdvice (ctx, "object+ ", "", true, pointCutObjectPlus, false);
+ registerAdvice (ctx, "collection ", "", true, pointCutCollection, false);
+ registerAdvice (ctx, "collection+ ", "", true, pointCutCollectionPlus, false);
+
+ assertEquals ("object object+ f(Object)", ctx.getFunctionDefContext().invoke(ctx, "f", Arrays.asList (new Object ())).toString());
+ assertEquals ("object object+ f(Object)", ctx.getFunctionDefContext().invoke(ctx, "f", Arrays.asList ("")).toString());
+ assertEquals ("object+ collection collection+ f(Collection)", ctx.getFunctionDefContext().invoke(ctx, "f", Arrays.asList (new HashSet<Object> ())).toString());
+ assertEquals ("object+ collection+ f(List)", ctx.getFunctionDefContext().invoke(ctx, "f", Arrays.asList (new ArrayList<Object> ())).toString());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testNameMatching () {
+ final ExecutionContext ctx = createEmptyExecutionContext();
+
+ final FunctionDefContextInternal fdc = new FunctionDefContextFactory (ctx.getTypesystem()).create();
+ ctx.setFunctionDefContext (fdc);
+
+ for (JavaDefinedFunction f: JavaDefinedFunction.createForEntireClass (AopTestFunctions.class, ctx.getTypesystem()))
+ fdc.register (new NamedFunction (f.getName(), f));
+
+ final Pointcut pointCutFirstPre = new ExecutionPointcut ("first*", Collections.EMPTY_LIST, true, new AdviceParamType (ObjectType.INSTANCE, true));
+ final Pointcut pointCutFirstPost = new ExecutionPointcut ("*tFunction", Collections.EMPTY_LIST, true, new AdviceParamType (ObjectType.INSTANCE, true));
+ final Pointcut pointCutFirstIn = new ExecutionPointcut ("*tF*", Collections.EMPTY_LIST, true, new AdviceParamType (ObjectType.INSTANCE, true));
+ final Pointcut pointCutFirstIn2 = new ExecutionPointcut ("fir*on", Collections.EMPTY_LIST, true, new AdviceParamType (ObjectType.INSTANCE, true));
+ final Pointcut pointCutSecondPre = new ExecutionPointcut ("second*", Collections.EMPTY_LIST, true, new AdviceParamType (ObjectType.INSTANCE, true));
+ final Pointcut pointCutSecondPost = new ExecutionPointcut ("*dFunction", Collections.EMPTY_LIST, true, new AdviceParamType (ObjectType.INSTANCE, true));
+ final Pointcut pointCutSecondIn = new ExecutionPointcut ("*dF*", Collections.EMPTY_LIST, true, new AdviceParamType (ObjectType.INSTANCE, true));
+ final Pointcut pointCutSecondIn2 = new ExecutionPointcut ("sec*on", Collections.EMPTY_LIST, true, new AdviceParamType (ObjectType.INSTANCE, true));
+ final Pointcut pointCutBothIn = new ExecutionPointcut ("*Fun*", Collections.EMPTY_LIST, true, new AdviceParamType (ObjectType.INSTANCE, true));
+
+ registerAdvice (ctx, "firstPre ", "", true, pointCutFirstPre, false);
+ registerAdvice (ctx, "firstPost ", "", true, pointCutFirstPost, false);
+ registerAdvice (ctx, "firstIn ", "", true, pointCutFirstIn, false);
+ registerAdvice (ctx, "firstIn2 ", "", true, pointCutFirstIn2, false);
+
+ registerAdvice (ctx, "secondPre ", "", true, pointCutSecondPre, false);
+ registerAdvice (ctx, "secondPost ", "", true, pointCutSecondPost, false);
+ registerAdvice (ctx, "secondIn ", "", true, pointCutSecondIn, false);
+ registerAdvice (ctx, "secondIn2 ", "", true, pointCutSecondIn2, false);
+
+ registerAdvice (ctx, "bothIn ", "", true, pointCutBothIn, false);
+
+ assertEquals ("firstPre firstPost firstIn firstIn2 bothIn first", ctx.getFunctionDefContext().invoke (ctx, "firstFunction", Collections.emptyList()).toString());
+ assertEquals ("secondPre secondPost secondIn secondIn2 bothIn second", ctx.getFunctionDefContext().invoke (ctx, "secondFunction", Collections.emptyList()).toString());
+ }
+
+ private long _counter = 0;
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testCacheable () {
+ _counter = 0;
+
+ final ExecutionContext ctx = createEmptyExecutionContext();
+
+ final FunctionDefContextInternal fdc = new FunctionDefContextFactory (ctx.getTypesystem()).create();
+ ctx.setFunctionDefContext (fdc);
+
+ for (JavaDefinedFunction f: JavaDefinedFunction.createForEntireClass (CounterFunction.class, ctx.getTypesystem()))
+ fdc.register (new NamedFunction (f.getName(), f));
+
+ fdc.register (new NamedFunction ("f", new Function () {
+
+ public ExpressionBase getGuard () {
+ return null;
+ }
+
+ public List<? extends BackendType> getParameterTypes () {
+ return new ArrayList<BackendType> ();
+ }
+
+ public Object invoke (ExecutionContext localCtx, Object[] params) {
+ return _counter++;
+ }
+
+ public boolean isCached () {
+ return true;
+ }
+ }));
+
+ final Pointcut pointCut = new ExecutionPointcut ("f", Collections.EMPTY_LIST, false, null);
+
+ registerSideEffectAdvice (ctx, "first ", pointCut, true);
+ registerSideEffectAdvice (ctx, "second ", pointCut, false);
+ registerSideEffectAdvice (ctx, "third ", pointCut, true);
+
+ assertEquals ("first 0 second 1 third 2 0", ctx.getFunctionDefContext().invoke (ctx, "f", Collections.EMPTY_LIST).toString());
+ assertEquals ("first 3 second 4 third 2 0", ctx.getFunctionDefContext().invoke (ctx, "f", Collections.EMPTY_LIST).toString());
+ assertEquals ("first 5 second 6 third 2 0", ctx.getFunctionDefContext().invoke (ctx, "f", Collections.EMPTY_LIST).toString());
+ }
+
+ @SuppressWarnings("unchecked")
+ private void registerSideEffectAdvice (ExecutionContext ctx, String marker, Pointcut pointCut, boolean cacheable) {
+ final List<ExpressionBase> toBeConcatenated = new ArrayList<ExpressionBase> ();
+
+ toBeConcatenated.add (new LiteralExpression (marker, null));
+ toBeConcatenated.add (new InvocationOnObjectExpression ("nextCounterValue", Collections.EMPTY_LIST, true, null));
+ toBeConcatenated.add (new LiteralExpression (" ", null));
+ toBeConcatenated.add (ConcatAdviceFactory.createProceedExpression());
+
+ ctx.setAdviceContext (ctx.getAdviceContext().copyWithAdvice (new AroundAdvice (new ConcatExpression (toBeConcatenated, null), pointCut, cacheable)));
+ }
+}
+
+
diff --git a/tests/org.eclipse.xtend.backend.test/src/org/eclipse/xtend/backend/aop/AopTestFunctions.java b/tests/org.eclipse.xtend.backend.test/src/org/eclipse/xtend/backend/aop/AopTestFunctions.java
new file mode 100644
index 0000000..b8a4640
--- /dev/null
+++ b/tests/org.eclipse.xtend.backend.test/src/org/eclipse/xtend/backend/aop/AopTestFunctions.java
@@ -0,0 +1,41 @@
+/*
+Copyright (c) 2008 Arno Haase.
+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://www.eclipse.org/legal/epl-v10.html
+
+Contributors:
+ Arno Haase - initial API and implementation
+ */
+package org.eclipse.xtend.backend.aop;
+
+import java.util.Collection;
+import java.util.List;
+
+
+/**
+ *
+ * @author Arno Haase (http://www.haase-consulting.com)
+ */
+public class AopTestFunctions {
+ public String firstFunction () {
+ return "first";
+ }
+
+ public String secondFunction () {
+ return "second";
+ }
+
+ public String f (Object o) {
+ return "f(Object)";
+ }
+
+ public String f (Collection<?> c) {
+ return "f(Collection)";
+ }
+
+ public String f(List<?> l) {
+ return "f(List)";
+ }
+}
diff --git a/tests/org.eclipse.xtend.backend.test/src/org/eclipse/xtend/backend/aop/ConcatAdviceFactory.java b/tests/org.eclipse.xtend.backend.test/src/org/eclipse/xtend/backend/aop/ConcatAdviceFactory.java
new file mode 100644
index 0000000..3e05315
--- /dev/null
+++ b/tests/org.eclipse.xtend.backend.test/src/org/eclipse/xtend/backend/aop/ConcatAdviceFactory.java
@@ -0,0 +1,29 @@
+package org.eclipse.xtend.backend.aop;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.xtend.backend.common.ExpressionBase;
+import org.eclipse.xtend.backend.common.SyntaxConstants;
+import org.eclipse.xtend.backend.expr.ConcatExpression;
+import org.eclipse.xtend.backend.expr.InvocationOnObjectExpression;
+import org.eclipse.xtend.backend.expr.LiteralExpression;
+import org.eclipse.xtend.backend.expr.LocalVarEvalExpression;
+
+
+class ConcatAdviceFactory {
+ public static ExpressionBase createConcatExpression (String prefix, String suffix, boolean callProceed) {
+ final List<ExpressionBase> toBeConcatenated = new ArrayList<ExpressionBase> ();
+ toBeConcatenated.add (new LiteralExpression (prefix, null));
+ if (callProceed)
+ toBeConcatenated.add (createProceedExpression());
+ toBeConcatenated.add (new LiteralExpression (suffix, null));
+
+ return new ConcatExpression (toBeConcatenated, null);
+ }
+
+ public static ExpressionBase createProceedExpression () {
+ return new InvocationOnObjectExpression ("proceed", Arrays.asList (new LocalVarEvalExpression (SyntaxConstants.THIS_JOINPOINT, null)), true, null);
+ }
+}