Merge "R8 Kotlin tests: also check symbols in input"
diff --git a/build.gradle b/build.gradle
index 8ee6b41..cd831cc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -113,7 +113,6 @@
java {
srcDirs = ['src/test/apiUsageSample']
}
- output.resourcesDir = 'build/classes/apiUsageSample'
}
debugTestResources {
java {
diff --git a/src/main/java/com/android/tools/r8/ApiLevelException.java b/src/main/java/com/android/tools/r8/ApiLevelException.java
index 14a530c..6d110a4 100644
--- a/src/main/java/com/android/tools/r8/ApiLevelException.java
+++ b/src/main/java/com/android/tools/r8/ApiLevelException.java
@@ -3,12 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.utils.AndroidApiLevel;
/**
* Exception to signal features that are not supported until a given API level.
*/
-public class ApiLevelException extends CompilationException {
+public class ApiLevelException extends CompilationError {
public ApiLevelException(
AndroidApiLevel minApiLevel, String unsupportedFeatures, String sourceString) {
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index 6d7ad87..6c9a286 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.utils.ArchiveBuilder;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.DirectoryBuilder;
-import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.io.ByteStreams;
@@ -132,11 +131,7 @@
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
- try {
- outputBuilder.close();
- } catch (IOException e) {
- handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
- }
+ outputBuilder.close(handler);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 2d52c64..1db187e 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -196,7 +196,7 @@
InternalOptions options,
Timing timing,
ExecutorService executor)
- throws IOException, ExecutionException, ApiLevelException {
+ throws IOException, ExecutionException {
final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
IRConverter converter = new IRConverter(appInfo, options, timing, printer);
diff --git a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
index ca86c70..f1c0fac 100644
--- a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.utils.ArchiveBuilder;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.DirectoryBuilder;
-import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.ZipUtils;
@@ -153,11 +152,7 @@
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
- try {
- outputBuilder.close();
- } catch (IOException e) {
- handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
- }
+ outputBuilder.close(handler);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index f26ab73..3bbab64 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -156,11 +156,7 @@
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
- try {
- outputBuilder.close();
- } catch (IOException e) {
- handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
- }
+ outputBuilder.close(handler);
}
public static void writeResources(Path archive, List<ProgramResource> resources)
@@ -242,11 +238,7 @@
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
- try {
- outputBuilder.close();
- } catch (IOException e) {
- handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
- }
+ outputBuilder.close(handler);
}
private synchronized void prepareDirectory() throws IOException {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
index 0a98cb3..3122404 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexType;
@@ -48,8 +47,7 @@
}
@Override
- public void buildIR(IRBuilder builder, CfState state, CfSourceCode code)
- throws ApiLevelException {
+ public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
builder.addConstMethodHandle(
state.push(builder.getFactory().methodHandleType).register, handle);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
index e933c11..ebbaf21 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
@@ -49,8 +48,7 @@
}
@Override
- public void buildIR(IRBuilder builder, CfState state, CfSourceCode code)
- throws ApiLevelException {
+ public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
builder.addConstMethodType(state.push(builder.getFactory().methodTypeType).register, type);
}
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index eaa7b2a..6d2a3d5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.UseRegistry;
@@ -49,8 +48,7 @@
return false;
}
- public abstract void buildIR(IRBuilder builder, CfState state, CfSourceCode code)
- throws ApiLevelException;
+ public abstract void buildIR(IRBuilder builder, CfState state, CfSourceCode code);
/** Return true if this instruction directly emits IR instructions. */
public boolean emitsIR() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 32c7022..5dfef0c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
@@ -91,8 +90,7 @@
}
@Override
- public void buildIR(IRBuilder builder, CfState state, CfSourceCode code)
- throws ApiLevelException {
+ public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
Invoke.Type type;
DexMethod canonicalMethod;
DexProto callSiteProto = null;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index 2dfc8fa..015b8a3 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.UseRegistry;
@@ -52,8 +51,7 @@
}
@Override
- public void buildIR(IRBuilder builder, CfState state, CfSourceCode code)
- throws ApiLevelException {
+ public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
int[] dimensions = state.popReverse(this.dimensions);
builder.addMultiNewArray(type, state.push(type).register, dimensions);
}
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
index a346020..bad4e47 100644
--- a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
@@ -71,7 +70,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addConstMethodHandle(AA, (DexMethodHandle) BBBB);
}
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
index 6a1bef5..84c090a 100644
--- a/src/main/java/com/android/tools/r8/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
@@ -71,7 +70,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addConstMethodType(AA, (DexProto) BBBB);
}
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArray.java b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
index 8aaf5b9..4f2ebb6 100644
--- a/src/main/java/com/android/tools/r8/code/FilledNewArray.java
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -42,7 +41,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeNewArray(getType(), A, new int[]{C, D, E, F, G});
}
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
index 2d39d50..0ee63b0 100644
--- a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -42,7 +41,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeRangeNewArray(getType(), AA, CCCC);
}
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
index 09d5671b..c8b5154 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.graph.DexCallSite;
@@ -184,7 +183,7 @@
return NO_TARGETS;
}
- public abstract void buildIR(IRBuilder builder) throws ApiLevelException;
+ public abstract void buildIR(IRBuilder builder);
public DexCallSite getCallSite() {
return null;
diff --git a/src/main/java/com/android/tools/r8/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/code/InvokeDirect.java
index 06b919b..08d6826 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeDirect.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.IndexedDexItem;
import com.android.tools.r8.graph.OffsetToObjectMapping;
@@ -51,7 +50,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeRegisters(Type.DIRECT, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java b/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
index e2b129d..8f72a2b 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeRange(Type.DIRECT, getMethod(), getProto(), AA, CCCC);
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/code/InvokeInterface.java
index c0d912a..b98af41 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeInterface.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.IndexedDexItem;
import com.android.tools.r8.graph.OffsetToObjectMapping;
@@ -51,7 +50,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeRegisters(
Type.INTERFACE, getMethod(), getProto(), A, new int[] {C, D, E, F, G});
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java b/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
index c63c41b..e4d63b4 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeRange(Type.INTERFACE, getMethod(), getProto(), AA, CCCC);
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java
index 891dcca..d5ecd11 100644
--- a/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.OffsetToObjectMapping;
@@ -30,7 +29,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeRegisters(
Type.POLYMORPHIC, getMethod(), getProto(), A, new int[] {C, D, E, F, G});
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java b/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java
index 3969a8c..45eb6e8 100644
--- a/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.OffsetToObjectMapping;
@@ -53,7 +52,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeRange(Type.POLYMORPHIC, getMethod(), getProto(), AA, CCCC);
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/code/InvokeStatic.java
index 6c0a724..73cb887 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeStatic.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeRegisters(Type.STATIC, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java b/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
index 912cd4b..6fd3f42 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeRange(Type.STATIC, getMethod(), getProto(), AA, CCCC);
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/code/InvokeSuper.java
index 10ed497..9575ed9 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeSuper.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.IndexedDexItem;
import com.android.tools.r8.graph.OffsetToObjectMapping;
@@ -51,7 +50,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeRegisters(Type.SUPER, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java b/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
index 8933527..63c6318 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeRange(Type.SUPER, getMethod(), getProto(), AA, CCCC);
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
index 132c564..003debf 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeRegisters(Type.VIRTUAL, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java b/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
index 00cb861..64e9b6d 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
}
@Override
- public void buildIR(IRBuilder builder) throws ApiLevelException {
+ public void buildIR(IRBuilder builder) {
builder.addInvokeRange(Type.VIRTUAL, getMethod(), getProto(), AA, CCCC);
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 382bc66..9721d3b 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.DataDirectoryResource;
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.DataResourceConsumer;
@@ -200,7 +199,6 @@
// Use a linked hash map as the order matters when addDexProgramData is called below.
Map<VirtualFile, Future<ObjectToOffsetMapping>> offsetMappingFutures = new LinkedHashMap<>();
for (VirtualFile newFile : distribute(executorService)) {
- assert !newFile.isEmpty();
if (!newFile.isEmpty()) {
offsetMappingFutures
.put(newFile, executorService.submit(() -> {
@@ -429,8 +427,7 @@
}
}
- private byte[] writeDexFile(ObjectToOffsetMapping mapping)
- throws ApiLevelException {
+ private byte[] writeDexFile(ObjectToOffsetMapping mapping) {
FileWriter fileWriter = new FileWriter(mapping, application, options, namingLens);
// Collect the non-fixed sections.
fileWriter.collect();
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 4767c64..b73f8a4 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -51,7 +51,6 @@
import com.android.tools.r8.utils.DexVersion;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.LebUtils;
-import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
@@ -135,7 +134,7 @@
return this;
}
- public byte[] generate() throws ApiLevelException {
+ public byte[] generate() {
// Check restrictions on interface methods.
checkInterfaceMethods();
@@ -203,7 +202,7 @@
return Arrays.copyOf(dest.asArray(), layout.getEndOfFile());
}
- private void checkInterfaceMethods() throws ApiLevelException {
+ private void checkInterfaceMethods() {
for (DexProgramClass clazz : mapping.getClasses()) {
if (clazz.isInterface()) {
for (DexEncodedMethod method : clazz.directMethods()) {
@@ -222,7 +221,7 @@
// -- starting with N interfaces may also have public or private
// static methods, as well as public non-abstract (default)
// and private instance methods.
- private void checkInterfaceMethod(DexEncodedMethod method) throws ApiLevelException {
+ private void checkInterfaceMethod(DexEncodedMethod method) {
if (application.dexItemFactory.isClassConstructor(method.method)) {
return; // Class constructor is always OK.
}
@@ -295,16 +294,16 @@
}
}
- private <T extends IndexedDexItem> void writeFixedSectionItems(Collection<T> items, int offset,
- ThrowingConsumer<T, ApiLevelException> writer) throws ApiLevelException {
+ private <T extends IndexedDexItem> void writeFixedSectionItems(
+ Collection<T> items, int offset, Consumer<T> writer) {
assert dest.position() == offset;
for (T item : items) {
writer.accept(item);
}
}
- private void writeFixedSectionItems(DexProgramClass[] items, int offset,
- ThrowingConsumer<DexProgramClass, ApiLevelException> writer) throws ApiLevelException {
+ private void writeFixedSectionItems(
+ DexProgramClass[] items, int offset, Consumer<DexProgramClass> writer) {
assert dest.position() == offset;
for (DexProgramClass item : items) {
writer.accept(item);
@@ -610,7 +609,7 @@
}
}
- private void writeMethodHandle(DexMethodHandle methodHandle) throws ApiLevelException {
+ private void writeMethodHandle(DexMethodHandle methodHandle) {
checkThatInvokeCustomIsAllowed();
MethodHandleType methodHandleDexType;
switch (methodHandle.type) {
@@ -636,7 +635,7 @@
dest.putShort((short) 0); // unused
}
- private void writeCallSite(DexCallSite callSite) throws ApiLevelException {
+ private void writeCallSite(DexCallSite callSite) {
checkThatInvokeCustomIsAllowed();
assert dest.isAligned(4);
dest.putInt(mixedSectionOffsets.getOffsetFor(callSite.getEncodedArray()));
@@ -1296,7 +1295,7 @@
}
}
- private void checkThatInvokeCustomIsAllowed() throws ApiLevelException {
+ private void checkThatInvokeCustomIsAllowed() {
if (!options.canUseInvokeCustom()) {
throw new ApiLevelException(
AndroidApiLevel.O,
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 6e93a90..1fd4a73 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfLabel;
@@ -201,11 +200,7 @@
@Override
public IRCode buildIR(
- DexEncodedMethod encodedMethod,
- AppInfo appInfo,
- InternalOptions options,
- Origin origin)
- throws ApiLevelException {
+ DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin) {
return internalBuild(encodedMethod, appInfo, options, null, null, origin);
}
@@ -216,8 +211,7 @@
InternalOptions options,
ValueNumberGenerator valueNumberGenerator,
Position callerPosition,
- Origin origin)
- throws ApiLevelException {
+ Origin origin) {
assert valueNumberGenerator != null;
assert callerPosition != null;
return internalBuild(
@@ -230,8 +224,7 @@
InternalOptions options,
ValueNumberGenerator generator,
Position callerPosition,
- Origin origin)
- throws ApiLevelException {
+ Origin origin) {
assert !options.isGeneratingDex() || !encodedMethod.accessFlags.isSynchronized()
: "Converting CfCode to IR not supported for DEX output of synchronized methods.";
CfSourceCode source = new CfSourceCode(this, encodedMethod, callerPosition, origin);
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 6f86412..60a5ef5 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
@@ -18,11 +17,7 @@
public abstract class Code extends CachedHashValueDexItem {
public abstract IRCode buildIR(
- DexEncodedMethod encodedMethod,
- AppInfo appInfo,
- InternalOptions options,
- Origin origin)
- throws ApiLevelException;
+ DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin);
public IRCode buildInliningIR(
DexEncodedMethod encodedMethod,
@@ -30,8 +25,7 @@
InternalOptions options,
ValueNumberGenerator valueNumberGenerator,
Position callerPosition,
- Origin origin)
- throws ApiLevelException {
+ Origin origin) {
throw new Unreachable("Unexpected attempt to build IR graph for inlining from: "
+ getClass().getCanonicalName());
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 685f32e..b1b5f8a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.code.SwitchPayload;
@@ -164,9 +163,8 @@
}
@Override
- public IRCode buildIR(DexEncodedMethod encodedMethod, AppInfo appInfo,
- InternalOptions options, Origin origin)
- throws ApiLevelException {
+ public IRCode buildIR(
+ DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin) {
DexSourceCode source =
new DexSourceCode(
this, encodedMethod, null, options.lineNumberOptimization == LineNumberOptimization.ON);
@@ -177,11 +175,11 @@
@Override
public IRCode buildInliningIR(
DexEncodedMethod encodedMethod,
- AppInfo appInfo, InternalOptions options,
+ AppInfo appInfo,
+ InternalOptions options,
ValueNumberGenerator valueNumberGenerator,
Position callerPosition,
- Origin origin)
- throws ApiLevelException {
+ Origin origin) {
DexSourceCode source =
new DexSourceCode(
this,
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index ee29ff3..0914e7b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -9,7 +9,6 @@
import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_SUBCLASS;
import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfThrow;
@@ -231,23 +230,21 @@
compilationState = CompilationState.NOT_PROCESSED;
}
- public IRCode buildIR(
- AppInfo appInfo, InternalOptions options, Origin origin) throws ApiLevelException {
+ public IRCode buildIR(AppInfo appInfo, InternalOptions options, Origin origin) {
return code == null ? null : code.buildIR(this, appInfo, options, origin);
}
public IRCode buildInliningIRForTesting(
- InternalOptions options, ValueNumberGenerator valueNumberGenerator)
- throws ApiLevelException {
+ InternalOptions options, ValueNumberGenerator valueNumberGenerator) {
return buildInliningIR(null, options, valueNumberGenerator, null, Origin.unknown());
}
public IRCode buildInliningIR(
- AppInfo appInfo, InternalOptions options,
+ AppInfo appInfo,
+ InternalOptions options,
ValueNumberGenerator valueNumberGenerator,
Position callerPosition,
- Origin origin)
- throws ApiLevelException {
+ Origin origin) {
return code.buildInliningIR(
this, appInfo, options, valueNumberGenerator, callerPosition, origin);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 86ca12a..4e4a78f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -230,6 +230,10 @@
public final DexType annotationThrows = createType("Ldalvik/annotation/Throws;");
public final DexType annotationSynthesizedClassMap =
createType("Lcom/android/tools/r8/annotations/SynthesizedClassMap;");
+ public final DexType annotationCovariantReturnType =
+ createType("Ldalvik/annotation/codegen/CovariantReturnType;");
+ public final DexType annotationCovariantReturnTypes =
+ createType("Ldalvik/annotation/codegen/CovariantReturnType$CovariantReturnTypes;");
private static final String METAFACTORY_METHOD_NAME = "metafactory";
private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 3bd4cde..640172f 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.errors.InvalidDebugInfoException;
import com.android.tools.r8.graph.JarClassFileReader.ReparseContext;
import com.android.tools.r8.ir.code.IRCode;
@@ -104,9 +103,8 @@
}
@Override
- public IRCode buildIR(DexEncodedMethod encodedMethod, AppInfo appInfo,
- InternalOptions options, Origin origin)
- throws ApiLevelException {
+ public IRCode buildIR(
+ DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin) {
triggerDelayedParsingIfNeccessary();
return options.debug
? internalBuildWithLocals(encodedMethod, appInfo, options, null, null)
@@ -116,11 +114,11 @@
@Override
public IRCode buildInliningIR(
DexEncodedMethod encodedMethod,
- AppInfo appInfo, InternalOptions options,
+ AppInfo appInfo,
+ InternalOptions options,
ValueNumberGenerator generator,
Position callerPosition,
- Origin origin)
- throws ApiLevelException {
+ Origin origin) {
assert generator != null;
triggerDelayedParsingIfNeccessary();
return options.debug
@@ -130,10 +128,10 @@
private IRCode internalBuildWithLocals(
DexEncodedMethod encodedMethod,
- AppInfo appInfo, InternalOptions options,
+ AppInfo appInfo,
+ InternalOptions options,
ValueNumberGenerator generator,
- Position callerPosition)
- throws ApiLevelException {
+ Position callerPosition) {
try {
return internalBuild(encodedMethod, appInfo, options, generator, callerPosition);
} catch (InvalidDebugInfoException e) {
@@ -145,10 +143,10 @@
private IRCode internalBuild(
DexEncodedMethod encodedMethod,
- AppInfo appInfo, InternalOptions options,
+ AppInfo appInfo,
+ InternalOptions options,
ValueNumberGenerator generator,
- Position callerPosition)
- throws ApiLevelException {
+ Position callerPosition) {
if (!options.debug) {
node.localVariables.clear();
}
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index bd6d3f7..8ecc1ab 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.cf.code.CfArrayLength;
import com.android.tools.r8.cf.code.CfArrayLoad;
@@ -175,9 +174,8 @@
}
@Override
- public IRCode buildIR(DexEncodedMethod encodedMethod, AppInfo appInfo,
- InternalOptions options, Origin origin)
- throws ApiLevelException {
+ public IRCode buildIR(
+ DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin) {
return asCfCode().buildIR(encodedMethod, appInfo, options, origin);
}
@@ -188,8 +186,7 @@
InternalOptions options,
ValueNumberGenerator valueNumberGenerator,
Position callerPosition,
- Origin origin)
- throws ApiLevelException {
+ Origin origin) {
return asCfCode().buildInliningIR(
encodedMethod, appInfo, options, valueNumberGenerator, callerPosition, origin);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 382a0a9..e68e3d5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -5,7 +5,6 @@
import static it.unimi.dsi.fastutil.ints.Int2ObjectSortedMaps.emptyMap;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.cf.code.CfFrame;
import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.cf.code.CfGoto;
@@ -359,8 +358,7 @@
@Override
public void buildInstruction(
- IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
- throws ApiLevelException {
+ IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
CfInstruction instruction = code.getInstructions().get(instructionIndex);
currentInstructionIndex = instructionIndex;
if (firstBlockInstruction) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 0d87723..31ee706 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.conversion;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.code.FillArrayData;
import com.android.tools.r8.code.FillArrayDataPayload;
import com.android.tools.r8.code.FilledNewArray;
@@ -173,8 +172,7 @@
@Override
public void buildInstruction(
- IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
- throws ApiLevelException {
+ IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
updateCurrentCatchHandlers(instructionIndex);
updateDebugPosition(instructionIndex, builder);
currentDexInstruction = code.instructions[instructionIndex];
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index c697105..702218e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -357,7 +357,7 @@
*
* @return The list of basic blocks. First block is the main entry.
*/
- public IRCode build() throws ApiLevelException {
+ public IRCode build() {
assert source != null;
source.setUp();
@@ -516,7 +516,7 @@
return true;
}
- private void processWorklist() throws ApiLevelException {
+ private void processWorklist() {
for (WorklistItem item = ssaWorklist.poll(); item != null; item = ssaWorklist.poll()) {
if (item.block.isFilled()) {
continue;
@@ -835,8 +835,7 @@
add(instruction);
}
- public void addConstMethodHandle(int dest, DexMethodHandle methodHandle)
- throws ApiLevelException {
+ public void addConstMethodHandle(int dest, DexMethodHandle methodHandle) {
if (!options.canUseConstantMethodHandle()) {
throw new ApiLevelException(
AndroidApiLevel.P,
@@ -848,8 +847,7 @@
add(instruction);
}
- public void addConstMethodType(int dest, DexProto methodType)
- throws ApiLevelException {
+ public void addConstMethodType(int dest, DexProto methodType) {
if (!options.canUseConstantMethodType()) {
throw new ApiLevelException(
AndroidApiLevel.P,
@@ -1040,8 +1038,7 @@
}
public void addInvoke(
- Type type, DexItem item, DexProto callSiteProto, List<Value> arguments, boolean itf)
- throws ApiLevelException {
+ Type type, DexItem item, DexProto callSiteProto, List<Value> arguments, boolean itf) {
if (type == Type.POLYMORPHIC) {
assert item instanceof DexMethod;
if (!options.canUseInvokePolymorphic()) {
@@ -1073,8 +1070,7 @@
add(Invoke.create(type, item, callSiteProto, null, arguments, itf));
}
- public void addInvoke(Type type, DexItem item, DexProto callSiteProto, List<Value> arguments)
- throws ApiLevelException {
+ public void addInvoke(Type type, DexItem item, DexProto callSiteProto, List<Value> arguments) {
addInvoke(type, item, callSiteProto, arguments, false);
}
@@ -1083,8 +1079,7 @@
DexItem item,
DexProto callSiteProto,
List<ValueType> types,
- List<Integer> registers)
- throws ApiLevelException {
+ List<Integer> registers) {
addInvoke(type, item, callSiteProto, types, registers, false);
}
@@ -1094,8 +1089,7 @@
DexProto callSiteProto,
List<ValueType> types,
List<Integer> registers,
- boolean itf)
- throws ApiLevelException {
+ boolean itf) {
assert types.size() == registers.size();
List<Value> arguments = new ArrayList<>(types.size());
for (int i = 0; i < types.size(); i++) {
@@ -1163,8 +1157,7 @@
DexMethod method,
DexProto callSiteProto,
int argumentRegisterCount,
- int[] argumentRegisters)
- throws ApiLevelException {
+ int[] argumentRegisters) {
// The value of argumentRegisterCount is the number of registers - not the number of values,
// but it is an upper bound on the number of arguments.
List<Value> arguments = new ArrayList<>(argumentRegisterCount);
@@ -1191,8 +1184,7 @@
addInvoke(type, method, callSiteProto, arguments);
}
- public void addInvokeNewArray(DexType type, int argumentCount, int[] argumentRegisters)
- throws ApiLevelException {
+ public void addInvokeNewArray(DexType type, int argumentCount, int[] argumentRegisters) {
String descriptor = type.descriptor.toString();
assert descriptor.charAt(0) == '[';
assert descriptor.length() >= 2;
@@ -1211,7 +1203,7 @@
addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments);
}
- public void addMultiNewArray(DexType type, int dest, int[] dimensions) throws ApiLevelException {
+ public void addMultiNewArray(DexType type, int dest, int[] dimensions) {
assert isGeneratingClassFiles();
List<Value> arguments = new ArrayList<>(dimensions.length);
for (int dimension : dimensions) {
@@ -1226,8 +1218,7 @@
DexMethod method,
DexProto callSiteProto,
int argumentCount,
- int firstArgumentRegister)
- throws ApiLevelException {
+ int firstArgumentRegister) {
// The value of argumentCount is the number of registers - not the number of values, but it
// is an upper bound on the number of arguments.
List<Value> arguments = new ArrayList<>(argumentCount);
@@ -1254,8 +1245,7 @@
addInvoke(type, method, callSiteProto, arguments);
}
- public void addInvokeRangeNewArray(DexType type, int argumentCount, int firstArgumentRegister)
- throws ApiLevelException {
+ public void addInvokeRangeNewArray(DexType type, int argumentCount, int firstArgumentRegister) {
String descriptor = type.descriptor.toString();
assert descriptor.charAt(0) == '[';
assert descriptor.length() >= 2;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 8477d9b..fa00cd1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -33,6 +33,7 @@
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.ir.desugar.StringConcatRewriter;
@@ -100,6 +101,7 @@
private final ProtoLitePruner protoLiteRewriter;
private final IdentifierNameStringMarker identifierNameStringMarker;
private final Devirtualizer devirtualizer;
+ private final CovariantReturnTypeAnnotationTransformer covariantReturnTypeAnnotationTransformer;
private final OptimizationFeedback ignoreOptimizationFeedback = new OptimizationFeedbackIgnore();
private DexString highestSortingString;
@@ -127,6 +129,10 @@
? new InterfaceMethodRewriter(this, options) : null;
this.lambdaMerger = options.enableLambdaMerging
? new LambdaMerger(appInfo.dexItemFactory, options.reporter) : null;
+ this.covariantReturnTypeAnnotationTransformer =
+ options.processCovariantReturnTypeAnnotations
+ ? new CovariantReturnTypeAnnotationTransformer(this, appInfo.dexItemFactory)
+ : null;
if (enableWholeProgramOptimizations) {
assert appInfo.hasLiveness();
this.nonNullTracker = new NonNullTracker();
@@ -233,7 +239,7 @@
}
}
- private void synthesizeLambdaClasses(Builder<?> builder) throws ApiLevelException {
+ private void synthesizeLambdaClasses(Builder<?> builder) {
if (lambdaRewriter != null) {
lambdaRewriter.adjustAccessibility();
lambdaRewriter.synthesizeLambdaClasses(builder);
@@ -241,15 +247,20 @@
}
private void desugarInterfaceMethods(
- Builder<?> builder, InterfaceMethodRewriter.Flavor includeAllResources)
- throws ApiLevelException {
+ Builder<?> builder, InterfaceMethodRewriter.Flavor includeAllResources) {
if (interfaceMethodRewriter != null) {
interfaceMethodRewriter.desugarInterfaceMethods(builder, includeAllResources);
}
}
+ private void processCovariantReturnTypeAnnotations(Builder<?> builder) throws ApiLevelException {
+ if (covariantReturnTypeAnnotationTransformer != null) {
+ covariantReturnTypeAnnotationTransformer.process(builder);
+ }
+ }
+
public DexApplication convertToDex(DexApplication application, ExecutorService executor)
- throws ExecutionException, ApiLevelException {
+ throws ExecutionException {
removeLambdaDeserializationMethods();
timing.begin("IR conversion");
@@ -261,6 +272,7 @@
synthesizeLambdaClasses(builder);
desugarInterfaceMethods(builder, ExcludeDexResources);
+ processCovariantReturnTypeAnnotations(builder);
handleSynthesizedClassMapping(builder);
timing.end();
@@ -347,7 +359,7 @@
ThreadUtils.awaitFutures(futures);
}
- void convertMethodToDex(DexEncodedMethod method) throws ApiLevelException {
+ void convertMethodToDex(DexEncodedMethod method) {
assert options.isGeneratingDex();
if (method.getCode() != null) {
boolean matchesMethodFilter = options.methodMatchesFilter(method);
@@ -362,8 +374,7 @@
}
}
- public DexApplication optimize(DexApplication application)
- throws ExecutionException, ApiLevelException {
+ public DexApplication optimize(DexApplication application) throws ExecutionException {
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
return optimize(application, executor);
@@ -372,9 +383,8 @@
}
}
- public DexApplication optimize(DexApplication application,
- ExecutorService executorService)
- throws ExecutionException, ApiLevelException {
+ public DexApplication optimize(DexApplication application, ExecutorService executorService)
+ throws ExecutionException {
removeLambdaDeserializationMethods();
collectLambdaMergingCandidates(application);
@@ -392,10 +402,14 @@
.build(application, appInfo.withLiveness(), graphLense, options);
timing.end();
timing.begin("IR conversion phase 1");
- callGraph.forEachMethod((method, isProcessedConcurrently) -> {
- processMethod(method, directFeedback, isProcessedConcurrently, callGraph,
- outliner == null ? Outliner::noProcessing : outliner::identifyCandidates);
- }, executorService);
+ BiConsumer<IRCode, DexEncodedMethod> outlineHandler =
+ outliner == null ? Outliner::noProcessing : outliner.identifyCandidateMethods();
+ callGraph.forEachMethod(
+ (method, isProcessedConcurrently) -> {
+ processMethod(
+ method, directFeedback, isProcessedConcurrently, callGraph, outlineHandler);
+ },
+ executorService);
timing.end();
}
@@ -418,25 +432,23 @@
if (outliner != null) {
timing.begin("IR conversion phase 2");
- // Compile all classes flagged for outlining and
- // add the outline support class IF needed.
- DexProgramClass outlineClass = prepareOutlining();
- if (outlineClass != null) {
- // We need a new call graph to ensure deterministic order and also processing inside out
- // to get maximal inlining. Use a identity lense, as the code has been rewritten.
- CallGraph callGraph = CallGraph
- .build(application, appInfo.withLiveness(), GraphLense.getIdentityLense(), options);
- Set<DexEncodedMethod> outlineMethods = outliner.getMethodsSelectedForOutlining();
- callGraph.forEachMethod((method, isProcessedConcurrently) -> {
- if (!outlineMethods.contains(method)) {
- return;
- }
- // This is the second time we compile this method first mark it not processed.
- assert !method.getCode().isOutlineCode();
- processMethod(method, ignoreOptimizationFeedback, isProcessedConcurrently, callGraph,
- outliner::applyOutliningCandidate);
- assert method.isProcessed();
- }, executorService);
+ if (outliner.selectMethodsForOutlining()) {
+ forEachSelectedOutliningMethod(
+ executorService,
+ (code, method) -> {
+ printMethod(code, "IR before outlining (SSA)");
+ outliner.identifyOutlineSites(code, method);
+ });
+ DexProgramClass outlineClass = outliner.buildOutlinerClass(computeOutlineClassType());
+ optimizeSynthesizedClass(outlineClass);
+ forEachSelectedOutliningMethod(
+ executorService,
+ (code, method) -> {
+ outliner.applyOutliningCandidate(code, method);
+ printMethod(code, "IR after outlining (SSA)");
+ finalizeIR(method, code, ignoreOptimizationFeedback);
+ });
+ assert outliner.checkAllOutlineSitesFoundAgain();
builder.addSynthesizedClass(outlineClass, true);
clearDexMethodCompilationState(outlineClass);
}
@@ -451,15 +463,45 @@
return builder.build();
}
+ private void forEachSelectedOutliningMethod(
+ ExecutorService executorService, BiConsumer<IRCode, DexEncodedMethod> consumer)
+ throws ExecutionException {
+ assert !options.skipIR;
+ Set<DexEncodedMethod> methods = outliner.getMethodsSelectedForOutlining();
+ List<Future<?>> futures = new ArrayList<>();
+ for (DexEncodedMethod method : methods) {
+ futures.add(
+ executorService.submit(
+ () -> {
+ IRCode code =
+ method.buildIR(appInfo, options, appInfo.originFor(method.method.holder));
+ assert code != null;
+ assert !method.getCode().isOutlineCode();
+ // Instead of repeating all the optimizations of rewriteCode(), only run the
+ // optimizations needed for outlining: rewriteMoveResult() to remove out-values on
+ // StringBuilder/StringBuffer method invocations, and removeDeadCode() to remove
+ // unused out-values.
+ codeRewriter.rewriteMoveResult(code);
+ DeadCodeRemover.removeDeadCode(code, codeRewriter, options);
+ consumer.accept(code, method);
+ return null;
+ }));
+ }
+ ThreadUtils.awaitFutures(futures);
+ }
+
private void collectLambdaMergingCandidates(DexApplication application) {
if (lambdaMerger != null) {
lambdaMerger.collectGroupCandidates(application, appInfo.withLiveness(), options);
}
}
- private void finalizeLambdaMerging(DexApplication application,
- OptimizationFeedback directFeedback, Builder<?> builder, ExecutorService executorService)
- throws ExecutionException, ApiLevelException {
+ private void finalizeLambdaMerging(
+ DexApplication application,
+ OptimizationFeedback directFeedback,
+ Builder<?> builder,
+ ExecutorService executorService)
+ throws ExecutionException {
if (lambdaMerger != null) {
lambdaMerger.applyLambdaClassMapping(
application, this, directFeedback, builder, executorService);
@@ -512,16 +554,7 @@
return result;
}
- private DexProgramClass prepareOutlining() throws ApiLevelException {
- if (!outliner.selectMethodsForOutlining()) {
- return null;
- }
- DexProgramClass outlineClass = outliner.buildOutlinerClass(computeOutlineClassType());
- optimizeSynthesizedClass(outlineClass);
- return outlineClass;
- }
-
- public void optimizeSynthesizedClass(DexProgramClass clazz) throws ApiLevelException {
+ public void optimizeSynthesizedClass(DexProgramClass clazz) {
try {
codeRewriter.enterCachedClass(clazz);
// Process the generated class, but don't apply any outlining.
@@ -531,7 +564,7 @@
}
}
- public void optimizeSynthesizedMethod(DexEncodedMethod method) throws ApiLevelException {
+ public void optimizeSynthesizedMethod(DexEncodedMethod method) {
// Process the generated method, but don't apply any outlining.
processMethod(method, ignoreOptimizationFeedback, x -> false, CallSiteInformation.empty(),
Outliner::noProcessing);
@@ -541,12 +574,12 @@
return options.useSmaliSyntax ? method.toSmaliString(null) : method.codeToString();
}
- public void processMethod(DexEncodedMethod method,
+ public void processMethod(
+ DexEncodedMethod method,
OptimizationFeedback feedback,
Predicate<DexEncodedMethod> isProcessedConcurrently,
CallSiteInformation callSiteInformation,
- BiConsumer<IRCode, DexEncodedMethod> outlineHandler)
- throws ApiLevelException {
+ BiConsumer<IRCode, DexEncodedMethod> outlineHandler) {
Code code = method.getCode();
boolean matchesMethodFilter = options.methodMatchesFilter(method);
if (code != null && matchesMethodFilter) {
@@ -565,12 +598,12 @@
}
}
- private void rewriteCode(DexEncodedMethod method,
+ private void rewriteCode(
+ DexEncodedMethod method,
OptimizationFeedback feedback,
Predicate<DexEncodedMethod> isProcessedConcurrently,
CallSiteInformation callSiteInformation,
- BiConsumer<IRCode, DexEncodedMethod> outlineHandler)
- throws ApiLevelException {
+ BiConsumer<IRCode, DexEncodedMethod> outlineHandler) {
if (options.verbose) {
options.reporter.info(
new StringDiagnostic("Processing: " + method.toSourceString()));
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 46cc8e9..b5f0a05 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
@@ -29,7 +28,6 @@
import com.android.tools.r8.ir.conversion.JarState.Local;
import com.android.tools.r8.ir.conversion.JarState.Slot;
import com.android.tools.r8.logging.Log;
-import com.android.tools.r8.utils.ThrowingBiConsumer;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
@@ -44,6 +42,7 @@
import java.util.List;
import java.util.Queue;
import java.util.Set;
+import java.util.function.BiConsumer;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
@@ -478,8 +477,7 @@
@Override
public void buildInstruction(
- IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
- throws ApiLevelException {
+ IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
if (instructionIndex == EXCEPTIONAL_SYNC_EXIT_OFFSET) {
buildExceptionalPostlude(builder);
return;
@@ -1816,7 +1814,7 @@
// IR instruction building procedures.
- private void build(AbstractInsnNode insn, IRBuilder builder) throws ApiLevelException {
+ private void build(AbstractInsnNode insn, IRBuilder builder) {
switch (insn.getType()) {
case AbstractInsnNode.INSN:
build((InsnNode) insn, builder);
@@ -2540,7 +2538,7 @@
}
}
- private void build(MethodInsnNode insn, IRBuilder builder) throws ApiLevelException {
+ private void build(MethodInsnNode insn, IRBuilder builder) {
// Resolve the target method of the invoke.
DexMethod method = application.getMethod(insn.owner, insn.name, insn.desc);
@@ -2619,8 +2617,7 @@
Type methodOwner,
boolean addImplicitReceiver,
IRBuilder builder,
- ThrowingBiConsumer<List<ValueType>, List<Integer>, ApiLevelException> creator)
- throws ApiLevelException {
+ BiConsumer<List<ValueType>, List<Integer>> creator) {
// Build the argument list of the form [owner, param1, ..., paramN].
// The arguments are in reverse order on the stack, so we pop off the parameters here.
@@ -2657,7 +2654,7 @@
registers.add(slot.register);
}
- private void build(InvokeDynamicInsnNode insn, IRBuilder builder) throws ApiLevelException {
+ private void build(InvokeDynamicInsnNode insn, IRBuilder builder) {
DexCallSite callSite = DexCallSite.fromAsmInvokeDynamic(insn, application, clazz);
buildInvoke(insn.desc, null /* Not needed */,
@@ -2716,7 +2713,7 @@
// Intentionally empty.
}
- private void build(LdcInsnNode insn, IRBuilder builder) throws ApiLevelException {
+ private void build(LdcInsnNode insn, IRBuilder builder) {
if (insn.cst instanceof Type) {
Type type = (Type) insn.cst;
if (type.getSort() == Type.METHOD) {
@@ -2781,7 +2778,7 @@
builder.addSwitch(index, keys, fallthroughOffset, labelOffsets);
}
- private void build(MultiANewArrayInsnNode insn, IRBuilder builder) throws ApiLevelException {
+ private void build(MultiANewArrayInsnNode insn, IRBuilder builder) {
// Type of the full array.
Type arrayType = application.getAsmObjectType(insn.desc);
DexType dexArrayType = application.getType(arrayType);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
index cd8efa7..e95790a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.conversion;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.Position;
@@ -48,8 +47,8 @@
// Delegates for IR building.
void buildPrelude(IRBuilder builder);
- void buildInstruction(IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
- throws ApiLevelException;
+ void buildInstruction(IRBuilder builder, int instructionIndex, boolean firstBlockInstruction);
+
void buildPostlude(IRBuilder builder);
// Helper to resolve switch payloads and build switch instructions (dex code only).
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
new file mode 100644
index 0000000..64fe450
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -0,0 +1,269 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
+import com.android.tools.r8.ir.synthetic.SynthesizedCode;
+import com.google.common.base.Predicates;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+// Responsible for processing the annotations dalvik.annotation.codegen.CovariantReturnType and
+// dalvik.annotation.codegen.CovariantReturnType$CovariantReturnTypes.
+//
+// Consider the following class:
+// public class B extends A {
+// @CovariantReturnType(returnType = B.class, presentAfter = 25)
+// @Override
+// public A m(...) { ... return new B(); }
+// }
+//
+// The annotation is used to indicate that the compiler should insert a synthetic method that is
+// equivalent to method m, but has return type B instead of A. Thus, for this example, this
+// component is responsible for inserting the following method in class B (in addition to the
+// existing method m):
+// public B m(...) { A result = "invoke B.m(...)A;"; return (B) result; }
+//
+// Note that a method may be annotated with more than one CovariantReturnType annotation. In this
+// case there will be a CovariantReturnType$CovariantReturnTypes annotation on the method that wraps
+// several CovariantReturnType annotations. In this case, a new method is synthesized for each of
+// the contained CovariantReturnType annotations.
+public final class CovariantReturnTypeAnnotationTransformer {
+ private final IRConverter converter;
+ private final DexItemFactory factory;
+
+ public CovariantReturnTypeAnnotationTransformer(IRConverter converter, DexItemFactory factory) {
+ this.converter = converter;
+ this.factory = factory;
+ }
+
+ public void process(DexApplication.Builder<?> builder) throws ApiLevelException {
+ // List of methods that should be added to the next class.
+ List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation = new LinkedList<>();
+ List<DexEncodedMethod> covariantReturnTypeMethods = new LinkedList<>();
+ for (DexClass clazz : builder.getProgramClasses()) {
+ // Construct the methods that should be added to clazz.
+ buildCovariantReturnTypeMethodsForClass(
+ clazz, methodsWithCovariantReturnTypeAnnotation, covariantReturnTypeMethods);
+ if (covariantReturnTypeMethods.isEmpty()) {
+ continue;
+ }
+ updateClass(clazz, methodsWithCovariantReturnTypeAnnotation, covariantReturnTypeMethods);
+ // Reset lists for the next class that will have a CovariantReturnType or
+ // CovariantReturnType$CovariantReturnTypes annotation.
+ methodsWithCovariantReturnTypeAnnotation.clear();
+ covariantReturnTypeMethods.clear();
+ }
+ }
+
+ private void updateClass(
+ DexClass clazz,
+ List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation,
+ List<DexEncodedMethod> covariantReturnTypeMethods) {
+ // It is a compilation error if the class already has a method with a signature similar to one
+ // of the methods in covariantReturnTypeMethods.
+ for (DexEncodedMethod syntheticMethod : covariantReturnTypeMethods) {
+ if (hasVirtualMethodWithSignature(clazz, syntheticMethod)) {
+ throw new CompilationError(
+ String.format(
+ "Cannot process CovariantReturnType annotation: Class %s already "
+ + "has a method \"%s\"",
+ clazz.getType(), syntheticMethod.toSourceString()));
+ }
+ }
+ // Remove the CovariantReturnType annotations.
+ for (DexEncodedMethod method : methodsWithCovariantReturnTypeAnnotation) {
+ method.annotations =
+ method.annotations.keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation));
+ }
+ // Add the newly constructed methods to the class.
+ DexEncodedMethod[] oldVirtualMethods = clazz.virtualMethods();
+ DexEncodedMethod[] newVirtualMethods =
+ new DexEncodedMethod[oldVirtualMethods.length + covariantReturnTypeMethods.size()];
+ System.arraycopy(oldVirtualMethods, 0, newVirtualMethods, 0, oldVirtualMethods.length);
+ int i = oldVirtualMethods.length;
+ for (DexEncodedMethod syntheticMethod : covariantReturnTypeMethods) {
+ newVirtualMethods[i] = syntheticMethod;
+ i++;
+ }
+ clazz.setVirtualMethods(newVirtualMethods);
+ }
+
+ // Processes all the dalvik.annotation.codegen.CovariantReturnType and dalvik.annotation.codegen.
+ // CovariantReturnTypes annotations in the given DexClass. Adds the newly constructed, synthetic
+ // methods to the list covariantReturnTypeMethods.
+ private void buildCovariantReturnTypeMethodsForClass(
+ DexClass clazz,
+ List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation,
+ List<DexEncodedMethod> covariantReturnTypeMethods)
+ throws ApiLevelException {
+ for (DexEncodedMethod method : clazz.virtualMethods()) {
+ if (methodHasCovariantReturnTypeAnnotation(method)) {
+ methodsWithCovariantReturnTypeAnnotation.add(method);
+ buildCovariantReturnTypeMethodsForMethod(clazz, method, covariantReturnTypeMethods);
+ }
+ }
+ }
+
+ private boolean methodHasCovariantReturnTypeAnnotation(DexEncodedMethod method) {
+ for (DexAnnotation annotation : method.annotations.annotations) {
+ if (isCovariantReturnTypeAnnotation(annotation.annotation)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Processes all the dalvik.annotation.codegen.CovariantReturnType and dalvik.annotation.Co-
+ // variantReturnTypes annotations on the given method. Adds the newly constructed, synthetic
+ // methods to the list covariantReturnTypeMethods.
+ private void buildCovariantReturnTypeMethodsForMethod(
+ DexClass clazz, DexEncodedMethod method, List<DexEncodedMethod> covariantReturnTypeMethods)
+ throws ApiLevelException {
+ assert methodHasCovariantReturnTypeAnnotation(method);
+ for (DexType covariantReturnType : getCovariantReturnTypes(clazz, method)) {
+ DexEncodedMethod covariantReturnTypeMethod =
+ buildCovariantReturnTypeMethod(clazz, method, covariantReturnType);
+ covariantReturnTypeMethods.add(covariantReturnTypeMethod);
+ }
+ }
+
+ // Builds a synthetic method that invokes the given method, casts the result to
+ // covariantReturnType, and then returns the result. The newly created method will have return
+ // type covariantReturnType.
+ //
+ // Note: any "synchronized" or "strictfp" modifier could be dropped safely.
+ private DexEncodedMethod buildCovariantReturnTypeMethod(
+ DexClass clazz, DexEncodedMethod method, DexType covariantReturnType)
+ throws ApiLevelException {
+ DexProto newProto =
+ factory.createProto(
+ covariantReturnType, method.method.proto.shorty, method.method.proto.parameters);
+ MethodAccessFlags newAccessFlags = method.accessFlags.copy();
+ newAccessFlags.setSynthetic();
+ DexEncodedMethod newVirtualMethod =
+ new DexEncodedMethod(
+ factory.createMethod(method.method.holder, newProto, method.method.name),
+ newAccessFlags,
+ method.annotations.keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation)),
+ method.parameterAnnotationsList.keepIf(Predicates.alwaysTrue()),
+ new SynthesizedCode(
+ new ForwardMethodSourceCode(
+ clazz.type,
+ newProto,
+ method.method.holder,
+ method.method,
+ Invoke.Type.VIRTUAL,
+ true)));
+ // Optimize to generate DexCode instead of SynthesizedCode.
+ converter.optimizeSynthesizedMethod(newVirtualMethod);
+ return newVirtualMethod;
+ }
+
+ // Returns the set of covariant return types for method.
+ //
+ // If the method is:
+ // @dalvik.annotation.codegen.CovariantReturnType(returnType=SubOfFoo, presentAfter=25)
+ // @dalvik.annotation.codegen.CovariantReturnType(returnType=SubOfSubOfFoo, presentAfter=28)
+ // @Override
+ // public Foo foo() { ... return new SubOfSubOfFoo(); }
+ // then this method returns the set { SubOfFoo, SubOfSubOfFoo }.
+ private Set<DexType> getCovariantReturnTypes(DexClass clazz, DexEncodedMethod method) {
+ Set<DexType> covariantReturnTypes = new HashSet<>();
+ for (DexAnnotation annotation : method.annotations.annotations) {
+ if (isCovariantReturnTypeAnnotation(annotation.annotation)) {
+ getCovariantReturnTypesFromAnnotation(
+ clazz, method, annotation.annotation, covariantReturnTypes);
+ }
+ }
+ return covariantReturnTypes;
+ }
+
+ private void getCovariantReturnTypesFromAnnotation(
+ DexClass clazz,
+ DexEncodedMethod method,
+ DexEncodedAnnotation annotation,
+ Set<DexType> covariantReturnTypes) {
+ assert isCovariantReturnTypeAnnotation(annotation);
+ boolean hasPresentAfterElement = false;
+ for (DexAnnotationElement element : annotation.elements) {
+ String name = element.name.toString();
+ if (annotation.type == factory.annotationCovariantReturnType) {
+ if (name.equals("returnType")) {
+ if (!(element.value instanceof DexValue.DexValueType)) {
+ throw new CompilationError(
+ String.format(
+ "Expected element \"returnType\" of CovariantReturnType annotation to "
+ + "reference a type (method: \"%s\", was: %s)",
+ method.toSourceString(), element.value.getClass().getCanonicalName()));
+ }
+
+ DexValue.DexValueType dexValueType = (DexValue.DexValueType) element.value;
+ covariantReturnTypes.add(dexValueType.value);
+ } else if (name.equals("presentAfter")) {
+ hasPresentAfterElement = true;
+ }
+ } else {
+ if (name.equals("value")) {
+ if (!(element.value instanceof DexValue.DexValueArray)) {
+ throw new CompilationError(
+ String.format(
+ "Expected element \"value\" of CovariantReturnTypes annotation to "
+ + "be an array (method: \"%s\", was: %s)",
+ method.toSourceString(), element.value.getClass().getCanonicalName()));
+ }
+
+ DexValue.DexValueArray array = (DexValue.DexValueArray) element.value;
+ // Handle the inner dalvik.annotation.codegen.CovariantReturnType annotations recursively.
+ for (DexValue value : array.getValues()) {
+ assert value instanceof DexValue.DexValueAnnotation;
+ DexValue.DexValueAnnotation innerAnnotation = (DexValue.DexValueAnnotation) value;
+ getCovariantReturnTypesFromAnnotation(
+ clazz, method, innerAnnotation.value, covariantReturnTypes);
+ }
+ }
+ }
+ }
+
+ if (annotation.type == factory.annotationCovariantReturnType && !hasPresentAfterElement) {
+ throw new CompilationError(
+ String.format(
+ "CovariantReturnType annotation for method \"%s\" is missing mandatory element "
+ + "\"presentAfter\" (class %s)",
+ clazz.getType(), method.toSourceString()));
+ }
+ }
+
+ private boolean isCovariantReturnTypeAnnotation(DexEncodedAnnotation annotation) {
+ return annotation.type == factory.annotationCovariantReturnType
+ || annotation.type == factory.annotationCovariantReturnTypes;
+ }
+
+ private static boolean hasVirtualMethodWithSignature(DexClass clazz, DexEncodedMethod method) {
+ for (DexEncodedMethod existingMethod : clazz.virtualMethods()) {
+ if (existingMethod.method.equals(method.method)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 8add960..73c5118 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.desugar;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.DexApplication.Builder;
@@ -325,7 +324,7 @@
* Move static and default interface methods to companion classes,
* add missing methods to forward to moved default methods implementation.
*/
- public void desugarInterfaceMethods(Builder<?> builder, Flavor flavour) throws ApiLevelException {
+ public void desugarInterfaceMethods(Builder<?> builder, Flavor flavour) {
// Process all classes first. Add missing forwarding methods to
// replace desugared default interface methods.
forwardingMethods.addAll(processClasses(builder, flavour));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index d9bd4bd..81407bd 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.desugar;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
@@ -425,7 +424,7 @@
}
// Ensure access of the referenced symbol(s).
- abstract boolean ensureAccessibility() throws ApiLevelException;
+ abstract boolean ensureAccessibility();
DexClass definitionFor(DexType type) {
return rewriter.converter.appInfo.app.definitionFor(type);
@@ -520,7 +519,7 @@
}
@Override
- boolean ensureAccessibility() throws ApiLevelException {
+ boolean ensureAccessibility() {
// Create a static accessor with proper accessibility.
DexProgramClass accessorClass = programDefinitionFor(callTarget.holder);
assert accessorClass != null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 11ff65b..d6b99a7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.desugar;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexApplication.Builder;
@@ -162,7 +161,7 @@
* Adjust accessibility of referenced application symbols or
* creates necessary accessors.
*/
- public void adjustAccessibility() throws ApiLevelException {
+ public void adjustAccessibility() {
// For each lambda class perform necessary adjustment of the
// referenced symbols to make them accessible. This can result in
// method access relaxation or creation of accessor method.
@@ -172,7 +171,7 @@
}
/** Generates lambda classes and adds them to the builder. */
- public void synthesizeLambdaClasses(Builder<?> builder) throws ApiLevelException {
+ public void synthesizeLambdaClasses(Builder<?> builder) {
for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
DexProgramClass synthesizedClass = lambdaClass.synthesizeLambdaClass();
converter.optimizeSynthesizedClass(synthesizedClass);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 90081d1..845e17b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -305,8 +304,7 @@
}
@Override
- public void ensureMethodProcessed(
- DexEncodedMethod target, IRCode inlinee) throws ApiLevelException {
+ public void ensureMethodProcessed(DexEncodedMethod target, IRCode inlinee) {
if (!target.isProcessed()) {
if (Log.ENABLED) {
Log.verbose(getClass(), "Forcing extra inline on " + target.toSourceString());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/EnumOrdinalMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/EnumOrdinalMapCollector.java
index 59ddc36..1b68f7d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/EnumOrdinalMapCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/EnumOrdinalMapCollector.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexProgramClass;
@@ -40,7 +39,7 @@
this.options = options;
}
- public AppInfoWithLiveness run() throws ApiLevelException {
+ public AppInfoWithLiveness run() {
for (DexProgramClass clazz : appInfo.classes()) {
processClasses(clazz);
}
@@ -50,7 +49,7 @@
return appInfo;
}
- private void processClasses(DexProgramClass clazz) throws ApiLevelException {
+ private void processClasses(DexProgramClass clazz) {
// Enum classes are flagged as such. Also, for library classes, the ordinals are not known.
if (!clazz.accessFlags.isEnum() || clazz.isLibraryClass() || !clazz.hasClassInitializer()) {
return;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 860cc21..d4cb8a8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.AccessFlags;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexClass;
@@ -151,8 +150,8 @@
return target;
}
- public synchronized void processDoubleInlineCallers(IRConverter converter,
- OptimizationFeedback feedback) throws ApiLevelException {
+ public synchronized void processDoubleInlineCallers(
+ IRConverter converter, OptimizationFeedback feedback) {
if (doubleInlineCallers.size() > 0) {
applyDoubleInlining = true;
List<DexEncodedMethod> methods = doubleInlineCallers
@@ -267,8 +266,7 @@
AppInfoWithSubtyping appInfo,
GraphLense graphLense,
InternalOptions options,
- Position callerPosition)
- throws ApiLevelException {
+ Position callerPosition) {
// Build the IR for a yet not processed method, and perform minimal IR processing.
Origin origin = appInfo.originFor(target.method.holder);
IRCode code = target.buildInliningIR(appInfo, options, generator, callerPosition, origin);
@@ -375,8 +373,10 @@
}
}
- public void performForcedInlining(DexEncodedMethod method, IRCode code,
- Map<InvokeMethodWithReceiver, InliningInfo> invokesToInline) throws ApiLevelException {
+ public void performForcedInlining(
+ DexEncodedMethod method,
+ IRCode code,
+ Map<InvokeMethodWithReceiver, InliningInfo> invokesToInline) {
ForcedInliningOracle oracle = new ForcedInliningOracle(method, invokesToInline);
performInliningImpl(oracle, oracle, method, code);
@@ -387,8 +387,7 @@
IRCode code,
TypeEnvironment typeEnvironment,
Predicate<DexEncodedMethod> isProcessedConcurrently,
- CallSiteInformation callSiteInformation)
- throws ApiLevelException {
+ CallSiteInformation callSiteInformation) {
DefaultInliningOracle oracle =
new DefaultInliningOracle(
@@ -405,11 +404,7 @@
}
private void performInliningImpl(
- InliningStrategy strategy,
- InliningOracle oracle,
- DexEncodedMethod method,
- IRCode code)
- throws ApiLevelException {
+ InliningStrategy strategy, InliningOracle oracle, DexEncodedMethod method, IRCode code) {
if (strategy.exceededAllowance()) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
index 2b0c7e5..7b88b38 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.optimize;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -17,8 +16,7 @@
void markInlined(IRCode inlinee);
- void ensureMethodProcessed(
- DexEncodedMethod target, IRCode inlinee) throws ApiLevelException;
+ void ensureMethodProcessed(DexEncodedMethod target, IRCode inlinee);
ListIterator<BasicBlock> updateTypeInformationIfNeeded(IRCode inlinee,
ListIterator<BasicBlock> blockIterator, BasicBlock block, BasicBlock invokeSuccessor);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 9462354..2e5b703 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.optimize;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
@@ -64,13 +63,44 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.function.BiConsumer;
+/**
+ * Support class for implementing outlining (i.e. extracting common code patterns as methods).
+ *
+ * <p>Outlining happens in three steps.
+ *
+ * <ul>
+ * <li>First, all methods are converted to IR and passed to {@link
+ * Outliner#identifyCandidateMethods()} to identify outlining candidates and the methods
+ * containing each candidate. IR is converted to the output format (DEX or CF) and thrown away
+ * along with the outlining candidates; only a list of lists of methods is kept, where each
+ * list of methods corresponds to methods containing an outlining candidate.
+ * <li>Second, {@link Outliner#selectMethodsForOutlining()} is called to retain the lists of
+ * methods found in the first step that are large enough (see {@link InternalOptions#outline}
+ * {@link OutlineOptions#threshold}), and the methods to be further analyzed for outlining is
+ * returned by {@link Outliner#getMethodsSelectedForOutlining}. Each selected method is then
+ * converted back to IR and passed to {@link Outliner#identifyOutlineSites(IRCode,
+ * DexEncodedMethod)}, which then stores concrete outlining candidates in {@link
+ * Outliner#outlineSites}.
+ * <li>Third, {@link Outliner#buildOutlinerClass(DexType)} is called to construct the <em>outline
+ * support class</em> containing a static helper method for each outline candidate that occurs
+ * frequently enough. Each selected method is then converted to IR, passed to {@link
+ * Outliner#applyOutliningCandidate(IRCode, DexEncodedMethod)} to perform the outlining, and
+ * converted back to the output format (DEX or CF).
+ * </ul>
+ */
public class Outliner {
private final InternalOptions options;
- private final Map<Outline, List<DexEncodedMethod>> candidates = new HashMap<>();
- private final Map<Outline, DexMethod> generatedOutlines = new HashMap<>();
+ /** Result of first step (see {@link Outliner#identifyCandidateMethods()}. */
+ private final List<List<DexEncodedMethod>> candidateMethodLists = new ArrayList<>();
+ /** Result of second step (see {@link Outliner#selectMethodsForOutlining()}. */
private final Set<DexEncodedMethod> methodsSelectedForOutlining = Sets.newIdentityHashSet();
+ /** Result of second step (see {@link Outliner#selectMethodsForOutlining()}. */
+ private final Map<Outline, List<DexEncodedMethod>> outlineSites = new HashMap<>();
+ /** Result of third step (see {@link Outliner#buildOutlinerClass(DexType)}. */
+ private final Map<Outline, DexMethod> generatedOutlines = new HashMap<>();
static final int MAX_IN_SIZE = 5; // Avoid using ranged calls for outlined code.
@@ -652,16 +682,42 @@
// Collect outlining candidates with the methods that can use them.
// TODO(sgjesse): This does not take several usages in the same method into account.
- private class OutlineIdentifier extends OutlineSpotter {
+ private class OutlineMethodIdentifier extends OutlineSpotter {
- OutlineIdentifier(DexEncodedMethod method, BasicBlock block) {
+ private final Map<Outline, List<DexEncodedMethod>> candidateMap;
+
+ OutlineMethodIdentifier(
+ DexEncodedMethod method,
+ BasicBlock block,
+ Map<Outline, List<DexEncodedMethod>> candidateMap) {
+ super(method, block);
+ this.candidateMap = candidateMap;
+ }
+
+ @Override
+ protected void handle(int start, int end, Outline outline) {
+ synchronized (candidateMap) {
+ candidateMap.computeIfAbsent(outline, this::addOutlineMethodList).add(method);
+ }
+ }
+
+ private List<DexEncodedMethod> addOutlineMethodList(Outline outline) {
+ List<DexEncodedMethod> result = new ArrayList<>();
+ candidateMethodLists.add(result);
+ return result;
+ }
+ }
+
+ private class OutlineSiteIdentifier extends OutlineSpotter {
+
+ OutlineSiteIdentifier(DexEncodedMethod method, BasicBlock block) {
super(method, block);
}
@Override
protected void handle(int start, int end, Outline outline) {
- synchronized (candidates) {
- candidates.computeIfAbsent(outline, k -> new ArrayList<>()).add(method);
+ synchronized (outlineSites) {
+ outlineSites.computeIfAbsent(outline, k -> new ArrayList<>()).add(method);
}
}
}
@@ -685,9 +741,9 @@
@Override
protected void handle(int start, int end, Outline outline) {
- if (candidates.containsKey(outline)) {
- DexMethod m = generatedOutlines.get(outline);
- assert m != null;
+ DexMethod m = generatedOutlines.get(outline);
+ if (m != null) {
+ assert removeMethodFromOutlineList(outline);
List<Value> in = new ArrayList<>();
Value returnValue = null;
argumentsMapIndex = 0;
@@ -748,6 +804,14 @@
}
}
}
+
+ /** When assertions are enabled, remove method from the outline's list. */
+ private boolean removeMethodFromOutlineList(Outline outline) {
+ synchronized (outlineSites) {
+ assert outlineSites.get(outline).remove(method);
+ }
+ return true;
+ }
}
public Outliner(AppInfoWithLiveness appInfo, InternalOptions options) {
@@ -756,26 +820,37 @@
this.options = options;
}
- public void identifyCandidates(IRCode code, DexEncodedMethod method) {
+ public BiConsumer<IRCode, DexEncodedMethod> identifyCandidateMethods() {
+ // Since optimizations may change the map identity of Outline objects (e.g. by setting the
+ // out-value of invokes to null), this map must not be used except for identifying methods
+ // potentially relevant to outlining. OutlineMethodIdentifier will add method lists to
+ // candidateMethodLists whenever it adds an entry to candidateMap.
+ Map<Outline, List<DexEncodedMethod>> candidateMap = new HashMap<>();
+ assert candidateMethodLists.isEmpty();
+ return (code, method) -> {
+ assert !(method.getCode() instanceof OutlineCode);
+ for (BasicBlock block : code.blocks) {
+ new OutlineMethodIdentifier(method, block, candidateMap).process();
+ }
+ };
+ }
+
+ public void identifyOutlineSites(IRCode code, DexEncodedMethod method) {
assert !(method.getCode() instanceof OutlineCode);
for (BasicBlock block : code.blocks) {
- new OutlineIdentifier(method, block).process();
+ new OutlineSiteIdentifier(method, block).process();
}
}
public boolean selectMethodsForOutlining() {
assert methodsSelectedForOutlining.size() == 0;
- List<Outline> toRemove = new ArrayList<>();
- for (Entry<Outline, List<DexEncodedMethod>> entry : candidates.entrySet()) {
- if (entry.getValue().size() < options.outline.threshold) {
- toRemove.add(entry.getKey());
- } else {
- methodsSelectedForOutlining.addAll(entry.getValue());
+ assert outlineSites.size() == 0;
+ for (List<DexEncodedMethod> outlineMethods : candidateMethodLists) {
+ if (outlineMethods.size() >= options.outline.threshold) {
+ methodsSelectedForOutlining.addAll(outlineMethods);
}
}
- for (Outline outline : toRemove) {
- candidates.remove(outline);
- }
+ candidateMethodLists.clear();
return methodsSelectedForOutlining.size() > 0;
}
@@ -783,6 +858,95 @@
return methodsSelectedForOutlining;
}
+ public DexProgramClass buildOutlinerClass(DexType type) {
+ // Build the outlined methods.
+ // By now the candidates are the actual selected outlines. Name the generated methods in a
+ // consistent order, to provide deterministic output.
+ List<Outline> outlines = selectOutlines();
+ outlines.sort(Comparator.naturalOrder());
+ DexEncodedMethod[] direct = new DexEncodedMethod[outlines.size()];
+ int count = 0;
+ for (Outline outline : outlines) {
+ MethodAccessFlags methodAccess =
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_STATIC, false);
+ DexString methodName = dexItemFactory.createString(OutlineOptions.METHOD_PREFIX + count);
+ DexMethod method = outline.buildMethod(type, methodName);
+ direct[count] =
+ new DexEncodedMethod(
+ method,
+ methodAccess,
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ new OutlineCode(outline));
+ generatedOutlines.put(outline, method);
+ count++;
+ }
+ // No need to sort the direct methods as they are generated in sorted order.
+
+ // Build the outliner class.
+ DexType superType = dexItemFactory.createType("Ljava/lang/Object;");
+ DexTypeList interfaces = DexTypeList.empty();
+ DexString sourceFile = dexItemFactory.createString("outline");
+ ClassAccessFlags accessFlags = ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC);
+ DexProgramClass clazz =
+ new DexProgramClass(
+ type,
+ null,
+ new SynthesizedOrigin("outlining", getClass()),
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ null,
+ Collections.emptyList(),
+ // TODO: Build dex annotations structure.
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY, // Static fields.
+ DexEncodedField.EMPTY_ARRAY, // Instance fields.
+ direct,
+ DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
+ options.itemFactory.getSkipNameValidationForTesting());
+ if (options.isGeneratingClassFiles()) {
+ // Don't set class file version below 50.0 (JDK release 1.6).
+ clazz.setClassFileVersion(Math.max(50, getClassFileVersion(outlines)));
+ }
+
+ return clazz;
+ }
+
+ private List<Outline> selectOutlines() {
+ assert outlineSites.size() > 0;
+ assert candidateMethodLists.isEmpty();
+ List<Outline> result = new ArrayList<>();
+ for (Entry<Outline, List<DexEncodedMethod>> entry : outlineSites.entrySet()) {
+ if (entry.getValue().size() >= options.outline.threshold) {
+ result.add(entry.getKey());
+ }
+ }
+ return result;
+ }
+
+ private int getClassFileVersion(List<Outline> outlines) {
+ assert options.isGeneratingClassFiles();
+ int classFileVersion = -1;
+ Set<DexType> seen = Sets.newIdentityHashSet();
+ for (Outline outline : outlines) {
+ List<DexEncodedMethod> methods = outlineSites.get(outline);
+ for (DexEncodedMethod method : methods) {
+ DexType holder = method.method.holder;
+ if (seen.add(holder)) {
+ DexProgramClass programClass = appInfo.definitionFor(holder).asProgramClass();
+ assert programClass != null : "Attempt to outline from library class";
+ assert programClass.originatesFromClassResource()
+ : "Attempt to outline from non-classfile input to classfile output";
+ classFileVersion = Math.max(classFileVersion, programClass.getClassFileVersion());
+ }
+ }
+ }
+ return classFileVersion;
+ }
+
public void applyOutliningCandidate(IRCode code, DexEncodedMethod method) {
assert !(method.getCode() instanceof OutlineCode);
ListIterator<BasicBlock> blocksIterator = code.blocks.listIterator();
@@ -794,6 +958,13 @@
}
}
+ public boolean checkAllOutlineSitesFoundAgain() {
+ for (Outline outline : generatedOutlines.keySet()) {
+ assert outlineSites.get(outline).isEmpty() : outlineSites.get(outline);
+ }
+ return true;
+ }
+
static public void noProcessing(IRCode code, DexEncodedMethod method) {
// No operation.
}
@@ -1004,9 +1175,8 @@
}
@Override
- public IRCode buildIR(DexEncodedMethod encodedMethod,
- AppInfo appInfo, InternalOptions options, Origin origin)
- throws ApiLevelException {
+ public IRCode buildIR(
+ DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin) {
OutlineSourceCode source = new OutlineSourceCode(outline);
IRBuilder builder = new IRBuilder(encodedMethod, appInfo, source, options);
return builder.build();
@@ -1037,81 +1207,4 @@
return null;
}
}
-
-
- public DexProgramClass buildOutlinerClass(DexType type) {
- if (candidates.size() == 0) {
- return null;
- }
-
- // Build the outlined methods.
- DexEncodedMethod[] direct = new DexEncodedMethod[candidates.size()];
- int count = 0;
-
- // By now the candidates are the actual selected outlines. Name the generated methods in a
- // consistent order, to provide deterministic output.
- List<Outline> outlines = new ArrayList<>(candidates.keySet());
- outlines.sort(Comparator.naturalOrder());
- for (Outline outline : outlines) {
- MethodAccessFlags methodAccess =
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_STATIC, false);
- DexString methodName = dexItemFactory.createString(OutlineOptions.METHOD_PREFIX + count);
- DexMethod method = outline.buildMethod(type, methodName);
- direct[count] = new DexEncodedMethod(method, methodAccess, DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(), new OutlineCode(outline));
- generatedOutlines.put(outline, method);
- count++;
- }
- // No need to sort the direct methods as they are generated in sorted order.
-
- // Build the outliner class.
- DexType superType = dexItemFactory.createType("Ljava/lang/Object;");
- DexTypeList interfaces = DexTypeList.empty();
- DexString sourceFile = dexItemFactory.createString("outline");
- ClassAccessFlags accessFlags = ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC);
- DexProgramClass clazz =
- new DexProgramClass(
- type,
- null,
- new SynthesizedOrigin("outlining", getClass()),
- accessFlags,
- superType,
- interfaces,
- sourceFile,
- null,
- Collections.emptyList(),
- // TODO: Build dex annotations structure.
- DexAnnotationSet.empty(),
- DexEncodedField.EMPTY_ARRAY, // Static fields.
- DexEncodedField.EMPTY_ARRAY, // Instance fields.
- direct,
- DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
- options.itemFactory.getSkipNameValidationForTesting());
- if (options.isGeneratingClassFiles()) {
- // Don't set class file version below 50.0 (JDK release 1.6).
- clazz.setClassFileVersion(Math.max(50, getClassFileVersion()));
- }
-
- return clazz;
- }
-
- private int getClassFileVersion() {
- assert options.isGeneratingClassFiles();
- int classFileVersion = -1;
- Set<DexType> seen = Sets.newIdentityHashSet();
- for (List<DexEncodedMethod> methods : candidates.values()) {
- for (DexEncodedMethod method : methods) {
- DexType holder = method.method.holder;
- if (seen.add(holder)) {
- DexProgramClass programClass = appInfo.definitionFor(holder).asProgramClass();
- assert programClass != null : "Attempt to outline from library class";
- assert programClass.originatesFromClassResource()
- : "Attempt to outline from non-classfile input to classfile output";
- classFileVersion = Math.max(classFileVersion, programClass.getClassFileVersion());
- }
- }
- }
- return classFileVersion;
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
index 1284671..a586355 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
@@ -75,7 +74,7 @@
intArrayType = appInfo.dexItemFactory.createType("[I");
}
- public AppInfoWithLiveness run() throws ApiLevelException {
+ public AppInfoWithLiveness run() {
for (DexProgramClass clazz : appInfo.classes()) {
processClasses(clazz);
}
@@ -85,7 +84,7 @@
return appInfo;
}
- private void processClasses(DexProgramClass clazz) throws ApiLevelException {
+ private void processClasses(DexProgramClass clazz) {
// Switchmap classes are synthetic and have a class initializer.
if (!clazz.accessFlags.isSynthetic() && !clazz.hasClassInitializer()) {
return;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 7d13b1a..9ff44eb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.optimize.classinliner;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -40,7 +39,7 @@
private static final Map<DexField, Integer> NO_MAPPING = new IdentityHashMap<>();
public interface InlinerAction {
- void inline(Map<InvokeMethodWithReceiver, InliningInfo> methods) throws ApiLevelException;
+ void inline(Map<InvokeMethodWithReceiver, InliningInfo> methods);
}
public ClassInliner(DexItemFactory factory) {
@@ -110,7 +109,7 @@
DexEncodedMethod method,
IRCode code,
Predicate<DexEncodedMethod> isProcessedConcurrently,
- InlinerAction inliner) throws ApiLevelException {
+ InlinerAction inliner) {
// Collect all the new-instance instructions in the code before inlining.
List<NewInstance> newInstances = Streams.stream(code.instructionIterator())
@@ -246,8 +245,8 @@
}
}
- private void inlineAllCalls(InlinerAction inliner,
- Map<InvokeMethodWithReceiver, InliningInfo> methodCalls) throws ApiLevelException {
+ private void inlineAllCalls(
+ InlinerAction inliner, Map<InvokeMethodWithReceiver, InliningInfo> methodCalls) {
if (!methodCalls.isEmpty()) {
inliner.inline(methodCalls);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 466117f..d1b56f2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.optimize.lambda;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -187,9 +186,13 @@
}
}
- public final void applyLambdaClassMapping(DexApplication app,
- IRConverter converter, OptimizationFeedback feedback, Builder<?> builder,
- ExecutorService executorService) throws ExecutionException, ApiLevelException {
+ public final void applyLambdaClassMapping(
+ DexApplication app,
+ IRConverter converter,
+ OptimizationFeedback feedback,
+ Builder<?> builder,
+ ExecutorService executorService)
+ throws ExecutionException {
if (lambdas.isEmpty()) {
return;
}
@@ -294,8 +297,7 @@
}
}
- private void rewriteLambdaReferences(
- IRConverter converter, OptimizationFeedback feedback) throws ApiLevelException {
+ private void rewriteLambdaReferences(IRConverter converter, OptimizationFeedback feedback) {
List<DexEncodedMethod> methods =
methodsToReprocess
.stream()
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 2f055ce..bb27c08 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -390,14 +390,10 @@
// Compute the final change in locals and insert it before nextInstruction.
boolean localsChanged = !ending.isEmpty() || !starting.isEmpty();
if (localsChanged) {
- boolean skipChange =
- nextInstruction == nextInstruction.getBlock().exit() && nextInstruction.isGoto();
- if (!skipChange) {
- DebugLocalsChange change = createLocalsChange(ending, starting);
- if (change != null) {
- // Insert the DebugLocalsChange instruction before nextInstruction.
- instructionIterator.add(change);
- }
+ DebugLocalsChange change = createLocalsChange(ending, starting);
+ if (change != null) {
+ // Insert the DebugLocalsChange instruction before nextInstruction.
+ instructionIterator.add(change);
}
// Create new maps for the next DebugLocalsChange instruction.
ending = new Int2ReferenceOpenHashMap<>();
@@ -636,13 +632,21 @@
LiveIntervals intervals = current.getLiveIntervals();
assert intervals.getRegisterLimit() == Constants.U16BIT_MAX;
boolean canUseArgumentRegister = true;
+ boolean couldUseArgumentRegister = true;
for (LiveIntervals child : intervals.getSplitChildren()) {
- if (child.getRegisterLimit() < highestUsedRegister()) {
- canUseArgumentRegister = false;
- break;
+ int registerConstraint = child.getRegisterLimit();
+ if (registerConstraint < Constants.U16BIT_MAX) {
+ couldUseArgumentRegister = false;
+
+ if (registerConstraint < highestUsedRegister()) {
+ canUseArgumentRegister = false;
+ break;
+ }
}
}
- if (canUseArgumentRegister) {
+ if (canUseArgumentRegister && !couldUseArgumentRegister) {
+ // Only return true if there is a constrained use where it turns out that we can use the
+ // original argument register. This way we will not unnecessarily redo move insertion.
argumentRegisterUnsplit = true;
for (LiveIntervals child : intervals.getSplitChildren()) {
child.clearRegisterAssignment();
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
index 9259485..0ba49d3 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
@@ -21,15 +21,27 @@
private final DexType targetReceiver;
private final DexMethod target;
private final Invoke.Type invokeType;
+ private final boolean castResult;
public ForwardMethodSourceCode(DexType receiver, DexProto proto,
DexType targetReceiver, DexMethod target, Invoke.Type invokeType) {
+ this(receiver, proto, targetReceiver, target, invokeType, false);
+ }
+
+ public ForwardMethodSourceCode(
+ DexType receiver,
+ DexProto proto,
+ DexType targetReceiver,
+ DexMethod target,
+ Invoke.Type invokeType,
+ boolean castResult) {
super(receiver, proto);
assert (targetReceiver == null) == (invokeType == Invoke.Type.STATIC);
this.target = target;
this.targetReceiver = targetReceiver;
this.invokeType = invokeType;
+ this.castResult = castResult;
assert checkSignatures();
switch (invokeType) {
@@ -67,7 +79,7 @@
assert (source.isClassType() && target.isClassType()) || source == target;
}
- assert this.proto.returnType == target.proto.returnType;
+ assert this.proto.returnType == target.proto.returnType || castResult;
return true;
}
@@ -99,6 +111,9 @@
ValueType valueType = ValueType.fromDexType(proto.returnType);
int tempValue = nextRegister(valueType);
add(builder -> builder.addMoveResult(tempValue));
+ if (this.proto.returnType != target.proto.returnType) {
+ add(builder -> builder.addCheckCast(tempValue, this.proto.returnType));
+ }
add(builder -> builder.addReturn(valueType, tempValue));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
index f8134f4..a5807c3 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.synthetic;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.Code;
@@ -40,9 +39,7 @@
@Override
public final IRCode buildIR(
- DexEncodedMethod encodedMethod, AppInfo appInfo,
- InternalOptions options, Origin origin)
- throws ApiLevelException {
+ DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin) {
return new IRBuilder(encodedMethod, appInfo, sourceCode, options).build();
}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
index 03206b5..80a439a 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo.NO_THROW;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexProto;
@@ -18,9 +17,9 @@
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.SourceCode;
-import com.android.tools.r8.utils.ThrowingConsumer;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
import java.util.function.Predicate;
public abstract class SyntheticSourceCode implements SourceCode {
@@ -43,7 +42,7 @@
private Value[] paramValues;
// Instruction constructors
- private List<ThrowingConsumer<IRBuilder, ApiLevelException>> constructors = new ArrayList<>();
+ private List<Consumer<IRBuilder>> constructors = new ArrayList<>();
private List<Predicate<IRBuilder>> traceEvents = new ArrayList<>();
protected SyntheticSourceCode(DexType receiver, DexProto proto) {
@@ -63,12 +62,11 @@
}
}
- protected final void add(ThrowingConsumer<IRBuilder, ApiLevelException> constructor) {
+ protected final void add(Consumer<IRBuilder> constructor) {
add(constructor, doesNotEndBlock);
}
- protected final void add(
- ThrowingConsumer<IRBuilder, ApiLevelException> constructor, Predicate<IRBuilder> traceEvent) {
+ protected final void add(Consumer<IRBuilder> constructor, Predicate<IRBuilder> traceEvent) {
constructors.add(constructor);
traceEvents.add(traceEvent);
}
@@ -189,8 +187,7 @@
@Override
public final void buildInstruction(
- IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
- throws ApiLevelException {
+ IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
constructors.get(instructionIndex).accept(builder);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index a87de4b..27b2da7 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -7,7 +7,6 @@
import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
import static com.android.tools.r8.shaking.ProguardConfigurationUtils.buildIdentifierNameStringRule;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
@@ -834,7 +833,7 @@
return;
}
if (Log.ENABLED) {
- Log.verbose(getClass(), "Register new instatiation of `%s`.", clazz);
+ Log.verbose(getClass(), "Register new instantiation of `%s`.", clazz);
}
workList.add(Action.markInstantiated(clazz, KeepReason.instantiatedIn(method)));
}
@@ -1343,13 +1342,8 @@
}
private void handleProguardReflectiveBehavior(DexEncodedMethod method) {
- try {
- IRCode code = method.buildIR(appInfo, options, appInfo.originFor(method.method.holder));
- code.instructionIterator().forEachRemaining(this::handleProguardReflectiveBehavior);
- } catch (ApiLevelException e) {
- // Ignore this exception here. It will be hit again further in the pipeline when
- // generating code.
- }
+ IRCode code = method.buildIR(appInfo, options, appInfo.originFor(method.method.holder));
+ code.instructionIterator().forEachRemaining(this::handleProguardReflectiveBehavior);
}
private void handleProguardReflectiveBehavior(Instruction instruction) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
index b007f83..568ad3d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -83,6 +83,10 @@
return nameList == null ? Collections::emptyIterator : nameList.getWildcards();
}
+ protected ProguardClassNameList materialize() {
+ return this;
+ }
+
public abstract void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer);
private static class EmptyClassNameList extends ProguardClassNameList {
@@ -149,6 +153,11 @@
}
@Override
+ protected SingleClassNameList materialize() {
+ return new SingleClassNameList(className.materialize());
+ }
+
+ @Override
public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
consumer.accept(className);
}
@@ -202,6 +211,12 @@
}
@Override
+ protected PositiveClassNameList materialize() {
+ return new PositiveClassNameList(
+ classNames.stream().map(ProguardTypeMatcher::materialize).collect(Collectors.toList()));
+ }
+
+ @Override
public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
classNames.forEach(consumer);
}
@@ -260,6 +275,13 @@
}
@Override
+ protected ProguardClassNameList materialize() {
+ Builder builder = builder();
+ classNames.forEach((m, negated) -> builder.addClassName(negated, m.materialize()));
+ return builder.build();
+ }
+
+ @Override
public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
classNames.object2BooleanEntrySet().forEach(entry -> consumer.accept(entry.getKey()));
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 8eb8e9c..4a25e42 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -717,6 +717,11 @@
}
}
+ private StringDiagnostic parseClassTypeUnexpected(Origin origin, TextPosition start) {
+ return new StringDiagnostic(
+ "Expected [!]interface|@interface|class|enum", origin, getPosition(start));
+ }
+
private void parseClassType(
ProguardClassSpecification.Builder builder) throws ProguardRuleParserException {
skipWhitespace();
@@ -724,17 +729,21 @@
if (acceptChar('!')) {
builder.setClassTypeNegated(true);
}
- if (acceptString("interface")) {
+ if (acceptChar('@')) {
+ skipWhitespace();
+ if (acceptString("interface")) {
+ builder.setClassType(ProguardClassType.ANNOTATION_INTERFACE);
+ } else {
+ throw reporter.fatalError(parseClassTypeUnexpected(origin, start));
+ }
+ } else if (acceptString("interface")) {
builder.setClassType(ProguardClassType.INTERFACE);
- } else if (acceptString("@interface")) {
- builder.setClassType(ProguardClassType.ANNOTATION_INTERFACE);
} else if (acceptString("class")) {
builder.setClassType(ProguardClassType.CLASS);
} else if (acceptString("enum")) {
builder.setClassType(ProguardClassType.ENUM);
} else {
- throw reporter.fatalError(new StringDiagnostic(
- "Expected [!]interface|@interface|class|enum", origin, getPosition(start)));
+ throw reporter.fatalError(parseClassTypeUnexpected(origin, start));
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index 2e8cccc..47f26ed 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -5,6 +5,7 @@
import com.google.common.collect.Iterables;
import java.util.List;
+import java.util.stream.Collectors;
public class ProguardIfRule extends ProguardKeepRule {
@@ -51,6 +52,24 @@
}
@Override
+ protected ProguardIfRule materialize() {
+ return new ProguardIfRule(
+ getClassAnnotation(),
+ getClassAccessFlags(),
+ getNegatedClassAccessFlags(),
+ getClassTypeNegated(),
+ getClassType(),
+ getClassNames().materialize(),
+ getInheritanceAnnotation() == null ? null : getInheritanceAnnotation().materialize(),
+ getInheritanceClassName() == null ? null : getInheritanceClassName().materialize(),
+ getInheritanceIsExtends(),
+ getMemberRules() == null ? null :
+ getMemberRules().stream()
+ .map(ProguardMemberRule::materialize).collect(Collectors.toList()),
+ subsequentRule.materialize());
+ }
+
+ @Override
public boolean equals(Object o) {
if (!(o instanceof ProguardIfRule)) {
return false;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
index 8756f1a..6651dae 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -5,6 +5,7 @@
import java.util.List;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
public class ProguardKeepRule extends ProguardConfigurationRule {
@@ -68,6 +69,24 @@
return modifiers;
}
+ protected ProguardKeepRule materialize() {
+ return new ProguardKeepRule(
+ getClassAnnotation(),
+ getClassAccessFlags(),
+ getNegatedClassAccessFlags(),
+ getClassTypeNegated(),
+ getClassType(),
+ getClassNames() == null ? null : getClassNames().materialize(),
+ getInheritanceAnnotation() == null ? null : getInheritanceAnnotation().materialize(),
+ getInheritanceClassName() == null ? null : getInheritanceClassName().materialize(),
+ getInheritanceIsExtends(),
+ getMemberRules() == null ? null :
+ getMemberRules().stream()
+ .map(ProguardMemberRule::materialize).collect(Collectors.toList()),
+ getType(),
+ getModifiers());
+ }
+
@Override
public boolean equals(Object o) {
if (!(o instanceof ProguardKeepRule)) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
index c3454a7..39737ea 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -12,6 +12,7 @@
import com.google.common.collect.Iterables;
import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class ProguardMemberRule {
@@ -278,6 +279,20 @@
);
}
+ ProguardMemberRule materialize() {
+ return new ProguardMemberRule(
+ getAnnotation() == null ? null : getAnnotation().materialize(),
+ getAccessFlags(),
+ getNegatedAccessFlags(),
+ getRuleType(),
+ getType() == null ? null : getType().materialize(),
+ getName() == null ? null : getName().materialize(),
+ getArguments() == null ? null :
+ getArguments().stream()
+ .map(ProguardTypeMatcher::materialize).collect(Collectors.toList()),
+ getReturnValue());
+ }
+
@Override
public boolean equals(Object o) {
if (!(o instanceof ProguardMemberRule)) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
index 4adb948..c217d45 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
@@ -9,6 +9,7 @@
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
public abstract class ProguardNameMatcher {
@@ -96,11 +97,19 @@
return nameMatcher == null ? Collections::emptyIterator : nameMatcher.getWildcards();
}
+ protected ProguardNameMatcher materialize() {
+ return this;
+ }
+
private static class MatchAllNames extends ProguardNameMatcher {
private final ProguardWildcard wildcard;
MatchAllNames() {
- this.wildcard = new Pattern("*");
+ this(new Pattern("*"));
+ }
+
+ private MatchAllNames(ProguardWildcard wildcard) {
+ this.wildcard = wildcard;
}
@Override
@@ -115,6 +124,11 @@
}
@Override
+ protected MatchAllNames materialize() {
+ return new MatchAllNames(wildcard.materialize());
+ }
+
+ @Override
public String toString() {
return "*";
}
@@ -145,6 +159,15 @@
}
@Override
+ protected MatchNamePattern materialize() {
+ List<ProguardWildcard> materializedWildcards =
+ wildcards.stream().map(ProguardWildcard::materialize).collect(Collectors.toList());
+ IdentifierPatternWithWildcards identifierPatternWithMaterializedWildcards =
+ new IdentifierPatternWithWildcards(pattern, materializedWildcards);
+ return new MatchNamePattern(identifierPatternWithMaterializedWildcards);
+ }
+
+ @Override
public String toString() {
return pattern;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
index 0d5016b..34ee554 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -13,6 +13,7 @@
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
public abstract class ProguardTypeMatcher {
@@ -40,6 +41,10 @@
return typeMatcher == null ? Collections::emptyIterator : typeMatcher.getWildcards();
}
+ protected ProguardTypeMatcher materialize() {
+ return this;
+ }
+
@Override
public abstract String toString();
@@ -99,7 +104,11 @@
private final ProguardWildcard wildcard;
MatchAllTypes() {
- this.wildcard = new Pattern(MATCH_ALL_PATTERN);
+ this(new Pattern(MATCH_ALL_PATTERN));
+ }
+
+ private MatchAllTypes(ProguardWildcard wildcard) {
+ this.wildcard = wildcard;
}
@Override
@@ -114,6 +123,11 @@
}
@Override
+ protected MatchAllTypes materialize() {
+ return new MatchAllTypes(wildcard.materialize());
+ }
+
+ @Override
public String toString() {
return MATCH_ALL_PATTERN;
}
@@ -170,9 +184,13 @@
private final ProguardWildcard wildcard;
private MatchClassTypes(String pattern) {
+ this(pattern, new Pattern(pattern));
+ }
+
+ private MatchClassTypes(String pattern, ProguardWildcard wildcard) {
assert pattern.equals(LEGACY_MATCH_CLASS_PATTERN) || pattern.equals(MATCH_CLASS_PATTERN);
this.pattern = pattern;
- this.wildcard = new Pattern(pattern);
+ this.wildcard = wildcard;
}
@Override
@@ -190,6 +208,11 @@
}
@Override
+ protected MatchClassTypes materialize() {
+ return new MatchClassTypes(pattern, wildcard.materialize());
+ }
+
+ @Override
public String toString() {
return pattern;
}
@@ -212,7 +235,11 @@
private final ProguardWildcard wildcard;
MatchBasicTypes() {
- this.wildcard = new Pattern(MATCH_BASIC_PATTERN);
+ this(new Pattern(MATCH_BASIC_PATTERN));
+ }
+
+ private MatchBasicTypes(ProguardWildcard wildcard) {
+ this.wildcard = wildcard;
}
@Override
@@ -230,6 +257,11 @@
}
@Override
+ protected MatchBasicTypes materialize() {
+ return new MatchBasicTypes(wildcard.materialize());
+ }
+
+ @Override
public String toString() {
return MATCH_BASIC_PATTERN;
}
@@ -311,6 +343,15 @@
return wildcards;
}
+ @Override
+ protected MatchTypePattern materialize() {
+ List<ProguardWildcard> materializedWildcards =
+ wildcards.stream().map(ProguardWildcard::materialize).collect(Collectors.toList());
+ IdentifierPatternWithWildcards identifierPatternWithMaterializedWildcards =
+ new IdentifierPatternWithWildcards(pattern, materializedWildcards);
+ return new MatchTypePattern(identifierPatternWithMaterializedWildcards, kind);
+ }
+
private static boolean matchClassOrTypeNameImpl(
String pattern, int patternIndex,
String name, int nameIndex,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardWildcard.java b/src/main/java/com/android/tools/r8/shaking/ProguardWildcard.java
index 84151e8..b6cb8e5 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardWildcard.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardWildcard.java
@@ -10,6 +10,7 @@
abstract void setCaptured(String captured);
abstract void clearCaptured();
abstract String getCaptured();
+ abstract ProguardWildcard materialize();
boolean isPattern() {
return false;
@@ -29,7 +30,6 @@
static class Pattern extends ProguardWildcard {
final String pattern;
- // TODO(b/79486261): Having captured part here makes back-reference handling not thread-safe.
private String captured = null;
Pattern(String pattern) {
@@ -37,21 +37,31 @@
}
@Override
- void setCaptured(String captured) {
+ synchronized void setCaptured(String captured) {
this.captured = captured;
}
@Override
- void clearCaptured() {
+ synchronized void clearCaptured() {
captured = null;
}
@Override
- String getCaptured() {
+ synchronized String getCaptured() {
return captured;
}
@Override
+ Pattern materialize() {
+ if (captured == null) {
+ return this;
+ }
+ Pattern copy = new Pattern(pattern);
+ copy.setCaptured(captured);
+ return copy;
+ }
+
+ @Override
boolean isPattern() {
return true;
}
@@ -96,6 +106,16 @@
}
@Override
+ BackReference materialize() {
+ if (reference == null || reference.getCaptured() == null) {
+ return this;
+ }
+ BackReference copy = new BackReference(referenceIndex);
+ copy.setReference(reference.materialize());
+ return copy;
+ }
+
+ @Override
boolean isBackReference() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 6a8adfa..700e146 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -348,10 +348,9 @@
if (ifRule.getClassNames().matches(currentLiveType)) {
Collection<ProguardMemberRule> memberKeepRules = ifRule.getMemberRules();
if (memberKeepRules.isEmpty()) {
- runPerRule(executorService, futures, ifRule.subsequentRule, ifRule);
- // TODO(b/79486261): remove this barrier per rule.
- ThreadUtils.awaitFutures(futures);
- futures.clear();
+ ProguardIfRule materializedRule = ifRule.materialize();
+ runPerRule(
+ executorService, futures, materializedRule.subsequentRule, materializedRule);
// No member rule to satisfy. Move on to the next live type.
continue;
}
@@ -391,17 +390,15 @@
}
}
if (satisfied) {
- runPerRule(executorService, futures, ifRule.subsequentRule, ifRule);
- // TODO(b/79486261): remove this barrier per rule.
- ThreadUtils.awaitFutures(futures);
- futures.clear();
+ ProguardIfRule materializedRule = ifRule.materialize();
+ runPerRule(
+ executorService, futures, materializedRule.subsequentRule, materializedRule);
}
}
}
}
}
- // TODO(b/79486261): This is the best place to fully utilize available threads.
- // ThreadUtils.awaitFutures(futures);
+ ThreadUtils.awaitFutures(futures);
}
} finally {
application.timing.end();
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
index cd21d17..25512f7 100644
--- a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
@@ -35,31 +35,38 @@
@Override
public synchronized void open() {
assert !closed;
- openCount ++;
+ openCount++;
}
@Override
- public synchronized void close() throws IOException {
+ public synchronized void close(DiagnosticsHandler handler) {
assert !closed;
openCount--;
if (openCount == 0) {
closed = true;
- if (stream != null) {
- stream.close();
+ try {
+ getStreamRaw().close();
stream = null;
+ } catch (IOException e) {
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
}
+ private ZipOutputStream getStreamRaw() throws IOException {
+ if (stream != null) {
+ return stream;
+ }
+ return new ZipOutputStream(Files.newOutputStream(
+ archive, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
+ }
+
/** Get or open the zip output stream. */
private synchronized ZipOutputStream getStream(DiagnosticsHandler handler) {
assert !closed;
if (stream == null) {
try {
- stream =
- new ZipOutputStream(
- Files.newOutputStream(
- archive, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
+ getStreamRaw();
} catch (IOException e) {
handler.error(new ExceptionDiagnostic(e, origin));
}
diff --git a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
index 2dc4a2d..2058d39 100644
--- a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
@@ -30,7 +30,7 @@
}
@Override
- public void close() {
+ public void close(DiagnosticsHandler handler) {
}
@Override
diff --git a/src/main/java/com/android/tools/r8/utils/OutputBuilder.java b/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
index 27d07c4..9237485 100644
--- a/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
@@ -7,14 +7,15 @@
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.origin.Origin;
-import java.io.Closeable;
import java.nio.file.Path;
-public interface OutputBuilder extends Closeable {
+public interface OutputBuilder {
char NAME_SEPARATOR = '/';
void open();
+ void close(DiagnosticsHandler handler);
+
void addDirectory(String name, DiagnosticsHandler handler);
void addFile(String name, DataEntryResource content, DiagnosticsHandler handler);
diff --git a/src/test/examples/multidex/main-dex-rules-whyareyoukeeping.txt b/src/test/examples/multidex/main-dex-rules-whyareyoukeeping.txt
new file mode 100644
index 0000000..4a94d41
--- /dev/null
+++ b/src/test/examples/multidex/main-dex-rules-whyareyoukeeping.txt
@@ -0,0 +1,30 @@
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class * extends **.Instrumentation {
+ <init>();
+}
+-keep public class * extends **.Application {
+ <init>();
+ void attachBaseContext(*.Context);
+}
+-keep public class * extends **.Activity {
+ <init>();
+}
+-keep public class * extends **.Service {
+ <init>();
+}
+-keep public class * extends **.ContentProvider {
+ <init>();
+}
+-keep public class * extends **.BroadcastReceiver {
+ <init>();
+}
+-keep public class * extends **.BackupAgent {
+ <init>();
+}
+
+-whyareyoukeeping class **
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index dc16cac..a11c960 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -418,6 +418,17 @@
.build()));
}
+ @Test
+ public void noInputOutputsEmptyZip() throws CompilationFailedException, IOException {
+ Path emptyZip = temp.getRoot().toPath().resolve("empty.zip");
+ D8.run(
+ D8Command.builder()
+ .setOutput(emptyZip, OutputMode.DexIndexed)
+ .build());
+ assertTrue(Files.exists(emptyZip));
+ assertEquals(0, new ZipFile(emptyZip.toFile()).size());
+ }
+
private D8Command parse(String... args) throws CompilationFailedException {
return D8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index a89cbd4..a7c7e02 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.origin.EmbeddedOrigin;
import com.android.tools.r8.utils.FileUtils;
import com.google.common.collect.ImmutableList;
+import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -21,6 +22,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.zip.ZipFile;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -428,6 +430,17 @@
assertTrue(result.stdout.contains("-printconfiguration"));
}
+ @Test
+ public void noInputOutputsEmptyZip() throws CompilationFailedException, IOException {
+ Path emptyZip = temp.getRoot().toPath().resolve("empty.zip");
+ R8.run(
+ R8Command.builder()
+ .setOutput(emptyZip, OutputMode.DexIndexed)
+ .build());
+ assertTrue(Files.exists(emptyZip));
+ assertEquals(0, new ZipFile(emptyZip.toFile()).size());
+ }
+
private R8Command parse(String... args) throws CompilationFailedException {
return R8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
index 5ec1bce..cc8633e4 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
@@ -432,7 +432,7 @@
assertEquals(0, javaResult.exitCode);
AndroidApp processedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
- // Disable inlining to avoid the (short) tested method from being inlined then removed.
+ // Disable inlining to avoid the (short) tested method from being inlined and then removed.
internalOptions -> internalOptions.enableInlining = false);
// Run processed (output) program on ART
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
index 35a7613..60df7d2 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
@@ -109,7 +109,7 @@
assertEquals(0, javaResult.exitCode);
AndroidApp optimizedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
- // Disable inlining to avoid the (short) tested method from being inlined then removed.
+ // Disable inlining to avoid the (short) tested method from being inlined and then removed.
internalOptions -> internalOptions.enableInlining = false);
// Run optimized (output) program on ART
diff --git a/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java b/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
index ad6a72b..d6e0542 100644
--- a/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.cf;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
@@ -14,8 +13,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.InvalidDebugInfoException;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.ThrowingConsumer;
import java.nio.file.Path;
@@ -36,18 +33,9 @@
ProcessResult run1 = ToolHelper.runJava(out1, CLASS.getCanonicalName());
assertEquals(runInput.toString(), run1.toString());
Path out2 = temp.getRoot().toPath().resolve("out2.zip");
- boolean invalidDebugInfo = false;
- try {
- build(builder -> builder.addProgramFiles(out1), new ClassFileConsumer.ArchiveConsumer(out2));
- } catch (CompilationError e) {
- invalidDebugInfo = e.getCause() instanceof InvalidDebugInfoException;
- }
- // TODO(b/77522100): Change to assertFalse when fixed.
- assertTrue(invalidDebugInfo);
- if (!invalidDebugInfo) {
- ProcessResult run2 = ToolHelper.runJava(out2, CLASS.getCanonicalName());
- assertEquals(runInput.toString(), run2.toString());
- }
+ build(builder -> builder.addProgramFiles(out1), new ClassFileConsumer.ArchiveConsumer(out2));
+ ProcessResult run2 = ToolHelper.runJava(out2, CLASS.getCanonicalName());
+ assertEquals(runInput.toString(), run2.toString());
}
private void build(ThrowingConsumer<Builder, Exception> input, ProgramConsumer consumer)
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
index 967b33e..aefe665 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
@@ -7,9 +7,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.AppInfo;
@@ -124,15 +122,11 @@
.method(
new MethodSignature("subtractConstants8bitRegisters", "int", ImmutableList.of()))
.getMethod();
- try {
- IRCode irCode = subtract.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
- TypeAnalysis analysis = new TypeAnalysis(appInfo, subtract, irCode);
- analysis.forEach((v, l) -> {
- assertEither(l, PRIMITIVE, NULL, TOP);
- });
- } catch (ApiLevelException e) {
- fail(e.getMessage());
- }
+ IRCode irCode = subtract.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+ TypeAnalysis analysis = new TypeAnalysis(appInfo, subtract, irCode);
+ analysis.forEach((v, l) -> {
+ assertEither(l, PRIMITIVE, NULL, TOP);
+ });
}
// A couple branches, along with some recursive calls.
@@ -142,15 +136,11 @@
inspector.clazz("Test")
.method(new MethodSignature("fibonacci", "int", ImmutableList.of("int")))
.getMethod();
- try {
- IRCode irCode = fib.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
- TypeAnalysis analysis = new TypeAnalysis(appInfo, fib, irCode);
- analysis.forEach((v, l) -> {
- assertEither(l, PRIMITIVE, NULL);
- });
- } catch (ApiLevelException e) {
- fail(e.getMessage());
- }
+ IRCode irCode = fib.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+ TypeAnalysis analysis = new TypeAnalysis(appInfo, fib, irCode);
+ analysis.forEach((v, l) -> {
+ assertEither(l, PRIMITIVE, NULL);
+ });
}
// fill-array-data
@@ -160,32 +150,28 @@
inspector.clazz("Test")
.method(new MethodSignature("test1", "int[]", ImmutableList.of()))
.getMethod();
- try {
- IRCode irCode = test1.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
- TypeAnalysis analysis = new TypeAnalysis(appInfo, test1, irCode);
- Value array = null;
- InstructionIterator iterator = irCode.instructionIterator();
- while (iterator.hasNext()) {
- Instruction instruction = iterator.next();
- if (instruction instanceof NewArrayEmpty) {
- array = instruction.outValue();
- break;
- }
+ IRCode irCode = test1.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+ TypeAnalysis analysis = new TypeAnalysis(appInfo, test1, irCode);
+ Value array = null;
+ InstructionIterator iterator = irCode.instructionIterator();
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ if (instruction instanceof NewArrayEmpty) {
+ array = instruction.outValue();
+ break;
}
- assertNotNull(array);
- final Value finalArray = array;
- analysis.forEach((v, l) -> {
- if (v == finalArray) {
- assertTrue(l.isArrayTypeLatticeElement());
- ArrayTypeLatticeElement lattice = l.asArrayTypeLatticeElement();
- assertTrue(lattice.getArrayType().isPrimitiveArrayType());
- assertEquals(1, lattice.getNesting());
- assertFalse(lattice.isNullable());
- }
- });
- } catch (ApiLevelException e) {
- fail(e.getMessage());
}
+ assertNotNull(array);
+ final Value finalArray = array;
+ analysis.forEach((v, l) -> {
+ if (v == finalArray) {
+ assertTrue(l.isArrayTypeLatticeElement());
+ ArrayTypeLatticeElement lattice = l.asArrayTypeLatticeElement();
+ assertTrue(lattice.getArrayType().isPrimitiveArrayType());
+ assertEquals(1, lattice.getNesting());
+ assertFalse(lattice.isNullable());
+ }
+ });
}
// filled-new-array
@@ -195,32 +181,28 @@
inspector.clazz("Test")
.method(new MethodSignature("test4", "int[]", ImmutableList.of()))
.getMethod();
- try {
- IRCode irCode = test4.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
- TypeAnalysis analysis = new TypeAnalysis(appInfo, test4, irCode);
- Value array = null;
- InstructionIterator iterator = irCode.instructionIterator();
- while (iterator.hasNext()) {
- Instruction instruction = iterator.next();
- if (instruction instanceof InvokeNewArray) {
- array = instruction.outValue();
- break;
- }
+ IRCode irCode = test4.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+ TypeAnalysis analysis = new TypeAnalysis(appInfo, test4, irCode);
+ Value array = null;
+ InstructionIterator iterator = irCode.instructionIterator();
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ if (instruction instanceof InvokeNewArray) {
+ array = instruction.outValue();
+ break;
}
- assertNotNull(array);
- final Value finalArray = array;
- analysis.forEach((v, l) -> {
- if (v == finalArray) {
- assertTrue(l.isArrayTypeLatticeElement());
- ArrayTypeLatticeElement lattice = l.asArrayTypeLatticeElement();
- assertTrue(lattice.getArrayType().isPrimitiveArrayType());
- assertEquals(1, lattice.getNesting());
- assertFalse(lattice.isNullable());
- }
- });
- } catch (ApiLevelException e) {
- fail(e.getMessage());
}
+ assertNotNull(array);
+ final Value finalArray = array;
+ analysis.forEach((v, l) -> {
+ if (v == finalArray) {
+ assertTrue(l.isArrayTypeLatticeElement());
+ ArrayTypeLatticeElement lattice = l.asArrayTypeLatticeElement();
+ assertTrue(lattice.getArrayType().isPrimitiveArrayType());
+ assertEquals(1, lattice.getNesting());
+ assertFalse(lattice.isNullable());
+ }
+ });
}
// Make sure the analysis does not hang.
@@ -230,20 +212,16 @@
inspector.clazz("Test")
.method(new MethodSignature("loop2", "void", ImmutableList.of()))
.getMethod();
- try {
- IRCode irCode = loop2.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
- TypeAnalysis analysis = new TypeAnalysis(appInfo, loop2, irCode);
- analysis.forEach((v, l) -> {
- if (l.isClassTypeLatticeElement()) {
- ClassTypeLatticeElement lattice = l.asClassTypeLatticeElement();
- assertEquals("Ljava/io/PrintStream;", lattice.getClassType().toDescriptorString());
- // TODO(b/70795205): Can be refined by using control-flow info.
- assertTrue(l.isNullable());
- }
- });
- } catch (ApiLevelException e) {
- fail(e.getMessage());
- }
+ IRCode irCode = loop2.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+ TypeAnalysis analysis = new TypeAnalysis(appInfo, loop2, irCode);
+ analysis.forEach((v, l) -> {
+ if (l.isClassTypeLatticeElement()) {
+ ClassTypeLatticeElement lattice = l.asClassTypeLatticeElement();
+ assertEquals("Ljava/io/PrintStream;", lattice.getClassType().toDescriptorString());
+ // TODO(b/70795205): Can be refined by using control-flow info.
+ assertTrue(l.isNullable());
+ }
+ });
}
// move-exception
@@ -253,19 +231,15 @@
inspector.clazz("Test")
.method(new MethodSignature("test2_throw", "int", ImmutableList.of()))
.getMethod();
- try {
- IRCode irCode = test2.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
- TypeAnalysis analysis = new TypeAnalysis(appInfo, test2, irCode);
- analysis.forEach((v, l) -> {
- if (l.isClassTypeLatticeElement()) {
- ClassTypeLatticeElement lattice = l.asClassTypeLatticeElement();
- assertEquals("Ljava/lang/Throwable;", lattice.getClassType().toDescriptorString());
- assertFalse(l.isNullable());
- }
- });
- } catch (ApiLevelException e) {
- fail(e.getMessage());
- }
+ IRCode irCode = test2.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+ TypeAnalysis analysis = new TypeAnalysis(appInfo, test2, irCode);
+ analysis.forEach((v, l) -> {
+ if (l.isClassTypeLatticeElement()) {
+ ClassTypeLatticeElement lattice = l.asClassTypeLatticeElement();
+ assertEquals("Ljava/lang/Throwable;", lattice.getClassType().toDescriptorString());
+ assertFalse(l.isNullable());
+ }
+ });
}
// One very complicated example.
@@ -283,13 +257,9 @@
ConstString.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, false),
CheckCast.class, new ClassTypeLatticeElement(test, true),
NewInstance.class, new ClassTypeLatticeElement(test, false));
- try {
- IRCode irCode = method.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
- TypeAnalysis analysis = new TypeAnalysis(appInfo, method, irCode);
- analysis.forEach((v, l) -> verifyTypeEnvironment(expectedLattices, v, l));
- } catch (ApiLevelException e) {
- fail(e.getMessage());
- }
+ IRCode irCode = method.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+ TypeAnalysis analysis = new TypeAnalysis(appInfo, method, irCode);
+ analysis.forEach((v, l) -> verifyTypeEnvironment(expectedLattices, v, l));
}
// One more complicated example.
@@ -305,13 +275,9 @@
ConstString.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, false),
InstanceOf.class, PRIMITIVE,
StaticGet.class, new ClassTypeLatticeElement(test, true));
- try {
- IRCode irCode = method.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
- TypeAnalysis analysis = new TypeAnalysis(appInfo, method, irCode);
- analysis.forEach((v, l) -> verifyTypeEnvironment(expectedLattices, v, l));
- } catch (ApiLevelException e) {
- fail(e.getMessage());
- }
+ IRCode irCode = method.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+ TypeAnalysis analysis = new TypeAnalysis(appInfo, method, irCode);
+ analysis.forEach((v, l) -> verifyTypeEnvironment(expectedLattices, v, l));
}
private static void assertEither(TypeLatticeElement actual, TypeLatticeElement... expected) {
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
index 9c58d25..1f4d2ba 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.utils.DexInspector;
import java.util.Collections;
import org.junit.Assert;
-import org.junit.Ignore;
import org.junit.Test;
public class CovariantReturnTypeAnnotationTransformerTest extends AsmTestBase {
@@ -49,7 +48,6 @@
failsIndependentOfFlag(input);
}
- @Ignore("b/78618808")
@Test
public void testVersion2WithClient1And2() throws Exception {
AndroidApp input =
@@ -63,7 +61,6 @@
succeedsIndependentOfFlag(input, true);
}
- @Ignore("b/78618808")
@Test
public void testVersion2WithClient3() throws Exception {
AndroidApp input =
@@ -108,6 +105,23 @@
succeedsIndependentOfFlag(input, false);
}
+ @Test
+ public void testRepeatedCompilation() throws Exception {
+ AndroidApp input =
+ buildAndroidApp(
+ ToolHelper.getClassAsBytes(Client.class),
+ ToolHelper.getClassAsBytes(A.class),
+ com.android.tools.r8.ir.desugar.annotations.version2.BDump.dump(),
+ com.android.tools.r8.ir.desugar.annotations.version2.CDump.dump());
+
+ AndroidApp output =
+ compileWithD8(input, options -> options.processCovariantReturnTypeAnnotations = true);
+
+ // Compilation will fail with a compilation error the second time if the implementation does
+ // not remove the CovariantReturnType annotations properly during the first compilation.
+ compileWithD8(output, options -> options.processCovariantReturnTypeAnnotations = true);
+ }
+
private void succeedsWithOption(
AndroidApp input, boolean option, boolean checkPresenceOfSyntheticMethods) throws Exception {
AndroidApp output =
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
index b50718d..eb6e943 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -28,6 +28,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -86,6 +87,7 @@
private final List<String> fields = new ArrayList<>();
private boolean makeInit = false;
private boolean hasInit = false;
+ private final List<String> clinit = new ArrayList<>();
private boolean isInterface = false;
private String access = "public";
@@ -217,6 +219,10 @@
return new MethodSignature(name, returnJavaType, argumentJavaTypes);
}
+ public void addClassInitializer(String... lines) {
+ clinit.addAll(Arrays.asList(lines));
+ }
+
public FieldSignature addField(String flags, String name, String type, String value) {
fields.add(
".field " + flags + " " + name + " " + type + (value != null ? (" = " + value) : ""));
@@ -262,6 +268,11 @@
for (String method : methods) {
builder.append(method).append("\n");
}
+ if (!clinit.isEmpty()) {
+ builder.append(".method public static <clinit>()V\n");
+ clinit.forEach(line -> builder.append(line).append('\n'));
+ builder.append(".end method\n");
+ }
return builder.toString();
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index bb2628a..43b4c0f 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -15,8 +15,12 @@
import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -40,6 +44,23 @@
@Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
@Test
+ public void traceMainDexList001_whyareyoukeeping() throws Throwable {
+ PrintStream stdout = System.out;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ doTest(
+ "traceMainDexList001_1",
+ "multidex001",
+ EXAMPLE_BUILD_DIR,
+ Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules-whyareyoukeeping.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
+ AndroidApiLevel.I);
+ String output = new String(baos.toByteArray(), Charset.defaultCharset());
+ Assert.assertTrue(output.contains("is live because referenced in keep rule:"));
+ System.setOut(stdout);
+ }
+
+ @Test
public void traceMainDexList001_1() throws Throwable {
doTest(
"traceMainDexList001_1",
@@ -271,10 +292,9 @@
}
private String mainDexStringToDescriptor(String mainDexString) {
- final String CLASS_EXTENSION = ".class";
- Assert.assertTrue(mainDexString.endsWith(CLASS_EXTENSION));
+ Assert.assertTrue(mainDexString.endsWith(FileUtils.CLASS_EXTENSION));
return DescriptorUtils.getDescriptorFromClassBinaryName(
- mainDexString.substring(0, mainDexString.length() - CLASS_EXTENSION.length()));
+ mainDexString.substring(0, mainDexString.length() - FileUtils.CLASS_EXTENSION.length()));
}
private void checkSameMainDexEntry(String reference, String computed) {
diff --git a/src/test/java/com/android/tools/r8/naming/b80083341/AnotherClass.java b/src/test/java/com/android/tools/r8/naming/b80083341/AnotherClass.java
new file mode 100644
index 0000000..e612fec
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b80083341/AnotherClass.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming.b80083341;
+
+public class AnotherClass {
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b80083341/B80083341.java b/src/test/java/com/android/tools/r8/naming/b80083341/B80083341.java
new file mode 100644
index 0000000..1a1ae50
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b80083341/B80083341.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming.b80083341;
+
+import static com.android.tools.r8.utils.DescriptorUtils.getClassNameFromDescriptor;
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(VmTestRunner.class)
+public class B80083341 extends TestBase {
+
+ @Ignore("b/80083341")
+ @Test
+ public void test() throws Exception {
+ Class mainClass = TestMain.class;
+ List<String> config = ImmutableList.of(
+ "-printmapping",
+ "-keepattributes EnclosingMethod,InnerClasses,Signature",
+ "-repackageclasses",
+ "-keepclassmembers class " + mainClass.getCanonicalName() + " {",
+ " public void main(...);",
+ "}",
+ "-keep,allowobfuscation class " + mainClass.getCanonicalName() + " {",
+ "}"
+ );
+ AndroidApp app = readClassesAndAndriodJar(ImmutableList.of(
+ mainClass, TestClass.class, AnotherClass.class,
+ PackagePrivateClass.class, PackagePrivateClass.Itf.class, PackagePrivateClass.Impl.class
+ ));
+ AndroidApp processedApp = compileWithR8(app, String.join(System.lineSeparator(), config));
+ DexInspector inspector = new DexInspector(processedApp);
+ ClassSubject mainSubject = inspector.clazz(mainClass);
+ assertThat(mainSubject, isPresent());
+
+ ProcessResult artResult =
+ runOnArtRaw(processedApp, getClassNameFromDescriptor(mainSubject.getFinalDescriptor()));
+ assertEquals(0, artResult.exitCode);
+ assertEquals(-1, artResult.stderr.indexOf("IllegalAccessError"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b80083341/PackagePrivateClass.java b/src/test/java/com/android/tools/r8/naming/b80083341/PackagePrivateClass.java
new file mode 100644
index 0000000..62a4181
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b80083341/PackagePrivateClass.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming.b80083341;
+
+final class PackagePrivateClass {
+ private static final boolean flag = false;
+
+ private PackagePrivateClass() {}
+
+ interface Itf<T> {
+ boolean foo();
+ }
+
+ static class Impl<T> implements Itf<T> {
+ private final Object[] objs;
+
+ Impl(int size) {
+ objs = new Object[size];
+ }
+
+ @Override
+ public boolean foo() {
+ return flag;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b80083341/TestClass.java b/src/test/java/com/android/tools/r8/naming/b80083341/TestClass.java
new file mode 100644
index 0000000..a5dc174
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b80083341/TestClass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming.b80083341;
+
+public class TestClass {
+ PackagePrivateClass.Impl<AnotherClass> anotherClasses = new PackagePrivateClass.Impl<>(8);
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b80083341/TestMain.java b/src/test/java/com/android/tools/r8/naming/b80083341/TestMain.java
new file mode 100644
index 0000000..6be514c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b80083341/TestMain.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming.b80083341;
+
+public class TestMain {
+ public static void main(String[] args) {
+ new TestClass().anotherClasses.foo();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
new file mode 100644
index 0000000..9e0429b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(VmTestRunner.class)
+public class FieldTypeTest extends TestBase {
+
+ @Ignore("b/78788577")
+ @Test
+ public void test_brokenTypeHierarchy() throws Exception {
+ JasminBuilder jasminBuilder = new JasminBuilder();
+ // interface Itf
+ ClassBuilder itf = jasminBuilder.addInterface("Itf");
+ MethodSignature foo = itf.addAbstractMethod("foo", ImmutableList.of(), "V");
+ // class Impl /* implements Itf */
+ ClassBuilder impl = jasminBuilder.addClass("Impl");
+ impl.addDefaultConstructor();
+ impl.addVirtualMethod("foo", ImmutableList.of(), "V",
+ ".limit locals 2",
+ ".limit stack 2",
+ "getstatic java/lang/System/out Ljava/io/PrintStream;",
+ "ldc \"" + "foo" + "\"",
+ "invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+ "return");
+ impl.addVirtualMethod("toString", ImmutableList.of(), "Ljava/lang/String;",
+ ".limit locals 1",
+ ".limit stack 2",
+ "ldc \"" + impl.name + "\"",
+ "areturn");
+ ClassBuilder client = jasminBuilder.addClass("Client");
+ FieldSignature obj = client.addStaticFinalField("obj", itf.getDescriptor(), null);
+ client.addClassInitializer(
+ ".limit locals 1",
+ ".limit stack 2",
+ "new " + impl.name,
+ "dup",
+ "invokespecial " + impl.name + "/<init>()V",
+ "putstatic " + client.name + "/" + obj.name + " " + itf.getDescriptor(),
+ "return"
+ );
+
+ ClassBuilder mainClass = jasminBuilder.addClass("Main");
+ mainClass.addMainMethod(
+ ".limit locals 2",
+ ".limit stack 2",
+ "getstatic java/lang/System/out Ljava/io/PrintStream;",
+ "getstatic " + client.name + "/" + obj.name + " " + itf.getDescriptor(),
+ /*
+ "astore_0",
+ "aload_0",
+ // java.lang.IncompatibleClassChangeError:
+ // Class Impl does not implement the requested interface Itf
+ "invokeinterface " + itf.name + "/" + foo.name + "()V 1",
+ "aload_0",
+ */
+ "invokevirtual java/io/PrintStream/print(Ljava/lang/Object;)V",
+ "return"
+ );
+
+ final String mainClassName = mainClass.name;
+ String proguardConfig = keepMainProguardConfiguration(mainClass.name, false, false);
+
+ // Run input program on java.
+ Path outputDirectory = temp.newFolder().toPath();
+ jasminBuilder.writeClassFiles(outputDirectory);
+ ProcessResult javaResult = ToolHelper.runJava(outputDirectory, mainClassName);
+ assertEquals(0, javaResult.exitCode);
+ assertThat(javaResult.stdout, containsString(impl.name));
+
+ AndroidApp processedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
+ // Disable inlining to avoid the (short) tested method from being inlined and then removed.
+ internalOptions -> internalOptions.enableInlining = false);
+
+ // Run processed (output) program on ART
+ ProcessResult artResult = runOnArtRaw(processedApp, mainClassName);
+ assertEquals(0, artResult.exitCode);
+ assertThat(artResult.stdout, containsString(impl.name));
+ assertEquals(-1, artResult.stderr.indexOf("DoFieldPut"));
+
+ DexInspector inspector = new DexInspector(processedApp);
+ ClassSubject itfSubject = inspector.clazz(itf.name);
+ assertThat(itfSubject, isPresent());
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index dd7465f..f558ee7 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -139,6 +139,8 @@
private Reporter reporter;
private KeepingDiagnosticHandler handler;
private ProguardConfigurationParser parser;
+ private List<String> whiteSpace = ImmutableList.of("", " ", " ", "\t", " \t", " \t", " \t ", " \t\t \t ");
+
@Before
public void reset() {
@@ -789,12 +791,10 @@
@Test
public void parseKeepModifiers() {
- List<String> ws = ImmutableList.of("", " ", " ", "\t", " \t", " \t", " \t ", " \t\t \t ");
-
- for (String before : ws) {
- for (String after : ws) {
+ for (String before : whiteSpace) {
+ for (String after : whiteSpace) {
reset();
- ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
+ parseAndVerifyParserEndsCleanly(ImmutableList.of(
"-keep"
+ before + "," + after + "includedescriptorclasses"
+ before + "," + after + "allowshrinking"
@@ -807,6 +807,16 @@
}
@Test
+ public void parseKeepAnnotation() {
+ for (String space : whiteSpace) {
+ reset();
+ parseAndVerifyParserEndsCleanly(ImmutableList.of(
+ "-keep @" + space + "interface A"
+ ));
+ }
+ }
+
+ @Test
public void regress78442725() {
parseAndVerifyParserEndsCleanly(ImmutableList.of(
"-keep, includedescriptorclasses class in.uncod.android.bypass.Document { *; }",
@@ -1717,6 +1727,24 @@
verifyWithProguard(proguardConfig);
}
+ @Test
+ public void parse_regress79925760() throws Exception {
+ Path proguardConfig = writeTextToTempFile(
+ "-keep public @ interface test.MyAnnotation"
+ );
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(new DexItemFactory(), reporter);
+ parser.parse(proguardConfig);
+ verifyParserEndsCleanly();
+
+ ProguardConfiguration config = parser.getConfig();
+ assertEquals(1, config.getRules().size());
+ ProguardKeepRule rule = (ProguardKeepRule) config.getRules().get(0);
+ assertEquals(ProguardClassType.ANNOTATION_INTERFACE, rule.getClassType());
+
+ verifyWithProguard(proguardConfig);
+ }
+
private ProguardConfiguration parseAndVerifyParserEndsCleanly(List<String> config) {
parser.parse(createConfigurationForTesting(config));
verifyParserEndsCleanly();
diff --git a/tests/d8_api_usage_sample.jar b/tests/d8_api_usage_sample.jar
index eb6f10f..f901b9d 100644
--- a/tests/d8_api_usage_sample.jar
+++ b/tests/d8_api_usage_sample.jar
Binary files differ
diff --git a/tests/r8_api_usage_sample.jar b/tests/r8_api_usage_sample.jar
index d838141..f901b9d 100644
--- a/tests/r8_api_usage_sample.jar
+++ b/tests/r8_api_usage_sample.jar
Binary files differ
diff --git a/tools/minify_tool.py b/tools/minify_tool.py
index 08b2ada..787c1e1 100755
--- a/tools/minify_tool.py
+++ b/tools/minify_tool.py
@@ -9,6 +9,9 @@
Given an input JAR (default: r8.jar) and a main-class, generates a new input JAR
with the given main-class in the manifest along with a -keep rule for keeping
just the main entrypoint, and runs R8 in release+classfile mode on the JAR.
+
+If --benchmark-name NAME is given, prints "<NAME>(RunTimeRaw): <elapsed> ms"
+to standard output at the end of the run.
'''
import argparse
@@ -44,8 +47,8 @@
'-O', '--no-debug', dest='debug', action='store_false',
help='Disable assertions when running R8')
parser.add_argument(
- '--print-runtimeraw', metavar='BENCHMARKNAME',
- help='Print "<BENCHMARKNAME>(RunTimeRaw): <elapsed> ms" at the end')
+ '--benchmark-name',
+ help='Print benchmarks with the given name')
def generate_output_name(input_jar, mainclass):
if not mainclass:
@@ -83,7 +86,7 @@
return mo.group(1)
def minify_tool(mainclass=None, input_jar=utils.R8_JAR, output_jar=None, lib=RT,
- debug=True, build=True, print_runtimeraw=None):
+ debug=True, build=True, benchmark_name=None):
if output_jar is None:
output_jar = generate_output_name(input_jar, mainclass)
with utils.TempDir() as path:
@@ -104,9 +107,9 @@
tmp_input_path)
start_time = time.time()
return_code = toolhelper.run('r8', args, debug=debug, build=build)
- if print_runtimeraw:
+ if benchmark_name:
elapsed_ms = 1000 * (time.time() - start_time)
- print('%s-Total(RunTimeRaw): %s ms' % (print_runtimeraw, elapsed_ms))
+ print('%s(RunTimeRaw): %s ms' % (benchmark_name, elapsed_ms))
return return_code
if __name__ == '__main__':
diff --git a/tools/run_bootstrap_benchmark.py b/tools/run_bootstrap_benchmark.py
index 0c608f9..2ae97b2 100755
--- a/tools/run_bootstrap_benchmark.py
+++ b/tools/run_bootstrap_benchmark.py
@@ -14,8 +14,8 @@
parser = argparse.ArgumentParser()
parser.add_argument(
- '--print-runtimeraw', metavar='BENCHMARKNAME',
- help='Print "<BENCHMARKNAME>(RunTimeRaw): <elapsed> ms" at the end')
+ '--name', metavar='NAME', dest='benchmark_name',
+ help='Print "<NAME>(RunTimeRaw): <elapsed> ms" at the end')
if __name__ == '__main__':