diff options
author | Søren Gjesse <sgjesse@google.com> | 2017-10-26 15:31:18 +0200 |
---|---|---|
committer | Søren Gjesse <sgjesse@google.com> | 2017-10-26 13:37:51 +0000 |
commit | f0b4740f2f7296d09e8b73d841ced21a27b8e7c6 (patch) | |
tree | ec32a1843590e97f640cc06b9ce7d474642b9409 | |
parent | 6a6117560d9f9bf6c0c82c1205f539d22adf7f8d (diff) | |
parent | 4d22d2d18bab390754a423972ffc2a7b47fbde79 (diff) | |
download | r8-f0b4740f2f7296d09e8b73d841ced21a27b8e7c6.tar.gz |
Update external/r8 to 4d22d2d1
Merge remote-tracking branch 'aosp/upstream-mirror' into merge-r8-4d22d2d1
Test: m -j USE_D8=true USE_R8=true tests
Change-Id: I0fdbb52222288b79254479882e2dbadb9051d9d2
70 files changed, 826 insertions, 517 deletions
diff --git a/build.gradle b/build.gradle index 9299592c7..c97aa9dfb 100644 --- a/build.gradle +++ b/build.gradle @@ -23,6 +23,7 @@ def errorProneConfiguration = [ '-Xep:OvershadowingSubclassFields:WARN', '-Xep:IntLongMath:WARN', '-Xep:EqualsHashCode:WARN', + '-Xep:InconsistentOverloads:WARN', '-Xep:ArrayHashCode:WARN', '-Xep:EqualsIncompatibleType:WARN', '-Xep:NonOverridingEquals:WARN', diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java index 5f438492d..6961024fd 100644 --- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java +++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java @@ -81,6 +81,8 @@ abstract class BaseCompilerCommand extends BaseCommand { if (outputPath == null) { return new IgnoreContentsOutputSink(); } else { + // TODO(zerny): Calling getInternalOptions here is incorrect since any modifications by an + // options consumer will not be visible to the sink. return FileSystemOutputSink.create(outputPath, getInternalOptions()); } } @@ -107,19 +109,19 @@ abstract class BaseCompilerCommand extends BaseCommand { private boolean enableDesugaring = true; protected Builder(CompilationMode mode) { - this(AndroidApp.builder(), mode, false); + this(mode, false, AndroidApp.builder()); } protected Builder(CompilationMode mode, boolean ignoreDexInArchive) { - this(AndroidApp.builder(), mode, ignoreDexInArchive); + this(mode, ignoreDexInArchive, AndroidApp.builder()); } // Internal constructor for testing. - Builder(AndroidApp app, CompilationMode mode) { - this(AndroidApp.builder(app), mode, false); + Builder(CompilationMode mode, AndroidApp app) { + this(mode, false, AndroidApp.builder(app)); } - private Builder(AndroidApp.Builder builder, CompilationMode mode, boolean ignoreDexInArchive) { + private Builder(CompilationMode mode, boolean ignoreDexInArchive, AndroidApp.Builder builder) { super(builder, ignoreDexInArchive); assert mode != null; this.mode = mode; diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java index a5213c5b7..ae370ae38 100644 --- a/src/main/java/com/android/tools/r8/D8.java +++ b/src/main/java/com/android/tools/r8/D8.java @@ -199,7 +199,7 @@ public final class D8 { throws IOException, ExecutionException, ApiLevelException { final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null; - IRConverter converter = new IRConverter(timing, appInfo, options, printer); + IRConverter converter = new IRConverter(appInfo, options, timing, printer); application = converter.convertToDex(application, executor); if (options.printCfg) { diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java index b96dfb73d..d0c99d503 100644 --- a/src/main/java/com/android/tools/r8/D8Command.java +++ b/src/main/java/com/android/tools/r8/D8Command.java @@ -44,7 +44,7 @@ public class D8Command extends BaseCompilerCommand { } private Builder(AndroidApp app) { - super(app, CompilationMode.DEBUG); + super(CompilationMode.DEBUG, app); } /** Add classpath file resources. */ diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java index 9d7478d59..d29d9ae19 100644 --- a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java +++ b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java @@ -68,7 +68,7 @@ public class GenerateMainDexListCommand extends BaseCommand { * Add proguard configuration for automatic main dex list calculation. */ public GenerateMainDexListCommand.Builder addMainDexRules(List<String> lines) { - mainDexRules.add(new ProguardConfigurationSourceStrings(Paths.get("."), lines)); + mainDexRules.add(new ProguardConfigurationSourceStrings(lines, Paths.get("."))); return self(); } diff --git a/src/main/java/com/android/tools/r8/OutputSink.java b/src/main/java/com/android/tools/r8/OutputSink.java index 8ffb792a1..a73391ae2 100644 --- a/src/main/java/com/android/tools/r8/OutputSink.java +++ b/src/main/java/com/android/tools/r8/OutputSink.java @@ -32,7 +32,8 @@ public interface OutputSink { * gives the current file count. * <p> * Files are not necessarily generated in order and files might be written concurrently. However, - * for each fileId only one file is ever written. + * for each fileId only one file is ever written. If this method is called, the other writeDexFile + * and writeClassFile methods will not be called. */ void writeDexFile(byte[] contents, Set<String> classDescriptors, int fileId) throws IOException; @@ -46,13 +47,28 @@ public interface OutputSink { * primaryClassName only one file is ever written. * <p> * This method is only invoked by D8 and only if compiling each class into its own dex file, e.g., - * for incremental compilation. If this method is called, the other writeDexFile method will - * not be called. + * for incremental compilation. If this method is called, the other writeDexFile and + * writeClassFile methods will not be called. */ void writeDexFile(byte[] contents, Set<String> classDescriptors, String primaryClassName) throws IOException; /** + * Write a Java classfile that contains the class primaryClassName and its companion classes. + * <p> + * This is equivalent to writing out the file com/foo/bar/Test.class given a primaryClassName of + * com.foo.bar.Test. + * <p> + * There is no guaranteed order and files might be written concurrently. However, for each + * primaryClassName only one file is ever written. + * <p> + * This method is only invoked by R8 and only if compiling to Java bytecode. If this method is + * called, the other writeDexFile and writeClassFile methods will not be called. + */ + void writeClassFile(byte[] contents, Set<String> classDescriptors, String primaryClassName) + throws IOException; + + /** * Provides the raw bytes that would be generated for the <code>-printusage</code> flag. * <p> * This method is only invoked by R8 and only if R8 is instructed to generate printusage diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java index 5255fc5d6..c9ede5106 100644 --- a/src/main/java/com/android/tools/r8/R8.java +++ b/src/main/java/com/android/tools/r8/R8.java @@ -19,6 +19,7 @@ import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.conversion.IRConverter; import com.android.tools.r8.ir.optimize.EnumOrdinalMapCollector; import com.android.tools.r8.ir.optimize.SwitchMapCollector; +import com.android.tools.r8.jar.CfApplicationWriter; import com.android.tools.r8.naming.Minifier; import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.naming.SourceFileRewriter; @@ -92,11 +93,14 @@ public class R8 { throws ExecutionException, DexOverflowException { try { Marker marker = getMarker(options); - new ApplicationWriter( - application, options, marker, deadCode, namingLens, proguardSeedsData) - .write(outputSink, executorService); + if (options.outputClassFiles) { + new CfApplicationWriter(application, options).write(outputSink, executorService); + } else { + new ApplicationWriter(application, options, marker, deadCode, namingLens, proguardSeedsData) + .write(outputSink, executorService); + } } catch (IOException e) { - throw new RuntimeException("Cannot write dex application", e); + throw new RuntimeException("Cannot write application", e); } } @@ -125,7 +129,7 @@ public class R8 { timing.begin("Create IR"); try { IRConverter converter = new IRConverter( - timing, appInfo, options, printer, graphLense); + appInfo, options, timing, printer, graphLense); application = converter.optimize(application, executorService); } finally { timing.end(); diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java index 1df56dbe7..fef695eb8 100644 --- a/src/main/java/com/android/tools/r8/R8Command.java +++ b/src/main/java/com/android/tools/r8/R8Command.java @@ -35,6 +35,7 @@ public class R8Command extends BaseCompilerCommand { private Optional<Boolean> treeShaking = Optional.empty(); private Optional<Boolean> discardedChecker = Optional.empty(); private Optional<Boolean> minification = Optional.empty(); + private boolean ignoreMissingClassesWhenNotShrinking = false; private boolean ignoreMissingClasses = false; private boolean forceProguardCompatibility = false; private Path proguardMapOutput = null; @@ -43,13 +44,16 @@ public class R8Command extends BaseCompilerCommand { super(CompilationMode.RELEASE); } - protected Builder(boolean ignoreDexInArchive, boolean forceProguardCompatibility) { + protected Builder(boolean ignoreDexInArchive, boolean forceProguardCompatibility, + boolean ignoreMissingClassesWhenNotShrinking, boolean ignoreMissingClasses) { super(CompilationMode.RELEASE, ignoreDexInArchive); this.forceProguardCompatibility = forceProguardCompatibility; + this.ignoreMissingClassesWhenNotShrinking = ignoreMissingClassesWhenNotShrinking; + this.ignoreMissingClasses = ignoreMissingClasses; } private Builder(AndroidApp app) { - super(app, CompilationMode.RELEASE); + super(CompilationMode.RELEASE, app); } @Override @@ -105,7 +109,7 @@ public class R8Command extends BaseCompilerCommand { * Add proguard configuration for automatic main dex list calculation. */ public Builder addMainDexRules(List<String> lines) { - mainDexRules.add(new ProguardConfigurationSourceStrings(Paths.get("."), lines)); + mainDexRules.add(new ProguardConfigurationSourceStrings(lines, Paths.get("."))); return self(); } @@ -138,7 +142,7 @@ public class R8Command extends BaseCompilerCommand { * Add proguard configuration. */ public Builder addProguardConfiguration(List<String> lines) { - proguardConfigs.add(new ProguardConfigurationSourceStrings(Paths.get("."), lines)); + proguardConfigs.add(new ProguardConfigurationSourceStrings(lines, Paths.get("."))); return self(); } @@ -256,6 +260,7 @@ public class R8Command extends BaseCompilerCommand { useMinification, ignoreMissingClasses, forceProguardCompatibility, + ignoreMissingClassesWhenNotShrinking, proguardMapOutput); } } @@ -297,6 +302,7 @@ public class R8Command extends BaseCompilerCommand { private final boolean useMinification; private final boolean ignoreMissingClasses; private final boolean forceProguardCompatibility; + private final boolean ignoreMissingClassesWhenNotShrinking; private final Path proguardMapOutput; public static Builder builder() { @@ -415,6 +421,7 @@ public class R8Command extends BaseCompilerCommand { boolean useMinification, boolean ignoreMissingClasses, boolean forceProguardCompatibility, + boolean ignoreMissingClassesWhenNotShrinking, Path proguardMapOutput) { super(inputApp, outputPath, outputMode, mode, minApiLevel, diagnosticsHandler, enableDesugaring); @@ -429,6 +436,7 @@ public class R8Command extends BaseCompilerCommand { this.useMinification = useMinification; this.ignoreMissingClasses = ignoreMissingClasses; this.forceProguardCompatibility = forceProguardCompatibility; + this.ignoreMissingClassesWhenNotShrinking = ignoreMissingClassesWhenNotShrinking; this.proguardMapOutput = proguardMapOutput; } @@ -442,6 +450,7 @@ public class R8Command extends BaseCompilerCommand { useMinification = false; ignoreMissingClasses = false; forceProguardCompatibility = false; + ignoreMissingClassesWhenNotShrinking = false; proguardMapOutput = null; } public boolean useTreeShaking() { @@ -479,6 +488,9 @@ public class R8Command extends BaseCompilerCommand { assert !internal.ignoreMissingClasses; internal.ignoreMissingClasses = ignoreMissingClasses; internal.ignoreMissingClasses |= proguardConfiguration.isIgnoreWarnings(); + internal.ignoreMissingClasses |= + ignoreMissingClassesWhenNotShrinking && !proguardConfiguration.isShrinking(); + assert !internal.verbose; internal.mainDexKeepRules = mainDexKeepRules; internal.minimalMainDex = internal.debug; diff --git a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java index 2120714ba..e904e53e9 100644 --- a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java +++ b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java @@ -22,6 +22,7 @@ import com.android.tools.r8.utils.ZipUtils; import com.google.common.collect.ImmutableMap; import com.google.common.io.ByteStreams; import java.io.IOException; +import java.io.InputStream; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -174,7 +175,9 @@ public class FrameworkIncrementalDexingBenchmark { throws IOException, CompilationException { List<byte[]> bytes = new ArrayList<>(outputs.size()); for (Resource input : outputs.values()) { - bytes.add(ByteStreams.toByteArray(input.getStream())); + try (InputStream inputStream = input.getStream()) { + bytes.add(ByteStreams.toByteArray(inputStream)); + } } long start = System.nanoTime(); D8Output out = diff --git a/src/main/java/com/android/tools/r8/code/Format45cc.java b/src/main/java/com/android/tools/r8/code/Format45cc.java index c269fad73..f5123cf70 100644 --- a/src/main/java/com/android/tools/r8/code/Format45cc.java +++ b/src/main/java/com/android/tools/r8/code/Format45cc.java @@ -117,13 +117,13 @@ public abstract class Format45cc extends Base4Format { StringBuilder builder = new StringBuilder(); appendRegisterArguments(builder, " "); builder.append(" "); - builder.append(toString(BBBB, naming)); + builder.append(itemToString(BBBB, naming)); builder.append(", "); - builder.append(toString(HHHH, naming)); + builder.append(itemToString(HHHH, naming)); return formatString(builder.toString()); } - private String toString(IndexedDexItem indexedDexItem, ClassNameMapper naming) { + private String itemToString(IndexedDexItem indexedDexItem, ClassNameMapper naming) { String str; if (naming == null) { str = indexedDexItem.toSmaliString(); diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java index 4c49a6a1b..a6b45ae17 100644 --- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java +++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java @@ -33,14 +33,16 @@ public class CompatProguard { public final String output; public final int minApi; public final boolean forceProguardCompatibility; + public final boolean ignoreMissingClasses; public final boolean multiDex; public final List<String> proguardConfig; CompatProguardOptions(List<String> proguardConfig, String output, int minApi, - boolean multiDex, boolean forceProguardCompatibility) { + boolean multiDex, boolean forceProguardCompatibility, boolean ignoreMissingClasses) { this.output = output; this.minApi = minApi; this.forceProguardCompatibility = forceProguardCompatibility; + this.ignoreMissingClasses = ignoreMissingClasses; this.multiDex = multiDex; this.proguardConfig = proguardConfig; } @@ -49,6 +51,7 @@ public class CompatProguard { String output = null; int minApi = 1; boolean forceProguardCompatibility = false; + boolean ignoreMissingClasses = false; boolean multiDex = false; boolean coreLibrary = false; @@ -62,6 +65,8 @@ public class CompatProguard { minApi = Integer.valueOf(args[++i]); } else if (arg.equals("--force-proguard-compatibility")) { forceProguardCompatibility = true; + } else if (arg.equals("--ignore-missing-classes")) { + ignoreMissingClasses = true; } else if (arg.equals("--output")) { output = args[++i]; } else if (arg.equals("--multi-dex")) { @@ -82,7 +87,7 @@ public class CompatProguard { builder.add(currentLine.toString()); } return new CompatProguardOptions(builder.build(), output, minApi, multiDex, - forceProguardCompatibility); + forceProguardCompatibility, ignoreMissingClasses); } } @@ -95,7 +100,8 @@ public class CompatProguard { // Run R8 passing all the options from the command line as a Proguard configuration. CompatProguardOptions options = CompatProguardOptions.parse(args); R8Command.Builder builder = - new CompatProguardCommandBuilder(options.forceProguardCompatibility); + new CompatProguardCommandBuilder( + options.forceProguardCompatibility, options.ignoreMissingClasses); builder.setOutputPath(Paths.get(options.output)) .addProguardConfiguration(options.proguardConfig) .setMinApiLevel(options.minApi); diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java index 71bc6eb0d..18ec474ac 100644 --- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java +++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java @@ -7,8 +7,9 @@ package com.android.tools.r8.compatproguard; import com.android.tools.r8.R8Command; public class CompatProguardCommandBuilder extends R8Command.Builder { - public CompatProguardCommandBuilder(boolean forceProguardCompatibility) { - super(true, forceProguardCompatibility); + public CompatProguardCommandBuilder(boolean forceProguardCompatibility, + boolean ignoreMissingClasses) { + super(true, forceProguardCompatibility, true, ignoreMissingClasses); setEnableDesugaring(false); } } diff --git a/src/main/java/com/android/tools/r8/dex/BaseFile.java b/src/main/java/com/android/tools/r8/dex/BaseFile.java index dd9b325c9..86143c84a 100644 --- a/src/main/java/com/android/tools/r8/dex/BaseFile.java +++ b/src/main/java/com/android/tools/r8/dex/BaseFile.java @@ -18,7 +18,9 @@ public abstract class BaseFile { protected final ByteBuffer buffer; protected BaseFile(Resource resource) throws IOException { - buffer = ByteBuffer.wrap(ByteStreams.toByteArray(resource.getStream())); + try (InputStream input = resource.getStream()) { + buffer = ByteBuffer.wrap(ByteStreams.toByteArray(input)); + } } protected BaseFile(String name) throws IOException { diff --git a/src/main/java/com/android/tools/r8/dex/DexFileReader.java b/src/main/java/com/android/tools/r8/dex/DexFileReader.java index 770afdf06..64909cc3a 100644 --- a/src/main/java/com/android/tools/r8/dex/DexFileReader.java +++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java @@ -983,7 +983,7 @@ public class DexFileReader { DexString shorty = indexedItems.getString(shortyIndex); DexType returnType = indexedItems.getType(returnTypeIndex); DexTypeList parameters = typeListAt(parametersOffsetIndex); - return dexItemFactory.createProto(shorty, returnType, parameters); + return dexItemFactory.createProto(returnType, shorty, parameters); } private DexMethod methodAt(int index) { diff --git a/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java b/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java index b51c7ecae..a5080d39a 100644 --- a/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java +++ b/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java @@ -14,8 +14,8 @@ public class InternalCompilerError extends IllegalStateException { public InternalCompilerError() { } - public InternalCompilerError(String s) { - super(s); + public InternalCompilerError(String message) { + super(message); } public InternalCompilerError(String message, Throwable cause) { 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 6a2fcbf51..a05299c77 100644 --- a/src/main/java/com/android/tools/r8/graph/DexCode.java +++ b/src/main/java/com/android/tools/r8/graph/DexCode.java @@ -168,11 +168,10 @@ public class DexCode extends Code { public IRCode buildIR( DexEncodedMethod encodedMethod, - ValueNumberGenerator valueNumberGenerator, - InternalOptions options) + InternalOptions options, ValueNumberGenerator valueNumberGenerator) throws ApiLevelException { DexSourceCode source = new DexSourceCode(this, encodedMethod); - IRBuilder builder = new IRBuilder(encodedMethod, source, valueNumberGenerator, options); + IRBuilder builder = new IRBuilder(encodedMethod, source, options, valueNumberGenerator); return builder.build(); } 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 99325f908..19743978f 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java @@ -265,11 +265,11 @@ public class DexEncodedMethod extends KeyedDexItem<DexMethod> { return code == null ? null : code.buildIR(this, options); } - public IRCode buildIR(ValueNumberGenerator valueNumberGenerator, InternalOptions options) + public IRCode buildIR(InternalOptions options, ValueNumberGenerator valueNumberGenerator) throws ApiLevelException { return code == null ? null - : code.asDexCode().buildIR(this, valueNumberGenerator, options); + : code.asDexCode().buildIR(this, options, valueNumberGenerator); } public void setCode( 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 153c2e651..a7d44ba31 100644 --- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java +++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java @@ -346,23 +346,19 @@ public class DexItemFactory { return createField(clazz, type, createString(name)); } - public DexProto createProto(DexString shorty, DexType returnType, DexTypeList parameters) { + public DexProto createProto(DexType returnType, DexString shorty, DexTypeList parameters) { assert !sorted; DexProto proto = new DexProto(shorty, returnType, parameters); return canonicalize(protos, proto); } - public DexProto createProto(DexString shorty, DexType returnType, DexType[] parameters) { + public DexProto createProto(DexType returnType, DexType... parameters) { assert !sorted; - return createProto(shorty, returnType, + return createProto(returnType, createShorty(returnType, parameters), parameters.length == 0 ? DexTypeList.empty() : new DexTypeList(parameters)); } - public DexProto createProto(DexType returnType, DexType... parameters) { - return createProto(createShorty(returnType, parameters), returnType, parameters); - } - - public DexString createShorty(DexType returnType, DexType[] argumentTypes) { + private DexString createShorty(DexType returnType, DexType[] argumentTypes) { StringBuilder shortyBuilder = new StringBuilder(); shortyBuilder.append(returnType.toShorty()); for (DexType argumentType : argumentTypes) { @@ -407,7 +403,7 @@ public class DexItemFactory { for (int i = 0; i < parameterDescriptors.length; i++) { parameterTypes[i] = createType(parameterDescriptors[i]); } - DexProto proto = createProto(shorty(returnType, parameterTypes), returnType, parameterTypes); + DexProto proto = createProto(returnType, parameterTypes); return createMethod(clazz, proto, name); } @@ -464,20 +460,6 @@ public class DexItemFactory { return method.name == classConstructorMethodName; } - private DexString shorty(DexType returnType, DexType[] parameters) { - StringBuilder builder = new StringBuilder(); - addToShorty(builder, returnType); - for (DexType parameter : parameters) { - addToShorty(builder, parameter); - } - return createString(builder.toString()); - } - - private void addToShorty(StringBuilder builder, DexType type) { - char first = type.toDescriptorString().charAt(0); - builder.append(first == '[' ? 'L' : first); - } - private static <S extends PresortedComparable<S>> void assignSortedIndices(Collection<S> items, NamingLens namingLens) { List<S> sorted = new ArrayList<>(items); diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java index b64053ae0..ad1af107f 100644 --- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java +++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java @@ -25,6 +25,7 @@ public class DexProgramClass extends DexClass implements Supplier<DexProgramClas private final ProgramResource.Kind originKind; private DexEncodedArray staticValues = SENTINEL_NOT_YET_COMPUTED; private final Collection<DexProgramClass> synthesizedFrom; + private int classFileVersion = -1; public DexProgramClass( DexType type, @@ -296,4 +297,13 @@ public class DexProgramClass extends DexClass implements Supplier<DexProgramClas public DexProgramClass get() { return this; } + + public void setClassFileVersion(int classFileVersion) { + this.classFileVersion = classFileVersion; + } + + public int getClassFileVersion() { + assert classFileVersion != -1; + return classFileVersion; + } } diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java index 3abe7c5bb..7d7d366ec 100644 --- a/src/main/java/com/android/tools/r8/graph/GraphLense.java +++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java @@ -44,10 +44,10 @@ public abstract class GraphLense { } public GraphLense build(DexItemFactory dexItemFactory) { - return build(new IdentityGraphLense(), dexItemFactory); + return build(dexItemFactory, new IdentityGraphLense()); } - public GraphLense build(GraphLense previousLense, DexItemFactory dexItemFactory) { + public GraphLense build(DexItemFactory dexItemFactory, GraphLense previousLense) { return new NestedGraphLense(typeMap, methodMap, fieldMap, previousLense, dexItemFactory); } @@ -118,7 +118,7 @@ public abstract class GraphLense { @Override public DexType lookupType(DexType type, DexEncodedMethod context) { if (type.isArrayType()) { - synchronized(this) { + synchronized (this) { // This block need to be synchronized due to arrayTypeCache. DexType result = arrayTypeCache.get(type); if (result == null) { diff --git a/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java index 757ae71fb..6bf1a1772 100644 --- a/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java +++ b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java @@ -18,6 +18,7 @@ import org.objectweb.asm.Type; * It does not currently support multithreaded reading. */ public class JarApplicationReader { + public final InternalOptions options; ConcurrentHashMap<String, DexString> stringCache = new ConcurrentHashMap<>(); @@ -111,8 +112,8 @@ public class JarApplicationReader { argumentDescriptors[i] = arguments[i].getDescriptor(); } DexProto proto = options.itemFactory.createProto( - getString(shortyDescriptor.toString()), getTypeFromDescriptor(returnType.getDescriptor()), + getString(shortyDescriptor.toString()), getTypeListFromDescriptors(argumentDescriptors)); return proto; } diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java index 001480fcb..9742df7d3 100644 --- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java +++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java @@ -101,6 +101,7 @@ public class JarClassFileReader { private final ReparseContext context = new ReparseContext(); // DexClass data. + private int version; private DexType type; private DexAccessFlags accessFlags; private DexType superType; @@ -174,6 +175,7 @@ public class JarClassFileReader { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + this.version = version; accessFlags = createAccessFlags(access); // Unset the (in dex) non-existent ACC_SUPER flag on the class. assert Constants.ACC_SYNCHRONIZED == Opcodes.ACC_SUPER; @@ -263,6 +265,9 @@ public class JarClassFileReader { if (classKind == ClassKind.PROGRAM) { context.owner = clazz.asProgramClass(); } + if (clazz.isProgramClass()) { + clazz.asProgramClass().setClassFileVersion(version); + } classConsumer.accept(clazz); } 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 5988ae5ce..44bc52926 100644 --- a/src/main/java/com/android/tools/r8/graph/JarCode.java +++ b/src/main/java/com/android/tools/r8/graph/JarCode.java @@ -27,6 +27,11 @@ import org.objectweb.asm.util.TraceMethodVisitor; public class JarCode extends Code { + // TODO(zerny): Write via the IR. + public void writeTo(MethodVisitor visitor) { + node.accept(visitor); + } + public static class ReparseContext { // This will hold the content of the whole class. Once all the methods of the class are swapped @@ -89,34 +94,34 @@ public class JarCode extends Code { throws ApiLevelException { triggerDelayedParsingIfNeccessary(); return options.debug - ? internalBuildWithLocals(encodedMethod, null, options) - : internalBuild(encodedMethod, null, options); + ? internalBuildWithLocals(encodedMethod, options, null) + : internalBuild(encodedMethod, options, null); } public IRCode buildIR( - DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options) + DexEncodedMethod encodedMethod, InternalOptions options, ValueNumberGenerator generator) throws ApiLevelException { assert generator != null; triggerDelayedParsingIfNeccessary(); return options.debug - ? internalBuildWithLocals(encodedMethod, generator, options) - : internalBuild(encodedMethod, generator, options); + ? internalBuildWithLocals(encodedMethod, options, generator) + : internalBuild(encodedMethod, options, generator); } private IRCode internalBuildWithLocals( - DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options) + DexEncodedMethod encodedMethod, InternalOptions options, ValueNumberGenerator generator) throws ApiLevelException { try { - return internalBuild(encodedMethod, generator, options); + return internalBuild(encodedMethod, options, generator); } catch (InvalidDebugInfoException e) { options.warningInvalidDebugInfo(encodedMethod, origin, e); node.localVariables.clear(); - return internalBuild(encodedMethod, generator, options); + return internalBuild(encodedMethod, options, generator); } } private IRCode internalBuild( - DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options) + DexEncodedMethod encodedMethod, InternalOptions options, ValueNumberGenerator generator) throws ApiLevelException { if (!options.debug) { node.localVariables.clear(); @@ -125,7 +130,7 @@ public class JarCode extends Code { IRBuilder builder = (generator == null) ? new IRBuilder(encodedMethod, source, options) - : new IRBuilder(encodedMethod, source, generator, options); + : new IRBuilder(encodedMethod, source, options, generator); return builder.build(); } diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java index bfbfb02c6..37b311962 100644 --- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java +++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java @@ -959,10 +959,10 @@ public class BasicBlock { * <p>The constructed basic block has no predecessors and has one * successors which is the target block. * - * @param target the target of the goto block * @param blockNumber the block number of the goto block + * @param target the target of the goto block */ - public static BasicBlock createGotoBlock(BasicBlock target, int blockNumber) { + public static BasicBlock createGotoBlock(int blockNumber, BasicBlock target) { BasicBlock block = createGotoBlock(blockNumber); block.getSuccessors().add(target); return block; diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java index ff9e25732..7bf8ec102 100644 --- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java +++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java @@ -219,7 +219,7 @@ public class BasicBlockInstructionIterator implements InstructionIterator, Instr } @Override - public BasicBlock split(int instructions, IRCode code, ListIterator<BasicBlock> blocksIterator) { + public BasicBlock split(IRCode code, int instructions, ListIterator<BasicBlock> blocksIterator) { // Split at the current cursor position. BasicBlock newBlock = split(code, blocksIterator); assert blocksIterator == null || IteratorUtils.peekPrevious(blocksIterator) == newBlock; @@ -341,7 +341,7 @@ public class BasicBlockInstructionIterator implements InstructionIterator, Instr List<BasicBlock> blocksToRemove, DexType downcast) { assert blocksToRemove != null; boolean inlineeCanThrow = canThrow(inlinee); - BasicBlock invokeBlock = split(1, code, blocksIterator); + BasicBlock invokeBlock = split(code, 1, blocksIterator); assert invokeBlock.getInstructions().size() == 2; assert invokeBlock.getInstructions().getFirst().isInvoke(); diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java index 5e0fe8ebf..e5fb6edfb 100644 --- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java +++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java @@ -74,7 +74,7 @@ public class IRCode { // correct predecessor and successor structure. It is inserted // at the end of the list of blocks disregarding branching // structure. - BasicBlock newBlock = BasicBlock.createGotoBlock(block, nextBlockNumber++); + BasicBlock newBlock = BasicBlock.createGotoBlock(nextBlockNumber++, block); newBlocks.add(newBlock); pred.replaceSuccessor(block, newBlock); newBlock.getPredecessors().add(pred); @@ -108,7 +108,7 @@ public class IRCode { fallthrough = fallthrough.exit().fallthroughBlock(); } if (fallthrough != null) { - BasicBlock newFallthrough = BasicBlock.createGotoBlock(fallthrough, nextBlockNumber++); + BasicBlock newFallthrough = BasicBlock.createGotoBlock(nextBlockNumber++, fallthrough); current.exit().setFallthroughBlock(newFallthrough); newFallthrough.getPredecessors().add(current); fallthrough.replacePredecessor(current, newFallthrough); diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java index fc7817b57..603ce6ecb 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java +++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java @@ -126,8 +126,8 @@ public interface InstructionListIterator extends ListIterator<Instruction>, * If the current block have catch handlers these catch handlers will be attached to the block * containing the throwing instruction after the split. * - * @param instructions the number of instructions to include in the second block. * @param code the IR code for the block this iterator originates from. + * @param instructions the number of instructions to include in the second block. * @param blockIterator basic block iterator used to iterate the blocks. This must be positioned * just after the block for this is the instruction iterator. After this method returns it will be * positioned just after the second block inserted. Calling {@link #remove} without further @@ -135,13 +135,13 @@ public interface InstructionListIterator extends ListIterator<Instruction>, * @return Returns the new block with the instructions after the cursor. */ // TODO(sgjesse): Refactor to avoid the need for passing code and blockIterator. - BasicBlock split(int instructions, IRCode code, ListIterator<BasicBlock> blockIterator); + BasicBlock split(IRCode code, int instructions, ListIterator<BasicBlock> blockIterator); /** - * See {@link #split(int, IRCode, ListIterator)}. + * See {@link #split(IRCode, int, ListIterator)}. */ - default BasicBlock split(int instructions, IRCode code) { - return split(instructions, code, null); + default BasicBlock split(IRCode code, int instructions) { + return split(code, instructions, null); } /** diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java index 78c5c4e45..dc6d50067 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Phi.java +++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java @@ -70,7 +70,7 @@ public class Phi extends Value { for (BasicBlock pred : block.getPredecessors()) { EdgeType edgeType = pred.getEdgeType(block); // Since this read has been delayed we must provide the local info for the value. - Value operand = builder.readRegister(register, pred, edgeType, type, getLocalInfo()); + Value operand = builder.readRegister(register, type, pred, edgeType, getLocalInfo()); canBeNull |= operand.canBeNull(); appendOperand(operand); } diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java index 1915155fb..ea43cf88c 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Value.java +++ b/src/main/java/com/android/tools/r8/ir/code/Value.java @@ -593,10 +593,10 @@ public class Value { public boolean isDead(InternalOptions options) { // Totally unused values are trivially dead. - return !isUsed() || isDead(new HashSet<>(), options); + return !isUsed() || isDead(options, new HashSet<>()); } - protected boolean isDead(Set<Value> active, InternalOptions options) { + protected boolean isDead(InternalOptions options, Set<Value> active) { // If the value has debug users we cannot eliminate it since it represents a value in a local // variable that should be visible in the debugger. if (numberOfDebugUsers() != 0) { @@ -613,12 +613,12 @@ public class Value { // Instructions with no out value cannot be dead code by the current definition // (unused out value). They typically side-effect input values or deals with control-flow. assert outValue != null; - if (!active.contains(outValue) && !outValue.isDead(active, options)) { + if (!active.contains(outValue) && !outValue.isDead(options, active)) { return false; } } for (Phi phi : uniquePhiUsers()) { - if (!active.contains(phi) && !phi.isDead(active, options)) { + if (!active.contains(phi) && !phi.isDead(options, active)) { return false; } } diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java index 042232b82..2335f2b46 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java @@ -386,7 +386,7 @@ public class DexBuilder { if (ifsNeedingRewrite.contains(block)) { If theIf = block.exit().asIf(); BasicBlock trueTarget = theIf.getTrueTarget(); - BasicBlock newBlock = BasicBlock.createGotoBlock(trueTarget, ir.blocks.size()); + BasicBlock newBlock = BasicBlock.createGotoBlock(ir.blocks.size(), trueTarget); theIf.setTrueTarget(newBlock); theIf.invert(); it.add(newBlock); @@ -641,7 +641,8 @@ public class DexBuilder { item = tryItems.get(i); coalescedTryItems.add(item); // Trim the range start for non-throwing instructions when starting a new range. - List<com.android.tools.r8.ir.code.Instruction> instructions = blocksWithHandlers.get(i).getInstructions(); + List<com.android.tools.r8.ir.code.Instruction> instructions = blocksWithHandlers.get(i) + .getInstructions(); for (com.android.tools.r8.ir.code.Instruction insn : instructions) { if (insn.instructionTypeCanThrow()) { item.start = getInfo(insn).getOffset(); @@ -819,7 +820,8 @@ public class DexBuilder { private Instruction[] instructions; private final int size; - public MultiFixedSizeInfo(com.android.tools.r8.ir.code.Instruction ir, Instruction[] instructions) { + public MultiFixedSizeInfo(com.android.tools.r8.ir.code.Instruction ir, + Instruction[] instructions) { super(ir); this.instructions = instructions; int size = 0; 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 2dceeab8f..4a96ae664 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 @@ -291,14 +291,13 @@ public class IRBuilder { private int nextBlockNumber = 0; public IRBuilder(DexEncodedMethod method, SourceCode source, InternalOptions options) { - this(method, source, new ValueNumberGenerator(), options); + this(method, source, options, new ValueNumberGenerator()); } public IRBuilder( DexEncodedMethod method, SourceCode source, - ValueNumberGenerator valueNumberGenerator, - InternalOptions options) { + InternalOptions options, ValueNumberGenerator valueNumberGenerator) { assert source != null; this.method = method; this.source = source; @@ -1624,7 +1623,7 @@ public class IRBuilder { public Value readRegister(int register, MoveType type) { DebugLocalInfo local = getCurrentLocal(register); - Value value = readRegister(register, currentBlock, EdgeType.NON_EDGE, type, local); + Value value = readRegister(register, type, currentBlock, EdgeType.NON_EDGE, local); // Check that any information about a current-local is consistent with the read. if (local != null && value.getLocalInfo() != local && !value.isUninitializedLocal()) { throw new InvalidDebugInfoException( @@ -1643,10 +1642,10 @@ public class IRBuilder { public Value readRegisterIgnoreLocal(int register, MoveType type) { DebugLocalInfo local = getCurrentLocal(register); - return readRegister(register, currentBlock, EdgeType.NON_EDGE, type, local); + return readRegister(register, type, currentBlock, EdgeType.NON_EDGE, local); } - public Value readRegister(int register, BasicBlock block, EdgeType readingEdge, MoveType type, + public Value readRegister(int register, MoveType type, BasicBlock block, EdgeType readingEdge, DebugLocalInfo local) { checkRegister(register); Value value = block.readCurrentDefinition(register, readingEdge); @@ -1665,7 +1664,7 @@ public class IRBuilder { assert block.verifyFilledPredecessors(); BasicBlock pred = block.getPredecessors().get(0); EdgeType edgeType = pred.getEdgeType(block); - value = readRegister(register, pred, edgeType, type, local); + value = readRegister(register, type, pred, edgeType, local); } else { Phi phi = new Phi(valueNumberGenerator.next(), block, type, local); // We need to write the phi before adding operands to break cycles. If the phi is trivial @@ -1939,7 +1938,7 @@ public class IRBuilder { BasicBlock target = pair.second; // New block with one unfilled predecessor. - BasicBlock newBlock = BasicBlock.createGotoBlock(target, nextBlockNumber++); + BasicBlock newBlock = BasicBlock.createGotoBlock(nextBlockNumber++, target); blocks.add(newBlock); newBlock.incrementUnfilledPredecessorCount(); @@ -2018,7 +2017,7 @@ public class IRBuilder { int otherPredecessorIndex = values.get(v); BasicBlock joinBlock = joinBlocks.get(otherPredecessorIndex); if (joinBlock == null) { - joinBlock = BasicBlock.createGotoBlock(block, blocks.size() + blocksToAdd.size()); + joinBlock = BasicBlock.createGotoBlock(blocks.size() + blocksToAdd.size(), block); joinBlocks.put(otherPredecessorIndex, joinBlock); blocksToAdd.add(joinBlock); BasicBlock otherPredecessor = block.getPredecessors().get(otherPredecessorIndex); 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 063bee1dd..da022fe86 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 @@ -82,11 +82,8 @@ public class IRConverter { private DexString highestSortingString; private IRConverter( - Timing timing, - AppInfo appInfo, - GraphLense graphLense, - InternalOptions options, - CfgPrinter printer, + AppInfo appInfo, InternalOptions options, Timing timing, + CfgPrinter printer, GraphLense graphLense, boolean enableWholeProgramOptimizations) { assert appInfo != null; assert options != null; @@ -127,30 +124,26 @@ public class IRConverter { public IRConverter( AppInfo appInfo, InternalOptions options) { - this(null, appInfo, null, options, null, false); + this(appInfo, options, null, null, null, false); } /** * Create an IR converter for processing methods with full program optimization disabled. */ public IRConverter( - Timing timing, - AppInfo appInfo, - InternalOptions options, + AppInfo appInfo, InternalOptions options, Timing timing, CfgPrinter printer) { - this(timing, appInfo, null, options, printer, false); + this(appInfo, options, timing, printer, null, false); } /** * Create an IR converter for processing methods with full program optimization enabled. */ public IRConverter( - Timing timing, - AppInfoWithSubtyping appInfo, - InternalOptions options, + AppInfoWithSubtyping appInfo, InternalOptions options, Timing timing, CfgPrinter printer, GraphLense graphLense) { - this(timing, appInfo, graphLense, options, printer, true); + this(appInfo, options, timing, printer, graphLense, true); } private boolean enableInterfaceMethodDesugaring() { @@ -581,7 +574,23 @@ public class IRConverter { } printMethod(code, "Optimized IR (SSA)"); + finalizeIR(method, code, feedback); + } + + private void finalizeIR(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) { + if (options.outputClassFiles) { + finalizeToCf(method, code, feedback); + } else { + finalizeToDex(method, code, feedback); + } + } + + private void finalizeToCf(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) { + assert method.getCode().isJarCode(); + // TODO(zerny): Actually convert IR back to Java bytecode. + } + private void finalizeToDex(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) { // Perform register allocation. RegisterAllocator registerAllocator = performRegisterAllocation(code, method); method.setCode(code, registerAllocator, options); diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java index ab3efdbd4..b5fbb0643 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java @@ -116,7 +116,7 @@ public class LensCodeRewriter { // If the current block has catch handlers split the check cast into its own block. if (newInvoke.getBlock().hasCatchHandlers()) { iterator.previous(); - iterator.split(1, code, blocks); + iterator.split(code, 1, blocks); } } } diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java index 160da3c16..1cf64fd2b 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java @@ -20,7 +20,7 @@ import java.util.List; final class AccessorMethodSourceCode extends SynthesizedLambdaSourceCode { AccessorMethodSourceCode(LambdaClass lambda) { - super(/* no receiver for static method */ null, lambda, lambda.target.callTarget); + super(lambda, lambda.target.callTarget, null /* no receiver for static method */); // We should never need an accessor for interface methods since // they are supposed to be public. assert !descriptor().implHandle.type.isInvokeInterface(); 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 31c6de175..408f53b60 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 @@ -58,6 +58,7 @@ import java.util.Set; // forward the call to an appropriate method in interface companion class. // public final class InterfaceMethodRewriter { + // Public for testing. public static final String COMPANION_CLASS_NAME_SUFFIX = "-CC"; private static final String DEFAULT_METHOD_PREFIX = "$default$"; @@ -75,11 +76,17 @@ public final class InterfaceMethodRewriter { */ private Set<DexItem> reportedMissing = Sets.newIdentityHashSet(); - /** Defines a minor variation in desugaring. */ + /** + * Defines a minor variation in desugaring. + */ public enum Flavor { - /** Process all application resources. */ + /** + * Process all application resources. + */ IncludeAllResources, - /** Process all but DEX application resources. */ + /** + * Process all but DEX application resources. + */ ExcludeDexResources } @@ -174,13 +181,14 @@ public final class InterfaceMethodRewriter { } else if (holderClass.isInterface()) { throw new Unimplemented( "Desugaring of static interface method handle as in `" - + referencedFrom.toSourceString() + "` in is not yet supported."); + + referencedFrom.toSourceString() + "` in is not yet supported."); } } } /** * Returns the class definition for the specified type. + * * @return may return null if no definition for the given type is available. */ final DexClass findDefinitionFor(DexType type) { @@ -313,7 +321,7 @@ public final class InterfaceMethodRewriter { .append("`"); } options.diagnosticsHandler.warning( - new StringDiagnostic(classToDesugar.getOrigin(), builder.toString())); + new StringDiagnostic(builder.toString(), classToDesugar.getOrigin())); } private void warnMissingType(DexMethod referencedFrom, DexType missing) { @@ -331,6 +339,6 @@ public final class InterfaceMethodRewriter { .append("`"); DexClass referencedFromClass = converter.appInfo.definitionFor(referencedFrom.getHolder()); options.diagnosticsHandler.warning( - new StringDiagnostic(referencedFromClass.getOrigin(), builder.toString())); + new StringDiagnostic(builder.toString(), referencedFromClass.getOrigin())); } } diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java index 539fcf07d..411ab4ced 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java @@ -15,7 +15,7 @@ import java.util.Collections; final class LambdaClassConstructorSourceCode extends SynthesizedLambdaSourceCode { LambdaClassConstructorSourceCode(LambdaClass lambda) { - super(null /* Class initializer is static */, lambda, lambda.classConstructor); + super(lambda, lambda.classConstructor, null /* Class initializer is static */); assert lambda.instanceField != null; } diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java index 9a3531f14..bd38e1acb 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java @@ -16,14 +16,14 @@ abstract class SynthesizedLambdaSourceCode extends SingleBlockSourceCode { final DexMethod currentMethod; final LambdaClass lambda; - SynthesizedLambdaSourceCode(DexType receiver, LambdaClass lambda, DexMethod currentMethod) { + SynthesizedLambdaSourceCode(LambdaClass lambda, DexMethod currentMethod, DexType receiver) { super(receiver, currentMethod.proto); this.lambda = lambda; this.currentMethod = currentMethod; } SynthesizedLambdaSourceCode(LambdaClass lambda, DexMethod currentMethod) { - this(lambda.type, lambda, currentMethod); + this(lambda, currentMethod, lambda.type); } final LambdaDescriptor descriptor() { 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 9699ec16b..ad4639e7f 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 @@ -232,14 +232,14 @@ public class Inliner { GraphLense graphLense, InternalOptions options) throws ApiLevelException { if (target.isProcessed()) { assert target.getCode().isDexCode(); - return target.buildIR(generator, options); + return target.buildIR(options, generator); } else { // Build the IR for a yet not processed method, and perform minimal IR processing. IRCode code; if (target.getCode().isJarCode()) { - code = target.getCode().asJarCode().buildIR(target, generator, options); + code = target.getCode().asJarCode().buildIR(target, options, generator); } else { - code = target.getCode().asDexCode().buildIR(target, generator, options); + code = target.getCode().asDexCode().buildIR(target, options, generator); } new LensCodeRewriter(graphLense, appInfo).rewrite(code, target); return code; diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java new file mode 100644 index 000000000..83788c7c2 --- /dev/null +++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java @@ -0,0 +1,94 @@ +// Copyright (c) 2017, 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.jar; + +import static org.objectweb.asm.Opcodes.ACC_SUPER; + +import com.android.tools.r8.OutputSink; +import com.android.tools.r8.errors.Unimplemented; +import com.android.tools.r8.graph.DexAccessFlags; +import com.android.tools.r8.graph.DexApplication; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.utils.InternalOptions; +import java.io.IOException; +import java.util.Collections; +import java.util.concurrent.ExecutorService; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Type; + +public class CfApplicationWriter { + private final DexApplication application; + private final InternalOptions options; + + public CfApplicationWriter(DexApplication application, InternalOptions options) { + this.application = application; + this.options = options; + } + + public void write(OutputSink outputSink, ExecutorService executor) throws IOException { + application.timing.begin("CfApplicationWriter.write"); + try { + writeApplication(outputSink, executor); + } finally { + application.timing.end(); + } + } + + private void writeApplication(OutputSink outputSink, ExecutorService executor) + throws IOException { + for (DexProgramClass clazz : application.classes()) { + if (clazz.getSynthesizedFrom().isEmpty()) { + writeClass(clazz, outputSink); + } else { + throw new Unimplemented("No support for synthetics in the Java bytecode backend."); + } + } + } + + private void writeClass(DexProgramClass clazz, OutputSink outputSink) throws IOException { + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + writer.visitSource(clazz.sourceFile.toString(), null); + int version = clazz.getClassFileVersion(); + int access = classAndInterfaceAccessFlags(clazz.accessFlags); + String desc = clazz.type.toDescriptorString(); + String name = internalName(clazz.type); + String signature = null; // TODO(zerny): Support generic signatures. + String superName = + clazz.type == options.itemFactory.objectType ? null : internalName(clazz.superType); + String[] interfaces = new String[clazz.interfaces.values.length]; + for (int i = 0; i < clazz.interfaces.values.length; i++) { + interfaces[i] = internalName(clazz.interfaces.values[i]); + } + writer.visit(version, access, name, signature, superName, interfaces); + // TODO(zerny): Methods and fields. + for (DexEncodedMethod method : clazz.directMethods()) { + writeMethod(method, writer); + } + outputSink.writeClassFile(writer.toByteArray(), Collections.singleton(desc), desc); + } + + private void writeMethod(DexEncodedMethod method, ClassWriter writer) { + int access = method.accessFlags.get(); + String name = method.method.name.toString(); + String desc = method.descriptor(); + String signature = null; // TODO(zerny): Support generic signatures. + String[] exceptions = null; + MethodVisitor visitor = writer.visitMethod(access, name, desc, signature, exceptions); + method.getCode().asJarCode().writeTo(visitor); + } + + private static int classAndInterfaceAccessFlags(DexAccessFlags accessFlags) { + // TODO(zerny): Refactor access flags to account for the union of both DEX and Java flags. + int access = accessFlags.get(); + access |= ACC_SUPER; + return access; + } + + private static String internalName(DexType type) { + return Type.getType(type.toDescriptorString()).getInternalName(); + } +} diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java b/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java index e9ec08c3c..0e971c2e5 100644 --- a/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java +++ b/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java @@ -43,24 +43,24 @@ public class MinifiedNameMapPrinter { return copy; } - private void write(DexProgramClass clazz, PrintStream out) { + private void writeClass(DexProgramClass clazz, PrintStream out) { seenTypes.add(clazz.type); DexString descriptor = namingLens.lookupDescriptor(clazz.type); out.print(DescriptorUtils.descriptorToJavaType(clazz.type.descriptor.toSourceString())); out.print(" -> "); out.print(DescriptorUtils.descriptorToJavaType(descriptor.toSourceString())); out.println(":"); - write(sortedCopy( + writeFields(sortedCopy( clazz.instanceFields(), Comparator.comparing(DexEncodedField::toSourceString)), out); - write(sortedCopy( + writeFields(sortedCopy( clazz.staticFields(), Comparator.comparing(DexEncodedField::toSourceString)), out); - write(sortedCopy( + writeMethods(sortedCopy( clazz.directMethods(), Comparator.comparing(DexEncodedMethod::toSourceString)), out); - write(sortedCopy( + writeMethods(sortedCopy( clazz.virtualMethods(), Comparator.comparing(DexEncodedMethod::toSourceString)), out); } - private void write(DexType type, PrintStream out) { + private void writeType(DexType type, PrintStream out) { if (type.isClassType() && seenTypes.add(type)) { DexString descriptor = namingLens.lookupDescriptor(type); out.print(DescriptorUtils.descriptorToJavaType(type.descriptor.toSourceString())); @@ -70,7 +70,7 @@ public class MinifiedNameMapPrinter { } } - private void write(DexEncodedField[] fields, PrintStream out) { + private void writeFields(DexEncodedField[] fields, PrintStream out) { for (DexEncodedField encodedField : fields) { DexField field = encodedField.field; DexString renamed = namingLens.lookupName(field); @@ -102,7 +102,7 @@ public class MinifiedNameMapPrinter { out.println(renamed); } - private void write(DexEncodedMethod[] methods, PrintStream out) { + private void writeMethods(DexEncodedMethod[] methods, PrintStream out) { for (DexEncodedMethod encodedMethod : methods) { DexMethod method = encodedMethod.method; DexString renamed = namingLens.lookupName(method); @@ -125,9 +125,9 @@ public class MinifiedNameMapPrinter { // First write out all classes that have been renamed. List<DexProgramClass> classes = new ArrayList<>(application.classes()); classes.sort(Comparator.comparing(DexProgramClass::toSourceString)); - classes.forEach(clazz -> write(clazz, out)); + classes.forEach(clazz -> writeClass(clazz, out)); // Now write out all types only mentioned in descriptors that have been renamed. - namingLens.forAllRenamedTypes(type -> write(type, out)); + namingLens.forAllRenamedTypes(type -> writeType(type, out)); } public void write(Path destination) throws IOException { diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java index 17da59b6c..71d6e468d 100644 --- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java @@ -219,6 +219,6 @@ public class MemberRebindingAnalysis { appInfo::lookupStaticTarget, DexClass::findStaticTarget); computeFieldRebinding(Sets.union(appInfo.instanceFieldReads, appInfo.instanceFieldWrites), appInfo::lookupInstanceTarget, DexClass::findInstanceTarget); - return builder.build(lense, appInfo.dexItemFactory); + return builder.build(appInfo.dexItemFactory, lense); } } diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationSourceStrings.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationSourceStrings.java index 44b3393f0..cb69320e8 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationSourceStrings.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationSourceStrings.java @@ -12,6 +12,7 @@ import java.util.List; import joptsimple.internal.Strings; public class ProguardConfigurationSourceStrings implements ProguardConfigurationSource { + private final Path basePath; private final List<String> config; @@ -20,13 +21,13 @@ public class ProguardConfigurationSourceStrings implements ProguardConfiguration * {@param basePath}, which allows all other options that use a relative path to reach out * to desired paths appropriately. */ - public ProguardConfigurationSourceStrings(Path basePath, List<String> config) { + public ProguardConfigurationSourceStrings(List<String> config, Path basePath) { this.basePath = basePath; this.config = config; } private ProguardConfigurationSourceStrings(List<String> config) { - this(Paths.get("."), config); + this(config, Paths.get(".")); } @VisibleForTesting @@ -36,7 +37,7 @@ public class ProguardConfigurationSourceStrings implements ProguardConfiguration } @Override - public String get() throws IOException{ + public String get() throws IOException { return Strings.join(config, System.lineSeparator()); } diff --git a/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java b/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java index 9affabf24..b50d78ea4 100644 --- a/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java @@ -181,7 +181,7 @@ public class SimpleClassMerger { if (Log.ENABLED) { Log.debug(getClass(), "Merged %d classes.", numberOfMerges); } - return renamedMembersLense.build(graphLense, application.dexItemFactory); + return renamedMembersLense.build(application.dexItemFactory, graphLense); } private class ClassMerger { @@ -438,7 +438,7 @@ public class SimpleClassMerger { DexType fixed = fixupType(type); lense.map(type, fixed); } - return lense.build(graphLense, application.dexItemFactory); + return lense.build(application.dexItemFactory, graphLense); } private DexEncodedMethod[] removeDupes(DexEncodedMethod[] methods) { diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java index b082d9068..980eb1957 100644 --- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java +++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java @@ -596,10 +596,14 @@ public class AndroidApp { /** * Add Java-bytecode program data. */ - public Builder addClassProgramData(Origin origin, byte[] data) { + public Builder addClassProgramData(byte[] data, Origin origin) { return addProgramResources(Kind.CLASS, Resource.fromBytes(origin, data)); } + public Builder addClassProgramData(byte[] data, Origin origin, Set<String> classDescriptors) { + return addProgramResources(Kind.CLASS, Resource.fromBytes(origin, data, classDescriptors)); + } + /** * Set dead-code data. */ diff --git a/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java b/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java index 914428404..0f85cacec 100644 --- a/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java +++ b/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java @@ -4,7 +4,10 @@ package com.android.tools.r8.utils; import com.android.tools.r8.OutputSink; +import com.android.tools.r8.Resource.Origin; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.util.TreeMap; @@ -13,6 +16,7 @@ public class AndroidAppOutputSink extends ForwardingOutputSink { private final AndroidApp.Builder builder = AndroidApp.builder(); private final TreeMap<String, DescriptorsWithContents> dexFilesWithPrimary = new TreeMap<>(); private final TreeMap<Integer, DescriptorsWithContents> dexFilesWithId = new TreeMap<>(); + private final List<DescriptorsWithContents> classFiles = new ArrayList<>(); private boolean closed = false; public AndroidAppOutputSink(OutputSink forwardTo) { @@ -26,6 +30,7 @@ public class AndroidAppOutputSink extends ForwardingOutputSink { @Override public synchronized void writeDexFile(byte[] contents, Set<String> classDescriptors, int fileId) throws IOException { + assert dexFilesWithPrimary.isEmpty() && classFiles.isEmpty(); // Sort the files by id so that their order is deterministic. Some tests depend on this. dexFilesWithId.put(fileId, new DescriptorsWithContents(classDescriptors, contents)); super.writeDexFile(contents, classDescriptors, fileId); @@ -35,6 +40,7 @@ public class AndroidAppOutputSink extends ForwardingOutputSink { public synchronized void writeDexFile(byte[] contents, Set<String> classDescriptors, String primaryClassName) throws IOException { + assert dexFilesWithId.isEmpty() && classFiles.isEmpty(); // Sort the files by their name for good measure. dexFilesWithPrimary .put(primaryClassName, new DescriptorsWithContents(classDescriptors, contents)); @@ -42,6 +48,14 @@ public class AndroidAppOutputSink extends ForwardingOutputSink { } @Override + public synchronized void writeClassFile( + byte[] contents, Set<String> classDescriptors, String primaryClassName) throws IOException { + assert dexFilesWithPrimary.isEmpty() && dexFilesWithId.isEmpty(); + classFiles.add(new DescriptorsWithContents(classDescriptors, contents)); + super.writeClassFile(contents, classDescriptors, primaryClassName); + } + + @Override public void writePrintUsedInformation(byte[] contents) throws IOException { builder.setDeadCode(contents); super.writePrintUsedInformation(contents); @@ -68,9 +82,18 @@ public class AndroidAppOutputSink extends ForwardingOutputSink { @Override public void close() throws IOException { assert !closed; - assert dexFilesWithId.isEmpty() || dexFilesWithPrimary.isEmpty(); - dexFilesWithPrimary.forEach((v, d) -> builder.addDexProgramData(d.contents, d.descriptors, v)); - dexFilesWithId.forEach((v, d) -> builder.addDexProgramData(d.contents, d.descriptors)); + if (!dexFilesWithPrimary.isEmpty()) { + assert dexFilesWithId.isEmpty() && classFiles.isEmpty(); + dexFilesWithPrimary.forEach( + (v, d) -> builder.addDexProgramData(d.contents, d.descriptors, v)); + } else if (!dexFilesWithId.isEmpty()) { + assert dexFilesWithPrimary.isEmpty() && classFiles.isEmpty(); + dexFilesWithId.forEach((v, d) -> builder.addDexProgramData(d.contents, d.descriptors)); + } else if (!classFiles.isEmpty()) { + assert dexFilesWithPrimary.isEmpty() && dexFilesWithId.isEmpty(); + classFiles.forEach( + d -> builder.addClassProgramData(d.contents, Origin.unknown(), d.descriptors)); + } closed = true; super.close(); } diff --git a/src/main/java/com/android/tools/r8/utils/DirectoryOutputSink.java b/src/main/java/com/android/tools/r8/utils/DirectoryOutputSink.java index 56e0ea149..6c1090a8c 100644 --- a/src/main/java/com/android/tools/r8/utils/DirectoryOutputSink.java +++ b/src/main/java/com/android/tools/r8/utils/DirectoryOutputSink.java @@ -3,6 +3,9 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.utils; +import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION; +import static com.android.tools.r8.utils.FileUtils.DEX_EXTENSION; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -43,7 +46,18 @@ public class DirectoryOutputSink extends FileSystemOutputSink { @Override public void writeDexFile(byte[] contents, Set<String> classDescriptors, String primaryClassName) throws IOException { - Path target = outputDirectory.resolve(getOutputFileName(primaryClassName)); + writeFileFromDescriptor(contents, primaryClassName, DEX_EXTENSION); + } + + @Override + public void writeClassFile(byte[] contents, Set<String> classDescriptors, String primaryClassName) + throws IOException { + writeFileFromDescriptor(contents, primaryClassName, CLASS_EXTENSION); + } + + private void writeFileFromDescriptor(byte[] contents, String descriptor, String extension) + throws IOException { + Path target = outputDirectory.resolve(getOutputFileName(descriptor, extension)); Files.createDirectories(target.getParent()); writeToFile(target, null, contents); } diff --git a/src/main/java/com/android/tools/r8/utils/FileSystemOutputSink.java b/src/main/java/com/android/tools/r8/utils/FileSystemOutputSink.java index c0aef0a6b..1ca0cf979 100644 --- a/src/main/java/com/android/tools/r8/utils/FileSystemOutputSink.java +++ b/src/main/java/com/android/tools/r8/utils/FileSystemOutputSink.java @@ -28,13 +28,13 @@ public abstract class FileSystemOutputSink implements OutputSink { } String getOutputFileName(int index) { + assert !options.outputClassFiles; return index == 0 ? "classes.dex" : ("classes" + (index + 1) + FileUtils.DEX_EXTENSION); } - String getOutputFileName(String classDescriptor) throws IOException { + String getOutputFileName(String classDescriptor, String extension) throws IOException { assert classDescriptor != null && DescriptorUtils.isClassDescriptor(classDescriptor); - return DescriptorUtils.getClassBinaryNameFromDescriptor(classDescriptor) - + FileUtils.DEX_EXTENSION; + return DescriptorUtils.getClassBinaryNameFromDescriptor(classDescriptor) + extension; } diff --git a/src/main/java/com/android/tools/r8/utils/ForwardingOutputSink.java b/src/main/java/com/android/tools/r8/utils/ForwardingOutputSink.java index 3ac0e2b54..f1763291a 100644 --- a/src/main/java/com/android/tools/r8/utils/ForwardingOutputSink.java +++ b/src/main/java/com/android/tools/r8/utils/ForwardingOutputSink.java @@ -33,6 +33,12 @@ public abstract class ForwardingOutputSink implements OutputSink { } @Override + public void writeClassFile(byte[] contents, Set<String> classDescriptors, String primaryClassName) + throws IOException { + forwardTo.writeClassFile(contents, classDescriptors, primaryClassName); + } + + @Override public void writePrintUsedInformation(byte[] contents) throws IOException { forwardTo.writePrintUsedInformation(contents); } diff --git a/src/main/java/com/android/tools/r8/utils/IgnoreContentsOutputSink.java b/src/main/java/com/android/tools/r8/utils/IgnoreContentsOutputSink.java index 8dbf3bcbd..384c7d5bc 100644 --- a/src/main/java/com/android/tools/r8/utils/IgnoreContentsOutputSink.java +++ b/src/main/java/com/android/tools/r8/utils/IgnoreContentsOutputSink.java @@ -20,6 +20,12 @@ public class IgnoreContentsOutputSink implements OutputSink { } @Override + public void writeClassFile( + byte[] contents, Set<String> classDescriptors, String primaryClassName) { + // Intentionally left empty. + } + + @Override public void writePrintUsedInformation(byte[] contents) { // Intentionally left empty. } diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java index 75f89beac..1c0cc61bb 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java @@ -51,6 +51,8 @@ public class InternalOptions { public boolean printTimes = false; + public boolean outputClassFiles = false; + // Optimization-related flags. These should conform to -dontoptimize. public boolean skipDebugLineNumberOpt = false; public boolean skipClassMerging = true; @@ -123,6 +125,7 @@ public class InternalOptions { public boolean minimalMainDex; public static class InvalidParameterAnnotationInfo { + final DexMethod method; final int expectedParameterCount; final int actualParameterCount; @@ -188,7 +191,7 @@ public class InternalOptions { .append(" actual count: ") .append(info.actualParameterCount); } - diagnosticsHandler.info(new StringDiagnostic(origin, builder.toString())); + diagnosticsHandler.info(new StringDiagnostic(builder.toString(), origin)); } printed = true; } @@ -207,7 +210,7 @@ public class InternalOptions { for (DexEncodedMethod method : warningInvalidDebugInfo.get(origin)) { builder.append("\n ").append(method.toSourceString()); } - diagnosticsHandler.info(new StringDiagnostic(origin, builder.toString())); + diagnosticsHandler.info(new StringDiagnostic(builder.toString(), origin)); } printed = true; printOutdatedToolchain = true; diff --git a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java index b2add4aaa..d2c6560a5 100644 --- a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java +++ b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java @@ -7,14 +7,15 @@ import com.android.tools.r8.Diagnostic; import com.android.tools.r8.Resource.Origin; public class StringDiagnostic implements Diagnostic { + private final Origin origin; private final String message; public StringDiagnostic(String message) { - this(Origin.unknown(), message); + this(message, Origin.unknown()); } - public StringDiagnostic(Origin origin, String message) { + public StringDiagnostic(String message, Origin origin) { this.origin = origin; this.message = message; } diff --git a/src/main/java/com/android/tools/r8/utils/ZipFileOutputSink.java b/src/main/java/com/android/tools/r8/utils/ZipFileOutputSink.java index d290b564f..c1ad6c933 100644 --- a/src/main/java/com/android/tools/r8/utils/ZipFileOutputSink.java +++ b/src/main/java/com/android/tools/r8/utils/ZipFileOutputSink.java @@ -3,6 +3,9 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.utils; +import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION; +import static com.android.tools.r8.utils.FileUtils.DEX_EXTENSION; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -31,7 +34,13 @@ public class ZipFileOutputSink extends FileSystemOutputSink { @Override public void writeDexFile(byte[] contents, Set<String> classDescriptors, String primaryClassName) throws IOException { - writeToZipFile(getOutputFileName(primaryClassName), contents); + writeToZipFile(getOutputFileName(primaryClassName, DEX_EXTENSION), contents); + } + + @Override + public void writeClassFile(byte[] contents, Set<String> classDescriptors, String primaryClassName) + throws IOException { + writeToZipFile(getOutputFileName(primaryClassName, CLASS_EXTENSION), contents); } @Override diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java index ca9a40433..76d84c2fa 100644 --- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java +++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java @@ -9,6 +9,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.nio.file.Path; import java.util.ArrayList; import java.util.Enumeration; @@ -53,8 +54,9 @@ public class ZipUtils { Path outPath = outDirectoryPath.resolve(name); File outFile = outPath.toFile(); outFile.getParentFile().mkdirs(); - FileOutputStream output = new FileOutputStream(outFile); - ByteStreams.copy(input, output); + try (OutputStream output = new FileOutputStream(outFile)) { + ByteStreams.copy(input, output); + } outFiles.add(outFile); } }); diff --git a/src/test/examplesAndroidO/invokecustom/TestGenerator.java b/src/test/examplesAndroidO/invokecustom/TestGenerator.java index 574fe1431..e42b9c9c9 100644 --- a/src/test/examplesAndroidO/invokecustom/TestGenerator.java +++ b/src/test/examplesAndroidO/invokecustom/TestGenerator.java @@ -3,13 +3,14 @@ // BSD-style license that can be found in the LICENSE file. package invokecustom; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import org.objectweb.asm.ClassReader; @@ -36,30 +37,34 @@ public class TestGenerator { } private void generateTests() throws IOException { - ClassReader cr = new ClassReader(new FileInputStream(classNamePath.toFile())); - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); - cr.accept( - new ClassVisitor(Opcodes.ASM6, cw) { - @Override - public void visitEnd() { - generateMethodTest1(cw); - generateMethodTest2(cw); - generateMethodTest3(cw); - generateMethodTest4(cw); - generateMethodTest5(cw); - generateMethodTest6(cw); - generateMethodTest7(cw); - generateMethodTest8(cw); - generateMethodTest9(cw); - generateMethodTest10(cw); - generateMethodTest11(cw); - generateMethodTest12(cw); - generateMethodTest13(cw); - generateMethodMain(cw); - super.visitEnd(); - } - }, 0); - new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray()); + try (InputStream input = Files.newInputStream(classNamePath)) { + ClassReader cr = new ClassReader(input); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + cr.accept( + new ClassVisitor(Opcodes.ASM6, cw) { + @Override + public void visitEnd() { + generateMethodTest1(cw); + generateMethodTest2(cw); + generateMethodTest3(cw); + generateMethodTest4(cw); + generateMethodTest5(cw); + generateMethodTest6(cw); + generateMethodTest7(cw); + generateMethodTest8(cw); + generateMethodTest9(cw); + generateMethodTest10(cw); + generateMethodTest11(cw); + generateMethodTest12(cw); + generateMethodTest13(cw); + generateMethodMain(cw); + super.visitEnd(); + } + }, 0); + try (OutputStream output = Files.newOutputStream(classNamePath)) { + output.write(cw.toByteArray()); + } + } } /* generate main method that only call all test methods. */ diff --git a/src/test/examplesAndroidO/invokecustom2/TestGenerator.java b/src/test/examplesAndroidO/invokecustom2/TestGenerator.java index ab77d32a1..9bbc03a59 100644 --- a/src/test/examplesAndroidO/invokecustom2/TestGenerator.java +++ b/src/test/examplesAndroidO/invokecustom2/TestGenerator.java @@ -4,13 +4,14 @@ package invokecustom2; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import org.objectweb.asm.ClassReader; @@ -37,26 +38,30 @@ public class TestGenerator { } private void generateTests() throws IOException { - ClassReader cr = new ClassReader(new FileInputStream(classNamePath.toFile())); - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); - cr.accept( - new ClassVisitor(Opcodes.ASM6, cw) { - @Override - public void visitEnd() { - generateMethodTest1(cw); - generateMethodTest2(cw); - generateMethodTest3(cw); - generateMethodTest4(cw); - generateMethodTest5(cw); - generateMethodTest6(cw); - generateMethodTest7(cw); - generateMethodTest8(cw); - generateMethodTest9(cw); - generateMethodMain(cw); - super.visitEnd(); - } - }, 0); - new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray()); + try (InputStream input = Files.newInputStream(classNamePath)) { + ClassReader cr = new ClassReader(input); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + cr.accept( + new ClassVisitor(Opcodes.ASM6, cw) { + @Override + public void visitEnd() { + generateMethodTest1(cw); + generateMethodTest2(cw); + generateMethodTest3(cw); + generateMethodTest4(cw); + generateMethodTest5(cw); + generateMethodTest6(cw); + generateMethodTest7(cw); + generateMethodTest8(cw); + generateMethodTest9(cw); + generateMethodMain(cw); + super.visitEnd(); + } + }, 0); + try (OutputStream output = Files.newOutputStream(classNamePath)) { + output.write(cw.toByteArray()); + } + } } /* generate main method that only call all test methods. */ diff --git a/src/test/examplesAndroidO/stringconcat/TestGenerator.java b/src/test/examplesAndroidO/stringconcat/TestGenerator.java index 6a837a4f4..e72f43813 100644 --- a/src/test/examplesAndroidO/stringconcat/TestGenerator.java +++ b/src/test/examplesAndroidO/stringconcat/TestGenerator.java @@ -3,12 +3,13 @@ // BSD-style license that can be found in the LICENSE file. package stringconcat; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -46,177 +47,181 @@ public class TestGenerator { } private static void generateTests(Path classNamePath) throws IOException { - ClassReader cr = new ClassReader(new FileInputStream(classNamePath.toFile())); - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); - cr.accept( - new ClassVisitor(Opcodes.ASM6, cw) { - @Override - public MethodVisitor visitMethod(int access, - final String methodName, String desc, String signature, String[] exceptions) { - MethodVisitor mv = super.visitMethod(access, methodName, desc, signature, exceptions); - return new MethodVisitor(Opcodes.ASM6, mv) { - private List<Object> recentConstants = new ArrayList<>(); - - @Override - public void visitLdcInsn(Object cst) { - if (!recentConstants.isEmpty() || - (cst instanceof String && ((String) cst).startsWith(RECIPE_PREFIX))) { - // Add the constant, don't push anything on stack. - recentConstants.add(cst); - return; + try (InputStream input = Files.newInputStream(classNamePath)) { + ClassReader cr = new ClassReader(input); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + cr.accept( + new ClassVisitor(Opcodes.ASM6, cw) { + @Override + public MethodVisitor visitMethod(int access, + final String methodName, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, methodName, desc, signature, exceptions); + return new MethodVisitor(Opcodes.ASM6, mv) { + private List<Object> recentConstants = new ArrayList<>(); + + @Override + public void visitLdcInsn(Object cst) { + if (!recentConstants.isEmpty() || + (cst instanceof String && ((String) cst).startsWith(RECIPE_PREFIX))) { + // Add the constant, don't push anything on stack. + recentConstants.add(cst); + return; + } + super.visitLdcInsn(cst); } - super.visitLdcInsn(cst); - } - - @Override - public void visitMethodInsn( - int opcode, String owner, String name, String desc, boolean itf) { - // Replace calls to 'makeConcat(...)' with appropriate `invokedynamic`. - if (opcode == Opcodes.INVOKESTATIC && name.equals("makeConcat")) { - mv.visitInvokeDynamicInsn(MAKE_CONCAT.getName(), desc, MAKE_CONCAT); - recentConstants.clear(); - return; + + @Override + public void visitMethodInsn( + int opcode, String owner, String name, String desc, boolean itf) { + // Replace calls to 'makeConcat(...)' with appropriate `invokedynamic`. + if (opcode == Opcodes.INVOKESTATIC && name.equals("makeConcat")) { + mv.visitInvokeDynamicInsn(MAKE_CONCAT.getName(), desc, MAKE_CONCAT); + recentConstants.clear(); + return; + } + + // Replace calls to 'makeConcat(...)' with appropriate `invokedynamic`. + if (opcode == Opcodes.INVOKESTATIC && name.equals("makeConcatWithConstants")) { + if (recentConstants.isEmpty()) { + throw new AssertionError("No constants detected in `" + + methodName + "`: call to " + name + desc); + } + recentConstants.set(0, + ((String) recentConstants.get(0)).substring(RECIPE_PREFIX.length())); + + mv.visitInvokeDynamicInsn(MAKE_CONCAT_WITH_CONSTANTS.getName(), + removeLastParams(desc, recentConstants.size()), MAKE_CONCAT_WITH_CONSTANTS, + recentConstants.toArray(new Object[recentConstants.size()])); + recentConstants.clear(); + return; + } + + // Otherwise fall back to default implementation. + super.visitMethodInsn(opcode, owner, name, desc, itf); } - // Replace calls to 'makeConcat(...)' with appropriate `invokedynamic`. - if (opcode == Opcodes.INVOKESTATIC && name.equals("makeConcatWithConstants")) { - if (recentConstants.isEmpty()) { - throw new AssertionError("No constants detected in `" + - methodName + "`: call to " + name + desc); + private String removeLastParams(String descr, int paramsToRemove) { + MethodType methodType = + MethodType.fromMethodDescriptorString( + descr, this.getClass().getClassLoader()); + return methodType + .dropParameterTypes( + methodType.parameterCount() - paramsToRemove, + methodType.parameterCount()) + .toMethodDescriptorString(); + } + + @Override + public void visitInsn(int opcode) { + switch (opcode) { + case Opcodes.ICONST_0: + if (!recentConstants.isEmpty()) { + recentConstants.add(0); + return; + } + break; + case Opcodes.ICONST_1: + if (!recentConstants.isEmpty()) { + recentConstants.add(1); + return; + } + break; + case Opcodes.ICONST_2: + if (!recentConstants.isEmpty()) { + recentConstants.add(2); + return; + } + break; + case Opcodes.ICONST_3: + if (!recentConstants.isEmpty()) { + recentConstants.add(3); + return; + } + break; + case Opcodes.ICONST_4: + if (!recentConstants.isEmpty()) { + recentConstants.add(4); + return; + } + break; + case Opcodes.ICONST_5: + if (!recentConstants.isEmpty()) { + recentConstants.add(5); + return; + } + break; + case Opcodes.ICONST_M1: + if (!recentConstants.isEmpty()) { + recentConstants.add(-1); + return; + } + break; + default: + recentConstants.clear(); + break; } - recentConstants.set(0, - ((String) recentConstants.get(0)).substring(RECIPE_PREFIX.length())); + super.visitInsn(opcode); + } - mv.visitInvokeDynamicInsn(MAKE_CONCAT_WITH_CONSTANTS.getName(), - removeLastParams(desc, recentConstants.size()), MAKE_CONCAT_WITH_CONSTANTS, - recentConstants.toArray(new Object[recentConstants.size()])); + @Override + public void visitIntInsn(int opcode, int operand) { recentConstants.clear(); - return; + super.visitIntInsn(opcode, operand); } - // Otherwise fall back to default implementation. - super.visitMethodInsn(opcode, owner, name, desc, itf); - } - - private String removeLastParams(String descr, int paramsToRemove) { - MethodType methodType = - MethodType.fromMethodDescriptorString( - descr, this.getClass().getClassLoader()); - return methodType - .dropParameterTypes( - methodType.parameterCount() - paramsToRemove, - methodType.parameterCount()) - .toMethodDescriptorString(); - } - - @Override - public void visitInsn(int opcode) { - switch (opcode) { - case Opcodes.ICONST_0: - if (!recentConstants.isEmpty()) { - recentConstants.add(0); - return; - } - break; - case Opcodes.ICONST_1: - if (!recentConstants.isEmpty()) { - recentConstants.add(1); - return; - } - break; - case Opcodes.ICONST_2: - if (!recentConstants.isEmpty()) { - recentConstants.add(2); - return; - } - break; - case Opcodes.ICONST_3: - if (!recentConstants.isEmpty()) { - recentConstants.add(3); - return; - } - break; - case Opcodes.ICONST_4: - if (!recentConstants.isEmpty()) { - recentConstants.add(4); - return; - } - break; - case Opcodes.ICONST_5: - if (!recentConstants.isEmpty()) { - recentConstants.add(5); - return; - } - break; - case Opcodes.ICONST_M1: - if (!recentConstants.isEmpty()) { - recentConstants.add(-1); - return; - } - break; - default: - recentConstants.clear(); - break; + @Override + public void visitVarInsn(int opcode, int var) { + recentConstants.clear(); + super.visitVarInsn(opcode, var); + } + + @Override + public void visitTypeInsn(int opcode, String type) { + recentConstants.clear(); + super.visitTypeInsn(opcode, type); + } + + @Override + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + recentConstants.clear(); + super.visitFieldInsn(opcode, owner, name, desc); + } + + @Override + public void visitJumpInsn(int opcode, Label label) { + recentConstants.clear(); + super.visitJumpInsn(opcode, label); + } + + @Override + public void visitIincInsn(int var, int increment) { + recentConstants.clear(); + super.visitIincInsn(var, increment); + } + + @Override + public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { + recentConstants.clear(); + super.visitTableSwitchInsn(min, max, dflt, labels); + } + + @Override + public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { + recentConstants.clear(); + super.visitLookupSwitchInsn(dflt, keys, labels); + } + + @Override + public void visitMultiANewArrayInsn(String desc, int dims) { + recentConstants.clear(); + super.visitMultiANewArrayInsn(desc, dims); } - super.visitInsn(opcode); - } - - @Override - public void visitIntInsn(int opcode, int operand) { - recentConstants.clear(); - super.visitIntInsn(opcode, operand); - } - - @Override - public void visitVarInsn(int opcode, int var) { - recentConstants.clear(); - super.visitVarInsn(opcode, var); - } - - @Override - public void visitTypeInsn(int opcode, String type) { - recentConstants.clear(); - super.visitTypeInsn(opcode, type); - } - - @Override - public void visitFieldInsn(int opcode, String owner, String name, String desc) { - recentConstants.clear(); - super.visitFieldInsn(opcode, owner, name, desc); - } - - @Override - public void visitJumpInsn(int opcode, Label label) { - recentConstants.clear(); - super.visitJumpInsn(opcode, label); - } - - @Override - public void visitIincInsn(int var, int increment) { - recentConstants.clear(); - super.visitIincInsn(var, increment); - } - - @Override - public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { - recentConstants.clear(); - super.visitTableSwitchInsn(min, max, dflt, labels); - } - - @Override - public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { - recentConstants.clear(); - super.visitLookupSwitchInsn(dflt, keys, labels); - } - - @Override - public void visitMultiANewArrayInsn(String desc, int dims) { - recentConstants.clear(); - super.visitMultiANewArrayInsn(desc, dims); - } - }; - } - }, 0); - new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray()); + }; + } + }, 0); + try (OutputStream output = Files.newOutputStream(classNamePath)) { + output.write(cw.toByteArray()); + } + } } } diff --git a/src/test/examplesAndroidP/invokecustom/TestGenerator.java b/src/test/examplesAndroidP/invokecustom/TestGenerator.java index 375dc3413..7ee4bdc0b 100644 --- a/src/test/examplesAndroidP/invokecustom/TestGenerator.java +++ b/src/test/examplesAndroidP/invokecustom/TestGenerator.java @@ -3,13 +3,14 @@ // BSD-style license that can be found in the LICENSE file. package invokecustom; -import java.io.FileInputStream; -import java.io.FileOutputStream; + import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.invoke.CallSite; -import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import org.objectweb.asm.ClassReader; @@ -36,19 +37,23 @@ public class TestGenerator { } private void generateTests() throws IOException { - ClassReader cr = new ClassReader(new FileInputStream(classNamePath.toFile())); - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); - cr.accept( - new ClassVisitor(Opcodes.ASM6, cw) { - @Override - public void visitEnd() { - generateMethodTest1(cw); - generateMethodTest2(cw); - generateMethodMain(cw); - super.visitEnd(); - } - }, 0); - new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray()); + try (InputStream inputStream = Files.newInputStream(classNamePath)) { + ClassReader cr = new ClassReader(inputStream); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + cr.accept( + new ClassVisitor(Opcodes.ASM6, cw) { + @Override + public void visitEnd() { + generateMethodTest1(cw); + generateMethodTest2(cw); + generateMethodMain(cw); + super.visitEnd(); + } + }, 0); + try (OutputStream output = Files.newOutputStream(classNamePath)) { + output.write(cw.toByteArray()); + } + } } /* Generate main method that only call all test methods. */ diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java index 92de2688c..4f3a4c8b2 100644 --- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java +++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java @@ -19,7 +19,6 @@ import com.android.tools.r8.utils.ArtErrorParser; import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo; import com.android.tools.r8.utils.DexInspector; import com.android.tools.r8.utils.FileUtils; -import com.android.tools.r8.utils.JarBuilder; import com.android.tools.r8.utils.ListUtils; import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; @@ -30,6 +29,7 @@ import com.google.common.collect.ObjectArrays; import com.google.common.collect.Sets; import java.io.File; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -45,6 +45,8 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.function.BiFunction; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; import java.util.stream.Collectors; import org.junit.ComparisonFailure; import org.junit.Rule; @@ -1576,7 +1578,7 @@ public abstract class R8RunArtTestsTest { // Run Art on JAR file with multiple dex files. processedFile = temp.getRoot().toPath().resolve(specification.name + ".jar").toFile(); - JarBuilder.buildJar(outputFiles, processedFile); + buildJar(outputFiles, processedFile); } boolean compileOnly = System.getProperty("jctf_compile_only", "0").equals("1"); @@ -1762,7 +1764,7 @@ public abstract class R8RunArtTestsTest { // Run Art on JAR file with multiple dex files. processedFile = temp.getRoot().toPath().resolve(specification.name + ".jar").toFile(); - JarBuilder.buildJar(outputFiles, processedFile); + buildJar(outputFiles, processedFile); } File expectedFile = specification.resolveFile("expected.txt"); @@ -1874,4 +1876,18 @@ public abstract class R8RunArtTestsTest { } throw errors.get(errors.size() - 1); } + + // TODO(zerny): Refactor tests to output jar files directly and eliminate this method. + private static void buildJar(File[] files, File jarFile) throws IOException { + try (JarOutputStream target = new JarOutputStream(new FileOutputStream(jarFile))) { + for (File file : files) { + // Only use the file name in the JAR entry (classes.dex, classes2.dex, ...) + JarEntry entry = new JarEntry(file.getName()); + entry.setTime(file.lastModified()); + target.putNextEntry(entry); + Files.copy(file.toPath(), target); + target.closeEntry(); + } + } + } } diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java index e21e1bbb5..96d9e8b5b 100644 --- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java +++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java @@ -7,6 +7,7 @@ import static com.android.tools.r8.TestCondition.R8_COMPILER; import static com.android.tools.r8.TestCondition.match; import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest; @@ -15,10 +16,8 @@ import com.android.tools.r8.ToolHelper.DexVm; import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.shaking.ProguardRuleParserException; -import com.android.tools.r8.utils.JarBuilder; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -44,6 +43,11 @@ public class R8RunExamplesTest { DX, JAVAC, JAVAC_ALL, JAVAC_NONE } + enum Output { + DEX, + CF + } + private static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_BUILD_DIR; // For local testing on a specific Art version(s) change this set. e.g. to @@ -67,7 +71,7 @@ public class R8RunExamplesTest { TestCondition.runtimes(Version.V6_0_1, Version.V5_1_1, Version.V4_4_4))) .build(); - @Parameters(name = "{0}_{1}_{2}_{3}") + @Parameters(name = "{0}_{1}_{2}_{3}_{5}") public static Collection<String[]> data() { String[] tests = { "arithmetic.Arithmetic", @@ -117,6 +121,10 @@ public class R8RunExamplesTest { "switchmaps.Switches", }; + String[] javaBytecodeTests = { + "hello.Hello", + }; + List<String[]> fullTestList = new ArrayList<>(tests.length * 2); for (String test : tests) { fullTestList.add(makeTest(Input.JAVAC, CompilerUnderTest.D8, CompilationMode.DEBUG, test)); @@ -132,13 +140,26 @@ public class R8RunExamplesTest { test)); fullTestList.add(makeTest(Input.DX, CompilerUnderTest.R8, CompilationMode.RELEASE, test)); } + // TODO(zerny): Once all tests pass create the java tests in the main test loop. + for (String test : javaBytecodeTests) { + fullTestList.add( + makeTest(Input.JAVAC_ALL, CompilerUnderTest.R8, CompilationMode.DEBUG, test, Output.CF)); + fullTestList.add( + makeTest( + Input.JAVAC_ALL, CompilerUnderTest.R8, CompilationMode.RELEASE, test, Output.CF)); + } return fullTestList; } private static String[] makeTest( Input input, CompilerUnderTest compiler, CompilationMode mode, String clazz) { + return makeTest(input, compiler, mode, clazz, Output.DEX); + } + + private static String[] makeTest( + Input input, CompilerUnderTest compiler, CompilationMode mode, String clazz, Output output) { String pkg = clazz.substring(0, clazz.lastIndexOf('.')); - return new String[]{pkg, input.name(), compiler.name(), mode.name(), clazz}; + return new String[] {pkg, input.name(), compiler.name(), mode.name(), clazz, output.name()}; } @Rule @@ -149,18 +170,25 @@ public class R8RunExamplesTest { private final CompilationMode mode; private final String pkg; private final String mainClass; + private final Output output; public R8RunExamplesTest( String pkg, String input, String compiler, String mode, - String mainClass) { + String mainClass, + String output) { this.pkg = pkg; this.input = Input.valueOf(input); this.compiler = CompilerUnderTest.valueOf(compiler); this.mode = CompilationMode.valueOf(mode); this.mainClass = mainClass; + this.output = Output.valueOf(output); + } + + private Path getOutputFile() { + return temp.getRoot().toPath().resolve("out.jar"); } private Path getInputFile() { @@ -190,18 +218,22 @@ public class R8RunExamplesTest { return input == Input.DX ? DexTool.DX : DexTool.NONE; } + private Path getOutputPath() { + return temp.getRoot().toPath().resolve("out.jar"); + } + @Rule public ExpectedException thrown = ExpectedException.none(); @Before public void compile() throws IOException, ProguardRuleParserException, ExecutionException, CompilationException { - Path out = temp.getRoot().toPath(); switch (compiler) { case D8: { + assertTrue(output == Output.DEX); ToolHelper.runD8(D8Command.builder() .addProgramFiles(getInputFile()) - .setOutputPath(out) + .setOutputPath(getOutputFile()) .setMode(mode) .build()); break; @@ -209,9 +241,10 @@ public class R8RunExamplesTest { case R8: { ToolHelper.runR8(R8Command.builder() .addProgramFiles(getInputFile()) - .setOutputPath(out) + .setOutputPath(getOutputFile()) .setMode(mode) - .build()); + .build(), + options -> options.outputClassFiles = (output == Output.CF)); break; } default: @@ -226,19 +259,7 @@ public class R8RunExamplesTest { } String original = getOriginalDexFile().toString(); - - File generated; - // Collect the generated dex files. - File[] outputFiles = - temp.getRoot().listFiles((File file) -> file.getName().endsWith(".dex")); - if (outputFiles.length == 1) { - // Just run Art on classes.dex. - generated = outputFiles[0]; - } else { - // Run Art on JAR file with multiple dex files. - generated = temp.getRoot().toPath().resolve(pkg + ".jar").toFile(); - JarBuilder.buildJar(outputFiles, generated); - } + Path generated = getOutputFile(); ToolHelper.ProcessResult javaResult = ToolHelper.runJava(ImmutableList.of(getOriginalJarFile("").toString()), mainClass); @@ -246,28 +267,36 @@ public class R8RunExamplesTest { fail("JVM failed for: " + mainClass); } - // TODO(ager): Once we have a bot running using dalvik (version 4.4.4) we should remove - // this explicit loop to get rid of repeated testing on the buildbots. - for (DexVm version : artVersions) { - TestCondition condition = failingRun.get(mainClass); - if (condition != null && condition.test(getTool(), compiler, version.getVersion(), mode)) { - thrown.expect(Throwable.class); - } else { - thrown = ExpectedException.none(); + if (output == Output.CF) { + ToolHelper.ProcessResult result = + ToolHelper.runJava(ImmutableList.of(generated.toString()), mainClass); + if (result.exitCode != 0) { + System.err.println(result.stderr); + fail("JVM failed on compiled output for: " + mainClass); } + assertEquals( + "JavaC/JVM and " + compiler.name() + "/JVM output differ", + javaResult.stdout, + result.stdout); + return; + } - // Check output against Art output on original dex file. - String output = - ToolHelper.checkArtOutputIdentical(original, generated.toString(), mainClass, version); - - // Check output against JVM output. - if (shouldMatchJVMOutput(version.getVersion())) { - String javaOutput = javaResult.stdout; - assertEquals( - "JVM and Art output differ:\n" + "JVM:\n" + javaOutput + "\nArt:\n" + output, - javaOutput, - output); - } + DexVm vm = ToolHelper.getDexVm(); + TestCondition condition = failingRun.get(mainClass); + if (condition != null && condition.test(getTool(), compiler, vm.getVersion(), mode)) { + thrown.expect(Throwable.class); + } else { + thrown = ExpectedException.none(); + } + + // Check output against Art output on original dex file. + String output = + ToolHelper.checkArtOutputIdentical(original, generated.toString(), mainClass, vm); + + // Check output against JVM output. + if (shouldMatchJVMOutput(vm.getVersion())) { + String javaOutput = javaResult.stdout; + assertEquals("JVM and Art output differ", javaOutput, output); } } diff --git a/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java b/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java index 2b0084d55..ddcbf8cf5 100644 --- a/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java +++ b/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java @@ -9,7 +9,7 @@ import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder; import com.google.common.collect.ImmutableList; import jasmin.ClassFile; import java.io.File; -import java.io.FileOutputStream; +import java.io.OutputStream; import java.io.StringReader; import java.nio.file.Files; import java.nio.file.Path; @@ -88,7 +88,9 @@ public class JasminDebugTest extends DebugTestBase { file.readJasmin(new StringReader(clazz.toString()), clazz.name, false); Path path = out.toPath().resolve(clazz.name + ".class"); Files.createDirectories(path.getParent()); - file.write(new FileOutputStream(path.toFile())); + try (OutputStream outputStream = Files.newOutputStream(path)) { + file.write(outputStream); + } if (isRunningJava()) { extraPaths.add(path); } else { diff --git a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java index cf191de53..ad9a26ae5 100644 --- a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java +++ b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java @@ -4,7 +4,6 @@ package com.android.tools.r8.ir; -import com.android.tools.r8.CompilationException; import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.ir.code.BasicBlock; @@ -52,12 +51,12 @@ public class BasicBlockIteratorTest extends SmaliTestBase { // Build the code, and split the code into three blocks. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); ListIterator<BasicBlock> blocks = code.listIterator(); InstructionListIterator iter = blocks.next().listIterator(); iter.nextUntil(i -> !i.isArgument()); iter.previous(); - iter.split(1, code, blocks); + iter.split(code, 1, blocks); return code; } diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java index 825832416..27a3ce979 100644 --- a/src/test/java/com/android/tools/r8/ir/InlineTest.java +++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java @@ -76,13 +76,13 @@ public class InlineTest extends SmaliTestBase { // Return the processed method for inspection. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); DexEncodedMethod methodA = getMethod(application, signatureA); - IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator); DexEncodedMethod methodB = getMethod(application, signatureB); - IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator); return new SmaliTestBase.TestApplication(application, method, code, ImmutableList.of(codeA, codeB), valueNumberGenerator, options); @@ -159,10 +159,10 @@ public class InlineTest extends SmaliTestBase { // Return the processed method for inspection. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); DexEncodedMethod methodA = getMethod(application, signatureA); - IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator); return new TestApplication(application, method, code, ImmutableList.of(codeA), valueNumberGenerator, options); @@ -239,19 +239,19 @@ public class InlineTest extends SmaliTestBase { // Return the processed method for inspection. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); // Build three copies of a and b for inlining three times. List<IRCode> additionalCode = new ArrayList<>(); for (int i = 0; i < 3; i++) { DexEncodedMethod methodA = getMethod(application, signatureA); - IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator); additionalCode.add(codeA); } for (int i = 0; i < 3; i++) { DexEncodedMethod methodB = getMethod(application, signatureB); - IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator); additionalCode.add(codeB); } @@ -373,13 +373,13 @@ public class InlineTest extends SmaliTestBase { // Return the processed method for inspection. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); DexEncodedMethod methodA = getMethod(application, signatureA); - IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator); DexEncodedMethod methodB = getMethod(application, signatureB); - IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator); return new TestApplication(application, method, code, ImmutableList.of(codeA, codeB), valueNumberGenerator, options); @@ -487,13 +487,13 @@ public class InlineTest extends SmaliTestBase { // Return the processed method for inspection. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); DexEncodedMethod methodA = getMethod(application, signatureA); - IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator); DexEncodedMethod methodB = getMethod(application, signatureB); - IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator); return new TestApplication(application, method, code, ImmutableList.of(codeA, codeB), valueNumberGenerator, options); @@ -600,13 +600,13 @@ public class InlineTest extends SmaliTestBase { // Return the processed method for inspection. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); DexEncodedMethod methodA = getMethod(application, signatureA); - IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator); DexEncodedMethod methodB = getMethod(application, signatureB); - IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator); return new TestApplication(application, method, code, ImmutableList.of(codeA, codeB), valueNumberGenerator, options); @@ -714,19 +714,19 @@ public class InlineTest extends SmaliTestBase { // Return the processed method for inspection. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); // Build three copies of a and b for inlining three times. List<IRCode> additionalCode = new ArrayList<>(); for (int i = 0; i < 3; i++) { DexEncodedMethod methodA = getMethod(application, signatureA); - IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator); additionalCode.add(codeA); } for (int i = 0; i < 3; i++) { DexEncodedMethod methodB = getMethod(application, signatureB); - IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator); additionalCode.add(codeB); } @@ -871,19 +871,19 @@ public class InlineTest extends SmaliTestBase { // Return the processed method for inspection. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); // Build three copies of a and b for inlining three times. List<IRCode> additionalCode = new ArrayList<>(); for (int i = 0; i < 3; i++) { DexEncodedMethod methodA = getMethod(application, signatureA); - IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator); additionalCode.add(codeA); } for (int i = 0; i < 3; i++) { DexEncodedMethod methodB = getMethod(application, signatureB); - IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator); additionalCode.add(codeB); } @@ -1118,13 +1118,13 @@ public class InlineTest extends SmaliTestBase { // Return the processed method for inspection. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); DexEncodedMethod methodA = getMethod(application, signatureA); - IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator); DexEncodedMethod methodB = getMethod(application, signatureB); - IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator); return new TestApplication(application, method, code, ImmutableList.of(codeA, codeB), valueNumberGenerator, options); diff --git a/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java b/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java index b9eef9cf3..a688ffe21 100644 --- a/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java +++ b/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java @@ -52,12 +52,12 @@ public class InstructionIteratorTest extends SmaliTestBase { // Build the code, and split the code into three blocks. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); ListIterator<BasicBlock> blocks = code.listIterator(); InstructionListIterator iter = blocks.next().listIterator(); iter.nextUntil(i -> !i.isArgument()); iter.previous(); - iter.split(1, code, blocks); + iter.split(code, 1, blocks); return code; } diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java index 735120621..9192b2fad 100644 --- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java +++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java @@ -64,7 +64,7 @@ public class SplitBlockTest extends SmaliTestBase { // Return the processed method for inspection. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); return new TestApplication(application, method, code, valueNumberGenerator, options); } @@ -123,7 +123,7 @@ public class SplitBlockTest extends SmaliTestBase { assertTrue(!block.getInstructions().get(i).isArgument()); InstructionListIterator iterator = test.listIteratorAt(block, i); - BasicBlock newBlock = iterator.split(1, code); + BasicBlock newBlock = iterator.split(code, 1); assertTrue(code.isConsistentSSA()); assertEquals(initialBlockCount + 2, code.blocks.size()); @@ -182,7 +182,7 @@ public class SplitBlockTest extends SmaliTestBase { // Return the processed method for inspection. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); return new TestApplication(application, method, code, valueNumberGenerator, options); } @@ -247,7 +247,7 @@ public class SplitBlockTest extends SmaliTestBase { assertEquals(secondBlockInstructions, instructionCount); InstructionListIterator iterator = test.listIteratorAt(block, i); - BasicBlock newBlock = iterator.split(1, code); + BasicBlock newBlock = iterator.split(code, 1); assertTrue(code.isConsistentSSA()); assertEquals(initialBlockCount + 2, code.blocks.size()); @@ -307,7 +307,7 @@ public class SplitBlockTest extends SmaliTestBase { // Return the processed method for inspection. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); return new TestApplication(application, method, code, valueNumberGenerator, options); } @@ -431,7 +431,7 @@ public class SplitBlockTest extends SmaliTestBase { // Return the processed method for inspection. ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator(); DexEncodedMethod method = getMethod(application, signature); - IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions()); + IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator); return new TestApplication(application, method, code, valueNumberGenerator, options); } diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java index c8ea3bbfc..a1046dbe5 100644 --- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java +++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java @@ -99,7 +99,8 @@ public class RegisterMoveSchedulerTest { } @Override - public BasicBlock split(int instructions, IRCode code, ListIterator<BasicBlock> blockIterator) { + public BasicBlock split(IRCode code, int instructions, + ListIterator<BasicBlock> blockIterator) { throw new Unimplemented(); } 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 663901f3b..caada82c7 100644 --- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java +++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java @@ -21,6 +21,7 @@ import java.io.ByteArrayOutputStream; import java.io.StringReader; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class JasminBuilder { @@ -45,6 +46,10 @@ public class JasminBuilder { return name + ".j"; } + public String getDescriptor() { + return "L" + name + ";"; + } + public MethodSignature addVirtualMethod( String name, List<String> argumentTypes, @@ -185,7 +190,8 @@ public class JasminBuilder { return clazz.getSourceFile(); } }; - builder.addClassProgramData(origin, compile(clazz)); + builder.addClassProgramData( + compile(clazz), origin, Collections.singleton(clazz.getDescriptor())); } return builder.build(); } diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java index 6962f976f..15cf848b2 100644 --- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java +++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java @@ -8,6 +8,7 @@ import static org.junit.Assert.assertTrue; import com.android.tools.r8.CompilationException; import com.android.tools.r8.R8; +import com.android.tools.r8.Resource; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.ProcessResult; import com.android.tools.r8.dex.ApplicationReader; @@ -27,10 +28,13 @@ import com.android.tools.r8.utils.OutputMode; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.Timing; import com.google.common.collect.ImmutableList; +import com.google.common.io.ByteStreams; import jasmin.ClassFile; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.StringReader; import java.nio.file.Files; import java.nio.file.Path; @@ -48,20 +52,37 @@ public class JasminTestBase { @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest(); + public static String getPathFromDescriptor(String classDescriptor) { + assert classDescriptor.startsWith("L"); + assert classDescriptor.endsWith(";"); + return classDescriptor.substring(1, classDescriptor.length() - 1) + ".class"; + } + protected ProcessResult runOnJavaRaw(JasminBuilder builder, String main) throws Exception { - File out = temp.newFolder("classes"); - for (ClassBuilder clazz : builder.getClasses()) { - ClassFile file = new ClassFile(); - file.readJasmin(new StringReader(clazz.toString()), clazz.name, false); - Path path = out.toPath().resolve(clazz.name + ".class"); + return runOnJavaRaw(builder.build(), main); + } + + protected ProcessResult runOnJavaRaw(AndroidApp app, String main) throws Exception { + File out = temp.newFolder(); + for (Resource clazz : app.getClassProgramResources()) { + assert clazz.getClassDescriptors().size() == 1; + String desc = clazz.getClassDescriptors().iterator().next(); + Path path = out.toPath().resolve(getPathFromDescriptor(desc)); Files.createDirectories(path.getParent()); - file.write(new FileOutputStream(path.toFile())); + try (InputStream input = clazz.getStream(); + OutputStream output = Files.newOutputStream(path)) { + ByteStreams.copy(input, output); + } } return ToolHelper.runJava(ImmutableList.of(out.getPath()), main); } protected String runOnJava(JasminBuilder builder, String main) throws Exception { - ProcessResult result = runOnJavaRaw(builder, main); + return runOnJava(builder.build(), main); + } + + protected String runOnJava(AndroidApp app, String main) throws Exception { + ProcessResult result = runOnJavaRaw(app, main); if (result.exitCode != 0) { System.out.println("Std out:"); System.out.println(result.stdout); @@ -111,7 +132,10 @@ public class JasminTestBase { for (ClassBuilder clazz : builder.getClasses()) { ClassFile file = new ClassFile(); file.readJasmin(new StringReader(clazz.toString()), clazz.name, false); - file.write(new FileOutputStream(classes.toPath().resolve(clazz.name + ".class").toFile())); + try (OutputStream outputStream = + Files.newOutputStream(classes.toPath().resolve(clazz.name + ".class"))) { + file.write(outputStream); + } } List<String> args = new ArrayList<>(); args.add("--output=" + dex.toString()); diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java index 1519f6c8a..298876ff3 100644 --- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java +++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java @@ -55,7 +55,7 @@ public class ForceProguardCompatibilityTest extends TestBase { private void runAnnotationsTest(boolean forceProguardCompatibility, boolean keepAnnotations) throws Exception { R8Command.Builder builder = - new CompatProguardCommandBuilder(forceProguardCompatibility); + new CompatProguardCommandBuilder(forceProguardCompatibility, false); // Add application classes including the annotation class. Class mainClass = TestMain.class; Class mentionedClassWithAnnotations = TestMain.MentionedClassWithAnnotation.class; diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestAnnotation.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestAnnotation.java new file mode 100644 index 000000000..0f2f5a426 --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestAnnotation.java @@ -0,0 +1,13 @@ +// Copyright (c) 2017, 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.forceproguardcompatibility; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface TestAnnotation { + public int value(); +}
\ No newline at end of file diff --git a/src/test/java/com/android/tools/r8/utils/JarBuilder.java b/src/test/java/com/android/tools/r8/utils/JarBuilder.java deleted file mode 100644 index 9186ff44a..000000000 --- a/src/test/java/com/android/tools/r8/utils/JarBuilder.java +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2016, 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.utils; - -import com.google.common.io.ByteStreams; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.jar.JarEntry; -import java.util.jar.JarOutputStream; - -public class JarBuilder { - public static void buildJar(File[] files, File jarFile) throws IOException { - JarOutputStream target = new JarOutputStream(new FileOutputStream(jarFile)); - for (File file : files) { - // Only use the file name in the JAR entry (classes.dex, classes2.dex, ...) - JarEntry entry = new JarEntry(file.getName()); - entry.setTime(file.lastModified()); - target.putNextEntry(entry); - InputStream in = new BufferedInputStream(new FileInputStream(file)); - ByteStreams.copy(in, target); - in.close(); - target.closeEntry(); - } - target.close(); - } -} |