Merge "Add support for -if rule with back references."
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index eb2d0fe..fbc6ca3 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.AbortException;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -66,6 +66,34 @@
// Internal access to the internal options.
abstract InternalOptions getInternalOptions();
+ abstract static class InputFileOrigin extends PathOrigin {
+ private final String inputType;
+
+ public InputFileOrigin(String inputType, Path file) {
+ super(file);
+ this.inputType = inputType;
+ }
+
+ @Override
+ public String part() {
+ return inputType + " '" + super.part() + "'";
+ }
+ }
+
+ private static class ProgramInputOrigin extends InputFileOrigin {
+
+ public ProgramInputOrigin(Path file) {
+ super("program input", file);
+ }
+ }
+
+ private static class LibraryInputOrigin extends InputFileOrigin {
+
+ public LibraryInputOrigin(Path file) {
+ super("library input", file);
+ }
+ }
+
/**
* Base builder for commands.
*
@@ -83,20 +111,16 @@
List<Path> programFiles = new ArrayList<>();
Builder() {
- this(AndroidApp.builder(), new DefaultDiagnosticsHandler());
+ this(AndroidApp.builder());
}
Builder(DiagnosticsHandler handler) {
- this(AndroidApp.builder(), handler);
+ this(AndroidApp.builder(new Reporter(handler)));
}
Builder(AndroidApp.Builder builder) {
- this(builder, new DefaultDiagnosticsHandler());
- }
-
- Builder(AndroidApp.Builder builder, DiagnosticsHandler handler) {
this.app = builder;
- this.reporter = new Reporter(handler);
+ this.reporter = builder.getReporter();
}
abstract B self();
@@ -147,7 +171,7 @@
app.addProgramFile(path);
programFiles.add(path);
} catch (IOException | CompilationError e) {
- error("Error with input file: ", path, e);
+ error(new ProgramInputOrigin(path), e);
}
});
});
@@ -181,7 +205,7 @@
try {
app.addLibraryFile(path);
} catch (IOException | CompilationError e) {
- error("Error with library file: ", path, e);
+ error(new LibraryInputOrigin(path), e);
}
});
});
@@ -289,9 +313,8 @@
void validate() {}
// Helper to signify an error.
- void error(String baseMessage, Path path, Throwable throwable) {
- reporter.error(new StringDiagnostic(
- baseMessage + throwable.getMessage(), new PathOrigin(path)), throwable);
+ void error(Origin origin, Throwable throwable) {
+ reporter.error(new ExceptionDiagnostic(throwable, origin));
}
// Helper to guard and handle exceptions.
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 1bf77b1..e393f32 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -119,20 +119,21 @@
private boolean disableDesugaring = false;
private boolean optimizeMultidexForLinearAlloc = false;
- Builder() {}
+ abstract CompilationMode defaultCompilationMode();
+
+ Builder() {
+ mode = defaultCompilationMode();
+ }
Builder(DiagnosticsHandler diagnosticsHandler) {
super(diagnosticsHandler);
+ mode = defaultCompilationMode();
}
// Internal constructor for testing.
Builder(AndroidApp app) {
super(AndroidApp.builder(app));
- }
-
- // Internal constructor for testing.
- Builder(AndroidApp app, DiagnosticsHandler diagnosticsHandler) {
- super(AndroidApp.builder(app), diagnosticsHandler);
+ mode = defaultCompilationMode();
}
/**
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index 0394441..6d7ad87 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.utils.ArchiveBuilder;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.DirectoryBuilder;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.io.ByteStreams;
@@ -135,7 +135,7 @@
try {
outputBuilder.close();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, outputBuilder.getOrigin()));
+ handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
}
}
diff --git a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
index f2f59a2..a6923ba 100644
--- a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
@@ -13,12 +13,17 @@
public CompatProguardCommandBuilder(
boolean forceProguardCompatibility, DiagnosticsHandler diagnosticsHandler) {
- super(forceProguardCompatibility, diagnosticsHandler);
+ super(diagnosticsHandler);
+ if (forceProguardCompatibility) {
+ internalForceProguardCompatibility();
+ }
setIgnoreDexInArchive(true);
}
public CompatProguardCommandBuilder(boolean forceProguardCompatibility) {
- super(forceProguardCompatibility);
+ if (forceProguardCompatibility) {
+ internalForceProguardCompatibility();
+ }
setIgnoreDexInArchive(true);
}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index c300bbf..bb7c37f 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -32,6 +32,13 @@
*/
public class D8Command extends BaseCompilerCommand {
+ private static class ClasspathInputOrigin extends InputFileOrigin {
+
+ public ClasspathInputOrigin(Path file) {
+ super("classpath input", file);
+ }
+ }
+
/**
* Builder for constructing a D8Command.
*
@@ -41,18 +48,14 @@
private boolean intermediate = false;
- Builder() {
- setMode(CompilationMode.DEBUG);
- }
+ private Builder() {}
- Builder(DiagnosticsHandler diagnosticsHandler) {
+ private Builder(DiagnosticsHandler diagnosticsHandler) {
super(diagnosticsHandler);
- setMode(CompilationMode.DEBUG);
}
private Builder(AndroidApp app) {
super(app);
- setMode(CompilationMode.DEBUG);
}
/** Add dex program-data. */
@@ -78,7 +81,7 @@
try {
getAppBuilder().addClasspathFile(file);
} catch (IOException e) {
- error("Error with classpath entry: ", file, e);
+ error(new ClasspathInputOrigin(file), e);
}
});
}
@@ -105,6 +108,11 @@
}
@Override
+ CompilationMode defaultCompilationMode() {
+ return CompilationMode.DEBUG;
+ }
+
+ @Override
void validate() {
Reporter reporter = getReporter();
if (getProgramConsumer() instanceof ClassFileConsumer) {
diff --git a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
index f93c338..ca86c70 100644
--- a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
@@ -8,8 +8,8 @@
import com.android.tools.r8.utils.ArchiveBuilder;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.DirectoryBuilder;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.io.ByteStreams;
@@ -156,7 +156,7 @@
try {
outputBuilder.close();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, outputBuilder.getOrigin()));
+ handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
}
}
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index 0f932d5..f26ab73 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -3,11 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.ArchiveBuilder;
import com.android.tools.r8.utils.DirectoryBuilder;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.io.ByteStreams;
@@ -126,6 +127,10 @@
}
}
+ public Origin getOrigin() {
+ return outputBuilder.getOrigin();
+ }
+
@Override
public DataResourceConsumer getDataResourceConsumer() {
return consumeDataResources ? this : null;
@@ -154,7 +159,7 @@
try {
outputBuilder.close();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, outputBuilder.getOrigin()));
+ handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
}
}
@@ -219,7 +224,7 @@
try {
prepareDirectory();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(directory)));
+ handler.error(new ExceptionDiagnostic(e, new PathOrigin(directory)));
}
outputBuilder.addFile(getDexFileName(fileIndex), data, handler);
}
@@ -240,7 +245,7 @@
try {
outputBuilder.close();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, outputBuilder.getOrigin()));
+ handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
}
}
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index ab86367..1fcd70a 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.FeatureClassMapping;
import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException;
import com.android.tools.r8.utils.InternalOptions;
@@ -33,14 +34,14 @@
public class DexSplitterHelper {
public static void run(
- D8Command command,
- FeatureClassMapping featureClassMapping,
- String output,
- String proguardMap)
- throws IOException, CompilationException, ExecutionException {
+ D8Command command, FeatureClassMapping featureClassMapping, String output, String proguardMap)
+ throws CompilationFailedException {
ExecutorService executor = ThreadUtils.getExecutorService(ThreadUtils.NOT_SPECIFIED);
try {
- run(command, featureClassMapping, output, proguardMap, executor);
+ ExceptionUtils.withCompilationHandler(
+ command.getReporter(),
+ () -> run(command, featureClassMapping, output, proguardMap, executor),
+ CompilationException::getMessage);
} finally {
executor.shutdown();
}
@@ -52,7 +53,7 @@
String output,
String proguardMap,
ExecutorService executor)
- throws IOException, CompilationException, ExecutionException {
+ throws IOException, CompilationException {
InternalOptions options = command.getInternalOptions();
options.enableDesugaring = false;
options.enableMainDexListCheck = false;
diff --git a/src/main/java/com/android/tools/r8/Disassemble.java b/src/main/java/com/android/tools/r8/Disassemble.java
index e51e4d4..4f64416 100644
--- a/src/main/java/com/android/tools/r8/Disassemble.java
+++ b/src/main/java/com/android/tools/r8/Disassemble.java
@@ -32,6 +32,7 @@
private Path outputPath = null;
private Path proguardMapFile = null;
private boolean useSmali = false;
+ private boolean allInfo = false;
@Override
Builder self() {
@@ -52,6 +53,11 @@
return this;
}
+ public Builder setAllInfo(boolean allInfo) {
+ this.allInfo = allInfo;
+ return this;
+ }
+
public Builder setUseSmali(boolean useSmali) {
this.useSmali = useSmali;
return this;
@@ -67,6 +73,7 @@
getAppBuilder().build(),
getOutputPath(),
proguardMapFile == null ? null : StringResource.fromFile(proguardMapFile),
+ allInfo,
useSmali);
}
}
@@ -75,6 +82,7 @@
"Usage: disasm [options] <input-files>",
" where <input-files> are dex files",
" and options are:",
+ " --all # Include all information in disassembly.",
" --smali # Disassemble using smali syntax.",
" --pg-map <file> # Proguard map <file> for mapping names.",
" --output # Specify a file or directory to write to.",
@@ -82,6 +90,7 @@
" --help # Print this message."));
+ private final boolean allInfo;
private final boolean useSmali;
public static Builder builder() {
@@ -103,6 +112,8 @@
builder.setPrintHelp(true);
} else if (arg.equals("--version")) {
builder.setPrintVersion(true);
+ } else if (arg.equals("--all")) {
+ builder.setAllInfo(true);
} else if (arg.equals("--smali")) {
builder.setUseSmali(true);
} else if (arg.equals("--pg-map")) {
@@ -121,10 +132,12 @@
}
private DisassembleCommand(
- AndroidApp inputApp, Path outputPath, StringResource proguardMap, boolean useSmali) {
+ AndroidApp inputApp, Path outputPath, StringResource proguardMap,
+ boolean allInfo, boolean useSmali) {
super(inputApp);
this.outputPath = outputPath;
this.proguardMap = proguardMap;
+ this.allInfo = allInfo;
this.useSmali = useSmali;
}
@@ -132,6 +145,7 @@
super(printHelp, printVersion);
outputPath = null;
proguardMap = null;
+ allInfo = false;
useSmali = false;
}
@@ -177,7 +191,7 @@
new ApplicationReader(app, options, timing).read(command.proguardMap, executor);
DexByteCodeWriter writer = command.useSmali()
? new SmaliWriter(application, options)
- : new AssemblyWriter(application, options);
+ : new AssemblyWriter(application, options, command.allInfo);
if (command.getOutputPath() != null) {
writer.write(command.getOutputPath());
} else {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 83f9dc2..919e982 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.Reporter;
@@ -65,35 +64,21 @@
private StringConsumer mainDexListConsumer = null;
- private Builder() {
- setMode(CompilationMode.RELEASE);
- }
+ // TODO(zerny): Consider refactoring CompatProguardCommandBuilder to avoid subclassing.
+ Builder() {}
- // Internal compatibility mode for use from CompatProguard tool.
- Builder(boolean forceProguardCompatibility) {
- setMode(CompilationMode.RELEASE);
- this.forceProguardCompatibility = forceProguardCompatibility;
- }
-
- Builder(boolean forceProguardCompatibility, DiagnosticsHandler diagnosticsHandler) {
+ Builder(DiagnosticsHandler diagnosticsHandler) {
super(diagnosticsHandler);
- setMode(CompilationMode.RELEASE);
- this.forceProguardCompatibility = forceProguardCompatibility;
- }
-
- private Builder(DiagnosticsHandler diagnosticsHandler) {
- super(diagnosticsHandler);
- setMode(CompilationMode.DEBUG);
}
private Builder(AndroidApp app) {
super(app);
- setMode(CompilationMode.RELEASE);
}
- private Builder(AndroidApp app, DiagnosticsHandler diagnosticsHandler) {
- super(app, diagnosticsHandler);
- setMode(CompilationMode.RELEASE);
+ // Internal
+
+ void internalForceProguardCompatibility() {
+ this.forceProguardCompatibility = true;
}
@Override
@@ -101,6 +86,11 @@
return this;
}
+ @Override
+ CompilationMode defaultCompilationMode() {
+ return CompilationMode.RELEASE;
+ }
+
/**
* Disable tree shaking.
*
@@ -278,22 +268,14 @@
@Override
R8Command makeCommand() {
- try {
- // If printing versions ignore everything else.
- if (isPrintHelp() || isPrintVersion()) {
- return new R8Command(isPrintHelp(), isPrintVersion());
- }
-
- return makeR8Command();
- } catch (IOException e) {
- throw getReporter().fatalError(new IOExceptionDiagnostic(e), e);
- } catch (CompilationException e) {
- throw getReporter().fatalError(new StringDiagnostic(e.getMessage()), e);
+ // If printing versions ignore everything else.
+ if (isPrintHelp() || isPrintVersion()) {
+ return new R8Command(isPrintHelp(), isPrintVersion());
}
+ return makeR8Command();
}
- private R8Command makeR8Command()
- throws IOException, CompilationException {
+ private R8Command makeR8Command() {
Reporter reporter = getReporter();
DexItemFactory factory = new DexItemFactory();
ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
@@ -429,7 +411,6 @@
" --help # Print this message."));
private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
- private DataResourceConsumer dataResourceConsumer;
private final StringConsumer mainDexListConsumer;
private final ProguardConfiguration proguardConfiguration;
private final boolean enableTreeShaking;
@@ -453,11 +434,6 @@
return new Builder(app);
}
- // Internal builder to start from an existing AndroidApp.
- static Builder builder(AndroidApp app, DiagnosticsHandler diagnosticsHandler) {
- return new Builder(app, diagnosticsHandler);
- }
-
/**
* Parse the R8 command-line.
*
diff --git a/src/main/java/com/android/tools/r8/StringConsumer.java b/src/main/java/com/android/tools/r8/StringConsumer.java
index 23883e9..cccb72e 100644
--- a/src/main/java/com/android/tools/r8/StringConsumer.java
+++ b/src/main/java/com/android/tools/r8/StringConsumer.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
@@ -102,7 +102,8 @@
try {
Files.write(outputPath, string.getBytes(encoding));
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(outputPath)));
+ Origin origin = new PathOrigin(outputPath);
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
}
@@ -149,7 +150,7 @@
writer.write(string);
writer.flush();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/UsageInformationConsumer.java b/src/main/java/com/android/tools/r8/UsageInformationConsumer.java
index e949276..a572908 100644
--- a/src/main/java/com/android/tools/r8/UsageInformationConsumer.java
+++ b/src/main/java/com/android/tools/r8/UsageInformationConsumer.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
@@ -89,7 +89,8 @@
try {
FileUtils.writeToFile(outputPath, null, data);
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(outputPath)));
+ Origin origin = new PathOrigin(outputPath);
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
}
@@ -125,7 +126,7 @@
try {
outputStream.write(data);
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index d7f1fa8..0712db2 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "v1.2.15-dev";
+ public static final String LABEL = "1.2.15-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 300335a..6d7ae19 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -3,11 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf;
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.cf.code.CfArrayLength;
import com.android.tools.r8.cf.code.CfArrayLoad;
import com.android.tools.r8.cf.code.CfArrayStore;
-import com.android.tools.r8.cf.code.CfBinop;
import com.android.tools.r8.cf.code.CfCheckCast;
+import com.android.tools.r8.cf.code.CfCmp;
import com.android.tools.r8.cf.code.CfConstClass;
import com.android.tools.r8.cf.code.CfConstMethodHandle;
import com.android.tools.r8.cf.code.CfConstMethodType;
@@ -27,11 +28,14 @@
import com.android.tools.r8.cf.code.CfInvokeDynamic;
import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
import com.android.tools.r8.cf.code.CfMonitor;
import com.android.tools.r8.cf.code.CfMultiANewArray;
+import com.android.tools.r8.cf.code.CfNeg;
import com.android.tools.r8.cf.code.CfNew;
import com.android.tools.r8.cf.code.CfNewArray;
import com.android.tools.r8.cf.code.CfNop;
+import com.android.tools.r8.cf.code.CfNumberConversion;
import com.android.tools.r8.cf.code.CfPosition;
import com.android.tools.r8.cf.code.CfReturn;
import com.android.tools.r8.cf.code.CfReturnVoid;
@@ -41,7 +45,6 @@
import com.android.tools.r8.cf.code.CfSwitch.Kind;
import com.android.tools.r8.cf.code.CfThrow;
import com.android.tools.r8.cf.code.CfTryCatch;
-import com.android.tools.r8.cf.code.CfUnop;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
@@ -230,12 +233,24 @@
print(monitor.getType() == Monitor.Type.ENTER ? "monitorenter" : "monitorexit");
}
- public void print(CfBinop binop) {
- print(opcodeName(binop.getOpcode()));
+ public void print(CfArithmeticBinop arithmeticBinop) {
+ print(opcodeName(arithmeticBinop.getAsmOpcode()));
}
- public void print(CfUnop unop) {
- print(opcodeName(unop.getOpcode()));
+ public void print(CfCmp cmp) {
+ print(opcodeName(cmp.getAsmOpcode()));
+ }
+
+ public void print(CfLogicalBinop logicalBinop) {
+ print(opcodeName(logicalBinop.getAsmOpcode()));
+ }
+
+ public void print(CfNeg neg) {
+ print(opcodeName(neg.getAsmOpcode()));
+ }
+
+ public void print(CfNumberConversion numberConversion) {
+ print(opcodeName(numberConversion.getAsmOpcode()));
}
public void print(CfConstString constString) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
new file mode 100644
index 0000000..423bba2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
@@ -0,0 +1,119 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfArithmeticBinop extends CfInstruction {
+
+ public enum Opcode {
+ Add,
+ Sub,
+ Mul,
+ Div,
+ Rem,
+ }
+
+ private final Opcode opcode;
+ private final NumericType type;
+
+ public CfArithmeticBinop(Opcode opcode, NumericType type) {
+ assert opcode != null;
+ assert type != null;
+ this.opcode = opcode;
+ this.type = type;
+ }
+
+ public static CfArithmeticBinop fromAsm(int opcode) {
+ switch (opcode) {
+ case Opcodes.IADD:
+ return new CfArithmeticBinop(Opcode.Add, NumericType.INT);
+ case Opcodes.LADD:
+ return new CfArithmeticBinop(Opcode.Add, NumericType.LONG);
+ case Opcodes.FADD:
+ return new CfArithmeticBinop(Opcode.Add, NumericType.FLOAT);
+ case Opcodes.DADD:
+ return new CfArithmeticBinop(Opcode.Add, NumericType.DOUBLE);
+ case Opcodes.ISUB:
+ return new CfArithmeticBinop(Opcode.Sub, NumericType.INT);
+ case Opcodes.LSUB:
+ return new CfArithmeticBinop(Opcode.Sub, NumericType.LONG);
+ case Opcodes.FSUB:
+ return new CfArithmeticBinop(Opcode.Sub, NumericType.FLOAT);
+ case Opcodes.DSUB:
+ return new CfArithmeticBinop(Opcode.Sub, NumericType.DOUBLE);
+ case Opcodes.IMUL:
+ return new CfArithmeticBinop(Opcode.Mul, NumericType.INT);
+ case Opcodes.LMUL:
+ return new CfArithmeticBinop(Opcode.Mul, NumericType.LONG);
+ case Opcodes.FMUL:
+ return new CfArithmeticBinop(Opcode.Mul, NumericType.FLOAT);
+ case Opcodes.DMUL:
+ return new CfArithmeticBinop(Opcode.Mul, NumericType.DOUBLE);
+ case Opcodes.IDIV:
+ return new CfArithmeticBinop(Opcode.Div, NumericType.INT);
+ case Opcodes.LDIV:
+ return new CfArithmeticBinop(Opcode.Div, NumericType.LONG);
+ case Opcodes.FDIV:
+ return new CfArithmeticBinop(Opcode.Div, NumericType.FLOAT);
+ case Opcodes.DDIV:
+ return new CfArithmeticBinop(Opcode.Div, NumericType.DOUBLE);
+ case Opcodes.IREM:
+ return new CfArithmeticBinop(Opcode.Rem, NumericType.INT);
+ case Opcodes.LREM:
+ return new CfArithmeticBinop(Opcode.Rem, NumericType.LONG);
+ case Opcodes.FREM:
+ return new CfArithmeticBinop(Opcode.Rem, NumericType.FLOAT);
+ case Opcodes.DREM:
+ return new CfArithmeticBinop(Opcode.Rem, NumericType.DOUBLE);
+ default:
+ throw new Unreachable("Wrong ASM opcode for CfArithmeticBinop " + opcode);
+ }
+ }
+
+ public int getAsmOpcode() {
+ switch (opcode) {
+ case Add:
+ return Opcodes.IADD + getAsmOpcodeTypeOffset();
+ case Sub:
+ return Opcodes.ISUB + getAsmOpcodeTypeOffset();
+ case Mul:
+ return Opcodes.IMUL + getAsmOpcodeTypeOffset();
+ case Div:
+ return Opcodes.IDIV + getAsmOpcodeTypeOffset();
+ case Rem:
+ return Opcodes.IREM + getAsmOpcodeTypeOffset();
+ default:
+ throw new Unreachable("CfArithmeticBinop has unknown opcode " + opcode);
+ }
+ }
+
+ private int getAsmOpcodeTypeOffset() {
+ switch (type) {
+ case LONG:
+ return Opcodes.LADD - Opcodes.IADD;
+ case FLOAT:
+ return Opcodes.FADD - Opcodes.IADD;
+ case DOUBLE:
+ return Opcodes.DADD - Opcodes.IADD;
+ default:
+ return 0;
+ }
+ }
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+
+ @Override
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ visitor.visitInsn(getAsmOpcode());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfBinop.java
deleted file mode 100644
index 6f8e868..0000000
--- a/src/main/java/com/android/tools/r8/cf/code/CfBinop.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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.cf.code;
-
-import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.naming.NamingLens;
-import org.objectweb.asm.MethodVisitor;
-
-public class CfBinop extends CfInstruction {
-
- private final int opcode;
-
- public CfBinop(int opcode) {
- this.opcode = opcode;
- }
-
- public int getOpcode() {
- return opcode;
- }
-
- @Override
- public void write(MethodVisitor visitor, NamingLens lens) {
- visitor.visitInsn(opcode);
- }
-
- @Override
- public void print(CfPrinter printer) {
- printer.print(this);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
new file mode 100644
index 0000000..9c6d3d7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.Cmp;
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfCmp extends CfInstruction {
+
+ private final Bias bias;
+ private final NumericType type;
+
+ public CfCmp(Bias bias, NumericType type) {
+ assert bias != null;
+ assert type != null;
+ assert type == NumericType.LONG || type == NumericType.FLOAT || type == NumericType.DOUBLE;
+ assert type != NumericType.LONG || bias == Cmp.Bias.NONE;
+ assert type == NumericType.LONG || bias != Cmp.Bias.NONE;
+ this.bias = bias;
+ this.type = type;
+ }
+
+ public static CfCmp fromAsm(int opcode) {
+ switch (opcode) {
+ case Opcodes.LCMP:
+ return new CfCmp(Bias.NONE, NumericType.LONG);
+ case Opcodes.FCMPL:
+ return new CfCmp(Bias.LT, NumericType.FLOAT);
+ case Opcodes.FCMPG:
+ return new CfCmp(Bias.GT, NumericType.FLOAT);
+ case Opcodes.DCMPL:
+ return new CfCmp(Bias.LT, NumericType.DOUBLE);
+ case Opcodes.DCMPG:
+ return new CfCmp(Bias.GT, NumericType.DOUBLE);
+ default:
+ throw new Unreachable("Wrong ASM opcode for CfCmp " + opcode);
+ }
+ }
+
+ public int getAsmOpcode() {
+ switch (type) {
+ case LONG:
+ return Opcodes.LCMP;
+ case FLOAT:
+ return bias == Cmp.Bias.LT ? Opcodes.FCMPL : Opcodes.FCMPG;
+ case DOUBLE:
+ return bias == Cmp.Bias.LT ? Opcodes.DCMPL : Opcodes.DCMPG;
+ default:
+ throw new Unreachable("CfCmp has unknown type " + type);
+ }
+ }
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+
+ @Override
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ visitor.visitInsn(getAsmOpcode());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
new file mode 100644
index 0000000..2aab288
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfLogicalBinop extends CfInstruction {
+
+ public enum Opcode {
+ Shl,
+ Shr,
+ Ushr,
+ And,
+ Or,
+ Xor,
+ }
+
+ private final Opcode opcode;
+ private final NumericType type;
+
+ public CfLogicalBinop(Opcode opcode, NumericType type) {
+ assert opcode != null;
+ assert type != null;
+ assert type != NumericType.FLOAT && type != NumericType.DOUBLE;
+ this.opcode = opcode;
+ this.type = type;
+ }
+
+ public static CfLogicalBinop fromAsm(int opcode) {
+ switch (opcode) {
+ case Opcodes.ISHL:
+ return new CfLogicalBinop(Opcode.Shl, NumericType.INT);
+ case Opcodes.LSHL:
+ return new CfLogicalBinop(Opcode.Shl, NumericType.LONG);
+ case Opcodes.ISHR:
+ return new CfLogicalBinop(Opcode.Shr, NumericType.INT);
+ case Opcodes.LSHR:
+ return new CfLogicalBinop(Opcode.Shr, NumericType.LONG);
+ case Opcodes.IUSHR:
+ return new CfLogicalBinop(Opcode.Ushr, NumericType.INT);
+ case Opcodes.LUSHR:
+ return new CfLogicalBinop(Opcode.Ushr, NumericType.LONG);
+ case Opcodes.IAND:
+ return new CfLogicalBinop(Opcode.And, NumericType.INT);
+ case Opcodes.LAND:
+ return new CfLogicalBinop(Opcode.And, NumericType.LONG);
+ case Opcodes.IOR:
+ return new CfLogicalBinop(Opcode.Or, NumericType.INT);
+ case Opcodes.LOR:
+ return new CfLogicalBinop(Opcode.Or, NumericType.LONG);
+ case Opcodes.IXOR:
+ return new CfLogicalBinop(Opcode.Xor, NumericType.INT);
+ case Opcodes.LXOR:
+ return new CfLogicalBinop(Opcode.Xor, NumericType.LONG);
+ default:
+ throw new Unreachable("Wrong ASM opcode for CfLogicalBinop " + opcode);
+ }
+ }
+
+ public int getAsmOpcode() {
+ switch (opcode) {
+ case Shl:
+ return type.isWide() ? Opcodes.LSHL : Opcodes.ISHL;
+ case Shr:
+ return type.isWide() ? Opcodes.LSHR : Opcodes.ISHR;
+ case Ushr:
+ return type.isWide() ? Opcodes.LUSHR : Opcodes.IUSHR;
+ case And:
+ return type.isWide() ? Opcodes.LAND : Opcodes.IAND;
+ case Or:
+ return type.isWide() ? Opcodes.LOR : Opcodes.IOR;
+ case Xor:
+ return type.isWide() ? Opcodes.LXOR : Opcodes.IXOR;
+ default:
+ throw new Unreachable("CfLogicalBinop has unknown opcode " + opcode);
+ }
+ }
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+
+ @Override
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ visitor.visitInsn(getAsmOpcode());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
new file mode 100644
index 0000000..db0019a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
@@ -0,0 +1,63 @@
+// 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.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfNeg extends CfInstruction {
+
+ private final NumericType type;
+
+ public CfNeg(NumericType type) {
+ this.type = type;
+ }
+
+ @Override
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ visitor.visitInsn(getAsmOpcode());
+ }
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+
+ public int getAsmOpcode() {
+ switch (type) {
+ case BYTE:
+ case CHAR:
+ case SHORT:
+ case INT:
+ return Opcodes.INEG;
+ case LONG:
+ return Opcodes.LNEG;
+ case FLOAT:
+ return Opcodes.FNEG;
+ case DOUBLE:
+ return Opcodes.DNEG;
+ default:
+ throw new Unreachable("Invalid type for CfNeg " + type);
+ }
+ }
+
+ public static CfNeg fromAsm(int opcode) {
+ switch (opcode) {
+ case Opcodes.INEG:
+ return new CfNeg(NumericType.INT);
+ case Opcodes.LNEG:
+ return new CfNeg(NumericType.LONG);
+ case Opcodes.FNEG:
+ return new CfNeg(NumericType.FLOAT);
+ case Opcodes.DNEG:
+ return new CfNeg(NumericType.DOUBLE);
+ default:
+ throw new Unreachable("Invalid opcode for CfNeg " + opcode);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
new file mode 100644
index 0000000..b585cb6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
@@ -0,0 +1,130 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfNumberConversion extends CfInstruction {
+
+ private final NumericType from;
+ private final NumericType to;
+
+ public CfNumberConversion(NumericType from, NumericType to) {
+ assert from != to;
+ assert from != NumericType.BYTE && from != NumericType.SHORT && from != NumericType.CHAR;
+ assert (to != NumericType.BYTE && to != NumericType.SHORT && to != NumericType.CHAR)
+ || from == NumericType.INT;
+ this.from = from;
+ this.to = to;
+ }
+
+ @Override
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ visitor.visitInsn(this.getAsmOpcode());
+ }
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+
+ public int getAsmOpcode() {
+ switch (from) {
+ case INT:
+ switch (to) {
+ case BYTE:
+ return Opcodes.I2B;
+ case CHAR:
+ return Opcodes.I2C;
+ case SHORT:
+ return Opcodes.I2S;
+ case LONG:
+ return Opcodes.I2L;
+ case FLOAT:
+ return Opcodes.I2F;
+ case DOUBLE:
+ return Opcodes.I2D;
+ default:
+ throw new Unreachable("Invalid CfNumberConversion from " + from + " to " + to);
+ }
+ case LONG:
+ switch (to) {
+ case INT:
+ return Opcodes.L2I;
+ case FLOAT:
+ return Opcodes.L2F;
+ case DOUBLE:
+ return Opcodes.L2D;
+ default:
+ throw new Unreachable("Invalid CfNumberConversion from " + from + " to " + to);
+ }
+ case FLOAT:
+ switch (to) {
+ case INT:
+ return Opcodes.F2I;
+ case LONG:
+ return Opcodes.F2L;
+ case DOUBLE:
+ return Opcodes.F2D;
+ default:
+ throw new Unreachable("Invalid CfNumberConversion from " + from + " to " + to);
+ }
+ case DOUBLE:
+ switch (to) {
+ case INT:
+ return Opcodes.D2I;
+ case LONG:
+ return Opcodes.D2L;
+ case FLOAT:
+ return Opcodes.D2F;
+ default:
+ throw new Unreachable("Invalid CfNumberConversion from " + from + " to " + to);
+ }
+ default:
+ throw new Unreachable("Invalid CfNumberConversion from " + from + " to " + to);
+ }
+ }
+
+ public static CfNumberConversion fromAsm(int opcode) {
+ switch (opcode) {
+ case Opcodes.I2L:
+ return new CfNumberConversion(NumericType.INT, NumericType.LONG);
+ case Opcodes.I2F:
+ return new CfNumberConversion(NumericType.INT, NumericType.FLOAT);
+ case Opcodes.I2D:
+ return new CfNumberConversion(NumericType.INT, NumericType.DOUBLE);
+ case Opcodes.L2I:
+ return new CfNumberConversion(NumericType.LONG, NumericType.INT);
+ case Opcodes.L2F:
+ return new CfNumberConversion(NumericType.LONG, NumericType.FLOAT);
+ case Opcodes.L2D:
+ return new CfNumberConversion(NumericType.LONG, NumericType.DOUBLE);
+ case Opcodes.F2I:
+ return new CfNumberConversion(NumericType.FLOAT, NumericType.INT);
+ case Opcodes.F2L:
+ return new CfNumberConversion(NumericType.FLOAT, NumericType.LONG);
+ case Opcodes.F2D:
+ return new CfNumberConversion(NumericType.FLOAT, NumericType.DOUBLE);
+ case Opcodes.D2I:
+ return new CfNumberConversion(NumericType.DOUBLE, NumericType.INT);
+ case Opcodes.D2L:
+ return new CfNumberConversion(NumericType.DOUBLE, NumericType.LONG);
+ case Opcodes.D2F:
+ return new CfNumberConversion(NumericType.DOUBLE, NumericType.FLOAT);
+ case Opcodes.I2B:
+ return new CfNumberConversion(NumericType.INT, NumericType.BYTE);
+ case Opcodes.I2C:
+ return new CfNumberConversion(NumericType.INT, NumericType.CHAR);
+ case Opcodes.I2S:
+ return new CfNumberConversion(NumericType.INT, NumericType.SHORT);
+ default:
+ throw new Unreachable("Unexpected CfNumberConversion opcode " + opcode);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfUnop.java b/src/main/java/com/android/tools/r8/cf/code/CfUnop.java
deleted file mode 100644
index 6b4bf76..0000000
--- a/src/main/java/com/android/tools/r8/cf/code/CfUnop.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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.cf.code;
-
-import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.naming.NamingLens;
-import org.objectweb.asm.MethodVisitor;
-
-public class CfUnop extends CfInstruction {
-
- private final int opcode;
-
- public CfUnop(int opcode) {
- this.opcode = opcode;
- }
-
- @Override
- public void write(MethodVisitor visitor, NamingLens lens) {
- visitor.visitInsn(this.opcode);
- }
-
- @Override
- public void print(CfPrinter printer) {
- printer.print(this);
- }
-
- public int getOpcode() {
- return opcode;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
index 1de9606..b9131e5 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -24,9 +24,10 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
@@ -547,7 +548,7 @@
StandardOpenOption.TRUNCATE_EXISTING,
StandardOpenOption.WRITE);
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e));
+ handler.error(new ExceptionDiagnostic(e, new PathOrigin(output)));
}
}
}
@@ -566,7 +567,7 @@
try {
writeZipWithClasses(handler);
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e));
+ handler.error(new ExceptionDiagnostic(e, getOrigin()));
}
super.finished(handler);
}
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 4524505..8042a1a 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -649,7 +649,7 @@
ClassAccessFlags flags = ClassAccessFlags.fromDexAccessFlags(accessFlags[i]);
// Check if constraints from
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1 are met.
- if (!flags.areValid(Constants.CORRESPONDING_CLASS_FILE_VERSION)) {
+ if (!flags.areValid(Constants.CORRESPONDING_CLASS_FILE_VERSION, false)) {
throw new CompilationError("Class " + type.toSourceString()
+ " has illegal access flags. Found: " + flags, origin);
}
diff --git a/src/main/java/com/android/tools/r8/dex/DexReader.java b/src/main/java/com/android/tools/r8/dex/DexReader.java
index 44bacc6..d8997f7 100644
--- a/src/main/java/com/android/tools/r8/dex/DexReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexReader.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.DexVersion;
import java.io.IOException;
+import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -38,6 +39,12 @@
// Parse the magic header and determine the dex file version.
private int parseMagic(ByteBuffer buffer) {
+ try {
+ buffer.get();
+ buffer.rewind();
+ } catch (BufferUnderflowException e) {
+ throw new CompilationError("Dex file is empty", origin);
+ }
int index = 0;
for (byte prefixByte : DEX_FILE_MAGIC_PREFIX) {
if (buffer.get(index++) != prefixByte) {
diff --git a/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java b/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
index 59aecfe..ba576e7 100644
--- a/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
+++ b/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
@@ -12,8 +12,8 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
import com.android.tools.r8.utils.OptionsParsing;
import com.android.tools.r8.utils.OptionsParsing.ParseContext;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -233,7 +233,7 @@
Files.newOutputStream(
path, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
return stream;
@@ -246,7 +246,7 @@
getStream(handler), getDexFileName(fileIndex), data, ZipEntry.DEFLATED, true);
hasWrittenSomething = true;
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
@@ -264,7 +264,7 @@
stream = null;
}
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
index b2fe1bc..f452cf1 100644
--- a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
+++ b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
@@ -4,23 +4,25 @@
package com.android.tools.r8.dexsplitter;
-import com.android.tools.r8.CompilationException;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.D8Command;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DexSplitterHelper;
-import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.utils.AbortException;
+import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
+import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.FeatureClassMapping;
import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException;
import com.android.tools.r8.utils.OptionsParsing;
import com.android.tools.r8.utils.OptionsParsing.ParseContext;
+import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableList;
-import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ExecutionException;
public class DexSplitter {
@@ -29,6 +31,37 @@
private static final boolean PRINT_ARGS = false;
+ public static class Reporter implements DiagnosticsHandler {
+
+ private final DiagnosticsHandler handler = new DefaultDiagnosticsHandler();
+ private boolean reportedErrors = false;
+
+ public RuntimeException fatal(Diagnostic error) {
+ error(error);
+ throw new AbortException();
+ }
+
+ public boolean hasReportedErrors() {
+ return reportedErrors;
+ }
+
+ @Override
+ public void error(Diagnostic error) {
+ handler.error(error);
+ reportedErrors = true;
+ }
+
+ @Override
+ public void warning(Diagnostic warning) {
+ handler.warning(warning);
+ }
+
+ @Override
+ public void info(Diagnostic info) {
+ handler.info(info);
+ }
+ }
+
public static class FeatureJar {
private String jar;
private String outputName;
@@ -62,6 +95,7 @@
}
public static class Options {
+ private final Reporter reporter = new Reporter();
private List<String> inputArchives = new ArrayList<>();
private List<FeatureJar> featureJars = new ArrayList<>();
private String baseOutputName = DEFAULT_BASE_NAME;
@@ -69,6 +103,10 @@
private String featureSplitMapping;
private String proguardMap;
+ public Reporter getReporter() {
+ return reporter;
+ }
+
public String getOutput() {
return output;
}
@@ -124,6 +162,11 @@
public ImmutableList<FeatureJar> getFeatureJars() {
return ImmutableList.copyOf(featureJars);
}
+
+ // Shorthand error messages.
+ public void error(String msg) {
+ reporter.error(new StringDiagnostic(msg));
+ }
}
/**
@@ -144,7 +187,7 @@
return new FeatureJar(argument);
}
- private static Options parseArguments(String[] args) throws IOException {
+ private static Options parseArguments(String[] args) {
Options options = new Options();
ParseContext context = new ParseContext(args);
while (context.head() != null) {
@@ -185,32 +228,35 @@
}
private static FeatureClassMapping createFeatureClassMapping(Options options)
- throws IOException, FeatureMappingException, ResourceException {
+ throws FeatureMappingException {
if (options.getFeatureSplitMapping() != null) {
- return FeatureClassMapping.fromSpecification(Paths.get(options.getFeatureSplitMapping()));
+ return FeatureClassMapping.fromSpecification(
+ Paths.get(options.getFeatureSplitMapping()), options.getReporter());
}
assert !options.getFeatureJars().isEmpty();
- return FeatureClassMapping.fromJarFiles(options.getFeatureJars(), options.getBaseOutputName());
+ return FeatureClassMapping.fromJarFiles(
+ options.getFeatureJars(), options.getBaseOutputName(), options.getReporter());
}
private static void run(String[] args)
- throws CompilationFailedException, IOException, CompilationException, ExecutionException,
- ResourceException, FeatureMappingException {
+ throws CompilationFailedException, FeatureMappingException {
Options options = parseArguments(args);
run(options);
}
public static void run(Options options)
- throws IOException, FeatureMappingException, ResourceException, CompilationException,
- ExecutionException, CompilationFailedException {
+ throws FeatureMappingException, CompilationFailedException {
if (options.getInputArchives().isEmpty()) {
- throw new RuntimeException("Need at least one --input");
+ options.error("Need at least one --input");
}
if (options.getFeatureSplitMapping() == null && options.getFeatureJars().isEmpty()) {
- throw new RuntimeException("You must supply a feature split mapping or feature jars");
+ options.error("You must supply a feature split mapping or feature jars");
}
if (options.getFeatureSplitMapping() != null && !options.getFeatureJars().isEmpty()) {
- throw new RuntimeException("You can't supply both a feature split mapping and feature jars");
+ options.error("You can't supply both a feature split mapping and feature jars");
+ }
+ if (options.getReporter().hasReportedErrors()) {
+ throw new AbortException();
}
D8Command.Builder builder = D8Command.builder();
@@ -228,20 +274,19 @@
}
public static void main(String[] args) {
- try {
- if (PRINT_ARGS) {
- printArgs(args);
- }
- run(args);
- } catch (CompilationFailedException
- | IOException
- | CompilationException
- | ExecutionException
- | ResourceException
- | FeatureMappingException e) {
- System.err.println("Splitting failed: " + e.getMessage());
- System.exit(1);
+ if (PRINT_ARGS) {
+ printArgs(args);
}
+ ExceptionUtils.withMainProgramHandler(
+ () -> {
+ try {
+ run(args);
+ } catch (FeatureMappingException e) {
+ // TODO(ricow): Report feature mapping errors via the reporter.
+ System.err.println("Splitting failed: " + e.getMessage());
+ System.exit(1);
+ }
+ });
}
private static void printArgs(String[] args) {
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
index 50ff578..63a2958 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -4,13 +4,21 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.utils.InternalOptions;
import java.io.PrintStream;
public class AssemblyWriter extends DexByteCodeWriter {
- public AssemblyWriter(DexApplication application, InternalOptions options) {
+ private final boolean writeAllClassInfo;
+ private final boolean writeFields;
+ private final boolean writeAnnotations;
+
+ public AssemblyWriter(DexApplication application, InternalOptions options, boolean allInfo) {
super(application, options);
+ this.writeAllClassInfo = allInfo;
+ this.writeFields = allInfo;
+ this.writeAnnotations = allInfo;
}
@Override
@@ -28,12 +36,43 @@
}
ps.println("# Bytecode for");
ps.println("# Class: '" + clazzName + "'");
+ if (writeAllClassInfo) {
+ writeAnnotations(clazz.annotations, ps);
+ ps.println("# Flags: '" + clazz.accessFlags + "'");
+ if (clazz.superType != application.dexItemFactory.objectType) {
+ ps.println("# Extends: '" + clazz.superType.toSourceString() + "'");
+ }
+ for (DexType value : clazz.interfaces.values) {
+ ps.println("# Implements: '" + value.toSourceString() + "'");
+ }
+ }
ps.println();
}
@Override
+ void writeFieldsHeader(DexProgramClass clazz, PrintStream ps) {
+ if (writeFields) {
+ ps.println("#");
+ ps.println("# Fields:");
+ ps.println("#");
+ }
+ }
+
+ @Override
void writeField(DexEncodedField field, PrintStream ps) {
- // Not implemented, yet.
+ if (writeFields) {
+ ClassNameMapper naming = application.getProguardMap();
+ FieldSignature fieldSignature = naming != null
+ ? naming.originalSignatureOf(field.field)
+ : FieldSignature.fromDexField(field.field);
+ writeAnnotations(field.annotations, ps);
+ ps.println(fieldSignature);
+ }
+ }
+
+ @Override
+ void writeFieldsFooter(DexProgramClass clazz, PrintStream ps) {
+ ps.println();
}
@Override
@@ -44,6 +83,7 @@
: method.method.name.toString();
ps.println("#");
ps.println("# Method: '" + methodName + "':");
+ writeAnnotations(method.annotations, ps);
ps.println("#");
ps.println();
Code code = method.getCode();
@@ -52,6 +92,18 @@
}
}
+ private void writeAnnotations(DexAnnotationSet annotations, PrintStream ps) {
+ if (writeAnnotations) {
+ if (!annotations.isEmpty()) {
+ ps.println("# Annotations:");
+ for (DexAnnotation annotation : annotations.annotations) {
+ ps.print("# ");
+ ps.println(annotation);
+ }
+ }
+ }
+ }
+
@Override
void writeClassFooter(DexProgramClass clazz, PrintStream ps) {
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
index 0111aa1..44d6726 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
@@ -92,15 +92,17 @@
* Checks whether the constraints from
* https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1 are met.
*/
- public boolean areValid(int majorVersion) {
+ public boolean areValid(int majorVersion, boolean isPackageInfo) {
if (isInterface()) {
// We ignore the super flags prior to JDK 9, as so did the VM.
if ((majorVersion >= 53) && isSuper()) {
return false;
}
- // We require interfaces to be abstract from JDK 7 onwards. Old versions of javac seem to
- // have produced package-info classes that are interfaces but not abstract.
- if ((majorVersion >= 51) && (!isAbstract())) {
+ // When not coming from DEX input we require interfaces to be abstract - except for
+ // package-info classes - as both old versions of javac and other tools can produce
+ // package-info classes that are interfaces but not abstract.
+ if (((majorVersion > Constants.CORRESPONDING_CLASS_FILE_VERSION) && !isAbstract())
+ && !isPackageInfo) {
return false;
}
return !isFinal() && !isEnum();
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 52217a3..08d65f0 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -60,6 +60,10 @@
throw new Unreachable(getClass().getCanonicalName() + ".asCfCode()");
}
+ public LazyCfCode asLazyCfCode() {
+ throw new Unreachable(getClass().getCanonicalName() + ".asLazyCfCode()");
+ }
+
public DexCode asDexCode() {
throw new Unreachable(getClass().getCanonicalName() + ".asDexCode()");
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
index 025a502..21900a01 100644
--- a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
@@ -82,8 +82,12 @@
private void writeClass(DexProgramClass clazz, PrintStream ps) {
writeClassHeader(clazz, ps);
+ writeFieldsHeader(clazz, ps);
clazz.forEachField(field -> writeField(field, ps));
+ writeFieldsFooter(clazz, ps);
+ writeMethodsHeader(clazz, ps);
clazz.forEachMethod(method -> writeMethod(method, ps));
+ writeMethodsFooter(clazz, ps);
writeClassFooter(clazz, ps);
}
@@ -91,9 +95,25 @@
abstract void writeClassHeader(DexProgramClass clazz, PrintStream ps);
+ void writeFieldsHeader(DexProgramClass clazz, PrintStream ps) {
+ // Do nothing.
+ }
+
abstract void writeField(DexEncodedField field, PrintStream ps);
+ void writeFieldsFooter(DexProgramClass clazz, PrintStream ps) {
+ // Do nothing.
+ }
+
+ void writeMethodsHeader(DexProgramClass clazz, PrintStream ps) {
+ // Do nothing.
+ }
+
abstract void writeMethod(DexEncodedMethod method, PrintStream ps);
+ void writeMethodsFooter(DexProgramClass clazz, PrintStream ps) {
+ // Do nothing.
+ }
+
abstract void writeClassFooter(DexProgramClass clazz, PrintStream ps);
}
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 bef2f57..2f1d3fd 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -3,54 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import static org.objectweb.asm.ClassReader.EXPAND_FRAMES;
import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
import static org.objectweb.asm.Opcodes.ACC_DEPRECATED;
import static org.objectweb.asm.Opcodes.ASM6;
import com.android.tools.r8.ProgramResource.Kind;
-import com.android.tools.r8.cf.code.CfArrayLength;
-import com.android.tools.r8.cf.code.CfArrayLoad;
-import com.android.tools.r8.cf.code.CfArrayStore;
-import com.android.tools.r8.cf.code.CfBinop;
-import com.android.tools.r8.cf.code.CfCheckCast;
-import com.android.tools.r8.cf.code.CfConstClass;
-import com.android.tools.r8.cf.code.CfConstMethodHandle;
-import com.android.tools.r8.cf.code.CfConstMethodType;
-import com.android.tools.r8.cf.code.CfConstNull;
-import com.android.tools.r8.cf.code.CfConstNumber;
-import com.android.tools.r8.cf.code.CfConstString;
-import com.android.tools.r8.cf.code.CfFieldInstruction;
-import com.android.tools.r8.cf.code.CfFrame;
-import com.android.tools.r8.cf.code.CfFrame.FrameType;
-import com.android.tools.r8.cf.code.CfGoto;
-import com.android.tools.r8.cf.code.CfIf;
-import com.android.tools.r8.cf.code.CfIfCmp;
-import com.android.tools.r8.cf.code.CfIinc;
-import com.android.tools.r8.cf.code.CfInstanceOf;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfInvokeDynamic;
-import com.android.tools.r8.cf.code.CfLabel;
-import com.android.tools.r8.cf.code.CfLoad;
-import com.android.tools.r8.cf.code.CfMonitor;
-import com.android.tools.r8.cf.code.CfMultiANewArray;
-import com.android.tools.r8.cf.code.CfNew;
-import com.android.tools.r8.cf.code.CfNewArray;
-import com.android.tools.r8.cf.code.CfNop;
-import com.android.tools.r8.cf.code.CfPosition;
-import com.android.tools.r8.cf.code.CfReturn;
-import com.android.tools.r8.cf.code.CfReturnVoid;
-import com.android.tools.r8.cf.code.CfStackInstruction;
-import com.android.tools.r8.cf.code.CfStore;
-import com.android.tools.r8.cf.code.CfSwitch;
-import com.android.tools.r8.cf.code.CfThrow;
-import com.android.tools.r8.cf.code.CfTryCatch;
-import com.android.tools.r8.cf.code.CfUnop;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.graph.DexValue.DexValueBoolean;
@@ -65,23 +25,15 @@
import com.android.tools.r8.graph.DexValue.DexValueShort;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
-import com.android.tools.r8.graph.JarCode.ReparseContext;
-import com.android.tools.r8.ir.code.If;
-import com.android.tools.r8.ir.code.MemberType;
-import com.android.tools.r8.ir.code.Monitor;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.InternalOptions;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.IdentityHashMap;
+import java.util.Arrays;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -90,10 +42,8 @@
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.TypePath;
@@ -102,6 +52,8 @@
*/
public class JarClassFileReader {
+ private static final byte[] CLASSFILE_HEADER = ByteBuffer.allocate(4).putInt(0xCAFEBABE).array();
+
// Hidden ASM "synthetic attribute" bit we need to clear.
private static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000;
// Descriptor used by ASM for missing annotations.
@@ -117,13 +69,28 @@
}
public void read(Origin origin, ClassKind classKind, InputStream input) throws IOException {
- ClassReader reader = new ClassReader(input);
- int flags = SKIP_FRAMES;
- if (application.options.enableCfFrontend) {
- flags = EXPAND_FRAMES;
+ if (!input.markSupported()) {
+ input = new BufferedInputStream(input);
}
+ byte[] header = new byte[CLASSFILE_HEADER.length];
+ input.mark(header.length);
+ int size = 0;
+ while (size < header.length) {
+ int read = input.read(header, size, header.length - size);
+ if (read < 0) {
+ throw new CompilationError("Invalid empty classfile", origin);
+ }
+ size += read;
+ }
+ if (!Arrays.equals(CLASSFILE_HEADER, header)) {
+ throw new CompilationError("Invalid classfile header", origin);
+ }
+ input.reset();
+
+ ClassReader reader = new ClassReader(input);
reader.accept(
- new CreateDexClassVisitor(origin, classKind, reader.b, application, classConsumer), flags);
+ new CreateDexClassVisitor(origin, classKind, reader.b, application, classConsumer),
+ SKIP_FRAMES);
}
private static int cleanAccessFlags(int access) {
@@ -218,6 +185,24 @@
: new EnclosingMethodAttribute(application.getMethod(ownerType, name, desc));
}
+ private String illegalClassFilePrefix(ClassAccessFlags accessFlags, String name) {
+ return "Illegal class file: "
+ + (accessFlags.isInterface() ? "Interface" : "Class")
+ + " "
+ + name;
+ }
+
+ private String illegalClassFilePostfix(int version) {
+ return "Class file version " + version;
+ }
+
+ private String illegalClassFileMessage(
+ ClassAccessFlags accessFlags, String name, int version, String message) {
+ return illegalClassFilePrefix(accessFlags, name)
+ + " " + message
+ + ". " + illegalClassFilePostfix(version) + ".";
+ }
+
@Override
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
@@ -226,18 +211,22 @@
type = application.getTypeFromName(name);
// Check if constraints from
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1 are met.
- if (!accessFlags.areValid(getMajorVersion())) {
- throw new CompilationError("Illegal class file: Class " + name
- + " has invalid access flags. Found: " + accessFlags.toString(), origin);
+ if (!accessFlags.areValid(getMajorVersion(), name.endsWith("/package-info"))) {
+ throw new CompilationError(
+ illegalClassFileMessage(accessFlags, name, version,
+ "has invalid access flags. Found: " + accessFlags.toString()), origin);
}
if (superName == null && !name.equals(Constants.JAVA_LANG_OBJECT_NAME)) {
- throw new CompilationError("Illegal class file: Class " + name
- + " is missing a super type.", origin);
+ throw new CompilationError(
+ illegalClassFileMessage(accessFlags, name, version,
+ "is missing a super type"), origin);
}
if (accessFlags.isInterface()
&& !Objects.equals(superName, Constants.JAVA_LANG_OBJECT_NAME)) {
- throw new CompilationError("Illegal class file: Interface " + name
- + " must extend class java.lang.Object. Found: " + Objects.toString(superName), origin);
+ throw new CompilationError(
+ illegalClassFileMessage(accessFlags, name, version,
+ "must extend class java.lang.Object. Found: " + Objects.toString(superName)),
+ origin);
}
assert superName != null || name.equals(Constants.JAVA_LANG_OBJECT_NAME);
superType = superName == null ? null : application.getTypeFromName(superName);
@@ -268,9 +257,6 @@
@Override
public MethodVisitor visitMethod(
int access, String name, String desc, String signature, String[] exceptions) {
- if (application.options.enableCfFrontend) {
- return new CfCreateMethodVisitor(access, name, desc, signature, exceptions, this);
- }
return new CreateMethodVisitor(access, name, desc, signature, exceptions, this);
}
@@ -580,7 +566,11 @@
public void visitCode() {
assert !flags.isAbstract() && !flags.isNative();
if (parent.classKind == ClassKind.PROGRAM) {
- code = new JarCode(method, parent.origin, parent.context, parent.application);
+ if (parent.application.options.enableCfFrontend) {
+ code = new LazyCfCode(method, parent.origin, parent.context, parent.application);
+ } else {
+ code = new JarCode(method, parent.origin, parent.context, parent.application);
+ }
}
}
@@ -634,646 +624,6 @@
}
}
- private static class CfCreateMethodVisitor extends CreateMethodVisitor {
-
- private final JarApplicationReader application;
- private final DexItemFactory factory;
- private int maxStack;
- private int maxLocals;
- private List<CfInstruction> instructions;
- private List<CfTryCatch> tryCatchRanges;
- private List<LocalVariableInfo> localVariables;
- private Map<Label, CfLabel> labelMap;
-
- public CfCreateMethodVisitor(
- int access,
- String name,
- String desc,
- String signature,
- String[] exceptions,
- CreateDexClassVisitor parent) {
- super(access, name, desc, signature, exceptions, parent);
- this.application = parent.application;
- this.factory = application.getFactory();
- }
-
- @Override
- public void visitCode() {
- assert !flags.isAbstract() && !flags.isNative();
- maxStack = 0;
- maxLocals = 0;
- instructions = new ArrayList<>();
- tryCatchRanges = new ArrayList<>();
- localVariables = new ArrayList<>();
- labelMap = new IdentityHashMap<>();
- }
-
- @Override
- public void visitEnd() {
- if (!flags.isAbstract() && !flags.isNative() && parent.classKind == ClassKind.PROGRAM) {
- code =
- new CfCode(method, maxStack, maxLocals, instructions, tryCatchRanges, localVariables);
- }
- super.visitEnd();
- }
-
- @Override
- public void visitFrame(
- int frameType, int nLocals, Object[] localTypes, int nStack, Object[] stackTypes) {
- assert frameType == Opcodes.F_NEW;
- Int2ReferenceSortedMap<FrameType> parsedLocals = parseLocals(nLocals, localTypes);
- List<FrameType> parsedStack = parseStack(nStack, stackTypes);
- instructions.add(new CfFrame(parsedLocals, parsedStack));
- }
-
- private Int2ReferenceSortedMap<FrameType> parseLocals(int typeCount, Object[] asmTypes) {
- Int2ReferenceSortedMap<FrameType> types = new Int2ReferenceAVLTreeMap<>();
- int i = 0;
- for (int j = 0; j < typeCount; j++) {
- Object localType = asmTypes[j];
- FrameType value = getFrameType(localType);
- types.put(i++, value);
- if (value.isWide()) {
- i++;
- }
- }
- return types;
- }
-
- private List<FrameType> parseStack(int nStack, Object[] stackTypes) {
- List<FrameType> dexStack = new ArrayList<>(nStack);
- for (int i = 0; i < nStack; i++) {
- dexStack.add(getFrameType(stackTypes[i]));
- }
- return dexStack;
- }
-
- private FrameType getFrameType(Object localType) {
- if (localType instanceof Label) {
- return FrameType.uninitializedNew(getLabel((Label) localType));
- } else if (localType == Opcodes.UNINITIALIZED_THIS) {
- return FrameType.uninitializedThis();
- } else if (localType == null || localType == Opcodes.TOP) {
- return FrameType.top();
- } else {
- return FrameType.initialized(parseAsmType(localType));
- }
- }
-
- private CfLabel getLabel(Label label) {
- return labelMap.computeIfAbsent(label, l -> new CfLabel());
- }
-
- private DexType parseAsmType(Object local) {
- assert local != null && local != Opcodes.TOP;
- if (local == Opcodes.INTEGER) {
- return factory.intType;
- } else if (local == Opcodes.FLOAT) {
- return factory.floatType;
- } else if (local == Opcodes.LONG) {
- return factory.longType;
- } else if (local == Opcodes.DOUBLE) {
- return factory.doubleType;
- } else if (local == Opcodes.NULL) {
- return DexItemFactory.nullValueType;
- } else if (local instanceof String) {
- return createTypeFromInternalType((String) local);
- } else {
- throw new Unreachable("Unexpected ASM type: " + local);
- }
- }
-
- private DexType createTypeFromInternalType(String local) {
- assert local.indexOf('.') == -1;
- return factory.createType("L" + local + ";");
- }
-
- @Override
- public void visitInsn(int opcode) {
- switch (opcode) {
- case Opcodes.NOP:
- instructions.add(new CfNop());
- break;
- case Opcodes.ACONST_NULL:
- instructions.add(new CfConstNull());
- break;
- case Opcodes.ICONST_M1:
- case Opcodes.ICONST_0:
- case Opcodes.ICONST_1:
- case Opcodes.ICONST_2:
- case Opcodes.ICONST_3:
- case Opcodes.ICONST_4:
- case Opcodes.ICONST_5:
- instructions.add(new CfConstNumber(opcode - Opcodes.ICONST_0, ValueType.INT));
- break;
- case Opcodes.LCONST_0:
- case Opcodes.LCONST_1:
- instructions.add(new CfConstNumber(opcode - Opcodes.LCONST_0, ValueType.LONG));
- break;
- case Opcodes.FCONST_0:
- case Opcodes.FCONST_1:
- case Opcodes.FCONST_2:
- instructions.add(
- new CfConstNumber(
- Float.floatToRawIntBits(opcode - Opcodes.FCONST_0), ValueType.FLOAT));
- break;
- case Opcodes.DCONST_0:
- case Opcodes.DCONST_1:
- instructions.add(
- new CfConstNumber(
- Double.doubleToRawLongBits(opcode - Opcodes.DCONST_0), ValueType.DOUBLE));
- break;
- case Opcodes.IALOAD:
- case Opcodes.LALOAD:
- case Opcodes.FALOAD:
- case Opcodes.DALOAD:
- case Opcodes.AALOAD:
- case Opcodes.BALOAD:
- case Opcodes.CALOAD:
- case Opcodes.SALOAD:
- instructions.add(new CfArrayLoad(getMemberTypeForOpcode(opcode)));
- break;
- case Opcodes.IASTORE:
- case Opcodes.LASTORE:
- case Opcodes.FASTORE:
- case Opcodes.DASTORE:
- case Opcodes.AASTORE:
- case Opcodes.BASTORE:
- case Opcodes.CASTORE:
- case Opcodes.SASTORE:
- instructions.add(new CfArrayStore(getMemberTypeForOpcode(opcode)));
- break;
- case Opcodes.POP:
- case Opcodes.POP2:
- case Opcodes.DUP:
- case Opcodes.DUP_X1:
- case Opcodes.DUP_X2:
- case Opcodes.DUP2:
- case Opcodes.DUP2_X1:
- case Opcodes.DUP2_X2:
- case Opcodes.SWAP:
- instructions.add(CfStackInstruction.fromAsm(opcode));
- break;
- case Opcodes.IADD:
- case Opcodes.LADD:
- case Opcodes.FADD:
- case Opcodes.DADD:
- case Opcodes.ISUB:
- case Opcodes.LSUB:
- case Opcodes.FSUB:
- case Opcodes.DSUB:
- case Opcodes.IMUL:
- case Opcodes.LMUL:
- case Opcodes.FMUL:
- case Opcodes.DMUL:
- case Opcodes.IDIV:
- case Opcodes.LDIV:
- case Opcodes.FDIV:
- case Opcodes.DDIV:
- case Opcodes.IREM:
- case Opcodes.LREM:
- case Opcodes.FREM:
- case Opcodes.DREM:
- instructions.add(new CfBinop(opcode));
- break;
- case Opcodes.INEG:
- case Opcodes.LNEG:
- case Opcodes.FNEG:
- case Opcodes.DNEG:
- instructions.add(new CfUnop(opcode));
- break;
- case Opcodes.ISHL:
- case Opcodes.LSHL:
- case Opcodes.ISHR:
- case Opcodes.LSHR:
- case Opcodes.IUSHR:
- case Opcodes.LUSHR:
- case Opcodes.IAND:
- case Opcodes.LAND:
- case Opcodes.IOR:
- case Opcodes.LOR:
- case Opcodes.IXOR:
- case Opcodes.LXOR:
- instructions.add(new CfBinop(opcode));
- break;
- case Opcodes.I2L:
- case Opcodes.I2F:
- case Opcodes.I2D:
- case Opcodes.L2I:
- case Opcodes.L2F:
- case Opcodes.L2D:
- case Opcodes.F2I:
- case Opcodes.F2L:
- case Opcodes.F2D:
- case Opcodes.D2I:
- case Opcodes.D2L:
- case Opcodes.D2F:
- case Opcodes.I2B:
- case Opcodes.I2C:
- case Opcodes.I2S:
- instructions.add(new CfUnop(opcode));
- break;
- case Opcodes.LCMP:
- case Opcodes.FCMPL:
- case Opcodes.FCMPG:
- case Opcodes.DCMPL:
- case Opcodes.DCMPG:
- instructions.add(new CfBinop(opcode));
- break;
- case Opcodes.IRETURN:
- instructions.add(new CfReturn(ValueType.INT));
- break;
- case Opcodes.LRETURN:
- instructions.add(new CfReturn(ValueType.LONG));
- break;
- case Opcodes.FRETURN:
- instructions.add(new CfReturn(ValueType.FLOAT));
- break;
- case Opcodes.DRETURN:
- instructions.add(new CfReturn(ValueType.DOUBLE));
- break;
- case Opcodes.ARETURN:
- instructions.add(new CfReturn(ValueType.OBJECT));
- break;
- case Opcodes.RETURN:
- instructions.add(new CfReturnVoid());
- break;
- case Opcodes.ARRAYLENGTH:
- instructions.add(new CfArrayLength());
- break;
- case Opcodes.ATHROW:
- instructions.add(new CfThrow());
- break;
- case Opcodes.MONITORENTER:
- instructions.add(new CfMonitor(Monitor.Type.ENTER));
- break;
- case Opcodes.MONITOREXIT:
- instructions.add(new CfMonitor(Monitor.Type.EXIT));
- break;
- default:
- throw new Unreachable("Unknown instruction");
- }
- }
-
- private DexType opType(int opcode, DexItemFactory factory) {
- switch (opcode) {
- case Opcodes.IADD:
- case Opcodes.ISUB:
- case Opcodes.IMUL:
- case Opcodes.IDIV:
- case Opcodes.IREM:
- case Opcodes.INEG:
- case Opcodes.ISHL:
- case Opcodes.ISHR:
- case Opcodes.IUSHR:
- return factory.intType;
- case Opcodes.LADD:
- case Opcodes.LSUB:
- case Opcodes.LMUL:
- case Opcodes.LDIV:
- case Opcodes.LREM:
- case Opcodes.LNEG:
- case Opcodes.LSHL:
- case Opcodes.LSHR:
- case Opcodes.LUSHR:
- return factory.longType;
- case Opcodes.FADD:
- case Opcodes.FSUB:
- case Opcodes.FMUL:
- case Opcodes.FDIV:
- case Opcodes.FREM:
- case Opcodes.FNEG:
- return factory.floatType;
- case Opcodes.DADD:
- case Opcodes.DSUB:
- case Opcodes.DMUL:
- case Opcodes.DDIV:
- case Opcodes.DREM:
- case Opcodes.DNEG:
- return factory.doubleType;
- default:
- throw new Unreachable("Unexpected opcode " + opcode);
- }
- }
-
- private static MemberType getMemberTypeForOpcode(int opcode) {
- switch (opcode) {
- case Opcodes.IALOAD:
- case Opcodes.IASTORE:
- return MemberType.INT;
- case Opcodes.FALOAD:
- case Opcodes.FASTORE:
- return MemberType.FLOAT;
- case Opcodes.LALOAD:
- case Opcodes.LASTORE:
- return MemberType.LONG;
- case Opcodes.DALOAD:
- case Opcodes.DASTORE:
- return MemberType.DOUBLE;
- case Opcodes.AALOAD:
- case Opcodes.AASTORE:
- return MemberType.OBJECT;
- case Opcodes.BALOAD:
- case Opcodes.BASTORE:
- return MemberType.BOOLEAN; // TODO: Distinguish byte and boolean.
- case Opcodes.CALOAD:
- case Opcodes.CASTORE:
- return MemberType.CHAR;
- case Opcodes.SALOAD:
- case Opcodes.SASTORE:
- return MemberType.SHORT;
- default:
- throw new Unreachable("Unexpected array opcode " + opcode);
- }
- }
-
- @Override
- public void visitIntInsn(int opcode, int operand) {
- switch (opcode) {
- case Opcodes.SIPUSH:
- case Opcodes.BIPUSH:
- instructions.add(new CfConstNumber(operand, ValueType.INT));
- break;
- case Opcodes.NEWARRAY:
- instructions.add(
- new CfNewArray(factory.createArrayType(1, arrayTypeDesc(operand, factory))));
- break;
- default:
- throw new Unreachable("Unexpected int opcode " + opcode);
- }
- }
-
- private static DexType arrayTypeDesc(int arrayTypeCode, DexItemFactory factory) {
- switch (arrayTypeCode) {
- case Opcodes.T_BOOLEAN:
- return factory.booleanType;
- case Opcodes.T_CHAR:
- return factory.charType;
- case Opcodes.T_FLOAT:
- return factory.floatType;
- case Opcodes.T_DOUBLE:
- return factory.doubleType;
- case Opcodes.T_BYTE:
- return factory.byteType;
- case Opcodes.T_SHORT:
- return factory.shortType;
- case Opcodes.T_INT:
- return factory.intType;
- case Opcodes.T_LONG:
- return factory.longType;
- default:
- throw new Unreachable("Unexpected array-type code " + arrayTypeCode);
- }
- }
-
- @Override
- public void visitVarInsn(int opcode, int var) {
- ValueType type;
- switch (opcode) {
- case Opcodes.ILOAD:
- case Opcodes.ISTORE:
- type = ValueType.INT;
- break;
- case Opcodes.FLOAD:
- case Opcodes.FSTORE:
- type = ValueType.FLOAT;
- break;
- case Opcodes.LLOAD:
- case Opcodes.LSTORE:
- type = ValueType.LONG;
- break;
- case Opcodes.DLOAD:
- case Opcodes.DSTORE:
- type = ValueType.DOUBLE;
- break;
- case Opcodes.ALOAD:
- case Opcodes.ASTORE:
- type = ValueType.OBJECT;
- break;
- case Opcodes.RET:
- throw new Unreachable("RET should be handled by the ASM jsr inliner");
- default:
- throw new Unreachable("Unexpected VarInsn opcode: " + opcode);
- }
- if (Opcodes.ILOAD <= opcode && opcode <= Opcodes.ALOAD) {
- instructions.add(new CfLoad(type, var));
- } else {
- instructions.add(new CfStore(type, var));
- }
- }
-
- @Override
- public void visitTypeInsn(int opcode, String typeName) {
- DexType type = createTypeFromInternalType(typeName);
- switch (opcode) {
- case Opcodes.NEW:
- instructions.add(new CfNew(type));
- break;
- case Opcodes.ANEWARRAY:
- instructions.add(new CfNewArray(factory.createArrayType(1, type)));
- break;
- case Opcodes.CHECKCAST:
- instructions.add(new CfCheckCast(type));
- break;
- case Opcodes.INSTANCEOF:
- instructions.add(new CfInstanceOf(type));
- break;
- default:
- throw new Unreachable("Unexpected TypeInsn opcode: " + opcode);
- }
- }
-
- @Override
- public void visitFieldInsn(int opcode, String owner, String name, String desc) {
- DexField field =
- factory.createField(createTypeFromInternalType(owner), factory.createType(desc), name);
- // TODO(mathiasr): Don't require CfFieldInstruction::declaringField. It is needed for proper
- // renaming in the backend, but it is not available here in the frontend.
- instructions.add(new CfFieldInstruction(opcode, field, field));
- }
-
- @Override
- public void visitMethodInsn(int opcode, String owner, String name, String desc) {
- visitMethodInsn(opcode, owner, name, desc, false);
- }
-
- @Override
- public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
- DexMethod method = application.getMethod(owner, name, desc);
- instructions.add(new CfInvoke(opcode, method, itf));
- }
-
- @Override
- public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
- DexCallSite callSite =
- DexCallSite.fromAsmInvokeDynamic(application, parent.type, name, desc, bsm, bsmArgs);
- instructions.add(new CfInvokeDynamic(callSite));
- }
-
- @Override
- public void visitJumpInsn(int opcode, Label label) {
- CfLabel target = getLabel(label);
- if (Opcodes.IFEQ <= opcode && opcode <= Opcodes.IF_ACMPNE) {
- if (opcode <= Opcodes.IFLE) {
- // IFEQ, IFNE, IFLT, IFGE, IFGT, or IFLE.
- instructions.add(new CfIf(ifType(opcode), ValueType.INT, target));
- } else {
- // IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, or
- // IF_ACMPNE.
- ValueType valueType;
- if (opcode <= Opcodes.IF_ICMPLE) {
- valueType = ValueType.INT;
- } else {
- valueType = ValueType.OBJECT;
- }
- instructions.add(new CfIfCmp(ifType(opcode), valueType, target));
- }
- } else {
- // GOTO, JSR, IFNULL or IFNONNULL.
- switch (opcode) {
- case Opcodes.GOTO:
- instructions.add(new CfGoto(target));
- break;
- case Opcodes.IFNULL:
- case Opcodes.IFNONNULL:
- If.Type type = opcode == Opcodes.IFNULL ? If.Type.EQ : If.Type.NE;
- instructions.add(new CfIf(type, ValueType.OBJECT, target));
- break;
- case Opcodes.JSR:
- throw new Unreachable("JSR should be handled by the ASM jsr inliner");
- default:
- throw new Unreachable("Unexpected JumpInsn opcode: " + opcode);
- }
- }
- }
-
- private static If.Type ifType(int opcode) {
- switch (opcode) {
- case Opcodes.IFEQ:
- case Opcodes.IF_ICMPEQ:
- case Opcodes.IF_ACMPEQ:
- return If.Type.EQ;
- case Opcodes.IFNE:
- case Opcodes.IF_ICMPNE:
- case Opcodes.IF_ACMPNE:
- return If.Type.NE;
- case Opcodes.IFLT:
- case Opcodes.IF_ICMPLT:
- return If.Type.LT;
- case Opcodes.IFGE:
- case Opcodes.IF_ICMPGE:
- return If.Type.GE;
- case Opcodes.IFGT:
- case Opcodes.IF_ICMPGT:
- return If.Type.GT;
- case Opcodes.IFLE:
- case Opcodes.IF_ICMPLE:
- return If.Type.LE;
- default:
- throw new Unreachable("Unexpected If instruction opcode: " + opcode);
- }
- }
-
- @Override
- public void visitLabel(Label label) {
- instructions.add(getLabel(label));
- }
-
- @Override
- public void visitLdcInsn(Object cst) {
- if (cst instanceof Type) {
- Type type = (Type) cst;
- if (type.getSort() == Type.METHOD) {
- DexProto proto = application.getProto(type.getDescriptor());
- instructions.add(new CfConstMethodType(proto));
- } else {
- instructions.add(new CfConstClass(factory.createType(type.getDescriptor())));
- }
- } else if (cst instanceof String) {
- instructions.add(new CfConstString(factory.createString((String) cst)));
- } else if (cst instanceof Long) {
- instructions.add(new CfConstNumber((Long) cst, ValueType.LONG));
- } else if (cst instanceof Double) {
- long l = Double.doubleToRawLongBits((Double) cst);
- instructions.add(new CfConstNumber(l, ValueType.DOUBLE));
- } else if (cst instanceof Integer) {
- instructions.add(new CfConstNumber((Integer) cst, ValueType.INT));
- } else if (cst instanceof Float) {
- long i = Float.floatToRawIntBits((Float) cst);
- instructions.add(new CfConstNumber(i, ValueType.FLOAT));
- } else if (cst instanceof Handle) {
- instructions.add(
- new CfConstMethodHandle(
- DexMethodHandle.fromAsmHandle((Handle) cst, application, parent.type)));
- } else {
- throw new CompilationError("Unsupported constant: " + cst.toString());
- }
- }
-
- @Override
- public void visitIincInsn(int var, int increment) {
- instructions.add(new CfIinc(var, increment));
- }
-
- @Override
- public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
- assert max == min + labels.length - 1;
- ArrayList<CfLabel> targets = new ArrayList<>(labels.length);
- for (Label label : labels) {
- targets.add(getLabel(label));
- }
- instructions.add(new CfSwitch(CfSwitch.Kind.TABLE, getLabel(dflt), new int[] {min}, targets));
- }
-
- @Override
- public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
- ArrayList<CfLabel> targets = new ArrayList<>(labels.length);
- for (Label label : labels) {
- targets.add(getLabel(label));
- }
- instructions.add(new CfSwitch(CfSwitch.Kind.LOOKUP, getLabel(dflt), keys, targets));
- }
-
- @Override
- public void visitMultiANewArrayInsn(String desc, int dims) {
- instructions.add(new CfMultiANewArray(factory.createType(desc), dims));
- }
-
- @Override
- public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
- List<DexType> guards =
- Collections.singletonList(
- type == null ? DexItemFactory.catchAllType : createTypeFromInternalType(type));
- List<CfLabel> targets = Collections.singletonList(getLabel(handler));
- tryCatchRanges.add(new CfTryCatch(getLabel(start), getLabel(end), guards, targets));
- }
-
- @Override
- public void visitLocalVariable(
- String name, String desc, String signature, Label start, Label end, int index) {
- DebugLocalInfo debugLocalInfo =
- new DebugLocalInfo(
- factory.createString(name),
- factory.createType(desc),
- signature == null ? null : factory.createString(signature));
- localVariables.add(
- new LocalVariableInfo(index, debugLocalInfo, getLabel(start), getLabel(end)));
- }
-
- @Override
- public void visitLineNumber(int line, Label start) {
- instructions.add(new CfPosition(getLabel(start), new Position(line, null, method, null)));
- }
-
- @Override
- public void visitMaxs(int maxStack, int maxLocals) {
- assert maxStack >= 0;
- assert maxLocals >= 0;
- this.maxStack = maxStack;
- this.maxLocals = maxLocals;
- }
- }
-
private static class CreateAnnotationVisitor extends AnnotationVisitor {
private final JarApplicationReader application;
@@ -1418,4 +768,14 @@
return getDexValueArray(value);
}
}
+
+ public static class ReparseContext {
+
+ // This will hold the content of the whole class. Once all the methods of the class are swapped
+ // from this to the actual JarCode, no other references would be left and the content can be
+ // GC'd.
+ public byte[] classCache;
+ public DexProgramClass owner;
+ public final List<Code> codeList = new ArrayList<>();
+ }
}
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 55ae757..dc75307 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.errors.InvalidDebugInfoException;
+import com.android.tools.r8.graph.JarClassFileReader.ReparseContext;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.ValueNumberGenerator;
@@ -17,9 +18,7 @@
import com.android.tools.r8.utils.InternalOptions;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.ArrayList;
import java.util.Iterator;
-import java.util.List;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
@@ -39,16 +38,6 @@
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
- // from this to the actual JarCode, no other references would be left and the content can be
- // GC'd.
- public byte[] classCache;
- public DexProgramClass owner;
- private final List<JarCode> codeList = new ArrayList<>();
- }
-
private final DexMethod method;
private final Origin origin;
private MethodNode node;
@@ -227,7 +216,7 @@
JarCode code = null;
MethodAccessFlags flags = JarClassFileReader.createMethodAccessFlags(name, access);
if (!flags.isAbstract() && !flags.isNative()) {
- code = context.codeList.get(methodIndex++);
+ code = context.codeList.get(methodIndex++).asJarCode();
assert code.method == application.getMethod(context.owner.type, name, desc);
}
if (code != null) {
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
new file mode 100644
index 0000000..a864aba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -0,0 +1,804 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
+import com.android.tools.r8.cf.code.CfArrayLength;
+import com.android.tools.r8.cf.code.CfArrayLoad;
+import com.android.tools.r8.cf.code.CfArrayStore;
+import com.android.tools.r8.cf.code.CfCheckCast;
+import com.android.tools.r8.cf.code.CfCmp;
+import com.android.tools.r8.cf.code.CfConstClass;
+import com.android.tools.r8.cf.code.CfConstMethodHandle;
+import com.android.tools.r8.cf.code.CfConstMethodType;
+import com.android.tools.r8.cf.code.CfConstNull;
+import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfConstString;
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
+import com.android.tools.r8.cf.code.CfGoto;
+import com.android.tools.r8.cf.code.CfIf;
+import com.android.tools.r8.cf.code.CfIfCmp;
+import com.android.tools.r8.cf.code.CfIinc;
+import com.android.tools.r8.cf.code.CfInstanceOf;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfInvokeDynamic;
+import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
+import com.android.tools.r8.cf.code.CfMonitor;
+import com.android.tools.r8.cf.code.CfMultiANewArray;
+import com.android.tools.r8.cf.code.CfNeg;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfNewArray;
+import com.android.tools.r8.cf.code.CfNop;
+import com.android.tools.r8.cf.code.CfNumberConversion;
+import com.android.tools.r8.cf.code.CfPosition;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStore;
+import com.android.tools.r8.cf.code.CfSwitch;
+import com.android.tools.r8.cf.code.CfThrow;
+import com.android.tools.r8.cf.code.CfTryCatch;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
+import com.android.tools.r8.graph.JarClassFileReader.ReparseContext;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.Monitor;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.InternalOptions;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.JSRInlinerAdapter;
+
+public class LazyCfCode extends Code {
+
+ public LazyCfCode(
+ DexMethod method, Origin origin, ReparseContext context, JarApplicationReader application) {
+
+ this.method = method;
+ this.origin = origin;
+ this.context = context;
+ this.application = application;
+ context.codeList.add(this);
+ }
+
+ private final DexMethod method;
+ private final Origin origin;
+ private final JarApplicationReader application;
+ private CfCode code;
+ private ReparseContext context;
+
+ @Override
+ public boolean isCfCode() {
+ return true;
+ }
+
+ @Override
+ public LazyCfCode asLazyCfCode() {
+ return this;
+ }
+
+ @Override
+ public CfCode asCfCode() {
+ if (code == null) {
+ assert context != null;
+ // The SecondVistor is in charge of setting the context to null.
+ DexProgramClass owner = context.owner;
+ ClassReader classReader = new ClassReader(context.classCache);
+ classReader.accept(new ClassCodeVisitor(context, application), ClassReader.EXPAND_FRAMES);
+ assert verifyNoReparseContext(owner);
+ }
+ assert code != null;
+ return code;
+ }
+
+ private void setCode(CfCode code) {
+ assert this.code == null;
+ assert this.context != null;
+ this.code = code;
+ this.context = null;
+ }
+
+ @Override
+ protected int computeHashCode() {
+ throw new Unimplemented();
+ }
+
+ @Override
+ protected boolean computeEquals(Object other) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public boolean isEmptyVoidMethod() {
+ return asCfCode().isEmptyVoidMethod();
+ }
+
+ @Override
+ public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
+ throws ApiLevelException {
+ return asCfCode().buildIR(encodedMethod, options);
+ }
+
+ @Override
+ public IRCode buildInliningIR(
+ DexEncodedMethod encodedMethod,
+ InternalOptions options,
+ ValueNumberGenerator valueNumberGenerator,
+ Position callerPosition)
+ throws ApiLevelException {
+ return asCfCode().buildInliningIR(encodedMethod, options, valueNumberGenerator, callerPosition);
+ }
+
+ @Override
+ public void registerCodeReferences(UseRegistry registry) {
+ asCfCode().registerCodeReferences(registry);
+ }
+
+ @Override
+ public String toString() {
+ return asCfCode().toString();
+ }
+
+ @Override
+ public String toString(DexEncodedMethod method, ClassNameMapper naming) {
+ return null;
+ }
+
+ private static class ClassCodeVisitor extends ClassVisitor {
+
+ private final ReparseContext context;
+ private final JarApplicationReader application;
+ private int methodIndex = 0;
+
+ ClassCodeVisitor(ReparseContext context, JarApplicationReader application) {
+ super(Opcodes.ASM6);
+ this.context = context;
+ this.application = application;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String desc, String signature, String[] exceptions) {
+ MethodAccessFlags flags = JarClassFileReader.createMethodAccessFlags(name, access);
+ if (!flags.isAbstract() && !flags.isNative()) {
+ LazyCfCode code = context.codeList.get(methodIndex++).asLazyCfCode();
+ DexMethod method = application.getMethod(context.owner.type, name, desc);
+ assert code.method == method;
+ MethodCodeVisitor methodVisitor = new MethodCodeVisitor(application, code);
+ return new JSRInlinerAdapter(methodVisitor, access, name, desc, signature, exceptions);
+ }
+ return null;
+ }
+ }
+
+ private static class MethodCodeVisitor extends MethodVisitor {
+ private final JarApplicationReader application;
+ private final DexItemFactory factory;
+ private int maxStack;
+ private int maxLocals;
+ private List<CfInstruction> instructions;
+ private List<CfTryCatch> tryCatchRanges;
+ private List<LocalVariableInfo> localVariables;
+ private Map<Label, CfLabel> labelMap;
+ private final LazyCfCode code;
+ private DexMethod method;
+
+ MethodCodeVisitor(JarApplicationReader application, LazyCfCode code) {
+ super(Opcodes.ASM6);
+ this.application = application;
+ this.factory = application.getFactory();
+ this.method = code.method;
+ this.code = code;
+ }
+
+ @Override
+ public void visitCode() {
+ maxStack = 0;
+ maxLocals = 0;
+ instructions = new ArrayList<>();
+ tryCatchRanges = new ArrayList<>();
+ localVariables = new ArrayList<>();
+ labelMap = new IdentityHashMap<>();
+ }
+
+ @Override
+ public void visitEnd() {
+ code.setCode(
+ new CfCode(method, maxStack, maxLocals, instructions, tryCatchRanges, localVariables));
+ }
+
+ @Override
+ public void visitFrame(
+ int frameType, int nLocals, Object[] localTypes, int nStack, Object[] stackTypes) {
+ assert frameType == Opcodes.F_NEW;
+ Int2ReferenceSortedMap<FrameType> parsedLocals = parseLocals(nLocals, localTypes);
+ List<FrameType> parsedStack = parseStack(nStack, stackTypes);
+ instructions.add(new CfFrame(parsedLocals, parsedStack));
+ }
+
+ private Int2ReferenceSortedMap<FrameType> parseLocals(int typeCount, Object[] asmTypes) {
+ Int2ReferenceSortedMap<FrameType> types = new Int2ReferenceAVLTreeMap<>();
+ int i = 0;
+ for (int j = 0; j < typeCount; j++) {
+ Object localType = asmTypes[j];
+ FrameType value = getFrameType(localType);
+ types.put(i++, value);
+ if (value.isWide()) {
+ i++;
+ }
+ }
+ return types;
+ }
+
+ private List<FrameType> parseStack(int nStack, Object[] stackTypes) {
+ List<FrameType> dexStack = new ArrayList<>(nStack);
+ for (int i = 0; i < nStack; i++) {
+ dexStack.add(getFrameType(stackTypes[i]));
+ }
+ return dexStack;
+ }
+
+ private FrameType getFrameType(Object localType) {
+ if (localType instanceof Label) {
+ return FrameType.uninitializedNew(getLabel((Label) localType));
+ } else if (localType == Opcodes.UNINITIALIZED_THIS) {
+ return FrameType.uninitializedThis();
+ } else if (localType == null || localType == Opcodes.TOP) {
+ return FrameType.top();
+ } else {
+ return FrameType.initialized(parseAsmType(localType));
+ }
+ }
+
+ private CfLabel getLabel(Label label) {
+ return labelMap.computeIfAbsent(label, l -> new CfLabel());
+ }
+
+ private DexType parseAsmType(Object local) {
+ assert local != null && local != Opcodes.TOP;
+ if (local == Opcodes.INTEGER) {
+ return factory.intType;
+ } else if (local == Opcodes.FLOAT) {
+ return factory.floatType;
+ } else if (local == Opcodes.LONG) {
+ return factory.longType;
+ } else if (local == Opcodes.DOUBLE) {
+ return factory.doubleType;
+ } else if (local == Opcodes.NULL) {
+ return DexItemFactory.nullValueType;
+ } else if (local instanceof String) {
+ return createTypeFromInternalType((String) local);
+ } else {
+ throw new Unreachable("Unexpected ASM type: " + local);
+ }
+ }
+
+ private DexType createTypeFromInternalType(String local) {
+ assert local.indexOf('.') == -1;
+ return factory.createType("L" + local + ";");
+ }
+
+ @Override
+ public void visitInsn(int opcode) {
+ switch (opcode) {
+ case Opcodes.NOP:
+ instructions.add(new CfNop());
+ break;
+ case Opcodes.ACONST_NULL:
+ instructions.add(new CfConstNull());
+ break;
+ case Opcodes.ICONST_M1:
+ case Opcodes.ICONST_0:
+ case Opcodes.ICONST_1:
+ case Opcodes.ICONST_2:
+ case Opcodes.ICONST_3:
+ case Opcodes.ICONST_4:
+ case Opcodes.ICONST_5:
+ instructions.add(new CfConstNumber(opcode - Opcodes.ICONST_0, ValueType.INT));
+ break;
+ case Opcodes.LCONST_0:
+ case Opcodes.LCONST_1:
+ instructions.add(new CfConstNumber(opcode - Opcodes.LCONST_0, ValueType.LONG));
+ break;
+ case Opcodes.FCONST_0:
+ case Opcodes.FCONST_1:
+ case Opcodes.FCONST_2:
+ instructions.add(
+ new CfConstNumber(
+ Float.floatToRawIntBits(opcode - Opcodes.FCONST_0), ValueType.FLOAT));
+ break;
+ case Opcodes.DCONST_0:
+ case Opcodes.DCONST_1:
+ instructions.add(
+ new CfConstNumber(
+ Double.doubleToRawLongBits(opcode - Opcodes.DCONST_0), ValueType.DOUBLE));
+ break;
+ case Opcodes.IALOAD:
+ case Opcodes.LALOAD:
+ case Opcodes.FALOAD:
+ case Opcodes.DALOAD:
+ case Opcodes.AALOAD:
+ case Opcodes.BALOAD:
+ case Opcodes.CALOAD:
+ case Opcodes.SALOAD:
+ instructions.add(new CfArrayLoad(getMemberTypeForOpcode(opcode)));
+ break;
+ case Opcodes.IASTORE:
+ case Opcodes.LASTORE:
+ case Opcodes.FASTORE:
+ case Opcodes.DASTORE:
+ case Opcodes.AASTORE:
+ case Opcodes.BASTORE:
+ case Opcodes.CASTORE:
+ case Opcodes.SASTORE:
+ instructions.add(new CfArrayStore(getMemberTypeForOpcode(opcode)));
+ break;
+ case Opcodes.POP:
+ case Opcodes.POP2:
+ case Opcodes.DUP:
+ case Opcodes.DUP_X1:
+ case Opcodes.DUP_X2:
+ case Opcodes.DUP2:
+ case Opcodes.DUP2_X1:
+ case Opcodes.DUP2_X2:
+ case Opcodes.SWAP:
+ instructions.add(CfStackInstruction.fromAsm(opcode));
+ break;
+ case Opcodes.IADD:
+ case Opcodes.LADD:
+ case Opcodes.FADD:
+ case Opcodes.DADD:
+ case Opcodes.ISUB:
+ case Opcodes.LSUB:
+ case Opcodes.FSUB:
+ case Opcodes.DSUB:
+ case Opcodes.IMUL:
+ case Opcodes.LMUL:
+ case Opcodes.FMUL:
+ case Opcodes.DMUL:
+ case Opcodes.IDIV:
+ case Opcodes.LDIV:
+ case Opcodes.FDIV:
+ case Opcodes.DDIV:
+ case Opcodes.IREM:
+ case Opcodes.LREM:
+ case Opcodes.FREM:
+ case Opcodes.DREM:
+ instructions.add(CfArithmeticBinop.fromAsm(opcode));
+ break;
+ case Opcodes.INEG:
+ case Opcodes.LNEG:
+ case Opcodes.FNEG:
+ case Opcodes.DNEG:
+ instructions.add(CfNeg.fromAsm(opcode));
+ break;
+ case Opcodes.ISHL:
+ case Opcodes.LSHL:
+ case Opcodes.ISHR:
+ case Opcodes.LSHR:
+ case Opcodes.IUSHR:
+ case Opcodes.LUSHR:
+ case Opcodes.IAND:
+ case Opcodes.LAND:
+ case Opcodes.IOR:
+ case Opcodes.LOR:
+ case Opcodes.IXOR:
+ case Opcodes.LXOR:
+ instructions.add(CfLogicalBinop.fromAsm(opcode));
+ break;
+ case Opcodes.I2L:
+ case Opcodes.I2F:
+ case Opcodes.I2D:
+ case Opcodes.L2I:
+ case Opcodes.L2F:
+ case Opcodes.L2D:
+ case Opcodes.F2I:
+ case Opcodes.F2L:
+ case Opcodes.F2D:
+ case Opcodes.D2I:
+ case Opcodes.D2L:
+ case Opcodes.D2F:
+ case Opcodes.I2B:
+ case Opcodes.I2C:
+ case Opcodes.I2S:
+ instructions.add(CfNumberConversion.fromAsm(opcode));
+ break;
+ case Opcodes.LCMP:
+ case Opcodes.FCMPL:
+ case Opcodes.FCMPG:
+ case Opcodes.DCMPL:
+ case Opcodes.DCMPG:
+ instructions.add(CfCmp.fromAsm(opcode));
+ break;
+ case Opcodes.IRETURN:
+ instructions.add(new CfReturn(ValueType.INT));
+ break;
+ case Opcodes.LRETURN:
+ instructions.add(new CfReturn(ValueType.LONG));
+ break;
+ case Opcodes.FRETURN:
+ instructions.add(new CfReturn(ValueType.FLOAT));
+ break;
+ case Opcodes.DRETURN:
+ instructions.add(new CfReturn(ValueType.DOUBLE));
+ break;
+ case Opcodes.ARETURN:
+ instructions.add(new CfReturn(ValueType.OBJECT));
+ break;
+ case Opcodes.RETURN:
+ instructions.add(new CfReturnVoid());
+ break;
+ case Opcodes.ARRAYLENGTH:
+ instructions.add(new CfArrayLength());
+ break;
+ case Opcodes.ATHROW:
+ instructions.add(new CfThrow());
+ break;
+ case Opcodes.MONITORENTER:
+ instructions.add(new CfMonitor(Monitor.Type.ENTER));
+ break;
+ case Opcodes.MONITOREXIT:
+ instructions.add(new CfMonitor(Monitor.Type.EXIT));
+ break;
+ default:
+ throw new Unreachable("Unknown instruction");
+ }
+ }
+
+ private static MemberType getMemberTypeForOpcode(int opcode) {
+ switch (opcode) {
+ case Opcodes.IALOAD:
+ case Opcodes.IASTORE:
+ return MemberType.INT;
+ case Opcodes.FALOAD:
+ case Opcodes.FASTORE:
+ return MemberType.FLOAT;
+ case Opcodes.LALOAD:
+ case Opcodes.LASTORE:
+ return MemberType.LONG;
+ case Opcodes.DALOAD:
+ case Opcodes.DASTORE:
+ return MemberType.DOUBLE;
+ case Opcodes.AALOAD:
+ case Opcodes.AASTORE:
+ return MemberType.OBJECT;
+ case Opcodes.BALOAD:
+ case Opcodes.BASTORE:
+ return MemberType.BOOLEAN; // TODO: Distinguish byte and boolean.
+ case Opcodes.CALOAD:
+ case Opcodes.CASTORE:
+ return MemberType.CHAR;
+ case Opcodes.SALOAD:
+ case Opcodes.SASTORE:
+ return MemberType.SHORT;
+ default:
+ throw new Unreachable("Unexpected array opcode " + opcode);
+ }
+ }
+
+ @Override
+ public void visitIntInsn(int opcode, int operand) {
+ switch (opcode) {
+ case Opcodes.SIPUSH:
+ case Opcodes.BIPUSH:
+ instructions.add(new CfConstNumber(operand, ValueType.INT));
+ break;
+ case Opcodes.NEWARRAY:
+ instructions.add(
+ new CfNewArray(factory.createArrayType(1, arrayTypeDesc(operand, factory))));
+ break;
+ default:
+ throw new Unreachable("Unexpected int opcode " + opcode);
+ }
+ }
+
+ private static DexType arrayTypeDesc(int arrayTypeCode, DexItemFactory factory) {
+ switch (arrayTypeCode) {
+ case Opcodes.T_BOOLEAN:
+ return factory.booleanType;
+ case Opcodes.T_CHAR:
+ return factory.charType;
+ case Opcodes.T_FLOAT:
+ return factory.floatType;
+ case Opcodes.T_DOUBLE:
+ return factory.doubleType;
+ case Opcodes.T_BYTE:
+ return factory.byteType;
+ case Opcodes.T_SHORT:
+ return factory.shortType;
+ case Opcodes.T_INT:
+ return factory.intType;
+ case Opcodes.T_LONG:
+ return factory.longType;
+ default:
+ throw new Unreachable("Unexpected array-type code " + arrayTypeCode);
+ }
+ }
+
+ @Override
+ public void visitVarInsn(int opcode, int var) {
+ ValueType type;
+ switch (opcode) {
+ case Opcodes.ILOAD:
+ case Opcodes.ISTORE:
+ type = ValueType.INT;
+ break;
+ case Opcodes.FLOAD:
+ case Opcodes.FSTORE:
+ type = ValueType.FLOAT;
+ break;
+ case Opcodes.LLOAD:
+ case Opcodes.LSTORE:
+ type = ValueType.LONG;
+ break;
+ case Opcodes.DLOAD:
+ case Opcodes.DSTORE:
+ type = ValueType.DOUBLE;
+ break;
+ case Opcodes.ALOAD:
+ case Opcodes.ASTORE:
+ type = ValueType.OBJECT;
+ break;
+ case Opcodes.RET:
+ throw new Unreachable("RET should be handled by the ASM jsr inliner");
+ default:
+ throw new Unreachable("Unexpected VarInsn opcode: " + opcode);
+ }
+ if (Opcodes.ILOAD <= opcode && opcode <= Opcodes.ALOAD) {
+ instructions.add(new CfLoad(type, var));
+ } else {
+ instructions.add(new CfStore(type, var));
+ }
+ }
+
+ @Override
+ public void visitTypeInsn(int opcode, String typeName) {
+ DexType type = factory.createType(Type.getObjectType(typeName).getDescriptor());
+ switch (opcode) {
+ case Opcodes.NEW:
+ instructions.add(new CfNew(type));
+ break;
+ case Opcodes.ANEWARRAY:
+ instructions.add(new CfNewArray(factory.createArrayType(1, type)));
+ break;
+ case Opcodes.CHECKCAST:
+ instructions.add(new CfCheckCast(type));
+ break;
+ case Opcodes.INSTANCEOF:
+ instructions.add(new CfInstanceOf(type));
+ break;
+ default:
+ throw new Unreachable("Unexpected TypeInsn opcode: " + opcode);
+ }
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ DexField field =
+ factory.createField(createTypeFromInternalType(owner), factory.createType(desc), name);
+ // TODO(mathiasr): Don't require CfFieldInstruction::declaringField. It is needed for proper
+ // renaming in the backend, but it is not available here in the frontend.
+ instructions.add(new CfFieldInstruction(opcode, field, field));
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+ visitMethodInsn(opcode, owner, name, desc, false);
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
+ DexMethod method = application.getMethod(owner, name, desc);
+ instructions.add(new CfInvoke(opcode, method, itf));
+ }
+
+ @Override
+ public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
+ DexCallSite callSite =
+ DexCallSite.fromAsmInvokeDynamic(application, method.holder, name, desc, bsm, bsmArgs);
+ instructions.add(new CfInvokeDynamic(callSite));
+ }
+
+ @Override
+ public void visitJumpInsn(int opcode, Label label) {
+ CfLabel target = getLabel(label);
+ if (Opcodes.IFEQ <= opcode && opcode <= Opcodes.IF_ACMPNE) {
+ if (opcode <= Opcodes.IFLE) {
+ // IFEQ, IFNE, IFLT, IFGE, IFGT, or IFLE.
+ instructions.add(new CfIf(ifType(opcode), ValueType.INT, target));
+ } else {
+ // IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, or
+ // IF_ACMPNE.
+ ValueType valueType;
+ if (opcode <= Opcodes.IF_ICMPLE) {
+ valueType = ValueType.INT;
+ } else {
+ valueType = ValueType.OBJECT;
+ }
+ instructions.add(new CfIfCmp(ifType(opcode), valueType, target));
+ }
+ } else {
+ // GOTO, JSR, IFNULL or IFNONNULL.
+ switch (opcode) {
+ case Opcodes.GOTO:
+ instructions.add(new CfGoto(target));
+ break;
+ case Opcodes.IFNULL:
+ case Opcodes.IFNONNULL:
+ If.Type type = opcode == Opcodes.IFNULL ? If.Type.EQ : If.Type.NE;
+ instructions.add(new CfIf(type, ValueType.OBJECT, target));
+ break;
+ case Opcodes.JSR:
+ throw new Unreachable("JSR should be handled by the ASM jsr inliner");
+ default:
+ throw new Unreachable("Unexpected JumpInsn opcode: " + opcode);
+ }
+ }
+ }
+
+ private static If.Type ifType(int opcode) {
+ switch (opcode) {
+ case Opcodes.IFEQ:
+ case Opcodes.IF_ICMPEQ:
+ case Opcodes.IF_ACMPEQ:
+ return If.Type.EQ;
+ case Opcodes.IFNE:
+ case Opcodes.IF_ICMPNE:
+ case Opcodes.IF_ACMPNE:
+ return If.Type.NE;
+ case Opcodes.IFLT:
+ case Opcodes.IF_ICMPLT:
+ return If.Type.LT;
+ case Opcodes.IFGE:
+ case Opcodes.IF_ICMPGE:
+ return If.Type.GE;
+ case Opcodes.IFGT:
+ case Opcodes.IF_ICMPGT:
+ return If.Type.GT;
+ case Opcodes.IFLE:
+ case Opcodes.IF_ICMPLE:
+ return If.Type.LE;
+ default:
+ throw new Unreachable("Unexpected If instruction opcode: " + opcode);
+ }
+ }
+
+ @Override
+ public void visitLabel(Label label) {
+ instructions.add(getLabel(label));
+ }
+
+ @Override
+ public void visitLdcInsn(Object cst) {
+ if (cst instanceof Type) {
+ Type type = (Type) cst;
+ if (type.getSort() == Type.METHOD) {
+ DexProto proto = application.getProto(type.getDescriptor());
+ instructions.add(new CfConstMethodType(proto));
+ } else {
+ instructions.add(new CfConstClass(factory.createType(type.getDescriptor())));
+ }
+ } else if (cst instanceof String) {
+ instructions.add(new CfConstString(factory.createString((String) cst)));
+ } else if (cst instanceof Long) {
+ instructions.add(new CfConstNumber((Long) cst, ValueType.LONG));
+ } else if (cst instanceof Double) {
+ long l = Double.doubleToRawLongBits((Double) cst);
+ instructions.add(new CfConstNumber(l, ValueType.DOUBLE));
+ } else if (cst instanceof Integer) {
+ instructions.add(new CfConstNumber((Integer) cst, ValueType.INT));
+ } else if (cst instanceof Float) {
+ long i = Float.floatToRawIntBits((Float) cst);
+ instructions.add(new CfConstNumber(i, ValueType.FLOAT));
+ } else if (cst instanceof Handle) {
+ instructions.add(
+ new CfConstMethodHandle(
+ DexMethodHandle.fromAsmHandle((Handle) cst, application, method.holder)));
+ } else {
+ throw new CompilationError("Unsupported constant: " + cst.toString());
+ }
+ }
+
+ @Override
+ public void visitIincInsn(int var, int increment) {
+ instructions.add(new CfIinc(var, increment));
+ }
+
+ @Override
+ public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
+ assert max == min + labels.length - 1;
+ ArrayList<CfLabel> targets = new ArrayList<>(labels.length);
+ for (Label label : labels) {
+ targets.add(getLabel(label));
+ }
+ instructions.add(new CfSwitch(CfSwitch.Kind.TABLE, getLabel(dflt), new int[] {min}, targets));
+ }
+
+ @Override
+ public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+ ArrayList<CfLabel> targets = new ArrayList<>(labels.length);
+ for (Label label : labels) {
+ targets.add(getLabel(label));
+ }
+ instructions.add(new CfSwitch(CfSwitch.Kind.LOOKUP, getLabel(dflt), keys, targets));
+ }
+
+ @Override
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ instructions.add(new CfMultiANewArray(factory.createType(desc), dims));
+ }
+
+ @Override
+ public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+ List<DexType> guards =
+ Collections.singletonList(
+ type == null ? DexItemFactory.catchAllType : createTypeFromInternalType(type));
+ List<CfLabel> targets = Collections.singletonList(getLabel(handler));
+ tryCatchRanges.add(new CfTryCatch(getLabel(start), getLabel(end), guards, targets));
+ }
+
+ @Override
+ public void visitLocalVariable(
+ String name, String desc, String signature, Label start, Label end, int index) {
+ DebugLocalInfo debugLocalInfo =
+ new DebugLocalInfo(
+ factory.createString(name),
+ factory.createType(desc),
+ signature == null ? null : factory.createString(signature));
+ localVariables.add(
+ new LocalVariableInfo(index, debugLocalInfo, getLabel(start), getLabel(end)));
+ }
+
+ @Override
+ public void visitLineNumber(int line, Label start) {
+ instructions.add(new CfPosition(getLabel(start), new Position(line, null, method, null)));
+ }
+
+ @Override
+ public void visitMaxs(int maxStack, int maxLocals) {
+ assert maxStack >= 0;
+ assert maxLocals >= 0;
+ this.maxStack = maxStack;
+ this.maxLocals = maxLocals;
+ }
+ }
+
+ private static boolean verifyNoReparseContext(DexProgramClass owner) {
+ for (DexEncodedMethod method : owner.virtualMethods()) {
+ Code code = method.getCode();
+ assert code == null || !(code instanceof LazyCfCode) || ((LazyCfCode) code).context == null;
+ }
+ for (DexEncodedMethod method : owner.directMethods()) {
+ Code code = method.getCode();
+ assert code == null || !(code instanceof LazyCfCode) || ((LazyCfCode) code).context == null;
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Add.java b/src/main/java/com/android/tools/r8/ir/code/Add.java
index a147d21..d5896da 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Add.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Add.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.code.AddDouble;
import com.android.tools.r8.code.AddDouble2Addr;
import com.android.tools.r8.code.AddFloat;
@@ -14,8 +15,6 @@
import com.android.tools.r8.code.AddIntLit8;
import com.android.tools.r8.code.AddLong;
import com.android.tools.r8.code.AddLong2Addr;
-import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
public class Add extends ArithmeticBinop {
@@ -119,21 +118,7 @@
}
@Override
- int getCfOpcode() {
- switch (type) {
- case BYTE:
- case CHAR:
- case SHORT:
- case INT:
- return Opcodes.IADD;
- case FLOAT:
- return Opcodes.FADD;
- case LONG:
- return Opcodes.LADD;
- case DOUBLE:
- return Opcodes.DADD;
- default:
- throw new Unreachable("Unexpected numeric type in add: " + type);
- }
+ CfArithmeticBinop.Opcode getCfOpcode() {
+ return CfArithmeticBinop.Opcode.Add;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/And.java b/src/main/java/com/android/tools/r8/ir/code/And.java
index 461b1ec..283f369 100644
--- a/src/main/java/com/android/tools/r8/ir/code/And.java
+++ b/src/main/java/com/android/tools/r8/ir/code/And.java
@@ -4,14 +4,13 @@
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
import com.android.tools.r8.code.AndInt;
import com.android.tools.r8.code.AndInt2Addr;
import com.android.tools.r8.code.AndIntLit16;
import com.android.tools.r8.code.AndIntLit8;
import com.android.tools.r8.code.AndLong;
import com.android.tools.r8.code.AndLong2Addr;
-import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
public class And extends LogicalBinop {
@@ -85,19 +84,7 @@
}
@Override
- int getCfOpcode() {
- switch (type) {
- case BYTE:
- case CHAR:
- case SHORT:
- case INT:
- case FLOAT:
- return Opcodes.IAND;
- case LONG:
- case DOUBLE:
- return Opcodes.LAND;
- default:
- throw new Unreachable("Unexpected numeric type in logical and: " + type);
- }
+ CfLogicalBinop.Opcode getCfOpcode() {
+ return CfLogicalBinop.Opcode.And;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
index b0b6b9e..296fba5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
@@ -3,11 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.analysis.constant.Bottom;
import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import java.util.function.Function;
@@ -157,4 +159,11 @@
}
return Bottom.getInstance();
}
+
+ abstract CfArithmeticBinop.Opcode getCfOpcode();
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfArithmeticBinop(getCfOpcode(), type));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Binop.java b/src/main/java/com/android/tools/r8/ir/code/Binop.java
index c2f21ac..9e6738a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Binop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Binop.java
@@ -7,13 +7,11 @@
import static com.android.tools.r8.dex.Constants.U8BIT_MAX;
import com.android.tools.r8.cf.LoadStoreHelper;
-import com.android.tools.r8.cf.code.CfBinop;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
-import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
@@ -136,13 +134,6 @@
helper.storeOutValue(this, it);
}
- abstract int getCfOpcode();
-
- @Override
- public void buildCf(CfBuilder builder) {
- builder.add(new CfBinop(getCfOpcode()));
- }
-
@Override
public TypeLatticeElement evaluate(
AppInfo appInfo, Function<Value, TypeLatticeElement> getLatticeElement) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Cmp.java b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
index 2e84e4e..239e404 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Cmp.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfCmp;
import com.android.tools.r8.code.CmpLong;
import com.android.tools.r8.code.CmpgDouble;
import com.android.tools.r8.code.CmpgFloat;
@@ -13,12 +14,12 @@
import com.android.tools.r8.ir.analysis.constant.Bottom;
import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.utils.LongInterval;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
import java.util.function.Function;
-import org.objectweb.asm.Opcodes;
public class Cmp extends Binop {
@@ -222,17 +223,7 @@
}
@Override
- int getCfOpcode() {
- switch (type) {
- case LONG:
- assert bias == Bias.NONE;
- return Opcodes.LCMP;
- case FLOAT:
- return bias == Bias.GT ? Opcodes.FCMPG : Opcodes.FCMPL;
- case DOUBLE:
- return bias == Bias.GT ? Opcodes.DCMPG : Opcodes.DCMPL;
- default:
- throw new Unreachable("Unexpected cmp type: " + type);
- }
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfCmp(bias, type));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Div.java b/src/main/java/com/android/tools/r8/ir/code/Div.java
index 0add78c..6c9537a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Div.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Div.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.code.DivDouble;
import com.android.tools.r8.code.DivDouble2Addr;
import com.android.tools.r8.code.DivFloat;
@@ -13,11 +14,9 @@
import com.android.tools.r8.code.DivIntLit8;
import com.android.tools.r8.code.DivLong;
import com.android.tools.r8.code.DivLong2Addr;
-import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.analysis.constant.Bottom;
import com.android.tools.r8.ir.analysis.constant.LatticeElement;
import java.util.function.Function;
-import org.objectweb.asm.Opcodes;
public class Div extends ArithmeticBinop {
@@ -140,21 +139,7 @@
}
@Override
- int getCfOpcode() {
- switch (type) {
- case BYTE:
- case CHAR:
- case SHORT:
- case INT:
- return Opcodes.IDIV;
- case FLOAT:
- return Opcodes.FDIV;
- case LONG:
- return Opcodes.LDIV;
- case DOUBLE:
- return Opcodes.DDIV;
- default:
- throw new Unreachable("Unexpected numeric type: " + type);
- }
+ CfArithmeticBinop.Opcode getCfOpcode() {
+ return CfArithmeticBinop.Opcode.Div;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index e9504cd..819bb4b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -119,8 +119,19 @@
assert !needsRangedInvoke(builder);
int i = 0;
for (Value value : arguments()) {
- int register = builder.allocatedRegister(value, getNumber());
- assert register <= Constants.U4BIT_MAX;
+ // If one of the arguments to the invoke instruction is an argument of the enclosing method
+ // that has been spilled at this location, then we need to take the argument from its
+ // original input register (because the register allocator never inserts moves from an
+ // argument register to a spill register). Note that this is only a problem if an argument
+ // has been spilled to a register that is not the argument's original register.
+ //
+ // For simplicity, we just use the original input register for all arguments if the register
+ // fits in 4 bits.
+ int register = builder.argumentOrAllocateRegister(value, getNumber());
+ if (register + value.requiredRegisters() - 1 > Constants.U4BIT_MAX) {
+ register = builder.allocatedRegister(value, getNumber());
+ }
+ assert register + value.requiredRegisters() - 1 <= Constants.U4BIT_MAX;
for (int j = 0; j < value.requiredRegisters(); j++) {
assert i < 5;
registers[i++] = register++;
diff --git a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
index 8314e3f..4b34ca1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
@@ -3,11 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.analysis.constant.Bottom;
import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import java.util.function.Function;
@@ -133,4 +135,11 @@
}
return Bottom.getInstance();
}
+
+ abstract CfLogicalBinop.Opcode getCfOpcode();
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfLogicalBinop(getCfOpcode(), type));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Mul.java b/src/main/java/com/android/tools/r8/ir/code/Mul.java
index b78d288..7407f2e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Mul.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Mul.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.code.MulDouble;
import com.android.tools.r8.code.MulDouble2Addr;
import com.android.tools.r8.code.MulFloat;
@@ -14,8 +15,6 @@
import com.android.tools.r8.code.MulIntLit8;
import com.android.tools.r8.code.MulLong;
import com.android.tools.r8.code.MulLong2Addr;
-import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
public class Mul extends ArithmeticBinop {
@@ -131,21 +130,7 @@
}
@Override
- int getCfOpcode() {
- switch (type) {
- case BYTE:
- case CHAR:
- case SHORT:
- case INT:
- return Opcodes.IMUL;
- case FLOAT:
- return Opcodes.FMUL;
- case LONG:
- return Opcodes.LMUL;
- case DOUBLE:
- return Opcodes.DMUL;
- default:
- throw new Unreachable("Unexpected numeric type: " + type);
- }
+ CfArithmeticBinop.Opcode getCfOpcode() {
+ return CfArithmeticBinop.Opcode.Mul;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Neg.java b/src/main/java/com/android/tools/r8/ir/code/Neg.java
index f6e91a4..16b2922 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Neg.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Neg.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfNeg;
import com.android.tools.r8.code.NegDouble;
import com.android.tools.r8.code.NegFloat;
import com.android.tools.r8.code.NegInt;
@@ -11,9 +12,9 @@
import com.android.tools.r8.ir.analysis.constant.Bottom;
import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import java.util.function.Function;
-import org.objectweb.asm.Opcodes;
public class Neg extends Unop {
@@ -99,18 +100,7 @@
}
@Override
- public int getCfOpcode() {
- switch (type) {
- case INT:
- return Opcodes.INEG;
- case FLOAT:
- return Opcodes.FNEG;
- case LONG:
- return Opcodes.LNEG;
- case DOUBLE:
- return Opcodes.DNEG;
- default:
- throw new Unreachable("Unexpected type: " + type);
- }
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfNeg(type));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Not.java b/src/main/java/com/android/tools/r8/ir/code/Not.java
index 8b35b02..1037c34 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Not.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Not.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.ir.code;
import com.android.tools.r8.cf.LoadStoreHelper;
-import com.android.tools.r8.cf.code.CfBinop;
import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
import com.android.tools.r8.code.NotInt;
import com.android.tools.r8.code.NotLong;
import com.android.tools.r8.errors.Unreachable;
@@ -15,7 +15,6 @@
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import java.util.function.Function;
-import org.objectweb.asm.Opcodes;
public class Not extends Unop {
@@ -96,13 +95,8 @@
}
@Override
- public int getCfOpcode() {
- throw new Unreachable("Unexpected request for 'not' opcode which is translated to 'xor -1'");
- }
-
- @Override
public void buildCf(CfBuilder builder) {
builder.add(new CfConstNumber(-1, ValueType.fromNumericType(type)));
- builder.add(new CfBinop(type.isWide() ? Opcodes.LXOR : Opcodes.IXOR));
+ builder.add(new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, type));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
index bc3be95..f1c0049 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfNumberConversion;
import com.android.tools.r8.code.DoubleToFloat;
import com.android.tools.r8.code.DoubleToInt;
import com.android.tools.r8.code.DoubleToLong;
@@ -19,8 +20,8 @@
import com.android.tools.r8.code.LongToFloat;
import com.android.tools.r8.code.LongToInt;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
-import org.objectweb.asm.Opcodes;
public class NumberConversion extends Unop {
@@ -145,60 +146,7 @@
}
@Override
- public int getCfOpcode() {
- switch (from) {
- case INT:
- switch (to) {
- case BYTE:
- return Opcodes.I2B;
- case CHAR:
- return Opcodes.I2C;
- case SHORT:
- return Opcodes.I2S;
- case FLOAT:
- return Opcodes.I2F;
- case LONG:
- return Opcodes.I2L;
- case DOUBLE:
- return Opcodes.I2D;
- default:
- throw new Unreachable("Unexpected type conversion: " + from + " -> " + to);
- }
- case FLOAT:
- switch (to) {
- case INT:
- return Opcodes.F2I;
- case LONG:
- return Opcodes.F2L;
- case DOUBLE:
- return Opcodes.F2D;
- default:
- throw new Unreachable("Unexpected type conversion: " + from + " -> " + to);
- }
- case LONG:
- switch (to) {
- case INT:
- return Opcodes.L2I;
- case FLOAT:
- return Opcodes.L2F;
- case DOUBLE:
- return Opcodes.L2D;
- default:
- throw new Unreachable("Unexpected type conversion: " + from + " -> " + to);
- }
- case DOUBLE:
- switch (to) {
- case INT:
- return Opcodes.D2I;
- case LONG:
- return Opcodes.D2L;
- case FLOAT:
- return Opcodes.D2F;
- default:
- throw new Unreachable("Unexpected type conversion: " + from + " -> " + to);
- }
- default:
- throw new Unreachable("Unexpected type conversion: " + from + " -> " + to);
- }
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfNumberConversion(from, to));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Or.java b/src/main/java/com/android/tools/r8/ir/code/Or.java
index 4e5dc9c..9136be3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Or.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Or.java
@@ -3,14 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
import com.android.tools.r8.code.OrInt;
import com.android.tools.r8.code.OrInt2Addr;
import com.android.tools.r8.code.OrIntLit16;
import com.android.tools.r8.code.OrIntLit8;
import com.android.tools.r8.code.OrLong;
import com.android.tools.r8.code.OrLong2Addr;
-import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
public class Or extends LogicalBinop {
@@ -84,17 +83,7 @@
}
@Override
- int getCfOpcode() {
- switch (type) {
- case BYTE:
- case CHAR:
- case SHORT:
- case INT:
- return Opcodes.IOR;
- case LONG:
- return Opcodes.LOR;
- default:
- throw new Unreachable("Unexpected numeric type for or: " + type);
- }
+ CfLogicalBinop.Opcode getCfOpcode() {
+ return CfLogicalBinop.Opcode.Or;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Rem.java b/src/main/java/com/android/tools/r8/ir/code/Rem.java
index d584eed..02eb712 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Rem.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Rem.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.code.RemDouble;
import com.android.tools.r8.code.RemDouble2Addr;
import com.android.tools.r8.code.RemFloat;
@@ -13,11 +14,9 @@
import com.android.tools.r8.code.RemIntLit8;
import com.android.tools.r8.code.RemLong;
import com.android.tools.r8.code.RemLong2Addr;
-import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.analysis.constant.Bottom;
import com.android.tools.r8.ir.analysis.constant.LatticeElement;
import java.util.function.Function;
-import org.objectweb.asm.Opcodes;
public class Rem extends ArithmeticBinop {
@@ -140,21 +139,7 @@
}
@Override
- int getCfOpcode() {
- switch (type) {
- case BYTE:
- case CHAR:
- case SHORT:
- case INT:
- return Opcodes.IREM;
- case FLOAT:
- return Opcodes.FREM;
- case LONG:
- return Opcodes.LREM;
- case DOUBLE:
- return Opcodes.DREM;
- default:
- throw new Unreachable("Unexpected numeric type: " + type);
- }
+ CfArithmeticBinop.Opcode getCfOpcode() {
+ return CfArithmeticBinop.Opcode.Rem;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shl.java b/src/main/java/com/android/tools/r8/ir/code/Shl.java
index c959740..57b3190 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Shl.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Shl.java
@@ -3,13 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
import com.android.tools.r8.code.ShlInt;
import com.android.tools.r8.code.ShlInt2Addr;
import com.android.tools.r8.code.ShlIntLit8;
import com.android.tools.r8.code.ShlLong;
import com.android.tools.r8.code.ShlLong2Addr;
import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
public class Shl extends LogicalBinop {
@@ -89,17 +89,7 @@
}
@Override
- int getCfOpcode() {
- switch (type) {
- case BYTE:
- case CHAR:
- case SHORT:
- case INT:
- return Opcodes.ISHL;
- case LONG:
- return Opcodes.LSHL;
- default:
- throw new Unreachable("Unexpected numeric type in shift: " + type);
- }
+ CfLogicalBinop.Opcode getCfOpcode() {
+ return CfLogicalBinop.Opcode.Shl;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shr.java b/src/main/java/com/android/tools/r8/ir/code/Shr.java
index 35f8f61..18c38ed 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Shr.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Shr.java
@@ -3,13 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
import com.android.tools.r8.code.ShrInt;
import com.android.tools.r8.code.ShrInt2Addr;
import com.android.tools.r8.code.ShrIntLit8;
import com.android.tools.r8.code.ShrLong;
import com.android.tools.r8.code.ShrLong2Addr;
import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
public class Shr extends LogicalBinop {
@@ -89,17 +89,7 @@
}
@Override
- int getCfOpcode() {
- switch (type) {
- case BYTE:
- case CHAR:
- case SHORT:
- case INT:
- return Opcodes.ISHR;
- case LONG:
- return Opcodes.LSHR;
- default:
- throw new Unreachable("Unexpected numeric type in shift: " + type);
- }
+ CfLogicalBinop.Opcode getCfOpcode() {
+ return CfLogicalBinop.Opcode.Shr;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Sub.java b/src/main/java/com/android/tools/r8/ir/code/Sub.java
index abd6c60..b263e63 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Sub.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Sub.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.code.AddIntLit16;
import com.android.tools.r8.code.AddIntLit8;
import com.android.tools.r8.code.RsubInt;
@@ -19,7 +20,6 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.conversion.DexBuilder;
-import org.objectweb.asm.Opcodes;
public class Sub extends ArithmeticBinop {
@@ -208,21 +208,7 @@
}
@Override
- int getCfOpcode() {
- switch (type) {
- case BYTE:
- case CHAR:
- case SHORT:
- case INT:
- return Opcodes.ISUB;
- case FLOAT:
- return Opcodes.FSUB;
- case LONG:
- return Opcodes.LSUB;
- case DOUBLE:
- return Opcodes.DSUB;
- default:
- throw new Unreachable("Unexpected numeric type: " + type);
- }
+ CfArithmeticBinop.Opcode getCfOpcode() {
+ return CfArithmeticBinop.Opcode.Sub;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Unop.java b/src/main/java/com/android/tools/r8/ir/code/Unop.java
index 94758fb..6db3ad5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Unop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Unop.java
@@ -4,13 +4,11 @@
package com.android.tools.r8.ir.code;
import com.android.tools.r8.cf.LoadStoreHelper;
-import com.android.tools.r8.cf.code.CfUnop;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
-import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import java.util.function.Function;
@@ -60,13 +58,6 @@
helper.storeOutValue(this, it);
}
- public abstract int getCfOpcode();
-
- @Override
- public void buildCf(CfBuilder builder) {
- builder.add(new CfUnop(getCfOpcode()));
- }
-
@Override
public TypeLatticeElement evaluate(
AppInfo appInfo, Function<Value, TypeLatticeElement> getLatticeElement) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Ushr.java b/src/main/java/com/android/tools/r8/ir/code/Ushr.java
index e0fb715..d1e5a69 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Ushr.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Ushr.java
@@ -3,13 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
import com.android.tools.r8.code.UshrInt;
import com.android.tools.r8.code.UshrInt2Addr;
import com.android.tools.r8.code.UshrIntLit8;
import com.android.tools.r8.code.UshrLong;
import com.android.tools.r8.code.UshrLong2Addr;
import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
public class Ushr extends LogicalBinop {
@@ -89,17 +89,7 @@
}
@Override
- int getCfOpcode() {
- switch (type) {
- case BYTE:
- case CHAR:
- case SHORT:
- case INT:
- return Opcodes.IUSHR;
- case LONG:
- return Opcodes.LUSHR;
- default:
- throw new Unreachable("Unexpected numeric type in shift: " + type);
- }
+ CfLogicalBinop.Opcode getCfOpcode() {
+ return CfLogicalBinop.Opcode.Ushr;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Xor.java b/src/main/java/com/android/tools/r8/ir/code/Xor.java
index 3050f70..2e9c497 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Xor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Xor.java
@@ -3,13 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
import com.android.tools.r8.code.XorInt;
import com.android.tools.r8.code.XorInt2Addr;
import com.android.tools.r8.code.XorIntLit16;
import com.android.tools.r8.code.XorIntLit8;
import com.android.tools.r8.code.XorLong;
import com.android.tools.r8.code.XorLong2Addr;
-import org.objectweb.asm.Opcodes;
public class Xor extends LogicalBinop {
@@ -83,7 +83,7 @@
}
@Override
- int getCfOpcode() {
- return type.isWide() ? Opcodes.LXOR : Opcodes.IXOR;
+ CfLogicalBinop.Opcode getCfOpcode() {
+ return CfLogicalBinop.Opcode.Xor;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index 8af4486..1a4b97d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -128,7 +128,7 @@
// propagated through dominance.
Set<Instruction> users = knownToBeNonNullValue.uniqueUsers();
Set<Instruction> dominatedUsers = Sets.newIdentityHashSet();
- Map<Phi, IntList> dominatedPhiUsersWithPotisions = new IdentityHashMap<>();
+ Map<Phi, IntList> dominatedPhiUsersWithPositions = new IdentityHashMap<>();
DominatorTree dominatorTree = new DominatorTree(code);
Set<BasicBlock> dominatedBlocks = Sets.newIdentityHashSet();
for (BasicBlock dominatee : dominatorTree.dominatedBlocks(blockWithNonNullInstruction)) {
@@ -151,11 +151,11 @@
IntList dominatedPredecessorIndexes =
findDominatedPredecessorIndexesInPhi(user, knownToBeNonNullValue, dominatedBlocks);
if (!dominatedPredecessorIndexes.isEmpty()) {
- dominatedPhiUsersWithPotisions.put(user, dominatedPredecessorIndexes);
+ dominatedPhiUsersWithPositions.put(user, dominatedPredecessorIndexes);
}
}
knownToBeNonNullValue.replaceSelectiveUsers(
- nonNullValue, dominatedUsers, dominatedPhiUsersWithPotisions);
+ nonNullValue, dominatedUsers, dominatedPhiUsersWithPositions);
}
// Add non-null on top of the successor block if the current block ends with a null check.
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index de85da5..2f055ce 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -1144,6 +1144,10 @@
}
private int getSpillRegister(LiveIntervals intervals) {
+ if (intervals.isArgumentInterval()) {
+ return intervals.getSplitParent().getRegister();
+ }
+
int register = maxRegisterNumber + 1;
increaseCapacity(maxRegisterNumber + intervals.requiredRegisters());
assert registersAreFree(register, intervals.getType().isWide());
@@ -1279,9 +1283,10 @@
return false;
}
- private boolean longOverlappingLong(int register1, int register2) {
- return register1 == register2 || register1 == (register2 + 1)
- || (register1 + 1) == register2 || (register1 + 1) == (register2 + 1);
+ // Check if the two longs are half-overlapping, that is first register of one is the second
+ // register of the other.
+ private boolean longHalfOverlappingLong(int register1, int register2) {
+ return register1 == (register2 + 1) || (register1 + 1) == register2;
}
private boolean isLongResultOverlappingLongOperands(
@@ -1297,7 +1302,8 @@
// The dalvik bug is actually only for overlap with the second operand, For now we
// make sure that there is no overlap with either register of either operand. Some vendor
// optimization have bees seen to need this more conservative check.
- return longOverlappingLong(register, leftReg) || longOverlappingLong(register, rightReg);
+ return longHalfOverlappingLong(register, leftReg)
+ || longHalfOverlappingLong(register, rightReg);
}
// Intervals overlap a move exception interval if one of the splits of the intervals does.
@@ -1474,14 +1480,7 @@
// of finding another candidate to spill via allocateBlockedRegister.
if (!unhandledInterval.getUses().first().hasConstraint()) {
int nextConstrainedPosition = unhandledInterval.firstUseWithConstraint().getPosition();
- int register;
- // Arguments are always in the argument registers, so for arguments just use that register
- // for the unconstrained prefix. For everything else, get a spill register.
- if (unhandledInterval.isArgumentInterval()) {
- register = unhandledInterval.getSplitParent().getRegister();
- } else {
- register = getSpillRegister(unhandledInterval);
- }
+ int register = getSpillRegister(unhandledInterval);
LiveIntervals split = unhandledInterval.splitBefore(nextConstrainedPosition);
assignFreeRegisterToUnhandledInterval(unhandledInterval, register);
unhandled.add(split);
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
index 7e398e6..74600f4 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
@@ -88,6 +88,9 @@
}
public void setSpilled(boolean value) {
+ // Check that we always spill arguments to their original register.
+ assert getRegister() != NO_REGISTER;
+ assert !(value && isArgumentInterval()) || getRegister() == getSplitParent().getRegister();
spilled = value;
}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 30a5418..5b908af 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -110,9 +110,9 @@
return (MethodSignature) canonicalizeSignature(signature);
}
- public Signature getRenamedFieldSignature(DexField field) {
+ public FieldSignature getRenamedFieldSignature(DexField field) {
String type = deobfuscateType(field.type.toDescriptorString());
- return canonicalizeSignature(new FieldSignature(field.name.toString(), type));
+ return (FieldSignature) canonicalizeSignature(new FieldSignature(field.name.toString(), type));
}
/**
@@ -231,6 +231,20 @@
return memberNaming.signature;
}
+ public FieldSignature originalSignatureOf(DexField field) {
+ String decoded = descriptorToJavaType(field.clazz.descriptor.toString());
+ FieldSignature memberSignature = getRenamedFieldSignature(field);
+ ClassNaming classNaming = getClassNaming(decoded);
+ if (classNaming == null) {
+ return memberSignature;
+ }
+ MemberNaming memberNaming = classNaming.lookup(memberSignature);
+ if (memberNaming == null) {
+ return memberSignature;
+ }
+ return (FieldSignature) memberNaming.signature;
+ }
+
public String originalNameOf(DexType clazz) {
return deobfuscateType(clazz.descriptor.toString());
}
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 d6ddf5d..1bf193b 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -10,11 +10,11 @@
import com.android.tools.r8.ArchiveClassFileProvider;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.ClassFileResourceProvider;
+import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DirectoryClassFileProvider;
import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ProgramResourceProvider;
@@ -78,18 +78,32 @@
this.mainDexClasses = mainDexClasses;
}
+ static Reporter defaultReporter() {
+ return new Reporter(new DefaultDiagnosticsHandler());
+ }
+
/**
* Create a new empty builder.
*/
public static Builder builder() {
- return new Builder();
+ return builder(defaultReporter());
+ }
+
+ /** Create a new empty builder. */
+ public static Builder builder(Reporter reporter) {
+ return new Builder(reporter);
}
/**
* Create a new builder initialized with the resources from @code{app}.
*/
public static Builder builder(AndroidApp app) {
- return new Builder(app);
+ return builder(app, defaultReporter());
+ }
+
+ /** Create a new builder initialized with the resources from @code{app}. */
+ public static Builder builder(AndroidApp app, Reporter reporter) {
+ return new Builder(reporter, app);
}
/** Get full collection of all program resources from all program providers. */
@@ -273,12 +287,16 @@
// Proguard map data is output only data. This should never be used as input to a compilation.
private StringResource proguardMapOutputData;
+ private final Reporter reporter;
+
// See AndroidApp::builder().
- private Builder() {
+ private Builder(Reporter reporter) {
+ this.reporter = reporter;
}
// See AndroidApp::builder(AndroidApp).
- private Builder(AndroidApp app) {
+ private Builder(Reporter reporter, AndroidApp app) {
+ this(reporter);
programResourceProviders.addAll(app.programResourceProviders);
classpathResourceProviders.addAll(app.classpathResourceProviders);
libraryResourceProviders.addAll(app.libraryResourceProviders);
@@ -287,6 +305,10 @@
mainDexListClasses = app.mainDexClasses;
}
+ public Reporter getReporter() {
+ return reporter;
+ }
+
/** Add program file resources. */
public Builder addProgramFiles(Path... files) throws NoSuchFileException {
return addProgramFiles(Arrays.asList(files));
@@ -301,8 +323,7 @@
}
/** Add filtered archives of program resources. */
- public Builder addFilteredProgramArchives(Collection<FilteredClassPath> filteredArchives)
- throws NoSuchFileException {
+ public Builder addFilteredProgramArchives(Collection<FilteredClassPath> filteredArchives) {
for (FilteredClassPath archive : filteredArchives) {
assert isArchive(archive.getPath());
ArchiveResourceProvider archiveResourceProvider =
@@ -379,11 +400,14 @@
}
/** Add library file resources. */
- public Builder addFilteredLibraryArchives(Collection<FilteredClassPath> filteredArchives)
- throws IOException {
+ public Builder addFilteredLibraryArchives(Collection<FilteredClassPath> filteredArchives) {
for (FilteredClassPath archive : filteredArchives) {
assert isArchive(archive.getPath());
- libraryResourceProviders.add(new FilteredArchiveClassFileProvider(archive));
+ try {
+ libraryResourceProviders.add(new FilteredArchiveClassFileProvider(archive));
+ } catch (IOException e) {
+ reporter.error(new ExceptionDiagnostic(e, new PathOrigin(archive.getPath())));
+ }
}
return this;
}
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
index 4a7abc3..cd21d17 100644
--- a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
@@ -61,7 +61,7 @@
Files.newOutputStream(
archive, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
return stream;
@@ -71,9 +71,9 @@
if (e instanceof ZipException && e.getMessage().startsWith("duplicate entry")) {
// For now we stick to the Proguard behaviour, see section "Warning: can't write resource ...
// Duplicate zip entry" on https://www.guardsquare.com/en/proguard/manual/troubleshooting.
- handler.warning(new IOExceptionDiagnostic(e, origin));
+ handler.warning(new ExceptionDiagnostic(e, origin));
} else {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/DiagnosticWithThrowable.java b/src/main/java/com/android/tools/r8/utils/DiagnosticWithThrowable.java
index da439fe..b169297 100644
--- a/src/main/java/com/android/tools/r8/utils/DiagnosticWithThrowable.java
+++ b/src/main/java/com/android/tools/r8/utils/DiagnosticWithThrowable.java
@@ -14,4 +14,8 @@
assert throwable != null;
this.throwable = throwable;
}
+
+ public Throwable getThrowable() {
+ return throwable;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
index b793f19..2dc4a2d 100644
--- a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
@@ -39,7 +39,7 @@
try {
Files.createDirectories(target.getParent());
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(target)));
+ handler.error(new ExceptionDiagnostic(e, new PathOrigin(target)));
}
}
@@ -48,7 +48,7 @@
try (InputStream in = content.getByteStream()) {
addFile(name, ByteStreams.toByteArray(in), handler);
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, content.getOrigin()));
+ handler.error(new ExceptionDiagnostic(e, content.getOrigin()));
} catch (ResourceException e) {
handler.error(new StringDiagnostic("Failed to open input: " + e.getMessage(),
content.getOrigin()));
@@ -62,7 +62,7 @@
Files.createDirectories(target.getParent());
FileUtils.writeToFile(target, null, content);
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(target)));
+ handler.error(new ExceptionDiagnostic(e, new PathOrigin(target)));
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java b/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java
new file mode 100644
index 0000000..9fd91d9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.io.FileNotFoundException;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.NoSuchFileException;
+
+public class ExceptionDiagnostic extends DiagnosticWithThrowable {
+
+ private final Origin origin;
+
+ public ExceptionDiagnostic(Throwable e, Origin origin) {
+ super(e);
+ this.origin = origin;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ Throwable e = getThrowable();
+ if (e instanceof NoSuchFileException || e instanceof FileNotFoundException) {
+ return "File not found: " + e.getMessage();
+ }
+ if (e instanceof FileAlreadyExistsException) {
+ return "File already exists: " + e.getMessage();
+ }
+ return e.getMessage();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index f4d3ae7..9c7f525 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -9,7 +9,11 @@
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
import java.io.IOException;
+import java.nio.file.FileSystemException;
+import java.nio.file.Paths;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -55,16 +59,13 @@
try {
action.run();
} catch (IOException e) {
- throw reporter.fatalError(new IOExceptionDiagnostic(e));
+ throw reporter.fatalError(new ExceptionDiagnostic(e, extractIOExceptionOrigin(e)));
} catch (CompilationException e) {
throw reporter.fatalError(new StringDiagnostic(compilerMessage.apply(e)), e);
} catch (CompilationError e) {
throw reporter.fatalError(e);
} catch (ResourceException e) {
- throw reporter.fatalError(
- e.getCause() instanceof IOException
- ? new IOExceptionDiagnostic((IOException) e.getCause(), e.getOrigin())
- : new StringDiagnostic(e.getMessage(), e.getOrigin()));
+ throw reporter.fatalError(new ExceptionDiagnostic(e, e.getOrigin()));
}
reporter.failIfPendingErrors();
} catch (AbortException e) {
@@ -89,6 +90,19 @@
cause.printStackTrace();
System.exit(STATUS_ERROR);
}
-
}
+
+ // We should try to avoid the use of this extraction as it signifies a point where we don't have
+ // enough context to associate a specific origin with an IOException. Concretely, we should move
+ // towards always catching IOException and rethrowing CompilationError with proper origins.
+ public static Origin extractIOExceptionOrigin(IOException e) {
+ if (e instanceof FileSystemException) {
+ FileSystemException fse = (FileSystemException) e;
+ if (fse.getFile() != null && !fse.getFile().isEmpty()) {
+ return new PathOrigin(Paths.get(fse.getFile()));
+ }
+ }
+ return Origin.unknown();
+ }
+
}
diff --git a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
index 1c969f6..e8c6df9 100644
--- a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
+++ b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.dexsplitter.DexSplitter;
import com.android.tools.r8.dexsplitter.DexSplitter.FeatureJar;
+import com.android.tools.r8.origin.PathOrigin;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -51,11 +53,39 @@
static final String COMMENT = "#";
static final String SEPARATOR = ":";
+ private static class SpecificationOrigin extends PathOrigin {
- public static FeatureClassMapping fromSpecification(Path file)
- throws FeatureMappingException, IOException {
+ public SpecificationOrigin(Path path) {
+ super(path);
+ }
+
+ @Override
+ public String part() {
+ return "specification file '" + super.part() + "'";
+ }
+ }
+
+ private static class JarFileOrigin extends PathOrigin {
+
+ public JarFileOrigin(Path path) {
+ super(path);
+ }
+
+ @Override
+ public String part() {
+ return "jar file '" + super.part() + "'";
+ }
+ }
+
+ public static FeatureClassMapping fromSpecification(Path file, DexSplitter.Reporter reporter)
+ throws FeatureMappingException {
FeatureClassMapping mapping = new FeatureClassMapping();
- List<String> lines = FileUtils.readAllLines(file);
+ List<String> lines = null;
+ try {
+ lines = FileUtils.readAllLines(file);
+ } catch (IOException e) {
+ throw reporter.fatal(new ExceptionDiagnostic(e, new SpecificationOrigin(file)));
+ }
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
mapping.parseAndAdd(line, i);
@@ -63,15 +93,21 @@
return mapping;
}
- public static FeatureClassMapping fromJarFiles(List<FeatureJar> featureJars, String baseName)
- throws FeatureMappingException, IOException {
+ public static FeatureClassMapping fromJarFiles(
+ List<FeatureJar> featureJars, String baseName, DexSplitter.Reporter reporter)
+ throws FeatureMappingException {
FeatureClassMapping mapping = new FeatureClassMapping();
if (mapping.baseName != null) {
mapping.baseName = baseName;
}
for (FeatureJar featureJar : featureJars) {
Path jarPath = Paths.get(featureJar.getJar());
- ArchiveClassFileProvider provider = new ArchiveClassFileProvider(jarPath);
+ ArchiveClassFileProvider provider = null;
+ try {
+ provider = new ArchiveClassFileProvider(jarPath);
+ } catch (IOException e) {
+ throw reporter.fatal(new ExceptionDiagnostic(e, new JarFileOrigin(jarPath)));
+ }
for (String javaDescriptor : provider.getClassDescriptors()) {
String javaType = DescriptorUtils.descriptorToJavaType(javaDescriptor);
mapping.addMapping(javaType, featureJar.getOutputName());
@@ -94,7 +130,7 @@
}
}
- public String featureForClass(String clazz) throws FeatureMappingException {
+ public String featureForClass(String clazz) {
if (usesOnlyExactMappings) {
return parsedRules.getOrDefault(clazz, baseName);
} else {
diff --git a/src/main/java/com/android/tools/r8/utils/IOExceptionDiagnostic.java b/src/main/java/com/android/tools/r8/utils/IOExceptionDiagnostic.java
deleted file mode 100644
index 642f2d7..0000000
--- a/src/main/java/com/android/tools/r8/utils/IOExceptionDiagnostic.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// 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.utils;
-
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.position.Position;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.nio.file.FileAlreadyExistsException;
-import java.nio.file.FileSystemException;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.Paths;
-
-public class IOExceptionDiagnostic extends DiagnosticWithThrowable {
-
- private final Origin origin;
- private final String message;
-
- public IOExceptionDiagnostic(IOException e) {
- super(e);
- origin = extractOrigin(e);
- message = extractMessage(e);
- }
-
- public IOExceptionDiagnostic(IOException e, Origin origin) {
- super(e);
- this.origin = origin;
- message = extractMessage(e);
- }
-
- private static String extractMessage(IOException e) {
- String message = e.getMessage();
- if (message == null || message.isEmpty()) {
- if (e instanceof NoSuchFileException || e instanceof FileNotFoundException) {
- message = "File not found";
- } else if (e instanceof FileAlreadyExistsException) {
- message = "File already exists";
- }
- }
- return message;
- }
-
- private static Origin extractOrigin(IOException e) {
- Origin origin = Origin.unknown();
-
- if (e instanceof FileSystemException) {
- FileSystemException fse = (FileSystemException) e;
- if (fse.getFile() != null && !fse.getFile().isEmpty()) {
- origin = new PathOrigin(Paths.get(fse.getFile()));
- }
- }
- return origin;
- }
-
- @Override
- public Origin getOrigin() {
- return origin;
- }
-
- @Override
- public Position getPosition() {
- return Position.UNKNOWN;
- }
-
- @Override
- public String getDiagnosticMessage() {
- return message;
- }
-
-}
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index ed7bbf8..dc16cac 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -15,8 +15,10 @@
import com.android.tools.r8.origin.EmbeddedOrigin;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
+import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -139,7 +141,7 @@
Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar");
ProcessResult result =
ToolHelper.forkD8(Paths.get("."), input.toString(), "--output", existingDir.toString());
- assertEquals(0, result.exitCode);
+ assertEquals(result.toString(), 0, result.exitCode);
assertTrue(Files.exists(classesFiles.get(0)));
for (int i = 1; i < classesFiles.size(); i++) {
Path file = classesFiles.get(i);
@@ -360,6 +362,62 @@
.build());
}
+ @Test(expected = CompilationFailedException.class)
+ public void errorOnEmptyClassfile() throws IOException, CompilationFailedException {
+ Path emptyFile = temp.getRoot().toPath().resolve("empty-file.class");
+ FileUtils.writeToFile(emptyFile, null, new byte[0]);
+ DiagnosticsChecker.checkErrorsContains(
+ "empty",
+ handler ->
+ D8.run(
+ D8Command.builder(handler)
+ .addProgramFiles(emptyFile)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .build()));
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void errorOnInvalidClassfileHeader() throws IOException, CompilationFailedException {
+ Path emptyFile = temp.getRoot().toPath().resolve("empty-file.class");
+ FileUtils.writeToFile(emptyFile, null, new byte[] {'C', 'A', 'F', 'E', 'B', 'A', 'B', 'F'});
+ DiagnosticsChecker.checkErrorsContains(
+ "header",
+ handler ->
+ D8.run(
+ D8Command.builder(handler)
+ .addProgramFiles(emptyFile)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .build()));
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void errorOnEmptyDex() throws IOException, CompilationFailedException {
+ Path emptyFile = temp.getRoot().toPath().resolve("empty-file.dex");
+ FileUtils.writeToFile(emptyFile, null, new byte[0]);
+ DiagnosticsChecker.checkErrorsContains(
+ "empty",
+ handler ->
+ D8.run(
+ D8Command.builder(handler)
+ .addProgramFiles(emptyFile)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .build()));
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void errorOnInvalidDexHeader() throws IOException, CompilationFailedException {
+ Path emptyFile = temp.getRoot().toPath().resolve("empty-file.dex");
+ FileUtils.writeToFile(emptyFile, null, new byte[] {'C', 'A', 'F', 'E', 'B', 'A', 'B', 'E'});
+ DiagnosticsChecker.checkErrorsContains(
+ "header",
+ handler ->
+ D8.run(
+ D8Command.builder(handler)
+ .addProgramFiles(emptyFile)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .build()));
+ }
+
private D8Command parse(String... args) throws CompilationFailedException {
return D8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
index 7cd32c7..fc22895 100644
--- a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
+++ b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
@@ -40,6 +40,8 @@
try {
runner.run(handler);
} catch (CompilationFailedException e) {
+ System.out.println("Expecting match for '" + snippet + "'");
+ System.out.println("StdErr:\n" + handler.errors);
assertTrue(
"Expected to find snippet '"
+ snippet
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index f17359d..afa8c12 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -789,12 +789,6 @@
return R8Command.builder(app).setProgramConsumer(DexIndexedConsumer.emptyConsumer());
}
- public static R8Command.Builder prepareR8CommandBuilder(
- AndroidApp app, DiagnosticsHandler diagnosticsHandler) {
- return R8Command.builder(app, diagnosticsHandler)
- .setProgramConsumer(DexIndexedConsumer.emptyConsumer());
- }
-
public static AndroidApp runR8(AndroidApp app) throws IOException, CompilationException {
try {
return runR8(prepareR8CommandBuilder(app).build());
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsLiveAtBlockEntryDebugTest.java b/src/test/java/com/android/tools/r8/debug/LocalsLiveAtBlockEntryDebugTest.java
new file mode 100644
index 0000000..36cdad0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/LocalsLiveAtBlockEntryDebugTest.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Test to check that locals that are introduced in a block that is not hit, still start in the
+ * block where they first become visible.
+ *
+ * <p>See b/75251251 or b/78617758
+ */
+public class LocalsLiveAtBlockEntryDebugTest extends DebugTestBase {
+
+ final String className = "LocalsLiveAtEntry";
+ final String sourcefile = className + ".j";
+ final String methodName = "test";
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void testCF() throws Throwable {
+ JasminBuilder builder = getBuilderForTest(className, methodName);
+ Path outdir = temp.newFolder().toPath();
+ builder.writeClassFiles(outdir);
+ CfDebugTestConfig config = new CfDebugTestConfig();
+ config.addPaths(outdir);
+ runTest(config);
+ }
+
+ @Test
+ @Ignore("b/78617758")
+ public void testD8() throws Throwable {
+ JasminBuilder builder = getBuilderForTest(className, methodName);
+ List<Path> outputs = builder.writeClassFiles(temp.newFolder().toPath());
+ runTest(new D8DebugTestConfig().compileAndAdd(temp, outputs));
+ }
+
+ private void runTest(DebugTestConfig config) throws Throwable {
+ DexInspector inspector =
+ new DexInspector(
+ (config instanceof CfDebugTestConfig)
+ ? Collections.singletonList(config.getPaths().get(1).resolve(className + ".class"))
+ : config.getPaths());
+ ClassSubject clazz = inspector.clazz(className);
+ MethodSubject method = clazz.method("void", methodName, ImmutableList.of("java.lang.Object"));
+ assertTrue(method.isPresent());
+ runDebugTest(
+ config,
+ className,
+ breakpoint(className, methodName),
+ run(),
+ checkLine(sourcefile, 1),
+ checkNoLocal("obj"),
+ stepOver(),
+ checkLine(sourcefile, 3),
+ checkLocal("obj"),
+ stepOver(),
+ checkLine(sourcefile, 100),
+ run());
+ }
+
+ private JasminBuilder getBuilderForTest(String testClassName, String testMethodName) {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass(testClassName);
+
+ clazz.addStaticMethod(
+ testMethodName,
+ ImmutableList.of("Ljava/lang/Object;"),
+ "V",
+ ".limit stack 2",
+ ".limit locals 3",
+ ".var 0 is obj L" + testClassName + "; from L1 to L3",
+ "L0:", // Preamble code that does not have any live locals (eg, no formals too!).
+ ".line 1",
+ " ldc 42",
+ " lookupswitch",
+ " 0: L1",
+ " default: L2",
+ "L1:", // late introduction of formals.
+ ".line 2",
+ " aconst_null",
+ " pop",
+ "L2:", // target block with first visible location of locals.
+ ".line 3",
+ " aconst_null",
+ " pop",
+ " return",
+ "L3:");
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ ".line 100",
+ "aconst_null",
+ "invokestatic " + testClassName + "/" + testMethodName + "(Ljava/lang/Object;)V",
+ "return");
+
+ return builder;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/B79405526.java b/src/test/java/com/android/tools/r8/ir/regalloc/B79405526.java
new file mode 100644
index 0000000..f03e6f6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/B79405526.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.regalloc;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import org.junit.Test;
+
+public class B79405526 extends TestBase {
+ @Test
+ public void test() throws Exception {
+ AndroidApp app = compileWithD8(readClasses(TestClass.class));
+ DexInspector inspector = new DexInspector(app);
+ DexInspector.ClassSubject clazz = inspector.clazz(TestClass.class);
+ assertThat(clazz, isPresent());
+ // TODO(christofferqa): Ensure runOnArt checks that there are no verification errors, and then
+ // use runOnArt instead of runOnArtRaw.
+ ToolHelper.ProcessResult d8Result = runOnArtRaw(app, TestClass.class.getCanonicalName());
+ assertThat(d8Result.stderr, not(containsString("Verification error")));
+ }
+
+ private static class TestClass {
+ public void method() {
+ Object x = this;
+ TestClass y = this;
+ nop(null, getObject("", this));
+ TestClass z = getObject(null);
+ nop(null, getObject("", this, X.Y.getLong(getLong() - 0L)));
+ if (getBoolean()) {
+ z = getObject(this);
+ nop1(null, null);
+ }
+ nop2(null, null, null, null);
+ }
+
+ public static void nop(Object a, Object b, Object... c) {}
+
+ public static void nop1(Object... a) {}
+
+ private void nop2(Object a, Object b, Object c, Object d, Object... e) {}
+
+ public boolean getBoolean() {
+ return true;
+ }
+
+ public long getLong() {
+ return 0L;
+ }
+
+ private TestClass getObject(Object a) {
+ return null;
+ }
+
+ private static TestClass getObject(Object a, Object... c) {
+ return null;
+ }
+
+ public enum X {
+ Y {};
+
+ public long getLong(long var1) {
+ return 0L;
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
index 515b1f9..c053e62 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
@@ -6,15 +6,16 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableList;
@@ -40,20 +41,21 @@
@Rule
public ExpectedException thrown = ExpectedException.none();
- @Test
+ @Test(expected = CompilationFailedException.class)
public void testNoMainDex() throws Exception {
Reporter reporter = new Reporter();
try {
Path mainDexListOutput = temp.getRoot().toPath().resolve("main-dex-output.txt");
- R8Command command =
- ToolHelper.prepareR8CommandBuilder(readClasses(HelloWorldMain.class), reporter)
- .setMainDexListOutputPath(mainDexListOutput)
- .build();
+ R8Command.builder(reporter)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .addClassProgramData(
+ ToolHelper.getClassAsBytes(HelloWorldMain.class), Origin.unknown())
+ .setMainDexListOutputPath(mainDexListOutput)
+ .build();
} catch (CompilationFailedException e) {
assertEquals(1, reporter.errorCount);
- return;
+ throw e;
}
- fail("Expected CompilationFailedException");
}
@Test
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index 47bf7d2..0a7f122 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -74,7 +74,7 @@
with open(version_file, 'r') as file:
for line in file:
if 'final String LABEL ' in line:
- result = line[line.find('"v') + 2:]
+ result = line[line.find('"') + 1:]
result = result[:result.find('"')]
return result
raise Exception('Unable to determine version.')