package org.enumerable.lambda.weaving.tree; import static org.enumerable.lambda.Lambda.*; import static org.enumerable.lambda.Parameters.*; import static org.enumerable.lambda.weaving.ClassFilter.createClassFilter; import static org.junit.Assert.*; import static org.objectweb.asm.Type.*; import java.io.IOException; import java.util.Comparator; import org.enumerable.lambda.Fn0; import org.enumerable.lambda.Fn1; import org.enumerable.lambda.Fn2; import org.enumerable.lambda.Fn3; import org.enumerable.lambda.TestBase; import org.enumerable.lambda.annotation.LambdaParameter; import org.enumerable.lambda.primitives.Fn1DtoI; import org.enumerable.lambda.primitives.Fn2DDtoD; import org.enumerable.lambda.primitives.Fn2LLtoL; import org.enumerable.lambda.primitives.LambdaPrimitives; import org.enumerable.lambda.weaving.tree.LambdaTreeWeaver.MethodAnalyzer; import org.enumerable.lambda.weaving.tree.LambdaTreeWeaver.MethodAnalyzer.LambdaAnalyzer; import org.junit.After; import org.junit.Test; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; @SuppressWarnings("unused") public class LambdaTreeWeaverTest extends TestBase implements Opcodes { @Test public void analyzingZeroArgumentLambda() throws Exception { class C { void m() { λ(); } } LambdaTreeWeaver weaver = analyze(C.class); assertEquals(getType(C.class).getInternalName(), weaver.c.name); assertEquals(2, weaver.methods.size()); MethodAnalyzer constructor = weaver.methods.get(0); assertEquals("<init>", constructor.m.name); assertTrue(constructor.lambdas.isEmpty()); MethodAnalyzer method = weaver.methods.get(1); assertEquals("m", method.m.name); assertEquals(1, method.lambdas.size()); LambdaAnalyzer lambda = method.lambdas.get(0); assertTrue(lambda.locals.isEmpty()); assertTrue(lambda.getMutableLocals().isEmpty()); assertTrue(lambda.parameters.isEmpty()); assertTrue(lambda.newLambdaParameterTypes.isEmpty()); assertEquals(object, lambda.expressionType); assertEquals(getType(Fn0.class), lambda.lambdaType); assertEquals("call", lambda.sam.getName()); assertEquals(0, lambda.sam.getArgumentTypes().length); assertEquals(object, lambda.sam.getReturnType()); assertFalse(lambda.returnNeedsUnboxing()); assertFalse(lambda.returnNeedsBoxing()); } @Test public void analyzingOneArgumentLambda() throws Exception { class C { void m() { λ(n, null); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertTrue(lambda.locals.isEmpty()); assertTrue(lambda.getMutableLocals().isEmpty()); assertEquals(list("n"), list(lambda.parameters.keySet())); assertEquals(list(INT_TYPE), lambda.getParameterTypes()); assertEquals(list(object), lambda.newLambdaParameterTypes); assertEquals(object, lambda.expressionType); assertEquals(getType(Fn1.class), lambda.lambdaType); assertEquals("call", lambda.sam.getName()); assertEquals(list(object), list(lambda.sam.getArgumentTypes())); assertEquals(object, lambda.sam.getReturnType()); assertTrue(lambda.parameterNeedsUnboxing("n")); } @Test public void analyzingTwoArgumentsLambda() throws Exception { class C { void m() { λ(s, n, null); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertTrue(lambda.locals.isEmpty()); assertTrue(lambda.getMutableLocals().isEmpty()); assertEquals(list("s", "n"), list(lambda.parameters.keySet())); assertEquals(list(getType(String.class), INT_TYPE), lambda.getParameterTypes()); assertEquals(list(object, object), lambda.newLambdaParameterTypes); assertEquals(object, lambda.expressionType); assertEquals(getType(Fn2.class), lambda.lambdaType); assertEquals("call", lambda.sam.getName()); assertEquals(list(object, object), list(lambda.sam.getArgumentTypes())); assertEquals(object, lambda.sam.getReturnType()); assertFalse(lambda.parameterNeedsUnboxing("s")); assertTrue(lambda.parameterNeedsUnboxing("n")); } @Test public void analyzingThreeArgumentLambda() throws Exception { class C { void m() { λ(s, n, b, null); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertTrue(lambda.locals.isEmpty()); assertTrue(lambda.getMutableLocals().isEmpty()); assertEquals(list("s", "n", "b"), list(lambda.parameters.keySet())); assertEquals(list(getType(String.class), INT_TYPE, BOOLEAN_TYPE), lambda.getParameterTypes()); assertEquals(list(object, object, object), lambda.newLambdaParameterTypes); assertEquals(object, lambda.expressionType); assertEquals(getType(Fn3.class), lambda.lambdaType); assertEquals("call", lambda.sam.getName()); assertEquals(list(object, object, object), list(lambda.sam.getArgumentTypes())); assertEquals(object, lambda.sam.getReturnType()); assertFalse(lambda.parameterNeedsUnboxing("s")); assertTrue(lambda.parameterNeedsUnboxing("n")); assertTrue(lambda.parameterNeedsUnboxing("b")); } @Test public void analyzingLambdaClosingOverEffectivelyFinalVariable() throws Exception { class C { void m() { int i = 1; λ(i); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(1, lambda.locals.size()); assertTrue(lambda.getMutableLocals().isEmpty()); assertEquals(list("i"), list(lambda.locals.keySet())); assertEquals(INT_TYPE, lambda.getLocalVariableType("i")); } @Test public void analyzingLambdaClosingOverMutableVariableMutatedOutsideLambda() throws Exception { class C { void m() { int i = 1; i = 2; λ(i); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(1, lambda.locals.size()); assertEquals(1, lambda.getMutableLocals().size()); assertEquals(list("i"), list(lambda.getMutableLocals().keySet())); } @Test public void analyzingtLambdaClosingOverMutableVariable() throws Exception { class C { void m() { int i = 1; λ(i = 2); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(1, lambda.locals.size()); assertEquals(1, lambda.getMutableLocals().size()); assertEquals(list("i"), list(lambda.getMutableLocals().keySet())); assertEquals(INT_TYPE, lambda.getLocalVariableType("i")); } @Test public void analyzingLambdaClosingOverMutableVariableChangedAfterLambda() throws Exception { class C { void m() { int i = 1; λ(i); i = 2; } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(1, lambda.locals.size()); assertEquals(1, lambda.getMutableLocals().size()); assertEquals(list("i"), list(lambda.getMutableLocals().keySet())); assertEquals(INT_TYPE, lambda.getLocalVariableType("i")); } @Test public void analyzingLambdaClosingOverIncreasedVariable() throws Exception { class C { void m() { int i = 1; λ(i++); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(1, lambda.locals.size()); assertEquals(1, lambda.getMutableLocals().size()); assertEquals(list("i"), list(lambda.getMutableLocals().keySet())); assertEquals(INT_TYPE, lambda.getLocalVariableType("i")); } @Test public void analyzingLambdaClosingOverIncreasedVariableOutsideLambda() throws Exception { class C { void m() { int i = 1; λ(i++); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(1, lambda.locals.size()); assertEquals(1, lambda.getMutableLocals().size()); assertEquals(list("i"), list(lambda.getMutableLocals().keySet())); assertEquals(INT_TYPE, lambda.getLocalVariableType("i")); } @Test public void analyzingLambdaClosingOverVariableInitialziedInLambda() throws Exception { class C { void m() { int i; λ(i = 2); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(1, lambda.locals.size()); assertEquals(1, lambda.getMutableLocals().size()); assertEquals(list("i"), list(lambda.getMutableLocals().keySet())); assertEquals(INT_TYPE, lambda.getLocalVariableType("i")); } @Test public void analyzingLambdaClosingOverEffectivelyFinalArgument() throws Exception { class C { void m(int i) { λ(i); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(1, lambda.locals.size()); assertTrue(lambda.getMutableLocals().isEmpty()); assertEquals(list("i"), list(lambda.locals.keySet())); assertEquals(INT_TYPE, lambda.getLocalVariableType("i")); } @Test public void analyzingLambdaClosingOverMutableArgument() throws Exception { class C { void m(int i) { i = 2; i = 4; λ(i); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(1, lambda.locals.size()); assertEquals(1, lambda.getMutableLocals().size()); assertEquals(list("i"), list(lambda.getMutableLocals().keySet())); assertEquals(INT_TYPE, lambda.getLocalVariableType("i")); } @Test public void analyzingLambdaCreatedFromGenericCast() throws Exception { class C { void m() { Runnable r = delegate(null); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertTrue(lambda.parameters.isEmpty()); assertTrue(lambda.newLambdaParameterTypes.isEmpty()); assertEquals(object, lambda.expressionType); assertEquals(getType(Runnable.class), lambda.lambdaType); assertEquals("run", lambda.sam.getName()); assertEquals(0, lambda.sam.getArgumentTypes().length); assertEquals(VOID_TYPE, lambda.sam.getReturnType()); assertFalse(lambda.returnNeedsBoxing()); } @LambdaParameter static Object o1; @LambdaParameter static Object o2; @Test public void analyzingLambdaCreatedFromGenericCastWhichAlsoDefinesMethodFromObject() throws Exception { class C { void m() { Comparator<?> c = delegate(o1, o2, 0); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(list("o1", "o2"), list(lambda.parameters.keySet())); assertEquals(list(object, object), lambda.newLambdaParameterTypes); assertEquals(object, lambda.expressionType); assertEquals(getType(Comparator.class), lambda.lambdaType); assertEquals("compare", lambda.sam.getName()); assertEquals(list(object, object), list(lambda.sam.getArgumentTypes())); assertEquals(INT_TYPE, lambda.sam.getReturnType()); assertTrue(lambda.returnNeedsUnboxing()); assertFalse(lambda.returnNeedsBoxing()); } interface I { long invoke(int x, Object y, double z); } @LambdaParameter static Double dbl; @Test public void analyzingLambdaCreatedFromGenericCastWithTypesThatNeedConversion() throws Exception { class C { void m() { I i = delegate(n, o1, dbl, 0); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(getType(I.class), lambda.lambdaType); assertEquals("invoke", lambda.sam.getName()); assertEquals(list("n", "o1", "dbl"), list(lambda.parameters.keySet())); assertEquals(list(INT_TYPE, object, DOUBLE_TYPE), list(lambda.sam.getArgumentTypes())); assertEquals(list(INT_TYPE, object, getType(Double.class)), lambda.getParameterTypes()); assertFalse(lambda.parameterNeedsUnboxing("n")); assertFalse(lambda.parameterNeedsBoxing("n")); assertFalse(lambda.parameterNeedsUnboxing("o1")); assertFalse(lambda.parameterNeedsBoxing("o1")); assertFalse(lambda.parameterNeedsUnboxing("dbl")); assertTrue(lambda.parameterNeedsBoxing("dbl")); assertEquals(object, lambda.expressionType); assertEquals(LONG_TYPE, lambda.sam.getReturnType()); assertTrue(lambda.returnNeedsUnboxing()); assertFalse(lambda.returnNeedsBoxing()); } @Test public void analyzingLambdaCreatedFromPrimitiveLambdaThatNeedsNarrowConversionFromDoubleToInt() throws Exception { class C { void m() { LambdaPrimitives.λ(d, idx, 0); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(getType(Fn2DDtoD.class), lambda.lambdaType); assertEquals(list(DOUBLE_TYPE, DOUBLE_TYPE), list(lambda.sam.getArgumentTypes())); assertEquals(list(DOUBLE_TYPE, INT_TYPE), lambda.getParameterTypes()); assertFalse(lambda.parameterNeedsUnboxing("d")); assertFalse(lambda.parameterNeedsBoxing("d")); assertFalse(lambda.parameterNeedsNarrowConversionFromActualArgument("d")); assertEquals(-1, lambda.parameterNarrowConversionOpcode("d")); assertFalse(lambda.parameterNeedsUnboxing("idx")); assertFalse(lambda.parameterNeedsBoxing("idx")); assertTrue(lambda.parameterNeedsNarrowConversionFromActualArgument("idx")); assertEquals(D2I, lambda.parameterNarrowConversionOpcode("idx")); assertEquals(DOUBLE_TYPE, lambda.expressionType); assertEquals(DOUBLE_TYPE, lambda.sam.getReturnType()); } @Test public void analyzingLambdaCreatedFromPrimitiveLambdaThatNeedsNarrowConversionFromDoubleToLong() throws Exception { class C { void m() { LambdaPrimitives.λ(d, l, 0); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(getType(Fn2DDtoD.class), lambda.lambdaType); assertEquals(list(DOUBLE_TYPE, DOUBLE_TYPE), list(lambda.sam.getArgumentTypes())); assertEquals(list(DOUBLE_TYPE, LONG_TYPE), lambda.getParameterTypes()); assertFalse(lambda.parameterNeedsUnboxing("l")); assertFalse(lambda.parameterNeedsBoxing("l")); assertTrue(lambda.parameterNeedsNarrowConversionFromActualArgument("l")); assertEquals(D2L, lambda.parameterNarrowConversionOpcode("l")); } @LambdaParameter static float fl; @Test public void analyzingLambdaCreatedFromPrimitiveLambdaThatNeedsNarrowConversionFromDoubleToFloat() throws Exception { class C { void m() { LambdaPrimitives.λ(d, fl, 0); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(getType(Fn2DDtoD.class), lambda.lambdaType); assertEquals(list(DOUBLE_TYPE, DOUBLE_TYPE), list(lambda.sam.getArgumentTypes())); assertEquals(list(DOUBLE_TYPE, FLOAT_TYPE), lambda.getParameterTypes()); assertFalse(lambda.parameterNeedsUnboxing("fl")); assertFalse(lambda.parameterNeedsBoxing("fl")); assertTrue(lambda.parameterNeedsNarrowConversionFromActualArgument("fl")); assertEquals(D2F, lambda.parameterNarrowConversionOpcode("fl")); } @Test public void analyzingLambdaCreatedFromPrimitiveLambdaThatNeedsNarrowConversionFromLongToInt() throws Exception { class C { void m() { LambdaPrimitives.λ(l, idx, 0); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(getType(Fn2LLtoL.class), lambda.lambdaType); assertEquals(list(LONG_TYPE, LONG_TYPE), list(lambda.sam.getArgumentTypes())); assertEquals(list(LONG_TYPE, INT_TYPE), lambda.getParameterTypes()); assertFalse(lambda.parameterNeedsUnboxing("l")); assertFalse(lambda.parameterNeedsBoxing("l")); assertFalse(lambda.parameterNeedsNarrowConversionFromActualArgument("l")); assertEquals(-1, lambda.parameterNarrowConversionOpcode("l")); assertFalse(lambda.parameterNeedsUnboxing("idx")); assertFalse(lambda.parameterNeedsBoxing("idx")); assertTrue(lambda.parameterNeedsNarrowConversionFromActualArgument("idx")); assertEquals(L2I, lambda.parameterNarrowConversionOpcode("idx")); assertEquals(LONG_TYPE, lambda.expressionType); assertEquals(LONG_TYPE, lambda.sam.getReturnType()); } @Test public void analyzingOneArgumentLambdaWithDefaultValue() throws Exception { class C { void m() { λ(n = 2, null); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertTrue(lambda.locals.isEmpty()); assertTrue(lambda.getMutableLocals().isEmpty()); assertEquals(list("n"), list(lambda.parameters.keySet())); assertEquals(list(INT_TYPE), lambda.getParameterTypes()); assertTrue(lambda.parametersWithDefaultValue.contains("n")); assertTrue(lambda.parameterDefaultValueNeedsBoxing("n")); } @Test public void analyzingTwoArgumentLambdaWithDefaultValue() throws Exception { class C { void m() { λ(n, s = "", null); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(list("n", "s"), list(lambda.parameters.keySet())); assertEquals(list(INT_TYPE, getType(String.class)), lambda.getParameterTypes()); assertFalse(lambda.parametersWithDefaultValue.contains("n")); assertTrue(lambda.parametersWithDefaultValue.contains("s")); assertTrue(lambda.parameterDefaultValueNeedsBoxing("n")); assertFalse(lambda.parameterDefaultValueNeedsBoxing("s")); } @Test public void analyzingLambdaCreatedFromPrimitiveLambdaThatHasDefaultValue() throws Exception { class C { void m() { LambdaPrimitives.λ(d = 2, 0); } } LambdaAnalyzer lambda = lambdaIn(C.class); assertEquals(getType(Fn1DtoI.class), lambda.lambdaType); assertEquals(list(DOUBLE_TYPE), list(lambda.sam.getArgumentTypes())); assertEquals(list(DOUBLE_TYPE), lambda.getParameterTypes()); assertTrue(lambda.parametersWithDefaultValue.contains("d")); assertTrue(lambda.parameterDefaultValueNeedsBoxing("d")); } @Test public void analyzingLambdaAccessingPrivateField() throws Exception { class C { private int p; void m() { λ(p); } } analyze(C.class); assertTrue(weaver.fieldsThatNeedStaticAccessMethod.containsKey("p")); } @Test public void analyzingLambdaAccessingPrivateMethod() throws Exception { class C { void m() { λ(p()); } private Object p() { return null; } } analyze(C.class); assertTrue(weaver.methodsThatNeedStaticAccessMethod.containsKey("p")); } static class PrivateStaticField { private static int p; void m() { λ(p); } } @Test public void analyzingLambdaAccessingPrivateStaticField() throws Exception { analyze(PrivateStaticField.class); assertTrue(weaver.fieldsThatNeedStaticAccessMethod.containsKey("p")); } static class PrivateStaticMethod { void m() { λ(p()); } private static Object p() { return null; } } @Test public void analyzingLambdaAccessingPrivateStaticMethod() throws Exception { analyze(PrivateStaticMethod.class); assertTrue(weaver.methodsThatNeedStaticAccessMethod.containsKey("p")); } LambdaAnalyzer lambdaIn(Class<?> aClass) throws Exception { return methodIn(aClass).lambdas.get(0); } MethodAnalyzer methodIn(Class<?> aClass) throws Exception { return analyze(aClass).methods.get(1); } @After public void transform() throws Exception { weaver.transform(); } LambdaTreeWeaver weaver; Type object = getType(Object.class); LambdaTreeWeaver analyze(Class<?> aClass) throws Exception { ClassReader cr = new ClassReader(aClass.getName()); weaver = new LambdaTreeWeaver(aClass.getClassLoader(), createClassFilter(), cr); weaver.analyze(); return weaver; } }