Merge "Fix coverage filters" into ub-jack
diff --git a/jack-tests/prebuilts/core-stubs-mini.jack b/jack-tests/prebuilts/core-stubs-mini.jack
index 07a520a..d1d6faa 100644
--- a/jack-tests/prebuilts/core-stubs-mini.jack
+++ b/jack-tests/prebuilts/core-stubs-mini.jack
Binary files differ
diff --git a/jack-tests/prebuilts/core-stubs-mini.jar b/jack-tests/prebuilts/core-stubs-mini.jar
index c18c4d0..91452bb 100644
--- a/jack-tests/prebuilts/core-stubs-mini.jar
+++ b/jack-tests/prebuilts/core-stubs-mini.jar
Binary files differ
diff --git a/jack-tests/prebuilts/junit4-lib.jack b/jack-tests/prebuilts/junit4-lib.jack
index a381784..4af150f 100644
--- a/jack-tests/prebuilts/junit4-lib.jack
+++ b/jack-tests/prebuilts/junit4-lib.jack
Binary files differ
diff --git a/jack-tests/tests/com/android/jack/java8/EcjLambdaTest.java b/jack-tests/tests/com/android/jack/java8/EcjLambdaTest.java
index 3641835..963c665 100644
--- a/jack-tests/tests/com/android/jack/java8/EcjLambdaTest.java
+++ b/jack-tests/tests/com/android/jack/java8/EcjLambdaTest.java
@@ -60,8 +60,7 @@
@Override
public boolean shouldRun(Description description) {
if (testWithApiUsage.contains(description.getMethodName())
- || testWithOtherErrorMsg.contains(description.getMethodName())
- || knownBugs.contains(description.getMethodName())) {
+ || testWithOtherErrorMsg.contains(description.getMethodName())) {
return false;
}
return true;
@@ -78,9 +77,6 @@
}
}
- private static final List<String> knownBugs =
- Arrays.asList("test430035c", "test430035d", "test430035e", "test428552", "test027");
-
@Nonnull
private static final List<String> testWithOtherErrorMsg =
Arrays.asList("test015", "test045", "test056", "test425512", "test430766", "test430766a",
diff --git a/jack-tests/tests/com/android/jack/java8/LambdaTest.java b/jack-tests/tests/com/android/jack/java8/LambdaTest.java
index 1c03f14..2700d71 100644
--- a/jack-tests/tests/com/android/jack/java8/LambdaTest.java
+++ b/jack-tests/tests/com/android/jack/java8/LambdaTest.java
@@ -18,7 +18,6 @@
import com.android.jack.test.helper.FileChecker;
import com.android.jack.test.helper.RuntimeTestHelper;
-import com.android.jack.test.junit.KnownIssue;
import com.android.jack.test.runtime.RuntimeTestInfo;
import com.android.jack.test.toolchain.AbstractTestTools;
import com.android.jack.test.toolchain.JackApiV01;
@@ -180,6 +179,19 @@
AbstractTestTools.getTestRootDir("com.android.jack.java8.lambda.test032"),
"com.android.jack.java8.lambda.test032.jack.Tests");
+ private RuntimeTestInfo LAMBDA033 = new RuntimeTestInfo(
+ AbstractTestTools.getTestRootDir("com.android.jack.java8.lambda.test033"),
+ "com.android.jack.java8.lambda.test033.jack.Tests")
+ .addProguardFlagsFileName("proguard.flags");
+
+ private RuntimeTestInfo LAMBDA034 = new RuntimeTestInfo(
+ AbstractTestTools.getTestRootDir("com.android.jack.java8.lambda.test034"),
+ "com.android.jack.java8.lambda.test034.jack.Tests");
+
+ private RuntimeTestInfo LAMBDA035 = new RuntimeTestInfo(
+ AbstractTestTools.getTestRootDir("com.android.jack.java8.lambda.test035"),
+ "com.android.jack.java8.lambda.test035.jack.Tests");
+
@Test
public void testLamba001() throws Exception {
run(LAMBDA001);
@@ -374,11 +386,25 @@
}
@Test
- @KnownIssue
public void testLamba032() throws Exception {
run(LAMBDA032);
}
+ @Test
+ public void testLamba033() throws Exception {
+ run(LAMBDA033);
+ }
+
+ @Test
+ public void testLamba034() throws Exception {
+ run(LAMBDA034);
+ }
+
+ @Test
+ public void testLamba035() throws Exception {
+ run(LAMBDA035);
+ }
+
private void run(@Nonnull RuntimeTestInfo rti) throws Exception {
new RuntimeTestHelper(rti)
.setSourceLevel(SourceLevel.JAVA_8)
diff --git a/jack-tests/tests/com/android/jack/java8/lambda/test033/jack/I.java b/jack-tests/tests/com/android/jack/java8/lambda/test033/jack/I.java
new file mode 100644
index 0000000..b37924d
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/lambda/test033/jack/I.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.java8.lambda.test033.jack;
+
+interface C<T> {
+ boolean check(T t);
+}
\ No newline at end of file
diff --git a/jack-tests/tests/com/android/jack/java8/lambda/test033/jack/Lambda.java b/jack-tests/tests/com/android/jack/java8/lambda/test033/jack/Lambda.java
new file mode 100644
index 0000000..74d413b
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/lambda/test033/jack/Lambda.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.java8.lambda.test033.jack;
+
+/**
+ * Lambda with obfuscation.
+ */
+public class Lambda {
+
+
+ public boolean testGet10(String s) {
+ C<String> c = (t) -> t.isEmpty();
+ return c.check(s);
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/java8/lambda/test033/jack/Tests.java b/jack-tests/tests/com/android/jack/java8/lambda/test033/jack/Tests.java
new file mode 100644
index 0000000..2f8ea55
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/lambda/test033/jack/Tests.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.java8.lambda.test033.jack;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Lambda with obfuscation to check that MethodId is well managed
+ */
+public class Tests {
+
+ @Test
+ public void test001() {
+ Assert.assertEquals(true, new Lambda().testGet10(""));
+ Assert.assertEquals(false, new Lambda().testGet10("notEmpty"));
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/java8/lambda/test033/mapping.flags b/jack-tests/tests/com/android/jack/java8/lambda/test033/mapping.flags
new file mode 100644
index 0000000..10efa52
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/lambda/test033/mapping.flags
@@ -0,0 +1,2 @@
+com.android.jack.java8.lambda.test033.jack.C -> com.android.jack.java8.lambda.test033.jack.Z:
+boolean check(java.lang.Object) -> renamedGetValue
\ No newline at end of file
diff --git a/jack-tests/tests/com/android/jack/java8/lambda/test033/proguard.flags b/jack-tests/tests/com/android/jack/java8/lambda/test033/proguard.flags
new file mode 100644
index 0000000..1a40ce3
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/lambda/test033/proguard.flags
@@ -0,0 +1,6 @@
+-applymapping mapping.flags
+
+-keepattributes
+-keep class * {
+ *;
+}
\ No newline at end of file
diff --git a/jack-tests/tests/com/android/jack/java8/lambda/test034/jack/Tests.java b/jack-tests/tests/com/android/jack/java8/lambda/test034/jack/Tests.java
new file mode 100644
index 0000000..9c8360c
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/lambda/test034/jack/Tests.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.java8.lambda.test034.jack;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+
+/**
+ * Lambda with reflect API
+ */
+public class Tests {
+
+ @Test
+ public void test001() throws Exception {
+ Callable<String> c = () -> "Hello";
+ Assert.assertEquals(1, c.getClass().getDeclaredMethods().length);
+ Assert.assertEquals(Object.class, c.getClass().getMethod("call").getReturnType());
+ }
+
+ interface Condition<T> {
+ boolean check(T arg);
+ }
+
+ @Test
+ public void test002() throws Exception {
+ Condition<String> c = String::isEmpty;
+ Assert.assertEquals(1, c.getClass().getDeclaredMethods().length);
+ Method m = c.getClass().getMethod("check", Object.class);
+ Assert.assertEquals(boolean.class, m.getReturnType());
+ Assert.assertFalse(m.isSynthetic());
+ Assert.assertFalse(m.isBridge());
+ }
+
+ @Test
+ public void test003() throws Exception {
+ Runnable r = () -> System.out.println("Hello");
+ Method m = r.getClass().getMethod("run");
+ Assert.assertSame(r.getClass(), m.getDeclaringClass());
+ Assert.assertFalse(m.isSynthetic());
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/java8/lambda/test035/jack/Tests.java b/jack-tests/tests/com/android/jack/java8/lambda/test035/jack/Tests.java
new file mode 100644
index 0000000..a33452d
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/lambda/test035/jack/Tests.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.java8.lambda.test035.jack;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+/**
+ * Lambda with obfuscation to check that MethodId is well managed
+ */
+public class Tests {
+
+ private static final String MSG = "Hello World";
+
+ private static String staticMethod() {
+ return MSG;
+ }
+
+ private interface MarkerInterface {
+ }
+
+ interface Condition<T> {
+ boolean check(T arg);
+ }
+
+ @Test
+ public void nonCapturingLambda() throws Exception {
+ Callable<String> r1 = () -> MSG;
+ Assert.assertNotNull(r1.getClass().getName());
+ assertGeneralLambdaClassCharacteristics(r1);
+ assertLambdaInterfaces(r1, Callable.class);
+ assertLambdaMethodCharacteristics(r1, Callable.class);
+ assertNonSerializableLambdaCharacteristics(r1);
+
+ assertCallableBehavior(r1, MSG);
+
+ Callable r2 = () -> MSG;
+ Assert.assertNotNull(r2.getClass().getName());
+ assertMultipleInstanceCharacteristics(r1, r2);
+ }
+
+ @Test
+ public void testInstanceMethodReferenceLambda() throws Exception {
+ Condition<String> c = String::isEmpty;
+ Class<?> lambdaClass = c.getClass();
+ Assert.assertNotNull(lambdaClass.getName());
+ assertGeneralLambdaClassCharacteristics(c);
+ assertLambdaInterfaces(c, Condition.class);
+ assertLambdaMethodCharacteristics(c, Condition.class);
+ assertNonSerializableLambdaCharacteristics(c);
+
+ Assert.assertTrue(c.check(""));
+ Assert.assertFalse(c.check("notEmpty"));
+
+ Method implCallMethod =
+ lambdaClass.getMethod("check", Object.class /* type erasure => not String.class */);
+ Assert.assertTrue((Boolean) implCallMethod.invoke(c, ""));
+ Assert.assertFalse((Boolean) implCallMethod.invoke(c, "notEmpty"));
+
+ Method interfaceCallMethod = Condition.class.getDeclaredMethod("check",
+ Object.class /* type erasure => not String.class */);
+ Assert.assertTrue((Boolean) interfaceCallMethod.invoke(c, ""));
+ Assert.assertFalse((Boolean) interfaceCallMethod.invoke(c, "notEmpty"));
+ }
+
+ @Test
+ public void testStaticMethodReferenceLambda() throws Exception {
+ Callable<String> r1 = Tests::staticMethod;
+ Assert.assertNotNull(r1.getClass().getName());
+ assertGeneralLambdaClassCharacteristics(r1);
+ assertLambdaInterfaces(r1, Callable.class);
+ assertLambdaMethodCharacteristics(r1, Callable.class);
+ assertNonSerializableLambdaCharacteristics(r1);
+
+ assertCallableBehavior(r1, MSG);
+
+ Callable<String> r2 = Tests::staticMethod;
+ Assert.assertNotNull(r2.getClass().getName());
+ assertMultipleInstanceCharacteristics(r1, r2);
+ }
+
+ @Test
+ public void testObjectMethodReferenceLambda() throws Exception {
+ StringBuilder o = new StringBuilder(MSG);
+ Callable<String> r1 = o::toString;
+ Assert.assertNotNull(r1.getClass().getName());
+ assertGeneralLambdaClassCharacteristics(r1);
+ assertLambdaInterfaces(r1, Callable.class);
+ assertLambdaMethodCharacteristics(r1, Callable.class);
+ assertNonSerializableLambdaCharacteristics(r1);
+
+ assertCallableBehavior(r1, MSG);
+
+ Callable<String> r2 = o::toString;
+ Assert.assertNotNull(r2.getClass().getName());
+ assertMultipleInstanceCharacteristics(r1, r2);
+ }
+
+ @Test
+ public void testArgumentCapturingLambda() throws Exception {
+ String msg = MSG;
+ Callable<String> r1 = () -> msg;
+ Assert.assertNotNull(r1.getClass().getName());
+ assertGeneralLambdaClassCharacteristics(r1);
+ assertLambdaInterfaces(r1, Callable.class);
+ assertLambdaMethodCharacteristics(r1, Callable.class);
+ assertNonSerializableLambdaCharacteristics(r1);
+
+ assertCallableBehavior(r1, MSG);
+
+ Callable<String> r2 = () -> msg;
+ Assert.assertNotNull(r2.getClass().getName());
+ assertMultipleInstanceCharacteristics(r1, r2);
+ }
+
+ @Test
+ public void testMultipleInterfaceLambda() throws Exception {
+ Callable<String> r1 = (Callable<String> & MarkerInterface) () -> MSG;
+ Assert.assertTrue(r1 instanceof MarkerInterface);
+ Assert.assertNotNull(r1.getClass().getName());
+ assertGeneralLambdaClassCharacteristics(r1);
+ assertLambdaMethodCharacteristics(r1, Callable.class);
+ assertLambdaInterfaces(r1, Callable.class, MarkerInterface.class);
+ assertNonSerializableLambdaCharacteristics(r1);
+
+ assertCallableBehavior(r1, MSG);
+ }
+
+
+
+ private static void assertNonSerializableLambdaCharacteristics(Object r1) throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (ObjectOutputStream os = new ObjectOutputStream(baos)) {
+ os.writeObject(r1);
+ os.flush();
+ Assert.fail();
+ } catch (ObjectStreamException expected) {
+ }
+ }
+
+ private static void assertMultipleInstanceCharacteristics(Object r1, Object r2) throws Exception {
+ Assert.assertNotSame(r1, r2);
+ Assert.assertTrue(!r1.equals(r2));
+
+ Class<?> lambda1Class = r1.getClass();
+ Class<?> lambda2Class = r2.getClass();
+ Assert.assertNotSame(lambda1Class, lambda2Class);
+ }
+
+ private static <T> void assertCallableBehavior(Callable<T> r1, T expectedResult)
+ throws Exception {
+ Assert.assertEquals(expectedResult, r1.call());
+ Method implCallMethod = r1.getClass().getDeclaredMethod("call");
+ Assert.assertEquals(expectedResult, implCallMethod.invoke(r1));
+
+ Method interfaceCallMethod = Callable.class.getDeclaredMethod("call");
+ Assert.assertEquals(expectedResult, interfaceCallMethod.invoke(r1));
+ }
+
+ private static void assertGeneralLambdaClassCharacteristics(Object r1) throws Exception {
+ Class<?> lambdaClass = r1.getClass();
+
+ Assert.assertFalse(lambdaClass.isAnnotation());
+ Assert.assertFalse(lambdaClass.isAnonymousClass());
+ Assert.assertFalse(lambdaClass.isInterface());
+ Assert.assertFalse(lambdaClass.isLocalClass());
+ Assert.assertNull(lambdaClass.getEnclosingMethod());
+ Assert.assertNull(lambdaClass.getEnclosingConstructor());
+ Assert.assertNotNull(lambdaClass.getDeclaringClass());
+ Assert.assertNotNull(lambdaClass.getEnclosingClass());
+ Assert.assertTrue(lambdaClass.isMemberClass());
+ Assert.assertEquals(1, lambdaClass.getConstructors().length);
+
+ Assert.assertNotNull(lambdaClass.getSimpleName());
+ Assert.assertNotNull(lambdaClass.getCanonicalName());
+ Assert.assertEquals(0, lambdaClass.getClasses().length);
+ Assert.assertFalse(lambdaClass.isArray());
+ Assert.assertFalse(lambdaClass.isEnum());
+ Assert.assertFalse(lambdaClass.isPrimitive());
+ Assert.assertTrue(lambdaClass.isSynthetic());
+ Assert.assertNull(lambdaClass.getComponentType());
+ Assert.assertTrue((Modifier.isFinal(lambdaClass.getModifiers())));
+ // Unexpected modifiers
+ int unexpectedModifiers = Modifier.PRIVATE | Modifier.PUBLIC | Modifier.PROTECTED
+ | Modifier.STATIC | Modifier.SYNCHRONIZED | Modifier.VOLATILE | Modifier.TRANSIENT
+ | Modifier.NATIVE | Modifier.INTERFACE | Modifier.ABSTRACT | Modifier.STRICT;
+ Assert.assertTrue((unexpectedModifiers & lambdaClass.getModifiers()) == 0);
+ Assert.assertNull(lambdaClass.getSigners());
+
+ Assert.assertSame(Tests.class.getClassLoader(), lambdaClass.getClassLoader());
+ Assert.assertEquals(0, lambdaClass.getTypeParameters().length);
+
+ Assert.assertSame(Object.class, lambdaClass.getSuperclass());
+ Assert.assertSame(Object.class, lambdaClass.getGenericSuperclass());
+ Assert.assertEquals(Tests.class.getPackage(), lambdaClass.getPackage());
+
+ Assert.assertNotNull(r1.toString());
+ Assert.assertTrue(r1.equals(r1));
+ Assert.assertEquals(System.identityHashCode(r1), r1.hashCode());
+
+ }
+
+ private static <T> void assertLambdaMethodCharacteristics(T r1, Class<?> samInterfaceClass)
+ throws Exception {
+ Method singleAbstractMethod = null;
+ for (Method method : samInterfaceClass.getDeclaredMethods()) {
+ if (Modifier.isAbstract(method.getModifiers())) {
+ singleAbstractMethod = method;
+ break;
+ }
+ }
+ Assert.assertNotNull(singleAbstractMethod);
+
+ Method implementationMethod = r1.getClass().getMethod(singleAbstractMethod.getName(),
+ singleAbstractMethod.getParameterTypes());
+ Assert.assertSame(singleAbstractMethod.getReturnType(), implementationMethod.getReturnType());
+ Assert.assertSame(r1.getClass(), implementationMethod.getDeclaringClass());
+ Assert.assertFalse(implementationMethod.isSynthetic());
+ Assert.assertFalse(implementationMethod.isBridge());
+ }
+
+ private static <T> void assertLambdaInterfaces(T r1, Class<?>... interfaceClasses)
+ throws Exception {
+ Class<?> lambdaClass = r1.getClass();
+ Assert.assertEquals(interfaceClasses.length, lambdaClass.getInterfaces().length);
+ Set<Class<?>> actual = new HashSet<>(Arrays.asList(lambdaClass.getInterfaces()));
+ Set<Class<?>> expected = new HashSet<>(Arrays.asList(interfaceClasses));
+ Assert.assertEquals(expected, actual);
+
+ Set<Method> declaredMethods = new HashSet<>();
+ addNonStaticPublicMethods(declaredMethods, lambdaClass);
+ Set<Method> expectedMethods = new HashSet<>();
+ for (Class<?> interfaceClass : interfaceClasses) {
+ while (interfaceClass != null) {
+ addNonStaticPublicMethods(expectedMethods, interfaceClass);
+ interfaceClass = interfaceClass.getSuperclass();
+ }
+ }
+ Assert.assertEquals(expectedMethods.size(), declaredMethods.size());
+ for (Method expectedMethod : expectedMethods) {
+ Method actualMethod =
+ lambdaClass.getMethod(expectedMethod.getName(), expectedMethod.getParameterTypes());
+ Assert.assertEquals(expectedMethod.getReturnType(), actualMethod.getReturnType());
+ }
+ }
+
+ private static void addNonStaticPublicMethods(Set<Method> methodSet, Class<?> clazz) {
+ for (Method interfaceMethod : clazz.getDeclaredMethods()) {
+ int modifiers = interfaceMethod.getModifiers();
+ if ((!Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
+ methodSet.add(interfaceMethod);
+ }
+ }
+ }
+}
diff --git a/jack/prebuilts/core-stubs-mini.jack b/jack/prebuilts/core-stubs-mini.jack
index 07a520a..d1d6faa 100644
--- a/jack/prebuilts/core-stubs-mini.jack
+++ b/jack/prebuilts/core-stubs-mini.jack
Binary files differ
diff --git a/jack/prebuilts/core-stubs-mini.jar b/jack/prebuilts/core-stubs-mini.jar
index c18c4d0..91452bb 100644
--- a/jack/prebuilts/core-stubs-mini.jar
+++ b/jack/prebuilts/core-stubs-mini.jar
Binary files differ
diff --git a/jack/src/com/android/jack/frontend/MethodIdDuplicateRemover.java b/jack/src/com/android/jack/frontend/MethodIdDuplicateRemover.java
index 83767af..65cc08f 100644
--- a/jack/src/com/android/jack/frontend/MethodIdDuplicateRemover.java
+++ b/jack/src/com/android/jack/frontend/MethodIdDuplicateRemover.java
@@ -18,11 +18,16 @@
import com.android.jack.ir.ast.JAnnotation;
import com.android.jack.ir.ast.JClassOrInterface;
+import com.android.jack.ir.ast.JInterface;
+import com.android.jack.ir.ast.JLambda;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JMethodCall;
import com.android.jack.ir.ast.JMethodId;
+import com.android.jack.ir.ast.JMethodIdWithReturnType;
import com.android.jack.ir.ast.JNameValuePair;
import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.ast.MethodKind;
+import com.android.jack.lookup.JMethodWithReturnLookupException;
import java.util.Collection;
@@ -50,6 +55,20 @@
}
@Override
+ public boolean visit(@Nonnull JLambda lambda) {
+ JInterface lambdaType = lambda.getType();
+ try {
+ JMethodIdWithReturnType mthIdWithReturnType = lambda.getMethodIdToImplement();
+ JMethodId newMthId = lambdaType.getMethodId(mthIdWithReturnType.getName(),
+ mthIdWithReturnType.getParameterTypes(), MethodKind.INSTANCE_VIRTUAL);
+ mthIdWithReturnType.setMethodId(newMthId);
+ } catch (JMethodWithReturnLookupException e) {
+ throw new AssertionError();
+ }
+ return super.visit(lambda);
+ }
+
+ @Override
public boolean visit(@Nonnull JMethodCall call) {
JMethodId id = getResolvedMethodId(call.getReceiverType(), call.getMethodId());
call.resolveMethodId(id);
diff --git a/jack/src/com/android/jack/frontend/MethodIdMerger.java b/jack/src/com/android/jack/frontend/MethodIdMerger.java
index d5eb410..622a607 100644
--- a/jack/src/com/android/jack/frontend/MethodIdMerger.java
+++ b/jack/src/com/android/jack/frontend/MethodIdMerger.java
@@ -23,7 +23,6 @@
import com.android.jack.ir.ast.JDefinedClassOrInterface;
import com.android.jack.ir.ast.JDefinedInterface;
import com.android.jack.ir.ast.JInterface;
-import com.android.jack.ir.ast.JLambda;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JMethodId;
import com.android.jack.ir.ast.JNode;
@@ -74,18 +73,6 @@
return super.visit(node);
}
- @Override
- public boolean visit(@Nonnull JLambda lambda) {
- // Ensure that interface implemented by a lambda is already visited
- JDefinedInterface type = lambda.getType();
- accept(type);
- VirtualMethodsMarker vmm = type.getMarker(VirtualMethodsMarker.class);
- assert vmm != null;
- addId(vmm, lambda.getMethod().getMethodId());
- accept(lambda.getBody());
- return super.visit(lambda);
- }
-
private void ensureHierarchyVisited(@Nonnull JClassOrInterface node) {
JClass zuper = getSuper(node);
if (zuper != null) {
diff --git a/jack/src/com/android/jack/ir/ast/JLambda.java b/jack/src/com/android/jack/ir/ast/JLambda.java
index d57d998..7a3c857 100644
--- a/jack/src/com/android/jack/ir/ast/JLambda.java
+++ b/jack/src/com/android/jack/ir/ast/JLambda.java
@@ -33,38 +33,58 @@
@Description("Lambda expression")
public class JLambda extends JExpression {
+ @Nonnull
+ private final JMethodIdWithReturnType mthIdToImplement;
+
+ @Nonnull
+ private final JInterface type;
+
+ @Nonnull
+ private final List<JInterface> interfaceBounds;
+
private boolean captureInstance;
@Nonnull
private final List<JVariableRef> capturedVariablesRef = new ArrayList<JVariableRef>(0);
+ // TODO(jack-team): JMethod must be replace by a JCallable
@Nonnull
private final JMethod method;
@Nonnull
- private final JDefinedInterface type;
+ private final List<JMethodIdWithReturnType> bridges = new ArrayList<JMethodIdWithReturnType>();
- @Nonnull
- private final JMethodBody body;
-
- @Nonnull
- private final List<JInterface> interfaceBounds;
-
- public JLambda(@Nonnull SourceInfo info, @Nonnull JMethod method, @Nonnull JDefinedInterface type,
- boolean captureInstance, @Nonnull List<JInterface> interfaceBounds) {
+ public JLambda(@Nonnull SourceInfo info, @Nonnull JMethodIdWithReturnType mthToImplement,
+ @Nonnull JMethod method, @Nonnull JInterface type, boolean captureInstance,
+ @Nonnull List<JInterface> interfaceBounds) {
super(info);
assert method != null;
assert type != null;
- assert type.isSingleAbstractMethodType();
+ this.mthIdToImplement = mthToImplement;
this.type = type;
this.method = method;
this.captureInstance = captureInstance;
- JMethodBody localBody = (JMethodBody) method.getBody();
- assert localBody != null;
- body = localBody;
this.interfaceBounds = interfaceBounds;
}
+ @Nonnull
+ public JMethodIdWithReturnType getMethodIdToImplement() {
+ return mthIdToImplement;
+ }
+
+ @Nonnull
+ public List<JMethodIdWithReturnType> getBridgeMethodIds() {
+ return bridges;
+ }
+
+ public void addBridgeMethodId(@Nonnull JMethodIdWithReturnType bridgeMethodId) {
+ this.bridges.add(bridgeMethodId);
+ }
+
+ public void addBridgeMethodIds(@Nonnull List<JMethodIdWithReturnType> bridgeMethodIds) {
+ this.bridges.addAll(bridgeMethodIds);
+ }
+
public void addCapturedVariable(@Nonnull JVariableRef capturedVariableRef) {
capturedVariablesRef.add(capturedVariableRef);
}
@@ -80,6 +100,8 @@
@Override
public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
schedule.process(this);
+ JMethodBody body = (JMethodBody) method.getBody();
+ assert body != null;
body.traverse(schedule);
}
@@ -90,13 +112,15 @@
@Override
@Nonnull
- public JDefinedInterface getType() {
+ public JInterface getType() {
return type;
}
@Nonnull
- public JBlock getBody() {
- return body.getBlock();
+ public JMethodBody getBody() {
+ JMethodBody body = (JMethodBody) method.getBody();
+ assert body != null;
+ return body;
}
@Nonnull
@@ -104,6 +128,7 @@
return method.getParams();
}
+ @Deprecated
@Nonnull
public JMethod getMethod() {
return method;
diff --git a/jack/src/com/android/jack/ir/ast/JMethodIdWithReturnType.java b/jack/src/com/android/jack/ir/ast/JMethodIdWithReturnType.java
new file mode 100644
index 0000000..a1f006d
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/JMethodIdWithReturnType.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * {@link JMethodIdWithReturnType} describes a method by using a {@link JMethodId} and its return
+ * type.
+ */
+public class JMethodIdWithReturnType {
+
+ @Nonnull
+ private JMethodId methodId;
+
+ @Nonnull
+ private final JType returnType;
+
+ public JMethodIdWithReturnType(@Nonnull JMethodId methodId, @Nonnull JType returnType) {
+ this.methodId = methodId;
+ this.returnType = returnType;
+ }
+
+ @Nonnull
+ public JMethodId getMethodId() {
+ return methodId;
+ }
+
+ public void setMethodId(@Nonnull JMethodId newMthId) {
+ methodId = newMthId;
+ }
+
+ @Nonnull
+ public JType getReturnType() {
+ return returnType;
+ }
+
+ @Nonnull
+ public List<JType> getParameterTypes() {
+ return methodId.getParamTypes();
+ }
+
+ @Nonnull
+ public String getName() {
+ return methodId.getName();
+ }
+}
\ No newline at end of file
diff --git a/jack/src/com/android/jack/ir/impl/JackIrBuilder.java b/jack/src/com/android/jack/ir/impl/JackIrBuilder.java
index 0557d0b..966b172 100644
--- a/jack/src/com/android/jack/ir/impl/JackIrBuilder.java
+++ b/jack/src/com/android/jack/ir/impl/JackIrBuilder.java
@@ -80,6 +80,7 @@
import com.android.jack.ir.ast.JMethodBody;
import com.android.jack.ir.ast.JMethodCall;
import com.android.jack.ir.ast.JMethodId;
+import com.android.jack.ir.ast.JMethodIdWithReturnType;
import com.android.jack.ir.ast.JModifier;
import com.android.jack.ir.ast.JMultiExpression;
import com.android.jack.ir.ast.JNameValuePair;
@@ -1394,6 +1395,8 @@
MethodInfo newMethodInfo =
createMethodInfoForLambda(referenceExpression.descriptor, /* arguments = */ null,
/* (MethodScope) referenceExpression.enclosingScope */ curMethod.scope);
+ JMethodIdWithReturnType methodIdToImplement =
+ getJMethodIdWithReturnType(referenceExpression.descriptor.original());
newMethodInfo.method.setThis(null);
newMethodInfo.method.setModifier(newMethodInfo.method.getModifier() & ~JModifier.STATIC);
JType returnTypeOfLambdaMethod = newMethodInfo.method.getType();
@@ -1407,10 +1410,10 @@
|| referenceExpression.lhs instanceof QualifiedThisReference;
boolean shouldCaptureInstance = isSuperRef || isThisRef;
- exprRepresentingLambda =
- new JLambda(sourceInfo, newMethodInfo.method,
- (JDefinedInterface) getTypeMap().get(referenceExpression.resolvedType),
- shouldCaptureInstance, getInterfaceBounds(referenceExpression, blockScope));
+ exprRepresentingLambda = new JLambda(sourceInfo, methodIdToImplement, newMethodInfo.method,
+ (JDefinedInterface) getTypeMap().get(referenceExpression.resolvedType),
+ shouldCaptureInstance, getInterfaceBounds(referenceExpression, blockScope));
+ ((JLambda) exprRepresentingLambda).addBridgeMethodIds(getBridges(referenceExpression));
if (!(referenceExpression.lhs instanceof TypeReference)) {
if (lhsExpr != null && !shouldCaptureInstance) {
@@ -1472,9 +1475,12 @@
} else if (referenceExpression.isArrayConstructorReference()) {
assert args.size() == 1;
- exprRepresentingLambda = new JLambda(sourceInfo, newMethodInfo.method,
- (JDefinedInterface) getTypeMap().get(referenceExpression.resolvedType),
- /* captureInstance= */ false, getInterfaceBounds(referenceExpression, blockScope));
+ exprRepresentingLambda =
+ new JLambda(sourceInfo, methodIdToImplement, newMethodInfo.method,
+ (JDefinedInterface) getTypeMap().get(referenceExpression.resolvedType),
+ /* captureInstance= */ false, getInterfaceBounds(referenceExpression, blockScope));
+ ((JLambda) exprRepresentingLambda)
+ .addBridgeMethodIds(getBridges(referenceExpression));
Expression lhs = referenceExpression.lhs;
JArrayType arrayType = (JArrayType) getTypeMap().get(lhs.resolvedType);
@@ -1530,9 +1536,12 @@
}
}
- JLambda lambda = new JLambda(sourceInfo, newMethodInfo.method,
+ JLambda lambda = new JLambda(sourceInfo,
+ getJMethodIdWithReturnType(referenceExpression.descriptor.original()),
+ newMethodInfo.method,
(JDefinedInterface) getTypeMap().get(referenceExpression.resolvedType),
shouldCaptureInstance, getInterfaceBounds(referenceExpression, blockScope));
+ lambda.addBridgeMethodIds(getBridges(referenceExpression));
addArgToMethodCall(referenceExpression, argsOfLambdaMth, constructor,
newInstance, paramCountCons, 0);
@@ -1663,6 +1672,32 @@
return true;
}
+ @Nonnull
+ private JMethodIdWithReturnType getJMethodIdWithReturnType(@Nonnull MethodBinding mb) {
+ JMethodId methodId =
+ new JMethodId(ReferenceMapper.intern(mb.selector), MethodKind.INSTANCE_VIRTUAL);
+
+ for (TypeBinding parameterType : mb.parameters) {
+ methodId.addParam(getTypeMap().get(parameterType));
+ }
+
+ return new JMethodIdWithReturnType(methodId, getTypeMap().get(mb.returnType));
+ }
+
+ @Nonnull
+ private List<JMethodIdWithReturnType> getBridges(@Nonnull FunctionalExpression fe) {
+ MethodBinding[] bridges = fe.getRequiredBridges();
+ List<JMethodIdWithReturnType> mds = new ArrayList<JMethodIdWithReturnType>();
+
+ if (bridges != null) {
+ for (MethodBinding bridge : bridges) {
+ mds.add(getJMethodIdWithReturnType(bridge));
+ }
+ }
+
+ return mds;
+ }
+
@Override
public void endVisit(LambdaExpression lambdaExpression, BlockScope blockScope) {
MethodInfo lambdaMethodInfo = curMethod;
@@ -1695,10 +1730,12 @@
popMethodInfo();
SourceInfo sourceInfo = makeSourceInfo(lambdaExpression);
- JLambda lambda = new JLambda(sourceInfo, lambdaMethodInfo.method,
+ JLambda lambda = new JLambda(sourceInfo,
+ getJMethodIdWithReturnType(lambdaExpression.descriptor.original()),
+ lambdaMethodInfo.method,
(JDefinedInterface) getTypeMap().get(lambdaExpression.resolvedType),
- lambdaExpression.shouldCaptureInstance,
- getInterfaceBounds(lambdaExpression, blockScope));
+ lambdaExpression.shouldCaptureInstance, getInterfaceBounds(lambdaExpression, blockScope));
+ lambda.addBridgeMethodIds(getBridges(lambdaExpression));
// Capture all local variable that are not already moved into fields
for (SyntheticArgumentBinding synthArg : lambdaExpression.outerLocalVariables) {
diff --git a/jack/src/com/android/jack/jayce/v0003/NodeFactory.java b/jack/src/com/android/jack/jayce/v0003/NodeFactory.java
index 7343d9c..6bb0cd2 100644
--- a/jack/src/com/android/jack/jayce/v0003/NodeFactory.java
+++ b/jack/src/com/android/jack/jayce/v0003/NodeFactory.java
@@ -93,6 +93,7 @@
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JMethodBody;
import com.android.jack.ir.ast.JMethodCall;
+import com.android.jack.ir.ast.JMethodIdWithReturnType;
import com.android.jack.ir.ast.JMethodLiteral;
import com.android.jack.ir.ast.JModOperation;
import com.android.jack.ir.ast.JMulOperation;
@@ -209,6 +210,7 @@
import com.android.jack.jayce.v0003.nodes.NMethod;
import com.android.jack.jayce.v0003.nodes.NMethodBody;
import com.android.jack.jayce.v0003.nodes.NMethodCall;
+import com.android.jack.jayce.v0003.nodes.NMethodIdWithReturnType;
import com.android.jack.jayce.v0003.nodes.NMethodLiteral;
import com.android.jack.jayce.v0003.nodes.NModOperation;
import com.android.jack.jayce.v0003.nodes.NMulOperation;
@@ -616,6 +618,7 @@
return false;
}
+
@Override
public boolean visit(@Nonnull JMethodLiteral x) {
newNode = new NMethodLiteral();
@@ -777,6 +780,8 @@
}
} else if (from instanceof Marker) {
return createMarkerNode((Marker) from);
+ } else if (from instanceof JMethodIdWithReturnType) {
+ return new NMethodIdWithReturnType();
}
throw new AssertionError("Not yet implemented (" + from.getClass().getCanonicalName() + ")");
}
diff --git a/jack/src/com/android/jack/jayce/v0003/Version.java b/jack/src/com/android/jack/jayce/v0003/Version.java
index 305ec4c..e1b2b0b 100644
--- a/jack/src/com/android/jack/jayce/v0003/Version.java
+++ b/jack/src/com/android/jack/jayce/v0003/Version.java
@@ -21,7 +21,7 @@
*/
public class Version {
- public static final int MINOR_MIN = 2;
+ public static final int MINOR_MIN = 3;
- public static final int CURRENT_MINOR = 2;
+ public static final int CURRENT_MINOR = 3;
}
diff --git a/jack/src/com/android/jack/jayce/v0003/io/Token.java b/jack/src/com/android/jack/jayce/v0003/io/Token.java
index 2cceb52..edd07b4 100644
--- a/jack/src/com/android/jack/jayce/v0003/io/Token.java
+++ b/jack/src/com/android/jack/jayce/v0003/io/Token.java
@@ -93,6 +93,7 @@
import com.android.jack.jayce.v0003.nodes.NMethod;
import com.android.jack.jayce.v0003.nodes.NMethodBody;
import com.android.jack.jayce.v0003.nodes.NMethodCall;
+import com.android.jack.jayce.v0003.nodes.NMethodIdWithReturnType;
import com.android.jack.jayce.v0003.nodes.NMethodLiteral;
import com.android.jack.jayce.v0003.nodes.NModOperation;
import com.android.jack.jayce.v0003.nodes.NMulOperation;
@@ -676,6 +677,13 @@
return new NMethodCall();
}
},
+ METHODID_WITH_RETURN_TYPE("method-id-with-return-type", NodeLevel.STRUCTURE) {
+ @Nonnull
+ @Override
+ public NNode newNode() {
+ return new NMethodIdWithReturnType();
+ }
+ },
METHOD_LITERAL("method-literal", NodeLevel.STRUCTURE) {
@Nonnull
@Override
diff --git a/jack/src/com/android/jack/jayce/v0003/nodes/NLambda.java b/jack/src/com/android/jack/jayce/v0003/nodes/NLambda.java
index 1edd24e..780440c 100644
--- a/jack/src/com/android/jack/jayce/v0003/nodes/NLambda.java
+++ b/jack/src/com/android/jack/jayce/v0003/nodes/NLambda.java
@@ -16,18 +16,15 @@
package com.android.jack.jayce.v0003.nodes;
-import com.android.jack.ir.ast.JDefinedInterface;
import com.android.jack.ir.ast.JExpression;
import com.android.jack.ir.ast.JInterface;
import com.android.jack.ir.ast.JLambda;
import com.android.jack.ir.ast.JMethod;
-import com.android.jack.ir.ast.JPhantomInterface;
+import com.android.jack.ir.ast.JMethodIdWithReturnType;
import com.android.jack.ir.ast.JTypeLookupException;
import com.android.jack.ir.ast.JVariableRef;
-import com.android.jack.ir.ast.MissingJTypeLookupException;
import com.android.jack.jayce.JayceClassOrInterfaceLoader;
import com.android.jack.jayce.NodeLevel;
-import com.android.jack.jayce.linker.VariableRefLinker;
import com.android.jack.jayce.v0003.io.ExportSession;
import com.android.jack.jayce.v0003.io.ImportHelper;
import com.android.jack.jayce.v0003.io.JayceInternalReaderImpl;
@@ -37,6 +34,7 @@
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import javax.annotation.CheckForNull;
@@ -65,7 +63,13 @@
public NSourceInfo sourceInfo;
@Nonnull
- private List<String> boundsIds = new ArrayList<String>();
+ private List<String> boundsIds = Collections.emptyList();
+
+ @CheckForNull
+ private NMethodIdWithReturnType mthIdToImplement;
+
+ @Nonnull
+ private List<NMethodIdWithReturnType> bridges = Collections.emptyList();
@Override
public void importFromJast(@Nonnull ImportHelper loader, @Nonnull Object node) {
@@ -78,6 +82,8 @@
typeSig = ImportHelper.getSignatureName(lambda.getType());
sourceInfo = loader.load(lambda.getSourceInfo());
boundsIds = ImportHelper.getSignatureNameList(lambda.getInterfaceBounds());
+ mthIdToImplement = (NMethodIdWithReturnType) loader.load(lambda.getMethodIdToImplement());
+ bridges = loader.load(NMethodIdWithReturnType.class, lambda.getBridgeMethodIds());
}
@Override
@@ -88,6 +94,7 @@
assert capturedVariableIds != null;
assert method != null;
assert typeSig != null;
+ assert mthIdToImplement != null;
ExportSession exportSessionForLambdaMethStructure = new ExportSession(exportSession.getLookup(),
exportSession.getSession(), NodeLevel.STRUCTURE);
@@ -115,17 +122,14 @@
jBounds.add(exportSession.getLookup().getInterface(bound));
}
- JInterface implementedInterface = exportSession.getLookup().getInterface(typeSig);
- if (implementedInterface instanceof JPhantomInterface) {
- throw new MissingJTypeLookupException((JPhantomInterface) implementedInterface);
- }
- JLambda lambda = new JLambda(sourceInfo.exportAsJast(exportSession), lambdaMethod,
- (JDefinedInterface) implementedInterface, captureInstance,
- jBounds);
+ JMethodIdWithReturnType mthIdToImplements =
+ (JMethodIdWithReturnType) mthIdToImplement.exportAsJast(exportSession);
- for (String capturedVariableId : capturedVariableIds) {
- exportSession.getVariableResolver().addLink(capturedVariableId,
- new VariableRefLinker(lambda));
+ JLambda lambda = new JLambda(sourceInfo.exportAsJast(exportSession), mthIdToImplements,
+ lambdaMethod, exportSession.getLookup().getInterface(typeSig), captureInstance, jBounds);
+
+ for (NMethodIdWithReturnType bridge : bridges) {
+ lambda.addBridgeMethodId((JMethodIdWithReturnType) bridge.exportAsJast(exportSession));
}
return lambda;
@@ -138,6 +142,8 @@
out.writeNode(method);
out.writeId(typeSig);
out.writeIds(boundsIds);
+ out.writeNode(mthIdToImplement);
+ out.writeNodes(bridges);
}
@Override
@@ -147,6 +153,8 @@
method = in.readNode(NMethod.class);
typeSig = in.readId();
boundsIds = in.readIds();
+ mthIdToImplement = in.readNode(NMethodIdWithReturnType.class);
+ bridges = in.readNodes(NMethodIdWithReturnType.class);
}
@Override
diff --git a/jack/src/com/android/jack/jayce/v0003/nodes/NMethodIdWithReturnType.java b/jack/src/com/android/jack/jayce/v0003/nodes/NMethodIdWithReturnType.java
new file mode 100644
index 0000000..d38c5f9
--- /dev/null
+++ b/jack/src/com/android/jack/jayce/v0003/nodes/NMethodIdWithReturnType.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.jayce.v0003.nodes;
+
+import com.android.jack.ir.ast.JMethodId;
+import com.android.jack.ir.ast.JMethodIdWithReturnType;
+import com.android.jack.ir.ast.JType;
+import com.android.jack.ir.ast.JTypeLookupException;
+import com.android.jack.ir.ast.MethodKind;
+import com.android.jack.jayce.v0003.NNode;
+import com.android.jack.jayce.v0003.io.ExportSession;
+import com.android.jack.jayce.v0003.io.ImportHelper;
+import com.android.jack.jayce.v0003.io.JayceInternalReaderImpl;
+import com.android.jack.jayce.v0003.io.JayceInternalWriterImpl;
+import com.android.jack.jayce.v0003.io.Token;
+import com.android.jack.lookup.JMethodLookupException;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * Node representing a method id with a return type.
+ */
+public class NMethodIdWithReturnType extends NNode {
+ @Nonnull
+ public static final Token TOKEN = Token.METHODID_WITH_RETURN_TYPE;
+
+ @CheckForNull String name;
+
+ @CheckForNull String returnTypeSig;
+
+ @Nonnull
+ private List<String> paramTypeSigs = Collections.emptyList();
+
+ @CheckForNull
+ private MethodKind methodKind;
+
+ @Override
+ public void importFromJast(@Nonnull ImportHelper loader, @Nonnull Object node) {
+ JMethodIdWithReturnType mthIdToImplement = (JMethodIdWithReturnType) node;
+ name = mthIdToImplement.getName();
+ returnTypeSig = ImportHelper.getSignatureName(mthIdToImplement.getReturnType());
+ paramTypeSigs = ImportHelper.getSignatureNameList(mthIdToImplement.getParameterTypes());
+ methodKind = mthIdToImplement.getMethodId().getKind();
+ }
+
+ @Override
+ @Nonnull
+ public Object exportAsJast(@Nonnull ExportSession exportSession)
+ throws JTypeLookupException, JMethodLookupException {
+ assert name != null;
+ assert returnTypeSig != null;
+ assert methodKind != null;
+ JType returnType = exportSession.getLookup().getType(returnTypeSig);
+ JMethodId mthId = new JMethodId(name, methodKind);
+ for (String paramSig : paramTypeSigs) {
+ mthId.addParam(exportSession.getLookup().getType(paramSig));
+ }
+ return new JMethodIdWithReturnType(mthId, returnType);
+ }
+
+ @Override
+ public void writeContent(@Nonnull JayceInternalWriterImpl out) throws IOException {
+ assert methodKind != null;
+ out.writeId(name);
+ out.writeMethodKindEnum(methodKind);
+ out.writeId(returnTypeSig);
+ out.writeIds(paramTypeSigs);
+ }
+
+ @Override
+ public void readContent(@Nonnull JayceInternalReaderImpl in) throws IOException {
+ name = in.readId();
+ methodKind = in.readMethodKindEnum();
+ returnTypeSig = in.readId();
+ paramTypeSigs = in.readIds();
+ }
+
+ @Override
+ @Nonnull
+ public Token getToken() {
+ return TOKEN;
+ }
+}
\ No newline at end of file
diff --git a/jack/src/com/android/jack/library/v0002/Version.java b/jack/src/com/android/jack/library/v0002/Version.java
index 0fbaa27..ab41e3f 100644
--- a/jack/src/com/android/jack/library/v0002/Version.java
+++ b/jack/src/com/android/jack/library/v0002/Version.java
@@ -21,9 +21,9 @@
*/
public class Version {
- public static final int MINOR_MIN = 1;
+ public static final int MINOR_MIN = 2;
- public static final int MINOR = 1;
+ public static final int MINOR = 2;
public static final int MAJOR = 2;
}
diff --git a/jack/src/com/android/jack/transformations/lambda/LambdaConverter.java b/jack/src/com/android/jack/transformations/lambda/LambdaConverter.java
index 88b450e..14f5280 100644
--- a/jack/src/com/android/jack/transformations/lambda/LambdaConverter.java
+++ b/jack/src/com/android/jack/transformations/lambda/LambdaConverter.java
@@ -18,6 +18,7 @@
import com.android.jack.Jack;
import com.android.jack.ir.ast.JAsgOperation;
+import com.android.jack.ir.ast.JBinaryOperator;
import com.android.jack.ir.ast.JBlock;
import com.android.jack.ir.ast.JClass;
import com.android.jack.ir.ast.JConstructor;
@@ -36,12 +37,14 @@
import com.android.jack.ir.ast.JMethodBody;
import com.android.jack.ir.ast.JMethodCall;
import com.android.jack.ir.ast.JMethodId;
+import com.android.jack.ir.ast.JMethodIdWithReturnType;
import com.android.jack.ir.ast.JModifier;
import com.android.jack.ir.ast.JNewInstance;
import com.android.jack.ir.ast.JParameter;
import com.android.jack.ir.ast.JParameterRef;
import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
import com.android.jack.ir.ast.JReturnStatement;
+import com.android.jack.ir.ast.JStatement;
import com.android.jack.ir.ast.JThis;
import com.android.jack.ir.ast.JThisRef;
import com.android.jack.ir.ast.JType;
@@ -59,6 +62,7 @@
import com.android.jack.scheduling.feature.SourceVersion8;
import com.android.jack.transformations.request.AppendField;
import com.android.jack.transformations.request.AppendMethod;
+import com.android.jack.transformations.request.PrependStatement;
import com.android.jack.transformations.request.Replace;
import com.android.jack.transformations.request.TransformationRequest;
import com.android.jack.util.NamingTools;
@@ -71,7 +75,6 @@
import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
@@ -98,11 +101,16 @@
public class LambdaConverter implements RunnableSchedulable<JMethod> {
/**
- * LambdaCtx save the relation between captured variables and theirs corresponding fields.
- * It also keep JThis of the method implementing the lambda.
+ * LambdaCtx keeps the following mappings:
+ * - mappings between captured variables and theirs corresponding fields
+ * - mappings between lambda parameters and theirs corresponding locals
+ * and it also keeps JThis of the method implementing the lambda.
*/
private static class LambdaCtx {
@Nonnull
+ private final Map<JParameter, JLocal> lambdaParam2Local = new HashMap<JParameter, JLocal>();
+
+ @Nonnull
private final Map<JVariable, JField> capturedVar2Field = new HashMap<JVariable, JField>();
@Nonnull
@@ -112,6 +120,21 @@
this.thisOfLambdaImpl = thisOfLambdaImpl;
}
+ public void addLambdaParam2LocalMapping(@Nonnull JParameter oldParam,
+ @Nonnull JLocal newLocal) {
+ assert !lambdaParam2Local.containsKey(oldParam);
+ lambdaParam2Local.put(oldParam, newLocal);
+ }
+
+ @CheckForNull
+ public JLocalRef getLambdaParameter(@Nonnull JParameter oldParam) {
+ JLocal local = lambdaParam2Local.get(oldParam);
+ if (local != null) {
+ return local.makeRef(SourceInfo.UNKNOWN);
+ }
+ return null;
+ }
+
public void addVar2FieldMapping(@Nonnull JVariable capturedVar, @Nonnull JField field) {
assert !capturedVar2Field.containsKey(capturedVar);
capturedVar2Field.put(capturedVar, field);
@@ -177,9 +200,10 @@
@Override
public boolean visit(@Nonnull JLambda lambdaExpr) {
- JMethod lambdaMethod = lambdaExpr.getMethod();
LambdaCtx lambdaCtx = null;
+ JMethod lambdaMethod = lambdaExpr.getMethod();
JConstructor lambdaImplCons = lambdaToLambaImplConst.get(lambdaMethod);
+ JMethodBody lambdaBody = lambdaExpr.getBody();
JThis capturedInstance = null;
@@ -190,24 +214,43 @@
if (lambdaImplCons == null) {
JDefinedClass lambdaImplClass = createLambdaImplClass(lambdaExpr);
- JMethod samMethod = lambdaExpr.getType().getSingleAbstractMethod();
- assert samMethod != null;
- if (needBridgeMethod(samMethod, lambdaExpr)) {
- synthesizeBridge(lambdaImplClass, samMethod, lambdaMethod);
+ JMethodIdWithReturnType mthIdToImplement = lambdaExpr.getMethodIdToImplement();
+ JMethod mthToImplement =
+ createMethod(lambdaImplClass, mthIdToImplement, /* isBridge= */ false);
+ JThis thisOfLambda = mthToImplement.getThis();
+ assert thisOfLambda != null;
+ lambdaCtx = new LambdaCtx(thisOfLambda);
+
+ // The body of the method to implement is the body of the lambda expression
+ mthToImplement.setBody(lambdaBody);
+ // Body is already transform, remove it
+ lambdaMethod.setBody(null);
+ JBlock blockOfBodytoImplement = lambdaBody.getBlock();
+
+ // Move parameters of lambda body as local variables of mthToImplement
+ assert mthToImplement.getParams().size() == mthToImplement.getParams().size();
+ int pIdx = 0;
+ for (JParameter param : lambdaMethod.getParams()) {
+ JLocal local = new JLocal(param.getSourceInfo(), param.getName(), param.getType(),
+ param.getModifier(), lambdaBody);
+ lambdaBody.addLocal(local);
+ lambdaCtx.addLambdaParam2LocalMapping(param, local);
+
+ JStatement stmt = JAsgOperation
+ .create(SourceInfo.UNKNOWN, JBinaryOperator.ASG, local.makeRef(SourceInfo.UNKNOWN),
+ new JDynamicCastOperation(SourceInfo.UNKNOWN,
+ mthToImplement.getParams().get(pIdx++).makeRef(SourceInfo.UNKNOWN),
+ local.getType()))
+ .makeStatement();
+
+ tr.append(new PrependStatement(blockOfBodytoImplement, stmt));
}
- // Move method representing lambda body into the class implementing lambda.
- // It is possible to do this, since lambda expression will be removed from
- // IR and thus the corresponding JMethod will not be longer attach to the IR and can be
- // directly reused.
- tr.append(new AppendMethod(lambdaImplClass, lambdaMethod));
- lambdaMethod.setModifier(JModifier.FINAL | JModifier.SYNTHETIC | JModifier.PUBLIC);
- lambdaMethod.setEnclosingType(lambdaImplClass);
- JThis thisOfLambdaImpl = new JThis(lambdaMethod);
- assert lambdaMethod.getThis() == null;
- lambdaMethod.setThis(thisOfLambdaImpl);
- lambdaCtx = new LambdaCtx(thisOfLambdaImpl);
-
+ for (JMethodIdWithReturnType bridgeMthIdWithReturnType : lambdaExpr.getBridgeMethodIds()) {
+ JMethod bridge =
+ createMethod(lambdaImplClass, bridgeMthIdWithReturnType, /* isBridge= */ true);
+ delegateImplementation(bridge, mthToImplement);
+ }
// Build <init> method of class implementing lambda and fields for all captured variables.
// Generated code looks like
@@ -259,7 +302,14 @@
JVariable capturedVar = capturedVarRef.getTarget();
JExpression arg = getCapturedVar(capturedVar);
if (arg == null) {
- arg = capturedVar.makeRef(SourceInfo.UNKNOWN);
+ if (capturedVarRef instanceof JParameterRef) {
+ // The parameter reference was not captured but it could be a parameter that was move to
+ // local
+ arg = getLambdaParameter((JParameter) capturedVarRef.getTarget());
+ }
+ if (arg == null) {
+ arg = capturedVar.makeRef(SourceInfo.UNKNOWN);
+ }
}
newAnnonymous.addArg(arg);
}
@@ -277,7 +327,9 @@
lambdaCtxStack.push(lambdaCtx);
- accept(lambdaExpr.getBody());
+ if (lambdaBody != null) {
+ accept(lambdaBody);
+ }
return false;
}
@@ -291,6 +343,15 @@
return fieldRef;
}
+ @CheckForNull
+ private JLocalRef getLambdaParameter(@Nonnull JParameter parameter) {
+ JLocalRef localRef = null;
+ if (!lambdaCtxStack.isEmpty()) {
+ localRef = lambdaCtxStack.peek().getLambdaParameter(parameter);
+ }
+ return localRef;
+ }
+
@Nonnull
private void createFieldAndAssignment(@Nonnull LambdaCtx lambdaCtx,
@Nonnull JConstructor constructor, @Nonnull JVariable capturedVar) {
@@ -324,51 +385,76 @@
super.endVisit(lambdaExpr);
}
- private void synthesizeBridge(@Nonnull JDefinedClass jClass, @Nonnull JMethod method,
- @Nonnull JMethod lamdbaMethodImpl) {
+ @Nonnull
+ private JMethod createMethod(@Nonnull JDefinedClass jClass,
+ @Nonnull JMethodIdWithReturnType methIdWithReturnType, boolean isBridge) {
SourceInfo sourceInfo = SourceInfo.UNKNOWN;
- JMethodId methodId = method.getMethodId();
- int bridgeModifier = method.getModifier();
- bridgeModifier &= ~(JModifier.ABSTRACT);
- bridgeModifier |= JModifier.SYNTHETIC | JModifier.BRIDGE;
+ int mthModifier =
+ JModifier.PUBLIC | (isBridge ? (JModifier.SYNTHETIC | JModifier.BRIDGE) : 0);
- JMethod bridge = new JMethod(sourceInfo, methodId, jClass, method.getType(), bridgeModifier);
+ JMethod mth = new JMethod(sourceInfo, methIdWithReturnType.getMethodId(), jClass,
+ methIdWithReturnType.getReturnType(), mthModifier);
- for (JParameter param : method.getParams()) {
- bridge.addParam(new JParameter(sourceInfo, param.getName(), param.getType(),
- param.getModifier(), bridge));
+ int pIdx = 0;
+ for (JType parameterType : methIdWithReturnType.getParameterTypes()) {
+ mth.addParam(
+ new JParameter(sourceInfo, "arg" + pIdx++, parameterType, JModifier.DEFAULT, mth));
}
+ tr.append(new AppendMethod(jClass, mth));
+
+ return mth;
+ }
+
+ private void delegateImplementation(@Nonnull JMethod mth, @Nonnull JMethod mthToCall) {
+ SourceInfo sourceInfo = SourceInfo.UNKNOWN;
+
JBlock bodyBlock = new JBlock(sourceInfo);
JMethodBody body = new JMethodBody(sourceInfo, bodyBlock);
- JThis jThis = bridge.getThis();
+ JThis jThis = mth.getThis();
assert jThis != null;
- JMethodCall callToSuper = new JMethodCall(sourceInfo, jThis.makeRef(sourceInfo), jClass,
- lamdbaMethodImpl.getMethodId(), lamdbaMethodImpl.getType(), true /* isVirtualDispatch */);
+ JMethodCall call =
+ new JMethodCall(sourceInfo, jThis.makeRef(sourceInfo), mth.getEnclosingType(),
+ mthToCall.getMethodId(), mthToCall.getType(), true /* isVirtualDispatch */);
- List<JType> paramType = lamdbaMethodImpl.getMethodId().getParamTypes();
- int pIndex = 0;
- for (JParameter param : bridge.getParams()) {
- callToSuper.addArg(new JDynamicCastOperation(sourceInfo,
- param.makeRef(sourceInfo), paramType.get(pIndex++)));
+ List<JType> paramType = mthToCall.getMethodId().getParamTypes();
+ int pIndex = 0;
+ for (JParameter param : mth.getParams()) {
+ call.addArg(new JDynamicCastOperation(sourceInfo, param.makeRef(sourceInfo),
+ paramType.get(pIndex++)));
}
- if (method.getType() != JPrimitiveTypeEnum.VOID.getType()) {
- bodyBlock.addStmt(new JReturnStatement(sourceInfo, callToSuper));
+ if (mth.getType() != JPrimitiveTypeEnum.VOID.getType()) {
+ bodyBlock.addStmt(new JReturnStatement(sourceInfo, call));
} else {
- bodyBlock.addStmt(new JExpressionStatement(sourceInfo, callToSuper));
+ bodyBlock.addStmt(new JExpressionStatement(sourceInfo, call));
bodyBlock.addStmt(new JReturnStatement(sourceInfo, null));
}
- bridge.setBody(body);
+ mth.setBody(body);
+ }
- tr.append(new AppendMethod(jClass, bridge));
+ @Override
+ public boolean visit(@Nonnull JParameterRef varRef) {
+ // Check if parameter reference targets a lambda parameter that must be rewrite in local
+ // reference
+
+ JExpression exprToUse = getLambdaParameter((JParameter) varRef.getTarget());
+ if (exprToUse != null) {
+ tr.append(new Replace(varRef, exprToUse));
+ return false;
+ }
+
+ // Need to visit super to check if this parameter reference targets a captured variable
+ return super.visit(varRef);
}
@Override
public boolean visit(@Nonnull JVariableRef varRef) {
+ // Check if a captured variable must be rewrite into a field access containing the value of
+ // the captured variable
JExpression exprToUse = getCapturedVar(varRef.getTarget());
if (exprToUse != null) {
tr.append(new Replace(varRef, exprToUse));
@@ -379,11 +465,10 @@
@Nonnull
private JDefinedClass createLambdaImplClass(@Nonnull JLambda lambdaExpr) {
String simpleName = lambdaClassNamePrefix + anonymousCountByMeth;
- JDefinedClass lambdaImpl =
- new JDefinedClass(new SourceInfoFactory().create(currentClass.getSourceInfo()
- .getFileName()), currentClass.getName() + "$" + simpleName,
- JModifier.PUBLIC | JModifier.SYNTHETIC, currentClass.getEnclosingPackage(),
- NopClassOrInterfaceLoader.INSTANCE);
+ JDefinedClass lambdaImpl = new JDefinedClass(
+ new SourceInfoFactory().create(currentClass.getSourceInfo().getFileName()),
+ currentClass.getName() + "$" + simpleName, JModifier.FINAL | JModifier.SYNTHETIC,
+ currentClass.getEnclosingPackage(), NopClassOrInterfaceLoader.INSTANCE);
anonymousCountByMeth++;
currentClass.addMemberType(lambdaImpl);
@@ -402,23 +487,6 @@
return lambdaImpl;
}
-
- private boolean needBridgeMethod(@Nonnull JMethod samMethod, @Nonnull JLambda lambdaExpr) {
- assert samMethod.getParams().size() == lambdaExpr.getParameters().size();
- JMethod lambdaMethod = lambdaExpr.getMethod();
-
- if (!lambdaMethod.getType().equals(samMethod.getType())) {
- return true;
- }
-
- Iterator<JParameter> lambdaParameter = lambdaMethod.getParams().iterator();
- for (JParameter samParameter : samMethod.getParams()) {
- if (!samParameter.getType().isSameType(lambdaParameter.next().getType())) {
- return true;
- }
- }
- return false;
- }
}
@Override
diff --git a/jack/src/com/android/jack/util/CloneExpressionVisitor.java b/jack/src/com/android/jack/util/CloneExpressionVisitor.java
index 8ad82d8..4ae4058 100644
--- a/jack/src/com/android/jack/util/CloneExpressionVisitor.java
+++ b/jack/src/com/android/jack/util/CloneExpressionVisitor.java
@@ -344,8 +344,8 @@
@Override
public boolean visit(@Nonnull JLambda x) {
- expression = new JLambda(x.getSourceInfo(), x.getMethod(), x.getType(),
- x.needToCaptureInstance(), x.getInterfaceBounds());
+ expression = new JLambda(x.getSourceInfo(), x.getMethodIdToImplement(), x.getMethod(),
+ x.getType(), x.needToCaptureInstance(), x.getInterfaceBounds());
return false;
}
}
\ No newline at end of file
diff --git a/jack/src/com/android/jack/util/CloneStatementVisitor.java b/jack/src/com/android/jack/util/CloneStatementVisitor.java
index dab1cf8..9f02e41 100644
--- a/jack/src/com/android/jack/util/CloneStatementVisitor.java
+++ b/jack/src/com/android/jack/util/CloneStatementVisitor.java
@@ -490,8 +490,9 @@
@Override
public boolean visit(@Nonnull JLambda lambda) {
- JLambda clonedLambda = new JLambda(lambda.getSourceInfo(), lambda.getMethod(), lambda.getType(),
- lambda.needToCaptureInstance(), lambda.getInterfaceBounds());
+ JLambda clonedLambda =
+ new JLambda(lambda.getSourceInfo(), lambda.getMethodIdToImplement(), lambda.getMethod(),
+ lambda.getType(), lambda.needToCaptureInstance(), lambda.getInterfaceBounds());
for (JVariableRef capturedVarRef : lambda.getCapturedVariables()) {
JVariable capturedVar = capturedVarRef.getTarget();
diff --git a/jill/src/com/android/jill/backend/jayce/Token.java b/jill/src/com/android/jill/backend/jayce/Token.java
index 8d18083..6a2005f 100644
--- a/jill/src/com/android/jill/backend/jayce/Token.java
+++ b/jill/src/com/android/jill/backend/jayce/Token.java
@@ -115,6 +115,7 @@
METHOD("method"),
METHOD_BODY("body"),
METHOD_CALL("call"),
+ METHODID_WITH_RETURN_TYPE("mthid-rt"),
METHOD_LITERAL("method-literal"),
MOD_OPERATION("%"),
MUL_OPERATION("*"),
diff --git a/jill/src/com/android/jill/frontend/java/JavaTransformer.java b/jill/src/com/android/jill/frontend/java/JavaTransformer.java
index 0b90108..13b2897 100644
--- a/jill/src/com/android/jill/frontend/java/JavaTransformer.java
+++ b/jill/src/com/android/jill/frontend/java/JavaTransformer.java
@@ -52,13 +52,13 @@
private static final String LIB_MAJOR_VERSION = "2";
@Nonnull
- private static final String LIB_MINOR_VERSION = "1";
+ private static final String LIB_MINOR_VERSION = "2";
@Nonnull
private static final String JAYCE_MAJOR_VERSION = "3";
@Nonnull
- private static final String JAYCE_MINOR_VERSION = "2";
+ private static final String JAYCE_MINOR_VERSION = "3";
@Nonnull
private static final String KEY_LIB_MAJOR_VERSION = "lib.version.major";