Initial push.
diff --git a/src/main/java/com/android/tools/r8/BSPatch.java b/src/main/java/com/android/tools/r8/BSPatch.java
new file mode 100644
index 0000000..eda5f23
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/BSPatch.java
@@ -0,0 +1,335 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.dex.DexFileReader;
+import com.android.tools.r8.dex.Segment;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import javax.imageio.ImageIO;
+import org.apache.commons.compress.compressors.CompressorException;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
+
+public class BSPatch {
+
+ private final static char[] BSDIFF_MAGIC = "BSDIFF40".toCharArray();
+ private final static int BSDIFF_HEADER_LENGTH = 32;
+
+ private final ByteBuffer patchInput;
+ private final ByteBuffer oldInput;
+ private final Path output;
+ private final Path dexPath;
+
+ private InputStream controlStream;
+ private InputStream diffStream;
+ private InputStream extraStream;
+
+ private long controlBytesRead;
+ private long diffBytesRead;
+ private long extraBytesRead;
+ private int controlBlockLen;
+ private int diffBlockLen;
+ private int extraBlockLen;
+
+ public static void main(String[] args) {
+ boolean imageMode = args.length > 1 && args[0].equals("-i");
+ int argOffset = imageMode ? 1 : 0;
+ if (args.length < argOffset + 3) {
+ System.out.println("Usage: [-i] <patch file> <original dex> <output file> [target dex]");
+ System.exit(1);
+ }
+ try {
+ new BSPatch(Paths.get(args[argOffset]), Paths.get(args[argOffset + 1]),
+ Paths.get(args[argOffset + 2]),
+ args.length != argOffset + 4 ? null : Paths.get(args[argOffset + 3]))
+ .apply(imageMode);
+ } catch (IOException e) {
+ System.err.println("File I/O error: " + e.toString());
+ } catch (CompressorException e) {
+ System.err.println("BZIP error: " + e.toString());
+ }
+ }
+
+ private BSPatch(Path patchInput, Path oldInput, Path output, Path dexPath) throws IOException {
+ this.patchInput = ByteBuffer.wrap(Files.readAllBytes(patchInput));
+ this.oldInput = ByteBuffer.wrap(Files.readAllBytes(oldInput));
+ this.output = output;
+ this.dexPath = dexPath;
+ }
+
+ public void apply(boolean imageMode) throws CompressorException, IOException {
+ PatchExecutor executor = imageMode ? new ImageExecutor(dexPath) : new FileExecutor();
+ checkHeader();
+ setupSegmentsAndOutput(executor);
+ processControl(executor);
+ executor.writeResult();
+ printStats();
+ }
+
+ private int percentOf(long a, long b) {
+ return (int) ((((double) a) / ((double) b)) * 100);
+ }
+
+ private void printStats() {
+ System.out.println("Size of control block (compressed bytes): " + controlBlockLen);
+ System.out.println("Size of control block (read bytes): " + controlBytesRead);
+ System.out
+ .println("Compression of control block: " + percentOf(controlBlockLen, controlBytesRead));
+ System.out.println("Size of diff data block (compressed bytes): " + diffBlockLen);
+ System.out.println("Size of diff data block (read bytes): " + diffBytesRead);
+ System.out
+ .println("Compression of diff data block: " + percentOf(diffBlockLen, diffBytesRead));
+ System.out.println("Size of extra data block (compressed bytes): " + extraBlockLen);
+ System.out.println("Size of extra data block (read bytes): " + extraBytesRead);
+ System.out
+ .println("Compression of extra data block: " + percentOf(extraBlockLen, extraBytesRead));
+ }
+
+ private void processControl(PatchExecutor executor) throws IOException {
+ int blockSize;
+ while ((blockSize = readNextControlEntry()) != Integer.MIN_VALUE) {
+ int extraSize = readNextControlEntry();
+ int advanceOld = readNextControlEntry();
+ executor.copyDiff(blockSize);
+ executor.copyOld(blockSize);
+ executor.submitBlock(blockSize);
+ executor.copyExtra(extraSize);
+ executor.skipOld(advanceOld);
+ }
+ }
+
+ private void checkHeader() {
+ for (int i = 0; i < BSDIFF_MAGIC.length; i++) {
+ if (patchInput.get() != BSDIFF_MAGIC[i]) {
+ throw new RuntimeException("Illegal patch, wrong magic!");
+ }
+ }
+ }
+
+ private void setupSegmentsAndOutput(PatchExecutor executor)
+ throws CompressorException, IOException {
+ controlBlockLen = readOffset();
+ diffBlockLen = readOffset();
+ int newFileSize = readOffset();
+
+ extraBlockLen =
+ patchInput.array().length - (BSDIFF_HEADER_LENGTH + controlBlockLen + diffBlockLen);
+
+ executor.createOutput(newFileSize);
+ controlStream = new BZip2CompressorInputStream(
+ new ByteArrayInputStream(patchInput.array(), BSDIFF_HEADER_LENGTH, controlBlockLen));
+ diffStream = new BZip2CompressorInputStream(
+ new ByteArrayInputStream(patchInput.array(), BSDIFF_HEADER_LENGTH + controlBlockLen,
+ diffBlockLen));
+ extraStream = new BZip2CompressorInputStream(
+ new ByteArrayInputStream(patchInput.array(),
+ BSDIFF_HEADER_LENGTH + controlBlockLen + diffBlockLen,
+ extraBlockLen));
+ }
+
+ private int readOffset() {
+ byte[] buffer = new byte[8];
+ patchInput.get(buffer);
+ return decodeOffset(buffer);
+ }
+
+ private int readNextControlEntry() throws IOException {
+ byte[] buffer = new byte[8];
+ int read = controlStream.read(buffer);
+ if (read == -1) {
+ return Integer.MIN_VALUE;
+ }
+ controlBytesRead += read;
+ assert read == buffer.length;
+ return decodeOffset(buffer);
+ }
+
+ private static int decodeOffset(byte[] buffer) {
+ long offset = buffer[7] & 0x7F;
+ for (int i = 6; i >= 0; i--) {
+ offset = (offset << 8) | (((int) buffer[i]) & 0xff);
+ }
+ if ((buffer[7] & 0x80) != 0) {
+ offset = -offset;
+ }
+ assert offset < Integer.MAX_VALUE && offset > Integer.MIN_VALUE;
+ return (int) offset;
+ }
+
+ private abstract class PatchExecutor {
+
+ public abstract void createOutput(int newFileSize);
+
+ public abstract void copyDiff(int blockSize) throws IOException;
+
+ public abstract void copyOld(int blockSize) throws IOException;
+
+ public abstract void submitBlock(int blockSize);
+
+ public abstract void copyExtra(int extraSize) throws IOException;
+
+ public abstract void skipOld(int advanceOld) throws IOException;
+
+ public abstract void writeResult() throws IOException;
+ }
+
+ private class FileExecutor extends PatchExecutor {
+
+ private ByteBuffer resultBuffer;
+ private byte[] mergeBuffer = null;
+
+ @Override
+ public void createOutput(int newFileSize) {
+ resultBuffer = ByteBuffer.allocate(newFileSize);
+ }
+
+ @Override
+ public void copyDiff(int blockSize) throws IOException {
+ assert mergeBuffer == null;
+ mergeBuffer = new byte[blockSize];
+ int read = diffStream.read(mergeBuffer);
+ diffBytesRead += read;
+ assert read == blockSize;
+ }
+
+ @Override
+ public void copyOld(int blockSize) throws IOException {
+ assert mergeBuffer != null;
+ assert mergeBuffer.length == blockSize;
+ byte[] data = new byte[blockSize];
+ oldInput.get(data);
+ for (int i = 0; i < mergeBuffer.length; i++) {
+ mergeBuffer[i] = (byte) ((((int) mergeBuffer[i]) & 0xff) + (((int) data[i]) & 0xff));
+ }
+ }
+
+ @Override
+ public void submitBlock(int blockSize) {
+ assert mergeBuffer != null;
+ assert mergeBuffer.length == blockSize;
+ resultBuffer.put(mergeBuffer);
+ mergeBuffer = null;
+ }
+
+ @Override
+ public void copyExtra(int extraSize) throws IOException {
+ byte[] data = new byte[extraSize];
+ int read = extraStream.read(data);
+ assert read == extraSize;
+ extraBytesRead += read;
+ resultBuffer.put(data);
+ }
+
+ @Override
+ public void skipOld(int delta) throws IOException {
+ oldInput.position(oldInput.position() + delta);
+ }
+
+ @Override
+ public void writeResult() throws IOException {
+ OutputStream outputStream = Files.newOutputStream(output);
+ outputStream.write(resultBuffer.array());
+ outputStream.close();
+ }
+ }
+
+ private class ImageExecutor extends PatchExecutor {
+
+ private final Path dexPath;
+
+ BufferedImage image;
+ int position = 0;
+ int width;
+ int height;
+
+ private ImageExecutor(Path dexPath) {
+ this.dexPath = dexPath;
+ }
+
+ @Override
+ public void createOutput(int newFileSize) {
+ int root = (int) Math.sqrt(newFileSize);
+ width = newFileSize / root;
+ height = newFileSize / width + 1;
+ image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
+ }
+
+ @Override
+ public void copyDiff(int blockSize) throws IOException {
+ byte[] buffer = new byte[blockSize];
+ int read = diffStream.read(buffer);
+ assert read == blockSize;
+ diffBytesRead += read;
+ for (int i = 0; i < buffer.length; i++) {
+ if (buffer[i] != 0) {
+ int y = (position + i) / width;
+ int x = (position + i) % width;
+ int rgb = image.getRGB(x, y);
+ rgb = rgb | 0xFF0000;
+ image.setRGB(x, y, rgb);
+ }
+ }
+ }
+
+ @Override
+ public void copyOld(int blockSize) throws IOException {
+ for (int i = 0; i < blockSize; i++) {
+ int y = (position + i) / width;
+ int x = (position + i) % width;
+ int rgb = image.getRGB(x, y);
+ if ((rgb & 0xFF0000) == 0) {
+ rgb = rgb | 0xFF00;
+ }
+ image.setRGB(x, y, rgb);
+ }
+ }
+
+ @Override
+ public void submitBlock(int blockSize) {
+ position += blockSize;
+ }
+
+ @Override
+ public void copyExtra(int extraSize) throws IOException {
+ long skipped = extraStream.skip(extraSize);
+ assert skipped == extraSize;
+ extraBytesRead += skipped;
+ for (int i = 0; i < extraSize; i++) {
+ int y = (position + i) / width;
+ int x = (position + i) % width;
+ int rgb = image.getRGB(x, y);
+ rgb = rgb | 0xFF;
+ image.setRGB(x, y, rgb);
+ }
+ position += extraSize;
+ }
+
+ @Override
+ public void skipOld(int advanceOld) throws IOException {
+ }
+
+ @Override
+ public void writeResult() throws IOException {
+ if (dexPath != null) {
+ Segment[] segments = DexFileReader.parseMapFrom(dexPath);
+ for (Segment segment : segments) {
+ int y = segment.offset / width;
+ for (int x = 0; x < width; x++) {
+ int val = (x / 10) % 2 == 0 ? 0 : 0xffffff;
+ image.setRGB(x, y, val);
+ }
+ System.out.println(segment);
+ }
+ }
+ ImageIO.write(image, "png", output.toFile());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
new file mode 100644
index 0000000..bf58365
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -0,0 +1,232 @@
+// 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;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+
+abstract class BaseCommand {
+
+ private final boolean printHelp;
+ private final boolean printVersion;
+
+ private final AndroidApp app;
+ private final Path outputPath;
+ private final CompilationMode mode;
+ private final int minApiLevel;
+
+ BaseCommand(boolean printHelp, boolean printVersion) {
+ this.printHelp = printHelp;
+ this.printVersion = printVersion;
+ // All other fields are initialized with stub/invalid values.
+ this.app = null;
+ this.outputPath = null;
+ this.mode = null;
+ this.minApiLevel = 0;
+ }
+
+ BaseCommand(AndroidApp app, Path outputPath, CompilationMode mode, int minApiLevel) {
+ assert app != null;
+ assert mode != null;
+ assert minApiLevel > 0;
+ this.app = app;
+ this.outputPath = outputPath;
+ this.mode = mode;
+ this.minApiLevel = minApiLevel;
+ // Print options are not set.
+ printHelp = false;
+ printVersion = false;
+ }
+
+ public boolean isPrintHelp() {
+ return printHelp;
+ }
+
+ public boolean isPrintVersion() {
+ return printVersion;
+ }
+
+ // Internal access to the input resources.
+ AndroidApp getInputApp() {
+ return app;
+ }
+
+ // Internal access to the internal options.
+ abstract InternalOptions getInternalOptions();
+
+ public Path getOutputPath() {
+ return outputPath;
+ }
+
+ public CompilationMode getMode() {
+ return mode;
+ }
+
+ public int getMinApiLevel() {
+ return minApiLevel;
+ }
+
+ abstract static class Builder<C extends BaseCommand, B extends Builder<C, B>> {
+
+ private boolean printHelp = false;
+ private boolean printVersion = false;
+ private final AndroidApp.Builder app;
+ private Path outputPath = null;
+ private CompilationMode mode;
+ private int minApiLevel = Constants.DEFAULT_ANDROID_API;
+
+ protected Builder(CompilationMode mode) {
+ this(AndroidApp.builder(), mode);
+ }
+
+ // Internal constructor for testing.
+ Builder(AndroidApp app, CompilationMode mode) {
+ this(AndroidApp.builder(app), mode);
+ }
+
+ private Builder(AndroidApp.Builder builder, CompilationMode mode) {
+ assert mode != null;
+ this.app = builder;
+ this.mode = mode;
+ }
+
+ abstract B self();
+
+ public abstract C build() throws CompilationException, IOException;
+
+ // Internal accessor for the application resources.
+ AndroidApp.Builder getAppBuilder() {
+ return app;
+ }
+
+ /** Add program file resources. */
+ public B addProgramFiles(Path... files) throws IOException {
+ app.addProgramFiles(files);
+ return self();
+ }
+
+ /** Add program file resources. */
+ public B addProgramFiles(Collection<Path> files) throws IOException {
+ app.addProgramFiles(files);
+ return self();
+ }
+
+ /** Add classpath file resources. */
+ public B addClasspathFiles(Path... files) throws IOException {
+ app.addClasspathFiles(files);
+ return self();
+ }
+
+ /** Add classpath file resources. */
+ public B addClasspathFiles(Collection<Path> files) throws IOException {
+ app.addClasspathFiles(files);
+ return self();
+ }
+
+ /** Add library file resources. */
+ public B addLibraryFiles(Path... files) throws IOException {
+ app.addLibraryFiles(files);
+ return self();
+ }
+
+ /** Add library file resources. */
+ public B addLibraryFiles(List<Path> files) throws IOException {
+ app.addLibraryFiles(files);
+ return self();
+ }
+
+ /** Add Java-bytecode program-data. */
+ public B addClassProgramData(byte[]... data) {
+ app.addClassProgramData(data);
+ return self();
+ }
+
+ /** Add Java-bytecode program-data. */
+ public B addClassProgramData(Collection<byte[]> data) {
+ app.addClassProgramData(data);
+ return self();
+ }
+
+ /** Add dex program-data. */
+ public B addDexProgramData(byte[]... data) {
+ app.addDexProgramData(data);
+ return self();
+ }
+
+ /** Add dex program-data. */
+ public B addDexProgramData(Collection<byte[]> data) {
+ app.addDexProgramData(data);
+ return self();
+ }
+
+ /** Get current compilation mode. */
+ public CompilationMode getMode() {
+ return mode;
+ }
+
+ /** Set compilation mode. */
+ public B setMode(CompilationMode mode) {
+ assert mode != null;
+ this.mode = mode;
+ return self();
+ }
+
+ /** Get the output path. Null if not set. */
+ public Path getOutputPath() {
+ return outputPath;
+ }
+
+ /** Set an output path. Must be an existing directory or a non-existent zip file. */
+ public B setOutputPath(Path outputPath) throws CompilationException {
+ this.outputPath = FileUtils.validateOutputFile(outputPath);
+ return self();
+ }
+
+ /** Get the minimum API level (aka SDK version). */
+ public int getMinApiLevel() {
+ return minApiLevel;
+ }
+
+ /** Set the minimum required API level (aka SDK version). */
+ public B setMinApiLevel(int minApiLevel) {
+ assert minApiLevel > 0;
+ this.minApiLevel = minApiLevel;
+ return self();
+ }
+
+ /** Set the main-dex list file. */
+ public B setMainDexListFile(Path file) {
+ app.setMainDexListFile(file);
+ return self();
+ }
+
+ /** True if the print-help flag is enabled. */
+ public boolean isPrintHelp() {
+ return printHelp;
+ }
+
+ /** Set the value of the print-help flag. */
+ public B setPrintHelp(boolean printHelp) {
+ this.printHelp = printHelp;
+ return self();
+ }
+
+ /** True if the print-version flag is enabled. */
+ public boolean isPrintVersion() {
+ return printVersion;
+ }
+
+ /** Set the value of the print-version flag. */
+ public B setPrintVersion(boolean printVersion) {
+ this.printVersion = printVersion;
+ return self();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/BaseOutput.java b/src/main/java/com/android/tools/r8/BaseOutput.java
new file mode 100644
index 0000000..0f082c9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/BaseOutput.java
@@ -0,0 +1,46 @@
+// 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;
+
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+
+abstract class BaseOutput {
+
+ private final AndroidApp app;
+
+ BaseOutput(AndroidApp app) {
+ this.app = app;
+ }
+
+ // Internal access to the underlying app.
+ AndroidApp getAndroidApp() {
+ return app;
+ }
+
+ /**
+ * Get the list of compiled DEX resources.
+ *
+ * <p>The order of the list corresponds to the usual naming convention:
+ *
+ * <pre>
+ * resources.get(0) ~=~ classes.dex (the main dex file)
+ * resources.get(N - 1) ~=~ classesN.dex (where N > 0).
+ * </pre>
+ *
+ * @return list of compiled DEX resources.
+ */
+ public ImmutableList<Resource> getDexResources() {
+ return ImmutableList.copyOf(app.getDexProgramResources());
+ }
+
+ /**
+ * Write the output resources to a zip-archive or directory.
+ *
+ * @param output Path to existing directory or non-existing zip-archive.
+ */
+ abstract public void write(Path output) throws IOException;
+}
diff --git a/src/main/java/com/android/tools/r8/CompilationException.java b/src/main/java/com/android/tools/r8/CompilationException.java
new file mode 100644
index 0000000..4fd3654
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/CompilationException.java
@@ -0,0 +1,40 @@
+// 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;
+
+/**
+ * Exception to signal an compilation error.
+ *
+ * This is always an expected error and considered a user input issue.
+ * A user-understandable message must be provided.
+ */
+public class CompilationException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Construct the exception with a {@link String} message.
+ * @param message the message
+ */
+ public CompilationException(String message) {
+ super(message);
+ }
+
+ /**
+ * Construct the exception with a {@link String} message and a {@link Throwable} cause.
+ * @param message the message
+ * @param cause the cause
+ */
+ public CompilationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Construct the exception with a {@link Throwable} cause.
+ * @param cause the cause
+ */
+ public CompilationException(Throwable cause) {
+ super(cause.getMessage(), cause);
+ }
+}
+
diff --git a/src/main/java/com/android/tools/r8/CompilationMode.java b/src/main/java/com/android/tools/r8/CompilationMode.java
new file mode 100644
index 0000000..f7aa4f0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/CompilationMode.java
@@ -0,0 +1,12 @@
+// 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;
+
+/** Compilation mode. */
+public enum CompilationMode {
+ /** Preserves debugging information during compilation, eg, line-numbers and locals. */
+ DEBUG,
+ /** Strips debugging information that cannot affect stack traces. */
+ RELEASE
+}
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
new file mode 100644
index 0000000..4ff573d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -0,0 +1,197 @@
+// 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;
+
+import static com.android.tools.r8.D8Command.USAGE_MESSAGE;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * The D8 dex compiler.
+ *
+ * <p>D8 performs modular compilation to DEX bytecode. It supports compilation of Java bytecode and
+ * Android DEX bytecode to DEX bytecode including merging a mix of these input formats.
+ *
+ * <p>The D8 dexer API is intentionally limited and should "do the right thing" given a command. If
+ * this API does not suffice please contact the D8/R8 team.
+ *
+ * <p>The compiler is invoked by calling {@link #run(D8Command) D8.run} with an appropriate {@link
+ * D8Command}. For example:
+ *
+ * <pre>
+ * D8.run(D8Command.builder()
+ * .addProgramFiles(inputPathA, inputPathB)
+ * .setOutputPath(outputPath)
+ * .build());
+ * </pre>
+ *
+ * The above reads the input files denoted by {@code inputPathA} and {@code inputPathB}, compiles
+ * them to DEX bytecode (compiling from Java bytecode for such inputs and merging for DEX inputs),
+ * and then writes the result to the directory or zip archive specified by {@code outputPath}.
+ */
+public final class D8 {
+
+ private static final int STATUS_ERROR = 1;
+
+ private D8() {}
+
+ /**
+ * Main API entry for the D8 dexer.
+ *
+ * @param command D8 command.
+ * @return the compilation result.
+ */
+ public static D8Output run(D8Command command) throws IOException {
+ D8Output output = new D8Output(
+ runForTesting(command.getInputApp(), command.getInternalOptions()));
+ if (command.getOutputPath() != null) {
+ output.write(command.getOutputPath());
+ }
+ return output;
+ }
+
+ /**
+ * Main API entry for the D8 dexer.
+ *
+ * <p>The D8 dexer API is intentionally limited and should "do the right thing" given a set of
+ * inputs. If the API does not suffice please contact the R8 team.
+ *
+ * @param command D8 command.
+ * @param executor executor service from which to get threads for multi-threaded processing.
+ * @return the compilation result.
+ */
+ public static D8Output run(D8Command command, ExecutorService executor) throws IOException {
+ D8Output output = new D8Output(
+ runForTesting(command.getInputApp(), command.getInternalOptions(), executor));
+ if (command.getOutputPath() != null) {
+ output.write(command.getOutputPath());
+ }
+ return output;
+ }
+
+ private static void run(String[] args) throws IOException, CompilationException {
+ D8Command.Builder builder = D8Command.parse(args);
+ if (builder.getOutputPath() == null) {
+ builder.setOutputPath(Paths.get("."));
+ }
+ D8Command command = builder.build();
+ if (command.isPrintHelp()) {
+ System.out.println(USAGE_MESSAGE);
+ return;
+ }
+ if (command.isPrintVersion()) {
+ System.out.println("D8 v0.0.1");
+ return;
+ }
+ run(command);
+ }
+
+ /** Command-line entry to D8. */
+ public static void main(String[] args) throws IOException {
+ if (args.length == 0) {
+ System.err.println(USAGE_MESSAGE);
+ System.exit(STATUS_ERROR);
+ }
+ try {
+ run(args);
+ } catch (NoSuchFileException e) {
+ System.err.println("File not found: " + e.getFile());
+ System.exit(STATUS_ERROR);
+ } catch (FileAlreadyExistsException e) {
+ System.err.println("File already exists: " + e.getFile());
+ System.exit(STATUS_ERROR);
+ } catch (IOException e) {
+ System.err.println("Failed to read or write application files: " + e.getMessage());
+ System.exit(STATUS_ERROR);
+ } catch (RuntimeException e) {
+ System.err.println("Compilation failed with an internal error.");
+ Throwable cause = e.getCause() == null ? e : e.getCause();
+ cause.printStackTrace();
+ System.exit(STATUS_ERROR);
+ } catch (CompilationException e) {
+ System.err.println("Compilation failed: " + e.getMessage());
+ System.err.println(USAGE_MESSAGE);
+ System.exit(STATUS_ERROR);
+ }
+ }
+
+ static AndroidApp runForTesting(AndroidApp inputApp, InternalOptions options) throws IOException {
+ ExecutorService executor = ThreadUtils.getExecutorService(options);
+ try {
+ return runForTesting(inputApp, options, executor);
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ static AndroidApp runForTesting(
+ AndroidApp inputApp, InternalOptions options, ExecutorService executor) throws IOException {
+ try {
+ assert !inputApp.hasPackageDistribution();
+
+ // Disable global optimizations.
+ options.skipMinification = true;
+ options.allowAccessModification = false;
+ options.inlineAccessors = false;
+ options.outline.enabled = false;
+
+ Timing timing = new Timing("DX timer");
+ DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
+ AppInfo appInfo = new AppInfo(app);
+ app = optimize(app, appInfo, options);
+
+ // If a method filter is present don't produce output since the application is likely partial.
+ if (!options.methodsFilter.isEmpty()) {
+ System.out.println("Finished compilation with method filter: ");
+ options.methodsFilter.forEach((m) -> System.out.println(" - " + m));
+ return null;
+ }
+
+ AndroidApp output =
+ new ApplicationWriter(app, appInfo, options, NamingLens.getIdentityLens(), null)
+ .write(null, executor);
+
+ options.printWarnings();
+ return output;
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e.getMessage(), e.getCause());
+ }
+ }
+
+ private static DexApplication optimize(
+ DexApplication application, AppInfo appInfo, InternalOptions options)
+ throws IOException, ExecutionException {
+ final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
+
+ IRConverter converter = new IRConverter(application, appInfo, options, printer);
+ application = converter.convertToDex();
+
+ if (options.printCfg) {
+ if (options.printCfgFile == null || options.printCfgFile.isEmpty()) {
+ System.out.print(printer.toString());
+ } else {
+ java.io.FileWriter writer = new java.io.FileWriter(options.printCfgFile);
+ writer.write(printer.toString());
+ writer.close();
+ }
+ }
+ return application;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
new file mode 100644
index 0000000..5b12cf1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -0,0 +1,153 @@
+// 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;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OffOrAuto;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Immutable command structure for an invocation of the {@code D8} compiler.
+ *
+ * <p>To build a D8 command use the {@code D8Command.Builder} class. For example:
+ *
+ * <pre>
+ * D8Command command = D8Command.builder()
+ * .addProgramFiles(path1, path2)
+ * .setMode(CompilationMode.RELEASE)
+ * .build();
+ * </pre>
+ */
+public class D8Command extends BaseCommand {
+
+ /** Builder for constructing a D8Command. */
+ public static class Builder extends BaseCommand.Builder<D8Command, Builder> {
+
+ private Builder() {
+ super(CompilationMode.DEBUG);
+ }
+
+ private Builder(AndroidApp app) {
+ super(app, CompilationMode.DEBUG);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
+
+ /** Build the final D8Command. */
+ @Override
+ public D8Command build() throws CompilationException {
+ if (isPrintHelp() || isPrintVersion()) {
+ return new D8Command(isPrintHelp(), isPrintVersion());
+ }
+ return new D8Command(getAppBuilder().build(), getOutputPath(), getMode(), getMinApiLevel());
+ }
+ }
+
+ static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
+ "Usage: d8 [options] <input-files>",
+ " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+ " and options are:",
+ " --debug # compile with debugging information (default enabled).",
+ " --release # compile without debugging information.",
+ " --output <file> # output result in <outfile>.",
+ " # <file> must be an existing directory or non-existent zip file.",
+ " --lib <file> # Add <file> as a library resource.",
+ " --classpath <file> # Add <file> as a classpath resource.",
+ " --min-sdk-version # minimum Android API level compatibility",
+ " --version # print the version of d8.",
+ " --help # print this message."));
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ // Internal builder to start from an existing AndroidApp.
+ static Builder builder(AndroidApp app) {
+ return new Builder(app);
+ }
+
+ public static Builder parse(String[] args) throws CompilationException, IOException {
+ CompilationMode modeSet = null;
+ Path outputPath = null;
+ Builder builder = builder();
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ if (arg.equals("--help")) {
+ builder.setPrintHelp(true);
+ } else if (arg.equals("--version")) {
+ builder.setPrintVersion(true);
+ } else if (arg.equals("--debug")) {
+ if (modeSet == CompilationMode.RELEASE) {
+ throw new CompilationException("Cannot compile in both --debug and --release mode.");
+ }
+ builder.setMode(CompilationMode.DEBUG);
+ modeSet = CompilationMode.DEBUG;
+ } else if (arg.equals("--release")) {
+ if (modeSet == CompilationMode.DEBUG) {
+ throw new CompilationException("Cannot compile in both --debug and --release mode.");
+ }
+ builder.setMode(CompilationMode.RELEASE);
+ modeSet = CompilationMode.RELEASE;
+ } else if (arg.equals("--output")) {
+ String output = args[++i];
+ if (outputPath != null) {
+ throw new CompilationException(
+ "Cannot output both to '" + outputPath.toString() + "' and '" + output + "'");
+ }
+ outputPath = Paths.get(output);
+ } else if (arg.equals("--lib")) {
+ builder.addLibraryFiles(Paths.get(args[++i]));
+ } else if (arg.equals("--classpath")) {
+ builder.addClasspathFiles(Paths.get(args[++i]));
+ } else if (arg.equals("--min-sdk-version")) {
+ builder.setMinApiLevel(Integer.valueOf(args[++i]));
+ } else {
+ if (arg.startsWith("--")) {
+ throw new CompilationException("Unknown option: " + arg);
+ }
+ builder.addProgramFiles(Paths.get(arg));
+ }
+ }
+ return builder.setOutputPath(outputPath);
+ }
+
+ private D8Command(AndroidApp inputApp, Path outputPath, CompilationMode mode, int minApiLevel) {
+ super(inputApp, outputPath, mode, minApiLevel);
+ }
+
+ private D8Command(boolean printHelp, boolean printVersion) {
+ super(printHelp, printVersion);
+ }
+
+ @Override
+ InternalOptions getInternalOptions() {
+ InternalOptions internal = new InternalOptions(new DexItemFactory());
+ internal.debug = getMode() == CompilationMode.DEBUG;
+ internal.minApiLevel = getMinApiLevel();
+ internal.fillDexFiles = true;
+ // Assert and fixup defaults.
+ assert !internal.skipMinification;
+ internal.skipMinification = true;
+ assert internal.useTreeShaking;
+ internal.useTreeShaking = false;
+ assert internal.interfaceMethodDesugaring == OffOrAuto.Off;
+ assert internal.allowAccessModification;
+ internal.allowAccessModification = false;
+ assert internal.inlineAccessors;
+ internal.inlineAccessors = false;
+ assert internal.outline.enabled;
+ internal.outline.enabled = false;
+ internal.lazyClasspathLoading = true;
+ internal.lazyLibraryLoading = true;
+ return internal;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/D8Output.java b/src/main/java/com/android/tools/r8/D8Output.java
new file mode 100644
index 0000000..84f8803
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/D8Output.java
@@ -0,0 +1,21 @@
+// 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;
+
+import com.android.tools.r8.utils.AndroidApp;
+import java.io.IOException;
+import java.nio.file.Path;
+
+/** Represents the output of a D8 compilation. */
+public class D8Output extends BaseOutput {
+
+ D8Output(AndroidApp app) {
+ super(app);
+ }
+
+ @Override
+ public void write(Path output) throws IOException {
+ getAndroidApp().write(output);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/Disassemble.java b/src/main/java/com/android/tools/r8/Disassemble.java
new file mode 100644
index 0000000..71daa5c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/Disassemble.java
@@ -0,0 +1,22 @@
+// 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;
+
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+
+public class Disassemble {
+ public static void main(String[] args)
+ throws IOException, ProguardRuleParserException, CompilationException, ExecutionException {
+ List<Path> files = Arrays.stream(args).map(s -> Paths.get(s)).collect(Collectors.toList());
+ R8Command command = R8Command.builder().addProgramFiles(files).build();
+ R8.disassemble(command);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/IncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/IncrementalDexingBenchmark.java
new file mode 100644
index 0000000..7b9d8aa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/IncrementalDexingBenchmark.java
@@ -0,0 +1,41 @@
+// 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;
+
+import com.android.tools.r8.utils.ThreadUtils;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutorService;
+
+public class IncrementalDexingBenchmark {
+ private static final int ITERATIONS = 1000;
+
+ public static void compile(ExecutorService executor) throws IOException, CompilationException {
+ D8Output output =
+ D8.run(
+ D8Command.builder()
+ .addProgramFiles(Paths.get("build/test/examples/arithmetic.jar"))
+ .setMode(CompilationMode.DEBUG)
+ .build(),
+ executor);
+ if (output.getDexResources().size() != 1) {
+ throw new RuntimeException("WAT");
+ }
+ }
+
+ public static void main(String[] args) throws IOException, CompilationException {
+ int threads = Integer.min(Runtime.getRuntime().availableProcessors(), 16) / 2;
+ ExecutorService executor = ThreadUtils.getExecutorService(threads);
+ try {
+ long start = System.nanoTime();
+ for (int i = 0; i < ITERATIONS; i++) {
+ compile(executor);
+ }
+ double elapsedMs = (System.nanoTime() - start) / 1000000.0;
+ System.out.println("Runtime: " + elapsedMs + " ms");
+ } finally {
+ executor.shutdown();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/PrintClassList.java b/src/main/java/com/android/tools/r8/PrintClassList.java
new file mode 100644
index 0000000..6c49769
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/PrintClassList.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.AndroidApp.Builder;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class PrintClassList {
+
+ public static void main(String[] args) throws IOException, ExecutionException {
+ List<String> dexFiles = Arrays.asList(args);
+ Builder builder = AndroidApp.builder();
+ if (args[0].endsWith("map")) {
+ builder.setProguardMapFile(Paths.get(args[0]));
+ dexFiles = dexFiles.subList(1, dexFiles.size());
+ }
+ builder.addProgramFiles(ListUtils.map(dexFiles, Paths::get));
+
+ ExecutorService executorService = Executors.newCachedThreadPool();
+ DexApplication application =
+ new ApplicationReader(builder.build(), new InternalOptions(), new Timing("PrintClassList"))
+ .read(executorService);
+ ClassNameMapper map = application.getProguardMap();
+ for (DexProgramClass clazz : application.classes()) {
+ System.out.print(maybeDeobfuscateType(map, clazz.type));
+ System.out.println();
+ printMethods(clazz.directMethods(), map);
+ printMethods(clazz.virtualMethods(), map);
+ printFields(clazz.staticFields(), map);
+ printFields(clazz.instanceFields(), map);
+ }
+ executorService.shutdown();
+ }
+
+ private static void printMethods(DexEncodedMethod[] methods, ClassNameMapper map) {
+ for (DexEncodedMethod encodedMethod : methods) {
+ DexMethod method = encodedMethod.method;
+
+ if (map != null) {
+ System.out.println(map.originalNameOf(method));
+ } else {
+ // Detour via Signature to get the same formatting.
+ MethodSignature signature = MethodSignature.fromDexMethod(method);
+ System.out.println(method.holder.toSourceString() + " " + signature);
+ }
+ }
+ }
+
+ private static void printFields(DexEncodedField[] fields, ClassNameMapper map) {
+ for (DexEncodedField encodedField : fields) {
+ DexField field = encodedField.field;
+ if (map != null) {
+ System.out.println(map.originalNameOf(field));
+ } else {
+ // Detour via Signature to get the same formatting.
+ FieldSignature signature = new FieldSignature(field.name.toSourceString(),
+ field.type.toSourceString());
+ System.out.println(field.clazz.toSourceString() + " " + signature);
+ }
+ }
+ }
+
+ private static String maybeDeobfuscateType(ClassNameMapper map, DexType type) {
+ return map == null ? type.toSourceString() : map.originalNameOf(type);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
new file mode 100644
index 0000000..44a687c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -0,0 +1,416 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static com.android.tools.r8.R8Command.USAGE_MESSAGE;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.ClassAndMemberPublicizer;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.naming.Minifier;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.optimize.BridgeMethodAnalysis;
+import com.android.tools.r8.optimize.DebugStripper;
+import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.optimize.VisibilityBridgeRemover;
+import com.android.tools.r8.shaking.AbstractMethodRemover;
+import com.android.tools.r8.shaking.AnnotationRemover;
+import com.android.tools.r8.shaking.DiscardedChecker;
+import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.shaking.ProguardTypeMatcher;
+import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
+import com.android.tools.r8.shaking.ReasonPrinter;
+import com.android.tools.r8.shaking.RootSetBuilder;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.shaking.TreePruner;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.AttributeRemovalOptions;
+import com.android.tools.r8.utils.PackageDistribution;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.ByteArrayOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class R8 {
+
+ private final Timing timing = new Timing("R8");
+ private final InternalOptions options;
+
+ // TODO(zerny): Refactor tests to go through testing methods and make this private.
+ public R8(InternalOptions options) {
+ this.options = options;
+ options.itemFactory.resetSortedIndices();
+ }
+
+ public static AndroidApp writeApplication(
+ ExecutorService executorService,
+ DexApplication application,
+ AppInfo appInfo,
+ NamingLens namingLens,
+ byte[] proguardSeedsData,
+ PackageDistribution packageDistribution,
+ InternalOptions options)
+ throws ExecutionException {
+ try {
+ return new ApplicationWriter(application, appInfo, options, namingLens, proguardSeedsData)
+ .write(packageDistribution, executorService);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot write dex application", e);
+ }
+ }
+
+ public DexApplication optimize(DexApplication application, AppInfoWithSubtyping appInfo)
+ throws IOException, ProguardRuleParserException, ExecutionException {
+ return optimize(application, appInfo, GraphLense.getIdentityLense(),
+ Executors.newSingleThreadExecutor());
+ }
+
+ public DexApplication optimize(DexApplication application, AppInfoWithSubtyping appInfo,
+ GraphLense graphLense, ExecutorService executorService)
+ throws IOException, ProguardRuleParserException, ExecutionException {
+ final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
+
+ timing.begin("Create IR");
+ try {
+ IRConverter converter = new IRConverter(
+ timing, application, appInfo, options, printer, graphLense);
+ application = converter.optimize(executorService);
+ } finally {
+ timing.end();
+ }
+
+ if (!options.skipDebugInfoOpt && (application.getProguardMap() != null)) {
+ try {
+ timing.begin("DebugStripper");
+ DebugStripper stripper = new DebugStripper(application.getProguardMap(), options);
+ application.classes().forEach(stripper::processClass);
+ } finally {
+ timing.end();
+ }
+ }
+
+ if (options.printCfg) {
+ if (options.printCfgFile == null || options.printCfgFile.isEmpty()) {
+ System.out.print(printer.toString());
+ } else {
+ java.io.FileWriter writer = new java.io.FileWriter(options.printCfgFile);
+ writer.write(printer.toString());
+ writer.close();
+ }
+ }
+ return application;
+ }
+
+ private Set<DexType> filterMissingClasses(Set<DexType> missingClasses,
+ Set<ProguardTypeMatcher> dontWarnPatterns) {
+ Set<DexType> result = new HashSet<>(missingClasses);
+ for (ProguardTypeMatcher matcher : dontWarnPatterns) {
+ if (matcher instanceof MatchSpecificType) {
+ result.remove(((MatchSpecificType) matcher).type);
+ } else {
+ result.removeIf(matcher::matches);
+ }
+ }
+ return result;
+ }
+
+ public static void disassemble(R8Command command) throws IOException, ExecutionException {
+ Path output = command.getOutputPath();
+ AndroidApp app = command.getInputApp();
+ InternalOptions options = command.getInternalOptions();
+ ExecutorService executor = ThreadUtils.getExecutorService(options);
+ Timing timing = new Timing("disassemble");
+ try {
+ DexApplication application = new ApplicationReader(app, options, timing).read(executor);
+ if (options.useSmaliSyntax) {
+ if (output != null) {
+ Files.createDirectories(output);
+ try (PrintStream ps = new PrintStream(
+ Files.newOutputStream(output.resolve("classes.smali")))) {
+ application.smali(options, ps);
+ }
+ } else {
+ application.smali(options, System.out);
+ }
+ } else {
+ application.disassemble(output, options);
+ }
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ static AndroidApp runForTesting(AndroidApp app, InternalOptions options)
+ throws ProguardRuleParserException, ExecutionException, IOException {
+ ExecutorService executor = ThreadUtils.getExecutorService(options);
+ try {
+ return runForTesting(app, options, executor);
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ static AndroidApp runForTesting(AndroidApp app, InternalOptions options, ExecutorService executor)
+ throws ProguardRuleParserException, ExecutionException, IOException {
+ return new R8(options).run(app, executor);
+ }
+
+ private AndroidApp run(AndroidApp inputApp, ExecutorService executorService)
+ throws IOException, ExecutionException, ProguardRuleParserException {
+ if (options.quiet) {
+ System.setOut(new PrintStream(ByteStreams.nullOutputStream()));
+ }
+ try {
+ DexApplication application =
+ new ApplicationReader(inputApp, options, timing).read(executorService);
+
+ AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
+ RootSet rootSet;
+ byte[] proguardSeedsData = null;
+ timing.begin("Strip unused code");
+ try {
+ Set<DexType> missingClasses = appInfo.getMissingClasses();
+ missingClasses = filterMissingClasses(missingClasses, options.dontWarnPatterns);
+ if (!missingClasses.isEmpty()) {
+ System.err.println();
+ System.err.println("WARNING, some classes are missing:");
+ missingClasses.forEach(clazz -> {
+ System.err.println(" - " + clazz.toSourceString());
+ });
+ if (!options.ignoreMissingClasses) {
+ throw new CompilationError(
+ "Shrinking can't be performed because some library classes are missing.");
+ }
+ }
+ rootSet = new RootSetBuilder(application, options.keepRules).run(executorService);
+ Enqueuer enqueuer = new Enqueuer(rootSet, appInfo);
+ appInfo = enqueuer.run(timing);
+ if (options.printSeeds) {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ PrintStream out = new PrintStream(bytes);
+ RootSetBuilder.writeSeeds(appInfo.withLiveness().pinnedItems, out);
+ out.flush();
+ proguardSeedsData = bytes.toByteArray();
+ }
+ if (options.useTreeShaking) {
+ application = new TreePruner(application, appInfo.withLiveness(), options).run();
+ // Recompute the subtyping information.
+ appInfo = appInfo.withLiveness().prunedCopyFrom(application);
+ new AbstractMethodRemover(appInfo).run();
+ new AnnotationRemover(appInfo.withLiveness(), options).run();
+ } else if (!options.skipMinification) {
+ // TODO(38188583): Ensure signatures are removed when minifying.
+ new AnnotationRemover(appInfo.withLiveness(), true,
+ AttributeRemovalOptions.filterOnlySignatures());
+ }
+ } finally {
+ timing.end();
+ }
+
+ if (options.allowAccessModification) {
+ ClassAndMemberPublicizer.run(application);
+ // We can now remove visibility bridges. Note that we do not need to update the
+ // invoke-targets here, as the existing invokes will simply dispatch to the now
+ // visible super-method. MemberRebinding, if run, will then dispatch it correctly.
+ application = new VisibilityBridgeRemover(appInfo, application).run();
+ }
+
+ GraphLense graphLense = GraphLense.getIdentityLense();
+
+ if (appInfo.withLiveness() != null) {
+ // No-op until class merger is added.
+ appInfo = appInfo.withLiveness().rewrittenWithLense(graphLense);
+ graphLense = new MemberRebindingAnalysis(appInfo.withLiveness(), graphLense).run();
+ }
+
+ graphLense = new BridgeMethodAnalysis(graphLense, appInfo.withSubtyping()).run();
+
+ application = optimize(application, appInfo, graphLense, executorService);
+ appInfo = new AppInfoWithSubtyping(application);
+
+ if (options.useTreeShaking || !options.skipMinification) {
+ timing.begin("Post optimization code stripping");
+ try {
+ Enqueuer enqueuer = new Enqueuer(rootSet, appInfo);
+ appInfo = enqueuer.run(timing);
+ if (options.useTreeShaking) {
+ application = new TreePruner(application, appInfo.withLiveness(), options).run();
+ appInfo = appInfo.withLiveness().prunedCopyFrom(application);
+ // Print reasons on the application after pruning, so that we reflect the actual result.
+ ReasonPrinter reasonPrinter = enqueuer.getReasonPrinter(rootSet.reasonAsked);
+ reasonPrinter.run(application);
+ }
+ } finally {
+ timing.end();
+ }
+ }
+
+ if (!rootSet.checkDiscarded.isEmpty()) {
+ new DiscardedChecker(rootSet, application).run();
+ }
+
+ timing.begin("Minification");
+ // If we did not have keep rules, everything will be marked as keep, so no minification
+ // will happen. Just avoid the overhead.
+ NamingLens namingLens =
+ options.skipMinification
+ ? NamingLens.getIdentityLens()
+ : new Minifier(appInfo.withLiveness(), rootSet, options).run(timing);
+ timing.end();
+
+ // If a method filter is present don't produce output since the application is likely partial.
+ if (!options.methodsFilter.isEmpty()) {
+ System.out.println("Finished compilation with method filter: ");
+ options.methodsFilter.forEach((m) -> System.out.println(" - " + m));
+ return null;
+ }
+
+ PackageDistribution packageDistribution = null;
+ if (inputApp.hasPackageDistribution()) {
+ try (Closer closer = Closer.create()) {
+ packageDistribution = PackageDistribution.load(inputApp.getPackageDistribution(closer));
+ }
+ }
+
+ // Generate the resulting application resources.
+ AndroidApp androidApp =
+ writeApplication(
+ executorService,
+ application,
+ appInfo,
+ namingLens,
+ proguardSeedsData,
+ packageDistribution,
+ options);
+
+ if (options.printMapping && androidApp.hasProguardMap() && !options.skipMinification) {
+ Path mapOutput = options.printMappingFile;
+ try (Closer closer = Closer.create()) {
+ OutputStream mapOut;
+ if (mapOutput == null) {
+ mapOut = System.out;
+ } else {
+ mapOut = new FileOutputStream(mapOutput.toFile());
+ closer.register(mapOut);
+ }
+ androidApp.writeProguardMap(closer, mapOut);
+ }
+ }
+ options.printWarnings();
+ return androidApp;
+ } finally {
+ // Dump timings.
+ if (options.printTimes) {
+ timing.report();
+ }
+ }
+ }
+
+ /**
+ * Main API entry for the R8 compiler.
+ *
+ * <p>The R8 API is intentionally limited and should "do the right thing" given a command. If this
+ * API does not suffice please contact the R8 team.
+ *
+ * @param command R8 command.
+ * @return the compilation result.
+ */
+ public static AndroidApp run(R8Command command)
+ throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+ AndroidApp outputApp = runForTesting(command.getInputApp(), command.getInternalOptions());
+ if (command.getOutputPath() != null) {
+ outputApp.write(command.getOutputPath());
+ }
+ return outputApp;
+ }
+
+ /**
+ * Main API entry for the R8 compiler.
+ *
+ * <p>The R8 API is intentionally limited and should "do the right thing" given a command. If this
+ * API does not suffice please contact the R8 team.
+ *
+ * @param command R8 command.
+ * @param executor executor service from which to get threads for multi-threaded processing.
+ * @return the compilation result.
+ */
+ public static AndroidApp run(R8Command command, ExecutorService executor)
+ throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+ AndroidApp outputApp =
+ runForTesting(command.getInputApp(), command.getInternalOptions(), executor);
+ if (command.getOutputPath() != null) {
+ outputApp.write(command.getOutputPath());
+ }
+ return outputApp;
+ }
+
+ private static void run(String[] args)
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ R8Command.Builder builder = R8Command.parse(args);
+ if (builder.getOutputPath() == null) {
+ builder.setOutputPath(Paths.get("."));
+ }
+ R8Command command = builder.build();
+ if (command.isPrintHelp()) {
+ System.out.println(USAGE_MESSAGE);
+ return;
+ }
+ if (command.isPrintVersion()) {
+ System.out.println("R8 v0.0.1");
+ return;
+ }
+ run(command);
+ }
+
+ public static void main(String[] args) {
+ try {
+ run(args);
+ } catch (NoSuchFileException e) {
+ System.err.println("File not found: " + e.getFile());
+ System.exit(1);
+ } catch (FileAlreadyExistsException e) {
+ System.err.println("File already exists: " + e.getFile());
+ } catch (IOException e) {
+ System.err.println("Failed to read or write Android app: " + e.getMessage());
+ System.exit(1);
+ } catch (ProguardRuleParserException e) {
+ System.err.println("Failed parsing proguard keep rules: " + e.getMessage());
+ System.exit(1);
+ } catch (RuntimeException | ExecutionException e) {
+ System.err.println("Compilation failed with an internal error.");
+ Throwable cause = e.getCause() == null ? e : e.getCause();
+ cause.printStackTrace();
+ System.exit(1);
+ } catch (CompilationException e) {
+ System.err.println("Compilation failed: " + e.getMessage());
+ System.err.println(USAGE_MESSAGE);
+ System.exit(1);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
new file mode 100644
index 0000000..7343fba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -0,0 +1,313 @@
+// 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;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardConfigurationParser;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+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.Collections;
+import java.util.List;
+import java.util.Optional;
+
+public class R8Command extends BaseCommand {
+
+ public static class Builder extends BaseCommand.Builder<R8Command, Builder> {
+
+ private final List<Path> proguardConfigFiles = new ArrayList<>();
+ private Optional<Boolean> treeShaking = Optional.empty();
+ private Optional<Boolean> minification = Optional.empty();
+ private boolean ignoreMissingClasses = false;
+
+ private Builder() {
+ super(CompilationMode.RELEASE);
+ }
+
+ private Builder(AndroidApp app) {
+ super(app, CompilationMode.RELEASE);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
+
+ /** Enable/disable tree shaking. This overrides any settings in proguard configuration files. */
+ public Builder setTreeShaking(boolean useTreeShaking) {
+ treeShaking = Optional.of(useTreeShaking);
+ return this;
+ }
+
+ /** Enable/disable minification. This overrides any settings in proguard configuration files. */
+ public Builder setMinification(boolean useMinification) {
+ minification = Optional.of(useMinification);
+ return this;
+ }
+
+ /** Add proguard configuration file resources. */
+ public Builder addProguardConfigurationFiles(Path... paths) {
+ Collections.addAll(proguardConfigFiles, paths);
+ return this;
+ }
+
+ /** Add proguard configuration file resources. */
+ public Builder addProguardConfigurationFiles(List<Path> paths) {
+ proguardConfigFiles.addAll(paths);
+ return this;
+ }
+
+ /** Set a proguard mapping file resource. */
+ public Builder setProguardMapFile(Path path) {
+ getAppBuilder().setProguardMapFile(path);
+ return this;
+ }
+
+ /** Set a package distribution file resource. */
+ public Builder setPackageDistributionFile(Path path) {
+ getAppBuilder().setPackageDistributionFile(path);
+ return this;
+ }
+
+ /**
+ * Deprecated flag to avoid failing if classes are missing during compilation.
+ *
+ * <p>TODO: Make compilation safely assume this flag to be true and remove the flag.
+ */
+ Builder setIgnoreMissingClasses(boolean ignoreMissingClasses) {
+ this.ignoreMissingClasses = ignoreMissingClasses;
+ return this;
+ }
+
+ @Override
+ public R8Command build() throws CompilationException, IOException {
+ // If printing versions ignore everything else.
+ if (isPrintHelp() || isPrintVersion()) {
+ return new R8Command(isPrintHelp(), isPrintVersion());
+ }
+
+ DexItemFactory factory = new DexItemFactory();
+ ProguardConfiguration configuration;
+ if (proguardConfigFiles.isEmpty()) {
+ configuration = ProguardConfiguration.defaultConfiguration(factory);
+ } else {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(factory);
+ try {
+ parser.parse(proguardConfigFiles);
+ } catch (ProguardRuleParserException e) {
+ throw new CompilationException(e.getMessage(), e.getCause());
+ }
+ configuration = parser.getConfig();
+ addProgramFiles(configuration.getInjars());
+ addLibraryFiles(configuration.getLibraryjars());
+ }
+
+ boolean useTreeShaking = treeShaking.orElse(configuration.isShrinking());
+ boolean useMinification = minification.orElse(configuration.isObfuscating());
+
+ return new R8Command(
+ getAppBuilder().build(),
+ getOutputPath(),
+ configuration,
+ getMode(),
+ getMinApiLevel(),
+ useTreeShaking,
+ useMinification,
+ ignoreMissingClasses);
+ }
+ }
+
+ // Internal state to verify parsing properties not enforced by the builder.
+ private static class ParseState {
+ CompilationMode mode = null;
+ }
+
+ static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
+ "Usage: r8 [options] <input-files>",
+ " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+ " and options are:",
+ " --debug # Compile with debugging information (default enabled).",
+ " --release # Compile without debugging information.",
+ " --output <file> # Output result in <file>.",
+ " # <file> must be an existing directory or non-existent zip file.",
+ " --lib <file> # Add <file> as a library resource.",
+ " --min-sdk-version # Minimum Android API level compatibility.",
+ " --pg-conf <file> # Proguard configuration <file> (implies tree shaking/minification).",
+ " --pg-map <file> # Proguard map <file>.",
+ " --no-tree-shaking # Force disable tree shaking of unreachable classes.",
+ " --no-minification # Force disable minification of names.",
+ " --version # Print the version of r8.",
+ " --help # Print this message."));
+
+ private final ProguardConfiguration proguardConfiguration;
+ private final boolean useTreeShaking;
+ private final boolean useMinification;
+ private final boolean ignoreMissingClasses;
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ // Internal builder to start from an existing AndroidApp.
+ static Builder builder(AndroidApp app) {
+ return new Builder(app);
+ }
+
+ public static Builder parse(String[] args) throws CompilationException, IOException {
+ Builder builder = builder();
+ parse(args, builder, new ParseState());
+ return builder;
+ }
+
+ private static ParseState parse(String[] args, Builder builder, ParseState state)
+ throws CompilationException, IOException {
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ if (arg.equals("--help")) {
+ builder.setPrintHelp(true);
+ } else if (arg.equals("--version")) {
+ builder.setPrintVersion(true);
+ } else if (arg.equals("--debug")) {
+ if (state.mode == CompilationMode.RELEASE) {
+ throw new CompilationException("Cannot compile in both --debug and --release mode.");
+ }
+ state.mode = CompilationMode.DEBUG;
+ builder.setMode(state.mode);
+ } else if (arg.equals("--release")) {
+ if (state.mode == CompilationMode.DEBUG) {
+ throw new CompilationException("Cannot compile in both --debug and --release mode.");
+ }
+ state.mode = CompilationMode.RELEASE;
+ builder.setMode(state.mode);
+ } else if (arg.equals("--output")) {
+ String outputPath = args[++i];
+ if (builder.getOutputPath() != null) {
+ throw new CompilationException(
+ "Cannot output both to '"
+ + builder.getOutputPath().toString()
+ + "' and '"
+ + outputPath
+ + "'");
+ }
+ builder.setOutputPath(Paths.get(outputPath));
+ } else if (arg.equals("--lib")) {
+ builder.addLibraryFiles(Paths.get(args[++i]));
+ } else if (arg.equals("--min-sdk-version")) {
+ builder.setMinApiLevel(Integer.valueOf(args[++i]));
+ } else if (arg.equals("--no-tree-shaking")) {
+ builder.setTreeShaking(false);
+ } else if (arg.equals("--no-minification")) {
+ builder.setMinification(false);
+ } else if (arg.equals("--pg-conf")) {
+ builder.addProguardConfigurationFiles(Paths.get(args[++i]));
+ } else if (arg.equals("--pg-map")) {
+ builder.setProguardMapFile(Paths.get(args[++i]));
+ } else if (arg.equals("--ignore-missing-classes")) {
+ builder.setIgnoreMissingClasses(true);
+ } else if (arg.startsWith("@")) {
+ // TODO(zerny): Replace this with pipe reading.
+ String argsFile = arg.substring(1);
+ try {
+ List<String> linesInFile = FileUtils.readTextFile(Paths.get(argsFile));
+ List<String> argsInFile = new ArrayList<>();
+ for (String line : linesInFile) {
+ for (String word : line.split("\\s")) {
+ String trimmed = word.trim();
+ if (!trimmed.isEmpty()) {
+ argsInFile.add(trimmed);
+ }
+ }
+ }
+ // TODO(zerny): We need to define what CWD should be for files referenced in an args file.
+ state = parse(argsInFile.toArray(new String[argsInFile.size()]), builder, state);
+ } catch (IOException | CompilationException e) {
+ throw new CompilationException(
+ "Failed to read arguments from file " + argsFile + ": " + e.getMessage());
+ }
+ } else {
+ if (arg.startsWith("--")) {
+ throw new CompilationException("Unknown option: " + arg);
+ }
+ builder.addProgramFiles(Paths.get(arg));
+ }
+ }
+ return state;
+ }
+
+ private R8Command(
+ AndroidApp inputApp,
+ Path outputPath,
+ ProguardConfiguration proguardConfiguration,
+ CompilationMode mode,
+ int minApiLevel,
+ boolean useTreeShaking,
+ boolean useMinification,
+ boolean ignoreMissingClasses) {
+ super(inputApp, outputPath, mode, minApiLevel);
+ assert proguardConfiguration != null;
+ this.proguardConfiguration = proguardConfiguration;
+ this.useTreeShaking = useTreeShaking;
+ this.useMinification = useMinification;
+ this.ignoreMissingClasses = ignoreMissingClasses;
+ }
+
+ private R8Command(boolean printHelp, boolean printVersion) {
+ super(printHelp, printVersion);
+ proguardConfiguration = null;
+ useTreeShaking = false;
+ useMinification = false;
+ ignoreMissingClasses = false;
+ }
+
+ public boolean useTreeShaking() {
+ return useTreeShaking;
+ }
+
+ public boolean useMinification() {
+ return useMinification;
+ }
+
+ InternalOptions getInternalOptions() {
+ InternalOptions internal = new InternalOptions(proguardConfiguration.getDexItemFactory());
+ internal.debug = getMode() == CompilationMode.DEBUG;
+ internal.minApiLevel = getMinApiLevel();
+ internal.skipMinification = !useMinification();
+ internal.useTreeShaking = useTreeShaking();
+ internal.ignoreMissingClasses = ignoreMissingClasses;
+
+ // TODO(zerny): Consider which other proguard options should be given flags.
+ internal.packagePrefix = proguardConfiguration.getPackagePrefix();
+ internal.allowAccessModification = proguardConfiguration.getAllowAccessModification();
+ for (String pattern : proguardConfiguration.getAttributesRemovalPatterns()) {
+ internal.attributeRemoval.applyPattern(pattern);
+ }
+ if (proguardConfiguration.isIgnoreWarnings()) {
+ internal.ignoreMissingClasses = true;
+ }
+ if (proguardConfiguration.getSeedFile() != null) {
+ internal.seedsFile = proguardConfiguration.getSeedFile();
+ }
+ if (proguardConfiguration.isVerbose()) {
+ internal.verbose = true;
+ }
+ if (!proguardConfiguration.isObfuscating()) {
+ internal.skipMinification = true;
+ }
+ internal.printSeeds |= proguardConfiguration.getPrintSeeds();
+ internal.printMapping |= proguardConfiguration.isPrintingMapping();
+ internal.printMappingFile = proguardConfiguration.getPrintMappingOutput();
+ internal.classObfuscationDictionary = proguardConfiguration.getClassObfuscationDictionary();
+ internal.obfuscationDictionary = proguardConfiguration.getObfuscationDictionary();
+ internal.keepRules = proguardConfiguration.getRules();
+ internal.dontWarnPatterns = proguardConfiguration.getDontWarnPatterns();
+ return internal;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ReadKeepFile.java b/src/main/java/com/android/tools/r8/ReadKeepFile.java
new file mode 100644
index 0000000..355d42c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ReadKeepFile.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardConfigurationParser;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.Paths;
+
+/**
+ * Benchmark for testing ability to and speed of parsing Proguard keep files.
+ */
+public class ReadKeepFile {
+
+ private static final String DEFAULT_KEEP_FILE_NAME = "build/proguard.cfg";
+
+ final Timing timing = new Timing("ReadKeepFile");
+
+ private void readProguardKeepFile(String fileName) throws ProguardRuleParserException {
+ try {
+ System.out.println(" - reading " + fileName);
+ timing.begin("Reading " + fileName);
+ new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(fileName));
+ timing.end();
+ } catch (IOException e) {
+ System.err.print("Failed to parse Proguard keep file: " + e.getMessage());
+ }
+ }
+
+ public static void main(String[] args) throws ProguardRuleParserException {
+ new ReadKeepFile().run(args);
+ }
+
+ private void run(String[] args) throws ProguardRuleParserException {
+ System.out.println("ProguardKeepRuleParser benchmark.");
+ if (args.length == 0) {
+ readProguardKeepFile(DEFAULT_KEEP_FILE_NAME);
+ } else {
+ for (String name : args) {
+ readProguardKeepFile(name);
+ }
+ }
+ timing.report();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ReadProguardMap.java b/src/main/java/com/android/tools/r8/ReadProguardMap.java
new file mode 100644
index 0000000..2ea2ca7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ReadProguardMap.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.naming.ProguardMapReader;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+/**
+ * Benchmark for testing speed of parsing Proguard mapping files.
+ */
+public class ReadProguardMap {
+
+ private static final String DEFAULT_MAP_FILE_NAME = "third_party/gmscore/v5/proguard.map";
+
+ final Timing timing = new Timing("ReadProguardMap");
+
+ private void readProguardMapFile(String fileName) {
+ try {
+ System.out.println(" - reading " + fileName);
+ timing.begin("Reading " + fileName);
+ ProguardMapReader.mapperFromFile(Paths.get(fileName));
+ timing.end();
+ } catch (IOException e) {
+ System.err.print("Failed to parse Proguard mapping file: " + e.getMessage());
+ }
+ }
+
+ public static void main(String[] args) {
+ new ReadProguardMap().run(args);
+ }
+
+ private void run(String[] args) {
+ System.out.println("ReadProguardMap benchmark.");
+ if (args.length == 0) {
+ readProguardMapFile(DEFAULT_MAP_FILE_NAME);
+ } else {
+ Arrays.asList(args).forEach((String name) -> readProguardMapFile(name));
+ }
+ timing.report();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/Resource.java b/src/main/java/com/android/tools/r8/Resource.java
new file mode 100644
index 0000000..8ea23d1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/Resource.java
@@ -0,0 +1,27 @@
+// 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;
+
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Represents application resources. */
+public abstract class Resource {
+
+ /** Application resource kind. */
+ public enum Kind {
+ PROGRAM, CLASSPATH, LIBRARY
+ }
+
+ /** Kind of the resource. */
+ public final Kind kind;
+
+ protected Resource(Kind kind) {
+ this.kind = kind;
+ }
+
+ /** Get the resource as a stream. */
+ public abstract InputStream getStream(Closer closer) throws IOException;
+}
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
new file mode 100644
index 0000000..b48f268
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -0,0 +1,203 @@
+// 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.bisect;
+
+import com.android.tools.r8.bisect.BisectOptions.Result;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.io.CharStreams;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class Bisect {
+
+ private final BisectOptions options;
+ private final DexItemFactory factory = new DexItemFactory();
+ private final Timing timing = new Timing("bisect");
+
+ public interface Command {
+
+ Result apply(DexApplication application) throws Exception;
+ }
+
+ private static class StreamReader implements Runnable {
+
+ private InputStream stream;
+ private String result;
+
+ public StreamReader(InputStream stream) {
+ this.stream = stream;
+ }
+
+ public String getResult() {
+ return result;
+ }
+
+ @Override
+ public void run() {
+ try {
+ result = CharStreams.toString(new InputStreamReader(stream, StandardCharsets.UTF_8));
+ stream.close();
+ } catch (IOException e) {
+ result = "Failed reading result for stream " + stream;
+ }
+ }
+ }
+
+ public Bisect(BisectOptions options) {
+ this.options = options;
+ }
+
+ public static DexProgramClass run(BisectState state, Command command, Path output,
+ ExecutorService executor)
+ throws Exception {
+ while (true) {
+ DexApplication app = state.bisect();
+ state.write();
+ if (app == null) {
+ return state.getFinalClass();
+ }
+ if (command == null) {
+ writeApp(app, output, executor);
+ System.out.println("Bisecting completed with build in " + output + "/");
+ System.out.println("Continue bisection by passing either --"
+ + BisectOptions.RESULT_GOOD_FLAG + " or --"
+ + BisectOptions.RESULT_BAD_FLAG);
+ return null;
+ }
+ state.setPreviousResult(command.apply(app));
+ }
+ }
+
+ public DexProgramClass run() throws Exception {
+ // Setup output directory (or write to a temp dir).
+ Path output;
+ if (options.output != null) {
+ output = options.output.toPath();
+ } else {
+ File temp = File.createTempFile("bisect", "", new File("/tmp"));
+ temp.delete();
+ temp.mkdir();
+ output = temp.toPath();
+ }
+
+ ExecutorService executor = Executors.newWorkStealingPool();
+ try {
+ DexApplication goodApp = readApp(options.goodBuild, executor);
+ DexApplication badApp = readApp(options.badBuild, executor);
+
+ File stateFile = options.stateFile != null
+ ? options.stateFile
+ : output.resolve("bisect.state").toFile();
+
+ // Setup initial (or saved) bisection state.
+ BisectState state = new BisectState(goodApp, badApp, stateFile);
+ if (options.stateFile != null) {
+ state.read();
+ }
+
+ // If a "previous" result is supplied on the command line, record it.
+ if (options.result != Result.UNKNOWN) {
+ state.setPreviousResult(options.result);
+ }
+
+ // Setup post-build command.
+ Command command = null;
+ if (options.command != null) {
+ command = (application) -> {
+ writeApp(application, output, executor);
+ return runCommand(output);
+ };
+ }
+
+ // Run bisection.
+ return run(state, command, output, executor);
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ private Result runCommand(Path output) throws IOException {
+ List<String> args = new ArrayList<>();
+ args.add("/bin/bash");
+ args.add(options.command.toString());
+ args.add(output.toString());
+ ProcessBuilder builder = new ProcessBuilder(args);
+ Process process = builder.start();
+ StreamReader stdoutReader = new StreamReader(process.getInputStream());
+ StreamReader stderrReader = new StreamReader(process.getErrorStream());
+ Thread stdoutThread = new Thread(stdoutReader);
+ Thread stderrThread = new Thread(stderrReader);
+ stdoutThread.start();
+ stderrThread.start();
+ try {
+ process.waitFor();
+ stdoutThread.join();
+ stderrThread.join();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Execution interrupted", e);
+ }
+ int result = process.exitValue();
+ if (result == 0) {
+ return Result.GOOD;
+ } else if (result == 1) {
+ return Result.BAD;
+ }
+ System.out.println("Failed to run command " + args);
+ System.out.println("Exit code: " + result + " (expected 0 for good, 1 for bad)");
+ System.out.println("Std out:\n" + stdoutReader.getResult());
+ System.out.println("Std err:\n" + stderrReader.getResult());
+ throw new CompilationError("Failed to run command " + args);
+ }
+
+ private DexApplication readApp(File apk, ExecutorService executor)
+ throws IOException, ExecutionException {
+ AndroidApp app = AndroidApp.fromProgramFiles(apk.toPath());
+ return new ApplicationReader(app, new InternalOptions(), timing).read(executor);
+ }
+
+ private static void writeApp(DexApplication app, Path output, ExecutorService executor)
+ throws IOException, ExecutionException {
+ InternalOptions options = new InternalOptions();
+ AppInfo appInfo = new AppInfo(app);
+ ApplicationWriter writer = new ApplicationWriter(app, appInfo, options, null, null);
+ AndroidApp outApp = writer.write(null, executor);
+ outApp.writeToDirectory(output);
+ }
+
+ public static void main(String[] args) throws Exception {
+ BisectOptions options = null;
+ try {
+ options = BisectOptions.parse(args);
+ } catch (CompilationError e) {
+ System.err.println(e.getMessage());
+ BisectOptions.printHelp(System.err);
+ return;
+ }
+ if (options == null) {
+ return;
+ }
+ DexProgramClass clazz = new Bisect(options).run();
+ if (clazz != null) {
+ System.out.println("Bisection found final bad class " + clazz);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/bisect/BisectOptions.java b/src/main/java/com/android/tools/r8/bisect/BisectOptions.java
new file mode 100644
index 0000000..d0051dc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/bisect/BisectOptions.java
@@ -0,0 +1,152 @@
+// 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.bisect;
+
+import com.android.tools.r8.errors.CompilationError;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+public class BisectOptions {
+ private static final String HELP_FLAG = "help";
+ public static final String BUILD_GOOD_FLAG = "good";
+ public static final String BUILD_BAD_FLAG = "bad";
+ public static final String RESULT_GOOD_FLAG = "result-good";
+ public static final String RESULT_BAD_FLAG = "result-bad";
+ public static final String STATE_FLAG = "state";
+ public static final String OUTPUT_FLAG = "output";
+ public static final String COMMAND_FLAG = "command";
+
+ public final File goodBuild;
+ public final File badBuild;
+ public final File stateFile;
+ public final File command;
+ public final File output;
+ public final Result result;
+
+ public enum Result { UNKNOWN, GOOD, BAD }
+
+ private static class ParserSpec {
+ OptionSpec<String> goodBuild;
+ OptionSpec<String> badBuild;
+ OptionSpec<String> command;
+ OptionSpec<String> stateFile;
+ OptionSpec<String> output;
+ OptionSpec<Void> resultGood;
+ OptionSpec<Void> resultBad;
+ OptionSpec<Void> help;
+
+ void init(OptionParser parser) {
+ help = parser.accepts(HELP_FLAG).forHelp();
+ resultGood = parser.accepts(RESULT_GOOD_FLAG, "Bisect again assuming previous run was good.");
+ resultBad = parser.accepts(RESULT_BAD_FLAG, "Bisect again assuming previous run was bad.");
+ goodBuild = parser.accepts(BUILD_GOOD_FLAG, "Known good APK.")
+ .withRequiredArg()
+ .describedAs("apk");
+ badBuild = parser.accepts(BUILD_BAD_FLAG, "Known bad APK.")
+ .withRequiredArg()
+ .describedAs("apk");
+ stateFile = parser.accepts(STATE_FLAG, "Bisection state.")
+ .requiredIf(resultGood, resultBad)
+ .withRequiredArg()
+ .describedAs("file");
+ output = parser.accepts(OUTPUT_FLAG, "Output directory.")
+ .withRequiredArg()
+ .describedAs("dir");
+ command = parser.accepts(COMMAND_FLAG, "Command to run after each bisection.")
+ .requiredUnless(stateFile)
+ .withRequiredArg()
+ .describedAs("file");
+ }
+
+ OptionSet parse(String[] args) {
+ OptionParser parser = new OptionParser();
+ init(parser);
+ return parser.parse(args);
+ }
+
+ static void printHelp(OutputStream out) throws IOException {
+ OptionParser parser = new OptionParser();
+ new ParserSpec().init(parser);
+ parser.printHelpOn(out);
+ }
+ }
+
+ private BisectOptions(File goodBuild, File badBuild, File stateFile, File command, File output,
+ Result result) {
+ this.goodBuild = goodBuild;
+ this.badBuild = badBuild;
+ this.stateFile = stateFile;
+ this.command = command;
+ this.output = output;
+ this.result = result;
+ }
+
+ public static BisectOptions parse(String[] args) throws IOException {
+ ParserSpec parser = new ParserSpec();
+ OptionSet options = parser.parse(args);
+ if (options.has(parser.help)) {
+ printHelp(System.out);
+ return null;
+ }
+ File goodBuild = exists(require(options, parser.goodBuild, BUILD_GOOD_FLAG), BUILD_GOOD_FLAG);
+ File badBuild = exists(require(options, parser.badBuild, BUILD_BAD_FLAG), BUILD_BAD_FLAG);
+ File stateFile = null;
+ if (options.valueOf(parser.stateFile) != null) {
+ stateFile = exists(options.valueOf(parser.stateFile), STATE_FLAG);
+ }
+ File command = null;
+ if (options.valueOf(parser.command) != null) {
+ command = exists(options.valueOf(parser.command), COMMAND_FLAG);
+ }
+ File output = null;
+ if (options.valueOf(parser.output) != null) {
+ output = directoryExists(options.valueOf(parser.output), OUTPUT_FLAG);
+ }
+ Result result = Result.UNKNOWN;
+ if (options.has(parser.resultGood)) {
+ result = Result.GOOD;
+ }
+ if (options.has(parser.resultBad)) {
+ if (result == Result.GOOD) {
+ throw new CompilationError("Cannot specify --" + RESULT_GOOD_FLAG
+ + " and --" + RESULT_BAD_FLAG + " simultaneously");
+ }
+ result = Result.BAD;
+ }
+ return new BisectOptions(goodBuild, badBuild, stateFile, command, output, result);
+ }
+
+ private static <T> T require(OptionSet options, OptionSpec<T> option, String flag)
+ throws IOException {
+ T value = options.valueOf(option);
+ if (value != null) {
+ return value;
+ }
+ throw new CompilationError("Missing required option: --" + flag);
+ }
+
+ private static File exists(String path, String flag) throws IOException {
+ File file = new File(path);
+ if (file.exists()) {
+ return file;
+ }
+ throw new CompilationError("File --" + flag + ": " + file + " does not exist");
+ }
+
+ private static File directoryExists(String path, String flag) throws IOException {
+ File file = new File(path);
+ if (file.exists() && file.isDirectory()) {
+ return file;
+ }
+ throw new CompilationError("File --" + flag + ": " + file + " is not a valid directory");
+ }
+
+ public static void printHelp(OutputStream out) throws IOException {
+ ParserSpec.printHelp(out);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/bisect/BisectState.java b/src/main/java/com/android/tools/r8/bisect/BisectState.java
new file mode 100644
index 0000000..945578a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/bisect/BisectState.java
@@ -0,0 +1,347 @@
+// 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.bisect;
+
+import com.android.tools.r8.bisect.BisectOptions.Result;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexClassPromise;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.NamingLens;
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.hash.Hashing;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class BisectState {
+
+ private static class Range {
+ final int start;
+ final int end;
+
+ public Range(int start, int end) {
+ this.start = start;
+ this.end = end;
+ assert verify();
+ }
+
+ public Range(String range) {
+ int sep = range.indexOf(' ');
+ start = Integer.parseInt(range.substring(0, sep).trim());
+ end = Integer.parseInt(range.substring(sep + 1).trim());
+ assert verify();
+ }
+
+ public void write(Writer writer) throws IOException {
+ writer.write("" + start);
+ writer.write(" ");
+ writer.write("" + end);
+ }
+
+ public boolean isEmpty() {
+ return start == end;
+ }
+
+ public int size() {
+ return end - start;
+ }
+
+ public Range add(Range other) {
+ if (isEmpty()) {
+ return other;
+ }
+ if (other.isEmpty()) {
+ return this;
+ }
+ assert start == other.end || end == other.start;
+ return new Range(Integer.min(start, other.start), Integer.max(end, other.end));
+ }
+
+ public Range sub(Range other) {
+ if (other.isEmpty()) {
+ return this;
+ }
+ assert start <= other.start && other.end <= end;
+ if (start == other.start) {
+ return new Range(other.end, end);
+ }
+ assert end == other.end;
+ return new Range(start, other.start);
+ }
+
+ public Range split() {
+ int length = size() / 2;
+ return new Range(start, start + length);
+ }
+
+ public boolean contains(int index) {
+ return start <= index && index < end;
+ }
+
+ @Override
+ public String toString() {
+ return "["+start+";"+end+"]";
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof Range)) {
+ return false;
+ }
+ Range o = (Range) other;
+ return start == o.start && end == o.end;
+ }
+
+ private boolean verify() {
+ return start <= end;
+ }
+ }
+
+ private static class Run {
+ final boolean good;
+ final Range range;
+
+ public Run(Result result, Range range) {
+ assert result != Result.UNKNOWN;
+ good = result == Result.GOOD;
+ this.range = range;
+ }
+
+ public Run(String nonLastEntry) {
+ int sep1 = nonLastEntry.indexOf(':');
+ good = nonLastEntry.substring(0, sep1).trim().equals("good");
+ String rangeEntry = nonLastEntry.substring(sep1 + 1).trim();
+ range = new Range(rangeEntry);
+ }
+
+ public void write(Writer writer) throws IOException {
+ writer.write(good ? "good" : "bad");
+ writer.write(':');
+ range.write(writer);
+ }
+
+ public boolean isGood() {
+ return good;
+ }
+
+ public boolean isBad() {
+ return !good;
+ }
+ }
+
+ private final String signature;
+ private final DexApplication goodApp;
+ private final DexApplication badApp;
+ private final List<DexProgramClass> sortedGoodClasses;
+ private final Map<DexType, Integer> indexMap;
+ private final File stateFile;
+
+ private List<Run> runs = new ArrayList<>();
+
+ // Computed data
+ private Range nextRange = null;
+
+ public BisectState(DexApplication goodApp, DexApplication badApp, File stateFile) {
+ this.goodApp = goodApp;
+ this.badApp = badApp;
+ this.stateFile = stateFile;
+ signature = makeSignature(goodApp);
+ if (!signature.equals(makeSignature(badApp))) {
+ throw new CompilationError(
+ "Bisecting application classes do not match classes in reference APK");
+ }
+ sortedGoodClasses = ImmutableList.copyOf(getSortedClasses(goodApp));
+ ImmutableMap.Builder<DexType, Integer> builder = ImmutableMap.builder();
+ for (int i = 0; i < sortedGoodClasses.size(); i++) {
+ builder.put(sortedGoodClasses.get(i).type, i);
+ }
+ indexMap = builder.build();
+ }
+
+ public void read() throws IOException {
+ if (stateFile == null) {
+ return;
+ }
+ List<String> data = new ArrayList<>();
+ try (BufferedReader reader = new BufferedReader(new FileReader(stateFile))) {
+ if (!signature.equals(readSignature(reader))) {
+ throw new CompilationError(
+ "Bisection state file does not match the reference build signature");
+ }
+ String run;
+ while ((run = reader.readLine()) != null) {
+ data.add(run);
+ }
+ }
+ if (data.isEmpty()) {
+ return;
+ }
+ runs = new ArrayList<>(data.size());
+ for (int i = 0; i < data.size() - 1; i++) {
+ runs.add(new Run(data.get(i)));
+ }
+ nextRange = new Range(data.get(data.size() - 1));
+ }
+
+ public void setPreviousResult(Result result) {
+ if (nextRange == null) {
+ throw new CompilationError(
+ "Invalid bisection state. Could not find information on previous runs.");
+ }
+ if (runs.size() == 0) {
+ assert nextRange.equals(new Range(0, 0));
+ if (result != Result.GOOD) {
+ throw new CompilationError(
+ "Expected good state for reference application run, got " + result);
+ }
+ }
+ if (runs.size() == 1) {
+ assert nextRange.equals(new Range(0, indexMap.size()));
+ if (result != Result.BAD) {
+ throw new CompilationError(
+ "Expected bad state for input application run, got " + result);
+ }
+ }
+ runs.add(new Run(result, nextRange));
+ System.out.println("Marked range " + nextRange + ": " + result);
+ nextRange = null;
+ }
+
+ public void verifySignature(DexApplication application) {
+ if (signatureMismatch(makeSignature(application))) {
+ throw new CompilationError(
+ "Bisection state file does not match the application signature");
+ }
+ }
+
+ public DexProgramClass getFinalClass() {
+ if (nextRange.size() == 1) {
+ int index = nextRange.start;
+ return (DexProgramClass) badApp.definitionFor(sortedGoodClasses.get(index).type);
+ }
+ return null;
+ }
+
+ public DexApplication bisect() {
+ assert nextRange == null;
+ if (runs.isEmpty()) {
+ // First run is a sanity check ensuring that the reference app results in a good state.
+ nextRange = new Range(0, 0);
+ } else if (runs.size() == 1) {
+ // Second run is a sanity check ensuring that full application results in a bad state.
+ nextRange = new Range(0, sortedGoodClasses.size());
+ } else {
+ // Subsequent runs continue with half of the currently-known bad range.
+ Range badRange = getLastBadRange();
+ if (badRange.isEmpty()) {
+ throw new CompilationError("Bad range is empty. Cannot continue bisecting :-(");
+ }
+ if (badRange.size() == 1) {
+ nextRange = badRange;
+ return null;
+ }
+ System.out.println("Last bad range: " + badRange);
+ nextRange = badRange.split();
+ }
+ System.out.println("Next bisection range: " + nextRange);
+ int goodClasses = 0;
+ int badClasses = 0;
+ Map<DexType, DexClassPromise> classes = new HashMap<>();
+ for (DexLibraryClass clazz : badApp.libraryClasses()) {
+ classes.put(clazz.type, clazz);
+ }
+ for (DexProgramClass clazz : badApp.classes()) {
+ DexProgramClass goodClass = getGoodClass(clazz);
+ if (goodClass != null) {
+ classes.put(goodClass.type, goodClass);
+ ++goodClasses;
+ } else {
+ classes.put(clazz.type, clazz);
+ assert !nextRange.isEmpty();
+ ++badClasses;
+ }
+ }
+ System.out.println("Class split is good: " + goodClasses + ", bad: " + badClasses);
+ return new Builder(badApp, classes).build();
+ }
+
+ private DexProgramClass getGoodClass(DexProgramClass clazz) {
+ Integer index = indexMap.get(clazz.type);
+ if (index != null && !nextRange.contains(index)) {
+ return sortedGoodClasses.get(index);
+ }
+ return null;
+ }
+
+ private Range getLastBadRange() {
+ Range good = new Range(0, 0);
+ for (int i = runs.size() - 1; i >= 0; i--) {
+ Run run = runs.get(i);
+ if (run.isBad()) {
+ return run.range.sub(good);
+ }
+ good = good.add(run.range);
+ }
+ throw new Unreachable("Did not find any bad range in bisection state");
+ }
+
+ private boolean signatureMismatch(String appSignature) {
+ return !signature.equals(appSignature);
+ }
+
+ private static String readSignature(BufferedReader reader) throws IOException {
+ return reader.readLine();
+ }
+
+ public void write() throws IOException {
+ if (stateFile == null) {
+ return;
+ }
+ try (FileWriter writer = new FileWriter(stateFile, false)) {
+ writer.write(signature);
+ writer.write("\n");
+ for (Run run : runs) {
+ run.write(writer);
+ writer.write("\n");
+ }
+ nextRange.write(writer);
+ writer.write("\n");
+ writer.flush();
+ }
+ }
+
+ private static List<DexProgramClass> getSortedClasses(DexApplication app) {
+ List<DexProgramClass> classes = new ArrayList<>();
+ for (DexProgramClass clazz : app.classes()) {
+ classes.add(clazz);
+ }
+ app.dexItemFactory.sort(NamingLens.getIdentityLens());
+ classes.sort((a, b) -> a.type.compareTo(b.type));
+ app.dexItemFactory.resetSortedIndices();
+ return classes;
+ }
+
+ private static String makeSignature(DexApplication app) {
+ List<DexProgramClass> classes = getSortedClasses(app);
+ StringBuilder builder = new StringBuilder();
+ for (DexProgramClass clazz : classes) {
+ builder.append(clazz.toString()).append(";");
+ }
+ return Hashing.sha1().hashString(builder.toString(), Charsets.UTF_8).toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddDouble.java b/src/main/java/com/android/tools/r8/code/AddDouble.java
new file mode 100644
index 0000000..11d431c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddDouble.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AddDouble extends Format23x {
+
+ public static final int OPCODE = 0xab;
+ public static final String NAME = "AddDouble";
+ public static final String SMALI_NAME = "add-double";
+
+ AddDouble(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AddDouble(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAdd(NumericType.DOUBLE, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddDouble2Addr.java b/src/main/java/com/android/tools/r8/code/AddDouble2Addr.java
new file mode 100644
index 0000000..b910977
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddDouble2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AddDouble2Addr extends Format12x {
+
+ public static final int OPCODE = 0xcb;
+ public static final String NAME = "AddDouble2Addr";
+ public static final String SMALI_NAME = "add-double/2addr";
+
+ AddDouble2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AddDouble2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAdd(NumericType.DOUBLE, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddFloat.java b/src/main/java/com/android/tools/r8/code/AddFloat.java
new file mode 100644
index 0000000..e13c1a8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddFloat.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AddFloat extends Format23x {
+
+ public static final int OPCODE = 0xa6;
+ public static final String NAME = "AddFloat";
+ public static final String SMALI_NAME = "add-float";
+
+ AddFloat(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AddFloat(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAdd(NumericType.FLOAT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddFloat2Addr.java b/src/main/java/com/android/tools/r8/code/AddFloat2Addr.java
new file mode 100644
index 0000000..867694e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddFloat2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AddFloat2Addr extends Format12x {
+
+ public static final int OPCODE = 0xc6;
+ public static final String NAME = "AddFloat2Addr";
+ public static final String SMALI_NAME = "add-float/2addr";
+
+ AddFloat2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AddFloat2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAdd(NumericType.FLOAT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddInt.java b/src/main/java/com/android/tools/r8/code/AddInt.java
new file mode 100644
index 0000000..fccb17e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AddInt extends Format23x {
+
+ public static final int OPCODE = 0x90;
+ public static final String NAME = "AddInt";
+ public static final String SMALI_NAME = "add-int";
+
+ AddInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AddInt(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAdd(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddInt2Addr.java b/src/main/java/com/android/tools/r8/code/AddInt2Addr.java
new file mode 100644
index 0000000..243bc73
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddInt2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AddInt2Addr extends Format12x {
+
+ public static final int OPCODE = 0xb0;
+ public static final String NAME = "AddInt2Addr";
+ public static final String SMALI_NAME = "add-int/2addr";
+
+ AddInt2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AddInt2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAdd(NumericType.INT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddIntLit16.java b/src/main/java/com/android/tools/r8/code/AddIntLit16.java
new file mode 100644
index 0000000..a2d458a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddIntLit16.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AddIntLit16 extends Format22s {
+
+ public static final int OPCODE = 0xd0;
+ public static final String NAME = "AddIntLit16";
+ public static final String SMALI_NAME = "add-int/lit16";
+
+ AddIntLit16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AddIntLit16(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAddLiteral(NumericType.INT, A, B, CCCC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddIntLit8.java b/src/main/java/com/android/tools/r8/code/AddIntLit8.java
new file mode 100644
index 0000000..c736cbe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddIntLit8.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AddIntLit8 extends Format22b {
+
+ public static final int OPCODE = 0xd8;
+ public static final String NAME = "AddIntLit8";
+ public static final String SMALI_NAME = "add-int/lit8";
+
+ AddIntLit8(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AddIntLit8(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAddLiteral(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddLong.java b/src/main/java/com/android/tools/r8/code/AddLong.java
new file mode 100644
index 0000000..a50490a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AddLong extends Format23x {
+
+ public static final int OPCODE = 0x9b;
+ public static final String NAME = "AddLong";
+ public static final String SMALI_NAME = "add-long";
+
+ AddLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AddLong(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAdd(NumericType.LONG, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddLong2Addr.java b/src/main/java/com/android/tools/r8/code/AddLong2Addr.java
new file mode 100644
index 0000000..fbd0c78
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddLong2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AddLong2Addr extends Format12x {
+
+ public static final int OPCODE = 0xbb;
+ public static final String NAME = "AddLong2Addr";
+ public static final String SMALI_NAME = "add-long/2addr";
+
+ AddLong2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AddLong2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAdd(NumericType.LONG, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Aget.java b/src/main/java/com/android/tools/r8/code/Aget.java
new file mode 100644
index 0000000..57216ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Aget.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Aget extends Format23x {
+
+ public static final int OPCODE = 0x44;
+ public static final String NAME = "Aget";
+ public static final String SMALI_NAME = "aget";
+
+ /*package*/ Aget(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public Aget(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayGet(MemberType.SINGLE, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AgetBoolean.java b/src/main/java/com/android/tools/r8/code/AgetBoolean.java
new file mode 100644
index 0000000..fbae5e1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AgetBoolean.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AgetBoolean extends Format23x {
+
+ public static final int OPCODE = 0x47;
+ public static final String NAME = "AgetBoolean";
+ public static final String SMALI_NAME = "aget-boolean";
+
+ AgetBoolean(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AgetBoolean(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayGet(MemberType.BOOLEAN, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AgetByte.java b/src/main/java/com/android/tools/r8/code/AgetByte.java
new file mode 100644
index 0000000..bc30152
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AgetByte.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AgetByte extends Format23x {
+
+ public static final int OPCODE = 0x48;
+ public static final String NAME = "AgetByte";
+ public static final String SMALI_NAME = "aget-byte";
+
+ /*package*/ AgetByte(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AgetByte(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayGet(MemberType.BYTE, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AgetChar.java b/src/main/java/com/android/tools/r8/code/AgetChar.java
new file mode 100644
index 0000000..f46fedf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AgetChar.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AgetChar extends Format23x {
+
+ public static final int OPCODE = 0x49;
+ public static final String NAME = "AgetChar";
+ public static final String SMALI_NAME = "aget-char";
+
+ /*package*/ AgetChar(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AgetChar(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayGet(MemberType.CHAR, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AgetObject.java b/src/main/java/com/android/tools/r8/code/AgetObject.java
new file mode 100644
index 0000000..7ddd0f7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AgetObject.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AgetObject extends Format23x {
+
+ public static final int OPCODE = 0x46;
+ public static final String NAME = "AgetObject";
+ public static final String SMALI_NAME = "aget-object";
+
+ AgetObject(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AgetObject(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayGet(MemberType.OBJECT, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AgetShort.java b/src/main/java/com/android/tools/r8/code/AgetShort.java
new file mode 100644
index 0000000..bc54dee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AgetShort.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AgetShort extends Format23x {
+
+ public static final int OPCODE = 0x4a;
+ public static final String NAME = "AgetShort";
+ public static final String SMALI_NAME = "aget-short";
+
+ AgetShort(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AgetShort(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayGet(MemberType.SHORT, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AgetWide.java b/src/main/java/com/android/tools/r8/code/AgetWide.java
new file mode 100644
index 0000000..a788da8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AgetWide.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AgetWide extends Format23x {
+
+ public static final int OPCODE = 0x45;
+ public static final String NAME = "AgetWide";
+ public static final String SMALI_NAME = "aget-wide";
+
+ /*package*/ AgetWide(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AgetWide(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayGet(MemberType.WIDE, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AndInt.java b/src/main/java/com/android/tools/r8/code/AndInt.java
new file mode 100644
index 0000000..1631409
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AndInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AndInt extends Format23x {
+
+ public static final int OPCODE = 0x95;
+ public static final String NAME = "AndInt";
+ public static final String SMALI_NAME = "and-int";
+
+ AndInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AndInt(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAnd(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AndInt2Addr.java b/src/main/java/com/android/tools/r8/code/AndInt2Addr.java
new file mode 100644
index 0000000..92f29e9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AndInt2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AndInt2Addr extends Format12x {
+
+ public static final int OPCODE = 0xb5;
+ public static final String NAME = "AndInt2Addr";
+ public static final String SMALI_NAME = "and-int/2addr";
+
+ AndInt2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AndInt2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAnd(NumericType.INT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AndIntLit16.java b/src/main/java/com/android/tools/r8/code/AndIntLit16.java
new file mode 100644
index 0000000..d440125
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AndIntLit16.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AndIntLit16 extends Format22s {
+
+ public static final int OPCODE = 0xd5;
+ public static final String NAME = "AndIntLit16";
+ public static final String SMALI_NAME = "and-int/lit16";
+
+ AndIntLit16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AndIntLit16(int dest, int left, int constant) {
+ super(dest, left, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAndLiteral(NumericType.INT, A, B, CCCC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AndIntLit8.java b/src/main/java/com/android/tools/r8/code/AndIntLit8.java
new file mode 100644
index 0000000..4546375
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AndIntLit8.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AndIntLit8 extends Format22b {
+
+ public static final int OPCODE = 0xdd;
+ public static final String NAME = "AndIntLit8";
+ public static final String SMALI_NAME = "and-int/lit8";
+
+ AndIntLit8(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AndIntLit8(int dest, int left, int constant) {
+ super(dest, left, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAndLiteral(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AndLong.java b/src/main/java/com/android/tools/r8/code/AndLong.java
new file mode 100644
index 0000000..54eb5c4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AndLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AndLong extends Format23x {
+
+ public static final int OPCODE = 0xA0;
+ public static final String NAME = "AndLong";
+ public static final String SMALI_NAME = "and-long";
+
+ AndLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AndLong(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAnd(NumericType.LONG, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AndLong2Addr.java b/src/main/java/com/android/tools/r8/code/AndLong2Addr.java
new file mode 100644
index 0000000..8ab3e14
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AndLong2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AndLong2Addr extends Format12x {
+
+ public static final int OPCODE = 0xc0;
+ public static final String NAME = "AndLong2Addr";
+ public static final String SMALI_NAME = "and-long/2addr";
+
+ AndLong2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AndLong2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addAnd(NumericType.LONG, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Aput.java b/src/main/java/com/android/tools/r8/code/Aput.java
new file mode 100644
index 0000000..3698393
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Aput.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Aput extends Format23x {
+
+ public static final int OPCODE = 0x4b;
+ public static final String NAME = "Aput";
+ public static final String SMALI_NAME = "aput";
+
+ /*package*/ Aput(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public Aput(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayPut(MemberType.SINGLE, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AputBoolean.java b/src/main/java/com/android/tools/r8/code/AputBoolean.java
new file mode 100644
index 0000000..859a30d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AputBoolean.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AputBoolean extends Format23x {
+
+ public static final int OPCODE = 0x4e;
+ public static final String NAME = "AputBoolean";
+ public static final String SMALI_NAME = "aput-boolean";
+
+ /*package*/ AputBoolean(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AputBoolean(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayPut(MemberType.BOOLEAN, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AputByte.java b/src/main/java/com/android/tools/r8/code/AputByte.java
new file mode 100644
index 0000000..926a109
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AputByte.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AputByte extends Format23x {
+
+ public static final int OPCODE = 0x4f;
+ public static final String NAME = "AputByte";
+ public static final String SMALI_NAME = "aput-byte";
+
+ /*package*/ AputByte(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AputByte(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayPut(MemberType.BYTE, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AputChar.java b/src/main/java/com/android/tools/r8/code/AputChar.java
new file mode 100644
index 0000000..4777d24
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AputChar.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AputChar extends Format23x {
+
+ public static final int OPCODE = 0x50;
+ public static final String NAME = "AputChar";
+ public static final String SMALI_NAME = "aput-char";
+
+ /*package*/ AputChar(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AputChar(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayPut(MemberType.CHAR, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AputObject.java b/src/main/java/com/android/tools/r8/code/AputObject.java
new file mode 100644
index 0000000..0f52da5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AputObject.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AputObject extends Format23x {
+
+ public static final int OPCODE = 0x4d;
+ public static final String NAME = "AputObject";
+ public static final String SMALI_NAME = "aput-object";
+
+ /*package*/ AputObject(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AputObject(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayPut(MemberType.OBJECT, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AputShort.java b/src/main/java/com/android/tools/r8/code/AputShort.java
new file mode 100644
index 0000000..ab477e0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AputShort.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AputShort extends Format23x {
+
+ public static final int OPCODE = 0x51;
+ public static final String NAME = "AputShort";
+ public static final String SMALI_NAME = "aput-short";
+
+ /*package*/ AputShort(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AputShort(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayPut(MemberType.SHORT, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AputWide.java b/src/main/java/com/android/tools/r8/code/AputWide.java
new file mode 100644
index 0000000..86adc5a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AputWide.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AputWide extends Format23x {
+
+ public static final int OPCODE = 0x4c;
+ public static final String NAME = "AputWide";
+ public static final String SMALI_NAME = "aput-wide";
+
+ /*package*/ AputWide(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public AputWide(int AA, int BB, int CC) {
+ super(AA, BB, CC);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayPut(MemberType.WIDE, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ArrayLength.java b/src/main/java/com/android/tools/r8/code/ArrayLength.java
new file mode 100644
index 0000000..59e5c00
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ArrayLength.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ArrayLength extends Format12x {
+
+ public static final int OPCODE = 0x21;
+ public static final String NAME = "ArrayLength";
+ public static final String SMALI_NAME = "array-length";
+
+ ArrayLength(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ArrayLength(int dest, int array) {
+ super(dest, array);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addArrayLength(A, B);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Base1Format.java b/src/main/java/com/android/tools/r8/code/Base1Format.java
new file mode 100644
index 0000000..b62853d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Base1Format.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+public abstract class Base1Format extends Instruction {
+
+ public Base1Format(BytecodeStream stream) {
+ super(stream);
+ }
+
+ protected Base1Format() {}
+
+ public int getSize() {
+ return 1;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Base2Format.java b/src/main/java/com/android/tools/r8/code/Base2Format.java
new file mode 100644
index 0000000..f8a48ac
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Base2Format.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+public abstract class Base2Format extends Instruction {
+
+ protected Base2Format() {}
+
+ public Base2Format(BytecodeStream stream) {
+ super(stream);
+ }
+
+ public int getSize() {
+ return 2;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Base3Format.java b/src/main/java/com/android/tools/r8/code/Base3Format.java
new file mode 100644
index 0000000..34bda57
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Base3Format.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+public abstract class Base3Format extends Instruction {
+
+ protected Base3Format() {}
+
+ public Base3Format(BytecodeStream stream) {
+ super(stream);
+ }
+
+ public int getSize() {
+ return 3;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/code/Base4Format.java b/src/main/java/com/android/tools/r8/code/Base4Format.java
new file mode 100644
index 0000000..7cdf1c5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Base4Format.java
@@ -0,0 +1,17 @@
+// 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.code;
+
+public abstract class Base4Format extends Instruction {
+
+ protected Base4Format() {}
+
+ public Base4Format(BytecodeStream stream) {
+ super(stream);
+ }
+
+ public int getSize() {
+ return 4;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/code/Base5Format.java b/src/main/java/com/android/tools/r8/code/Base5Format.java
new file mode 100644
index 0000000..cc67572
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Base5Format.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+public abstract class Base5Format extends Instruction {
+
+ protected Base5Format() {}
+
+ public Base5Format(BytecodeStream stream) {
+ super(stream);
+ }
+
+ public int getSize() {
+ return 5;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java b/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java
new file mode 100644
index 0000000..8637cca
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java
@@ -0,0 +1,461 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+
+abstract class BaseInstructionFactory {
+
+ static Instruction create(int high, int opcode, BytecodeStream stream,
+ OffsetToObjectMapping mapping) {
+ switch (opcode) {
+ case 0x0:
+ return Nop.create(high, stream);
+ case Move.OPCODE:
+ return new Move(high, stream);
+ case MoveFrom16.OPCODE:
+ return new MoveFrom16(high, stream);
+ case Move16.OPCODE:
+ return new Move16(high, stream);
+ case MoveWide.OPCODE:
+ return new MoveWide(high, stream);
+ case MoveWideFrom16.OPCODE:
+ return new MoveWideFrom16(high, stream);
+ case MoveWide16.OPCODE:
+ return new MoveWide16(high, stream);
+ case MoveObject.OPCODE:
+ return new MoveObject(high, stream);
+ case MoveObjectFrom16.OPCODE:
+ return new MoveObjectFrom16(high, stream);
+ case MoveObject16.OPCODE:
+ return new MoveObject16(high, stream);
+ case MoveResult.OPCODE:
+ return new MoveResult(high, stream);
+ case MoveResultWide.OPCODE:
+ return new MoveResultWide(high, stream);
+ case MoveResultObject.OPCODE:
+ return new MoveResultObject(high, stream);
+ case MoveException.OPCODE:
+ return new MoveException(high, stream);
+ case ReturnVoid.OPCODE:
+ return new ReturnVoid(high, stream);
+ case Return.OPCODE:
+ return new Return(high, stream);
+ case ReturnWide.OPCODE:
+ return new ReturnWide(high, stream);
+ case ReturnObject.OPCODE:
+ return new ReturnObject(high, stream);
+ case Const4.OPCODE:
+ return new Const4(high, stream);
+ case Const16.OPCODE:
+ return new Const16(high, stream);
+ case Const.OPCODE:
+ return new Const(high, stream);
+ case ConstHigh16.OPCODE:
+ return new ConstHigh16(high, stream);
+ case ConstWide16.OPCODE:
+ return new ConstWide16(high, stream);
+ case ConstWide32.OPCODE:
+ return new ConstWide32(high, stream);
+ case ConstWide.OPCODE:
+ return new ConstWide(high, stream);
+ case ConstWideHigh16.OPCODE:
+ return new ConstWideHigh16(high, stream);
+ case ConstString.OPCODE:
+ return new ConstString(high, stream, mapping);
+ case ConstStringJumbo.OPCODE:
+ return new ConstStringJumbo(high, stream, mapping);
+ case ConstClass.OPCODE:
+ return new ConstClass(high, stream, mapping);
+ case MonitorEnter.OPCODE:
+ return new MonitorEnter(high, stream);
+ case MonitorExit.OPCODE:
+ return new MonitorExit(high, stream);
+ case CheckCast.OPCODE:
+ return new CheckCast(high, stream, mapping);
+ case InstanceOf.OPCODE:
+ return new InstanceOf(high, stream, mapping);
+ case ArrayLength.OPCODE:
+ return new ArrayLength(high, stream);
+ case NewInstance.OPCODE:
+ return new NewInstance(high, stream, mapping);
+ case NewArray.OPCODE:
+ return new NewArray(high, stream, mapping);
+ case FilledNewArray.OPCODE:
+ return new FilledNewArray(high, stream, mapping);
+ case FilledNewArrayRange.OPCODE:
+ return new FilledNewArrayRange(high, stream, mapping);
+ case FillArrayData.OPCODE:
+ return new FillArrayData(high, stream);
+ case Throw.OPCODE:
+ return new Throw(high, stream);
+ case Goto.OPCODE:
+ return new Goto(high, stream);
+ case Goto16.OPCODE:
+ return new Goto16(high, stream);
+ case Goto32.OPCODE:
+ return new Goto32(high, stream);
+ case PackedSwitch.OPCODE:
+ return new PackedSwitch(high, stream);
+ case SparseSwitch.OPCODE:
+ return new SparseSwitch(high, stream);
+ case CmplFloat.OPCODE:
+ return new CmplFloat(high, stream);
+ case CmpgFloat.OPCODE:
+ return new CmpgFloat(high, stream);
+ case CmplDouble.OPCODE:
+ return new CmplDouble(high, stream);
+ case CmpgDouble.OPCODE:
+ return new CmpgDouble(high, stream);
+ case CmpLong.OPCODE:
+ return new CmpLong(high, stream);
+ case IfEq.OPCODE:
+ return new IfEq(high, stream);
+ case IfNe.OPCODE:
+ return new IfNe(high, stream);
+ case IfLt.OPCODE:
+ return new IfLt(high, stream);
+ case IfGe.OPCODE:
+ return new IfGe(high, stream);
+ case IfGt.OPCODE:
+ return new IfGt(high, stream);
+ case IfLe.OPCODE:
+ return new IfLe(high, stream);
+ case IfEqz.OPCODE:
+ return new IfEqz(high, stream);
+ case IfNez.OPCODE:
+ return new IfNez(high, stream);
+ case IfLtz.OPCODE:
+ return new IfLtz(high, stream);
+ case IfGez.OPCODE:
+ return new IfGez(high, stream);
+ case IfGtz.OPCODE:
+ return new IfGtz(high, stream);
+ case IfLez.OPCODE:
+ return new IfLez(high, stream);
+ case Aget.OPCODE:
+ return new Aget(high, stream);
+ case AgetWide.OPCODE:
+ return new AgetWide(high, stream);
+ case AgetObject.OPCODE:
+ return new AgetObject(high, stream);
+ case AgetBoolean.OPCODE:
+ return new AgetBoolean(high, stream);
+ case AgetByte.OPCODE:
+ return new AgetByte(high, stream);
+ case AgetChar.OPCODE:
+ return new AgetChar(high, stream);
+ case AgetShort.OPCODE:
+ return new AgetShort(high, stream);
+ case Aput.OPCODE:
+ return new Aput(high, stream);
+ case AputWide.OPCODE:
+ return new AputWide(high, stream);
+ case AputObject.OPCODE:
+ return new AputObject(high, stream);
+ case AputBoolean.OPCODE:
+ return new AputBoolean(high, stream);
+ case AputByte.OPCODE:
+ return new AputByte(high, stream);
+ case AputChar.OPCODE:
+ return new AputChar(high, stream);
+ case AputShort.OPCODE:
+ return new AputShort(high, stream);
+ case Iget.OPCODE:
+ return new Iget(high, stream, mapping);
+ case IgetWide.OPCODE:
+ return new IgetWide(high, stream, mapping);
+ case IgetObject.OPCODE:
+ return new IgetObject(high, stream, mapping);
+ case IgetBoolean.OPCODE:
+ return new IgetBoolean(high, stream, mapping);
+ case IgetByte.OPCODE:
+ return new IgetByte(high, stream, mapping);
+ case IgetChar.OPCODE:
+ return new IgetChar(high, stream, mapping);
+ case IgetShort.OPCODE:
+ return new IgetShort(high, stream, mapping);
+ case Iput.OPCODE:
+ return new Iput(high, stream, mapping);
+ case IputWide.OPCODE:
+ return new IputWide(high, stream, mapping);
+ case IputObject.OPCODE:
+ return new IputObject(high, stream, mapping);
+ case IputBoolean.OPCODE:
+ return new IputBoolean(high, stream, mapping);
+ case IputByte.OPCODE:
+ return new IputByte(high, stream, mapping);
+ case IputChar.OPCODE:
+ return new IputChar(high, stream, mapping);
+ case IputShort.OPCODE:
+ return new IputShort(high, stream, mapping);
+ case Sget.OPCODE:
+ return new Sget(high, stream, mapping);
+ case SgetWide.OPCODE:
+ return new SgetWide(high, stream, mapping);
+ case SgetObject.OPCODE:
+ return new SgetObject(high, stream, mapping);
+ case SgetBoolean.OPCODE:
+ return new SgetBoolean(high, stream, mapping);
+ case SgetByte.OPCODE:
+ return new SgetByte(high, stream, mapping);
+ case SgetChar.OPCODE:
+ return new SgetChar(high, stream, mapping);
+ case SgetShort.OPCODE:
+ return new SgetShort(high, stream, mapping);
+ case Sput.OPCODE:
+ return new Sput(high, stream, mapping);
+ case SputWide.OPCODE:
+ return new SputWide(high, stream, mapping);
+ case SputObject.OPCODE:
+ return new SputObject(high, stream, mapping);
+ case SputBoolean.OPCODE:
+ return new SputBoolean(high, stream, mapping);
+ case SputByte.OPCODE:
+ return new SputByte(high, stream, mapping);
+ case SputChar.OPCODE:
+ return new SputChar(high, stream, mapping);
+ case SputShort.OPCODE:
+ return new SputShort(high, stream, mapping);
+ case InvokeVirtual.OPCODE:
+ return new InvokeVirtual(high, stream, mapping);
+ case InvokeSuper.OPCODE:
+ return new InvokeSuper(high, stream, mapping);
+ case InvokeDirect.OPCODE:
+ return new InvokeDirect(high, stream, mapping);
+ case InvokeStatic.OPCODE:
+ return new InvokeStatic(high, stream, mapping);
+ case InvokeInterface.OPCODE:
+ return new InvokeInterface(high, stream, mapping);
+ case InvokeVirtualRange.OPCODE:
+ return new InvokeVirtualRange(high, stream, mapping);
+ case InvokeSuperRange.OPCODE:
+ return new InvokeSuperRange(high, stream, mapping);
+ case InvokeDirectRange.OPCODE:
+ return new InvokeDirectRange(high, stream, mapping);
+ case InvokeStaticRange.OPCODE:
+ return new InvokeStaticRange(high, stream, mapping);
+ case InvokeInterfaceRange.OPCODE:
+ return new InvokeInterfaceRange(high, stream, mapping);
+ case NegInt.OPCODE:
+ return new NegInt(high, stream);
+ case NotInt.OPCODE:
+ return new NotInt(high, stream);
+ case NegLong.OPCODE:
+ return new NegLong(high, stream);
+ case NotLong.OPCODE:
+ return new NotLong(high, stream);
+ case NegFloat.OPCODE:
+ return new NegFloat(high, stream);
+ case NegDouble.OPCODE:
+ return new NegDouble(high, stream);
+ case IntToLong.OPCODE:
+ return new IntToLong(high, stream);
+ case IntToFloat.OPCODE:
+ return new IntToFloat(high, stream);
+ case IntToDouble.OPCODE:
+ return new IntToDouble(high, stream);
+ case LongToInt.OPCODE:
+ return new LongToInt(high, stream);
+ case LongToFloat.OPCODE:
+ return new LongToFloat(high, stream);
+ case LongToDouble.OPCODE:
+ return new LongToDouble(high, stream);
+ case FloatToInt.OPCODE:
+ return new FloatToInt(high, stream);
+ case FloatToLong.OPCODE:
+ return new FloatToLong(high, stream);
+ case FloatToDouble.OPCODE:
+ return new FloatToDouble(high, stream);
+ case DoubleToInt.OPCODE:
+ return new DoubleToInt(high, stream);
+ case DoubleToLong.OPCODE:
+ return new DoubleToLong(high, stream);
+ case DoubleToFloat.OPCODE:
+ return new DoubleToFloat(high, stream);
+ case IntToByte.OPCODE:
+ return new IntToByte(high, stream);
+ case IntToChar.OPCODE:
+ return new IntToChar(high, stream);
+ case IntToShort.OPCODE:
+ return new IntToShort(high, stream);
+ case AddInt.OPCODE:
+ return new AddInt(high, stream);
+ case SubInt.OPCODE:
+ return new SubInt(high, stream);
+ case MulInt.OPCODE:
+ return new MulInt(high, stream);
+ case DivInt.OPCODE:
+ return new DivInt(high, stream);
+ case RemInt.OPCODE:
+ return new RemInt(high, stream);
+ case AndInt.OPCODE:
+ return new AndInt(high, stream);
+ case OrInt.OPCODE:
+ return new OrInt(high, stream);
+ case XorInt.OPCODE:
+ return new XorInt(high, stream);
+ case ShlInt.OPCODE:
+ return new ShlInt(high, stream);
+ case ShrInt.OPCODE:
+ return new ShrInt(high, stream);
+ case UshrInt.OPCODE:
+ return new UshrInt(high, stream);
+ case AddLong.OPCODE:
+ return new AddLong(high, stream);
+ case SubLong.OPCODE:
+ return new SubLong(high, stream);
+ case MulLong.OPCODE:
+ return new MulLong(high, stream);
+ case DivLong.OPCODE:
+ return new DivLong(high, stream);
+ case RemLong.OPCODE:
+ return new RemLong(high, stream);
+ case AndLong.OPCODE:
+ return new AndLong(high, stream);
+ case OrLong.OPCODE:
+ return new OrLong(high, stream);
+ case XorLong.OPCODE:
+ return new XorLong(high, stream);
+ case ShlLong.OPCODE:
+ return new ShlLong(high, stream);
+ case ShrLong.OPCODE:
+ return new ShrLong(high, stream);
+ case UshrLong.OPCODE:
+ return new UshrLong(high, stream);
+ case AddFloat.OPCODE:
+ return new AddFloat(high, stream);
+ case SubFloat.OPCODE:
+ return new SubFloat(high, stream);
+ case MulFloat.OPCODE:
+ return new MulFloat(high, stream);
+ case DivFloat.OPCODE:
+ return new DivFloat(high, stream);
+ case RemFloat.OPCODE:
+ return new RemFloat(high, stream);
+ case AddDouble.OPCODE:
+ return new AddDouble(high, stream);
+ case SubDouble.OPCODE:
+ return new SubDouble(high, stream);
+ case MulDouble.OPCODE:
+ return new MulDouble(high, stream);
+ case DivDouble.OPCODE:
+ return new DivDouble(high, stream);
+ case RemDouble.OPCODE:
+ return new RemDouble(high, stream);
+ case AddInt2Addr.OPCODE:
+ return new AddInt2Addr(high, stream);
+ case SubInt2Addr.OPCODE:
+ return new SubInt2Addr(high, stream);
+ case MulInt2Addr.OPCODE:
+ return new MulInt2Addr(high, stream);
+ case DivInt2Addr.OPCODE:
+ return new DivInt2Addr(high, stream);
+ case RemInt2Addr.OPCODE:
+ return new RemInt2Addr(high, stream);
+ case AndInt2Addr.OPCODE:
+ return new AndInt2Addr(high, stream);
+ case OrInt2Addr.OPCODE:
+ return new OrInt2Addr(high, stream);
+ case XorInt2Addr.OPCODE:
+ return new XorInt2Addr(high, stream);
+ case ShlInt2Addr.OPCODE:
+ return new ShlInt2Addr(high, stream);
+ case ShrInt2Addr.OPCODE:
+ return new ShrInt2Addr(high, stream);
+ case UshrInt2Addr.OPCODE:
+ return new UshrInt2Addr(high, stream);
+ case AddLong2Addr.OPCODE:
+ return new AddLong2Addr(high, stream);
+ case SubLong2Addr.OPCODE:
+ return new SubLong2Addr(high, stream);
+ case MulLong2Addr.OPCODE:
+ return new MulLong2Addr(high, stream);
+ case DivLong2Addr.OPCODE:
+ return new DivLong2Addr(high, stream);
+ case RemLong2Addr.OPCODE:
+ return new RemLong2Addr(high, stream);
+ case AndLong2Addr.OPCODE:
+ return new AndLong2Addr(high, stream);
+ case OrLong2Addr.OPCODE:
+ return new OrLong2Addr(high, stream);
+ case XorLong2Addr.OPCODE:
+ return new XorLong2Addr(high, stream);
+ case ShlLong2Addr.OPCODE:
+ return new ShlLong2Addr(high, stream);
+ case ShrLong2Addr.OPCODE:
+ return new ShrLong2Addr(high, stream);
+ case UshrLong2Addr.OPCODE:
+ return new UshrLong2Addr(high, stream);
+ case AddFloat2Addr.OPCODE:
+ return new AddFloat2Addr(high, stream);
+ case SubFloat2Addr.OPCODE:
+ return new SubFloat2Addr(high, stream);
+ case MulFloat2Addr.OPCODE:
+ return new MulFloat2Addr(high, stream);
+ case DivFloat2Addr.OPCODE:
+ return new DivFloat2Addr(high, stream);
+ case RemFloat2Addr.OPCODE:
+ return new RemFloat2Addr(high, stream);
+ case AddDouble2Addr.OPCODE:
+ return new AddDouble2Addr(high, stream);
+ case SubDouble2Addr.OPCODE:
+ return new SubDouble2Addr(high, stream);
+ case MulDouble2Addr.OPCODE:
+ return new MulDouble2Addr(high, stream);
+ case DivDouble2Addr.OPCODE:
+ return new DivDouble2Addr(high, stream);
+ case RemDouble2Addr.OPCODE:
+ return new RemDouble2Addr(high, stream);
+ case AddIntLit16.OPCODE:
+ return new AddIntLit16(high, stream);
+ case RsubInt.OPCODE:
+ return new RsubInt(high, stream);
+ case MulIntLit16.OPCODE:
+ return new MulIntLit16(high, stream);
+ case DivIntLit16.OPCODE:
+ return new DivIntLit16(high, stream);
+ case RemIntLit16.OPCODE:
+ return new RemIntLit16(high, stream);
+ case AndIntLit16.OPCODE:
+ return new AndIntLit16(high, stream);
+ case OrIntLit16.OPCODE:
+ return new OrIntLit16(high, stream);
+ case XorIntLit16.OPCODE:
+ return new XorIntLit16(high, stream);
+ case AddIntLit8.OPCODE:
+ return new AddIntLit8(high, stream);
+ case RsubIntLit8.OPCODE:
+ return new RsubIntLit8(high, stream);
+ case MulIntLit8.OPCODE:
+ return new MulIntLit8(high, stream);
+ case DivIntLit8.OPCODE:
+ return new DivIntLit8(high, stream);
+ case RemIntLit8.OPCODE:
+ return new RemIntLit8(high, stream);
+ case AndIntLit8.OPCODE:
+ return new AndIntLit8(high, stream);
+ case OrIntLit8.OPCODE:
+ return new OrIntLit8(high, stream);
+ case XorIntLit8.OPCODE:
+ return new XorIntLit8(high, stream);
+ case ShlIntLit8.OPCODE:
+ return new ShlIntLit8(high, stream);
+ case ShrIntLit8.OPCODE:
+ return new ShrIntLit8(high, stream);
+ case UshrIntLit8.OPCODE:
+ return new UshrIntLit8(high, stream);
+ case InvokePolymorphic.OPCODE:
+ return new InvokePolymorphic(high, stream, mapping);
+ case InvokePolymorphicRange.OPCODE:
+ return new InvokePolymorphicRange(high, stream, mapping);
+ case InvokeCustom.OPCODE:
+ return new InvokeCustom(high, stream, mapping);
+ case InvokeCustomRange.OPCODE:
+ return new InvokeCustomRange(high, stream, mapping);
+ default:
+ throw new IllegalArgumentException("Illegal Opcode: 0x" + Integer.toString(opcode, 16));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/BytecodeStream.java b/src/main/java/com/android/tools/r8/code/BytecodeStream.java
new file mode 100644
index 0000000..e928c3e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/BytecodeStream.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+public interface BytecodeStream {
+
+ /**
+ * Returns the current position from the starting index in shorts.
+ *
+ * @return offset from start in shorts.
+ */
+ int getOffset();
+
+ /**
+ * Returns the next short value from the stream of values.
+ *
+ * @return next short value in stream.
+ */
+ int nextShort();
+
+ /**
+ * Returns the next byte value from the stream, i.e., the high value of the next short followed by
+ * the low value.
+ *
+ * Both bytes need to be consumed before the next call to {@link #nextShort()}.
+ *
+ * @return next byte value in the stream.
+ */
+ int nextByte();
+
+ /**
+ * Returns true of there are more values to be consumed.
+ *
+ * @return true if more values can be consumed.
+ */
+ boolean hasMore();
+}
diff --git a/src/main/java/com/android/tools/r8/code/CheckCast.java b/src/main/java/com/android/tools/r8/code/CheckCast.java
new file mode 100644
index 0000000..1c7973b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/CheckCast.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class CheckCast extends Format21c {
+
+ public static final int OPCODE = 0x1f;
+ public static final String NAME = "CheckCast";
+ public static final String SMALI_NAME = "check-cast";
+
+ CheckCast(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getTypeMap());
+ }
+
+ public CheckCast(int valueRegister, DexType type) {
+ super(valueRegister, type);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerTypeReference(getType());
+ }
+
+ public DexType getType() {
+ return (DexType) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addCheckCast(AA, getType());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/CmpLong.java b/src/main/java/com/android/tools/r8/code/CmpLong.java
new file mode 100644
index 0000000..65d80fa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/CmpLong.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class CmpLong extends Format23x {
+
+ public static final int OPCODE = 0x31;
+ public static final String NAME = "CmpLong";
+ public static final String SMALI_NAME = "cmp-long";
+
+ CmpLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public CmpLong(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addCmp(NumericType.LONG, Bias.NONE, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/CmpgDouble.java b/src/main/java/com/android/tools/r8/code/CmpgDouble.java
new file mode 100644
index 0000000..5eea470
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/CmpgDouble.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class CmpgDouble extends Format23x {
+
+ public static final int OPCODE = 0x30;
+ public static final String NAME = "CmpgDouble";
+ public static final String SMALI_NAME = "cmpg-double";
+
+ CmpgDouble(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public CmpgDouble(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addCmp(NumericType.DOUBLE, Bias.GT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/CmpgFloat.java b/src/main/java/com/android/tools/r8/code/CmpgFloat.java
new file mode 100644
index 0000000..0462c8d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/CmpgFloat.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class CmpgFloat extends Format23x {
+
+ public static final int OPCODE = 0x2e;
+ public static final String NAME = "CmpgFloat";
+ public static final String SMALI_NAME = "cmpg-float";
+
+ CmpgFloat(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public CmpgFloat(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addCmp(NumericType.FLOAT, Bias.GT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/CmplDouble.java b/src/main/java/com/android/tools/r8/code/CmplDouble.java
new file mode 100644
index 0000000..474d03b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/CmplDouble.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class CmplDouble extends Format23x {
+
+ public static final int OPCODE = 0x2f;
+ public static final String NAME = "CmplDouble";
+ public static final String SMALI_NAME = "cmpl-double";
+
+ CmplDouble(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public CmplDouble(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addCmp(NumericType.DOUBLE, Bias.LT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/CmplFloat.java b/src/main/java/com/android/tools/r8/code/CmplFloat.java
new file mode 100644
index 0000000..3054fbf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/CmplFloat.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class CmplFloat extends Format23x {
+
+ public static final int OPCODE = 0x2d;
+ public static final String NAME = "CmplFloat";
+ public static final String SMALI_NAME = "cmpl-float";
+
+ CmplFloat(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public CmplFloat(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addCmp(NumericType.FLOAT, Bias.LT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Const.java b/src/main/java/com/android/tools/r8/code/Const.java
new file mode 100644
index 0000000..301f982
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Const.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.SingleConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class Const extends Format31i implements SingleConstant {
+
+ public static final int OPCODE = 0x14;
+ public static final String NAME = "Const";
+ public static final String SMALI_NAME = "const";
+
+ Const(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public Const(int register, int constant) {
+ super(register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public int decodedValue() {
+ return BBBBBBBB;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 8) +
+ " (" + decodedValue() + ")");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 8) +
+ " # " + decodedValue());
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConst(MoveType.SINGLE, AA, decodedValue());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Const16.java b/src/main/java/com/android/tools/r8/code/Const16.java
new file mode 100644
index 0000000..aa00341
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Const16.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.SingleConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class Const16 extends Format21s implements SingleConstant {
+
+ public static final int OPCODE = 0x13;
+ public static final String NAME = "Const16";
+ public static final String SMALI_NAME = "const/16";
+
+ /*package*/ Const16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public Const16(int dest, int constant) {
+ super(dest, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public int decodedValue() {
+ return BBBB;
+ }
+
+ @Override
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 4) +
+ " (" + decodedValue() + ")");
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConst(MoveType.SINGLE, AA, decodedValue());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Const4.java b/src/main/java/com/android/tools/r8/code/Const4.java
new file mode 100644
index 0000000..26da02a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Const4.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.SingleConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class Const4 extends Format11n implements SingleConstant {
+
+ public static final int OPCODE = 0x12;
+ public static final String NAME = "Const4";
+ public static final String SMALI_NAME = "const/4";
+
+ Const4(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public Const4(int dest, int constant) {
+ super(dest, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public int decodedValue() {
+ return B;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + A + ", 0x" + StringUtils.hexString(decodedValue(), 1) +
+ " (" + decodedValue() + ")");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + A + ", 0x" + StringUtils.hexString(decodedValue(), 2) +
+ " # " + decodedValue());
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConst(MoveType.SINGLE, A, decodedValue());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstClass.java b/src/main/java/com/android/tools/r8/code/ConstClass.java
new file mode 100644
index 0000000..04fda4b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstClass.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ConstClass extends Format21c {
+
+ public static final int OPCODE = 0x1c;
+ public static final String NAME = "ConstClass";
+ public static final String SMALI_NAME = "const-class";
+
+ ConstClass(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getTypeMap());
+ }
+
+ public ConstClass(int dest, DexType type) {
+ super(dest, type);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerTypeReference(getType());
+ }
+
+ public DexType getType() {
+ return (DexType) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConstClass(AA, getType());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstHigh16.java b/src/main/java/com/android/tools/r8/code/ConstHigh16.java
new file mode 100644
index 0000000..3245fa9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstHigh16.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.SingleConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class ConstHigh16 extends Format21h implements SingleConstant {
+
+ public static final int OPCODE = 0x15;
+ public static final String NAME = "ConstHigh16";
+ public static final String SMALI_NAME = "const/high16";
+
+ /*package*/ ConstHigh16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ConstHigh16(int register, int constantHighBits) {
+ super(register, constantHighBits);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public int decodedValue() {
+ return BBBB << 16;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 8) +
+ " (" + decodedValue() + ")");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 8) +
+ " # " + decodedValue());
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConst(MoveType.SINGLE, AA, decodedValue());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstString.java b/src/main/java/com/android/tools/r8/code/ConstString.java
new file mode 100644
index 0000000..ea4bd77
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstString.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+public class ConstString extends Format21c {
+
+ public static final int OPCODE = 0x1a;
+ public static final String NAME = "ConstString";
+ public static final String SMALI_NAME = "const-string";
+
+ ConstString(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getStringMap());
+ }
+
+ public ConstString(int register, DexString string) {
+ super(register, string);
+ }
+
+ public DexString getString() {
+ return (DexString) BBBB;
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", \"" + BBBB.toString() + "\"");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", \"" + BBBB.toString() + "\"");
+ }
+
+ @Override
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ int index = BBBB.getOffset(mapping);
+ if (index != (index & 0xffff)) {
+ throw new InternalCompilerError("String-index overflow.");
+ }
+ super.write(dest, mapping);
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConstString(AA, (DexString) BBBB);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstStringJumbo.java b/src/main/java/com/android/tools/r8/code/ConstStringJumbo.java
new file mode 100644
index 0000000..f68de1b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstStringJumbo.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+
+public class ConstStringJumbo extends Format31c {
+
+ public static final int OPCODE = 0x1b;
+ public static final String NAME = "ConstStringJumbo";
+ public static final String SMALI_NAME = "const-string/jumbo";
+
+ ConstStringJumbo(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getStringMap());
+ }
+
+ public ConstStringJumbo(int register, DexString string) {
+ super(register, string);
+ }
+
+ public DexString getString() {
+ return BBBBBBBB;
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", \"" + BBBBBBBB.toString() + "\"");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", \"" + BBBBBBBB.toString() + "\"");
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConstString(AA, BBBBBBBB);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstWide.java b/src/main/java/com/android/tools/r8/code/ConstWide.java
new file mode 100644
index 0000000..7323f9b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstWide.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.WideConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class ConstWide extends Format51l implements WideConstant {
+
+ public static final int OPCODE = 0x18;
+ public static final String NAME = "ConstWide";
+ public static final String SMALI_NAME = "const-wide";
+
+ ConstWide(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ConstWide(int dest, long constant) {
+ super(dest, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public long decodedValue() {
+ return BBBBBBBBBBBBBBBB;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+ " (" + decodedValue() + ")");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+ "L # " + decodedValue());
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConst(MoveType.WIDE, AA, decodedValue());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstWide16.java b/src/main/java/com/android/tools/r8/code/ConstWide16.java
new file mode 100644
index 0000000..edd93fc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstWide16.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.WideConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class ConstWide16 extends Format21s implements WideConstant {
+
+ public static final int OPCODE = 0x16;
+ public static final String NAME = "ConstWide16";
+ public static final String SMALI_NAME = "const-wide/16";
+
+ ConstWide16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ConstWide16(int dest, int constant) {
+ super(dest, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public long decodedValue() {
+ return BBBB;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+ " (" + decodedValue() + ")");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString(
+ "v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) + "L # " + decodedValue());
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConst(MoveType.WIDE, AA, decodedValue());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstWide32.java b/src/main/java/com/android/tools/r8/code/ConstWide32.java
new file mode 100644
index 0000000..540e840
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstWide32.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.WideConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class ConstWide32 extends Format31i implements WideConstant {
+
+ public static final int OPCODE = 0x17;
+ public static final String NAME = "ConstWide32";
+ public static final String SMALI_NAME = "const-wide/32";
+
+ ConstWide32(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ConstWide32(int dest, int constant) {
+ super(dest, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public long decodedValue() {
+ return BBBBBBBB;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+ " (" + decodedValue() + ")");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString(
+ "v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) + " # " + decodedValue());
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConst(MoveType.WIDE, AA, decodedValue());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstWideHigh16.java b/src/main/java/com/android/tools/r8/code/ConstWideHigh16.java
new file mode 100644
index 0000000..864a079
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstWideHigh16.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.WideConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class ConstWideHigh16 extends Format21h implements WideConstant {
+
+ public static final int OPCODE = 0x19;
+ public static final String NAME = "ConstWideHigh16";
+ public static final String SMALI_NAME = "const-wide/high16";
+
+ ConstWideHigh16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ConstWideHigh16(int dest, int constantHighBits) {
+ super(dest, constantHighBits);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ public long decodedValue() {
+ return ((long) BBBB) << 48;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+ " (" + decodedValue() + ")");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+ "L # " + decodedValue());
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConst(MoveType.WIDE, AA, decodedValue());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivDouble.java b/src/main/java/com/android/tools/r8/code/DivDouble.java
new file mode 100644
index 0000000..d9164de
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivDouble.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class DivDouble extends Format23x {
+
+ public static final int OPCODE = 0xae;
+ public static final String NAME = "DivDouble";
+ public static final String SMALI_NAME = "div-double";
+
+ DivDouble(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public DivDouble(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addDiv(NumericType.DOUBLE, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivDouble2Addr.java b/src/main/java/com/android/tools/r8/code/DivDouble2Addr.java
new file mode 100644
index 0000000..02347a1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivDouble2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DivDouble2Addr extends Format12x {
+
+ public static final int OPCODE = 0xce;
+ public static final String NAME = "DivDouble2Addr";
+ public static final String SMALI_NAME = "div-double/2addr";
+
+ DivDouble2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public DivDouble2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addDiv(NumericType.DOUBLE, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivFloat.java b/src/main/java/com/android/tools/r8/code/DivFloat.java
new file mode 100644
index 0000000..2a3b360
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivFloat.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class DivFloat extends Format23x {
+
+ public static final int OPCODE = 0xa9;
+ public static final String NAME = "DivFloat";
+ public static final String SMALI_NAME = "div-float";
+
+ DivFloat(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public DivFloat(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addDiv(NumericType.FLOAT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivFloat2Addr.java b/src/main/java/com/android/tools/r8/code/DivFloat2Addr.java
new file mode 100644
index 0000000..2b3812f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivFloat2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DivFloat2Addr extends Format12x {
+
+ public static final int OPCODE = 0xc9;
+ public static final String NAME = "DivFloat2Addr";
+ public static final String SMALI_NAME = "div-float/2addr";
+
+ DivFloat2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public DivFloat2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addDiv(NumericType.FLOAT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivInt.java b/src/main/java/com/android/tools/r8/code/DivInt.java
new file mode 100644
index 0000000..cc437e1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivInt.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DivInt extends Format23x {
+
+ public static final int OPCODE = 0x93;
+ public static final String NAME = "DivInt";
+ public static final String SMALI_NAME = "div-int";
+
+ /*package*/ DivInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public DivInt(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addDiv(NumericType.INT, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivInt2Addr.java b/src/main/java/com/android/tools/r8/code/DivInt2Addr.java
new file mode 100644
index 0000000..21cff23
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivInt2Addr.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DivInt2Addr extends Format12x {
+
+ public static final int OPCODE = 0xb3;
+ public static final String NAME = "DivInt2Addr";
+ public static final String SMALI_NAME = "div-int/2addr";
+
+ /*package*/ DivInt2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public DivInt2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addDiv(NumericType.INT, A, A, B);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivIntLit16.java b/src/main/java/com/android/tools/r8/code/DivIntLit16.java
new file mode 100644
index 0000000..4a4035c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivIntLit16.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class DivIntLit16 extends Format22s {
+
+ public static final int OPCODE = 0xd3;
+ public static final String NAME = "DivIntLit16";
+ public static final String SMALI_NAME = "div-int/lit16";
+
+ /*package*/ DivIntLit16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public DivIntLit16(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addDivLiteral(NumericType.INT, A, B, CCCC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivIntLit8.java b/src/main/java/com/android/tools/r8/code/DivIntLit8.java
new file mode 100644
index 0000000..12b1ee8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivIntLit8.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DivIntLit8 extends Format22b {
+
+ public static final int OPCODE = 0xdb;
+ public static final String NAME = "DivIntLit8";
+ public static final String SMALI_NAME = "div-int/lit8";
+
+ DivIntLit8(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public DivIntLit8(int dest, int left, int constant) {
+ super(dest, left, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addDivLiteral(NumericType.INT, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivLong.java b/src/main/java/com/android/tools/r8/code/DivLong.java
new file mode 100644
index 0000000..26f66a7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivLong.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class DivLong extends Format23x {
+
+ public static final int OPCODE = 0x9e;
+ public static final String NAME = "DivLong";
+ public static final String SMALI_NAME = "div-long";
+
+ DivLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public DivLong(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addDiv(NumericType.LONG, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivLong2Addr.java b/src/main/java/com/android/tools/r8/code/DivLong2Addr.java
new file mode 100644
index 0000000..6a79fbd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivLong2Addr.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DivLong2Addr extends Format12x {
+
+ public static final int OPCODE = 0xbe;
+ public static final String NAME = "DivLong2Addr";
+ public static final String SMALI_NAME = "div-long/2addr";
+
+ DivLong2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public DivLong2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addDiv(NumericType.LONG, A, A, B);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DoubleToFloat.java b/src/main/java/com/android/tools/r8/code/DoubleToFloat.java
new file mode 100644
index 0000000..5c2ce2c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DoubleToFloat.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DoubleToFloat extends Format12x {
+
+ public static final int OPCODE = 0x8c;
+ public static final String NAME = "DoubleToFloat";
+ public static final String SMALI_NAME = "double-to-float";
+
+ DoubleToFloat(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public DoubleToFloat(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.FLOAT, NumericType.DOUBLE, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DoubleToInt.java b/src/main/java/com/android/tools/r8/code/DoubleToInt.java
new file mode 100644
index 0000000..1b4619e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DoubleToInt.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class DoubleToInt extends Format12x {
+
+ public static final int OPCODE = 0x8a;
+ public static final String NAME = "DoubleToInt";
+ public static final String SMALI_NAME = "doubleto-int";
+
+ DoubleToInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public DoubleToInt(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.INT, NumericType.DOUBLE, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DoubleToLong.java b/src/main/java/com/android/tools/r8/code/DoubleToLong.java
new file mode 100644
index 0000000..dfc271d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DoubleToLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class DoubleToLong extends Format12x {
+
+ public static final int OPCODE = 0x8b;
+ public static final String NAME = "DoubleToLong";
+ public static final String SMALI_NAME = "double-to-long";
+
+ DoubleToLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public DoubleToLong(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.LONG, NumericType.DOUBLE, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FillArrayData.java b/src/main/java/com/android/tools/r8/code/FillArrayData.java
new file mode 100644
index 0000000..06ba020
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FillArrayData.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+
+public class FillArrayData extends Format31t {
+
+ public static final int OPCODE = 0x26;
+ public static final String NAME = "FillArrayData";
+ public static final String SMALI_NAME = "fill-array-data";
+
+ FillArrayData(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public FillArrayData(int value) {
+ super(value, -1);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.resolveAndBuildNewArrayFilledData(AA, getOffset() + getPayloadOffset());
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", :label_" + (getOffset() + BBBBBBBB));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java b/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java
new file mode 100644
index 0000000..0fb8f1e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java
@@ -0,0 +1,130 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+import java.util.Arrays;
+
+public class FillArrayDataPayload extends Nop {
+
+ public final int element_width;
+ public final long size;
+ public final short[] data;
+
+ FillArrayDataPayload(int high, BytecodeStream stream) {
+ super(high, stream);
+ element_width = read16BitValue(stream);
+ size = read32BitValue(stream);
+ assert size * element_width < Integer.MAX_VALUE;
+ // Read the data as shorts (which is faster than reading bytes) as they are always 2-byte
+ // aligned and the size is 2-byte aligned as well.
+ int numberOfShorts = (int) (size * element_width + 1) / 2;
+ data = new short[numberOfShorts];
+ for (int i = 0; i < data.length; i++) {
+ data[i] = (short) readSigned16BitValue(stream);
+ }
+ }
+
+ public FillArrayDataPayload(int element_width, long size, short[] data) {
+ this.element_width = element_width;
+ this.size = size;
+ this.data = data;
+ }
+
+ public boolean isPayload() {
+ return true;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(3, dest); // Pseudo-opcode = 0x0300
+ write16BitValue(element_width, dest);
+ write32BitValue(size, dest);
+ for (int i = 0; i < data.length; i++) {
+ write16BitValue(data[i], dest);
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!super.equals(other)) {
+ return false;
+ }
+ FillArrayDataPayload that = (FillArrayDataPayload) other;
+ return size == that.size && element_width == that.element_width
+ && Arrays.equals(data, that.data);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + element_width;
+ result = 31 * result + (int) (size ^ (size >>> 32));
+ result = 31 * result + Arrays.hashCode(data);
+ return result;
+ }
+
+ public int getSize() {
+ return 4 + data.length;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return super.toString(naming) + "[FillArrayPayload], " +
+ "width: " + element_width + ", size: " + size;
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(" ");
+ builder.append(".array-data");
+ builder.append(" 0x");
+ builder.append(StringUtils.hexString(element_width, 1));
+ builder.append(" # ");
+ builder.append(element_width);
+ builder.append("\n");
+ if (element_width == 1) {
+ // For element width 1 split the 16-bit data units into bytes.
+ for (int i = 0; i < data.length; i++) {
+ for (int j = 0; j < 2; j++) {
+ int value = (data[i] >> j * 8) & 0xff;
+ if (i * 2 + j < size) {
+ builder.append(" ");
+ builder.append("0x");
+ builder.append(StringUtils.hexString(value, 2));
+ builder.append(" # ");
+ builder.append(value);
+ builder.append("\n");
+ }
+ }
+ }
+ } else {
+ // For element width > 1 combine the 16-bit data units into 16-bit/32-bit or 64-bit values.
+ assert element_width == 2 || element_width == 4 || element_width == 8;
+ long value = 0;
+ for (int i = 0; i < data.length; i++) {
+ value = (Short.toUnsignedLong(data[i]) << (16 * (i % (element_width / 2)))) | value;
+ if ((((i + 1) * 2) % element_width) == 0) {
+ builder.append(" ");
+ builder.append("0x");
+ builder.append(StringUtils.hexString(value, element_width * 2));
+ builder.append(" # ");
+ builder.append(value);
+ builder.append("\n");
+ value = 0;
+ }
+ }
+ }
+ builder.append(" ");
+ builder.append(".end array-data");
+ return builder.toString();
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ // FilledArrayData payloads are not represented in the IR.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArray.java b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
new file mode 100644
index 0000000..085b6fa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class FilledNewArray extends Format35c {
+
+ public static final int OPCODE = 0x24;
+ public static final String NAME = "FilledNewArray";
+ public static final String SMALI_NAME = "filled-new-array";
+
+ FilledNewArray(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getTypeMap());
+ }
+
+ public FilledNewArray(int size, DexType type, int v0, int v1, int v2, int v3, int v4) {
+ super(size, type, v0, v1, v2, v3, v4);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ public DexType getType() {
+ return (DexType) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeNewArray(getType(), A, new int[]{C, D, E, F, G});
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
new file mode 100644
index 0000000..25a941f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class FilledNewArrayRange extends Format3rc {
+
+ public static final int OPCODE = 0x25;
+ public static final String NAME = "FilledNewArrayRange";
+ public static final String SMALI_NAME = "filled-new-array/range";
+
+ FilledNewArrayRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getTypeMap());
+ }
+
+ public FilledNewArrayRange(int firstContentRegister, int size, DexType type) {
+ super(firstContentRegister, size, type);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ public DexType getType() {
+ return (DexType) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeRangeNewArray(getType(), AA, CCCC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FloatToDouble.java b/src/main/java/com/android/tools/r8/code/FloatToDouble.java
new file mode 100644
index 0000000..ce76060
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FloatToDouble.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class FloatToDouble extends Format12x {
+
+ public static final int OPCODE = 0x89;
+ public static final String NAME = "FloatToDouble";
+ public static final String SMALI_NAME = "float-to-double";
+
+ FloatToDouble(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public FloatToDouble(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.DOUBLE, NumericType.FLOAT, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FloatToInt.java b/src/main/java/com/android/tools/r8/code/FloatToInt.java
new file mode 100644
index 0000000..46fad1e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FloatToInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class FloatToInt extends Format12x {
+
+ public static final int OPCODE = 0x87;
+ public static final String NAME = "FloatToInt";
+ public static final String SMALI_NAME = "float-to-int";
+
+ FloatToInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public FloatToInt(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.INT, NumericType.FLOAT, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FloatToLong.java b/src/main/java/com/android/tools/r8/code/FloatToLong.java
new file mode 100644
index 0000000..afbc599
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FloatToLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class FloatToLong extends Format12x {
+
+ public static final int OPCODE = 0x88;
+ public static final String NAME = "FloatToLong";
+ public static final String SMALI_NAME = "float-to-long";
+
+ FloatToLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public FloatToLong(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.LONG, NumericType.FLOAT, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format10t.java b/src/main/java/com/android/tools/r8/code/Format10t.java
new file mode 100644
index 0000000..ee2e6fa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format10t.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format10t extends Base1Format {
+
+ public final /* offset */ int AA;
+
+ // +AA | op
+ Format10t(int high, BytecodeStream stream) {
+ super(stream);
+ // AA is an offset, so convert to signed.
+ AA = (byte) high;
+ }
+
+ protected Format10t(int AA) {
+ assert Byte.MIN_VALUE <= AA && AA <= Byte.MAX_VALUE;
+ this.AA = AA;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ }
+
+ public final int hashCode() {
+ return AA ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ return ((Format10t) other).AA == AA;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ String relative = AA >= 0 ? ("+" + AA) : Integer.toString(AA);
+ return formatString(relative + " (" + (getOffset() + AA) + ")");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString(":label_" + (getOffset() + AA));
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format10x.java b/src/main/java/com/android/tools/r8/code/Format10x.java
new file mode 100644
index 0000000..132a5b9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format10x.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format10x extends Base1Format {
+
+ // øø | op
+ Format10x(int high, BytecodeStream stream) {
+ // Intentionally left empty.
+ super(stream);
+ }
+
+ protected Format10x() {
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(0, dest);
+ }
+
+
+ public int hashCode() {
+ return getClass().hashCode();
+ }
+
+ public boolean equals(Object other) {
+ return (other != null) && (this.getClass() == other.getClass());
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString(null);
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString(null);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format11n.java b/src/main/java/com/android/tools/r8/code/Format11n.java
new file mode 100644
index 0000000..561c7c9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format11n.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format11n extends Base1Format {
+
+ public final int A, B;
+
+ // #+B | vA | op
+ /*package*/ Format11n(int high, BytecodeStream stream) {
+ super(stream);
+ A = high & 0xf;
+ // Sign extend 4bit value.
+ high >>= 4;
+ if ((high & Constants.S4BIT_SIGN_MASK) != 0) {
+ B = ~(~high & 0xf);
+ } else {
+ B = high & 0xf;
+ }
+ }
+
+ /*package*/ Format11n(int A, int B) {
+ assert 0 <= A && A <= Constants.U4BIT_MAX;
+ assert Constants.S4BIT_MIN <= B && B <= Constants.S4BIT_MAX;
+ this.A = A;
+ this.B = B;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(B, A, dest);
+ }
+
+ public final int hashCode() {
+ return ((A << 4) | B) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ Format11n o = (Format11n) other;
+ return o.A == A && o.B == B;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + A + ", #" + B);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format11x.java b/src/main/java/com/android/tools/r8/code/Format11x.java
new file mode 100644
index 0000000..baf4b57
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format11x.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format11x extends Base1Format {
+
+ public final int AA;
+
+ // vAA | op
+ Format11x(int high, BytecodeStream stream) {
+ super(stream);
+ AA = high;
+ }
+
+ protected Format11x(int AA) {
+ assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+ this.AA = AA;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ }
+
+ public final int hashCode() {
+ return AA ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ return ((Format11x) other).AA == AA;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA);
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format12x.java b/src/main/java/com/android/tools/r8/code/Format12x.java
new file mode 100644
index 0000000..4338b46
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format12x.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format12x extends Base1Format {
+
+ public final int A, B;
+
+ // vB | vA | op
+ Format12x(int high, BytecodeStream stream) {
+ super(stream);
+ A = high & 0xF;
+ B = (high >> 4) & 0xF;
+ }
+
+ Format12x(int A, int B) {
+ assert 0 <= A && A <= Constants.U4BIT_MAX;
+ assert 0 <= B && B <= Constants.U4BIT_MAX;
+ this.A = A;
+ this.B = B;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(B, A, dest);
+ }
+
+ public final int hashCode() {
+ return ((A << 4) | B) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format12x o = (Format12x) other;
+ return o.A == A && o.B == B;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + A + ", v" + B);
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + A + ", v" + B);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format20t.java b/src/main/java/com/android/tools/r8/code/Format20t.java
new file mode 100644
index 0000000..4f6f235
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format20t.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format20t extends Base2Format {
+
+ public final /* offset */ int AAAA;
+
+ // øø | op | +AAAA
+ Format20t(int high, BytecodeStream stream) {
+ super(stream);
+ AAAA = readSigned16BitValue(stream);
+ }
+
+ protected Format20t(int AAAA) {
+ assert Short.MIN_VALUE <= AAAA && AAAA <= Short.MAX_VALUE;
+ this.AAAA = AAAA;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(0, dest);
+ write16BitValue(AAAA, dest);
+ }
+
+ public final int hashCode() {
+ return AAAA ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ return ((Format20t) other).AAAA == AAAA;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("" + AAAA + " (" + (getOffset() + AAAA) + ")");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString(":label_" + (getOffset() + AAAA));
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format21c.java b/src/main/java/com/android/tools/r8/code/Format21c.java
new file mode 100644
index 0000000..411bc02
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format21c.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+abstract class Format21c extends Base2Format {
+
+ public final int AA;
+ public IndexedDexItem BBBB;
+
+ // AA | op | [type|field|string]@BBBB
+ Format21c(int high, BytecodeStream stream, IndexedDexItem[] map) {
+ super(stream);
+ AA = high;
+ BBBB = map[read16BitValue(stream)];
+ }
+
+ protected Format21c(int AA, IndexedDexItem BBBB) {
+ assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+ this.AA = AA;
+ this.BBBB = BBBB;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ write16BitReference(BBBB, dest, mapping);
+ }
+
+ public final int hashCode() {
+ return ((BBBB.hashCode() << 8) | AA) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format21c o = (Format21c) other;
+ return o.AA == AA && o.BBBB.equals(BBBB);
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString(
+ "v" + AA + ", " + (naming == null ? BBBB.toString() : naming.originalNameOf(BBBB)));
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ // TODO(sgjesse): Add support for smali name mapping.
+ return formatSmaliString("v" + AA + ", " + BBBB.toSmaliString());
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ BBBB.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format21c o = (Format21c) other;
+ return o.AA == AA && equality.test(BBBB, o.BBBB);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format21h.java b/src/main/java/com/android/tools/r8/code/Format21h.java
new file mode 100644
index 0000000..5f3f247
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format21h.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import java.nio.ShortBuffer;
+
+abstract class Format21h extends Base2Format {
+
+ public final int AA;
+ public final int BBBB;
+
+ // AA | op | BBBB0000[00000000]
+ /*package*/ Format21h(int high, BytecodeStream stream) {
+ super(stream);
+ AA = high;
+ BBBB = read16BitValue(stream);
+ }
+
+ /*package*/ Format21h(int AA, int BBBB) {
+ assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+ assert 0 <= BBBB && BBBB <= Constants.U16BIT_MAX;
+ this.AA = AA;
+ this.BBBB = BBBB;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ write16BitValue(BBBB, dest);
+ }
+
+ public final int hashCode() {
+ return ((BBBB << 8) | AA) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format21h o = (Format21h) other;
+ return o.AA == AA && o.BBBB == BBBB;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format21s.java b/src/main/java/com/android/tools/r8/code/Format21s.java
new file mode 100644
index 0000000..79d15ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format21s.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+
+abstract class Format21s extends Base2Format {
+
+ public final int AA;
+ public final int BBBB;
+
+ // AA | op | #+BBBB
+ /*package*/ Format21s(int high, BytecodeStream stream) {
+ super(stream);
+ AA = high;
+ BBBB = readSigned16BitValue(stream);
+ }
+
+ /*package*/ Format21s(int AA, int BBBB) {
+ assert Short.MIN_VALUE <= BBBB && BBBB <= Short.MAX_VALUE;
+ assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+ this.AA = AA;
+ this.BBBB = BBBB;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ write16BitValue(BBBB, dest);
+ }
+
+ public final int hashCode() {
+ return ((BBBB << 8) | AA) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format21s o = (Format21s) other;
+ return o.AA == AA && o.BBBB == BBBB;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", #" + BBBB);
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(BBBB, 4) + " # " + BBBB);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format21t.java b/src/main/java/com/android/tools/r8/code/Format21t.java
new file mode 100644
index 0000000..a49abb5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format21t.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format21t extends Base2Format {
+
+ public final int AA;
+ public final /* offset */ int BBBB;
+
+ // AA | op | +BBBB
+ Format21t(int high, BytecodeStream stream) {
+ super(stream);
+ AA = high;
+ BBBB = readSigned16BitValue(stream);
+ }
+
+ Format21t(int register, int offset) {
+ assert Short.MIN_VALUE <= offset && offset <= Short.MAX_VALUE;
+ assert 0 <= register && register <= Constants.U8BIT_MAX;
+ AA = register;
+ BBBB = offset;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ write16BitValue(BBBB, dest);
+ }
+
+ public final int hashCode() {
+ return ((BBBB << 8) | AA) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format21t o = (Format21t) other;
+ return o.AA == AA && o.BBBB == BBBB;
+ }
+
+ public abstract Type getType();
+
+ @Override
+ public int[] getTargets() {
+ return new int[]{BBBB, getSize()};
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ int offset = getOffset();
+ int size = getSize();
+ builder.addIfZero(getType(), AA, offset + BBBB, offset + size);
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", +" + BBBB + " (" + (getOffset() + BBBB) + ")");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", :label_" + (getOffset() + BBBB));
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format22b.java b/src/main/java/com/android/tools/r8/code/Format22b.java
new file mode 100644
index 0000000..a92a9f1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format22b.java
@@ -0,0 +1,66 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+
+public abstract class Format22b extends Base2Format {
+
+ public final int AA;
+ public final int BB;
+ public final int CC;
+
+ // vAA | op | #+CC | VBB
+ /*package*/ Format22b(int high, BytecodeStream stream) {
+ super(stream);
+ AA = high;
+ CC = readSigned8BitValue(stream);
+ BB = read8BitValue(stream);
+ }
+
+ /*package*/ Format22b(int AA, int BB, int CC) {
+ assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+ assert 0 <= BB && BB <= Constants.U8BIT_MAX;
+ assert Byte.MIN_VALUE <= CC && CC <= Byte.MAX_VALUE;
+ this.AA = AA;
+ this.BB = BB;
+ this.CC = CC;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ write16BitValue(combineBytes(CC, BB), dest);
+ }
+
+ public final int hashCode() {
+ return ((AA << 16) | (BB << 8) | CC) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format22b o = (Format22b) other;
+ return o.AA == AA && o.BB == BB && o.CC == CC;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", v" + BB + ", #" + CC);
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString(
+ "v" + AA + ", v" + BB + ", 0x" + StringUtils.hexString(CC, 2) + " # " + CC);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format22c.java b/src/main/java/com/android/tools/r8/code/Format22c.java
new file mode 100644
index 0000000..f97089c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format22c.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+abstract class Format22c extends Base2Format {
+
+ public final int A;
+ public final int B;
+ public IndexedDexItem CCCC;
+
+ // vB | vA | op | [type|field]@CCCC
+ /*package*/ Format22c(int high, BytecodeStream stream, IndexedDexItem[] map) {
+ super(stream);
+ A = high & 0xf;
+ B = (high >> 4) & 0xf;
+ CCCC = map[read16BitValue(stream)];
+ }
+
+ /*package*/ Format22c(int A, int B, IndexedDexItem CCCC) {
+ assert 0 <= A && A <= Constants.U4BIT_MAX;
+ assert 0 <= B && B <= Constants.U4BIT_MAX;
+ this.A = A;
+ this.B = B;
+ this.CCCC = CCCC;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(B, A, dest);
+ write16BitReference(CCCC, dest, mapping);
+ }
+
+ public final int hashCode() {
+ return ((CCCC.hashCode() << 8) | (A << 4) | B) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format22c o = (Format22c) other;
+ return o.A == A && o.B == B && o.CCCC.equals(CCCC);
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString(
+ "v" + A + ", v" + B + ", " + (naming == null ? CCCC : naming.originalNameOf(CCCC)));
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ // TODO(sgjesse): Add support for smali name mapping.
+ return formatSmaliString("v" + A + ", v" + B + ", " + CCCC.toSmaliString());
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ CCCC.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format22c o = (Format22c) other;
+ return o.A == A && o.B == B && equality.test(CCCC, o.CCCC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format22s.java b/src/main/java/com/android/tools/r8/code/Format22s.java
new file mode 100644
index 0000000..6b045f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format22s.java
@@ -0,0 +1,66 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+
+public abstract class Format22s extends Base2Format {
+
+ public final int A;
+ public final int B;
+ public final int CCCC;
+
+ // vB | vA | op | #+CCCC
+ /*package*/ Format22s(int high, BytecodeStream stream) {
+ super(stream);
+ A = high & 0xf;
+ B = (high >> 4) & 0xf;
+ CCCC = readSigned16BitValue(stream);
+ }
+
+ /*package*/ Format22s(int A, int B, int CCCC) {
+ assert 0 <= A && A <= Constants.U4BIT_MAX;
+ assert 0 <= B && B <= Constants.U4BIT_MAX;
+ assert Short.MIN_VALUE <= CCCC && CCCC <= Short.MAX_VALUE;
+ this.A = A;
+ this.B = B;
+ this.CCCC = CCCC;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(B, A, dest);
+ write16BitValue(CCCC, dest);
+ }
+
+ public final int hashCode() {
+ return ((CCCC << 8) | (A << 4) | B) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format22s o = (Format22s) other;
+ return o.A == A && o.B == B && o.CCCC == CCCC;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + A + ", v" + B + ", #" + CCCC);
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString(
+ "v" + A + ", v" + B + ", 0x" + StringUtils.hexString(CCCC, 4) + " # " + CCCC);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format22t.java b/src/main/java/com/android/tools/r8/code/Format22t.java
new file mode 100644
index 0000000..04a3d2e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format22t.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format22t extends Base2Format {
+
+ public final int A;
+ public final int B;
+ public final /* offset */ int CCCC;
+
+ // vB | vA | op | +CCCC
+ Format22t(int high, BytecodeStream stream) {
+ super(stream);
+ A = high & 0xf;
+ B = (high >> 4) & 0xf;
+ CCCC = readSigned16BitValue(stream);
+ }
+
+ Format22t(int register1, int register2, int offset) {
+ assert 0 <= register1 && register1 <= Constants.U4BIT_MAX;
+ assert 0 <= register2 && register2 <= Constants.U4BIT_MAX;
+ assert Short.MIN_VALUE <= offset && offset <= Short.MAX_VALUE;
+ A = register1;
+ B = register2;
+ CCCC = offset;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(B, A, dest);
+ write16BitValue(CCCC, dest);
+ }
+
+ public final int hashCode() {
+ return ((CCCC << 8) | (B << 4) | A) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format22t o = (Format22t) other;
+ return o.A == A && o.B == B && o.CCCC == CCCC;
+ }
+
+ public abstract Type getType();
+
+ @Override
+ public int[] getTargets() {
+ return new int[]{CCCC, getSize()};
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ int offset = getOffset();
+ int size = getSize();
+ builder.addIf(getType(), A, B, offset + CCCC, offset + size);
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + A + ", v" + B + ", +" + CCCC + " (" + (getOffset() + CCCC) + ")");
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + A + ", v" + B + ", :label_" + (getOffset() + CCCC));
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format22x.java b/src/main/java/com/android/tools/r8/code/Format22x.java
new file mode 100644
index 0000000..789b12c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format22x.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format22x extends Base2Format {
+
+ public final int AA;
+ public final int BBBB;
+
+ // AA | op | vBBBB
+ Format22x(int high, BytecodeStream stream) {
+ super(stream);
+ AA = high;
+ BBBB = read16BitValue(stream);
+ }
+
+ Format22x(int dest, int src) {
+ assert 0 <= dest && dest <= Constants.U8BIT_MAX;
+ assert 0 <= src && src <= Constants.U16BIT_MAX;
+ AA = dest;
+ BBBB = src;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ write16BitValue(BBBB, dest);
+ }
+
+ public final int hashCode() {
+ return ((BBBB << 8) | AA) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format22x o = (Format22x) other;
+ return o.AA == AA && o.BBBB == BBBB;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", v" + BBBB);
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", v" + BBBB);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format23x.java b/src/main/java/com/android/tools/r8/code/Format23x.java
new file mode 100644
index 0000000..fd3bdab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format23x.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format23x extends Base2Format {
+
+ public final int AA;
+ public final int BB;
+ public final int CC;
+
+ // vAA | op | vCC | vBB
+ Format23x(int high, BytecodeStream stream) {
+ super(stream);
+ AA = high;
+ CC = read8BitValue(stream);
+ BB = read8BitValue(stream);
+ }
+
+ Format23x(int AA, int BB, int CC) {
+ assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+ assert 0 <= BB && BB <= Constants.U8BIT_MAX;
+ assert 0 <= CC && CC <= Constants.U8BIT_MAX;
+ this.AA = AA;
+ this.BB = BB;
+ this.CC = CC;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ write16BitValue(combineBytes(CC, BB), dest);
+ }
+
+ public final int hashCode() {
+ return ((AA << 16) | (BB << 8) | CC) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format23x o = (Format23x) other;
+ return o.AA == AA && o.BB == BB && o.CC == CC;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", v" + BB + ", v" + CC);
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", v" + BB + ", v" + CC);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format30t.java b/src/main/java/com/android/tools/r8/code/Format30t.java
new file mode 100644
index 0000000..927c879
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format30t.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format30t extends Base3Format {
+
+ public final /* offset */ int AAAAAAAA;
+
+ // øø | op | AAAAlo | AAAAhi
+ Format30t(int high, BytecodeStream stream) {
+ super(stream);
+ AAAAAAAA = readSigned32BitValue(stream);
+ }
+
+ protected Format30t(int AAAAAAAA) {
+ this.AAAAAAAA = AAAAAAAA;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(0, dest);
+ write32BitValue(AAAAAAAA, dest);
+ }
+
+ public final int hashCode() {
+ return AAAAAAAA ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ return ((Format30t) other).AAAAAAAA == AAAAAAAA;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("" + AAAAAAAA);
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString(":label_" + (getOffset() + AAAAAAAA));
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format31c.java b/src/main/java/com/android/tools/r8/code/Format31c.java
new file mode 100644
index 0000000..b86d58b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format31c.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import static com.android.tools.r8.dex.Constants.U8BIT_MAX;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+abstract class Format31c extends Base3Format {
+
+ public final int AA;
+ public DexString BBBBBBBB;
+
+ // vAA | op | string@BBBBlo | string@#+BBBBhi
+ Format31c(int high, BytecodeStream stream, DexString[] map) {
+ super(stream);
+ AA = high;
+ BBBBBBBB = map[(int) read32BitValue(stream)];
+ }
+
+ Format31c(int AA, DexString BBBBBBBB) {
+ assert 0 <= AA && AA <= U8BIT_MAX;
+ this.AA = AA;
+ this.BBBBBBBB = BBBBBBBB;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ write32BitReference(BBBBBBBB, dest, mapping);
+ }
+
+ public final int hashCode() {
+ return ((BBBBBBBB.hashCode() << 8) | AA) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ Format31c o = (Format31c) other;
+ return o.AA == AA && o.BBBBBBBB.equals(BBBBBBBB);
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString(
+ "v" + AA + ", " + (naming == null ? BBBBBBBB : naming.originalNameOf(BBBBBBBB)));
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ BBBBBBBB.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ Format31c o = (Format31c) other;
+ return o.AA == AA && equality.test(BBBBBBBB, o.BBBBBBBB);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format31i.java b/src/main/java/com/android/tools/r8/code/Format31i.java
new file mode 100644
index 0000000..09192d6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format31i.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format31i extends Base3Format {
+
+ public final int AA;
+ public final int BBBBBBBB;
+
+ // vAA | op | #+BBBBlo | #+BBBBhi
+ /*package*/ Format31i(int high, BytecodeStream stream) {
+ super(stream);
+ AA = high;
+ BBBBBBBB = readSigned32BitValue(stream);
+ }
+
+ /*package*/ Format31i(int AA, int BBBBBBBB) {
+ assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+ this.AA = AA;
+ this.BBBBBBBB = BBBBBBBB;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ write32BitValue(BBBBBBBB, dest);
+ }
+
+ public final int hashCode() {
+ return ((BBBBBBBB << 8) | AA) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ Format31i o = (Format31i) other;
+ return o.AA == AA && o.BBBBBBBB == BBBBBBBB;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", #" + BBBBBBBB);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format31t.java b/src/main/java/com/android/tools/r8/code/Format31t.java
new file mode 100644
index 0000000..f179017
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format31t.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+public abstract class Format31t extends Base3Format {
+
+ public final int AA;
+ protected /* offset */ int BBBBBBBB;
+
+ // vAA | op | +BBBBlo | +BBBBhi
+ Format31t(int high, BytecodeStream stream) {
+ super(stream);
+ AA = high;
+ BBBBBBBB = readSigned32BitValue(stream);
+ }
+
+ Format31t(int register, int payloadOffset) {
+ assert 0 <= register && register <= Constants.U8BIT_MAX;
+ AA = register;
+ BBBBBBBB = payloadOffset;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ assert (getOffset() + BBBBBBBB) % 2 == 0;
+ write32BitValue(BBBBBBBB, dest);
+ }
+
+ public boolean hasPayload() {
+ return true;
+ }
+
+ public int getPayloadOffset() {
+ return BBBBBBBB;
+ }
+
+ public void setPayloadOffset(int offset) {
+ BBBBBBBB = offset;
+ }
+
+ public final int hashCode() {
+ return ((BBBBBBBB << 8) | AA) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ Format31t o = (Format31t) other;
+ return o.AA == AA && o.BBBBBBBB == BBBBBBBB;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", +" + BBBBBBBB + " (" + (getOffset() + BBBBBBBB) + ")");
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format32x.java b/src/main/java/com/android/tools/r8/code/Format32x.java
new file mode 100644
index 0000000..1dace6f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format32x.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import static com.android.tools.r8.dex.Constants.U16BIT_MAX;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format32x extends Base3Format {
+
+ public final int AAAA;
+ public final int BBBB;
+
+ // øø | op | AAAA | BBBB
+ Format32x(int high, BytecodeStream stream) {
+ super(stream);
+ AAAA = read16BitValue(stream);
+ BBBB = read16BitValue(stream);
+ }
+
+ Format32x(int dest, int src) {
+ assert 0 <= dest && dest <= U16BIT_MAX;
+ assert 0 <= src && src <= U16BIT_MAX;
+ AAAA = dest;
+ BBBB = src;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(0, dest);
+ write16BitValue(AAAA, dest);
+ write16BitValue(BBBB, dest);
+ }
+
+ public final int hashCode() {
+ return ((AAAA << 16) | BBBB) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ Format32x o = (Format32x) other;
+ return o.AAAA == AAAA && o.BBBB == BBBB;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AAAA + ", v" + BBBB);
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AAAA + ", v" + BBBB);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format35c.java b/src/main/java/com/android/tools/r8/code/Format35c.java
new file mode 100644
index 0000000..03771e5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format35c.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+public abstract class Format35c extends Base3Format {
+
+ public final int A;
+ public final int C;
+ public final int D;
+ public final int E;
+ public final int F;
+ public final int G;
+ public IndexedDexItem BBBB;
+
+ // A | G | op | BBBB | F | E | D | C
+ Format35c(int high, BytecodeStream stream, IndexedDexItem[] map) {
+ super(stream);
+ G = high & 0xf;
+ A = (high >> 4) & 0xf;
+ BBBB = map[read16BitValue(stream)];
+ int next = read8BitValue(stream);
+ E = next & 0xf;
+ F = (next >> 4) & 0xf;
+ next = read8BitValue(stream);
+ C = next & 0xf;
+ D = (next >> 4) & 0xf;
+ }
+
+ protected Format35c(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+ assert 0 <= A && A <= Constants.U4BIT_MAX;
+ assert 0 <= C && C <= Constants.U4BIT_MAX;
+ assert 0 <= D && D <= Constants.U4BIT_MAX;
+ assert 0 <= E && E <= Constants.U4BIT_MAX;
+ assert 0 <= F && F <= Constants.U4BIT_MAX;
+ assert 0 <= G && G <= Constants.U4BIT_MAX;
+ this.A = A;
+ this.BBBB = BBBB;
+ this.C = C;
+ this.D = D;
+ this.E = E;
+ this.F = F;
+ this.G = G;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(A, G, dest);
+ write16BitReference(BBBB, dest, mapping);
+ write16BitValue(combineBytes(makeByte(F, E), makeByte(D, C)), dest);
+ }
+
+ public final int hashCode() {
+ return ((BBBB.hashCode() << 24) | (A << 20) | (C << 16) | (D << 12) | (E << 8) | (F << 4)
+ | G) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ Format35c o = (Format35c) other;
+ return o.A == A && o.C == C && o.D == D && o.E == E && o.F == F && o.G == G
+ && o.BBBB.equals(BBBB);
+ }
+
+ private void appendRegisterArguments(StringBuilder builder, String separator) {
+ builder.append("{ ");
+ int[] values = new int[]{C, D, E, F, G};
+ for (int i = 0; i < A; i++) {
+ if (i != 0) {
+ builder.append(separator);
+ }
+ builder.append("v").append(values[i]);
+ }
+ builder.append(" }");
+ }
+
+ public String toString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder();
+ appendRegisterArguments(builder, " ");
+ builder.append(" ");
+ if (naming == null) {
+ builder.append(BBBB.toSmaliString());
+ } else {
+ builder.append(naming.originalNameOf(BBBB));
+ }
+ return formatString(builder.toString());
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder();
+ appendRegisterArguments(builder, ", ");
+ builder.append(", ");
+ // TODO(sgjesse): Add support for smali name mapping.
+ builder.append(BBBB.toSmaliString());
+ return formatSmaliString(builder.toString());
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ BBBB.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ Format35c o = (Format35c) other;
+ return o.A == A && o.C == C && o.D == D && o.E == E && o.F == F && o.G == G
+ && equality.test(BBBB, o.BBBB);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format3rc.java b/src/main/java/com/android/tools/r8/code/Format3rc.java
new file mode 100644
index 0000000..b8ee74e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format3rc.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+public abstract class Format3rc extends Base3Format {
+
+ public final int AA;
+ public final int CCCC;
+ public IndexedDexItem BBBB;
+
+ // AA | op | [meth|type]@BBBBB | CCCC
+ Format3rc(int high, BytecodeStream stream, IndexedDexItem[] map) {
+ super(stream);
+ this.AA = high;
+ this.BBBB = map[read16BitValue(stream)];
+ this.CCCC = read16BitValue(stream);
+ }
+
+ Format3rc(int firstArgumentRegister, int argumentCount, IndexedDexItem dexItem) {
+ assert 0 <= firstArgumentRegister && firstArgumentRegister <= Constants.U16BIT_MAX;
+ assert 0 <= argumentCount && argumentCount <= Constants.U8BIT_MAX;
+ this.CCCC = firstArgumentRegister;
+ this.AA = argumentCount;
+ BBBB = dexItem;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ write16BitReference(BBBB, dest, mapping);
+ write16BitValue(CCCC, dest);
+ }
+
+ public final int hashCode() {
+ return ((CCCC << 24) | (BBBB.hashCode() << 4) | AA) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ Format3rc o = (Format3rc) other;
+ return o.AA == AA && o.CCCC == CCCC && o.BBBB.equals(BBBB);
+ }
+
+ private void appendRegisterRange(StringBuilder builder) {
+ builder.append("{ ");
+ builder.append("v").append(CCCC);
+ if (AA != 1) {
+ builder.append(" .. v").append(CCCC + AA - 1);
+ }
+ builder.append(" }");
+ }
+
+ public String toString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder();
+ appendRegisterRange(builder);
+ builder.append(" ");
+ if (naming == null) {
+ builder.append(BBBB.toSmaliString());
+ } else {
+ builder.append(naming.originalNameOf(BBBB));
+ }
+ return formatString(builder.toString());
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder();
+ appendRegisterRange(builder);
+ builder.append(", ");
+ // TODO(sgjesse): Add support for smali name mapping.
+ builder.append(BBBB.toSmaliString());
+ return formatSmaliString(builder.toString());
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ BBBB.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ Format3rc o = (Format3rc) other;
+ return o.AA == AA && o.CCCC == CCCC && equality.test(BBBB, o.BBBB);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format45cc.java b/src/main/java/com/android/tools/r8/code/Format45cc.java
new file mode 100644
index 0000000..bea1807
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format45cc.java
@@ -0,0 +1,155 @@
+// 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.code;
+
+import static com.android.tools.r8.dex.Constants.U4BIT_MAX;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+/** Format45cc for instructions of size 4, with 5 registers and 2 constant pool index. */
+public abstract class Format45cc extends Base4Format {
+
+ public final int A;
+ public final int C;
+ public final int D;
+ public final int E;
+ public final int F;
+ public final int G;
+ public DexMethod BBBB;
+ public DexProto HHHH;
+
+ Format45cc(int high, BytecodeStream stream, DexMethod[] methodMap, DexProto[] protoMap) {
+ super(stream);
+ G = high & 0xf;
+ A = (high >> 4) & 0xf;
+ BBBB = methodMap[read16BitValue(stream)];
+ int next = read8BitValue(stream);
+ E = next & 0xf;
+ F = (next >> 4) & 0xf;
+ next = read8BitValue(stream);
+ C = next & 0xf;
+ D = (next >> 4) & 0xf;
+ HHHH = protoMap[read16BitValue(stream)];
+ }
+
+ // A | G | op | [meth]@BBBB | F | E | D | C | [proto]@HHHH
+ protected Format45cc(int A, DexMethod BBBB, DexProto HHHH, int C, int D, int E, int F, int G) {
+ assert 0 <= A && A <= U4BIT_MAX;
+ assert 0 <= C && C <= U4BIT_MAX;
+ assert 0 <= D && D <= U4BIT_MAX;
+ assert 0 <= E && E <= U4BIT_MAX;
+ assert 0 <= F && F <= U4BIT_MAX;
+ assert 0 <= G && G <= U4BIT_MAX;
+ this.A = A;
+ this.BBBB = BBBB;
+ this.HHHH = HHHH;
+ this.C = C;
+ this.D = D;
+ this.E = E;
+ this.F = F;
+ this.G = G;
+ }
+
+ public final int hashCode() {
+ return ((HHHH.hashCode() << 28)
+ | (BBBB.hashCode() << 24)
+ | (A << 20)
+ | (C << 16)
+ | (D << 12)
+ | (E << 8)
+ | (F << 4)
+ | G)
+ ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ Format45cc o = (Format45cc) other;
+ return o.A == A
+ && o.C == C
+ && o.D == D
+ && o.E == E
+ && o.F == F
+ && o.G == G
+ && o.BBBB.equals(BBBB)
+ && o.HHHH.equals(HHHH);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ BBBB.collectIndexedItems(indexedItems);
+ HHHH.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(A, G, dest);
+ write16BitReference(BBBB, dest, mapping);
+ write16BitValue(combineBytes(makeByte(F, E), makeByte(D, C)), dest);
+ write16BitReference(HHHH, dest, mapping);
+ }
+
+ @Override
+ public String toSmaliString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder();
+ appendRegisterArguments(builder, ", ");
+ builder.append(", ");
+ // TODO(sgjesse): Add support for smali name mapping.
+ builder.append(BBBB.toSmaliString());
+ builder.append(", ");
+ builder.append(HHHH.toSmaliString());
+ return formatSmaliString(builder.toString());
+ }
+
+ @Override
+ public String toString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder();
+ appendRegisterArguments(builder, " ");
+ builder.append(" ");
+ builder.append(toString(BBBB, naming));
+ builder.append(", ");
+ builder.append(toString(HHHH, naming));
+ return formatString(builder.toString());
+ }
+
+ private String toString(IndexedDexItem indexedDexItem, ClassNameMapper naming) {
+ String str;
+ if (naming == null) {
+ str = indexedDexItem.toSmaliString();
+ } else {
+ str = naming.originalNameOf(indexedDexItem);
+ }
+ return str;
+ }
+
+ private void appendRegisterArguments(StringBuilder builder, String separator) {
+ builder.append("{ ");
+ int[] values = new int[] {C, D, E, F, G};
+ for (int i = 0; i < A; i++) {
+ if (i != 0) {
+ builder.append(separator);
+ }
+ builder.append("v").append(values[i]);
+ }
+ builder.append(" }");
+ }
+
+ @Override
+ public DexMethod getMethod() {
+ return BBBB;
+ }
+
+ @Override
+ public DexProto getProto() {
+ return HHHH;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format4rcc.java b/src/main/java/com/android/tools/r8/code/Format4rcc.java
new file mode 100644
index 0000000..7072805
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format4rcc.java
@@ -0,0 +1,126 @@
+// 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.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+/** Format4rcc for instructions of size 4, with a range of registers and 2 constant pool index. */
+public abstract class Format4rcc extends Base4Format {
+
+ public final int AA;
+ public final int CCCC;
+ public DexMethod BBBB;
+ public DexProto HHHH;
+
+ // AA | op | [meth]@BBBB | CCCC | [proto]@HHHH
+ Format4rcc(int high, BytecodeStream stream, DexMethod[] methodMap, DexProto[] protoMap) {
+ super(stream);
+ this.AA = high;
+ this.BBBB = methodMap[read16BitValue(stream)];
+ this.CCCC = read16BitValue(stream);
+ this.HHHH = protoMap[read16BitValue(stream)];
+ }
+
+ Format4rcc(int firstArgumentRegister, int argumentCount, DexMethod method, DexProto proto) {
+ assert 0 <= firstArgumentRegister && firstArgumentRegister <= Constants.U16BIT_MAX;
+ assert 0 <= argumentCount && argumentCount <= Constants.U8BIT_MAX;
+ this.CCCC = firstArgumentRegister;
+ this.AA = argumentCount;
+ BBBB = method;
+ HHHH = proto;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ write16BitReference(BBBB, dest, mapping);
+ write16BitValue(CCCC, dest);
+ write16BitReference(HHHH, dest, mapping);
+ }
+
+ public final int hashCode() {
+ return ((CCCC << 24) | (HHHH.hashCode() << 12) | (BBBB.hashCode() << 4) | AA)
+ ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ Format4rcc o = (Format4rcc) other;
+ return o.AA == AA && o.CCCC == CCCC && o.BBBB.equals(BBBB) & o.HHHH.equals(HHHH);
+ }
+
+ public String toString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder();
+ appendRegisterRange(builder);
+ builder.append(" ");
+ if (naming == null) {
+ builder.append(BBBB.toSmaliString());
+ } else {
+ builder.append(naming.originalNameOf(BBBB));
+ }
+ if (naming == null) {
+ builder.append(HHHH.toSmaliString());
+ } else {
+ builder.append(naming.originalNameOf(HHHH));
+ }
+ return formatString(builder.toString());
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder();
+ appendRegisterRange(builder);
+ builder.append(", ");
+ // TODO(sgjesse): Add support for smali name mapping.
+ builder.append(BBBB.toSmaliString());
+ builder.append(", ");
+ builder.append(HHHH.toSmaliString());
+ return formatSmaliString(builder.toString());
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ BBBB.collectIndexedItems(indexedItems);
+ HHHH.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+ if (other == null || (this.getClass() != other.getClass())) {
+ return false;
+ }
+ Format4rcc o = (Format4rcc) other;
+ return o.AA == AA
+ && o.CCCC == CCCC
+ && equality.test(BBBB, o.BBBB)
+ && equality.test(HHHH, o.HHHH);
+ }
+
+ private void appendRegisterRange(StringBuilder builder) {
+ builder.append("{ ");
+ builder.append("v").append(CCCC);
+ if (AA != 1) {
+ builder.append(" .. v").append(CCCC + AA - 1);
+ }
+ builder.append(" }");
+ }
+
+ @Override
+ public DexMethod getMethod() {
+ return BBBB;
+ }
+
+ @Override
+ public DexProto getProto() {
+ return HHHH;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format51l.java b/src/main/java/com/android/tools/r8/code/Format51l.java
new file mode 100644
index 0000000..d135ffe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format51l.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format51l extends Base5Format {
+
+ public final int AA;
+ public final long BBBBBBBBBBBBBBBB;
+
+ // AA | op | BBBB | BBBB | BBBB | BBBB
+ Format51l(int high, BytecodeStream stream) {
+ super(stream);
+ AA = high;
+ BBBBBBBBBBBBBBBB = read64BitValue(stream);
+ }
+
+ public Format51l(int AA, long BBBBBBBBBBBBBBBB) {
+ assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+ this.AA = AA;
+ this.BBBBBBBBBBBBBBBB = BBBBBBBBBBBBBBBB;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(AA, dest);
+ write64BitValue(BBBBBBBBBBBBBBBB, dest);
+ }
+
+ public final int hashCode() {
+ return ((((int) BBBBBBBBBBBBBBBB) << 8) | AA) ^ getClass().hashCode();
+ }
+
+ public final boolean equals(Object other) {
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ Format51l o = (Format51l) other;
+ return o.AA == AA && o.BBBBBBBBBBBBBBBB == BBBBBBBBBBBBBBBB;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ return formatString("v" + AA + ", #" + BBBBBBBBBBBBBBBB);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // No references.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Goto.java b/src/main/java/com/android/tools/r8/code/Goto.java
new file mode 100644
index 0000000..99ecc9b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Goto.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Goto extends Format10t {
+
+ public static final int OPCODE = 0x28;
+ public static final String NAME = "Goto";
+ public static final String SMALI_NAME = "goto";
+
+ Goto(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public Goto(int AA) {
+ super(AA);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public int[] getTargets() {
+ return new int[] { AA };
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addGoto(getOffset() + AA);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Goto16.java b/src/main/java/com/android/tools/r8/code/Goto16.java
new file mode 100644
index 0000000..c1d65f0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Goto16.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Goto16 extends Format20t {
+
+ public static final int OPCODE = 0x29;
+ public static final String NAME = "Goto16";
+ public static final String SMALI_NAME = "goto/16";
+
+ Goto16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public Goto16(int AAAA) {
+ super(AAAA);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public int[] getTargets() {
+ return new int[]{AAAA};
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addGoto(getOffset() + AAAA);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Goto32.java b/src/main/java/com/android/tools/r8/code/Goto32.java
new file mode 100644
index 0000000..d1c2a92
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Goto32.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Goto32 extends Format30t {
+
+ public static final int OPCODE = 0x2a;
+ public static final String NAME = "Goto32";
+ public static final String SMALI_NAME = "goto/32";
+
+ Goto32(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public Goto32(int AAAAAAAA) {
+ super(AAAAAAAA);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public int[] getTargets() {
+ return new int[]{AAAAAAAA};
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addGoto(getOffset() + AAAAAAAA);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfEq.java b/src/main/java/com/android/tools/r8/code/IfEq.java
new file mode 100644
index 0000000..a65051e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfEq.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfEq extends Format22t {
+
+ public static final int OPCODE = 0x32;
+ public static final String NAME = "IfEq";
+ public static final String SMALI_NAME = "if-eq";
+
+ IfEq(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IfEq(int register1, int register2, int offset) {
+ super(register1, register2, offset);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.EQ;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfEqz.java b/src/main/java/com/android/tools/r8/code/IfEqz.java
new file mode 100644
index 0000000..dbcb65b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfEqz.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfEqz extends Format21t {
+
+ public static final int OPCODE = 0x38;
+ public static final String NAME = "IfEqz";
+ public static final String SMALI_NAME = "if-eqz";
+
+ IfEqz(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IfEqz(int register, int offset) {
+ super(register, offset);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.EQ;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfGe.java b/src/main/java/com/android/tools/r8/code/IfGe.java
new file mode 100644
index 0000000..e54280e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfGe.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfGe extends Format22t {
+
+ public static final int OPCODE = 0x35;
+ public static final String NAME = "IfGe";
+ public static final String SMALI_NAME = "if-ge";
+
+ IfGe(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IfGe(int register1, int register2, int offset) {
+ super(register1, register2, offset);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.GE;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfGez.java b/src/main/java/com/android/tools/r8/code/IfGez.java
new file mode 100644
index 0000000..04216c8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfGez.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfGez extends Format21t {
+
+ public static final int OPCODE = 0x3b;
+ public static final String NAME = "IfGez";
+ public static final String SMALI_NAME = "if-gez";
+
+ IfGez(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IfGez(int register, int offset) {
+ super(register, offset);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.GE;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfGt.java b/src/main/java/com/android/tools/r8/code/IfGt.java
new file mode 100644
index 0000000..75b2f88
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfGt.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfGt extends Format22t {
+
+ public static final int OPCODE = 0x36;
+ public static final String NAME = "IfGt";
+ public static final String SMALI_NAME = "if-gt";
+
+ IfGt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IfGt(int register1, int register2, int offset) {
+ super(register1, register2, offset);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.GT;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfGtz.java b/src/main/java/com/android/tools/r8/code/IfGtz.java
new file mode 100644
index 0000000..e4cc597
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfGtz.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfGtz extends Format21t {
+
+ public static final int OPCODE = 0x3c;
+ public static final String NAME = "IfGtz";
+ public static final String SMALI_NAME = "if-gtz";
+
+ IfGtz(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IfGtz(int register, int offset) {
+ super(register, offset);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.GT;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfLe.java b/src/main/java/com/android/tools/r8/code/IfLe.java
new file mode 100644
index 0000000..1a1f0b8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfLe.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfLe extends Format22t {
+
+ public static final int OPCODE = 0x37;
+ public static final String NAME = "IfLe";
+ public static final String SMALI_NAME = "if-le";
+
+ IfLe(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IfLe(int register1, int register2, int offset) {
+ super(register1, register2, offset);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.LE;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfLez.java b/src/main/java/com/android/tools/r8/code/IfLez.java
new file mode 100644
index 0000000..ff42a07
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfLez.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfLez extends Format21t {
+
+ public static final int OPCODE = 0x3d;
+ public static final String NAME = "IfLez";
+ public static final String SMALI_NAME = "if-lez";
+
+ IfLez(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IfLez(int register, int offset) {
+ super(register, offset);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.LE;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfLt.java b/src/main/java/com/android/tools/r8/code/IfLt.java
new file mode 100644
index 0000000..3b45ece
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfLt.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfLt extends Format22t {
+
+ public static final int OPCODE = 0x34;
+ public static final String NAME = "IfLt";
+ public static final String SMALI_NAME = "if-lt";
+
+ IfLt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IfLt(int register1, int register2, int offset) {
+ super(register1, register2, offset);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.LT;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfLtz.java b/src/main/java/com/android/tools/r8/code/IfLtz.java
new file mode 100644
index 0000000..a4162bc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfLtz.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfLtz extends Format21t {
+
+ public static final int OPCODE = 0x3a;
+ public static final String NAME = "IfLtz";
+ public static final String SMALI_NAME = "if-ltz";
+
+ IfLtz(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IfLtz(int register, int offset) {
+ super(register, offset);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.LT;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfNe.java b/src/main/java/com/android/tools/r8/code/IfNe.java
new file mode 100644
index 0000000..2a69bbb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfNe.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfNe extends Format22t {
+
+ public static final int OPCODE = 0x33;
+ public static final String NAME = "IfNe";
+ public static final String SMALI_NAME = "if-ne";
+
+ IfNe(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IfNe(int register1, int register2, int offset) {
+ super(register1, register2, offset);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.NE;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfNez.java b/src/main/java/com/android/tools/r8/code/IfNez.java
new file mode 100644
index 0000000..a0073f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfNez.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfNez extends Format21t {
+
+ public static final int OPCODE = 0x39;
+ public static final String NAME = "IfNez";
+ public static final String SMALI_NAME = "if-nez";
+
+ IfNez(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IfNez(int register, int offset) {
+ super(register, offset);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.NE;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Iget.java b/src/main/java/com/android/tools/r8/code/Iget.java
new file mode 100644
index 0000000..c3887b2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Iget.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Iget extends Format22c {
+
+ public static final int OPCODE = 0x52;
+ public static final String NAME = "Iget";
+ public static final String SMALI_NAME = "iget";
+
+ /*package*/ Iget(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public Iget(int destRegister, int objectRegister, DexField field) {
+ super(destRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldRead(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstanceGet(MemberType.SINGLE, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IgetBoolean.java b/src/main/java/com/android/tools/r8/code/IgetBoolean.java
new file mode 100644
index 0000000..02dd1db
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IgetBoolean.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IgetBoolean extends Format22c {
+
+ public static final int OPCODE = 0x55;
+ public static final String NAME = "IgetBoolean";
+ public static final String SMALI_NAME = "iget-boolean";
+
+ /*package*/ IgetBoolean(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public IgetBoolean(int destRegister, int objectRegister, DexField field) {
+ super(destRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldRead(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstanceGet(MemberType.BOOLEAN, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IgetByte.java b/src/main/java/com/android/tools/r8/code/IgetByte.java
new file mode 100644
index 0000000..51d3698
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IgetByte.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IgetByte extends Format22c {
+
+ public static final int OPCODE = 0x56;
+ public static final String NAME = "IgetByte";
+ public static final String SMALI_NAME = "iget-byte";
+
+ /*package*/ IgetByte(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public IgetByte(int destRegister, int objectRegister, DexField field) {
+ super(destRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldRead(getField());
+ }
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstanceGet(MemberType.BYTE, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IgetChar.java b/src/main/java/com/android/tools/r8/code/IgetChar.java
new file mode 100644
index 0000000..f9f5d66
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IgetChar.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IgetChar extends Format22c {
+
+ public static final int OPCODE = 0x57;
+ public static final String NAME = "IgetChar";
+ public static final String SMALI_NAME = "iget-char";
+
+ /*package*/ IgetChar(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public IgetChar(int destRegister, int objectRegister, DexField field) {
+ super(destRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldRead(getField());
+ }
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstanceGet(MemberType.CHAR, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IgetObject.java b/src/main/java/com/android/tools/r8/code/IgetObject.java
new file mode 100644
index 0000000..3b28fb0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IgetObject.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IgetObject extends Format22c {
+
+ public static final int OPCODE = 0x54;
+ public static final String NAME = "IgetObject";
+ public static final String SMALI_NAME = "iget-object";
+
+ /*package*/ IgetObject(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public IgetObject(int destRegister, int objectRegister, DexField field) {
+ super(destRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldRead(getField());
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstanceGet(MemberType.OBJECT, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IgetShort.java b/src/main/java/com/android/tools/r8/code/IgetShort.java
new file mode 100644
index 0000000..e49c4e4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IgetShort.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IgetShort extends Format22c {
+
+ public static final int OPCODE = 0x58;
+ public static final String NAME = "IgetShort";
+ public static final String SMALI_NAME = "iget-short";
+
+ /*package*/ IgetShort(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public IgetShort(int destRegister, int objectRegister, DexField field) {
+ super(destRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldRead(getField());
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstanceGet(MemberType.SHORT, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IgetWide.java b/src/main/java/com/android/tools/r8/code/IgetWide.java
new file mode 100644
index 0000000..226d995a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IgetWide.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IgetWide extends Format22c {
+
+ public static final int OPCODE = 0x53;
+ public static final String NAME = "IgetWide";
+ public static final String SMALI_NAME = "iget-wide";
+
+ /*package*/ IgetWide(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public IgetWide(int destRegister, int objectRegister, DexField field) {
+ super(destRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldRead(getField());
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstanceGet(MemberType.WIDE, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InstanceOf.java b/src/main/java/com/android/tools/r8/code/InstanceOf.java
new file mode 100644
index 0000000..bab2d4b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InstanceOf.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InstanceOf extends Format22c {
+
+ public static final int OPCODE = 0x20;
+ public static final String NAME = "InstanceOf";
+ public static final String SMALI_NAME = "instance-of";
+
+ InstanceOf(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getTypeMap());
+ }
+
+ public InstanceOf(int dest, int value, DexType type) {
+ super(dest, value, type);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerTypeReference(getType());
+ }
+
+ public DexType getType() {
+ return (DexType) CCCC;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstanceOf(A, B, getType());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
new file mode 100644
index 0000000..316a759
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -0,0 +1,235 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+public abstract class Instruction {
+
+ public final static int[] NO_TARGETS = null;
+ public final static int[] EXIT_TARGET = new int[]{};
+
+ public int offset;
+
+ Instruction(BytecodeStream stream) {
+ // When this constructor is invoked, we have already read 1 ushort from the stream.
+ this.offset = stream.getOffset() - 1;
+ }
+
+ protected Instruction() {
+ this.offset = -1;
+ }
+
+ static int readSigned8BitValue(BytecodeStream stream) {
+ return (byte) stream.nextByte();
+ }
+
+ static int read8BitValue(BytecodeStream stream) {
+ int result = stream.nextByte();
+ return result;
+ }
+
+ static int readSigned16BitValue(BytecodeStream stream) {
+ // Convert to signed.
+ return (short) stream.nextShort();
+ }
+
+ static int read16BitValue(BytecodeStream stream) {
+ return stream.nextShort() & 0xffff;
+ }
+
+ static int readSigned32BitValue(BytecodeStream stream) {
+ int low = read16BitValue(stream);
+ int high = read16BitValue(stream);
+ int result = ((high << 16) & 0xffff0000) | (low & 0xffff);
+ return result;
+ }
+
+ static long read32BitValue(BytecodeStream stream) {
+ long low = read16BitValue(stream);
+ long high = read16BitValue(stream);
+ long result = ((high & 0xffff) << 16) | (low & 0xffff);
+ return result;
+ }
+
+ static long read64BitValue(BytecodeStream stream) {
+ long low = read32BitValue(stream);
+ long high = read32BitValue(stream);
+ long result = (high << 32) | low;
+ return result;
+ }
+
+ protected static short combineBytes(int high, int low) {
+ return (short) (((high & 0xff) << 8) | (low & 0xff));
+ }
+
+ protected static int makeByte(int high, int low) {
+ return ((high & 0xf) << 4) | low & 0xf;
+ }
+
+ protected void writeFirst(int aa, ShortBuffer dest) {
+ dest.put((short) (((aa & 0xff) << 8) | (getOpcode() & 0xff)));
+ }
+
+ protected void writeFirst(int a, int b, ShortBuffer dest) {
+ dest.put((short) (((a & 0xf) << 12) | ((b & 0xf) << 8) | (getOpcode() & 0xff)));
+ }
+
+ protected void write16BitValue(int value, ShortBuffer dest) {
+ dest.put((short) value);
+ }
+
+ protected void write32BitValue(long value, ShortBuffer dest) {
+ dest.put((short) (value & 0xffff));
+ dest.put((short) ((value >> 16) & 0xffff));
+ }
+
+ protected void write64BitValue(long value, ShortBuffer dest) {
+ write32BitValue(value & 0xffffffff, dest);
+ write32BitValue((value >> 32) & 0xffffffff, dest);
+ }
+
+ protected void write16BitReference(IndexedDexItem item, ShortBuffer dest,
+ ObjectToOffsetMapping mapping) {
+ int index = item.getOffset(mapping);
+ assert index == (index & 0xffff);
+ write16BitValue(index, dest);
+ }
+
+ protected void write32BitReference(IndexedDexItem item, ShortBuffer dest,
+ ObjectToOffsetMapping mapping) {
+ write32BitValue(item.getOffset(mapping), dest);
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ public boolean isPayload() {
+ return false;
+ }
+
+ public boolean isSwitchPayload() {
+ return false;
+ }
+
+ public boolean hasPayload() {
+ return false;
+ }
+
+ public boolean isSwitch() {
+ return false;
+ }
+
+ public int getPayloadOffset() {
+ return 0;
+ }
+
+ String formatString(String left) {
+ StringBuilder builder = new StringBuilder();
+ StringUtils.appendLeftPadded(builder, Integer.toString(getOffset()), 6);
+ builder.append(": ");
+ StringUtils.appendRightPadded(builder, getName(), 20);
+ builder.append(left == null ? "" : left);
+ return builder.toString();
+ }
+
+ String formatSmaliString(String left) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(" ");
+ if (left != null) {
+ StringUtils.appendRightPadded(builder, getSmaliName(), 20);
+ builder.append(left);
+ } else {
+ builder.append(getSmaliName());
+ }
+ return builder.toString();
+ }
+
+ public int[] getTargets() {
+ return NO_TARGETS;
+ }
+
+ public abstract void buildIR(IRBuilder builder);
+
+ public DexCallSite getCallSite() {
+ return null;
+ }
+
+ public DexMethod getMethod() {
+ return null;
+ }
+
+ public DexProto getProto() {
+ return null;
+ }
+
+ public DexField getField() {
+ return null;
+ }
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ @Override
+ public abstract int hashCode();
+
+ public abstract String getName();
+
+ public abstract String getSmaliName();
+
+ public abstract int getOpcode();
+
+ public abstract int getSize();
+
+ public String toSmaliString(Instruction payloadUser) {
+ throw new InternalCompilerError("Instruction " + payloadUser + " is not a payload user");
+ }
+
+ public abstract String toSmaliString(ClassNameMapper naming);
+
+ public String toSmaliString() {
+ return toSmaliString((ClassNameMapper) null);
+ }
+
+ public abstract String toString(ClassNameMapper naming);
+
+ public String toString() {
+ return toString(null);
+ }
+
+ public abstract void write(ShortBuffer buffer, ObjectToOffsetMapping mapping);
+
+ public abstract void collectIndexedItems(IndexedItemCollection indexedItems);
+
+ public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+ // In the default case, there is nothing to substitute.
+ return this.equals(other);
+ }
+
+ public void registerUse(UseRegistry registry) {
+ // Intentionally empty
+ }
+
+ public boolean canThrow() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InstructionFactory.java b/src/main/java/com/android/tools/r8/code/InstructionFactory.java
new file mode 100644
index 0000000..2664d83
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InstructionFactory.java
@@ -0,0 +1,103 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import java.nio.ByteBuffer;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+public class InstructionFactory extends BaseInstructionFactory {
+
+ private DexString highestSortingString = null;
+
+ static private Instruction readFrom(ShortBufferBytecodeStream stream,
+ OffsetToObjectMapping mapping) {
+ int high = stream.nextByte();
+ int opcode = stream.nextByte();
+ return create(high, opcode, stream, mapping);
+ }
+
+ public Instruction[] readSequenceFrom(ByteBuffer buffer, int startIndex, int length,
+ OffsetToObjectMapping mapping) {
+ return readSequenceFrom(buffer.asShortBuffer(), startIndex, length, mapping);
+ }
+
+ public Instruction[] readSequenceFrom(ShortBuffer buffer, int startIndex, int length,
+ OffsetToObjectMapping mapping) {
+ ShortBufferBytecodeStream range =
+ new ShortBufferBytecodeStream(buffer, startIndex, length);
+ List<Instruction> insn = new ArrayList<>(length);
+ while (range.hasMore()) {
+ Instruction instruction = readFrom(range, mapping);
+ if (instruction instanceof ConstString) {
+ updateHighestSortingString(((ConstString) instruction).getString());
+ } else if (instruction instanceof ConstStringJumbo) {
+ updateHighestSortingString(((ConstStringJumbo) instruction).getString());
+ }
+ insn.add(instruction);
+ }
+ return insn.toArray(new Instruction[insn.size()]);
+ }
+
+ public DexString getHighestSortingString() {
+ return highestSortingString;
+ }
+
+ private void updateHighestSortingString(DexString string) {
+ if (highestSortingString == null || highestSortingString.slowCompareTo(string) < 0) {
+ highestSortingString = string;
+ }
+ }
+
+ private static class ShortBufferBytecodeStream implements BytecodeStream {
+
+ private final int length;
+ private final int startIndex;
+ private final ShortBuffer source;
+
+ private int offset = 0;
+ private int nextByte;
+ private boolean cacheContainsValidByte = false;
+
+ ShortBufferBytecodeStream(ShortBuffer source, int startIndex, int length) {
+ this.startIndex = startIndex;
+ this.length = length;
+ this.source = source;
+ }
+
+ @Override
+ public int nextShort() {
+ assert !cacheContainsValidByte : "Unread byte in cache.";
+ assert offset < length;
+ int result = source.get(startIndex + offset);
+ offset += 1;
+ return result;
+ }
+
+ public int nextByte() {
+ if (cacheContainsValidByte) {
+ cacheContainsValidByte = false;
+ return nextByte;
+ } else {
+ int next = nextShort();
+ nextByte = next & 0xff;
+ cacheContainsValidByte = true;
+ return (next >> 8) & 0xff;
+ }
+ }
+
+ @Override
+ public boolean hasMore() {
+ return length - offset > 0;
+ }
+
+ @Override
+ public int getOffset() {
+ return offset;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IntToByte.java b/src/main/java/com/android/tools/r8/code/IntToByte.java
new file mode 100644
index 0000000..b16b2b7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IntToByte.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class IntToByte extends Format12x {
+
+ public static final int OPCODE = 0x8d;
+ public static final String NAME = "IntToByte";
+ public static final String SMALI_NAME = "int-to-byte";
+
+ IntToByte(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IntToByte(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.BYTE, NumericType.INT, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IntToChar.java b/src/main/java/com/android/tools/r8/code/IntToChar.java
new file mode 100644
index 0000000..d16d050
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IntToChar.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IntToChar extends Format12x {
+
+ public static final int OPCODE = 0x8e;
+ public static final String NAME = "IntToChar";
+ public static final String SMALI_NAME = "int-to-char";
+
+ IntToChar(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IntToChar(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.CHAR, NumericType.INT, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IntToDouble.java b/src/main/java/com/android/tools/r8/code/IntToDouble.java
new file mode 100644
index 0000000..422a581
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IntToDouble.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IntToDouble extends Format12x {
+
+ public static final int OPCODE = 0x83;
+ public static final String NAME = "IntToDouble";
+ public static final String SMALI_NAME = "int-to-double";
+
+ IntToDouble(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IntToDouble(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.DOUBLE, NumericType.INT, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IntToFloat.java b/src/main/java/com/android/tools/r8/code/IntToFloat.java
new file mode 100644
index 0000000..fb2f922
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IntToFloat.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class IntToFloat extends Format12x {
+
+ public static final int OPCODE = 0x82;
+ public static final String NAME = "IntToFloat";
+ public static final String SMALI_NAME = "int-to-float";
+
+ IntToFloat(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IntToFloat(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.FLOAT, NumericType.INT, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IntToLong.java b/src/main/java/com/android/tools/r8/code/IntToLong.java
new file mode 100644
index 0000000..0198a60
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IntToLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IntToLong extends Format12x {
+
+ public static final int OPCODE = 0x81;
+ public static final String NAME = "IntToLong";
+ public static final String SMALI_NAME = "int-to-long";
+
+ IntToLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IntToLong(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.LONG, NumericType.INT, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IntToShort.java b/src/main/java/com/android/tools/r8/code/IntToShort.java
new file mode 100644
index 0000000..cd9e035
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IntToShort.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IntToShort extends Format12x {
+
+ public static final int OPCODE = 0x8f;
+ public static final String NAME = "IntToShort";
+ public static final String SMALI_NAME = "int-to-short";
+
+ IntToShort(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public IntToShort(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.SHORT, NumericType.INT, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
new file mode 100644
index 0000000..504a9e3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
@@ -0,0 +1,111 @@
+// 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.code;
+
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
+import com.android.tools.r8.graph.DexValue.DexValueType;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeCustom extends Format35c {
+
+ public static final int OPCODE = 0xfc;
+ public static final String NAME = "InvokeCustom";
+ public static final String SMALI_NAME = "invoke-custom";
+
+ InvokeCustom(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getCallSiteMap());
+ }
+
+ public InvokeCustom(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+ super(A, BBBB, C, D, E, F, G);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registerCallSite(registry, getCallSite());
+ }
+
+ @Override
+ public DexCallSite getCallSite() {
+ return (DexCallSite) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeCustomRegisters(getCallSite(), A, new int[]{C, D, E, F, G});
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+
+ static void registerCallSite(UseRegistry registry, DexCallSite callSite) {
+ InvokeCustom.registerMethodHandle(registry, callSite.bootstrapMethod);
+
+ // Register bootstrap method arguments, only Type and MethodHandle need to be register.
+ for (DexValue arg : callSite.bootstrapArgs) {
+ if (arg instanceof DexValueType) {
+ registry.registerTypeReference(((DexValueType) arg).value);
+ } else if (arg instanceof DexValueMethodHandle) {
+ InvokeCustom.registerMethodHandle(registry, ((DexValueMethodHandle) arg).value);
+ }
+ }
+ }
+
+ static void registerMethodHandle(UseRegistry registry, DexMethodHandle methodHandle) {
+ switch (methodHandle.type) {
+ case INSTANCE_GET:
+ registry.registerInstanceFieldRead(methodHandle.asField());
+ break;
+ case INSTANCE_PUT:
+ registry.registerInstanceFieldWrite(methodHandle.asField());
+ break;
+ case STATIC_GET:
+ registry.registerStaticFieldRead(methodHandle.asField());
+ break;
+ case STATIC_PUT:
+ registry.registerStaticFieldWrite(methodHandle.asField());
+ break;
+ case INVOKE_INSTANCE:
+ registry.registerInvokeVirtual(methodHandle.asMethod());
+ break;
+ case INVOKE_STATIC:
+ registry.registerInvokeStatic(methodHandle.asMethod());
+ break;
+ case INVOKE_CONSTRUCTOR:
+ DexMethod method = methodHandle.asMethod();
+ registry.registerNewInstance(method.getHolder());
+ registry.registerInvokeDirect(method);
+ break;
+ case INVOKE_INTERFACE:
+ registry.registerInvokeInterface(methodHandle.asMethod());
+ break;
+ case INVOKE_SUPER:
+ registry.registerInvokeSuper(methodHandle.asMethod());
+ break;
+ default:
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
new file mode 100644
index 0000000..c66b812
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
@@ -0,0 +1,55 @@
+// 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.code;
+
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeCustomRange extends Format3rc {
+
+ public static final int OPCODE = 0xfd;
+ public static final String NAME = "InvokeCustomRange";
+ public static final String SMALI_NAME = "invoke-custom/range";
+
+ InvokeCustomRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getCallSiteMap());
+ }
+
+ public InvokeCustomRange(int firstArgumentRegister, int argumentCount, DexCallSite callSite) {
+ super(firstArgumentRegister, argumentCount, callSite);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ public DexCallSite getCallSite() {
+ return (DexCallSite) BBBB;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ InvokeCustom.registerCallSite(registry, getCallSite());
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeCustomRange(getCallSite(), AA, CCCC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/code/InvokeDirect.java
new file mode 100644
index 0000000..58fb8c1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeDirect.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeDirect extends Format35c {
+
+ public static final int OPCODE = 0x70;
+ public static final String NAME = "InvokeDirect";
+ public static final String SMALI_NAME = "invoke-direct";
+
+ InvokeDirect(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getMethodMap());
+ }
+
+ public InvokeDirect(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+ super(A, BBBB, C, D, E, F, G);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInvokeDirect(getMethod());
+ }
+
+ @Override
+ public DexMethod getMethod() {
+ return (DexMethod) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeRegisters(Type.DIRECT, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java b/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
new file mode 100644
index 0000000..742546d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeDirectRange extends Format3rc {
+
+ public static final int OPCODE = 0x76;
+ public static final String NAME = "InvokeDirectRange";
+ public static final String SMALI_NAME = "invoke-direct/range";
+
+ InvokeDirectRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getMethodMap());
+ }
+
+ public InvokeDirectRange(int firstArgumentRegister, int argumentCount, DexMethod method) {
+ super(firstArgumentRegister, argumentCount, method);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ public DexMethod getMethod() {
+ return (DexMethod) BBBB;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInvokeDirect(getMethod());
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeRange(Type.DIRECT, getMethod(), getProto(), AA, CCCC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/code/InvokeInterface.java
new file mode 100644
index 0000000..512f6bc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeInterface.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeInterface extends Format35c {
+
+ public static final int OPCODE = 0x72;
+ public static final String NAME = "InvokeInterface";
+ public static final String SMALI_NAME = "invoke-interface";
+
+ InvokeInterface(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getMethodMap());
+ }
+
+ public InvokeInterface(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+ super(A, BBBB, C, D, E, F, G);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInvokeInterface(getMethod());
+ }
+
+ @Override
+ public DexMethod getMethod() {
+ return (DexMethod) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeRegisters(
+ Type.INTERFACE, getMethod(), getProto(), A, new int[] {C, D, E, F, G});
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java b/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
new file mode 100644
index 0000000..06eb69e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeInterfaceRange extends Format3rc {
+
+ public static final int OPCODE = 0x78;
+ public static final String NAME = "InvokeInterfaceRange";
+ public static final String SMALI_NAME = "invoke-interface/range";
+
+ InvokeInterfaceRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getMethodMap());
+ }
+
+ public InvokeInterfaceRange(int firstArgumentRegister, int argumentCount, DexMethod method) {
+ super(firstArgumentRegister, argumentCount, method);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInvokeInterface(getMethod());
+ }
+
+ @Override
+ public DexMethod getMethod() {
+ return (DexMethod) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeRange(Type.INTERFACE, getMethod(), getProto(), AA, CCCC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java
new file mode 100644
index 0000000..d5ecd11
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java
@@ -0,0 +1,56 @@
+// 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.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+/**
+ * An invoke-polymorphic instruction used to invoke a method in a MethodHandle using either
+ * MethodHandle.invoke or MethodHandle.invokeExact.
+ */
+public class InvokePolymorphic extends Format45cc {
+
+ public static final int OPCODE = 0xfa;
+ public static final String NAME = "InvokePolymorphic";
+ public static final String SMALI_NAME = "invoke-polymorphic";
+
+ InvokePolymorphic(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getMethodMap(), mapping.getProtosMap());
+ }
+
+ public InvokePolymorphic(
+ int A, DexMethod BBBB, DexProto HHHH, int C, int D, int E, int F, int G) {
+ super(A, BBBB, HHHH, C, D, E, F, G);
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeRegisters(
+ Type.POLYMORPHIC, getMethod(), getProto(), A, new int[] {C, D, E, F, G});
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ @Override
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java b/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java
new file mode 100644
index 0000000..9b102b2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java
@@ -0,0 +1,59 @@
+// 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.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+/** An invoke-polymorphic range instruction used to call method with polymorphic signature. */
+public class InvokePolymorphicRange extends Format4rcc {
+
+ public static final int OPCODE = 0xfb;
+ public static final String NAME = "InvokePolymorphicRange";
+ public static final String SMALI_NAME = "invoke-polymorphic/range";
+
+ InvokePolymorphicRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getMethodMap(), mapping.getProtosMap());
+ }
+
+ public InvokePolymorphicRange(
+ int firstArgumentRegister, int argumentCount, DexMethod method, DexProto proto) {
+ super(firstArgumentRegister, argumentCount, method, proto);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ public DexMethod getMethod() {
+ return (DexMethod) BBBB;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInvokeDirect(getMethod());
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeRange(Type.POLYMORPHIC, getMethod(), getProto(), AA, CCCC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/code/InvokeStatic.java
new file mode 100644
index 0000000..8fbf95c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeStatic.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeStatic extends Format35c {
+
+ public static final int OPCODE = 0x71;
+ public static final String NAME = "InvokeStatic";
+ public static final String SMALI_NAME = "invoke-static";
+
+ InvokeStatic(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getMethodMap());
+ }
+
+ public InvokeStatic(int A, DexMethod BBBB, int C, int D, int E, int F, int G) {
+ super(A, BBBB, C, D, E, F, G);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInvokeStatic(getMethod());
+ }
+
+ @Override
+ public DexMethod getMethod() {
+ return (DexMethod) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeRegisters(Type.STATIC, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java b/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
new file mode 100644
index 0000000..92a4047
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeStaticRange extends Format3rc {
+
+ public static final int OPCODE = 0x77;
+ public static final String NAME = "InvokeStaticRange";
+ public static final String SMALI_NAME = "invoke-static/range";
+
+ InvokeStaticRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getMethodMap());
+ }
+
+ public InvokeStaticRange(int firstArgumentRegister, int argumentCount, DexMethod method) {
+ super(firstArgumentRegister, argumentCount, method);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInvokeStatic(getMethod());
+ }
+
+ @Override
+ public DexMethod getMethod() {
+ return (DexMethod) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeRange(Type.STATIC, getMethod(), getProto(), AA, CCCC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/code/InvokeSuper.java
new file mode 100644
index 0000000..7a6e32a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeSuper.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeSuper extends Format35c {
+
+ public static final int OPCODE = 0x6f;
+ public static final String NAME = "InvokeSuper";
+ public static final String SMALI_NAME = "invoke-super";
+
+ InvokeSuper(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getMethodMap());
+ }
+
+ public InvokeSuper(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+ super(A, BBBB, C, D, E, F, G);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInvokeSuper(getMethod());
+ }
+
+ @Override
+ public DexMethod getMethod() {
+ return (DexMethod) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeRegisters(Type.SUPER, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java b/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
new file mode 100644
index 0000000..9ee05b7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeSuperRange extends Format3rc {
+
+ public static final int OPCODE = 0x75;
+ public static final String NAME = "InvokeSuperRange";
+ public static final String SMALI_NAME = "invoke-super/range";
+
+ InvokeSuperRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getMethodMap());
+ }
+
+ public InvokeSuperRange(int firstArgumentRegister, int argumentCount, DexMethod method) {
+ super(firstArgumentRegister, argumentCount, method);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInvokeSuper(getMethod());
+ }
+
+ @Override
+ public DexMethod getMethod() {
+ return (DexMethod) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeRange(Type.SUPER, getMethod(), getProto(), AA, CCCC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
new file mode 100644
index 0000000..1fbf9d9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeVirtual extends Format35c {
+
+ public static final int OPCODE = 0x6e;
+ public static final String NAME = "InvokeVirtual";
+ public static final String SMALI_NAME = "invoke-virtual";
+
+ InvokeVirtual(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getMethodMap());
+ }
+
+ public InvokeVirtual(int A, DexMethod BBBB, int C, int D, int E, int F, int G) {
+ super(A, BBBB, C, D, E, F, G);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInvokeVirtual(getMethod());
+ }
+
+ @Override
+ public DexMethod getMethod() {
+ return (DexMethod) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeRegisters(Type.VIRTUAL, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java b/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
new file mode 100644
index 0000000..c5c8249
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeVirtualRange extends Format3rc {
+
+ public static final int OPCODE = 0x74;
+ public static final String NAME = "InvokeVirtualRange";
+ public static final String SMALI_NAME = "invoke-virtual/range";
+
+ InvokeVirtualRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getMethodMap());
+ }
+
+ public InvokeVirtualRange(int firstArgumentRegister, int argumentCount, DexMethod method) {
+ super(firstArgumentRegister, argumentCount, method);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInvokeVirtual(getMethod());
+ }
+
+ @Override
+ public DexMethod getMethod() {
+ return (DexMethod) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInvokeRange(Type.VIRTUAL, getMethod(), getProto(), AA, CCCC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Iput.java b/src/main/java/com/android/tools/r8/code/Iput.java
new file mode 100644
index 0000000..87dc47e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Iput.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Iput extends Format22c {
+
+ public static final int OPCODE = 0x59;
+ public static final String NAME = "Iput";
+ public static final String SMALI_NAME = "iput";
+
+ /*package*/ Iput(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public Iput(int valueRegister, int objectRegister, DexField field) {
+ super(valueRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstancePut(MemberType.SINGLE, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IputBoolean.java b/src/main/java/com/android/tools/r8/code/IputBoolean.java
new file mode 100644
index 0000000..6bc56fb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IputBoolean.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IputBoolean extends Format22c {
+
+ public static final int OPCODE = 0x5c;
+ public static final String NAME = "IputBoolean";
+ public static final String SMALI_NAME = "iput-boolean";
+
+ /*package*/ IputBoolean(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public IputBoolean(int valueRegister, int objectRegister, DexField field) {
+ super(valueRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstancePut(MemberType.BOOLEAN, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IputByte.java b/src/main/java/com/android/tools/r8/code/IputByte.java
new file mode 100644
index 0000000..527e11d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IputByte.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IputByte extends Format22c {
+
+ public static final int OPCODE = 0x5d;
+ public static final String NAME = "IputByte";
+ public static final String SMALI_NAME = "iput-byte";
+
+ /*package*/ IputByte(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public IputByte(int valueRegister, int objectRegister, DexField field) {
+ super(valueRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstancePut(MemberType.BYTE, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IputChar.java b/src/main/java/com/android/tools/r8/code/IputChar.java
new file mode 100644
index 0000000..7010b3a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IputChar.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IputChar extends Format22c {
+
+ public static final int OPCODE = 0x5e;
+ public static final String NAME = "IputChar";
+ public static final String SMALI_NAME = "iput-char";
+
+ /*package*/ IputChar(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public IputChar(int valueRegister, int objectRegister, DexField field) {
+ super(valueRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstancePut(MemberType.CHAR, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IputObject.java b/src/main/java/com/android/tools/r8/code/IputObject.java
new file mode 100644
index 0000000..7e1eb17
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IputObject.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IputObject extends Format22c {
+
+ public static final int OPCODE = 0x5b;
+ public static final String NAME = "IputObject";
+ public static final String SMALI_NAME = "iput-object";
+
+ /*package*/ IputObject(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public IputObject(int valueRegister, int objectRegister, DexField field) {
+ super(valueRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstancePut(MemberType.OBJECT, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IputShort.java b/src/main/java/com/android/tools/r8/code/IputShort.java
new file mode 100644
index 0000000..0f40bb0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IputShort.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IputShort extends Format22c {
+
+ public static final int OPCODE = 0x5f;
+ public static final String NAME = "IputShort";
+ public static final String SMALI_NAME = "iput-short";
+
+ /*package*/ IputShort(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public IputShort(int valueRegister, int objectRegister, DexField field) {
+ super(valueRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstancePut(MemberType.SHORT, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IputWide.java b/src/main/java/com/android/tools/r8/code/IputWide.java
new file mode 100644
index 0000000..d845e1b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IputWide.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IputWide extends Format22c {
+
+ public static final int OPCODE = 0x5a;
+ public static final String NAME = "IputWide";
+ public static final String SMALI_NAME = "iput-wide";
+
+ /*package*/ IputWide(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public IputWide(int valueRegister, int objectRegister, DexField field) {
+ super(valueRegister, objectRegister, field);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInstanceFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) CCCC;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInstancePut(MemberType.WIDE, A, B, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/LongToDouble.java b/src/main/java/com/android/tools/r8/code/LongToDouble.java
new file mode 100644
index 0000000..5b72828
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/LongToDouble.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class LongToDouble extends Format12x {
+
+ public static final int OPCODE = 0x86;
+ public static final String NAME = "LongToDouble";
+ public static final String SMALI_NAME = "long-to-double";
+
+ LongToDouble(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public LongToDouble(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.DOUBLE, NumericType.LONG, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/LongToFloat.java b/src/main/java/com/android/tools/r8/code/LongToFloat.java
new file mode 100644
index 0000000..db01018
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/LongToFloat.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class LongToFloat extends Format12x {
+
+ public static final int OPCODE = 0x85;
+ public static final String NAME = "LongToFloat";
+ public static final String SMALI_NAME = "long-to-float";
+
+ LongToFloat(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public LongToFloat(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.FLOAT, NumericType.LONG, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/LongToInt.java b/src/main/java/com/android/tools/r8/code/LongToInt.java
new file mode 100644
index 0000000..89219c8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/LongToInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class LongToInt extends Format12x {
+
+ public static final int OPCODE = 0x84;
+ public static final String NAME = "LongToInt";
+ public static final String SMALI_NAME = "long-to-int";
+
+ LongToInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public LongToInt(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addConversion(NumericType.INT, NumericType.LONG, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MonitorEnter.java b/src/main/java/com/android/tools/r8/code/MonitorEnter.java
new file mode 100644
index 0000000..b6ebbff
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MonitorEnter.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Monitor.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MonitorEnter extends Format11x {
+
+ public static final int OPCODE = 0x1d;
+ public static final String NAME = "MonitorEnter";
+ public static final String SMALI_NAME = "monitor-enter";
+
+ MonitorEnter(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MonitorEnter(int register) {
+ super(register);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMonitor(Type.ENTER, AA);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MonitorExit.java b/src/main/java/com/android/tools/r8/code/MonitorExit.java
new file mode 100644
index 0000000..bad666f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MonitorExit.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Monitor.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MonitorExit extends Format11x {
+
+ public static final int OPCODE = 0x1e;
+ public static final String NAME = "MonitorExit";
+ public static final String SMALI_NAME = "monitor-exit";
+
+ MonitorExit(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MonitorExit(int register) {
+ super(register);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMonitor(Type.EXIT, AA);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Move.java b/src/main/java/com/android/tools/r8/code/Move.java
new file mode 100644
index 0000000..01eeada
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Move.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Move extends Format12x {
+
+ public static final int OPCODE = 0x1;
+ public static final String NAME = "Move";
+ public static final String SMALI_NAME = "move";
+
+ Move(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public Move(int dest, int src) {
+ super(dest, src);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMove(MoveType.SINGLE, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Move16.java b/src/main/java/com/android/tools/r8/code/Move16.java
new file mode 100644
index 0000000..cfa4349
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Move16.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class Move16 extends Format32x {
+
+ public static final int OPCODE = 0x3;
+ public static final String NAME = "Move16";
+ public static final String SMALI_NAME = "move/16";
+
+ Move16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public Move16(int dest, int src) {
+ super(dest, src);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMove(MoveType.SINGLE, AAAA, BBBB);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveException.java b/src/main/java/com/android/tools/r8/code/MoveException.java
new file mode 100644
index 0000000..8b66c40
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveException.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MoveException extends Format11x {
+
+ public static final int OPCODE = 0xd;
+ public static final String NAME = "MoveException";
+ public static final String SMALI_NAME = "move-exception";
+
+ MoveException(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MoveException(int AA) {
+ super(AA);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMoveException(AA);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveFrom16.java b/src/main/java/com/android/tools/r8/code/MoveFrom16.java
new file mode 100644
index 0000000..785babe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveFrom16.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MoveFrom16 extends Format22x {
+
+ public static final int OPCODE = 0x2;
+ public static final String NAME = "MoveFrom16";
+ public static final String SMALI_NAME = "move-from/16";
+
+ MoveFrom16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MoveFrom16(int dest, int src) {
+ super(dest, src);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMove(MoveType.SINGLE, AA, BBBB);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveObject.java b/src/main/java/com/android/tools/r8/code/MoveObject.java
new file mode 100644
index 0000000..a409025
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveObject.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MoveObject extends Format12x {
+
+ public static final int OPCODE = 0x7;
+ public static final String NAME = "MoveObject";
+ public static final String SMALI_NAME = "move-object";
+
+ MoveObject(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MoveObject(int dest, int src) {
+ super(dest, src);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMove(MoveType.OBJECT, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveObject16.java b/src/main/java/com/android/tools/r8/code/MoveObject16.java
new file mode 100644
index 0000000..a1857bb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveObject16.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MoveObject16 extends Format32x {
+
+ public static final int OPCODE = 0x9;
+ public static final String NAME = "MoveObject16";
+ public static final String SMALI_NAME = "move-object/16";
+
+ MoveObject16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MoveObject16(int dest, int src) {
+ super(dest, src);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMove(MoveType.OBJECT, AAAA, BBBB);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveObjectFrom16.java b/src/main/java/com/android/tools/r8/code/MoveObjectFrom16.java
new file mode 100644
index 0000000..94c3101
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveObjectFrom16.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MoveObjectFrom16 extends Format22x {
+
+ public static final int OPCODE = 0x8;
+ public static final String NAME = "MoveObjectFrom16";
+ public static final String SMALI_NAME = "move-object-from/16";
+
+ MoveObjectFrom16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MoveObjectFrom16(int dest, int src) {
+ super(dest, src);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMove(MoveType.OBJECT, AA, BBBB);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveResult.java b/src/main/java/com/android/tools/r8/code/MoveResult.java
new file mode 100644
index 0000000..98469f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveResult.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MoveResult extends Format11x {
+
+ public static final int OPCODE = 0xa;
+ public static final String NAME = "MoveResult";
+ public static final String SMALI_NAME = "move-result";
+
+ /*package*/ MoveResult(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MoveResult(int AA) {
+ super(AA);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMoveResult(MoveType.SINGLE, AA);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveResultObject.java b/src/main/java/com/android/tools/r8/code/MoveResultObject.java
new file mode 100644
index 0000000..6043f78
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveResultObject.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MoveResultObject extends Format11x {
+
+ public static final int OPCODE = 0xc;
+ public static final String NAME = "MoveResultObject";
+ public static final String SMALI_NAME = "move-result-object";
+
+ /*package*/ MoveResultObject(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MoveResultObject(int AA) {
+ super(AA);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMoveResult(MoveType.OBJECT, AA);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveResultWide.java b/src/main/java/com/android/tools/r8/code/MoveResultWide.java
new file mode 100644
index 0000000..44f5016
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveResultWide.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MoveResultWide extends Format11x {
+
+ public static final int OPCODE = 0xb;
+ public static final String NAME = "MoveResultWide";
+ public static final String SMALI_NAME = "move-result-wide";
+
+ /*package*/ MoveResultWide(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MoveResultWide(int AA) {
+ super(AA);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMoveResult(MoveType.WIDE, AA);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveWide.java b/src/main/java/com/android/tools/r8/code/MoveWide.java
new file mode 100644
index 0000000..12c4ea7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveWide.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MoveWide extends Format12x {
+
+ public static final int OPCODE = 0x4;
+ public static final String NAME = "MoveWide";
+ public static final String SMALI_NAME = "move-wide";
+
+ MoveWide(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MoveWide(int dest, int src) {
+ super(dest, src);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMove(MoveType.WIDE, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveWide16.java b/src/main/java/com/android/tools/r8/code/MoveWide16.java
new file mode 100644
index 0000000..708332d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveWide16.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MoveWide16 extends Format32x {
+
+ public static final int OPCODE = 0x6;
+ public static final String NAME = "MoveWide16";
+ public static final String SMALI_NAME = "move-wide/16";
+
+ MoveWide16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MoveWide16(int dest, int src) {
+ super(dest, src);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMove(MoveType.WIDE, AAAA, BBBB);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveWideFrom16.java b/src/main/java/com/android/tools/r8/code/MoveWideFrom16.java
new file mode 100644
index 0000000..0236e17
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveWideFrom16.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MoveWideFrom16 extends Format22x {
+
+ public static final int OPCODE = 0x5;
+ public static final String NAME = "MoveWideFrom16";
+ public static final String SMALI_NAME = "move-wide-from/16";
+
+ MoveWideFrom16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MoveWideFrom16(int dest, int src) {
+ super(dest, src);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMove(MoveType.WIDE, AA, BBBB);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulDouble.java b/src/main/java/com/android/tools/r8/code/MulDouble.java
new file mode 100644
index 0000000..2d63172
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulDouble.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulDouble extends Format23x {
+
+ public static final int OPCODE = 0xad;
+ public static final String NAME = "MulDouble";
+ public static final String SMALI_NAME = "mul-double";
+
+ MulDouble(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MulDouble(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMul(NumericType.DOUBLE, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulDouble2Addr.java b/src/main/java/com/android/tools/r8/code/MulDouble2Addr.java
new file mode 100644
index 0000000..523bdd3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulDouble2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulDouble2Addr extends Format12x {
+
+ public static final int OPCODE = 0xcd;
+ public static final String NAME = "MulDouble2Addr";
+ public static final String SMALI_NAME = "mul-double/2addr";
+
+ MulDouble2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MulDouble2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMul(NumericType.DOUBLE, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulFloat.java b/src/main/java/com/android/tools/r8/code/MulFloat.java
new file mode 100644
index 0000000..bb10eaf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulFloat.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulFloat extends Format23x {
+
+ public static final int OPCODE = 0xa8;
+ public static final String NAME = "MulFloat";
+ public static final String SMALI_NAME = "mul-float";
+
+ MulFloat(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MulFloat(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMul(NumericType.FLOAT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulFloat2Addr.java b/src/main/java/com/android/tools/r8/code/MulFloat2Addr.java
new file mode 100644
index 0000000..3a50b77
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulFloat2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulFloat2Addr extends Format12x {
+
+ public static final int OPCODE = 0xc8;
+ public static final String NAME = "MulFloat2Addr";
+ public static final String SMALI_NAME = "mul-float/2addr";
+
+ MulFloat2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MulFloat2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMul(NumericType.FLOAT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulInt.java b/src/main/java/com/android/tools/r8/code/MulInt.java
new file mode 100644
index 0000000..cb24a89
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulInt.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MulInt extends Format23x {
+
+ public static final int OPCODE = 0x92;
+ public static final String NAME = "MulInt";
+ public static final String SMALI_NAME = "mul-int";
+
+ MulInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MulInt(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMul(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulInt2Addr.java b/src/main/java/com/android/tools/r8/code/MulInt2Addr.java
new file mode 100644
index 0000000..50fa865
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulInt2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MulInt2Addr extends Format12x {
+
+ public static final int OPCODE = 0xb2;
+ public static final String NAME = "MulInt2Addr";
+ public static final String SMALI_NAME = "mul-int/2addr";
+
+ MulInt2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MulInt2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMul(NumericType.INT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulIntLit16.java b/src/main/java/com/android/tools/r8/code/MulIntLit16.java
new file mode 100644
index 0000000..0188ba7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulIntLit16.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulIntLit16 extends Format22s {
+
+ public static final int OPCODE = 0xd2;
+ public static final String NAME = "MulIntLit16";
+ public static final String SMALI_NAME = "mul-int/lit16";
+
+ MulIntLit16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MulIntLit16(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMulLiteral(NumericType.INT, A, B, CCCC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulIntLit8.java b/src/main/java/com/android/tools/r8/code/MulIntLit8.java
new file mode 100644
index 0000000..fde33be
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulIntLit8.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulIntLit8 extends Format22b {
+
+ public static final int OPCODE = 0xda;
+ public static final String NAME = "MulIntLit8";
+ public static final String SMALI_NAME = "mul-int/lit8";
+
+ MulIntLit8(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MulIntLit8(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMulLiteral(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulLong.java b/src/main/java/com/android/tools/r8/code/MulLong.java
new file mode 100644
index 0000000..487f786
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulLong extends Format23x {
+
+ public static final int OPCODE = 0x9d;
+ public static final String NAME = "MulLong";
+ public static final String SMALI_NAME = "mul-long";
+
+ MulLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MulLong(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMul(NumericType.LONG, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulLong2Addr.java b/src/main/java/com/android/tools/r8/code/MulLong2Addr.java
new file mode 100644
index 0000000..ffc7bfa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulLong2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulLong2Addr extends Format12x {
+
+ public static final int OPCODE = 0xbd;
+ public static final String NAME = "MulLong2Addr";
+ public static final String SMALI_NAME = "mul-long/2addr";
+
+ MulLong2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public MulLong2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addMul(NumericType.LONG, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NegDouble.java b/src/main/java/com/android/tools/r8/code/NegDouble.java
new file mode 100644
index 0000000..4fda3d2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NegDouble.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class NegDouble extends Format12x {
+
+ public static final int OPCODE = 0x80;
+ public static final String NAME = "NegDouble";
+ public static final String SMALI_NAME = "neg-double";
+
+ /*package*/ NegDouble(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public NegDouble(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addNeg(NumericType.DOUBLE, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NegFloat.java b/src/main/java/com/android/tools/r8/code/NegFloat.java
new file mode 100644
index 0000000..c0618f7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NegFloat.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class NegFloat extends Format12x {
+
+ public static final int OPCODE = 0x7f;
+ public static final String NAME = "NegFloat";
+ public static final String SMALI_NAME = "neg-float";
+
+ /*package*/ NegFloat(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public NegFloat(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addNeg(NumericType.FLOAT, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NegInt.java b/src/main/java/com/android/tools/r8/code/NegInt.java
new file mode 100644
index 0000000..92e5cc4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NegInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class NegInt extends Format12x {
+
+ public static final int OPCODE = 0x7b;
+ public static final String NAME = "NegInt";
+ public static final String SMALI_NAME = "neg-int";
+
+ /*package*/ NegInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public NegInt(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addNeg(NumericType.INT, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NegLong.java b/src/main/java/com/android/tools/r8/code/NegLong.java
new file mode 100644
index 0000000..8c020ae
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NegLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class NegLong extends Format12x {
+
+ public static final int OPCODE = 0x7d;
+ public static final String NAME = "NegLong";
+ public static final String SMALI_NAME = "neg-long";
+
+ /*package*/ NegLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public NegLong(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addNeg(NumericType.LONG, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NewArray.java b/src/main/java/com/android/tools/r8/code/NewArray.java
new file mode 100644
index 0000000..4dd5361
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NewArray.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class NewArray extends Format22c {
+
+ public static final int OPCODE = 0x23;
+ public static final String NAME = "NewArray";
+ public static final String SMALI_NAME = "new-array";
+
+ /*package*/ NewArray(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getTypeMap());
+ }
+
+ public NewArray(int dest, int size, DexType type) {
+ super(dest, size, type);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerTypeReference(getType());
+ }
+
+ public DexType getType() {
+ return (DexType) CCCC;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addNewArrayEmpty(A, B, getType());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NewInstance.java b/src/main/java/com/android/tools/r8/code/NewInstance.java
new file mode 100644
index 0000000..1592d08
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NewInstance.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class NewInstance extends Format21c {
+
+ public static final int OPCODE = 0x22;
+ public static final String NAME = "NewInstance";
+ public static final String SMALI_NAME = "new-instance";
+
+ NewInstance(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getTypeMap());
+ }
+
+ public NewInstance(int AA, DexType BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerNewInstance(getType());
+ }
+
+ public DexType getType() {
+ return (DexType) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addNewInstance(AA, getType());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Nop.java b/src/main/java/com/android/tools/r8/code/Nop.java
new file mode 100644
index 0000000..4a0bcdc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Nop.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Nop extends Format10x {
+
+ public static final int OPCODE = 0x0;
+ public static final String NAME = "Nop";
+ public static final String SMALI_NAME = "nop";
+
+ Nop(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public Nop() {
+ }
+
+ public static Nop create(int high, BytecodeStream stream) {
+ switch (high) {
+ case 0x01:
+ return new PackedSwitchPayload(high, stream);
+ case 0x02:
+ return new SparseSwitchPayload(high, stream);
+ case 0x03:
+ return new FillArrayDataPayload(high, stream);
+ default:
+ return new Nop(high, stream);
+ }
+ }
+
+ public int hashCode() {
+ return NAME.hashCode() * 7 + super.hashCode();
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ // The IR does not represent nops.
+ // Nops needed by dex, eg, for tight infinite loops, will be created upon conversion to dex.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NotInt.java b/src/main/java/com/android/tools/r8/code/NotInt.java
new file mode 100644
index 0000000..16940ca
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NotInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class NotInt extends Format12x {
+
+ public static final int OPCODE = 0x7c;
+ public static final String NAME = "NotInt";
+ public static final String SMALI_NAME = "not-int";
+
+ NotInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public NotInt(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addNot(NumericType.INT, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NotLong.java b/src/main/java/com/android/tools/r8/code/NotLong.java
new file mode 100644
index 0000000..c0efa5b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NotLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class NotLong extends Format12x {
+
+ public static final int OPCODE = 0x7e;
+ public static final String NAME = "NotLong";
+ public static final String SMALI_NAME = "not-long";
+
+ NotLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public NotLong(int dest, int source) {
+ super(dest, source);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addNot(NumericType.LONG, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/OrInt.java b/src/main/java/com/android/tools/r8/code/OrInt.java
new file mode 100644
index 0000000..4d474d3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/OrInt.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class OrInt extends Format23x {
+
+ public static final int OPCODE = 0x96;
+ public static final String NAME = "OrInt";
+ public static final String SMALI_NAME = "or-int";
+
+ OrInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public OrInt(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addOr(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/OrInt2Addr.java b/src/main/java/com/android/tools/r8/code/OrInt2Addr.java
new file mode 100644
index 0000000..ac2f6d0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/OrInt2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class OrInt2Addr extends Format12x {
+
+ public static final int OPCODE = 0xb6;
+ public static final String NAME = "OrInt2Addr";
+ public static final String SMALI_NAME = "or-int/2addr";
+
+ OrInt2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public OrInt2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addOr(NumericType.INT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/OrIntLit16.java b/src/main/java/com/android/tools/r8/code/OrIntLit16.java
new file mode 100644
index 0000000..d4befd5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/OrIntLit16.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class OrIntLit16 extends Format22s {
+
+ public static final int OPCODE = 0xd6;
+ public static final String NAME = "OrIntLit16";
+ public static final String SMALI_NAME = "or-int/lit16";
+
+ OrIntLit16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public OrIntLit16(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addOrLiteral(NumericType.INT, A, B, CCCC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/OrIntLit8.java b/src/main/java/com/android/tools/r8/code/OrIntLit8.java
new file mode 100644
index 0000000..e4fc7e6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/OrIntLit8.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class OrIntLit8 extends Format22b {
+
+ public static final int OPCODE = 0xde;
+ public static final String NAME = "OrIntLit8";
+ public static final String SMALI_NAME = "or-int/lit8";
+
+ OrIntLit8(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public OrIntLit8(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addOrLiteral(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/OrLong.java b/src/main/java/com/android/tools/r8/code/OrLong.java
new file mode 100644
index 0000000..ebcc386
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/OrLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class OrLong extends Format23x {
+
+ public static final int OPCODE = 0xA1;
+ public static final String NAME = "OrLong";
+ public static final String SMALI_NAME = "or-long";
+
+ OrLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public OrLong(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addOr(NumericType.LONG, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/OrLong2Addr.java b/src/main/java/com/android/tools/r8/code/OrLong2Addr.java
new file mode 100644
index 0000000..c49898b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/OrLong2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class OrLong2Addr extends Format12x {
+
+ public static final int OPCODE = 0xc1;
+ public static final String NAME = "OrLong2Addr";
+ public static final String SMALI_NAME = "or-long/2addr";
+
+ OrLong2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public OrLong2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addOr(NumericType.LONG, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/PackedSwitch.java b/src/main/java/com/android/tools/r8/code/PackedSwitch.java
new file mode 100644
index 0000000..051da72
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/PackedSwitch.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Switch.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+
+public class PackedSwitch extends Format31t {
+
+ public static final int OPCODE = 0x2b;
+ public static final String NAME = "PackedSwitch";
+ public static final String SMALI_NAME = "packed-switch";
+
+ PackedSwitch(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public PackedSwitch(int valueRegister) {
+ super(valueRegister, -1);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public boolean isSwitch() {
+ return true;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ int offset = getOffset();
+ int payloadOffset = offset + getPayloadOffset();
+ int fallthroughOffset = offset + getSize();
+ builder.resolveAndBuildSwitch(Type.PACKED, AA, fallthroughOffset, payloadOffset);
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", :label_" + (getOffset() + BBBBBBBB));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java b/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java
new file mode 100644
index 0000000..25a5dcb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+import java.util.Arrays;
+
+public class PackedSwitchPayload extends SwitchPayload {
+
+ public final int size;
+ public final int first_key;
+ public final /* offset */ int[] targets;
+
+ public PackedSwitchPayload(int high, BytecodeStream stream) {
+ super(high, stream);
+ size = read16BitValue(stream);
+ first_key = readSigned32BitValue(stream);
+ targets = new int[size];
+ for (int i = 0; i < size; i++) {
+ targets[i] = readSigned32BitValue(stream);
+ }
+ }
+
+ public PackedSwitchPayload(int size, int first_key, int[] targets) {
+ assert size > 0; // Empty switches should be eliminated.
+ this.size = size;
+ this.first_key = first_key;
+ this.targets = targets;
+ }
+
+ public boolean isPayload() {
+ return true;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(1, dest); // Pseudo-opcode = 0x0100
+ write16BitValue(size, dest);
+ write32BitValue(first_key, dest);
+ for (int i = 0; i < size; i++) {
+ write32BitValue(targets[i], dest);
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!super.equals(other)) {
+ return false;
+ }
+ PackedSwitchPayload that = (PackedSwitchPayload) other;
+ return size == that.size && first_key == that.first_key && Arrays.equals(targets, that.targets);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + size;
+ result = 31 * result + first_key;
+ result = 31 * result + Arrays.hashCode(targets);
+ return result;
+ }
+
+ public int getSize() {
+ return 4 + (2 * targets.length);
+ }
+
+ public static int getSizeForTargets(int targets) {
+ return 4 + (2 * targets);
+ }
+
+ @Override
+ public int numberOfKeys() {
+ return size;
+ }
+
+ @Override
+ public int[] switchTargetOffsets() {
+ return targets;
+ }
+
+ @Override
+ public int[] keys() {
+ return new int[]{first_key};
+ }
+
+ public String toString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder("[PackedSwitchPayload]\n");
+ for (int i = 0; i < size; i++) {
+ StringUtils.appendLeftPadded(builder, (first_key + i) + " -> " + targets[i] + "\n", 20);
+ }
+ return super.toString(naming) + builder.toString();
+ }
+
+ public String toSmaliString(Instruction payloadUser) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(" ");
+ builder.append(".packed-switch ");
+ builder.append("0x");
+ builder.append(StringUtils.hexString(first_key, 8));
+ builder.append(" # ");
+ builder.append(first_key);
+ builder.append("\n");
+ for (int target : targets) {
+ builder.append(" :label_");
+ builder.append(payloadUser.getOffset() + target);
+ builder.append("\n");
+ }
+ builder.append(" ");
+ builder.append(".end packed-switch");
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemDouble.java b/src/main/java/com/android/tools/r8/code/RemDouble.java
new file mode 100644
index 0000000..00ec16b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemDouble.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RemDouble extends Format23x {
+
+ public static final int OPCODE = 0xaf;
+ public static final String NAME = "RemDouble";
+ public static final String SMALI_NAME = "rem-double";
+
+ RemDouble(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public RemDouble(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addRem(NumericType.DOUBLE, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemDouble2Addr.java b/src/main/java/com/android/tools/r8/code/RemDouble2Addr.java
new file mode 100644
index 0000000..6381b5d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemDouble2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class RemDouble2Addr extends Format12x {
+
+ public static final int OPCODE = 0xcf;
+ public static final String NAME = "RemDouble2Addr";
+ public static final String SMALI_NAME = "rem-double/2addr";
+
+ RemDouble2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public RemDouble2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addRem(NumericType.DOUBLE, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemFloat.java b/src/main/java/com/android/tools/r8/code/RemFloat.java
new file mode 100644
index 0000000..77031f5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemFloat.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class RemFloat extends Format23x {
+
+ public static final int OPCODE = 0xaA;
+ public static final String NAME = "RemFloat";
+ public static final String SMALI_NAME = "rem-float";
+
+ RemFloat(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public RemFloat(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addRem(NumericType.FLOAT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemFloat2Addr.java b/src/main/java/com/android/tools/r8/code/RemFloat2Addr.java
new file mode 100644
index 0000000..415c5c8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemFloat2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class RemFloat2Addr extends Format12x {
+
+ public static final int OPCODE = 0xca;
+ public static final String NAME = "RemFloat2Addr";
+ public static final String SMALI_NAME = "rem-float/2addr";
+
+ RemFloat2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public RemFloat2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addRem(NumericType.FLOAT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemInt.java b/src/main/java/com/android/tools/r8/code/RemInt.java
new file mode 100644
index 0000000..c045c80
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemInt.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RemInt extends Format23x {
+
+ public static final int OPCODE = 0x94;
+ public static final String NAME = "RemInt";
+ public static final String SMALI_NAME = "rem-int";
+
+ RemInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public RemInt(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addRem(NumericType.INT, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemInt2Addr.java b/src/main/java/com/android/tools/r8/code/RemInt2Addr.java
new file mode 100644
index 0000000..0bab68b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemInt2Addr.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RemInt2Addr extends Format12x {
+
+ public static final int OPCODE = 0xb4;
+ public static final String NAME = "RemInt2Addr";
+ public static final String SMALI_NAME = "rem-int/2addr";
+
+ RemInt2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public RemInt2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addRem(NumericType.INT, A, A, B);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemIntLit16.java b/src/main/java/com/android/tools/r8/code/RemIntLit16.java
new file mode 100644
index 0000000..c2133d3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemIntLit16.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RemIntLit16 extends Format22s {
+
+ public static final int OPCODE = 0xd4;
+ public static final String NAME = "RemIntLit16";
+ public static final String SMALI_NAME = "rem-int/lit16";
+
+ RemIntLit16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public RemIntLit16(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addRemLiteral(NumericType.INT, A, B, CCCC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemIntLit8.java b/src/main/java/com/android/tools/r8/code/RemIntLit8.java
new file mode 100644
index 0000000..bc11aee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemIntLit8.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class RemIntLit8 extends Format22b {
+
+ public static final int OPCODE = 0xdc;
+ public static final String NAME = "RemIntLit8";
+ public static final String SMALI_NAME = "rem-int/lit8";
+
+ RemIntLit8(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public RemIntLit8(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addRemLiteral(NumericType.INT, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemLong.java b/src/main/java/com/android/tools/r8/code/RemLong.java
new file mode 100644
index 0000000..715a15c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemLong.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RemLong extends Format23x {
+
+ public static final int OPCODE = 0x9f;
+ public static final String NAME = "RemLong";
+ public static final String SMALI_NAME = "rem-long";
+
+ RemLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public RemLong(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addRem(NumericType.LONG, AA, BB, CC);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemLong2Addr.java b/src/main/java/com/android/tools/r8/code/RemLong2Addr.java
new file mode 100644
index 0000000..c37b099
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemLong2Addr.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RemLong2Addr extends Format12x {
+
+ public static final int OPCODE = 0xbf;
+ public static final String NAME = "RemLong2Addr";
+ public static final String SMALI_NAME = "rem-long/2addr";
+
+ RemLong2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public RemLong2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addRem(NumericType.LONG, A, A, B);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Return.java b/src/main/java/com/android/tools/r8/code/Return.java
new file mode 100644
index 0000000..7e61ef3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Return.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Return extends Format11x {
+
+ public static final int OPCODE = 0xf;
+ public static final String NAME = "Return";
+ public static final String SMALI_NAME = "return";
+
+ Return(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public Return(int AA) {
+ super(AA);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public int[] getTargets() {
+ return EXIT_TARGET;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addReturn(MoveType.SINGLE, AA);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ReturnObject.java b/src/main/java/com/android/tools/r8/code/ReturnObject.java
new file mode 100644
index 0000000..67a59fd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ReturnObject.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ReturnObject extends Format11x {
+
+ public static final int OPCODE = 0x11;
+ public static final String NAME = "ReturnObject";
+ public static final String SMALI_NAME = "return-object";
+
+ ReturnObject(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ReturnObject(int AA) {
+ super(AA);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public int[] getTargets() {
+ return EXIT_TARGET;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addReturn(MoveType.OBJECT, AA);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ReturnVoid.java b/src/main/java/com/android/tools/r8/code/ReturnVoid.java
new file mode 100644
index 0000000..07ca0cd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ReturnVoid.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ReturnVoid extends Format10x {
+
+ public static final int OPCODE = 0xe;
+ public static final String NAME = "ReturnVoid";
+ public static final String SMALI_NAME = "return-void";
+
+ ReturnVoid(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ReturnVoid() {}
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public int[] getTargets() {
+ return EXIT_TARGET;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addReturn();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ReturnWide.java b/src/main/java/com/android/tools/r8/code/ReturnWide.java
new file mode 100644
index 0000000..22a8efa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ReturnWide.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ReturnWide extends Format11x {
+
+ public static final int OPCODE = 0x10;
+ public static final String NAME = "ReturnWide";
+ public static final String SMALI_NAME = "return-wide";
+
+ ReturnWide(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ReturnWide(int AA) {
+ super(AA);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public int[] getTargets() {
+ return EXIT_TARGET;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addReturn(MoveType.WIDE, AA);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/code/RsubInt.java b/src/main/java/com/android/tools/r8/code/RsubInt.java
new file mode 100644
index 0000000..93fe424
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RsubInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RsubInt extends Format22s {
+
+ public static final int OPCODE = 0xd1;
+ public static final String NAME = "RsubInt";
+ public static final String SMALI_NAME = "rsub-int";
+
+ RsubInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public RsubInt(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addRsubLiteral(NumericType.INT, A, B, CCCC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RsubIntLit8.java b/src/main/java/com/android/tools/r8/code/RsubIntLit8.java
new file mode 100644
index 0000000..a29c878
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RsubIntLit8.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class RsubIntLit8 extends Format22b {
+
+ public static final int OPCODE = 0xd9;
+ public static final String NAME = "RsubIntLit8";
+ public static final String SMALI_NAME = "rsub-int/lit8";
+
+ RsubIntLit8(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public RsubIntLit8(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addRsubLiteral(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Sget.java b/src/main/java/com/android/tools/r8/code/Sget.java
new file mode 100644
index 0000000..3d068ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Sget.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Sget extends Format21c {
+
+ public static final int OPCODE = 0x60;
+ public static final String NAME = "Sget";
+ public static final String SMALI_NAME = "sget";
+
+ Sget(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public Sget(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldRead(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticGet(MemberType.SINGLE, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SgetBoolean.java b/src/main/java/com/android/tools/r8/code/SgetBoolean.java
new file mode 100644
index 0000000..5e6b249
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SgetBoolean.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SgetBoolean extends Format21c {
+
+ public static final int OPCODE = 0x63;
+ public static final String NAME = "SgetBoolean";
+ public static final String SMALI_NAME = "sget-boolean";
+
+ SgetBoolean(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public SgetBoolean(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldRead(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticGet(MemberType.BOOLEAN, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SgetByte.java b/src/main/java/com/android/tools/r8/code/SgetByte.java
new file mode 100644
index 0000000..d8e9ce3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SgetByte.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SgetByte extends Format21c {
+
+ public static final int OPCODE = 0x64;
+ public static final String NAME = "SgetByte";
+ public static final String SMALI_NAME = "sget-byte";
+
+ SgetByte(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public SgetByte(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldRead(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticGet(MemberType.BYTE, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SgetChar.java b/src/main/java/com/android/tools/r8/code/SgetChar.java
new file mode 100644
index 0000000..126f340
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SgetChar.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SgetChar extends Format21c {
+
+ public static final int OPCODE = 0x65;
+ public static final String NAME = "SgetChar";
+ public static final String SMALI_NAME = "sget-char";
+
+ SgetChar(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public SgetChar(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldRead(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticGet(MemberType.CHAR, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SgetObject.java b/src/main/java/com/android/tools/r8/code/SgetObject.java
new file mode 100644
index 0000000..00dfd73
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SgetObject.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SgetObject extends Format21c {
+
+ public static final int OPCODE = 0x62;
+ public static final String NAME = "SgetObject";
+ public static final String SMALI_NAME = "sget-object";
+
+ SgetObject(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public SgetObject(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldRead(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticGet(MemberType.OBJECT, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SgetShort.java b/src/main/java/com/android/tools/r8/code/SgetShort.java
new file mode 100644
index 0000000..b525858
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SgetShort.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SgetShort extends Format21c {
+
+ public static final int OPCODE = 0x66;
+ public static final String NAME = "SgetShort";
+ public static final String SMALI_NAME = "sget-short";
+
+ SgetShort(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public SgetShort(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldRead(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticGet(MemberType.SHORT, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SgetWide.java b/src/main/java/com/android/tools/r8/code/SgetWide.java
new file mode 100644
index 0000000..e82478b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SgetWide.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SgetWide extends Format21c {
+
+ public static final int OPCODE = 0x61;
+ public static final String NAME = "SgetWide";
+ public static final String SMALI_NAME = "sget-wide";
+
+ SgetWide(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public SgetWide(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldRead(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticGet(MemberType.WIDE, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShlInt.java b/src/main/java/com/android/tools/r8/code/ShlInt.java
new file mode 100644
index 0000000..67cce9c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShlInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShlInt extends Format23x {
+
+ public static final int OPCODE = 0x98;
+ public static final String NAME = "ShlInt";
+ public static final String SMALI_NAME = "shl-int";
+
+ ShlInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ShlInt(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addShl(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShlInt2Addr.java b/src/main/java/com/android/tools/r8/code/ShlInt2Addr.java
new file mode 100644
index 0000000..c136476
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShlInt2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShlInt2Addr extends Format12x {
+
+ public static final int OPCODE = 0xb8;
+ public static final String NAME = "ShlInt2Addr";
+ public static final String SMALI_NAME = "shl-int/2addr";
+
+ ShlInt2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ShlInt2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addShl(NumericType.INT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShlIntLit8.java b/src/main/java/com/android/tools/r8/code/ShlIntLit8.java
new file mode 100644
index 0000000..4b34c78
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShlIntLit8.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class ShlIntLit8 extends Format22b {
+
+ public static final int OPCODE = 0xe0;
+ public static final String NAME = "ShlIntLit8";
+ public static final String SMALI_NAME = "shl-int/lit8";
+
+ ShlIntLit8(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ShlIntLit8(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addShlLiteral(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShlLong.java b/src/main/java/com/android/tools/r8/code/ShlLong.java
new file mode 100644
index 0000000..9fb3c43
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShlLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShlLong extends Format23x {
+
+ public static final int OPCODE = 0xA3;
+ public static final String NAME = "ShlLong";
+ public static final String SMALI_NAME = "shl-long";
+
+ ShlLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ShlLong(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addShl(NumericType.LONG, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShlLong2Addr.java b/src/main/java/com/android/tools/r8/code/ShlLong2Addr.java
new file mode 100644
index 0000000..329b455
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShlLong2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShlLong2Addr extends Format12x {
+
+ public static final int OPCODE = 0xc3;
+ public static final String NAME = "ShlLong2Addr";
+ public static final String SMALI_NAME = "shl-long/2addr";
+
+ ShlLong2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ShlLong2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addShl(NumericType.LONG, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShrInt.java b/src/main/java/com/android/tools/r8/code/ShrInt.java
new file mode 100644
index 0000000..ed094c7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShrInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShrInt extends Format23x {
+
+ public static final int OPCODE = 0x99;
+ public static final String NAME = "ShrInt";
+ public static final String SMALI_NAME = "shr-int";
+
+ ShrInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ShrInt(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addShr(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShrInt2Addr.java b/src/main/java/com/android/tools/r8/code/ShrInt2Addr.java
new file mode 100644
index 0000000..66c8967
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShrInt2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShrInt2Addr extends Format12x {
+
+ public static final int OPCODE = 0xb9;
+ public static final String NAME = "ShrInt2Addr";
+ public static final String SMALI_NAME = "shr-int/2addr";
+
+ ShrInt2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ShrInt2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addShr(NumericType.INT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShrIntLit8.java b/src/main/java/com/android/tools/r8/code/ShrIntLit8.java
new file mode 100644
index 0000000..b8b53ef
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShrIntLit8.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShrIntLit8 extends Format22b {
+
+ public static final int OPCODE = 0xe1;
+ public static final String NAME = "ShrIntLit8";
+ public static final String SMALI_NAME = "shr-int/lit8";
+
+ ShrIntLit8(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ShrIntLit8(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addShrLiteral(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShrLong.java b/src/main/java/com/android/tools/r8/code/ShrLong.java
new file mode 100644
index 0000000..0827c59
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShrLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShrLong extends Format23x {
+
+ public static final int OPCODE = 0xA4;
+ public static final String NAME = "ShrLong";
+ public static final String SMALI_NAME = "shr-long";
+
+ ShrLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ShrLong(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addShr(NumericType.LONG, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShrLong2Addr.java b/src/main/java/com/android/tools/r8/code/ShrLong2Addr.java
new file mode 100644
index 0000000..d99dfd6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShrLong2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShrLong2Addr extends Format12x {
+
+ public static final int OPCODE = 0xc4;
+ public static final String NAME = "ShrLong2Addr";
+ public static final String SMALI_NAME = "shr-long/2addr";
+
+ ShrLong2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public ShrLong2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addShr(NumericType.LONG, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SparseSwitch.java b/src/main/java/com/android/tools/r8/code/SparseSwitch.java
new file mode 100644
index 0000000..8e6862a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SparseSwitch.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Switch.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+
+public class SparseSwitch extends Format31t {
+
+ public static final int OPCODE = 0x2c;
+ public static final String NAME = "SparseSwitch";
+ public static final String SMALI_NAME = "sparse-switch";
+
+ SparseSwitch(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public SparseSwitch(int value) {
+ super(value, -1);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ @Override
+ public boolean isSwitch() {
+ return true;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ int offset = getOffset();
+ int payloadOffset = offset + getPayloadOffset();
+ int fallthroughOffset = offset + getSize();
+ builder.resolveAndBuildSwitch(Type.SPARSE, AA, fallthroughOffset, payloadOffset);
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + AA + ", :label_" + (getOffset() + BBBBBBBB));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java b/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java
new file mode 100644
index 0000000..027c253b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java
@@ -0,0 +1,122 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+import java.util.Arrays;
+
+public class SparseSwitchPayload extends SwitchPayload {
+
+ public final int size;
+ public final int[] keys;
+ public final /* offset */ int[] targets;
+
+ public SparseSwitchPayload(int high, BytecodeStream stream) {
+ super(high, stream);
+ size = read16BitValue(stream);
+ keys = new int[size];
+ for (int i = 0; i < size; i++) {
+ keys[i] = readSigned32BitValue(stream);
+ }
+
+ targets = new int[size];
+ for (int i = 0; i < size; i++) {
+ targets[i] = readSigned32BitValue(stream);
+ }
+ }
+
+ public SparseSwitchPayload(int size, int[] keys, int[] targets) {
+ assert size > 0; // Empty switches should be eliminated.
+ this.size = size;
+ this.keys = keys;
+ this.targets = targets;
+ }
+
+ public boolean isPayload() {
+ return true;
+ }
+
+ public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+ writeFirst(2, dest); // Pseudo-opcode = 0x0200
+ write16BitValue(size, dest);
+ for (int i = 0; i < size; i++) {
+ write32BitValue(keys[i], dest);
+ }
+ for (int i = 0; i < size; i++) {
+ write32BitValue(targets[i], dest);
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!super.equals(other)) {
+ return false;
+ }
+ SparseSwitchPayload that = (SparseSwitchPayload) other;
+ return size == that.size && Arrays.equals(keys, that.keys) && Arrays
+ .equals(targets, that.targets);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + size;
+ result = 31 * result + Arrays.hashCode(keys);
+ result = 31 * result + Arrays.hashCode(targets);
+ return result;
+ }
+
+ public int getSize() {
+ return 2 + (2 * keys.length) + (2 * targets.length);
+ }
+
+ public static int getSizeForTargets(int targets) {
+ return 2 + (4 * targets);
+ }
+
+ @Override
+ public int numberOfKeys() {
+ return size;
+ }
+
+ @Override
+ public int[] keys() {
+ return keys;
+ }
+
+ public int[] switchTargetOffsets() {
+ return targets;
+ }
+
+ public String toString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder("[SparseSwitchPayload]\n");
+ for (int i = 0; i < size; i++) {
+ StringUtils.appendLeftPadded(builder, keys[i] + " -> " + targets[i] + "\n", 20);
+ }
+ return super.toString(naming) + builder.toString();
+ }
+
+ public String toSmaliString(Instruction payloadUser) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(" ");
+ builder.append(".sparse-switch");
+ builder.append("\n");
+ for (int i = 0; i < keys.length; i++) {
+ builder.append(" ");
+ builder.append("0x");
+ builder.append(StringUtils.hexString(keys[i], 8));
+ builder.append(" -> :label_");
+ builder.append(payloadUser.getOffset() + targets[i]);
+ builder.append(" # ");
+ builder.append(keys[i]);
+ builder.append("\n");
+ }
+ builder.append(" ");
+ builder.append(".end sparse-switch");
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Sput.java b/src/main/java/com/android/tools/r8/code/Sput.java
new file mode 100644
index 0000000..c501fe9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Sput.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Sput extends Format21c {
+
+ public static final int OPCODE = 0x67;
+ public static final String NAME = "Sput";
+ public static final String SMALI_NAME = "sput";
+
+ Sput(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public Sput(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticPut(MemberType.SINGLE, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SputBoolean.java b/src/main/java/com/android/tools/r8/code/SputBoolean.java
new file mode 100644
index 0000000..cea42d1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SputBoolean.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import static com.android.tools.r8.ir.code.MemberType.BOOLEAN;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SputBoolean extends Format21c {
+
+ public static final int OPCODE = 0x6a;
+ public static final String NAME = "SputBoolean";
+ public static final String SMALI_NAME = "sput-boolean";
+
+ /*package*/ SputBoolean(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public SputBoolean(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticPut(BOOLEAN, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SputByte.java b/src/main/java/com/android/tools/r8/code/SputByte.java
new file mode 100644
index 0000000..a60c477
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SputByte.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SputByte extends Format21c {
+
+ public static final int OPCODE = 0x6b;
+ public static final String NAME = "SputByte";
+ public static final String SMALI_NAME = "sput-byte";
+
+ /*package*/ SputByte(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public SputByte(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticPut(MemberType.BYTE, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SputChar.java b/src/main/java/com/android/tools/r8/code/SputChar.java
new file mode 100644
index 0000000..c2bf45a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SputChar.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SputChar extends Format21c {
+
+ public static final int OPCODE = 0x6c;
+ public static final String NAME = "SputChar";
+ public static final String SMALI_NAME = "sput-char";
+
+ /*package*/ SputChar(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public SputChar(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticPut(MemberType.CHAR, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SputObject.java b/src/main/java/com/android/tools/r8/code/SputObject.java
new file mode 100644
index 0000000..55bdf82
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SputObject.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SputObject extends Format21c {
+
+ public static final int OPCODE = 0x69;
+ public static final String NAME = "SputObject";
+ public static final String SMALI_NAME = "sput-object";
+
+ /*package*/ SputObject(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public SputObject(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticPut(MemberType.OBJECT, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SputShort.java b/src/main/java/com/android/tools/r8/code/SputShort.java
new file mode 100644
index 0000000..62819e2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SputShort.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SputShort extends Format21c {
+
+ public static final int OPCODE = 0x6d;
+ public static final String NAME = "SputShort";
+ public static final String SMALI_NAME = "sput-short";
+
+ /*package*/ SputShort(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public SputShort(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticPut(MemberType.SHORT, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SputWide.java b/src/main/java/com/android/tools/r8/code/SputWide.java
new file mode 100644
index 0000000..3c4f5c5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SputWide.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SputWide extends Format21c {
+
+ public static final int OPCODE = 0x68;
+ public static final String NAME = "SputWide";
+ public static final String SMALI_NAME = "sput-wide";
+
+ /*package*/ SputWide(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+ super(high, stream, mapping.getFieldMap());
+ }
+
+ public SputWide(int AA, DexField BBBB) {
+ super(AA, BBBB);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerStaticFieldWrite(getField());
+ }
+
+ @Override
+ public DexField getField() {
+ return (DexField) BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addStaticPut(MemberType.WIDE, AA, getField());
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubDouble.java b/src/main/java/com/android/tools/r8/code/SubDouble.java
new file mode 100644
index 0000000..b5600b5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubDouble.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SubDouble extends Format23x {
+
+ public static final int OPCODE = 0xac;
+ public static final String NAME = "SubDouble";
+ public static final String SMALI_NAME = "sub-double";
+
+ SubDouble(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public SubDouble(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addSub(NumericType.DOUBLE, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubDouble2Addr.java b/src/main/java/com/android/tools/r8/code/SubDouble2Addr.java
new file mode 100644
index 0000000..725fd33
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubDouble2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SubDouble2Addr extends Format12x {
+
+ public static final int OPCODE = 0xcc;
+ public static final String NAME = "SubDouble2Addr";
+ public static final String SMALI_NAME = "sub-double/2addr";
+
+ SubDouble2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public SubDouble2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addSub(NumericType.DOUBLE, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubFloat.java b/src/main/java/com/android/tools/r8/code/SubFloat.java
new file mode 100644
index 0000000..7ab0dfa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubFloat.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SubFloat extends Format23x {
+
+ public static final int OPCODE = 0xA7;
+ public static final String NAME = "SubFloat";
+ public static final String SMALI_NAME = "sub-float";
+
+ SubFloat(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public SubFloat(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addSub(NumericType.FLOAT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubFloat2Addr.java b/src/main/java/com/android/tools/r8/code/SubFloat2Addr.java
new file mode 100644
index 0000000..0126bd2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubFloat2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SubFloat2Addr extends Format12x {
+
+ public static final int OPCODE = 0xc7;
+ public static final String NAME = "SubFloat2Addr";
+ public static final String SMALI_NAME = "sub-float/2addr";
+
+ SubFloat2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public SubFloat2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addSub(NumericType.FLOAT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubInt.java b/src/main/java/com/android/tools/r8/code/SubInt.java
new file mode 100644
index 0000000..735005c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SubInt extends Format23x {
+
+ public static final int OPCODE = 0x91;
+ public static final String NAME = "SubInt";
+ public static final String SMALI_NAME = "sub-int";
+
+ SubInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public SubInt(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addSub(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubInt2Addr.java b/src/main/java/com/android/tools/r8/code/SubInt2Addr.java
new file mode 100644
index 0000000..5cd8be8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubInt2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class SubInt2Addr extends Format12x {
+
+ public static final int OPCODE = 0xb1;
+ public static final String NAME = "SubInt2Addr";
+ public static final String SMALI_NAME = "sub-int/2addr";
+
+ SubInt2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public SubInt2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addSub(NumericType.INT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubLong.java b/src/main/java/com/android/tools/r8/code/SubLong.java
new file mode 100644
index 0000000..a8a0b3b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SubLong extends Format23x {
+
+ public static final int OPCODE = 0x9c;
+ public static final String NAME = "SubLong";
+ public static final String SMALI_NAME = "sub-long";
+
+ SubLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public SubLong(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addSub(NumericType.LONG, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubLong2Addr.java b/src/main/java/com/android/tools/r8/code/SubLong2Addr.java
new file mode 100644
index 0000000..b902e00
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubLong2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class SubLong2Addr extends Format12x {
+
+ public static final int OPCODE = 0xbc;
+ public static final String NAME = "SubLong2Addr";
+ public static final String SMALI_NAME = "sub-long/2addr";
+
+ SubLong2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public SubLong2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addSub(NumericType.LONG, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SwitchPayload.java b/src/main/java/com/android/tools/r8/code/SwitchPayload.java
new file mode 100644
index 0000000..0525e41
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SwitchPayload.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public abstract class SwitchPayload extends Nop {
+ SwitchPayload(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public SwitchPayload() {
+ }
+
+ public abstract int[] keys();
+ public abstract int numberOfKeys();
+ public abstract int[] switchTargetOffsets();
+
+ @Override
+ public boolean isSwitchPayload() {
+ return true;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ // Switch payloads are not represented in the IR.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Throw.java b/src/main/java/com/android/tools/r8/code/Throw.java
new file mode 100644
index 0000000..eabef03
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Throw.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Throw extends Format11x {
+
+ public static final int OPCODE = 0x27;
+ public static final String NAME = "Throw";
+ public static final String SMALI_NAME = "throw";
+
+ Throw(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public Throw(int AA) {
+ super(AA);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addThrow(AA);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/UshrInt.java b/src/main/java/com/android/tools/r8/code/UshrInt.java
new file mode 100644
index 0000000..64a7c3e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/UshrInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class UshrInt extends Format23x {
+
+ public static final int OPCODE = 0x9A;
+ public static final String NAME = "UshrInt";
+ public static final String SMALI_NAME = "ushr-int";
+
+ UshrInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public UshrInt(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addUshr(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/UshrInt2Addr.java b/src/main/java/com/android/tools/r8/code/UshrInt2Addr.java
new file mode 100644
index 0000000..240b663
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/UshrInt2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class UshrInt2Addr extends Format12x {
+
+ public static final int OPCODE = 0xba;
+ public static final String NAME = "UshrInt2Addr";
+ public static final String SMALI_NAME = "ushr-int/2addr";
+
+ UshrInt2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public UshrInt2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addUshr(NumericType.INT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/UshrIntLit8.java b/src/main/java/com/android/tools/r8/code/UshrIntLit8.java
new file mode 100644
index 0000000..d3fada0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/UshrIntLit8.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class UshrIntLit8 extends Format22b {
+
+ public static final int OPCODE = 0xe2;
+ public static final String NAME = "UshrIntLit8";
+ public static final String SMALI_NAME = "ushr-int/lit8";
+
+ UshrIntLit8(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public UshrIntLit8(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addUshrLiteral(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/UshrLong.java b/src/main/java/com/android/tools/r8/code/UshrLong.java
new file mode 100644
index 0000000..0143494
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/UshrLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class UshrLong extends Format23x {
+
+ public static final int OPCODE = 0xa5;
+ public static final String NAME = "UshrLong";
+ public static final String SMALI_NAME = "ushr-long";
+
+ UshrLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public UshrLong(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addUshr(NumericType.LONG, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/UshrLong2Addr.java b/src/main/java/com/android/tools/r8/code/UshrLong2Addr.java
new file mode 100644
index 0000000..0ac851d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/UshrLong2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class UshrLong2Addr extends Format12x {
+
+ public static final int OPCODE = 0xc5;
+ public static final String NAME = "UshrLong2Addr";
+ public static final String SMALI_NAME = "ushr-long/2addr";
+
+ UshrLong2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public UshrLong2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addUshr(NumericType.LONG, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/XorInt.java b/src/main/java/com/android/tools/r8/code/XorInt.java
new file mode 100644
index 0000000..a3f6b0e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/XorInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class XorInt extends Format23x {
+
+ public static final int OPCODE = 0x97;
+ public static final String NAME = "XorInt";
+ public static final String SMALI_NAME = "xor-int";
+
+ XorInt(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public XorInt(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addXor(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/XorInt2Addr.java b/src/main/java/com/android/tools/r8/code/XorInt2Addr.java
new file mode 100644
index 0000000..0c6486f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/XorInt2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class XorInt2Addr extends Format12x {
+
+ public static final int OPCODE = 0xb7;
+ public static final String NAME = "XorInt2Addr";
+ public static final String SMALI_NAME = "xor-int/2addr";
+
+ XorInt2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public XorInt2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addXor(NumericType.INT, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/XorIntLit16.java b/src/main/java/com/android/tools/r8/code/XorIntLit16.java
new file mode 100644
index 0000000..8ce8120
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/XorIntLit16.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class XorIntLit16 extends Format22s {
+
+ public static final int OPCODE = 0xd7;
+ public static final String NAME = "XorIntLit16";
+ public static final String SMALI_NAME = "xor-int/lit16";
+
+ XorIntLit16(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public XorIntLit16(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addXorLiteral(NumericType.INT, A, B, CCCC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/XorIntLit8.java b/src/main/java/com/android/tools/r8/code/XorIntLit8.java
new file mode 100644
index 0000000..14beeef
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/XorIntLit8.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class XorIntLit8 extends Format22b {
+
+ public static final int OPCODE = 0xdf;
+ public static final String NAME = "XorIntLit8";
+ public static final String SMALI_NAME = "xor-int/lit8";
+
+ XorIntLit8(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public XorIntLit8(int dest, int register, int constant) {
+ super(dest, register, constant);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addXorLiteral(NumericType.INT, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/XorLong.java b/src/main/java/com/android/tools/r8/code/XorLong.java
new file mode 100644
index 0000000..ec6de04
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/XorLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class XorLong extends Format23x {
+
+ public static final int OPCODE = 0xA2;
+ public static final String NAME = "XorLong";
+ public static final String SMALI_NAME = "xor-long";
+
+ XorLong(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public XorLong(int dest, int left, int right) {
+ super(dest, left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addXor(NumericType.LONG, AA, BB, CC);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/XorLong2Addr.java b/src/main/java/com/android/tools/r8/code/XorLong2Addr.java
new file mode 100644
index 0000000..23e4f36
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/XorLong2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class XorLong2Addr extends Format12x {
+
+ public static final int OPCODE = 0xc2;
+ public static final String NAME = "XorLong2Addr";
+ public static final String SMALI_NAME = "xor-long/2addr";
+
+ XorLong2Addr(int high, BytecodeStream stream) {
+ super(high, stream);
+ }
+
+ public XorLong2Addr(int left, int right) {
+ super(left, right);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addXor(NumericType.LONG, A, A, B);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
new file mode 100644
index 0000000..1e6751c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -0,0 +1,487 @@
+// 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.compatdx;
+
+import static com.android.tools.r8.utils.FileUtils.isApkFile;
+import static com.android.tools.r8.utils.FileUtils.isClassFile;
+import static com.android.tools.r8.utils.FileUtils.isDexFile;
+import static com.android.tools.r8.utils.FileUtils.isJarFile;
+import static com.android.tools.r8.utils.FileUtils.isZipFile;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.D8Output;
+import com.android.tools.r8.compatdx.CompatDx.DxCompatOptions.DxUsageMessage;
+import com.android.tools.r8.compatdx.CompatDx.DxCompatOptions.PositionInfo;
+import com.android.tools.r8.dex.Constants;
+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.utils.FileUtils;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.Closer;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+/**
+ * Dx compatibility interface for d8.
+ *
+ * This should become a mostly drop-in replacement for uses of the DX dexer (eg, dx --dex ...).
+ */
+public class CompatDx {
+
+ private static final String USAGE_HEADER = "Usage: compatdx [options] <input files>";
+
+ /**
+ * Compatibility options parsing for the DX --dex sub-command.
+ */
+ public static class DxCompatOptions {
+ // Final values after parsing.
+ // Note: These are ordered by their occurrence in "dx --help"
+ public final boolean help;
+ public final boolean debug;
+ public final boolean verbose;
+ public final PositionInfo positions;
+ public final boolean noLocals;
+ public final boolean noOptimize;
+ public final boolean statistics;
+ public final String optimizeList;
+ public final String noOptimizeList;
+ public final boolean noStrict;
+ public final boolean keepClasses;
+ public final String output;
+ public final String dumpTo;
+ public final int dumpWidth;
+ public final String dumpMethod;
+ public final boolean verboseDump;
+ public final boolean noFiles;
+ public final boolean coreLibrary;
+ public final int numThreads;
+ public final boolean incremental;
+ public final boolean forceJumbo;
+ public final boolean noWarning;
+ public final boolean multiDex;
+ public final String mainDexList;
+ public final boolean minimalMainDex;
+ public final int minApiLevel;
+ public final String inputList;
+ public final ImmutableList<String> inputs;
+ // Undocumented option
+ public final int maxIndexNumber;
+
+ private static final String FILE_ARG = "file";
+ private static final String NUM_ARG = "number";
+ private static final String METHOD_ARG = "method";
+
+ public enum PositionInfo {
+ NONE, IMPORTANT, LINES
+ }
+
+ // Exception thrown on invalid dx compat usage.
+ public static class DxUsageMessage extends Exception {
+ public final String message;
+
+ public DxUsageMessage() {
+ this("");
+ }
+
+ DxUsageMessage(String message) {
+ this.message = message;
+ }
+
+ void printHelpOn(PrintStream sink) throws IOException {
+ sink.println(message);
+ }
+ }
+
+ // Exception thrown on options parse error.
+ private static class DxParseError extends DxUsageMessage {
+ private final OptionParser parser;
+
+ private DxParseError(OptionParser parser) {
+ this.parser = parser;
+ }
+
+ @Override
+ public void printHelpOn(PrintStream sink) throws IOException {
+ parser.printHelpOn(sink);
+ }
+ }
+
+ // Parsing specification.
+ private static class Spec {
+ final OptionParser parser;
+
+ // Note: These are ordered by their occurrence in "dx --help"
+ final OptionSpec<Void> debug;
+ final OptionSpec<Void> verbose;
+ final OptionSpec<String> positions;
+ final OptionSpec<Void> noLocals;
+ final OptionSpec<Void> noOptimize;
+ final OptionSpec<Void> statistics;
+ final OptionSpec<String> optimizeList;
+ final OptionSpec<String> noOptimizeList;
+ final OptionSpec<Void> noStrict;
+ final OptionSpec<Void> keepClasses;
+ final OptionSpec<String> output;
+ final OptionSpec<String> dumpTo;
+ final OptionSpec<Integer> dumpWidth;
+ final OptionSpec<String> dumpMethod;
+ final OptionSpec<Void> verboseDump;
+ final OptionSpec<Void> noFiles;
+ final OptionSpec<Void> coreLibrary;
+ final OptionSpec<Integer> numThreads;
+ final OptionSpec<Void> incremental;
+ final OptionSpec<Void> forceJumbo;
+ final OptionSpec<Void> noWarning;
+ final OptionSpec<Void> multiDex;
+ final OptionSpec<String> mainDexList;
+ final OptionSpec<Void> minimalMainDex;
+ final OptionSpec<Integer> minApiLevel;
+ final OptionSpec<String> inputList;
+ final OptionSpec<String> inputs;
+ final OptionSpec<Void> help;
+ final OptionSpec<Integer> maxIndexNumber;
+
+ Spec() {
+ parser = new OptionParser();
+ parser.accepts("dex");
+ debug = parser.accepts("debug", "Print debug information");
+ verbose = parser.accepts("verbose", "Print verbose information");
+ positions = parser
+ .accepts("positions",
+ "What source-position information to keep. One of: none, lines, important")
+ .withOptionalArg()
+ .describedAs("keep")
+ .defaultsTo("lines");
+ noLocals = parser.accepts("no-locals", "Don't keep local variable information");
+ statistics = parser.accepts("statistics", "Print statistics information");
+ noOptimize = parser.accepts("no-optimize", "Don't optimize");
+ optimizeList = parser
+ .accepts("optimize-list", "File listing methods to optimize")
+ .withRequiredArg()
+ .describedAs(FILE_ARG);
+ noOptimizeList = parser
+ .accepts("no-optimize-list", "File listing methods not to optimize")
+ .withRequiredArg()
+ .describedAs(FILE_ARG);
+ noStrict = parser.accepts("no-strict", "Disable strict file/class name checks");
+ keepClasses = parser.accepts("keep-classes", "Keep input class files in in output jar");
+ output = parser
+ .accepts("output", "Output file or directory")
+ .withRequiredArg()
+ .describedAs(FILE_ARG);
+ dumpTo = parser
+ .accepts("dump-to", "File to dump information to")
+ .withRequiredArg()
+ .describedAs(FILE_ARG);
+ dumpWidth = parser
+ .accepts("dump-width", "Max width for columns in dump output")
+ .withRequiredArg()
+ .ofType(Integer.class)
+ .defaultsTo(0)
+ .describedAs(NUM_ARG);
+ dumpMethod = parser
+ .accepts("dump-method", "Method to dump information for")
+ .withRequiredArg()
+ .describedAs(METHOD_ARG);
+ verboseDump = parser.accepts("verbose-dump", "Dump verbose information");
+ noFiles = parser.accepts("no-files", "Don't fail if given no files");
+ coreLibrary = parser.accepts("core-library", "Construct a core library");
+ numThreads = parser
+ .accepts("num-threads", "Number of threads to run with")
+ .withRequiredArg()
+ .ofType(Integer.class)
+ .defaultsTo(1)
+ .describedAs(NUM_ARG);
+ incremental = parser.accepts("incremental", "Merge result with the output if it exists");
+ forceJumbo = parser.accepts("force-jumbo", "Force use of string-jumbo instructions");
+ noWarning = parser.accepts("no-warning", "Suppress warnings");
+ maxIndexNumber = parser.accepts("set-max-idx-number",
+ "Undocumented: Set maximal index number to use in a dex file.")
+ .withRequiredArg()
+ .ofType(Integer.class)
+ .defaultsTo(0)
+ .describedAs("Maximum index");
+ minimalMainDex = parser.accepts("minimal-main-dex", "Produce smallest possible main dex");
+ mainDexList = parser
+ .accepts("main-dex-list", "File listing classes that must be in the main dex file")
+ .withRequiredArg()
+ .describedAs(FILE_ARG);
+ multiDex = parser.accepts("multi-dex", "Allow generation of multi-dex")
+ .requiredIf(noStrict, minimalMainDex, mainDexList, maxIndexNumber);
+ minApiLevel = parser
+ .accepts("min-sdk-version", "Minimum Android API level compatibility.")
+ .withRequiredArg().ofType(Integer.class);
+ inputList = parser
+ .accepts("input-list", "File listing input files")
+ .withRequiredArg()
+ .describedAs(FILE_ARG);
+ inputs = parser.nonOptions("Input files");
+ help = parser.accepts("help", "Print this message").forHelp();
+ }
+ }
+
+ private DxCompatOptions(OptionSet options, Spec spec) throws DxParseError {
+ help = options.has(spec.help);
+ debug = options.has(spec.debug);
+ verbose = options.has(spec.verbose);
+ if (options.has(spec.positions)) {
+ switch (options.valueOf(spec.positions)) {
+ case "none":
+ positions = PositionInfo.NONE;
+ break;
+ case "important":
+ positions = PositionInfo.IMPORTANT;
+ break;
+ case "lines":
+ positions = PositionInfo.LINES;
+ break;
+ default:
+ // Should be checked by parser.
+ throw new DxParseError(spec.parser);
+ }
+ } else {
+ positions = PositionInfo.LINES;
+ }
+ noLocals = options.has(spec.noLocals);
+ noOptimize = options.has(spec.noOptimize);
+ statistics = options.has(spec.statistics);
+ optimizeList = options.valueOf(spec.optimizeList);
+ noOptimizeList = options.valueOf(spec.noOptimizeList);
+ noStrict = options.has(spec.noStrict);
+ keepClasses = options.has(spec.keepClasses);
+ output = options.valueOf(spec.output);
+ dumpTo = options.valueOf(spec.dumpTo);
+ dumpWidth = options.valueOf(spec.dumpWidth);
+ dumpMethod = options.valueOf(spec.dumpMethod);
+ verboseDump = options.has(spec.verboseDump);
+ noFiles = options.has(spec.noFiles);
+ coreLibrary = options.has(spec.coreLibrary);
+ numThreads = options.valueOf(spec.numThreads);
+ incremental = options.has(spec.incremental);
+ forceJumbo = options.has(spec.forceJumbo);
+ noWarning = options.has(spec.noWarning);
+ multiDex = options.has(spec.multiDex);
+ mainDexList = options.valueOf(spec.mainDexList);
+ minimalMainDex = options.has(spec.minimalMainDex);
+ minApiLevel = options.has(spec.minApiLevel)
+ ? options.valueOf(spec.minApiLevel)
+ : Constants.DEFAULT_ANDROID_API;
+ inputList = options.valueOf(spec.inputList);
+ inputs = ImmutableList.copyOf(options.valuesOf(spec.inputs));
+ maxIndexNumber = options.valueOf(spec.maxIndexNumber);
+ }
+
+ public static DxCompatOptions parse(String[] args) throws DxParseError {
+ Spec spec = new Spec();
+ return new DxCompatOptions(spec.parser.parse(args), spec);
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ try {
+ run(args);
+ } catch (CompilationException e) {
+ System.err.println(e.getMessage());
+ System.exit(1);
+ } catch (DxUsageMessage e) {
+ System.err.println(USAGE_HEADER);
+ e.printHelpOn(System.err);
+ System.exit(1);
+ }
+ }
+
+ private static void run(String[] args) throws DxUsageMessage, IOException, CompilationException {
+ DxCompatOptions dexArgs = DxCompatOptions.parse(args);
+ if (dexArgs.help) {
+ printHelpOn(System.out);
+ return;
+ }
+ CompilationMode mode = CompilationMode.RELEASE;
+ Path output = null;
+ List<Path> inputs = new ArrayList<>();
+ boolean singleDexFile = !dexArgs.multiDex;
+ Path mainDexList = null;
+ int numberOfThreads = 1;
+
+ for (String path : dexArgs.inputs) {
+ processPath(new File(path), inputs);
+ }
+ if (inputs.isEmpty()) {
+ if (dexArgs.noFiles) {
+ return;
+ }
+ throw new DxUsageMessage("No input files specified");
+ }
+
+ if (!Log.ENABLED && (!dexArgs.noWarning || dexArgs.debug || dexArgs.verbose)) {
+ System.out.println("Warning: logging is not enabled for this build.");
+ }
+
+ if (dexArgs.verboseDump) {
+ throw new Unimplemented("verbose dump file not yet supported");
+ }
+
+ if (dexArgs.dumpMethod != null) {
+ throw new Unimplemented("method-dump not yet supported");
+ }
+
+ if (dexArgs.output != null) {
+ output = Paths.get(dexArgs.output);
+ if (FileUtils.isDexFile(output)) {
+ if (!singleDexFile) {
+ throw new DxUsageMessage("Cannot output to a single dex-file when running with multidex");
+ }
+ } else if (!FileUtils.isArchive(output)
+ && (!output.toFile().exists() || !output.toFile().isDirectory())) {
+ throw new DxUsageMessage("Unsupported output file or output directory does not exist. "
+ + "Output must be a directory or a file of type dex, apk, jar or zip.");
+ }
+ }
+
+ if (dexArgs.dumpTo != null) {
+ throw new Unimplemented("dump-to file not yet supported");
+ }
+
+ if (dexArgs.keepClasses) {
+ throw new Unimplemented("keeping classes in jar not yet supported");
+ }
+
+ if (dexArgs.positions != PositionInfo.NONE) {
+ mode = CompilationMode.DEBUG;
+ }
+
+ if (!dexArgs.noLocals) {
+ mode = CompilationMode.DEBUG;
+ }
+
+ if (dexArgs.incremental) {
+ throw new Unimplemented("incremental merge not supported yet");
+ }
+
+ if (dexArgs.forceJumbo) {
+ throw new Unimplemented("force jumbo not yet supported");
+ }
+
+ if (dexArgs.noOptimize) {
+ System.out.println("Warning: no support for not optimizing yet");
+ }
+
+ if (dexArgs.optimizeList != null) {
+ throw new Unimplemented("no support for optimize-method list");
+ }
+
+ if (dexArgs.noOptimizeList != null) {
+ throw new Unimplemented("no support for dont-optimize-method list");
+ }
+
+ if (dexArgs.statistics) {
+ throw new Unimplemented("no support for printing statistics yet");
+ }
+
+ if (dexArgs.numThreads > 1) {
+ numberOfThreads = dexArgs.numThreads;
+ }
+
+ if (dexArgs.mainDexList != null) {
+ mainDexList = Paths.get(dexArgs.mainDexList);
+ }
+
+ if (dexArgs.noStrict) {
+ System.out.println("Warning: conservative main-dex list not yet supported");
+ }
+
+ if (dexArgs.minimalMainDex) {
+ System.out.println("Warning: minimal main-dex support is not yet supported");
+ }
+
+ if (dexArgs.maxIndexNumber != 0) {
+ System.out.println("Warning: internal maximum-index setting is not supported");
+ }
+
+ if (numberOfThreads < 1) {
+ throw new DxUsageMessage("Invalid numThreads value of " + numberOfThreads);
+ }
+ ExecutorService executor = ThreadUtils.getExecutorService(numberOfThreads);
+ D8Output result;
+ try {
+ result = D8.run(
+ D8Command.builder()
+ .addProgramFiles(inputs)
+ .setMode(mode)
+ .setMinApiLevel(dexArgs.minApiLevel)
+ .setMainDexListFile(mainDexList)
+ .build());
+ } finally {
+ executor.shutdown();
+ }
+
+ if (output == null) {
+ return;
+ }
+
+ if (singleDexFile) {
+ if (result.getDexResources().size() > 1) {
+ throw new CompilationError(
+ "Compilation result could not fit into a single dex file. "
+ + "Reduce the input-program size or run with --multi-dex enabled");
+ }
+ if (isDexFile(output)) {
+ try (Closer closer = Closer.create()) {
+ InputStream stream = result.getDexResources().get(0).getStream(closer);
+ Files.copy(stream, output, StandardCopyOption.REPLACE_EXISTING);
+ }
+ return;
+ }
+ }
+
+ result.write(output);
+ }
+
+ static void printHelpOn(PrintStream sink) throws IOException {
+ sink.println(USAGE_HEADER);
+ new DxCompatOptions.Spec().parser.printHelpOn(sink);
+ }
+
+ private static void processPath(File file, List<Path> files) {
+ if (!file.exists()) {
+ throw new CompilationError("File does not exist: " + file);
+ }
+ if (file.isDirectory()) {
+ processDirectory(file, files);
+ return;
+ }
+ Path path = file.toPath();
+ if (isZipFile(path) || isJarFile(path) || isClassFile(path)) {
+ files.add(path);
+ return;
+ }
+ if (isApkFile(path)) {
+ throw new Unimplemented("apk files not yet supported");
+ }
+ }
+
+ private static void processDirectory(File directory, List<Path> files) {
+ assert directory.exists();
+ for (File file : directory.listFiles()) {
+ processPath(file, files);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
new file mode 100644
index 0000000..672f6bd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -0,0 +1,221 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import static com.android.tools.r8.dex.Constants.ANDROID_N_API;
+import static com.android.tools.r8.dex.Constants.ANDROID_N_DEX_VERSION;
+import static com.android.tools.r8.dex.Constants.ANDROID_O_API;
+import static com.android.tools.r8.dex.Constants.ANDROID_O_DEX_VERSION;
+import static com.android.tools.r8.dex.Constants.DEFAULT_ANDROID_API;
+import static com.android.tools.r8.utils.FileUtils.DEFAULT_DEX_FILENAME;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.JarApplicationReader;
+import com.android.tools.r8.graph.JarClassFileReader;
+import com.android.tools.r8.graph.LazyClassFileLoader;
+import com.android.tools.r8.naming.ProguardMapReader;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalResource;
+import com.android.tools.r8.utils.MainDexList;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class ApplicationReader {
+
+ final InternalOptions options;
+ final DexItemFactory itemFactory;
+ final Timing timing;
+ private final AndroidApp inputApp;
+
+ public ApplicationReader(AndroidApp inputApp, InternalOptions options, Timing timing) {
+ this.options = options;
+ itemFactory = options.itemFactory;
+ this.timing = timing;
+ this.inputApp = inputApp;
+ }
+
+ public DexApplication read() throws IOException, ExecutionException {
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ try {
+ return read(executor);
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ public final DexApplication read(ExecutorService executorService)
+ throws IOException, ExecutionException {
+ timing.begin("DexApplication.read");
+ final DexApplication.Builder builder = new DexApplication.Builder(itemFactory, timing);
+ try (Closer closer = Closer.create()) {
+ List<Future<?>> futures = new ArrayList<>();
+ readProguardMap(builder, executorService, futures, closer);
+ readMainDexList(builder, executorService, futures, closer);
+ readDexSources(builder, executorService, futures, closer);
+ readClassSources(builder, closer);
+ ThreadUtils.awaitFutures(futures);
+ } catch (ExecutionException e) {
+ // If the reading failed with a valid compilation error, rethrow the unwrapped exception.
+ Throwable cause = e.getCause();
+ if (cause != null && cause instanceof CompilationError) {
+ throw (CompilationError) cause;
+ }
+ throw e;
+ } finally {
+ timing.end();
+ }
+ return builder.build();
+ }
+
+ private void readClassSources(DexApplication.Builder builder, Closer closer)
+ throws IOException, ExecutionException {
+ JarApplicationReader application = new JarApplicationReader(options);
+ JarClassFileReader reader = new JarClassFileReader(
+ application, builder::addClassIgnoringLibraryDuplicates);
+ for (InternalResource input : inputApp.getClassProgramResources()) {
+ reader.read(DEFAULT_DEX_FILENAME, InternalResource.Kind.PROGRAM, input.getStream(closer));
+ }
+ for (InternalResource input : inputApp.getClassClasspathResources()) {
+ if (options.lazyClasspathLoading && input.getClassDescriptor() != null) {
+ addLazyLoader(application, builder, input);
+ } else {
+ reader.read(DEFAULT_DEX_FILENAME, InternalResource.Kind.CLASSPATH, input.getStream(closer));
+ }
+ }
+ for (InternalResource input : inputApp.getClassLibraryResources()) {
+ if (options.lazyLibraryLoading && input.getClassDescriptor() != null) {
+ addLazyLoader(application, builder, input);
+ } else {
+ reader.read(DEFAULT_DEX_FILENAME, InternalResource.Kind.LIBRARY, input.getStream(closer));
+ }
+ }
+ }
+
+ private void addLazyLoader(JarApplicationReader application,
+ DexApplication.Builder builder, InternalResource resource) {
+ // Generate expected DEX type.
+ String classDescriptor = resource.getClassDescriptor();
+ assert classDescriptor != null;
+ DexType type = options.itemFactory.createType(classDescriptor);
+ LazyClassFileLoader newLoader = new LazyClassFileLoader(type, resource, application);
+ builder.addClassPromise(newLoader, true);
+ }
+
+ private void readDexSources(DexApplication.Builder builder, ExecutorService executorService,
+ List<Future<?>> futures, Closer closer)
+ throws IOException, ExecutionException {
+ List<InternalResource> dexProgramSources = inputApp.getDexProgramResources();
+ List<InternalResource> dexClasspathSources = inputApp.getDexClasspathResources();
+ List<InternalResource> dexLibrarySources = inputApp.getDexLibraryResources();
+ int numberOfFiles = dexProgramSources.size()
+ + dexLibrarySources.size() + dexClasspathSources.size();
+ if (numberOfFiles > 0) {
+ List<DexFileReader> fileReaders = new ArrayList<>(numberOfFiles);
+ int computedMinApiLevel = options.minApiLevel;
+ for (InternalResource input : dexProgramSources) {
+ DexFile file = new DexFile(input.getStream(closer));
+ computedMinApiLevel = verifyOrComputeMinApiLevel(computedMinApiLevel, file);
+ fileReaders.add(new DexFileReader(file, InternalResource.Kind.PROGRAM, itemFactory));
+ }
+ for (InternalResource input : dexClasspathSources) {
+ DexFile file = new DexFile(input.getStream(closer));
+ fileReaders.add(new DexFileReader(file, InternalResource.Kind.CLASSPATH, itemFactory));
+ }
+ for (InternalResource input : dexLibrarySources) {
+ DexFile file = new DexFile(input.getStream(closer));
+ computedMinApiLevel = verifyOrComputeMinApiLevel(computedMinApiLevel, file);
+ fileReaders.add(new DexFileReader(file, InternalResource.Kind.LIBRARY, itemFactory));
+ }
+ options.minApiLevel = computedMinApiLevel;
+ for (DexFileReader reader : fileReaders) {
+ DexFileReader.populateIndexTables(reader);
+ }
+ // Read the DexCode items and DexProgramClass items in parallel.
+ for (DexFileReader reader : fileReaders) {
+ futures.add(executorService.submit(() -> {
+ reader.addCodeItemsTo(); // Depends on Everything for parsing.
+ reader.addClassDefsTo(builder::addClassPromise); // Depends on Methods, Code items etc.
+ }));
+ }
+ }
+ }
+
+ private int verifyOrComputeMinApiLevel(int computedMinApiLevel, DexFile file) {
+ int version = file.getDexVersion();
+ if (options.minApiLevel == DEFAULT_ANDROID_API) {
+ computedMinApiLevel = Math.max(computedMinApiLevel, dexVersionToMinSdk(version));
+ } else if (!minApiMatchesDexVersion(version)) {
+ throw new CompilationError("Dex file with version '" + version +
+ "' cannot be used with min sdk level '" + options.minApiLevel + "'.");
+ }
+ return computedMinApiLevel;
+ }
+
+ private boolean minApiMatchesDexVersion(int version) {
+ switch (version) {
+ case ANDROID_O_DEX_VERSION:
+ return options.minApiLevel >= ANDROID_O_API;
+ case ANDROID_N_DEX_VERSION:
+ return options.minApiLevel >= ANDROID_N_API;
+ default:
+ return true;
+ }
+ }
+
+ private int dexVersionToMinSdk(int version) {
+ switch (version) {
+ case ANDROID_O_DEX_VERSION:
+ return ANDROID_O_API;
+ case ANDROID_N_DEX_VERSION:
+ return ANDROID_N_API;
+ default:
+ return DEFAULT_ANDROID_API;
+ }
+ }
+
+ private void readProguardMap(DexApplication.Builder builder, ExecutorService executorService,
+ List<Future<?>> futures, Closer closer)
+ throws IOException {
+ // Read the Proguard mapping file in parallel with DexCode and DexProgramClass items.
+ if (inputApp.hasProguardMap()) {
+ futures.add(executorService.submit(() -> {
+ try {
+ InputStream map = inputApp.getProguardMap(closer);
+ builder.setProguardMap(ProguardMapReader.mapperFromInputStream(map));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }));
+ }
+ }
+
+ private void readMainDexList(DexApplication.Builder builder, ExecutorService executorService,
+ List<Future<?>> futures, Closer closer)
+ throws IOException {
+ if (inputApp.hasMainDexList()) {
+ futures.add(executorService.submit(() -> {
+ try {
+ InputStream input = inputApp.getMainDexList(closer);
+ builder.addToMainDexList(MainDexList.parse(input, itemFactory));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }));
+ }
+ }
+
+}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
new file mode 100644
index 0000000..d86f929
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -0,0 +1,177 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedArray;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.naming.MinifiedNameMapPrinter;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.PackageDistribution;
+import com.android.tools.r8.utils.ThreadUtils;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+public class ApplicationWriter {
+
+ public final DexApplication application;
+ public final AppInfo appInfo;
+ public final NamingLens namingLens;
+ public final byte[] proguardSeedsData;
+ public final InternalOptions options;
+
+ private static class SortAnnotations extends MixedSectionCollection {
+
+ @Override
+ public boolean add(DexAnnotationSet dexAnnotationSet) {
+ // Annotation sets are sorted by annotation types.
+ dexAnnotationSet.sort();
+ return true;
+ }
+
+ @Override
+ public boolean add(DexAnnotation annotation) {
+ // The elements of encoded annotation must be sorted by name.
+ annotation.annotation.sort();
+ return true;
+ }
+
+ @Override
+ public boolean add(DexEncodedArray dexEncodedArray) {
+ // Dex values must potentially be sorted, eg, for DexValueAnnotation.
+ for (DexValue value : dexEncodedArray.values) {
+ value.sort();
+ }
+ return true;
+ }
+
+ @Override
+ public boolean add(DexProgramClass dexClassData) {
+ return true;
+ }
+
+ @Override
+ public boolean add(DexCode dexCode) {
+ return true;
+ }
+
+ @Override
+ public boolean add(DexDebugInfo dexDebugInfo) {
+ return true;
+ }
+
+ @Override
+ public boolean add(DexTypeList dexTypeList) {
+ return true;
+ }
+
+ @Override
+ public boolean add(DexAnnotationSetRefList annotationSetRefList) {
+ return true;
+ }
+ }
+
+ public ApplicationWriter(
+ DexApplication application,
+ AppInfo appInfo,
+ InternalOptions options,
+ NamingLens namingLens,
+ byte[] proguardSeedsData) {
+ assert application != null;
+ this.application = application;
+ this.appInfo = appInfo;
+ assert options != null;
+ this.options = options;
+ this.namingLens = namingLens;
+ this.proguardSeedsData = proguardSeedsData;
+ }
+
+ public AndroidApp write(PackageDistribution packageDistribution, ExecutorService executorService)
+ throws IOException, ExecutionException {
+ application.timing.begin("DexApplication.write");
+ try {
+ application.dexItemFactory.sort(namingLens);
+ SortAnnotations sortAnnotations = new SortAnnotations();
+ application.classes().forEach((clazz) -> clazz.addDependencies(sortAnnotations));
+ Map<Integer, VirtualFile> newFiles =
+ VirtualFile.fileSetFrom(this, packageDistribution, executorService);
+
+ // Write the dex files and the Proguard mapping file in parallel.
+ List<Future<byte[]>> dexDataFutures = new ArrayList<>();
+ for (Integer index : newFiles.keySet()) {
+ VirtualFile newFile = newFiles.get(index);
+ if (!newFile.isEmpty()) {
+ dexDataFutures.add(executorService.submit(() -> writeDexFile(newFile)));
+ }
+ }
+
+ // Wait for all the spawned futures to terminate.
+ List<byte[]> dexData = ThreadUtils.awaitFutures(dexDataFutures);
+ AndroidApp.Builder builder = AndroidApp.builder();
+ dexData.forEach(builder::addDexProgramData);
+ // Write the proguard map file after writing the dex files, as the map writer traverses
+ // the DexProgramClass structures, which are destructively updated during dex file writing.
+ byte[] proguardMapResult = writeProguardMapFile();
+ if (proguardMapResult != null) {
+ builder.setProguardMapData(proguardMapResult);
+ }
+ if (proguardSeedsData != null) {
+ builder.setProguardSeedsData(proguardSeedsData);
+ }
+ return builder.build();
+ } finally {
+ application.timing.end();
+ }
+ }
+
+ private byte[] writeDexFile(VirtualFile vfile) {
+ FileWriter fileWriter =
+ new FileWriter(
+ vfile.computeMapping(application), application, appInfo, options, namingLens);
+ // The file writer now knows the indexes of the fixed sections including strings.
+ fileWriter.rewriteCodeWithJumboStrings(vfile.classes());
+ // Collect the non-fixed sections.
+ fileWriter.collect();
+ // Generate and write the bytes.
+ return fileWriter.generate();
+ }
+
+ private byte[] writeProguardMapFile() throws IOException {
+ // TODO(herhut): Should writing of the proguard-map file be split like this?
+ if (!namingLens.isIdentityLens()) {
+ MinifiedNameMapPrinter printer = new MinifiedNameMapPrinter(application, namingLens);
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ PrintStream stream = new PrintStream(bytes);
+ printer.write(stream);
+ stream.flush();
+ return bytes.toByteArray();
+ } else if (application.getProguardMap() != null) {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ Writer writer = new PrintWriter(bytes);
+ application.getProguardMap().write(writer, !options.skipDebugLineNumberOpt);
+ writer.flush();
+ return bytes.toByteArray();
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java
new file mode 100644
index 0000000..201fc82
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -0,0 +1,143 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+public class Constants {
+
+ public static final byte[] DEX_FILE_MAGIC_PREFIX = {'d', 'e', 'x', '\n'};
+ public static final byte DEX_FILE_MAGIC_SUFFIX = '\0';
+
+ public static final int ANDROID_O_API = 26;
+ public static final int ANDROID_N_API = 24;
+ public static final int ANDROID_K_API = 19;
+ public static final int DEFAULT_ANDROID_API = 1;
+
+ /** dex file version number for Android O (API level 26) */
+ public static final int ANDROID_O_DEX_VERSION = 38;
+ public static final byte[] ANDROID_O_DEX_VERSION_BYTES = {'0', '3', '8'};
+ /** dex file version number for Android N (API level 24) */
+ public static final int ANDROID_N_DEX_VERSION = 37;
+ public static final byte[] ANDROID_N_DEX_VERSION_BYTES = {'0', '3', '7'};
+ /** dex file version number for all releases prior to Android N */
+ public static final int ANDROID_PRE_N_DEX_VERSION = 35;
+ public static final byte[] ANDROID_PRE_N_DEX_VERSION_BYTES = {'0', '3', '5'};
+
+ public static final int DEX_MAGIC_SIZE = 8;
+
+ public static final int HEADER_SIZE = 0x70;
+ public static final int MAGIC_OFFSET = 0;
+ public static final int CHECKSUM_OFFSET = MAGIC_OFFSET + DEX_MAGIC_SIZE;
+ public static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + 4;
+ public static final int FILE_SIZE_OFFSET = SIGNATURE_OFFSET + 20;
+ public static final int HEADER_SIZE_OFFSET = FILE_SIZE_OFFSET + 4;
+ public static final int ENDIAN_TAG_OFFSET = HEADER_SIZE_OFFSET + 4;
+ public static final int LINK_SIZE_OFFSET = ENDIAN_TAG_OFFSET + 4;
+ public static final int LINK_OFF_OFFSET = LINK_SIZE_OFFSET + 4;
+ public static final int MAP_OFF_OFFSET = LINK_OFF_OFFSET + 4;
+ public static final int STRING_IDS_SIZE_OFFSET = MAP_OFF_OFFSET + 4;
+ public static final int STRING_IDS_OFF_OFFSET = STRING_IDS_SIZE_OFFSET + 4;
+ public static final int TYPE_IDS_SIZE_OFFSET = STRING_IDS_OFF_OFFSET + 4;
+ public static final int TYPE_IDS_OFF_OFFSET = TYPE_IDS_SIZE_OFFSET + 4;
+ public static final int PROTO_IDS_SIZE_OFFSET = TYPE_IDS_OFF_OFFSET + 4;
+ public static final int PROTO_IDS_OFF_OFFSET = PROTO_IDS_SIZE_OFFSET + 4;
+ public static final int FIELD_IDS_SIZE_OFFSET = PROTO_IDS_OFF_OFFSET + 4;
+ public static final int FIELD_IDS_OFF_OFFSET = FIELD_IDS_SIZE_OFFSET + 4;
+ public static final int METHOD_IDS_SIZE_OFFSET = FIELD_IDS_OFF_OFFSET + 4;
+ public static final int METHOD_IDS_OFF_OFFSET = METHOD_IDS_SIZE_OFFSET + 4;
+ public static final int CLASS_DEFS_SIZE_OFFSET = METHOD_IDS_OFF_OFFSET + 4;
+ public static final int CLASS_DEFS_OFF_OFFSET = CLASS_DEFS_SIZE_OFFSET + 4;
+ public static final int DATA_SIZE_OFFSET = CLASS_DEFS_OFF_OFFSET + 4;
+ public static final int DATA_OFF_OFFSET = DATA_SIZE_OFFSET + 4;
+
+ public static final int ENDIAN_CONSTANT = 0x12345678;
+ public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412;
+
+ public static final int TYPE_HEADER_ITEM = 0x0;
+ public static final int TYPE_HEADER_ITEM_SIZE = 0x70;
+ public static final int TYPE_STRING_ID_ITEM = 0x0001;
+ public static final int TYPE_STRING_ID_ITEM_SIZE = 0x04;
+ public static final int TYPE_TYPE_ID_ITEM = 0x0002;
+ public static final int TYPE_TYPE_ID_ITEM_SIZE = 0x04;
+ public static final int TYPE_PROTO_ID_ITEM = 0x0003;
+ public static final int TYPE_PROTO_ID_ITEM_SIZE = 0x0c;
+ public static final int TYPE_FIELD_ID_ITEM = 0x0004;
+ public static final int TYPE_FIELD_ID_ITEM_SIZE = 0x08;
+ public static final int TYPE_METHOD_ID_ITEM = 0x0005;
+ public static final int TYPE_METHOD_ID_ITEM_SIZE = 0x08;
+ public static final int TYPE_CLASS_DEF_ITEM = 0x0006;
+ public static final int TYPE_CLASS_DEF_ITEM_SIZE = 0x20;
+ public static final int TYPE_CALL_SITE_ID_ITEM = 0x0007;
+ public static final int TYPE_CALL_SITE_ID_ITEM_SIZE = 0x04;
+ public static final int TYPE_METHOD_HANDLE_ITEM = 0x0008;
+ public static final int TYPE_METHOD_HANDLE_ITEM_SIZE = 0x0008;
+ public static final int TYPE_MAP_LIST = 0x1000;
+ public static final int TYPE_MAP_LIST_ITEM_SIZE = 0x0c;
+ public static final int TYPE_TYPE_LIST = 0x1001;
+ public static final int TYPE_ANNOTATION_SET_REF_LIST = 0x1002;
+ public static final int TYPE_ANNOTATION_SET_ITEM = 0x1003;
+ public static final int TYPE_CLASS_DATA_ITEM = 0x2000;
+ public static final int TYPE_CODE_ITEM = 0x2001;
+ public static final int TYPE_STRING_DATA_ITEM = 0x2002;
+ public static final int TYPE_DEBUG_INFO_ITEM = 0x2003;
+ public static final int TYPE_ANNOTATION_ITEM = 0x2004;
+ public static final int TYPE_ENCODED_ARRAY_ITEM = 0x2005;
+ public static final int TYPE_ANNOTATIONS_DIRECTORY_ITEM = 0x2006;
+
+ public static final int DBG_START_LOCAL = 0x03;
+ public static final int DBG_START_LOCAL_EXTENDED = 0x04;
+ public static final int DBG_END_LOCAL = 0x05;
+ public static final int DBG_RESTART_LOCAL = 0x06;
+ public static final int DBG_SET_FILE = 0x09;
+ public static final int DBG_END_SEQUENCE = 0x00;
+ public static final int DBG_ADVANCE_PC = 0x01;
+ public static final int DBG_ADVANCE_LINE = 0x02;
+ public static final int DBG_SET_PROLOGUE_END = 0x07;
+ public static final int DBG_SET_EPILOGUE_BEGIN = 0x08;
+ public static final int DBG_FIRST_SPECIAL = 0x0a;
+ public static final int DBG_LAST_SPECIAL = 0xff;
+ public static final int DBG_LINE_BASE = -4;
+ public static final int DBG_LINE_RANGE = 15;
+ public static final int DBG_ADDRESS_RANGE = 16;
+
+ public static final int NO_OFFSET = 0;
+ public static final int NO_INDEX = -1;
+
+ public static final int S4BIT_SIGN_MASK = 1 << 3;
+ public static final int S4BIT_MIN = -(1 << 3);
+ public static final int S4BIT_MAX = (1 << 3) - 1;
+ public static final int S8BIT_MIN = -(1 << 7);
+ public static final int S8BIT_MAX = (1 << 7) - 1;
+
+ public static final int U4BIT_MAX = (1 << 4) - 1;
+ public static final int U8BIT_MAX = (1 << 8) - 1;
+ public static final int U16BIT_MAX = (1 << 16) - 1;
+ public static final int ACC_PUBLIC = 0x1;
+ public static final int ACC_PRIVATE = 0x2;
+ public static final int ACC_PROTECTED = 0x4;
+ public static final int ACC_STATIC = 0x8;
+ public static final int ACC_FINAL = 0x10;
+ public static final int ACC_SYNCHRONIZED = 0x20;
+ public static final int ACC_VOLATILE = 0x40;
+ public static final int ACC_BRIDGE = 0x40;
+ public static final int ACC_TRANSIENT = 0x80;
+ public static final int ACC_VARARGS = 0x80;
+ public static final int ACC_NATIVE = 0x100;
+ public static final int ACC_INTERFACE = 0x200;
+ public static final int ACC_ABSTRACT = 0x400;
+ public static final int ACC_STRICT = 0x800;
+ public static final int ACC_SYNTHETIC = 0x1000;
+ public static final int ACC_ANNOTATION = 0x2000;
+ public static final int ACC_ENUM = 0x4000;
+ public static final int ACC_CONSTRUCTOR = 0x10000;
+ public static final int ACC_DECLARED_SYNCHRONIZED = 0x20000;
+
+ public static final String JAVA_LANG_OBJECT_NAME = "java/lang/Object";
+ public static final String INSTANCE_INITIALIZER_NAME = "<init>";
+ public static final String CLASS_INITIALIZER_NAME = "<clinit>";
+
+ public static final int MAX_NON_JUMBO_INDEX = U16BIT_MAX;
+ public static final int FIRST_JUMBO_INDEX = MAX_NON_JUMBO_INDEX + 1;
+
+ public static final int KILOBYTE = 1 << 10;
+}
diff --git a/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java b/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java
new file mode 100644
index 0000000..06dbcfe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.graph.DexDebugEvent;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.utils.LebUtils;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+public class DebugBytecodeWriter {
+
+ private final ObjectToOffsetMapping mapping;
+ private final DexDebugInfo info;
+ private ByteBuffer buffer;
+
+ public DebugBytecodeWriter(DexDebugInfo info, ObjectToOffsetMapping mapping) {
+ this.info = info;
+ this.mapping = mapping;
+ // Never allocate a zero-sized buffer, as we need to write the header, and the growth policy
+ // requires it to have a positive capacity.
+ this.buffer = ByteBuffer.allocate(info.events.length * 5 + 4);
+ }
+
+ public byte[] generate() {
+ // Header.
+ putUleb128(info.startLine); // line_start
+ putUleb128(info.parameters.length);
+ for (DexString name : info.parameters) {
+ putString(name);
+ }
+ // Body.
+ for (DexDebugEvent event : info.events) {
+ event.writeOn(this, mapping);
+ }
+ // Tail.
+ putByte(Constants.DBG_END_SEQUENCE);
+ return Arrays.copyOf(buffer.array(), buffer.position());
+ }
+
+ private void maybeGrow(int size) {
+ if (buffer.remaining() < size) {
+ ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity() * 2);
+ newBuffer.put(buffer.array(), 0, buffer.position());
+ buffer = newBuffer;
+ }
+ }
+
+ public void putByte(int item) {
+ maybeGrow(1);
+ buffer.put((byte) item);
+ }
+
+ public void putSleb128(int item) {
+ byte[] encoded = LebUtils.encodeSleb128(item);
+ maybeGrow(encoded.length);
+ buffer.put(encoded, 0, encoded.length);
+ }
+
+ public void putUleb128(int item) {
+ byte[] encoded = LebUtils.encodeUleb128(item);
+ maybeGrow(encoded.length);
+ buffer.put(encoded, 0, encoded.length);
+ }
+
+ private void putUleb128p1(int item) {
+ putUleb128(item + 1);
+ }
+
+ private void putNoIndex() {
+ putUleb128(0);
+ }
+
+ public void putType(DexType type) {
+ if (type == null) {
+ putNoIndex();
+ } else {
+ int index = mapping.getOffsetFor(type);
+ putUleb128p1(index);
+ }
+ }
+
+ public void putString(DexString string) {
+ if (string == null) {
+ putNoIndex();
+ } else {
+ int index = mapping.getOffsetFor(string);
+ putUleb128p1(index);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/DexFile.java b/src/main/java/com/android/tools/r8/dex/DexFile.java
new file mode 100644
index 0000000..28aff79
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/DexFile.java
@@ -0,0 +1,169 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import static com.android.tools.r8.dex.Constants.DEX_FILE_MAGIC_PREFIX;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.LebUtils;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class DexFile {
+
+ final String name;
+ private final ByteBuffer buffer;
+ private final int version;
+
+ DexFile(String name) throws IOException {
+ this.name = name;
+ Path path = Paths.get(name);
+ buffer = ByteBuffer.wrap(Files.readAllBytes(path));
+ version = parseMagic(buffer);
+ }
+
+ DexFile(InputStream input) throws IOException {
+ // TODO(zerny): Remove dependencies on file names.
+ name = "input-stream.dex";
+ buffer = ByteBuffer.wrap(ByteStreams.toByteArray(input));
+ version = parseMagic(buffer);
+ }
+
+ /**
+ * Returns a File that contains the bytes provided as argument. Used for testing.
+ *
+ * @param bytes contents of the file
+ */
+ DexFile(byte[] bytes) {
+ this.name = "mockfile.dex";
+ buffer = ByteBuffer.wrap(bytes);
+ version = parseMagic(buffer);
+ }
+
+ // Parse the magic header and determine the dex file version.
+ private int parseMagic(ByteBuffer buffer) {
+ int index = 0;
+ for (byte prefixByte : DEX_FILE_MAGIC_PREFIX) {
+ if (buffer.get(index++) != prefixByte) {
+ throw new CompilationError("Dex file has invalid header: " + name);
+ }
+ }
+ if (buffer.get(index++) != '0' || buffer.get(index++) != '3') {
+ throw new CompilationError("Dex file has invalid version number: " + name);
+ }
+ byte versionByte = buffer.get(index++);
+ int version;
+ switch (versionByte) {
+ case '8':
+ version = 38;
+ break;
+ case '7':
+ version = 37;
+ break;
+ case '5':
+ version = 35;
+ break;
+ default:
+ throw new CompilationError("Dex file has invalid version number: " + name);
+ }
+ if (buffer.get(index++) != '\0') {
+ throw new CompilationError("Dex file has invalid header: " + name);
+ }
+ return version;
+ }
+
+ int getDexVersion() {
+ return version;
+ }
+
+ byte[] getByteArray(int size) {
+ byte[] result = new byte[size];
+ buffer.get(result);
+ return result;
+ }
+
+ int getUleb128() {
+ return LebUtils.parseUleb128(this);
+ }
+
+ int getSleb128() {
+ return LebUtils.parseSleb128(this);
+ }
+
+ int getUleb128p1() {
+ return getUleb128() - 1;
+ }
+
+ int getUint() {
+ int result = buffer.getInt();
+ assert result >= 0; // Ensure the java int didn't overflow.
+ return result;
+ }
+
+ int getUshort() {
+ int result = buffer.getShort() & 0xffff;
+ assert result >= 0; // Ensure we have a non-negative number.
+ return result;
+ }
+
+ short getShort() {
+ return buffer.getShort();
+ }
+
+ int getUint(int offset) {
+ int result = buffer.getInt(offset);
+ assert result >= 0; // Ensure the java int didn't overflow.
+ return result;
+ }
+
+ public int getInt() {
+ return buffer.getInt();
+ }
+
+ void setByteOrder() {
+ // Make sure we set the right endian for reading.
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ int endian = buffer.getInt(Constants.ENDIAN_TAG_OFFSET);
+ if (endian == Constants.REVERSE_ENDIAN_CONSTANT) {
+ buffer.order(ByteOrder.BIG_ENDIAN);
+ } else {
+ assert endian == Constants.ENDIAN_CONSTANT;
+ }
+ }
+
+ int position() {
+ return buffer.position();
+ }
+
+ void position(int position) {
+ buffer.position(position);
+ }
+
+ void align(int alignment) {
+ assert (alignment & (alignment - 1)) == 0; // Check alignment is power of 2.
+ int p = buffer.position();
+ p += (alignment - (p % alignment)) & (alignment - 1);
+ buffer.position(p);
+ }
+
+ public byte get() {
+ return buffer.get();
+ }
+
+ int getUbyte() {
+ int result = buffer.get() & 0xff;
+ assert result >= 0; // Ensure we have a non-negative result.
+ return result;
+ }
+
+ int end() {
+ return buffer.capacity();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/DexFileReader.java b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
new file mode 100644
index 0000000..bdafdc4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
@@ -0,0 +1,1018 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import static com.android.tools.r8.utils.EncodedValueUtils.parseDouble;
+import static com.android.tools.r8.utils.EncodedValueUtils.parseFloat;
+import static com.android.tools.r8.utils.EncodedValueUtils.parseSigned;
+import static com.android.tools.r8.utils.EncodedValueUtils.parseUnsigned;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.InstructionFactory;
+import com.android.tools.r8.graph.Descriptor;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
+import com.android.tools.r8.graph.DexDebugEvent;
+import com.android.tools.r8.graph.DexDebugEvent.SetEpilogueBegin;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexEncodedArray;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMemberAnnotation;
+import com.android.tools.r8.graph.DexMemberAnnotation.DexFieldAnnotation;
+import com.android.tools.r8.graph.DexMemberAnnotation.DexMethodAnnotation;
+import com.android.tools.r8.graph.DexMemberAnnotation.DexParameterAnnotation;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
+import com.android.tools.r8.graph.DexValue.DexValueMethodType;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.IntHashMap;
+import com.android.tools.r8.utils.InternalResource;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ShortBuffer;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class DexFileReader {
+
+ final int NO_INDEX = -1;
+ private DexFile file;
+ private final Segment[] segments;
+ private int[] stringIDs;
+ private final InternalResource.Kind fileKind;
+
+ public static Segment[] parseMapFrom(Path file) throws IOException {
+ DexFileReader reader =
+ new DexFileReader(
+ new DexFile(file.toString()), InternalResource.Kind.PROGRAM, new DexItemFactory());
+ return reader.parseMap();
+ }
+
+ public void close() {
+ // This close behavior is needed to reduce peak memory usage of D8/R8.
+ indexedItems = null;
+ codes = null;
+ offsetMap = null;
+ file = null;
+ stringIDs = null;
+ }
+
+ // Mapping from indexes to indexable dex items.
+ private OffsetToObjectMapping indexedItems = new OffsetToObjectMapping();
+
+ // Mapping from offset to code item;
+ private IntHashMap<DexCode> codes = new IntHashMap<>();
+
+ // Mapping from offset to dex item;
+ private IntHashMap<Object> offsetMap = new IntHashMap<>();
+
+ // Factory to canonicalize certain dexitems.
+ private final DexItemFactory dexItemFactory;
+
+ public DexFileReader(
+ DexFile file, InternalResource.Kind fileKind, DexItemFactory dexItemFactory) {
+ this.file = file;
+ this.dexItemFactory = dexItemFactory;
+ file.setByteOrder();
+ segments = parseMap();
+ parseStringIDs();
+ this.fileKind = fileKind;
+ }
+
+ public OffsetToObjectMapping getIndexedItemsMap() {
+ return indexedItems;
+ }
+
+ void addCodeItemsTo() {
+ if (fileKind == InternalResource.Kind.LIBRARY) {
+ // Ignore contents of library files.
+ return;
+ }
+ Segment segment = lookupSegment(Constants.TYPE_CODE_ITEM);
+ if (segment.size == 0) {
+ return;
+ }
+ file.position(segment.offset);
+ for (int i = 0; i < segment.size; i++) {
+ file.align(4); // code items are 4 byte aligned.
+ int offset = file.position();
+ DexCode code = parseCodeItem();
+ codes.put(offset, code); // Update the file local offset to code mapping.
+ }
+ }
+
+ private DexTypeList parseTypeList() {
+ DexType[] result = new DexType[file.getUint()];
+ for (int j = 0; j < result.length; j++) {
+ result[j] = indexedItems.getType(file.getUshort());
+ }
+ return new DexTypeList(result);
+ }
+
+ private DexTypeList typeListAt(int offset) {
+ if (offset == 0) return DexTypeList.empty();
+ return (DexTypeList) cacheAt(offset, this::parseTypeList);
+ }
+
+ public DexValue parseEncodedValue() {
+ int header = file.get() & 0xff;
+ int valueArg = header >> 5;
+ int valueType = header & 0x1f;
+ switch (valueType) {
+ case DexValue.VALUE_BYTE: {
+ assert valueArg == 0;
+ byte value = (byte) parseSigned(file, 1);
+ return DexValue.DexValueByte.create(value);
+ }
+ case DexValue.VALUE_SHORT: {
+ int size = valueArg + 1;
+ short value = (short) parseSigned(file, size);
+ return DexValue.DexValueShort.create(value);
+ }
+ case DexValue.VALUE_CHAR: {
+ int size = valueArg + 1;
+ char value = (char) parseUnsigned(file, size);
+ return DexValue.DexValueChar.create(value);
+ }
+ case DexValue.VALUE_INT: {
+ int size = valueArg + 1;
+ int value = (int) parseSigned(file, size);
+ return DexValue.DexValueInt.create(value);
+ }
+ case DexValue.VALUE_LONG: {
+ int size = valueArg + 1;
+ long value = parseSigned(file, size);
+ return DexValue.DexValueLong.create(value);
+ }
+ case DexValue.VALUE_FLOAT: {
+ int size = valueArg + 1;
+ return DexValue.DexValueFloat.create(parseFloat(file, size));
+ }
+ case DexValue.VALUE_DOUBLE: {
+ int size = valueArg + 1;
+ return DexValue.DexValueDouble.create(parseDouble(file, size));
+ }
+ case DexValue.VALUE_STRING: {
+ int size = valueArg + 1;
+ int index = (int) parseUnsigned(file, size);
+ DexString value = indexedItems.getString(index);
+ return new DexValue.DexValueString(value);
+ }
+ case DexValue.VALUE_TYPE: {
+ int size = valueArg + 1;
+ DexType value = indexedItems.getType((int) parseUnsigned(file, size));
+ return new DexValue.DexValueType(value);
+ }
+ case DexValue.VALUE_FIELD: {
+ int size = valueArg + 1;
+ DexField value = indexedItems.getField((int) parseUnsigned(file, size));
+ return new DexValue.DexValueField(value);
+ }
+ case DexValue.VALUE_METHOD: {
+ int size = valueArg + 1;
+ DexMethod value = indexedItems.getMethod((int) parseUnsigned(file, size));
+ return new DexValue.DexValueMethod(value);
+ }
+ case DexValue.VALUE_ENUM: {
+ int size = valueArg + 1;
+ DexField value = indexedItems.getField((int) parseUnsigned(file, size));
+ return new DexValue.DexValueEnum(value);
+ }
+ case DexValue.VALUE_ARRAY: {
+ assert valueArg == 0;
+ return new DexValue.DexValueArray(parseEncodedArrayValues());
+ }
+ case DexValue.VALUE_ANNOTATION: {
+ assert valueArg == 0;
+ return new DexValue.DexValueAnnotation(parseEncodedAnnotation());
+ }
+ case DexValue.VALUE_NULL: {
+ assert valueArg == 0;
+ return DexValue.NULL;
+ }
+ case DexValue.VALUE_BOOLEAN: {
+ // 0 is false, and 1 is true.
+ return DexValue.DexValueBoolean.create(valueArg != 0);
+ }
+ case DexValue.VALUE_METHOD_TYPE: {
+ int size = valueArg + 1;
+ DexProto value = indexedItems.getProto((int) parseUnsigned(file, size));
+ return new DexValue.DexValueMethodType(value);
+ }
+ case DexValue.VALUE_METHOD_HANDLE: {
+ int size = valueArg + 1;
+ DexMethodHandle value = indexedItems.getMethodHandle((int) parseUnsigned(file, size));
+ return new DexValue.DexValueMethodHandle(value);
+ }
+ default:
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ private DexEncodedAnnotation parseEncodedAnnotation() {
+ int typeIdx = file.getUleb128();
+ int size = file.getUleb128();
+ DexAnnotationElement[] elements = new DexAnnotationElement[size];
+ for (int i = 0; i < size; i++) {
+ int nameIdx = file.getUleb128();
+ DexValue value = parseEncodedValue();
+ elements[i] = new DexAnnotationElement(indexedItems.getString(nameIdx), value);
+ }
+ return new DexEncodedAnnotation(indexedItems.getType(typeIdx), elements);
+ }
+
+ private DexValue[] parseEncodedArrayValues() {
+ int size = file.getUleb128();
+ DexValue[] values = new DexValue[size];
+ for (int i = 0; i < size; i++) {
+ values[i] = parseEncodedValue();
+ }
+ return values;
+ }
+
+
+ private DexEncodedArray parseEncodedArray() {
+ return new DexEncodedArray(parseEncodedArrayValues());
+ }
+
+ private DexEncodedArray encodedArrayAt(int offset) {
+ return (DexEncodedArray) cacheAt(offset, this::parseEncodedArray);
+ }
+
+ private DexFieldAnnotation[] parseFieldAnnotations(int size) {
+ if (size == 0) {
+ return null;
+ }
+ int[] fieldIndices = new int[size];
+ int[] annotationOffsets = new int[size];
+ for (int i = 0; i < size; i++) {
+ fieldIndices[i] = file.getUint();
+ annotationOffsets[i] = file.getUint();
+ }
+ int saved = file.position();
+ DexFieldAnnotation[] result = new DexFieldAnnotation[size];
+ for (int i = 0; i < size; i++) {
+ DexField field = indexedItems.getField(fieldIndices[i]);
+ DexAnnotationSet annotation = annotationSetAt(annotationOffsets[i]);
+ result[i] = new DexFieldAnnotation(field, annotation);
+ }
+ file.position(saved);
+ return result;
+ }
+
+ private DexMethodAnnotation[] parseMethodAnnotations(int size) {
+ if (size == 0) {
+ return null;
+ }
+ int[] methodIndices = new int[size];
+ int[] annotationOffsets = new int[size];
+ for (int i = 0; i < size; i++) {
+ methodIndices[i] = file.getUint();
+ annotationOffsets[i] = file.getUint();
+ }
+ int saved = file.position();
+ DexMethodAnnotation[] result = new DexMethodAnnotation[size];
+ for (int i = 0; i < size; i++) {
+ DexMethod method = indexedItems.getMethod(methodIndices[i]);
+ DexAnnotationSet annotation = annotationSetAt(annotationOffsets[i]);
+ result[i] = new DexMethodAnnotation(method, annotation);
+ }
+ file.position(saved);
+ return result;
+ }
+
+ private DexAnnotationSetRefList annotationSetRefListAt(int offset) {
+ return (DexAnnotationSetRefList) cacheAt(offset, this::parseAnnotationSetRefList);
+ }
+
+ private DexAnnotationSetRefList parseAnnotationSetRefList() {
+ int size = file.getUint();
+ int[] annotationOffsets = new int[size];
+ for (int i = 0; i < size; i++) {
+ annotationOffsets[i] = file.getUint();
+ }
+ DexAnnotationSet[] values = new DexAnnotationSet[size];
+ for (int i = 0; i < size; i++) {
+ values[i] = annotationSetAt(annotationOffsets[i]);
+ }
+ return new DexAnnotationSetRefList(values);
+ }
+
+ private DexParameterAnnotation[] parseParameterAnnotations(int size) {
+ if (size == 0) {
+ return null;
+ }
+ int[] methodIndices = new int[size];
+ int[] annotationOffsets = new int[size];
+ for (int i = 0; i < size; i++) {
+ methodIndices[i] = file.getUint();
+ annotationOffsets[i] = file.getUint();
+ }
+ int saved = file.position();
+ DexParameterAnnotation[] result = new DexParameterAnnotation[size];
+ for (int i = 0; i < size; i++) {
+ DexMethod method = indexedItems.getMethod(methodIndices[i]);
+ result[i] = new DexParameterAnnotation(
+ method,
+ annotationSetRefListAt(annotationOffsets[i]));
+ }
+ file.position(saved);
+ return result;
+ }
+
+ private <T> Object cacheAt(int offset, Supplier<T> function, Supplier<T> defaultValue) {
+ if (offset == 0) {
+ return defaultValue.get();
+ }
+ return cacheAt(offset, function);
+ }
+
+ private <T> Object cacheAt(int offset, Supplier<T> function) {
+ if (offset == 0) return null; // return null for offset zero.
+ Object result = offsetMap.get(offset);
+ if (result != null) return result; // return the cached result.
+ // Cache is empty so parse the structure.
+ file.position(offset);
+ result = function.get();
+ // Update the map.
+ offsetMap.put(offset, result);
+ assert offsetMap.get(offset) == result;
+ return result;
+ }
+
+ private DexAnnotation parseAnnotation() {
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Reading Annotation @ 0x%08x.", file.position());
+ }
+ int visibility = file.get();
+ return new DexAnnotation(visibility, parseEncodedAnnotation());
+ }
+
+ private DexAnnotation annotationAt(int offset) {
+ return (DexAnnotation) cacheAt(offset, this::parseAnnotation);
+ }
+
+ private DexAnnotationSet parseAnnotationSet() {
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Reading AnnotationSet @ 0x%08x.", file.position());
+ }
+ int size = file.getUint();
+ int[] annotationOffsets = new int[size];
+ for (int i = 0; i < size; i++) {
+ annotationOffsets[i] = file.getUint();
+ }
+ DexAnnotation[] result = new DexAnnotation[size];
+ for (int i = 0; i < size; i++) {
+ result[i] = annotationAt(annotationOffsets[i]);
+ }
+ return new DexAnnotationSet(result);
+ }
+
+ private DexAnnotationSet annotationSetAt(int offset) {
+ return (DexAnnotationSet) cacheAt(offset, this::parseAnnotationSet, DexAnnotationSet::empty);
+ }
+
+ private AnnotationsDirectory annotationsDirectoryAt(int offset) {
+ return (AnnotationsDirectory) cacheAt(offset, this::parseAnnotationsDirectory,
+ AnnotationsDirectory::empty);
+ }
+
+ private AnnotationsDirectory parseAnnotationsDirectory() {
+ int classAnnotationsOff = file.getUint();
+ int fieldsSize = file.getUint();
+ int methodsSize = file.getUint();
+ int parametersSize = file.getUint();
+ final DexFieldAnnotation[] fields = parseFieldAnnotations(fieldsSize);
+ final DexMethodAnnotation[] methods = parseMethodAnnotations(methodsSize);
+ final DexParameterAnnotation[] parameters = parseParameterAnnotations(parametersSize);
+ return new AnnotationsDirectory(
+ annotationSetAt(classAnnotationsOff),
+ fields,
+ methods,
+ parameters);
+ }
+
+ private DexDebugInfo debugInfoAt(int offset) {
+ return (DexDebugInfo) cacheAt(offset, this::parseDebugInfo);
+ }
+
+ private DexDebugInfo parseDebugInfo() {
+ int start = file.getUleb128();
+ int parametersSize = file.getUleb128();
+ DexString[] parameters = new DexString[parametersSize];
+ for (int i = 0; i < parametersSize; i++) {
+ int index = file.getUleb128p1();
+ if (index != NO_INDEX) {
+ parameters[i] = indexedItems.getString(index);
+ }
+ }
+ List<DexDebugEvent> events = new ArrayList<>();
+ for (int head = file.getUbyte(); head != Constants.DBG_END_SEQUENCE; head = file.getUbyte()) {
+ switch (head) {
+ case Constants.DBG_ADVANCE_PC:
+ events.add(new DexDebugEvent.AdvancePC(file.getUleb128()));
+ break;
+ case Constants.DBG_ADVANCE_LINE:
+ events.add(new DexDebugEvent.AdvanceLine(file.getSleb128()));
+ break;
+ case Constants.DBG_START_LOCAL: {
+ int registerNum = file.getUleb128();
+ int nameIdx = file.getUleb128p1();
+ int typeIdx = file.getUleb128p1();
+ events.add(new DexDebugEvent.StartLocal(
+ registerNum,
+ nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx),
+ typeIdx == NO_INDEX ? null : indexedItems.getType(typeIdx),
+ null));
+ break;
+ }
+ case Constants.DBG_START_LOCAL_EXTENDED: {
+ int registerNum = file.getUleb128();
+ int nameIdx = file.getUleb128p1();
+ int typeIdx = file.getUleb128p1();
+ int sigIdx = file.getUleb128p1();
+ events.add(new DexDebugEvent.StartLocal(
+ registerNum,
+ nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx),
+ typeIdx == NO_INDEX ? null : indexedItems.getType(typeIdx),
+ sigIdx == NO_INDEX ? null : indexedItems.getString(sigIdx)));
+ break;
+ }
+ case Constants.DBG_END_LOCAL: {
+ events.add(new DexDebugEvent.EndLocal(file.getUleb128()));
+ break;
+ }
+ case Constants.DBG_RESTART_LOCAL: {
+ events.add(new DexDebugEvent.RestartLocal(file.getUleb128()));
+ break;
+ }
+ case Constants.DBG_SET_PROLOGUE_END: {
+ events.add(new DexDebugEvent.SetPrologueEnd());
+ break;
+ }
+ case Constants.DBG_SET_EPILOGUE_BEGIN: {
+ events.add(new SetEpilogueBegin());
+ break;
+ }
+ case Constants.DBG_SET_FILE: {
+ int nameIdx = file.getUleb128p1();
+ DexString sourceFile = nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx);
+ events.add(new DexDebugEvent.SetFile(sourceFile));
+ break;
+ }
+ default: {
+ assert head >= 0x0a && head <= 0xff;
+ events.add(new DexDebugEvent.Default(head));
+ }
+ }
+ }
+ return new DexDebugInfo(start, parameters, events.toArray(new DexDebugEvent[events.size()]));
+ }
+
+ private static class MemberAnnotationIterator<S extends Descriptor<?, S>, T extends DexItem> {
+
+ private int index = 0;
+ private final DexMemberAnnotation<S, T>[] annotations;
+ private final Supplier<T> emptyValue;
+
+ private MemberAnnotationIterator(DexMemberAnnotation<S, T>[] annotations,
+ Supplier<T> emptyValue) {
+ this.annotations = annotations;
+ this.emptyValue = emptyValue;
+ }
+
+ // Get the annotation set for an item. This method assumes that it is always called with
+ // an item that is higher in the sorting order than the last item.
+ T getNextFor(S item) {
+ // TODO(ager): We could use the indices from the file to make this search faster using
+ // compareTo instead of slowCompareTo. That would require us to assign indices during
+ // reading. Those indices should be cleared after reading to make sure that we resort
+ // everything correctly at the end.
+ while (index < annotations.length && annotations[index].item.slowCompareTo(item) < 0) {
+ index++;
+ }
+ if (index >= annotations.length || !annotations[index].item.equals(item)) {
+ return emptyValue.get();
+ }
+ return annotations[index].annotations;
+ }
+ }
+
+ private DexEncodedField[] readFields(int size, DexFieldAnnotation[] annotations,
+ DexValue[] staticValues) {
+ DexEncodedField[] fields = new DexEncodedField[size];
+ int fieldIndex = 0;
+ MemberAnnotationIterator<DexField, DexAnnotationSet> annotationIterator =
+ new MemberAnnotationIterator<>(annotations, DexAnnotationSet::empty);
+ for (int i = 0; i < size; i++) {
+ fieldIndex += file.getUleb128();
+ DexField field = indexedItems.getField(fieldIndex);
+ DexAccessFlags accessFlags = new DexAccessFlags(file.getUleb128());
+ DexAnnotationSet fieldAnnotations = annotationIterator.getNextFor(field);
+ DexValue staticValue = null;
+ if (accessFlags.isStatic()) {
+ if (staticValues != null && i < staticValues.length) {
+ staticValue = staticValues[i];
+ } else {
+ staticValue = DexValue.defaultForType(field.type, dexItemFactory);
+ }
+ }
+ fields[i] = new DexEncodedField(field, accessFlags, fieldAnnotations, staticValue);
+ }
+ return fields;
+ }
+
+ private DexEncodedMethod[] readMethods(int size, DexMethodAnnotation[] annotations,
+ DexParameterAnnotation[] parameters, boolean skipCodes) {
+ DexEncodedMethod[] methods = new DexEncodedMethod[size];
+ int methodIndex = 0;
+ MemberAnnotationIterator<DexMethod, DexAnnotationSet> annotationIterator =
+ new MemberAnnotationIterator<>(annotations, DexAnnotationSet::empty);
+ MemberAnnotationIterator<DexMethod, DexAnnotationSetRefList> parameterAnnotationsIterator =
+ new MemberAnnotationIterator<>(parameters, DexAnnotationSetRefList::empty);
+ for (int i = 0; i < size; i++) {
+ methodIndex += file.getUleb128();
+ DexAccessFlags accessFlags = new DexAccessFlags(file.getUleb128());
+ int codeOff = file.getUleb128();
+ DexCode code = null;
+ if (!skipCodes) {
+ assert codeOff == 0 || codes.get(codeOff) != null;
+ code = codes.get(codeOff);
+ }
+ DexMethod method = indexedItems.getMethod(methodIndex);
+ methods[i] = new DexEncodedMethod(method, accessFlags, annotationIterator.getNextFor(method),
+ parameterAnnotationsIterator.getNextFor(method), code);
+ }
+ return methods;
+ }
+
+ void addClassDefsTo(Consumer<DexClass> classCollection) {
+ final Segment segment = lookupSegment(Constants.TYPE_CLASS_DEF_ITEM);
+ final int size = segment.size;
+ indexedItems.initializeClasses(size);
+ if (size == 0) {
+ return;
+ }
+ file.position(segment.offset);
+
+ int[] classIndices = new int[size];
+ int[] accessFlags = new int[size];
+ int[] superclassIndices = new int[size];
+ int[] interfacesOffsets = new int[size];
+ int[] sourceFileIndices = new int[size];
+ int[] annotationsOffsets = new int[size];
+ int[] classDataOffsets = new int[size];
+ int[] staticValuesOffsets = new int[size];
+
+ for (int i = 0; i < size; i++) {
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Reading ClassDef @ 0x%08x.", file.position());
+ }
+ classIndices[i] = file.getUint();
+ accessFlags[i] = file.getUint();
+ superclassIndices[i] = file.getInt();
+ interfacesOffsets[i] = file.getUint();
+ sourceFileIndices[i] = file.getInt();
+ annotationsOffsets[i] = file.getUint();
+ classDataOffsets[i] = file.getUint();
+ staticValuesOffsets[i] = file.getUint();
+ }
+
+ for (int i = 0; i < size; i++) {
+ int superclassIdx = superclassIndices[i];
+ DexType superclass = superclassIdx == NO_INDEX ? null : indexedItems.getType(superclassIdx);
+ int srcIdx = sourceFileIndices[i];
+ DexString source = srcIdx == NO_INDEX ? null : indexedItems.getString(srcIdx);
+ // fix annotations.
+ DexType type = indexedItems.getType(classIndices[i]);
+ DexAccessFlags flags = new DexAccessFlags(accessFlags[i]);
+ DexClass clazz;
+ DexEncodedField[] staticFields = DexEncodedField.EMPTY_ARRAY;
+ DexEncodedField[] instanceFields = DexEncodedField.EMPTY_ARRAY;
+ DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
+ DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
+ AnnotationsDirectory annotationsDirectory = annotationsDirectoryAt(annotationsOffsets[i]);
+ if (classDataOffsets[i] != 0) {
+ DexEncodedArray staticValues = encodedArrayAt(staticValuesOffsets[i]);
+
+ file.position(classDataOffsets[i]);
+ int staticFieldsSize = file.getUleb128();
+ int instanceFieldsSize = file.getUleb128();
+ int directMethodsSize = file.getUleb128();
+ int virtualMethodsSize = file.getUleb128();
+
+ staticFields = readFields(staticFieldsSize, annotationsDirectory.fields,
+ staticValues != null ? staticValues.values : null);
+ instanceFields = readFields(instanceFieldsSize, annotationsDirectory.fields, null);
+ directMethods =
+ readMethods(
+ directMethodsSize,
+ annotationsDirectory.methods,
+ annotationsDirectory.parameters,
+ fileKind != InternalResource.Kind.PROGRAM);
+ virtualMethods =
+ readMethods(
+ virtualMethodsSize,
+ annotationsDirectory.methods,
+ annotationsDirectory.parameters,
+ fileKind != InternalResource.Kind.PROGRAM);
+ }
+ clazz = DexClass.factoryForResourceKind(fileKind).create(
+ type,
+ DexClass.Origin.Dex,
+ flags,
+ superclass,
+ typeListAt(interfacesOffsets[i]),
+ source,
+ annotationsDirectory.clazz,
+ staticFields,
+ instanceFields,
+ directMethods,
+ virtualMethods);
+ classCollection.accept(clazz); // Update the application object.
+ }
+ }
+
+ private void parseStringIDs() {
+ Segment segment = lookupSegment(Constants.TYPE_STRING_ID_ITEM);
+ stringIDs = new int[segment.size];
+ if (segment.size == 0) {
+ return;
+ }
+ file.position(segment.offset);
+ for (int i = 0; i < segment.size; i++) {
+ stringIDs[i] = file.getUint();
+ }
+ }
+
+ private Segment lookupSegment(int type) {
+ for (Segment s : segments) {
+ if (s.type == type) {
+ return s;
+ }
+ }
+ // If the segment doesn't exist, return an empty segment of this type.
+ return new Segment(type, 0, 0, 0);
+ }
+
+ private Segment[] parseMap() {
+ // Read the segments information from the MAP.
+ int mapOffset = file.getUint(Constants.MAP_OFF_OFFSET);
+ file.position(mapOffset);
+ int mapSize = file.getUint();
+ final Segment[] result = new Segment[mapSize];
+ for (int i = 0; i < mapSize; i++) {
+ int type = file.getUshort();
+ int unused = file.getUshort();
+ int size = file.getUint();
+ int offset = file.getUint();
+ result[i] = new Segment(type, unused, size, offset);
+ }
+ if (Log.ENABLED) {
+ for (int i = 0; i < result.length; i++) {
+ Segment segment = result[i];
+ int nextOffset = i < result.length - 1 ? result[i + 1].offset : segment.offset;
+ Log.debug(this.getClass(), "Read segment 0x%04x @ 0x%08x #items %08d size 0x%08x.",
+ segment.type, segment.offset, segment.size, nextOffset - segment.offset);
+ }
+ }
+ for (int i = 0; i < mapSize - 1; i++) {
+ result[i].setEnd(result[i + 1].offset);
+ }
+ result[mapSize - 1].setEnd(file.end());
+ return result;
+ }
+
+ private DexCode parseCodeItem() {
+ int registerSize = file.getUshort();
+ int insSize = file.getUshort();
+ int outsSize = file.getUshort();
+ int triesSize = file.getUshort();
+ int debugInfoOff = file.getUint();
+ int insnsSize = file.getUint();
+ short[] code = new short[insnsSize];
+ Try[] tries = new Try[triesSize];
+ DexCode.TryHandler[] handlers = null;
+
+ if (insnsSize != 0) {
+ for (int i = 0; i < insnsSize; i++) {
+ code[i] = file.getShort();
+ }
+ if (insnsSize % 2 != 0) {
+ file.getUshort(); // Skip padding ushort
+ }
+ if (triesSize > 0) {
+ Hashtable<Integer, Integer> handlerMap = new Hashtable<>();
+ // tries: try_item[tries_size].
+ for (int i = 0; i < triesSize; i++) {
+ int startAddr = file.getUint();
+ int insnCount = file.getUshort();
+ int handlerOff = file.getUshort();
+ tries[i] = new Try(startAddr, insnCount, handlerOff);
+ }
+ // handlers: encoded_catch_handler_list
+ int encodedCatchHandlerListPosition = file.position();
+ // - size: uleb128
+ int size = file.getUleb128();
+ handlers = new TryHandler[size];
+ // - list: encoded_catch_handler[handlers_size]
+ for (int i = 0; i < size; i++) {
+ // encoded_catch_handler
+ int encodedCatchHandlerOffset = file.position() - encodedCatchHandlerListPosition;
+ handlerMap.put(encodedCatchHandlerOffset, i);
+ // - size: sleb128
+ int hsize = file.getSleb128();
+ int realHsize = Math.abs(hsize);
+ // - handlers encoded_type_addr_pair[abs(size)]
+ TryHandler.TypeAddrPair pairs[] = new TryHandler.TypeAddrPair[realHsize];
+ for (int j = 0; j < realHsize; j++) {
+ int typeIdx = file.getUleb128();
+ int addr = file.getUleb128();
+ pairs[j] = new TypeAddrPair(indexedItems.getType(typeIdx), addr,
+ encodedCatchHandlerOffset);
+ }
+ int catchAllAddr = -1;
+ if (hsize <= 0) {
+ catchAllAddr = file.getUleb128();
+ }
+ handlers[i] = new TryHandler(pairs, catchAllAddr);
+ }
+ // Convert the handler offsets inside the Try objects to indexes.
+ for (Try t : tries) {
+ t.setHandlerIndex(handlerMap);
+ }
+ }
+ }
+ // Store and restore offset information around reading debug info.
+ int saved = file.position();
+ DexDebugInfo debugInfo = debugInfoAt(debugInfoOff);
+ file.position(saved);
+ InstructionFactory factory = new InstructionFactory();
+ Instruction[] instructions =
+ factory.readSequenceFrom(ShortBuffer.wrap(code), 0, code.length, indexedItems);
+ return new DexCode(
+ registerSize,
+ insSize,
+ outsSize,
+ instructions,
+ tries,
+ handlers,
+ debugInfo,
+ factory.getHighestSortingString());
+ }
+
+ static void populateIndexTables(DexFileReader fileReader) {
+ // Populate structures that are already sorted upon read.
+ DexFileReader.populateStrings(fileReader); // Depends on nothing.
+ DexFileReader.populateTypes(fileReader); // Depends on Strings.
+ DexFileReader.populateFields(fileReader); // Depends on Types, and Strings.
+ DexFileReader.populateProtos(fileReader); // Depends on Types and Strings.
+ DexFileReader.populateMethods(fileReader); // Depends on Protos, Types, and Strings.
+ DexFileReader.populateMethodHandles(fileReader); // Depends on Methods and Fields
+ DexFileReader.populateCallSites(fileReader); // Depends on MethodHandles
+ }
+
+ private static void populateStrings(DexFileReader reader) {
+ reader.indexedItems.initializeStrings(reader.stringIDs.length);
+ for (int i = 0; i < reader.stringIDs.length; i++) {
+ reader.indexedItems.setString(i, reader.stringAt(i));
+ }
+ }
+
+ private static void populateMethodHandles(DexFileReader reader) {
+ Segment segment = reader.lookupSegment(Constants.TYPE_METHOD_HANDLE_ITEM);
+ reader.indexedItems.initializeMethodHandles(segment.size);
+ for (int i = 0; i < segment.size; i++){
+ reader.indexedItems.setMethodHandle(i, reader.methodHandleAt(i));
+ }
+ }
+
+ private static void populateCallSites(DexFileReader reader) {
+ Segment segment = reader.lookupSegment(Constants.TYPE_CALL_SITE_ID_ITEM);
+ reader.indexedItems.initializeCallSites(segment.size);
+ for (int i = 0; i < segment.size; i++){
+ reader.indexedItems.setCallSites(i, reader.callSiteAt(i));
+ }
+ }
+
+ private static void populateTypes(DexFileReader reader) {
+ Segment segment = reader.lookupSegment(Constants.TYPE_TYPE_ID_ITEM);
+ reader.indexedItems.initializeTypes(segment.size);
+ for (int i = 0; i < segment.size; i++){
+ reader.indexedItems.setType(i, reader.typeAt(i));
+ }
+ }
+
+ private static void populateFields(DexFileReader reader) {
+ Segment segment = reader.lookupSegment(Constants.TYPE_FIELD_ID_ITEM);
+ reader.indexedItems.initializeFields(segment.size);
+ for (int i = 0; i < segment.size; i++){
+ reader.indexedItems.setField(i, reader.fieldAt(i));
+ }
+ }
+
+ private static void populateProtos(DexFileReader reader) {
+ Segment segment = reader.lookupSegment(Constants.TYPE_PROTO_ID_ITEM);
+ reader.indexedItems.initializeProtos(segment.size);
+ for (int i = 0; i < segment.size; i++){
+ reader.indexedItems.setProto(i, reader.protoAt(i));
+ }
+ }
+
+ private static void populateMethods(DexFileReader reader) {
+ Segment segment = reader.lookupSegment(Constants.TYPE_METHOD_ID_ITEM);
+ reader.indexedItems.initializeMethods(segment.size);
+ for (int i = 0; i < segment.size; i++){
+ reader.indexedItems.setMethod(i, reader.methodAt(i));
+ }
+ }
+
+ private DexString stringAt(int index) {
+ final int offset = stringIDs[index];
+ file.position(offset);
+ int size = file.getUleb128();
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ byte read;
+ do {
+ read = file.get();
+ os.write(read);
+ } while (read != 0);
+ return dexItemFactory.createString(size, os.toByteArray());
+ }
+
+ private DexType typeAt(int index) {
+ Segment segment = lookupSegment(Constants.TYPE_TYPE_ID_ITEM);
+ if (index >= segment.size) {
+ return null;
+ }
+ int offset = segment.offset + (Constants.TYPE_TYPE_ID_ITEM_SIZE * index);
+ int stringIndex = file.getUint(offset);
+ return dexItemFactory.createType(indexedItems.getString(stringIndex));
+ }
+
+ private DexField fieldAt(int index) {
+ Segment segment = lookupSegment(Constants.TYPE_FIELD_ID_ITEM);
+ if (index >= segment.size) {
+ return null;
+ }
+ int offset = segment.offset + (Constants.TYPE_FIELD_ID_ITEM_SIZE * index);
+ file.position(offset);
+ int classIndex = file.getUshort();
+ int typeIndex = file.getUshort();
+ int nameIndex = file.getUint();
+ DexType clazz = indexedItems.getType(classIndex);
+ DexType type = indexedItems.getType(typeIndex);
+ DexString name = indexedItems.getString(nameIndex);
+ return dexItemFactory.createField(clazz, type, name);
+ }
+
+ private DexMethodHandle methodHandleAt(int index) {
+ Segment segment = lookupSegment(Constants.TYPE_METHOD_HANDLE_ITEM);
+ if (index >= segment.size) {
+ return null;
+ }
+ int offset = segment.offset + (Constants.TYPE_METHOD_HANDLE_ITEM_SIZE * index);
+ file.position(offset);
+ MethodHandleType type = MethodHandleType.getKind(file.getUshort());
+ file.getUshort(); // unused
+ int indexFieldOrMethod = file.getUshort();
+ Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod;
+ switch (type) {
+ case INSTANCE_GET:
+ case INSTANCE_PUT:
+ case STATIC_GET:
+ case STATIC_PUT: {
+ fieldOrMethod = indexedItems.getField(indexFieldOrMethod);
+ break;
+ }
+ case INVOKE_INSTANCE:
+ case INVOKE_STATIC: {
+ fieldOrMethod = indexedItems.getMethod(indexFieldOrMethod);
+ break;
+ }
+ default:
+ throw new AssertionError("Method handle type unsupported in a dex file.");
+ }
+ file.getUshort(); // unused
+
+ return dexItemFactory.createMethodHandle(type, fieldOrMethod);
+ }
+
+ private DexCallSite callSiteAt(int index) {
+ Segment segment = lookupSegment(Constants.TYPE_CALL_SITE_ID_ITEM);
+ if (index >= segment.size) {
+ return null;
+ }
+ int callSiteOffset =
+ file.getUint(segment.offset + (Constants.TYPE_CALL_SITE_ID_ITEM_SIZE * index));
+ DexEncodedArray callSiteEncodedArray = encodedArrayAt(callSiteOffset);
+ DexValue[] values = callSiteEncodedArray.values;
+ assert values[0] instanceof DexValueMethodHandle;
+ assert values[1] instanceof DexValueString;
+ assert values[2] instanceof DexValueMethodType;
+
+ return dexItemFactory.createCallSite(
+ ((DexValueString) values[1]).value,
+ ((DexValueMethodType) values[2]).value,
+ ((DexValueMethodHandle) values[0]).value,
+ // 3 means first extra args
+ Arrays.asList(Arrays.copyOfRange(values, 3, values.length)));
+ }
+
+ private DexProto protoAt(int index) {
+ Segment segment = lookupSegment(Constants.TYPE_PROTO_ID_ITEM);
+ if (index >= segment.size) {
+ return null;
+ }
+ int offset = segment.offset + (Constants.TYPE_PROTO_ID_ITEM_SIZE * index);
+ file.position(offset);
+ int shortyIndex = file.getUint();
+ int returnTypeIndex = file.getUint();
+ int parametersOffsetIndex = file.getUint();
+ DexString shorty = indexedItems.getString(shortyIndex);
+ DexType returnType = indexedItems.getType(returnTypeIndex);
+ DexTypeList parameters = typeListAt(parametersOffsetIndex);
+ return dexItemFactory.createProto(shorty, returnType, parameters);
+ }
+
+ private DexMethod methodAt(int index) {
+ Segment segment = lookupSegment(Constants.TYPE_METHOD_ID_ITEM);
+ if (index >= segment.size) {
+ return null;
+ }
+ int offset = segment.offset + (Constants.TYPE_METHOD_ID_ITEM_SIZE * index);
+ file.position(offset);
+ int classIndex = file.getUshort();
+ int protoIndex = file.getUshort();
+ int nameIndex = file.getUint();
+ return dexItemFactory.createMethod(
+ indexedItems.getType(classIndex),
+ indexedItems.getProto(protoIndex),
+ indexedItems.getString(nameIndex));
+ }
+
+ private static class AnnotationsDirectory {
+
+ private static final DexParameterAnnotation[] NO_PARAMETER_ANNOTATIONS =
+ new DexParameterAnnotation[0];
+
+ private static final DexFieldAnnotation[] NO_FIELD_ANNOTATIONS =
+ new DexFieldAnnotation[0];
+
+ private static final DexMethodAnnotation[] NO_METHOD_ANNOTATIONS =
+ new DexMethodAnnotation[0];
+
+ private static final AnnotationsDirectory THE_EMPTY_ANNOTATIONS_DIRECTORY =
+ new AnnotationsDirectory(DexAnnotationSet.empty(),
+ NO_FIELD_ANNOTATIONS, new DexMethodAnnotation[0],
+ NO_PARAMETER_ANNOTATIONS);
+
+ public final DexAnnotationSet clazz;
+ public final DexFieldAnnotation[] fields;
+ public final DexMethodAnnotation[] methods;
+ public final DexParameterAnnotation[] parameters;
+
+ AnnotationsDirectory(DexAnnotationSet clazz,
+ DexFieldAnnotation[] fields,
+ DexMethodAnnotation[] methods,
+ DexParameterAnnotation[] parameters) {
+ this.clazz = clazz == null ? DexAnnotationSet.empty() : clazz;
+ this.fields = fields == null ? NO_FIELD_ANNOTATIONS : fields;
+ this.methods = methods == null ? NO_METHOD_ANNOTATIONS : methods;
+ this.parameters = parameters == null ? NO_PARAMETER_ANNOTATIONS : parameters;
+ }
+
+ public static AnnotationsDirectory empty() {
+ return THE_EMPTY_ANNOTATIONS_DIRECTORY;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
new file mode 100644
index 0000000..fac5e53
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
@@ -0,0 +1,133 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.utils.EncodedValueUtils;
+import com.android.tools.r8.utils.LebUtils;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.ShortBuffer;
+
+/**
+ * Provides an abstraction around a {@link ByteBuffer} with write operations for
+ * additional DEX specific formats, like Leb128.
+ */
+public class DexOutputBuffer {
+ private static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
+
+ private ByteBuffer byteBuffer;
+
+ public DexOutputBuffer() {
+ byteBuffer = allocate(DEFAULT_BUFFER_SIZE);
+ }
+
+ private void ensureSpaceFor(int bytes) {
+ if (byteBuffer.remaining() < bytes) {
+ int newSize = byteBuffer.capacity() + Math.max(byteBuffer.capacity(), bytes * 2);
+ ByteBuffer newBuffer = allocate(newSize);
+ System.arraycopy(byteBuffer.array(), 0, newBuffer.array(), 0, byteBuffer.position());
+ newBuffer.position(byteBuffer.position());
+ byteBuffer = newBuffer;
+ }
+ }
+
+ private ByteBuffer allocate(int size) {
+ ByteBuffer buffer = ByteBuffer.allocate(size);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ return buffer;
+ }
+
+ public void putUleb128(int value) {
+ LebUtils.putUleb128(this, value);
+ }
+
+ public void putSleb128(int value) {
+ LebUtils.putSleb128(this, value);
+ }
+
+ public int putSignedEncodedValue(long value, int expectedSize) {
+ return EncodedValueUtils.putSigned(this, value, expectedSize);
+ }
+
+ public int putUnsignedEncodedValue(long value, int expectedSize) {
+ return EncodedValueUtils.putUnsigned(this, value, expectedSize);
+ }
+
+ public void putInstructions(Instruction[] insns, ObjectToOffsetMapping mapping) {
+ int size = 0;
+ for (Instruction insn : insns) {
+ size += insn.getSize();
+ }
+ ensureSpaceFor(size * Short.BYTES);
+ assert byteBuffer.position() % 2 == 0;
+ ShortBuffer shortBuffer = byteBuffer.asShortBuffer();
+ for (int i = 0; i < insns.length; i++) {
+ insns[i].write(shortBuffer, mapping);
+ }
+ byteBuffer.position(byteBuffer.position() + shortBuffer.position() * Short.BYTES);
+ }
+
+ public void putByte(byte aByte) {
+ ensureSpaceFor(Byte.BYTES);
+ byteBuffer.put(aByte);
+ }
+
+ public void putBytes(byte[] bytes) {
+ ensureSpaceFor(bytes.length);
+ byteBuffer.put(bytes);
+ }
+
+ public void putShort(short aShort) {
+ ensureSpaceFor(Short.BYTES);
+ byteBuffer.putShort(aShort);
+ }
+
+ public void putInt(int anInteger) {
+ ensureSpaceFor(Integer.BYTES);
+ byteBuffer.putInt(anInteger);
+ }
+
+ /**
+ * Moves the position in the bytebuffer forward until it is aligned.
+ *
+ * @param bytes alignment requirement in bytes
+ * @return the new position after alignment
+ */
+ public int align(int bytes) {
+ assert bytes > 0;
+ int mask = bytes - 1;
+ int newPosition = (byteBuffer.position() + mask) & ~mask;
+ ensureSpaceFor(newPosition - position());
+ byteBuffer.position(newPosition);
+ return newPosition;
+ }
+
+ public int position() {
+ return byteBuffer.position();
+ }
+
+ public void forward(int bytes) {
+ ensureSpaceFor(bytes);
+ byteBuffer.position(byteBuffer.position() + bytes);
+ }
+
+ public void rewind(int bytes) {
+ forward(-bytes);
+ }
+
+ public void moveTo(int position) {
+ ensureSpaceFor(position - byteBuffer.position());
+ byteBuffer.position(position);
+ }
+
+ public boolean isAligned(int bytes) {
+ return position() % bytes == 0;
+ }
+
+ public byte[] asArray() {
+ return byteBuffer.array();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
new file mode 100644
index 0000000..33cea6d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -0,0 +1,1345 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import static com.android.tools.r8.utils.LebUtils.sizeAsUleb128;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.Descriptor;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexEncodedArray;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.KeyedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.PresortedComparable;
+import com.android.tools.r8.graph.ProgramClassVisitor;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.LebUtils;
+import com.android.tools.r8.utils.OrderedMergingIterator;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.ToIntFunction;
+import java.util.zip.Adler32;
+
+public class FileWriter {
+
+ private final ObjectToOffsetMapping mapping;
+ private final DexApplication application;
+ private final AppInfo appInfo;
+ private final InternalOptions options;
+ private final NamingLens namingLens;
+ private final DexOutputBuffer dest = new DexOutputBuffer();
+ private final MixedSectionOffsets mixedSectionOffsets;
+ private int numberOfAnnotationDirectories;
+
+ public FileWriter(
+ ObjectToOffsetMapping mapping,
+ DexApplication application,
+ AppInfo appinfo,
+ InternalOptions options,
+ NamingLens namingLens) {
+ this.mapping = mapping;
+ this.application = application;
+ this.appInfo = appinfo;
+ this.options = options;
+ this.namingLens = namingLens;
+ this.mixedSectionOffsets = new MixedSectionOffsets();
+ }
+
+ public static void writeEncodedAnnotation(DexEncodedAnnotation annotation, DexOutputBuffer dest,
+ ObjectToOffsetMapping mapping) {
+ if (Log.ENABLED) {
+ Log.verbose(FileWriter.class, "Writing encoded annotation @ %08x", dest.position());
+ }
+ dest.putUleb128(mapping.getOffsetFor(annotation.type));
+ dest.putUleb128(annotation.elements.length);
+ assert isSorted(annotation.elements, (element) -> element.name);
+ for (DexAnnotationElement element : annotation.elements) {
+ dest.putUleb128(mapping.getOffsetFor(element.name));
+ element.value.writeTo(dest, mapping);
+ }
+ }
+
+ private static <T extends PresortedComparable<T>> boolean isSorted(KeyedDexItem<T>[] items) {
+ return isSorted(items, KeyedDexItem::getKey);
+ }
+
+ private static <S, T extends Comparable<T>> boolean isSorted(S[] items, Function<S, T> getter) {
+ T current = null;
+ for (S item : items) {
+ T next = getter.apply(item);
+ if (current != null && current.compareTo(next) >= 0) {
+ return false;
+ }
+ current = next;
+ }
+ return true;
+ }
+
+ public FileWriter collect() {
+ // Use the class array from the mapping, as it has a deterministic iteration order.
+ new ProgramClassDependencyCollector(application, mapping.getClasses()).run(mapping.getClasses());
+
+ // Sort the class members.
+ // Needed before adding static-value arrays and writing annotation directories and classes.
+ sortClassData(mixedSectionOffsets.getClassesWithData());
+
+ // Add the static values for all fields now that we have committed to their sorting.
+ mixedSectionOffsets.getClassesWithData().forEach(this::addStaticFieldValues);
+
+ // String data is not tracked by the MixedSectionCollection.new AppInfo(application, null)
+ assert mixedSectionOffsets.stringData.size() == 0;
+ for (DexString string : mapping.getStrings()) {
+ mixedSectionOffsets.add(string);
+ }
+ // Neither are the typelists in protos...
+ for (DexProto proto : mapping.getProtos()) {
+ mixedSectionOffsets.add(proto.parameters);
+ }
+
+ DexItem.collectAll(mixedSectionOffsets, mapping.getCallSites());
+
+ return this;
+ }
+
+ private void rewriteCodeWithJumboStrings(IRConverter converter, DexEncodedMethod[] methods) {
+ for (int i = 0; i < methods.length; i++) {
+ DexEncodedMethod method = methods[i];
+ if (method.getCode() == null) {
+ continue;
+ }
+ DexCode code = method.getCode().asDexCode();
+ if (code.highestSortingString != null) {
+ if (mapping.getOffsetFor(code.highestSortingString) > Constants.MAX_NON_JUMBO_INDEX) {
+ converter.processJumboStrings(method, mapping.getFirstJumboString());
+ }
+ }
+ }
+ }
+
+ public FileWriter rewriteCodeWithJumboStrings(List<DexProgramClass> classes) {
+ // If there are no strings with jumbo indices at all this is a no-op.
+ if (!mapping.hasJumboStrings()) {
+ return this;
+ }
+ // If the globally highest sorting string is not a jumbo string this is also a no-op.
+ if (application.highestSortingString != null &&
+ application.highestSortingString.slowCompareTo(mapping.getFirstJumboString()) < 0) {
+ return this;
+ }
+ // At least one method needs a jumbo string.
+ IRConverter converter = new IRConverter(application, appInfo, options, null, false);
+ for (DexProgramClass clazz : classes) {
+ rewriteCodeWithJumboStrings(converter, clazz.directMethods());
+ rewriteCodeWithJumboStrings(converter, clazz.virtualMethods());
+ }
+ return this;
+ }
+
+ public byte[] generate() {
+ // Check restrictions on interface methods.
+ checkInterfaceMethods();
+
+ Layout layout = Layout.from(mapping);
+ layout.setCodesOffset(layout.dataSectionOffset);
+
+ // Sort the codes first, as their order might impact size due to alignment constraints.
+ List<DexCode> codes = sortDexCodesByClassName(mixedSectionOffsets.getCodes(), application);
+
+ // Output the debug_info_items first, as they have no dependencies.
+ dest.moveTo(layout.getCodesOffset() + sizeOfCodeItems(codes));
+ writeItems(mixedSectionOffsets.getDebugInfos(), layout::setDebugInfosOffset,
+ this::writeDebugItem);
+
+ // Remember the typelist offset for later.
+ layout.setTypeListsOffset(dest.align(4)); // type_list are aligned.
+
+ // Now output the code.
+ dest.moveTo(layout.getCodesOffset());
+ assert dest.isAligned(4);
+ writeItems(codes, layout::alreadySetOffset, this::writeCodeItem, 4);
+ assert layout.getDebugInfosOffset() == 0 || dest.position() == layout.getDebugInfosOffset();
+
+ // Now the type lists and rest.
+ dest.moveTo(layout.getTypeListsOffset());
+ writeItems(mixedSectionOffsets.getTypeLists(), layout::alreadySetOffset, this::writeTypeList);
+ writeItems(mixedSectionOffsets.getStringData(), layout::setStringDataOffsets,
+ this::writeStringData);
+ writeItems(mixedSectionOffsets.getAnnotations(), layout::setAnnotationsOffset,
+ this::writeAnnotation);
+ writeItems(mixedSectionOffsets.getAnnotationSets(), layout::setAnnotationSetsOffset,
+ this::writeAnnotationSet, 4);
+ writeItems(mixedSectionOffsets.getAnnotationSetRefLists(),
+ layout::setAnnotationSetRefListsOffset, this::writeAnnotationSetRefList, 4);
+ // Write the annotation directories.
+ writeItems(Arrays.asList(mapping.getClasses()), layout::setAnnotationDirectoriesOffset,
+ this::writeAnnotationDirectoryForClass, 4);
+ // Write the rest.
+ writeItems(mixedSectionOffsets.getClassesWithData(), layout::setClassDataOffset,
+ this::writeClassData);
+ writeItems(mixedSectionOffsets.getEncodedArrays(), layout::setEncodedArrarysOffset,
+ this::writeEncodedArray);
+
+ // Add the map at the end
+ layout.setMapOffset(dest.align(4));
+ writeMap(layout);
+ layout.setEndOfFile(dest.position());
+
+ // Now that we have all mixedSectionOffsets, lets write the indexed items.
+ dest.moveTo(Constants.HEADER_SIZE);
+ writeFixedSectionItems(mapping.getStrings(), layout.stringIdsOffset, this::writeStringItem);
+ writeFixedSectionItems(mapping.getTypes(), layout.typeIdsOffset, this::writeTypeItem);
+ writeFixedSectionItems(mapping.getProtos(), layout.protoIdsOffset, this::writeProtoItem);
+ writeFixedSectionItems(mapping.getFields(), layout.fieldIdsOffset, this::writeFieldItem);
+ writeFixedSectionItems(mapping.getMethods(), layout.methodIdsOffset, this::writeMethodItem);
+ writeFixedSectionItems(mapping.getClasses(), layout.classDefsOffset, this::writeClassDefItem);
+ writeFixedSectionItems(mapping.getCallSites(), layout.callSiteIdsOffset, this::writeCallSite);
+ writeFixedSectionItems(
+ mapping.getMethodHandles(), layout.methodHandleIdsOffset, this::writeMethodHandle);
+
+ // Fill in the header information.
+ writeHeader(layout);
+ writeSignature(layout);
+ writeChecksum(layout);
+
+ // Turn into an array
+ return Arrays.copyOf(dest.asArray(), layout.getEndOfFile());
+ }
+
+ private void sortClassData(List<DexProgramClass> classesWithData) {
+ for (DexProgramClass clazz : classesWithData) {
+ sortEncodedFields(clazz.instanceFields);
+ sortEncodedFields(clazz.staticFields);
+ sortEncodedMethods(clazz.directMethods);
+ sortEncodedMethods(clazz.virtualMethods);
+ }
+ }
+
+ private void sortEncodedFields(DexEncodedField[] fields) {
+ Arrays.sort(fields, (DexEncodedField a, DexEncodedField b) -> a.field.compareTo(b.field));
+ }
+
+ private void sortEncodedMethods(DexEncodedMethod[] methods) {
+ Arrays.sort(methods, (DexEncodedMethod a, DexEncodedMethod b) -> a.method.compareTo(b.method));
+ }
+
+ private void checkInterfaceMethods() {
+ for (DexProgramClass clazz : mapping.getClasses()) {
+ if (clazz.isInterface()) {
+ checkInterfaceMethods(clazz.directMethods());
+ checkInterfaceMethods(clazz.virtualMethods());
+ }
+ }
+ }
+
+ // Ensures interface methods comply with requirements imposed by Android runtime:
+ // -- in pre-N Android versions interfaces may only have class
+ // initializer and public abstract methods.
+ // -- starting with N interfaces may also have public or private
+ // static methods, as well as public non-abstract (default)
+ // and private instance methods.
+ private void checkInterfaceMethods(DexEncodedMethod[] methods) {
+ for (DexEncodedMethod method : methods) {
+ if (application.dexItemFactory.isClassConstructor(method.method)) {
+ continue; // Class constructor is always OK.
+ }
+ if (method.accessFlags.isStatic()) {
+ if (!options.canUseDefaultAndStaticInterfaceMethods()) {
+ throw new CompilationError("Static interface methods are only supported "
+ + "starting with Android N (--min-sdk-version " + Constants.ANDROID_N_API + "): "
+ + method.method.toSourceString());
+ }
+
+ } else {
+ if (method.accessFlags.isConstructor()) {
+ throw new CompilationError(
+ "Interface must not have constructors: " + method.method.toSourceString());
+ }
+ if (!method.accessFlags.isAbstract() && !method.accessFlags.isPrivate() &&
+ !options.canUseDefaultAndStaticInterfaceMethods()) {
+ throw new CompilationError("Default interface methods are only supported "
+ + "starting with Android N (--min-sdk-version " + Constants.ANDROID_N_API + "): "
+ + method.method.toSourceString());
+ }
+ }
+
+ if (method.accessFlags.isPrivate()) {
+ if (options.canUsePrivateInterfaceMethods()) {
+ continue;
+ }
+ throw new CompilationError("Private interface methods are only supported "
+ + "starting with Android N (--min-sdk-version " + Constants.ANDROID_N_API + "): "
+ + method.method.toSourceString());
+ }
+
+ if (!method.accessFlags.isPublic()) {
+ throw new CompilationError("Interface methods must not be "
+ + "protected or package private: " + method.method.toSourceString());
+ }
+ }
+ }
+
+ private List<DexCode> sortDexCodesByClassName(List<DexCode> codes, DexApplication application) {
+ Map<DexCode, String> codeToSignatureMap = new IdentityHashMap<>();
+ for (DexProgramClass clazz : mapping.getClasses()) {
+ addSignaturesFromMethods(clazz.directMethods(), codeToSignatureMap,
+ application.getProguardMap());
+ addSignaturesFromMethods(clazz.virtualMethods(), codeToSignatureMap,
+ application.getProguardMap());
+ }
+ DexCode[] codesArray = codes.toArray(new DexCode[codes.size()]);
+ Arrays.sort(codesArray, Comparator.comparing(codeToSignatureMap::get));
+ return Arrays.asList(codesArray);
+ }
+
+ private static void addSignaturesFromMethods(DexEncodedMethod[] methods,
+ Map<DexCode, String> codeToSignatureMap,
+ ClassNameMapper proguardMap) {
+ for (DexEncodedMethod method : methods) {
+ if (method.getCode() == null) {
+ assert method.accessFlags.isAbstract() || method.accessFlags.isNative();
+ } else {
+ Signature signature;
+ String originalClassName;
+ if (proguardMap != null) {
+ signature = proguardMap.originalSignatureOf(method.method);
+ originalClassName = proguardMap.originalNameOf(method.method.holder);
+ } else {
+ signature = MethodSignature.fromDexMethod(method.method);
+ originalClassName = method.method.holder.toSourceString();
+ }
+ codeToSignatureMap.put(method.getCode().asDexCode(), originalClassName + signature);
+ }
+ }
+ }
+
+ private <T extends DexItem> void writeFixedSectionItems(T[] items, int offset,
+ Consumer<T> writer) {
+ assert dest.position() == offset;
+ for (T item : items) {
+ writer.accept(item);
+ }
+ }
+
+ private <T extends DexItem> void writeItems(List<T> items, Consumer<Integer> offsetSetter,
+ Consumer<T> writer) {
+ writeItems(items, offsetSetter, writer, 1);
+ }
+
+ private <T extends DexItem> void writeItems(List<T> items, Consumer<Integer> offsetSetter,
+ Consumer<T> writer, int alignment) {
+ if (items.isEmpty()) {
+ offsetSetter.accept(0);
+ } else {
+ offsetSetter.accept(dest.align(alignment));
+ items.forEach(writer);
+ }
+ }
+
+ private int sizeOfCodeItems(Iterable<DexCode> codes) {
+ int size = 0;
+ for (DexCode code : codes) {
+ size = alignSize(4, size);
+ size += sizeOfCodeItem(code);
+ }
+ return size;
+ }
+
+ private int sizeOfCodeItem(DexCode code) {
+ int result = 16;
+ int insnSize = 0;
+ for (Instruction insn : code.instructions) {
+ insnSize += insn.getSize();
+ }
+ result += insnSize * 2;
+ result += code.tries.length * 8;
+ if ((code.handlers != null) && (code.handlers.length > 0)) {
+ result = alignSize(4, result);
+ result += LebUtils.sizeAsUleb128(code.handlers.length);
+ for (TryHandler handler : code.handlers) {
+ boolean hasCatchAll = handler.catchAllAddr != TryHandler.NO_HANDLER;
+ result += LebUtils
+ .sizeAsSleb128(hasCatchAll ? -handler.pairs.length : handler.pairs.length);
+ for (TypeAddrPair pair : handler.pairs) {
+
+ result += sizeAsUleb128(mapping.getOffsetFor(pair.type));
+ result += sizeAsUleb128(pair.addr);
+ }
+ if (hasCatchAll) {
+ result += sizeAsUleb128(handler.catchAllAddr);
+ }
+ }
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Computed size item %08d.", result);
+ }
+ return result;
+ }
+
+ private void writeStringItem(DexString string) {
+ dest.putInt(mixedSectionOffsets.getOffsetFor(string));
+ }
+
+ private void writeTypeItem(DexType type) {
+ DexString descriptor = namingLens.lookupDescriptor(type);
+ dest.putInt(mapping.getOffsetFor(descriptor));
+ }
+
+ private void writeProtoItem(DexProto proto) {
+ dest.putInt(mapping.getOffsetFor(proto.shorty));
+ dest.putInt(mapping.getOffsetFor(proto.returnType));
+ dest.putInt(mixedSectionOffsets.getOffsetFor(proto.parameters));
+ }
+
+ private void writeFieldItem(DexField field) {
+ int classIdx = mapping.getOffsetFor(field.clazz);
+ assert (short) classIdx == classIdx;
+ dest.putShort((short) classIdx);
+ int typeIdx = mapping.getOffsetFor(field.type);
+ assert (short) typeIdx == typeIdx;
+ dest.putShort((short) typeIdx);
+ DexString name = namingLens.lookupName(field);
+ dest.putInt(mapping.getOffsetFor(name));
+ }
+
+ private void writeMethodItem(DexMethod method) {
+ int classIdx = mapping.getOffsetFor(method.holder);
+ assert (short) classIdx == classIdx;
+ dest.putShort((short) classIdx);
+ int protoIdx = mapping.getOffsetFor(method.proto);
+ assert (short) protoIdx == protoIdx;
+ dest.putShort((short) protoIdx);
+ DexString name = namingLens.lookupName(method);
+ dest.putInt(mapping.getOffsetFor(name));
+ }
+
+ private void writeClassDefItem(DexProgramClass clazz) {
+ dest.putInt(mapping.getOffsetFor(clazz.type));
+ dest.putInt(clazz.accessFlags.get());
+ dest.putInt(
+ clazz.superType == null ? Constants.NO_INDEX : mapping.getOffsetFor(clazz.superType));
+ dest.putInt(mixedSectionOffsets.getOffsetFor(clazz.interfaces));
+ dest.putInt(
+ clazz.sourceFile == null ? Constants.NO_INDEX : mapping.getOffsetFor(clazz.sourceFile));
+ dest.putInt(mixedSectionOffsets.getOffsetForAnnotationsDirectory(clazz));
+ dest.putInt(clazz.hasMethodsOrFields() ? mixedSectionOffsets.getOffsetFor(clazz) : Constants.NO_OFFSET);
+ dest.putInt(mixedSectionOffsets.getOffsetFor(clazz.getStaticValues()));
+ }
+
+ private void writeDebugItem(DexDebugInfo debugInfo) {
+ mixedSectionOffsets.setOffsetFor(debugInfo, dest.position());
+ dest.putBytes(new DebugBytecodeWriter(debugInfo, mapping).generate());
+ }
+
+ private void writeCodeItem(DexCode code) {
+ mixedSectionOffsets.setOffsetFor(code, dest.align(4));
+ // Fixed size header information.
+ dest.putShort((short) code.registerSize);
+ dest.putShort((short) code.incomingRegisterSize);
+ dest.putShort((short) code.outgoingRegisterSize);
+ dest.putShort((short) code.tries.length);
+ dest.putInt(mixedSectionOffsets.getOffsetFor(code.getDebugInfo()));
+ // Jump over the size.
+ int insnSizeOffset = dest.position();
+ dest.forward(4);
+ // Write instruction stream.
+ dest.putInstructions(code.instructions, mapping);
+ // Compute size and do the backward/forward dance to write the size at the beginning.
+ int insnSize = dest.position() - insnSizeOffset - 4;
+ dest.rewind(insnSize + 4);
+ dest.putInt(insnSize / 2);
+ dest.forward(insnSize);
+ if (code.tries.length > 0) {
+ // The tries need to be 4 byte aligned.
+ int beginOfTriesOffset = dest.align(4);
+ // First write the handlers, so that we know their mixedSectionOffsets.
+ dest.forward(code.tries.length * 8);
+ int beginOfHandlersOffset = dest.position();
+ dest.putUleb128(code.handlers.length);
+ short[] offsets = new short[code.handlers.length];
+ int i = 0;
+ for (TryHandler handler : code.handlers) {
+ offsets[i++] = (short) (dest.position() - beginOfHandlersOffset);
+ boolean hasCatchAll = handler.catchAllAddr != TryHandler.NO_HANDLER;
+ dest.putSleb128(hasCatchAll ? -handler.pairs.length : handler.pairs.length);
+ for (TypeAddrPair pair : handler.pairs) {
+ dest.putUleb128(mapping.getOffsetFor(pair.type));
+ dest.putUleb128(pair.addr);
+ }
+ if (hasCatchAll) {
+ dest.putUleb128(handler.catchAllAddr);
+ }
+ }
+ int endOfCodeOffset = dest.position();
+ // Now write the tries.
+ dest.moveTo(beginOfTriesOffset);
+ for (Try aTry : code.tries) {
+ dest.putInt(aTry.startAddress);
+ dest.putShort((short) aTry.instructionCount);
+ dest.putShort(offsets[aTry.handlerIndex]);
+ }
+ // And move to the end.
+ dest.moveTo(endOfCodeOffset);
+ }
+ }
+
+ private void writeTypeList(DexTypeList list) {
+ assert !list.isEmpty();
+ mixedSectionOffsets.setOffsetFor(list, dest.align(4));
+ DexType[] values = list.values;
+ dest.putInt(values.length);
+ for (DexType type : values) {
+ dest.putShort((short) mapping.getOffsetFor(type));
+ }
+ }
+
+ private void writeStringData(DexString string) {
+ mixedSectionOffsets.setOffsetFor(string, dest.position());
+ dest.putUleb128(string.size);
+ dest.putBytes(string.content);
+ }
+
+ private void writeAnnotation(DexAnnotation annotation) {
+ mixedSectionOffsets.setOffsetFor(annotation, dest.position());
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Writing Annotation @ 0x%08x.", dest.position());
+ }
+ dest.putByte((byte) annotation.visibility);
+ writeEncodedAnnotation(annotation.annotation, dest, mapping);
+ }
+
+ private void writeAnnotationSet(DexAnnotationSet set) {
+ assert !set.isEmpty();
+ assert isSorted(set.annotations, (item) -> item.annotation.type);
+ mixedSectionOffsets.setOffsetFor(set, dest.align(4));
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Writing AnnotationSet @ 0x%08x.", dest.position());
+ }
+ dest.putInt(set.annotations.length);
+ for (DexAnnotation annotation : set.annotations) {
+ dest.putInt(mixedSectionOffsets.getOffsetFor(annotation));
+ }
+ }
+
+ private void writeAnnotationSetRefList(DexAnnotationSetRefList setRefList) {
+ assert !setRefList.isEmpty();
+ mixedSectionOffsets.setOffsetFor(setRefList, dest.align(4));
+ dest.putInt(setRefList.values.length);
+ for (DexAnnotationSet set : setRefList.values) {
+ dest.putInt(mixedSectionOffsets.getOffsetFor(set));
+ }
+ }
+
+ private <S extends Descriptor<T, S>, T extends KeyedDexItem<S>> void writeMemberAnnotations(
+ List<T> items, ToIntFunction<T> getter) {
+ for (T item : items) {
+ dest.putInt(item.getKey().getOffset(mapping));
+ dest.putInt(getter.applyAsInt(item));
+ }
+ }
+
+ private void writeAnnotationDirectoryForClass(DexProgramClass clazz) {
+ if (clazz.hasAnnotations()) {
+ mixedSectionOffsets.setOffsetForAnnotationsDirectory(clazz, dest.align(4));
+ numberOfAnnotationDirectories++;
+ assert isSorted(clazz.directMethods());
+ assert isSorted(clazz.virtualMethods());
+ OrderedMergingIterator<DexEncodedMethod, DexMethod> methods =
+ new OrderedMergingIterator<>(clazz.directMethods(), clazz.virtualMethods());
+ List<DexEncodedMethod> methodAnnotations = new ArrayList<>();
+ List<DexEncodedMethod> parameterAnnotations = new ArrayList<>();
+ while (methods.hasNext()) {
+ DexEncodedMethod method = methods.next();
+ if (!method.annotations.isEmpty()) {
+ methodAnnotations.add(method);
+ }
+ if (!method.parameterAnnotations.isEmpty()) {
+ parameterAnnotations.add(method);
+ }
+ }
+ assert isSorted(clazz.staticFields());
+ assert isSorted(clazz.instanceFields());
+ OrderedMergingIterator<DexEncodedField, DexField> fields =
+ new OrderedMergingIterator<>(clazz.staticFields(), clazz.instanceFields());
+ List<DexEncodedField> fieldAnnotations = new ArrayList<>();
+ while (fields.hasNext()) {
+ DexEncodedField field = fields.next();
+ if (!field.annotations.isEmpty()) {
+ fieldAnnotations.add(field);
+ }
+ }
+ dest.putInt(mixedSectionOffsets.getOffsetFor(clazz.annotations));
+ dest.putInt(fieldAnnotations.size());
+ dest.putInt(methodAnnotations.size());
+ dest.putInt(parameterAnnotations.size());
+ writeMemberAnnotations(fieldAnnotations,
+ item -> mixedSectionOffsets.getOffsetFor(item.annotations));
+ writeMemberAnnotations(methodAnnotations,
+ item -> mixedSectionOffsets.getOffsetFor(item.annotations));
+ writeMemberAnnotations(parameterAnnotations,
+ item -> mixedSectionOffsets.getOffsetFor(item.parameterAnnotations));
+ }
+ }
+
+ private void writeEncodedFields(DexEncodedField[] fields) {
+ assert isSorted(fields);
+ int currentOffset = 0;
+ for (DexEncodedField field : fields) {
+ int nextOffset = mapping.getOffsetFor(field.field);
+ assert nextOffset - currentOffset >= 0;
+ dest.putUleb128(nextOffset - currentOffset);
+ currentOffset = nextOffset;
+ dest.putUleb128(field.accessFlags.get());
+ }
+ }
+
+ private void writeEncodedMethods(DexEncodedMethod[] methods) {
+ assert isSorted(methods);
+ int currentOffset = 0;
+ for (DexEncodedMethod method : methods) {
+ int nextOffset = mapping.getOffsetFor(method.method);
+ assert nextOffset - currentOffset >= 0;
+ dest.putUleb128(nextOffset - currentOffset);
+ currentOffset = nextOffset;
+ dest.putUleb128(method.accessFlags.get());
+ if (method.getCode() == null) {
+ assert method.accessFlags.isAbstract() || method.accessFlags.isNative();
+ dest.putUleb128(0);
+ } else {
+ dest.putUleb128(mixedSectionOffsets.getOffsetFor(method.getCode().asDexCode()));
+ // Writing the methods starts to take up memory so we are going to flush the
+ // code objects since they are no longer necessary after this.
+ method.removeCode();
+ }
+ }
+ }
+
+ private void writeClassData(DexProgramClass clazz) {
+ assert clazz.hasMethodsOrFields();
+ mixedSectionOffsets.setOffsetFor(clazz, dest.position());
+ dest.putUleb128(clazz.staticFields().length);
+ dest.putUleb128(clazz.instanceFields().length);
+ dest.putUleb128(clazz.directMethods().length);
+ dest.putUleb128(clazz.virtualMethods().length);
+ writeEncodedFields(clazz.staticFields());
+ writeEncodedFields(clazz.instanceFields());
+ writeEncodedMethods(clazz.directMethods());
+ writeEncodedMethods(clazz.virtualMethods());
+ }
+
+ private void addStaticFieldValues(DexProgramClass clazz) {
+ DexEncodedField[] fields = clazz.staticFields();
+ int length = 0;
+ List<DexValue> values = new ArrayList<>(fields.length);
+ for (int i = 0; i < fields.length; i++) {
+ DexEncodedField field = fields[i];
+ assert field.staticValue != null;
+ values.add(field.staticValue);
+ if (!field.staticValue.isDefault(field.field.type, application.dexItemFactory)) {
+ length = i + 1;
+ }
+ }
+ if (length > 0) {
+ DexEncodedArray staticValues = new DexEncodedArray(
+ values.subList(0, length).toArray(new DexValue[length]));
+ clazz.setStaticValues(staticValues);
+ mixedSectionOffsets.add(staticValues);
+ }
+ }
+
+ private void writeMethodHandle(DexMethodHandle methodHandle) {
+ checkThatInvokeCustomIsAllowed();
+ MethodHandleType methodHandleDexType = methodHandle.type;
+ switch (methodHandleDexType) {
+ case INVOKE_CONSTRUCTOR:
+ throw new CompilationError("Constructor method handle type is not yet supported.");
+ case INVOKE_INTERFACE:
+ case INVOKE_SUPER:
+ methodHandleDexType = MethodHandleType.INVOKE_INSTANCE;
+ break;
+ }
+ assert dest.isAligned(4);
+ dest.putShort(methodHandleDexType.getValue());
+ dest.putShort((short) 0); // unused
+ int fieldOrMethodIdx;
+ if (methodHandle.isMethodHandle()) {
+ fieldOrMethodIdx = mapping.getOffsetFor(methodHandle.asMethod());
+ } else {
+ assert methodHandle.isFieldHandle();
+ fieldOrMethodIdx = mapping.getOffsetFor(methodHandle.asField());
+ }
+ assert (short) fieldOrMethodIdx == fieldOrMethodIdx;
+ dest.putShort((short) fieldOrMethodIdx);
+ dest.putShort((short) 0); // unused
+ }
+
+ private void writeCallSite(DexCallSite callSite) {
+ checkThatInvokeCustomIsAllowed();
+ assert dest.isAligned(4);
+ dest.putInt(mixedSectionOffsets.getOffsetFor(callSite.getEncodedArray()));
+ }
+
+ private void writeEncodedArray(DexEncodedArray array) {
+ mixedSectionOffsets.setOffsetFor(array, dest.position());
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Writing EncodedArray @ 0x%08x [%s].", dest.position(), array);
+ }
+ dest.putUleb128(array.values.length);
+ for (DexValue value : array.values) {
+ value.writeTo(dest, mapping);
+ }
+ }
+
+ private int writeMapItem(int type, int offset, int length) {
+ if (length == 0) {
+ return 0;
+ }
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Map entry 0x%04x @ 0x%08x # %08d.", type, offset, length);
+ }
+ dest.putShort((short) type);
+ dest.putShort((short) 0);
+ dest.putInt(length);
+ dest.putInt(offset);
+ return 1;
+ }
+
+ private void writeMap(Layout layout) {
+ int startOfMap = dest.align(4);
+ dest.forward(4); // Leave space for size;
+ int size = 0;
+ size += writeMapItem(Constants.TYPE_HEADER_ITEM, 0, 1);
+ size += writeMapItem(Constants.TYPE_STRING_ID_ITEM, layout.stringIdsOffset,
+ mapping.getStrings().length);
+ size += writeMapItem(Constants.TYPE_TYPE_ID_ITEM, layout.typeIdsOffset,
+ mapping.getTypes().length);
+ size += writeMapItem(Constants.TYPE_PROTO_ID_ITEM, layout.protoIdsOffset,
+ mapping.getProtos().length);
+ size += writeMapItem(Constants.TYPE_FIELD_ID_ITEM, layout.fieldIdsOffset,
+ mapping.getFields().length);
+ size += writeMapItem(Constants.TYPE_METHOD_ID_ITEM, layout.methodIdsOffset,
+ mapping.getMethods().length);
+ size += writeMapItem(Constants.TYPE_CLASS_DEF_ITEM, layout.classDefsOffset,
+ mapping.getClasses().length);
+ size += writeMapItem(Constants.TYPE_CALL_SITE_ID_ITEM, layout.callSiteIdsOffset,
+ mapping.getCallSites().length);
+ size += writeMapItem(Constants.TYPE_METHOD_HANDLE_ITEM, layout.methodHandleIdsOffset,
+ mapping.getMethodHandles().length);
+ size += writeMapItem(Constants.TYPE_CODE_ITEM, layout.getCodesOffset(),
+ mixedSectionOffsets.getCodes().size());
+ size += writeMapItem(Constants.TYPE_DEBUG_INFO_ITEM, layout.getDebugInfosOffset(),
+ mixedSectionOffsets.getDebugInfos().size());
+ size += writeMapItem(Constants.TYPE_TYPE_LIST, layout.getTypeListsOffset(),
+ mixedSectionOffsets.getTypeLists().size());
+ size += writeMapItem(Constants.TYPE_STRING_DATA_ITEM, layout.getStringDataOffsets(),
+ mixedSectionOffsets.getStringData().size());
+ size += writeMapItem(Constants.TYPE_ANNOTATION_ITEM, layout.getAnnotationsOffset(),
+ mixedSectionOffsets.getAnnotations().size());
+ size += writeMapItem(Constants.TYPE_ANNOTATION_SET_ITEM, layout.getAnnotationSetsOffset(),
+ mixedSectionOffsets.getAnnotationSets().size());
+ size += writeMapItem(Constants.TYPE_ANNOTATION_SET_REF_LIST,
+ layout.getAnnotationSetRefListsOffset(),
+ mixedSectionOffsets.getAnnotationSetRefLists().size());
+ size += writeMapItem(Constants.TYPE_ANNOTATIONS_DIRECTORY_ITEM,
+ layout.getAnnotationDirectoriesOffset(),
+ numberOfAnnotationDirectories);
+ size += writeMapItem(Constants.TYPE_CLASS_DATA_ITEM, layout.getClassDataOffset(),
+ mixedSectionOffsets.getClassesWithData().size());
+ size += writeMapItem(Constants.TYPE_ENCODED_ARRAY_ITEM, layout.getEncodedArrarysOffset(),
+ mixedSectionOffsets.getEncodedArrays().size());
+ size += writeMapItem(Constants.TYPE_MAP_LIST, layout.getMapOffset(), 1);
+ dest.moveTo(startOfMap);
+ dest.putInt(size);
+ dest.forward(size * Constants.TYPE_MAP_LIST_ITEM_SIZE);
+ }
+
+ private static byte[] convertApiLevelToDexVersion(int apiLevel) {
+ if (apiLevel >= Constants.ANDROID_O_API) {
+ return Constants.ANDROID_O_DEX_VERSION_BYTES;
+ }
+ if (apiLevel >= Constants.ANDROID_N_API) {
+ return Constants.ANDROID_N_DEX_VERSION_BYTES;
+ }
+ return Constants.ANDROID_PRE_N_DEX_VERSION_BYTES;
+ }
+
+ private void writeHeader(Layout layout) {
+ dest.moveTo(0);
+ dest.putBytes(Constants.DEX_FILE_MAGIC_PREFIX);
+ dest.putBytes(convertApiLevelToDexVersion(options.minApiLevel));
+ dest.putByte(Constants.DEX_FILE_MAGIC_SUFFIX);
+ // Leave out checksum and signature for now.
+ dest.moveTo(Constants.FILE_SIZE_OFFSET);
+ dest.putInt(layout.getEndOfFile());
+ dest.putInt(Constants.HEADER_SIZE);
+ dest.putInt(Constants.ENDIAN_CONSTANT);
+ dest.putInt(0);
+ dest.putInt(0);
+ dest.putInt(layout.getMapOffset());
+ int numberOfStrings = mapping.getStrings().length;
+ dest.putInt(numberOfStrings);
+ dest.putInt(numberOfStrings == 0 ? 0 : layout.stringIdsOffset);
+ int numberOfTypes = mapping.getTypes().length;
+ dest.putInt(numberOfTypes);
+ dest.putInt(numberOfTypes == 0 ? 0 : layout.typeIdsOffset);
+ int numberOfProtos = mapping.getProtos().length;
+ dest.putInt(numberOfProtos);
+ dest.putInt(numberOfProtos == 0 ? 0 : layout.protoIdsOffset);
+ int numberOfFields = mapping.getFields().length;
+ dest.putInt(numberOfFields);
+ dest.putInt(numberOfFields == 0 ? 0 : layout.fieldIdsOffset);
+ int numberOfMethods = mapping.getMethods().length;
+ dest.putInt(numberOfMethods);
+ dest.putInt(numberOfMethods == 0 ? 0 : layout.methodIdsOffset);
+ int numberOfClasses = mapping.getClasses().length;
+ dest.putInt(numberOfClasses);
+ dest.putInt(numberOfClasses == 0 ? 0 : layout.classDefsOffset);
+ dest.putInt(layout.getDataSectionSize());
+ dest.putInt(layout.dataSectionOffset);
+ assert dest.position() == layout.stringIdsOffset;
+ }
+
+ private void writeSignature(Layout layout) {
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ md.update(dest.asArray(), Constants.FILE_SIZE_OFFSET,
+ layout.getEndOfFile() - Constants.FIELD_IDS_OFF_OFFSET);
+ md.digest(dest.asArray(), Constants.SIGNATURE_OFFSET, 20);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void writeChecksum(Layout layout) {
+ Adler32 adler = new Adler32();
+ adler.update(dest.asArray(), Constants.SIGNATURE_OFFSET,
+ layout.getEndOfFile() - Constants.SIGNATURE_OFFSET);
+ dest.moveTo(Constants.CHECKSUM_OFFSET);
+ dest.putInt((int) adler.getValue());
+ }
+
+ private int alignSize(int bytes, int value) {
+ int mask = bytes - 1;
+ return (value + mask) & ~mask;
+ }
+
+ private static class Layout {
+
+ private static final int NOT_SET = -1;
+
+ // Fixed size constant pool sections
+ final int stringIdsOffset;
+ final int typeIdsOffset;
+ final int protoIdsOffset;
+ final int fieldIdsOffset;
+ final int methodIdsOffset;
+ final int classDefsOffset;
+ final int callSiteIdsOffset;
+ final int methodHandleIdsOffset;
+ final int dataSectionOffset;
+
+ // Mixed size sections
+ private int codesOffset = NOT_SET; // aligned
+ private int debugInfosOffset = NOT_SET;
+
+ private int typeListsOffset = NOT_SET; // aligned
+ private int stringDataOffsets = NOT_SET;
+ private int annotationsOffset = NOT_SET;
+ private int annotationSetsOffset = NOT_SET; // aligned
+ private int annotationSetRefListsOffset = NOT_SET; // aligned
+ private int annotationDirectoriesOffset = NOT_SET; // aligned
+ private int classDataOffset = NOT_SET;
+ private int encodedArrarysOffset = NOT_SET;
+ private int mapOffset = NOT_SET;
+ private int endOfFile = NOT_SET;
+
+ private Layout(int stringIdsOffset, int typeIdsOffset, int protoIdsOffset, int fieldIdsOffset,
+ int methodIdsOffset, int classDefsOffset, int callSiteIdsOffset, int methodHandleIdsOffset,
+ int dataSectionOffset) {
+ this.stringIdsOffset = stringIdsOffset;
+ this.typeIdsOffset = typeIdsOffset;
+ this.protoIdsOffset = protoIdsOffset;
+ this.fieldIdsOffset = fieldIdsOffset;
+ this.methodIdsOffset = methodIdsOffset;
+ this.classDefsOffset = classDefsOffset;
+ this.callSiteIdsOffset = callSiteIdsOffset;
+ this.methodHandleIdsOffset = methodHandleIdsOffset;
+ this.dataSectionOffset = dataSectionOffset;
+ assert stringIdsOffset <= typeIdsOffset;
+ assert typeIdsOffset <= protoIdsOffset;
+ assert protoIdsOffset <= fieldIdsOffset;
+ assert fieldIdsOffset <= methodIdsOffset;
+ assert methodIdsOffset <= classDefsOffset;
+ assert classDefsOffset <= dataSectionOffset;
+ assert callSiteIdsOffset <= dataSectionOffset;
+ assert methodHandleIdsOffset <= dataSectionOffset;
+ }
+
+ static Layout from(ObjectToOffsetMapping mapping) {
+ int offset = 0;
+ return new Layout(
+ offset = Constants.HEADER_SIZE,
+ offset += mapping.getStrings().length * Constants.TYPE_STRING_ID_ITEM_SIZE,
+ offset += mapping.getTypes().length * Constants.TYPE_TYPE_ID_ITEM_SIZE,
+ offset += mapping.getProtos().length * Constants.TYPE_PROTO_ID_ITEM_SIZE,
+ offset += mapping.getFields().length * Constants.TYPE_FIELD_ID_ITEM_SIZE,
+ offset += mapping.getMethods().length * Constants.TYPE_METHOD_ID_ITEM_SIZE,
+ offset += mapping.getClasses().length * Constants.TYPE_CLASS_DEF_ITEM_SIZE,
+ offset += mapping.getCallSites().length * Constants.TYPE_CALL_SITE_ID_ITEM_SIZE,
+ offset += mapping.getMethodHandles().length * Constants.TYPE_METHOD_HANDLE_ITEM_SIZE);
+ }
+
+ int getDataSectionSize() {
+ int size = getEndOfFile() - dataSectionOffset;
+ assert size % 4 == 0;
+ return size;
+ }
+
+ private boolean isValidOffset(int value, boolean isAligned) {
+ return value != NOT_SET && (!isAligned || value % 4 == 0);
+ }
+
+ public int getCodesOffset() {
+ assert isValidOffset(codesOffset, true);
+ return codesOffset;
+ }
+
+ public void setCodesOffset(int codesOffset) {
+ assert this.codesOffset == NOT_SET;
+ this.codesOffset = codesOffset;
+ }
+
+ public int getDebugInfosOffset() {
+ assert isValidOffset(debugInfosOffset, false);
+ return debugInfosOffset;
+ }
+
+ public void setDebugInfosOffset(int debugInfosOffset) {
+ assert this.debugInfosOffset == NOT_SET;
+ this.debugInfosOffset = debugInfosOffset;
+ }
+
+ public int getTypeListsOffset() {
+ assert isValidOffset(typeListsOffset, true);
+ return typeListsOffset;
+ }
+
+ public void setTypeListsOffset(int typeListsOffset) {
+ assert this.typeListsOffset == NOT_SET;
+ this.typeListsOffset = typeListsOffset;
+ }
+
+ public int getStringDataOffsets() {
+ assert isValidOffset(stringDataOffsets, false);
+ return stringDataOffsets;
+ }
+
+ public void setStringDataOffsets(int stringDataOffsets) {
+ assert this.stringDataOffsets == NOT_SET;
+ this.stringDataOffsets = stringDataOffsets;
+ }
+
+ public int getAnnotationsOffset() {
+ assert isValidOffset(annotationsOffset, false);
+ return annotationsOffset;
+ }
+
+ public void setAnnotationsOffset(int annotationsOffset) {
+ assert this.annotationsOffset == NOT_SET;
+ this.annotationsOffset = annotationsOffset;
+ }
+
+ public int getAnnotationSetsOffset() {
+ assert isValidOffset(annotationSetsOffset, true);
+ return annotationSetsOffset;
+ }
+
+ public void alreadySetOffset(int ignored) {
+ // Intentionally empty.
+ }
+
+ public void setAnnotationSetsOffset(int annotationSetsOffset) {
+ assert this.annotationSetsOffset == NOT_SET;
+ this.annotationSetsOffset = annotationSetsOffset;
+ }
+
+ public int getAnnotationSetRefListsOffset() {
+ assert isValidOffset(annotationSetRefListsOffset, true);
+ return annotationSetRefListsOffset;
+ }
+
+ public void setAnnotationSetRefListsOffset(int annotationSetRefListsOffset) {
+ assert this.annotationSetRefListsOffset == NOT_SET;
+ this.annotationSetRefListsOffset = annotationSetRefListsOffset;
+ }
+
+ public int getAnnotationDirectoriesOffset() {
+ assert isValidOffset(annotationDirectoriesOffset, true);
+ return annotationDirectoriesOffset;
+ }
+
+ public void setAnnotationDirectoriesOffset(int annotationDirectoriesOffset) {
+ assert this.annotationDirectoriesOffset == NOT_SET;
+ this.annotationDirectoriesOffset = annotationDirectoriesOffset;
+ }
+
+ public int getClassDataOffset() {
+ assert isValidOffset(classDataOffset, false);
+ return classDataOffset;
+ }
+
+ public void setClassDataOffset(int classDataOffset) {
+ assert this.classDataOffset == NOT_SET;
+ this.classDataOffset = classDataOffset;
+ }
+
+ public int getEncodedArrarysOffset() {
+ assert isValidOffset(encodedArrarysOffset, false);
+ return encodedArrarysOffset;
+ }
+
+ public void setEncodedArrarysOffset(int encodedArrarysOffset) {
+ assert this.encodedArrarysOffset == NOT_SET;
+ this.encodedArrarysOffset = encodedArrarysOffset;
+ }
+
+ public int getMapOffset() {
+ return mapOffset;
+ }
+
+ public void setMapOffset(int mapOffset) {
+ this.mapOffset = mapOffset;
+ }
+
+ public int getEndOfFile() {
+ return endOfFile;
+ }
+
+ public void setEndOfFile(int endOfFile) {
+ this.endOfFile = endOfFile;
+ }
+ }
+
+ /**
+ * Encapsulates information on the offsets of items in the sections of the mixed data part of the
+ * DEX file.
+ * Initially, items are collected using the {@link MixedSectionCollection} traversal and all
+ * offsets are unset. When writing a section, the offsets of the written items are stored.
+ * These offsets are then used to resolve cross-references between items from different sections
+ * into a file offset.
+ */
+ private static class MixedSectionOffsets extends MixedSectionCollection {
+
+ private static final int NOT_SET = -1;
+
+ private final Map<DexCode, Integer> codes = Maps.newIdentityHashMap();
+ private final List<DexCode> codesList = new LinkedList<>();
+ private final Hashtable<DexDebugInfo, Integer> debugInfos = new Hashtable<>();
+ private final List<DexDebugInfo> debugInfosList = new LinkedList<>();
+ private final Hashtable<DexTypeList, Integer> typeLists = new Hashtable<>();
+ private final List<DexTypeList> typeListsList = new LinkedList<>();
+ private final Hashtable<DexString, Integer> stringData = new Hashtable<>();
+ private final List<DexString> stringDataList = new LinkedList<>();
+ private final Hashtable<DexAnnotation, Integer> annotations = new Hashtable<>();
+ private final List<DexAnnotation> annotationsList = new LinkedList<>();
+ private final Hashtable<DexAnnotationSet, Integer> annotationSets = new Hashtable<>();
+ private final List<DexAnnotationSet> annotationSetsList = new LinkedList<>();
+ private final Hashtable<DexAnnotationSetRefList, Integer> annotationSetRefLists
+ = new Hashtable<>();
+ private final List<DexAnnotationSetRefList> annotationSetRefListsList = new LinkedList<>();
+ private final Hashtable<DexProgramClass, Integer> annotationDirectories
+ = new Hashtable<>();
+ private final Hashtable<DexProgramClass, Integer> classesWithData = new Hashtable<>();
+ private final List<DexProgramClass> classesWithDataList = new LinkedList<>();
+ private final Hashtable<DexEncodedArray, Integer> encodedArrays = new Hashtable<>();
+ private final List<DexEncodedArray> encodedArraysList = new LinkedList<>();
+
+ private <T> boolean add(Map<T, Integer> map, List<T> list, T item) {
+ boolean notSeen = map.put(item, NOT_SET) == null;
+ if (notSeen) {
+ list.add(item);
+ }
+ return notSeen;
+ }
+
+ @Override
+ public boolean add(DexProgramClass aClassWithData) {
+ return add(classesWithData, classesWithDataList, aClassWithData);
+ }
+
+ @Override
+ public boolean add(DexEncodedArray encodedArray) {
+ return add(encodedArrays, encodedArraysList, encodedArray);
+ }
+
+ @Override
+ public boolean add(DexAnnotationSet annotationSet) {
+ if (annotationSet.isEmpty()) {
+ return false;
+ }
+ return add(annotationSets, annotationSetsList, annotationSet);
+ }
+
+ @Override
+ public boolean add(DexCode code) {
+ return add(codes, codesList, code);
+ }
+
+ @Override
+ public boolean add(DexDebugInfo debugInfo) {
+ return add(debugInfos, debugInfosList, debugInfo);
+ }
+
+ @Override
+ public boolean add(DexTypeList typeList) {
+ if (typeList.isEmpty()) {
+ return false;
+ }
+ return add(typeLists, typeListsList, typeList);
+ }
+
+ @Override
+ public boolean add(DexAnnotationSetRefList annotationSetRefList) {
+ if (annotationSetRefList.isEmpty()) {
+ return false;
+ }
+ return add(annotationSetRefLists, annotationSetRefListsList, annotationSetRefList);
+ }
+
+ @Override
+ public boolean add(DexAnnotation annotation) {
+ return add(annotations, annotationsList, annotation);
+ }
+
+ public boolean add(DexString string) {
+ return add(stringData, stringDataList, string);
+ }
+
+ public List<DexCode> getCodes() {
+ return Collections.unmodifiableList(codesList);
+ }
+
+ public List<DexDebugInfo> getDebugInfos() {
+ return Collections.unmodifiableList(debugInfosList);
+ }
+
+ public List<DexTypeList> getTypeLists() {
+ return Collections.unmodifiableList(typeListsList);
+ }
+
+ public List<DexString> getStringData() {
+ return Collections.unmodifiableList(stringDataList);
+ }
+
+ public List<DexAnnotation> getAnnotations() {
+ return Collections.unmodifiableList(annotationsList);
+ }
+
+ public List<DexAnnotationSet> getAnnotationSets() {
+ return Collections.unmodifiableList(annotationSetsList);
+ }
+
+ public List<DexAnnotationSetRefList> getAnnotationSetRefLists() {
+ return Collections.unmodifiableList(annotationSetRefListsList);
+ }
+
+ public List<DexProgramClass> getClassesWithData() {
+ return Collections.unmodifiableList(classesWithDataList);
+ }
+
+ public List<DexEncodedArray> getEncodedArrays() {
+ return Collections.unmodifiableList(encodedArraysList);
+ }
+
+ private <T> int lookup(T item, Map<T, Integer> table) {
+ if (item == null) {
+ return Constants.NO_OFFSET;
+ }
+ Integer offset = table.get(item);
+ assert offset != null;
+ assert offset != NOT_SET;
+ return offset;
+ }
+
+ public int getOffsetFor(DexString item) {
+ return lookup(item, stringData);
+ }
+
+ public int getOffsetFor(DexTypeList parameters) {
+ if (parameters.isEmpty()) {
+ return 0;
+ }
+ return lookup(parameters, typeLists);
+ }
+
+ public int getOffsetFor(DexProgramClass aClassWithData) {
+ return lookup(aClassWithData, classesWithData);
+ }
+
+ public int getOffsetFor(DexEncodedArray encodedArray) {
+ return lookup(encodedArray, encodedArrays);
+ }
+
+ public int getOffsetFor(DexDebugInfo debugInfo) {
+ return lookup(debugInfo, debugInfos);
+ }
+
+
+ public int getOffsetForAnnotationsDirectory(DexProgramClass clazz) {
+ Integer offset = annotationDirectories.get(clazz);
+ if (offset == null) {
+ return Constants.NO_OFFSET;
+ }
+ return offset;
+ }
+
+ public int getOffsetFor(DexAnnotation annotation) {
+ return lookup(annotation, annotations);
+ }
+
+ public int getOffsetFor(DexAnnotationSet annotationSet) {
+ if (annotationSet.isEmpty()) {
+ return 0;
+ }
+ return lookup(annotationSet, annotationSets);
+ }
+
+ public int getOffsetFor(DexAnnotationSetRefList annotationSetRefList) {
+ if (annotationSetRefList.isEmpty()) {
+ return 0;
+ }
+ return lookup(annotationSetRefList, annotationSetRefLists);
+ }
+
+ public int getOffsetFor(DexCode code) {
+ return lookup(code, codes);
+ }
+
+ private <T> void setOffsetFor(T item, int offset, Map<T, Integer> table) {
+ Integer old = table.put(item, offset);
+ assert old != null;
+ assert old <= NOT_SET;
+ }
+
+ void setOffsetFor(DexDebugInfo debugInfo, int offset) {
+ setOffsetFor(debugInfo, offset, debugInfos);
+ }
+
+ void setOffsetFor(DexCode code, int offset) {
+ setOffsetFor(code, offset, codes);
+ }
+
+ void setOffsetFor(DexTypeList typeList, int offset) {
+ assert offset != 0 && !typeLists.isEmpty();
+ setOffsetFor(typeList, offset, typeLists);
+ }
+
+ void setOffsetFor(DexString string, int offset) {
+ setOffsetFor(string, offset, stringData);
+ }
+
+ void setOffsetFor(DexAnnotation annotation, int offset) {
+ setOffsetFor(annotation, offset, annotations);
+ }
+
+ void setOffsetFor(DexAnnotationSet annotationSet, int offset) {
+ setOffsetFor(annotationSet, offset, annotationSets);
+ }
+
+ void setOffsetForAnnotationsDirectory(DexProgramClass clazz,
+ int offset) {
+ Integer previous = annotationDirectories.put(clazz, offset);
+ assert previous == null;
+ }
+
+ void setOffsetFor(DexProgramClass aClassWithData, int offset) {
+ setOffsetFor(aClassWithData, offset, classesWithData);
+ }
+
+ void setOffsetFor(DexEncodedArray encodedArray, int offset) {
+ setOffsetFor(encodedArray, offset, encodedArrays);
+ }
+
+ void setOffsetFor(DexAnnotationSetRefList annotationSetRefList, int offset) {
+ assert offset != 0 && !annotationSetRefList.isEmpty();
+ setOffsetFor(annotationSetRefList, offset, annotationSetRefLists);
+ }
+ }
+
+ private class ProgramClassDependencyCollector extends ProgramClassVisitor {
+
+ private final Set<DexClass> includedClasses = Sets.newIdentityHashSet();
+
+ ProgramClassDependencyCollector(DexApplication application, DexProgramClass[] includedClasses) {
+ super(application);
+ Collections.addAll(this.includedClasses, includedClasses);
+ }
+
+ @Override
+ public void visit(DexType type) {
+ // Intentionally left empty.
+ }
+
+ @Override
+ public void visit(DexClass clazz) {
+ // Only visit classes that are part of the current file.
+ if (!includedClasses.contains(clazz)) {
+ return;
+ }
+ clazz.addDependencies(mixedSectionOffsets);
+ }
+ }
+
+ private void checkThatInvokeCustomIsAllowed() {
+ if (!options.canUseInvokeCustom()) {
+ throw new CompilationError("Invoke-custom is unsupported before Android O (--min-sdk-version "
+ + Constants.ANDROID_O_API + ")");
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java b/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java
new file mode 100644
index 0000000..73d5d86
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.IndexedDexItem;
+
+/**
+ * Common interface for constant pools.
+ *
+ * <p>This is semantically a wrapper around a number of sets for all subtypes of
+ * {@link IndexedDexItem}. <b>Items should not be added directly to this collection.</b> Instead see
+ * {@link DexItem#collectIndexedItems}.
+ *
+ * <p>Note that the various add methods of this class are not transitive, i.e., they do not add
+ * components of the {@link IndexedDexItem} itself. Use a call to
+ * {@link IndexedDexItem#collectIndexedItems} for this.
+ */
+public interface IndexedItemCollection {
+
+ /**
+ * Adds the given class to the collection.
+ *
+ * <p>Does not add the class' components.
+ *
+ * @return true if the class was not in the pool before.
+ */
+ boolean addClass(DexProgramClass dexProgramClass);
+
+ /**
+ * Adds the given field to the collection.
+ *
+ * <p>Does not add the field's components.
+ *
+ * @return true if the field was not in the pool before.
+ */
+ boolean addField(DexField field);
+
+ /**
+ * Adds the given method to the collection.
+ *
+ * <p>Does not add the method's components.
+ *
+ * @return true if the method was not in the pool before.
+ */
+ boolean addMethod(DexMethod method);
+
+ /**
+ * Adds the given class to the collection.
+ *
+ * <p>Does not add the classes components.
+ *
+ * @return true if the class was not in the pool before.
+ */
+ boolean addString(DexString string);
+
+ /**
+ * Adds the given proto to the collection.
+ *
+ * <p>Does not add the proto's components.
+ *
+ * @return true if the proto was not in the pool before.
+ */
+ boolean addProto(DexProto proto);
+
+ /**
+ * Adds the given type to the collection.
+ *
+ * <p>Does not add the type's components.
+ *
+ * @return true if the type was not in the pool before.
+ */
+ boolean addType(DexType type);
+
+ /**
+ * Adds the given call site to the collection.
+ *
+ * <p>Does not add the call site's components.
+ *
+ * @return true if the call site was not in the pool before.
+ */
+ boolean addCallSite(DexCallSite callSite);
+
+ /**
+ * Adds the given method handle to the collection.
+ *
+ * <p>Does not add the method handle site's components.
+ *
+ * @return true if the method handle was not in the pool before.
+ */
+ boolean addMethodHandle(DexMethodHandle methodHandle);
+
+ default DexString getRenamedName(DexMethod method) {
+ return method.name;
+ }
+
+ default DexString getRenamedName(DexField field) {
+ return field.name;
+ }
+
+ default DexString getRenamedDescriptor(DexType type) {
+ return type.descriptor;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
new file mode 100644
index 0000000..bc3fe98
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
@@ -0,0 +1,98 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedArray;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexTypeList;
+
+/**
+ * Collection of the various components of the mixed section of a dex file.
+ *
+ * <p>This semantically is just a wrapper around a bunch of collections. We do not expose the
+ * collections directly to allow for implementations that under the hood do not use collections.
+ *
+ * <p>See {@link DexItem#collectMixedSectionItems(MixedSectionCollection)} for
+ * information on how to fill a {@link MixedSectionCollection}.
+ */
+public abstract class MixedSectionCollection {
+
+ /**
+ * Adds the given class data to the collection.
+ *
+ * Does not add any dependencies.
+ *
+ * @return true if the item was not added before
+ */
+ public abstract boolean add(DexProgramClass dexClassData);
+
+ /**
+ * Adds the given encoded array to the collection.
+ *
+ * Does not add any dependencies.
+ *
+ * @return true if the item was not added before
+ */
+ public abstract boolean add(DexEncodedArray dexEncodedArray);
+
+ /**
+ * Adds the given annotation set to the collection.
+ *
+ * Does not add any dependencies.
+ *
+ * @return true if the item was not added before
+ */
+ public abstract boolean add(DexAnnotationSet dexAnnotationSet);
+
+ /**
+ * Adds the given code item to the collection.
+ *
+ * Does not add any dependencies.
+ *
+ * @return true if the item was not added before
+ */
+ public abstract boolean add(DexCode dexCode);
+
+ /**
+ * Adds the given debug info to the collection.
+ *
+ * Does not add any dependencies.
+ *
+ * @return true if the item was not added before
+ */
+ public abstract boolean add(DexDebugInfo dexDebugInfo);
+
+ /**
+ * Adds the given type list to the collection.
+ *
+ * Does not add any dependencies.
+ *
+ * @return true if the item was not added before
+ */
+ public abstract boolean add(DexTypeList dexTypeList);
+
+ /**
+ * Adds the given annotation-set reference list to the collection.
+ *
+ * Does not add any dependencies.
+ *
+ * @return true if the item was not added before
+ */
+ public abstract boolean add(DexAnnotationSetRefList annotationSetRefList);
+
+ /**
+ * Adds the given annotation to the collection.
+ *
+ * Does not add any dependencies.
+ *
+ * @return true if the item was not added before
+ */
+ public abstract boolean add(DexAnnotation annotation);
+}
diff --git a/src/main/java/com/android/tools/r8/dex/Segment.java b/src/main/java/com/android/tools/r8/dex/Segment.java
new file mode 100644
index 0000000..32b0c0b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/Segment.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+public class Segment {
+
+ final int type;
+ final int size;
+ public final int offset;
+ int end;
+
+ public Segment(int type, int unused, int size, int offset) {
+ this.type = type;
+ assert unused == 0;
+ this.size = size;
+ this.offset = offset;
+ this.end = -1;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ void setEnd(int end) {
+ this.end = end;
+ }
+
+ String typeName() {
+ switch (type) {
+ case Constants.TYPE_HEADER_ITEM:
+ return "Header";
+ case Constants.TYPE_STRING_ID_ITEM:
+ return "Strings";
+ case Constants.TYPE_TYPE_ID_ITEM:
+ return "Types";
+ case Constants.TYPE_PROTO_ID_ITEM:
+ return "Protos";
+ case Constants.TYPE_FIELD_ID_ITEM:
+ return "Fields";
+ case Constants.TYPE_METHOD_ID_ITEM:
+ return "Methods";
+ case Constants.TYPE_CLASS_DEF_ITEM:
+ return "Class defs";
+ case Constants.TYPE_MAP_LIST:
+ return "Maps";
+ case Constants.TYPE_TYPE_LIST:
+ return "Type lists";
+ case Constants.TYPE_ANNOTATION_SET_REF_LIST:
+ return "Annotation set refs";
+ case Constants.TYPE_ANNOTATION_SET_ITEM:
+ return "Annotation sets";
+ case Constants.TYPE_CLASS_DATA_ITEM:
+ return "Class data";
+ case Constants.TYPE_CODE_ITEM:
+ return "Code";
+ case Constants.TYPE_STRING_DATA_ITEM:
+ return "String data";
+ case Constants.TYPE_DEBUG_INFO_ITEM:
+ return "Debug info";
+ case Constants.TYPE_ANNOTATION_ITEM:
+ return "Annotation";
+ case Constants.TYPE_ENCODED_ARRAY_ITEM:
+ return "Encoded arrays";
+ case Constants.TYPE_ANNOTATIONS_DIRECTORY_ITEM:
+ return "Annotations directory";
+ default:
+ return "Unknown";
+ }
+ }
+
+ public String toString() {
+ return typeName() + " @" + offset + " " + size;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
new file mode 100644
index 0000000..e575cee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -0,0 +1,831 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.PackageDistribution;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.function.Function;
+
+public class VirtualFile {
+
+ private static final int MAX_ENTRIES = (Short.MAX_VALUE << 1) + 1;
+ /**
+ * When distributing classes across files we aim to leave some space. The amount of space left is
+ * driven by this constant.
+ */
+ private static final int MAX_PREFILL_ENTRIES = MAX_ENTRIES - 5000;
+
+ private final int id;
+ private final VirtualFileIndexedItemCollection indexedItems;
+ private final IndexedItemTransaction transaction;
+
+ private VirtualFile(int id, NamingLens namingLens) {
+ this.id = id;
+ this.indexedItems = new VirtualFileIndexedItemCollection(id);
+ this.transaction = new IndexedItemTransaction(indexedItems, namingLens);
+ }
+
+ public static Map<Integer, VirtualFile> fileSetFrom(
+ ApplicationWriter writer,
+ PackageDistribution packageDistribution,
+ ExecutorService executorService)
+ throws ExecutionException, IOException {
+ // Strategy for distributing classes for write out:
+ // 1. Place all files in the package distribution file in the proposed files (if any).
+ // 2. Place the remaining files based on their packages in sorted order.
+ DexApplication application = writer.application;
+ InternalOptions options = writer.options;
+ Map<Integer, VirtualFile> nameToFileMap = new LinkedHashMap<>();
+ if (packageDistribution != null) {
+ int maxReferencedIndex = packageDistribution.maxReferencedIndex();
+ for (int index = 0; index <= maxReferencedIndex; index++) {
+ VirtualFile file = new VirtualFile(index, writer.namingLens);
+ nameToFileMap.put(index, file);
+ }
+ } else {
+ // If we had no map we default to 1 file, the package populator will add more if needed.
+ nameToFileMap.put(0, new VirtualFile(0, writer.namingLens));
+ }
+ Set<DexProgramClass> classes = Sets.newHashSet(application.classes());
+
+ // Compute the original names.
+ Map<DexProgramClass, String> originalNames = computeOriginalNameMapping(classes,
+ application.getProguardMap());
+
+ if (application.mainDexList != null) {
+ VirtualFile mainDexFile = nameToFileMap.get(0);
+ for (DexType type : application.mainDexList) {
+ DexClass clazz = application.definitionFor(type);
+ if (clazz != null && clazz.isProgramClass()) {
+ DexProgramClass programClass = (DexProgramClass) clazz;
+ mainDexFile.addClass(programClass);
+ if (mainDexFile.isFull()) {
+ throw new CompilationError("Cannot fit requested classes in main-dex file.");
+ }
+ classes.remove(programClass);
+ } else {
+ System.out.println(
+ "WARNING: Application does not contain `"
+ + type.toDescriptorString()
+ + "` as referenced in main-dex-list.");
+ }
+ mainDexFile.commitTransaction();
+ }
+ }
+
+ // Sort the classes based on the original names.
+ // This with make classes from the same package be adjacent.
+ classes = sortClassesByPackage(classes, originalNames);
+
+ Set<String> usedPrefixes = null;
+ if (packageDistribution != null) {
+ ArrayList<Future<List<DexProgramClass>>> futures = new ArrayList<>(nameToFileMap.size());
+ usedPrefixes = packageDistribution.getFiles();
+ for (VirtualFile file : nameToFileMap.values()) {
+ PackageMapPopulator populator =
+ new PackageMapPopulator(file, classes, packageDistribution, originalNames);
+ futures.add(executorService.submit(populator));
+ }
+ ThreadUtils.awaitFutures(futures).forEach(classes::removeAll);
+ }
+
+ // TODO(zerny): Add the package map to AndroidApp and refactor its generation.
+ Path newPackageMap = Paths.get("package.map");
+ Map<String, Integer> newAssignments;
+ if (classes.isEmpty()) {
+ newAssignments = Collections.emptyMap();
+ } else {
+ newAssignments =
+ new PackageSplitPopulator(
+ nameToFileMap, classes, originalNames, usedPrefixes, application.dexItemFactory,
+ options, writer.namingLens)
+ .call();
+ if (!newAssignments.isEmpty() && nameToFileMap.size() > 1) {
+ if (packageDistribution == null) {
+ System.out.println(" * Consider using a package map to improve patch sizes.");
+ } else {
+ System.err.println(" * The used package map is missing entries. The following default "
+ + "mappings have been used:");
+ Writer output = new OutputStreamWriter(System.err);
+ for (Entry<String, Integer> entry : newAssignments.entrySet()) {
+ output.write(" ");
+ PackageDistribution.formatEntry(entry, output);
+ output.write("\n");
+ }
+ output.flush();
+ System.err.println(" * Consider updating the map.");
+ }
+ }
+ }
+ if (packageDistribution != null || nameToFileMap.size() > 1) {
+ System.out.println(" - " + newPackageMap.toString());
+ PackageDistribution.writePackageToFileMap(newPackageMap, newAssignments, packageDistribution);
+ }
+ return nameToFileMap;
+ }
+
+ public static String deriveCommonPrefixAndSanityCheck(List<String> fileNames) {
+ Iterator<String> nameIterator = fileNames.iterator();
+ String first = nameIterator.next();
+ if (!first.toLowerCase().endsWith(FileUtils.DEX_EXTENSION)) {
+ throw new RuntimeException("Illegal suffix for dex file: `" + first + "`.");
+ }
+ String prefix = first.substring(0, first.length() - FileUtils.DEX_EXTENSION.length());
+ int index = 2;
+ while (nameIterator.hasNext()) {
+ String next = nameIterator.next();
+ if (!next.toLowerCase().endsWith(FileUtils.DEX_EXTENSION)) {
+ throw new RuntimeException("Illegal suffix for dex file: `" + first + "`.");
+ }
+ if (!next.startsWith(prefix)) {
+ throw new RuntimeException("Input filenames lack common prefix.");
+ }
+ String numberPart = next.substring(prefix.length(), next.length() - FileUtils.DEX_EXTENSION.length());
+ if (Integer.parseInt(numberPart) != index++) {
+ throw new RuntimeException("DEX files are not numbered consecutively.");
+ }
+ }
+ return prefix;
+ }
+
+ private static Map<DexProgramClass, String> computeOriginalNameMapping(
+ Collection<DexProgramClass> classes,
+ ClassNameMapper proguardMap) {
+ Map<DexProgramClass, String> originalNames = new HashMap<>();
+ classes.forEach((DexProgramClass c) ->
+ originalNames.put(c,
+ DescriptorUtils.descriptorToJavaType(c.type.toDescriptorString(), proguardMap)));
+ return originalNames;
+ }
+
+ private static TreeSet<DexProgramClass> sortClassesByPackage(Set<DexProgramClass> classes,
+ Map<DexProgramClass, String> originalNames) {
+ TreeSet<DexProgramClass> sortedClasses = new TreeSet<>(
+ (DexProgramClass a, DexProgramClass b) -> {
+ String originalA = originalNames.get(a);
+ String originalB = originalNames.get(b);
+ int indexA = originalA.lastIndexOf('.');
+ int indexB = originalB.lastIndexOf('.');
+ if (indexA == -1 && indexB == -1) {
+ // Empty package, compare the class names.
+ return originalA.compareTo(originalB);
+ }
+ if (indexA == -1) {
+ // Empty package name comes first.
+ return -1;
+ }
+ if (indexB == -1) {
+ // Empty package name comes first.
+ return 1;
+ }
+ String prefixA = originalA.substring(0, indexA);
+ String prefixB = originalB.substring(0, indexB);
+ int result = prefixA.compareTo(prefixB);
+ if (result != 0) {
+ return result;
+ }
+ return originalA.compareTo(originalB);
+ });
+ sortedClasses.addAll(classes);
+ return sortedClasses;
+ }
+
+ private static String extractPrefixToken(int prefixLength, String className, boolean addStar) {
+ int index = 0;
+ int lastIndex = 0;
+ int segmentCount = 0;
+ while (lastIndex != -1 && segmentCount++ < prefixLength) {
+ index = lastIndex;
+ lastIndex = className.indexOf('.', index + 1);
+ }
+ String prefix = className.substring(0, index);
+ if (addStar && segmentCount >= prefixLength) {
+ // Full match, add a * to also match sub-packages.
+ prefix += ".*";
+ }
+ return prefix;
+ }
+
+ public ObjectToOffsetMapping computeMapping(DexApplication application) {
+ assert transaction.isEmpty();
+ return new ObjectToOffsetMapping(
+ id,
+ application,
+ indexedItems.classes.toArray(new DexProgramClass[indexedItems.classes.size()]),
+ indexedItems.protos.toArray(new DexProto[indexedItems.protos.size()]),
+ indexedItems.types.toArray(new DexType[indexedItems.types.size()]),
+ indexedItems.methods.toArray(new DexMethod[indexedItems.methods.size()]),
+ indexedItems.fields.toArray(new DexField[indexedItems.fields.size()]),
+ indexedItems.strings.toArray(new DexString[indexedItems.strings.size()]),
+ indexedItems.callSites.toArray(new DexCallSite[indexedItems.callSites.size()]),
+ indexedItems.methodHandles.toArray(new DexMethodHandle[indexedItems.methodHandles.size()]));
+ }
+
+ private void addClass(DexProgramClass clazz) {
+ transaction.addClassAndDependencies(clazz);
+ }
+
+ private static boolean isFull(int numberOfMethods, int numberOfFields, int maximum) {
+ return (numberOfMethods > maximum) || (numberOfFields > maximum);
+ }
+
+ private boolean isFull() {
+ return isFull(transaction.getNumberOfMethods(), transaction.getNumberOfFields(), MAX_ENTRIES);
+ }
+
+ private boolean isFilledEnough(InternalOptions options) {
+ return isFull(
+ transaction.getNumberOfMethods(),
+ transaction.getNumberOfFields(),
+ options.fillDexFiles ? MAX_ENTRIES : MAX_PREFILL_ENTRIES);
+ }
+
+ public void abortTransaction() {
+ transaction.abort();
+ }
+
+ public void commitTransaction() {
+ transaction.commit();
+ }
+
+ public boolean isEmpty() {
+ return indexedItems.classes.isEmpty();
+ }
+
+ public List<DexProgramClass> classes() {
+ return indexedItems.classes;
+ }
+
+ private static class VirtualFileIndexedItemCollection implements IndexedItemCollection {
+
+ final int id;
+
+ private final List<DexProgramClass> classes = new ArrayList<>();
+ private final List<DexProto> protos = new ArrayList<>();
+ private final List<DexType> types = new ArrayList<>();
+ private final List<DexMethod> methods = new ArrayList<>();
+ private final List<DexField> fields = new ArrayList<>();
+ private final List<DexString> strings = new ArrayList<>();
+ private final List<DexCallSite> callSites = new ArrayList<>();
+ private final List<DexMethodHandle> methodHandles = new ArrayList<>();
+
+ private final Set<DexClass> seenClasses = Sets.newIdentityHashSet();
+
+ private VirtualFileIndexedItemCollection(int id) {
+ this.id = id;
+ }
+
+ private <T extends IndexedDexItem> boolean addItem(T item, List<T> itemList) {
+ assert item != null;
+ if (item.assignToVirtualFile(id)) {
+ itemList.add(item);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean addClass(DexProgramClass clazz) {
+ if (seenClasses.add(clazz)) {
+ classes.add(clazz);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean addField(DexField field) {
+ return addItem(field, fields);
+ }
+
+ @Override
+ public boolean addMethod(DexMethod method) {
+ return addItem(method, methods);
+ }
+
+ @Override
+ public boolean addString(DexString string) {
+ return addItem(string, strings);
+ }
+
+ @Override
+ public boolean addProto(DexProto proto) {
+ return addItem(proto, protos);
+ }
+
+ @Override
+ public boolean addCallSite(DexCallSite callSite) {
+ return addItem(callSite, callSites);
+ }
+
+ @Override
+ public boolean addMethodHandle(DexMethodHandle methodHandle) {
+ return addItem(methodHandle, methodHandles);
+ }
+
+ @Override
+ public boolean addType(DexType type) {
+ return addItem(type, types);
+ }
+
+ public int getNumberOfMethods() {
+ return methods.size();
+ }
+
+ public int getNumberOfFields() {
+ return fields.size();
+ }
+
+ public int getNumberOfStrings() {
+ return strings.size();
+ }
+ }
+
+ private static class IndexedItemTransaction implements IndexedItemCollection {
+
+ private final VirtualFileIndexedItemCollection base;
+ private final NamingLens namingLens;
+
+ private final Set<DexProgramClass> classes = new LinkedHashSet<>();
+ private final Set<DexField> fields = new LinkedHashSet<>();
+ private final Set<DexMethod> methods = new LinkedHashSet<>();
+ private final Set<DexType> types = new LinkedHashSet<>();
+ private final Set<DexProto> protos = new LinkedHashSet<>();
+ private final Set<DexString> strings = new LinkedHashSet<>();
+ private final Set<DexCallSite> callSites = new LinkedHashSet<>();
+ private final Set<DexMethodHandle> methodHandles = new LinkedHashSet<>();
+
+ private IndexedItemTransaction(VirtualFileIndexedItemCollection base,
+ NamingLens namingLens) {
+ this.base = base;
+ this.namingLens = namingLens;
+ }
+
+ private <T extends IndexedDexItem> boolean maybeInsert(T item, Set<T> set) {
+ if (item.hasVirtualFileData(base.id) || set.contains(item)) {
+ return false;
+ }
+ set.add(item);
+ return true;
+ }
+
+ void addClassAndDependencies(DexProgramClass clazz) {
+ clazz.collectIndexedItems(this);
+ }
+
+ @Override
+ public boolean addClass(DexProgramClass dexProgramClass) {
+ if (base.seenClasses.contains(dexProgramClass) || classes.contains(dexProgramClass)) {
+ return false;
+ }
+ classes.add(dexProgramClass);
+ return true;
+ }
+
+ @Override
+ public boolean addField(DexField field) {
+ return maybeInsert(field, fields);
+ }
+
+ @Override
+ public boolean addMethod(DexMethod method) {
+ return maybeInsert(method, methods);
+ }
+
+ @Override
+ public boolean addString(DexString string) {
+ return maybeInsert(string, strings);
+ }
+
+ @Override
+ public boolean addProto(DexProto proto) {
+ return maybeInsert(proto, protos);
+ }
+
+ @Override
+ public boolean addType(DexType type) {
+ return maybeInsert(type, types);
+ }
+
+ @Override
+ public boolean addCallSite(DexCallSite callSite) {
+ return maybeInsert(callSite, callSites);
+ }
+
+ @Override
+ public boolean addMethodHandle(DexMethodHandle methodHandle) {
+ return maybeInsert(methodHandle, methodHandles);
+ }
+
+ @Override
+ public DexString getRenamedDescriptor(DexType type) {
+ return namingLens.lookupDescriptor(type);
+ }
+
+ @Override
+ public DexString getRenamedName(DexMethod method) {
+ assert namingLens.checkTargetCanBeTranslated(method);
+ return namingLens.lookupName(method);
+ }
+
+ @Override
+ public DexString getRenamedName(DexField field) {
+ return namingLens.lookupName(field);
+ }
+
+ int getNumberOfMethods() {
+ return methods.size() + base.getNumberOfMethods();
+ }
+
+ int getNumberOfFields() {
+ return fields.size() + base.getNumberOfFields();
+ }
+
+ private <T extends DexItem> void commitItemsIn(Set<T> set, Function<T, Boolean> hook) {
+ set.forEach((item) -> {
+ boolean newlyAdded = hook.apply(item);
+ assert newlyAdded;
+ });
+ set.clear();
+ }
+
+ void commit() {
+ commitItemsIn(classes, base::addClass);
+ commitItemsIn(fields, base::addField);
+ commitItemsIn(methods, base::addMethod);
+ commitItemsIn(protos, base::addProto);
+ commitItemsIn(types, base::addType);
+ commitItemsIn(strings, base::addString);
+ commitItemsIn(callSites, base::addCallSite);
+ commitItemsIn(methodHandles, base::addMethodHandle);
+ }
+
+ void abort() {
+ classes.clear();
+ fields.clear();
+ methods.clear();
+ protos.clear();
+ types.clear();
+ strings.clear();
+ }
+
+ public boolean isEmpty() {
+ return classes.isEmpty() && fields.isEmpty() && methods.isEmpty() && protos.isEmpty()
+ && types.isEmpty() && strings.isEmpty();
+ }
+
+ int getNumberOfStrings() {
+ return strings.size() + base.getNumberOfStrings();
+ }
+
+ int getNumberOfClasses() {
+ return classes.size() + base.classes.size();
+ }
+ }
+
+ /**
+ * Adds all classes from the given set that are covered by a corresponding package map
+ * specification to the given file.
+ */
+ private static class PackageMapPopulator implements Callable<List<DexProgramClass>> {
+
+ private final VirtualFile file;
+ private final Collection<DexProgramClass> classes;
+ private final PackageDistribution packageDistribution;
+ private final Map<DexProgramClass, String> originalNames;
+
+ PackageMapPopulator(
+ VirtualFile file,
+ Collection<DexProgramClass> classes,
+ PackageDistribution packageDistribution,
+ Map<DexProgramClass, String> originalNames) {
+ this.file = file;
+ this.classes = classes;
+ this.packageDistribution = packageDistribution;
+ this.originalNames = originalNames;
+ }
+
+ public List<DexProgramClass> call() {
+ String currentPrefix = null;
+ int currentFileId = -1;
+ List<DexProgramClass> inserted = new ArrayList<>();
+ for (DexProgramClass clazz : classes) {
+ String originalName = originalNames.get(clazz);
+ assert originalName != null;
+ if (!coveredByPrefix(originalName, currentPrefix)) {
+ if (currentPrefix != null) {
+ file.commitTransaction();
+ }
+ currentPrefix = lookupPrefixFor(originalName);
+ if (currentPrefix == null) {
+ currentFileId = -1;
+ } else {
+ currentFileId = packageDistribution.get(currentPrefix);
+ }
+ }
+ if (currentFileId == file.id) {
+ file.addClass(clazz);
+ inserted.add(clazz);
+ }
+ if (file.isFull()) {
+ throw new RuntimeException(
+ "Cannot fit package " + currentPrefix
+ + " in requested dex file, consider to remove mapping.");
+ }
+ }
+ file.commitTransaction();
+ return inserted;
+ }
+
+ private String lookupPrefixFor(String originalName) {
+ // First, check whether we have a match on the full package name.
+ int lastIndexOfDot = originalName.lastIndexOf('.');
+ if (lastIndexOfDot < 0) {
+ return null;
+ }
+ String prefix = originalName.substring(0, lastIndexOfDot);
+ if (packageDistribution.containsFile(prefix)) {
+ return prefix;
+ }
+ // Second, look for .* qualified entries.
+ int index;
+ prefix = originalName;
+ while ((index = prefix.lastIndexOf('.')) != -1) {
+ prefix = prefix.substring(0, index);
+ if (packageDistribution.containsFile(prefix + ".*")) {
+ return prefix + ".*";
+ }
+ }
+ return null;
+ }
+
+ static boolean coveredByPrefix(String originalName, String currentPrefix) {
+ if (currentPrefix == null) {
+ return false;
+ }
+ if (currentPrefix.endsWith(".*")) {
+ return originalName.startsWith(currentPrefix.substring(0, currentPrefix.length() - 2));
+ } else {
+ return originalName.startsWith(currentPrefix)
+ && originalName.lastIndexOf('.') == currentPrefix.length();
+ }
+ }
+ }
+
+ /**
+ * Distributes the given classes over the files in package order.
+ *
+ * <p>The populator avoids package splits. Big packages are split into subpackages if their size
+ * exceeds 20% of the dex file. This populator also avoids filling files completely to cater for
+ * future growth.
+ *
+ * <p>The populator cycles through the files until all classes have been successfully placed and
+ * adds new files to the passed in map if it can't fit in the existing files.
+ */
+ private static class PackageSplitPopulator implements Callable<Map<String, Integer>> {
+
+ /**
+ * Android suggests com.company.product for package names, so the components will be at level 4
+ */
+ private static final int MINIMUM_PREFIX_LENGTH = 4;
+ private static final int MAXIMUM_PREFIX_LENGTH = 7;
+ /**
+ * We allow 1/MIN_FILL_FACTOR of a file to remain empty when moving to the next file, i.e., a
+ * rollback with less than 1/MAX_FILL_FACTOR of the total classes in a file will move to the
+ * next file.
+ */
+ private static final int MIN_FILL_FACTOR = 5;
+
+ private final Map<Integer, VirtualFile> files;
+ private final List<DexProgramClass> classes;
+ private final Map<DexProgramClass, String> originalNames;
+ private final Set<String> previousPrefixes;
+ private final DexItemFactory dexItemFactory;
+ private final InternalOptions options;
+ private final NamingLens namingLens;
+
+ PackageSplitPopulator(
+ Map<Integer, VirtualFile> files,
+ Set<DexProgramClass> classes,
+ Map<DexProgramClass, String> originalNames,
+ Set<String> previousPrefixes,
+ DexItemFactory dexItemFactory,
+ InternalOptions options,
+ NamingLens namingLens) {
+ this.files = files;
+ this.classes = new ArrayList<>(classes);
+ this.originalNames = originalNames;
+ this.previousPrefixes = previousPrefixes;
+ this.dexItemFactory = dexItemFactory;
+ this.options = options;
+ this.namingLens = namingLens;
+ }
+
+ private String getOriginalName(DexProgramClass clazz) {
+ return originalNames != null ? originalNames.get(clazz) : clazz.toString();
+ }
+
+ public Map<String, Integer> call() throws IOException {
+ Iterator<VirtualFile> allFilesCyclic = Iterators.cycle(files.values());
+ Iterator<VirtualFile> activeFiles = Iterators.limit(allFilesCyclic, files.size());
+ int prefixLength = MINIMUM_PREFIX_LENGTH;
+ int transactionStartIndex = 0;
+ int fileStartIndex = 0;
+ String currentPrefix = null;
+ Map<String, Integer> newPackageAssignments = new LinkedHashMap<>();
+ VirtualFile current = activeFiles.next();
+ List<DexProgramClass> nonPackageClasses = new ArrayList<>();
+ for (int classIndex = 0; classIndex < classes.size(); classIndex++) {
+ DexProgramClass clazz = classes.get(classIndex);
+ String originalName = getOriginalName(clazz);
+ if (!PackageMapPopulator.coveredByPrefix(originalName, currentPrefix)) {
+ if (currentPrefix != null) {
+ current.commitTransaction();
+ // Reset the iterator to again iterate over all files, starting with the current one.
+ activeFiles = Iterators.limit(allFilesCyclic, files.size());
+ assert !newPackageAssignments.containsKey(currentPrefix);
+ newPackageAssignments.put(currentPrefix, current.id);
+ // Try to reduce the prefix length if possible. Only do this on a successful commit.
+ prefixLength = MINIMUM_PREFIX_LENGTH - 1;
+ }
+ String newPrefix;
+ // Also, we need to avoid new prefixes that are a prefix of previously used prefixes, as
+ // otherwise we might generate an overlap that will trigger problems when reusing the
+ // package mapping generated here. For example, if an existing map contained
+ // com.android.foo.*
+ // but we now try to place some new subpackage
+ // com.android.bar.*,
+ // we locally could use
+ // com.android.*.
+ // However, when writing out the final package map, we get overlapping patterns
+ // com.android.* and com.android.foo.*.
+ do {
+ newPrefix = extractPrefixToken(++prefixLength, originalName, false);
+ } while (currentPrefix != null &&
+ (currentPrefix.startsWith(newPrefix)
+ || conflictsWithPreviousPrefix(newPrefix, originalName)));
+ // Don't set the current prefix if we did not extract one.
+ if (!newPrefix.equals("")) {
+ currentPrefix = extractPrefixToken(prefixLength, originalName, true);
+ }
+ transactionStartIndex = classIndex;
+ }
+ if (currentPrefix != null) {
+ assert clazz.superType != null || clazz.type == dexItemFactory.objectType;
+ current.addClass(clazz);
+ } else {
+ assert clazz.superType != null;
+ // We don't have a package, add this to a list of classes that we will add last.
+ assert current.transaction.isEmpty();
+ nonPackageClasses.add(clazz);
+ continue;
+ }
+ if (current.isFilledEnough(options) || current.isFull()) {
+ current.abortTransaction();
+ // We allow for a final rollback that has at most 20% of classes in it.
+ // This is a somewhat random number that was empirically chosen.
+ if (classIndex - transactionStartIndex > (classIndex - fileStartIndex) / MIN_FILL_FACTOR
+ && prefixLength < MAXIMUM_PREFIX_LENGTH) {
+ prefixLength++;
+ } else {
+ // Reset the state to after the last commit and cycle through files.
+ // The idea is that we do not increase the number of files, so it has to fit
+ // somewhere.
+ fileStartIndex = transactionStartIndex;
+ if (!activeFiles.hasNext()) {
+ // Special case where we simply will never be able to fit the current package into
+ // one dex file. This is currently the case for Strings in jumbo tests, see:
+ // b/33227518
+ if (current.transaction.getNumberOfClasses() == 0) {
+ for (int j = transactionStartIndex; j <= classIndex; j++) {
+ nonPackageClasses.add(classes.get(j));
+ }
+ transactionStartIndex = classIndex + 1;
+ }
+ // All files are filled up to the 20% mark.
+ files.put(files.size(), new VirtualFile(files.size(), namingLens));
+ allFilesCyclic = Iterators.cycle(files.values());
+ activeFiles = Iterators.limit(allFilesCyclic, files.size());
+ }
+ current = activeFiles.next();
+ }
+ currentPrefix = null;
+ // Go back to previous start index.
+ classIndex = transactionStartIndex - 1;
+ assert current != null;
+ }
+ }
+ current.commitTransaction();
+ assert !newPackageAssignments.containsKey(currentPrefix);
+ if (currentPrefix != null) {
+ newPackageAssignments.put(currentPrefix, current.id);
+ }
+ if (nonPackageClasses.size() > 0) {
+ addNonPackageClasses(Iterators.limit(allFilesCyclic, files.size()), nonPackageClasses);
+ }
+ return newPackageAssignments;
+ }
+
+ private void addNonPackageClasses(Iterator<VirtualFile> activeFiles,
+ List<DexProgramClass> nonPackageClasses) throws IOException {
+ VirtualFile current;
+ current = activeFiles.next();
+ for (DexProgramClass clazz : nonPackageClasses) {
+ if (current.isFilledEnough(options)) {
+ current = getVirtualFile(activeFiles);
+ }
+ current.addClass(clazz);
+ while (current.isFull()) {
+ // This only happens if we have a huge class, that takes up more than 20% of a dex file.
+ current.abortTransaction();
+ current = getVirtualFile(activeFiles);
+ boolean wasEmpty = current.isEmpty();
+ current.addClass(clazz);
+ if (wasEmpty && current.isFull()) {
+ throw new InternalCompilerError(
+ "Class " + clazz.toString() + " does not fit into a single dex file.");
+ }
+ }
+ current.commitTransaction();
+ }
+ }
+
+ private VirtualFile getVirtualFile(Iterator<VirtualFile> activeFiles) throws IOException {
+ VirtualFile current = null;
+ while (activeFiles.hasNext() && (current = activeFiles.next()).isFilledEnough(options)) {}
+ if (current == null || current.isFilledEnough(options)) {
+ current = new VirtualFile(files.size(), namingLens);
+ files.put(files.size(), current);
+ }
+ return current;
+ }
+
+ private boolean conflictsWithPreviousPrefix(String newPrefix, String originalName) {
+ if (previousPrefixes == null) {
+ return false;
+ }
+ for (String previous : previousPrefixes) {
+ // Check whether a previous prefix starts with this new prefix and, if so,
+ // whether the new prefix already is maximal. So for example a new prefix of
+ // foo.bar
+ // would match
+ // foo.bar.goo.*
+ // However, if the original class is
+ // foo.bar.X
+ // then this prefix is the best we can do, and will not turn into a .* prefix and
+ // thus does not conflict.
+ if (previous.startsWith(newPrefix)
+ && (originalName.lastIndexOf('.') > newPrefix.length())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/CompilationError.java b/src/main/java/com/android/tools/r8/errors/CompilationError.java
new file mode 100644
index 0000000..060699e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/CompilationError.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.errors;
+
+/**
+ * Exception to signal an compilation error.
+ *
+ * This is always an expected error and considered a user input issue.
+ * A user-understandable message must be provided.
+ */
+public class CompilationError extends RuntimeException {
+
+ public CompilationError(String message) {
+ super(message);
+ }
+
+ public CompilationError(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java b/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java
new file mode 100644
index 0000000..b51c7ec
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.errors;
+
+/**
+ * Exception to signal an unexpected state internally to the compiler.
+ *
+ * Exceptions of this type always represent an bug in the compiler.
+ * For expected errors, such as invalid input, the compiler should generate a CompilationError.
+ */
+public class InternalCompilerError extends IllegalStateException {
+
+ public InternalCompilerError() {
+ }
+
+ public InternalCompilerError(String s) {
+ super(s);
+ }
+
+ public InternalCompilerError(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public InternalCompilerError(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/Unimplemented.java b/src/main/java/com/android/tools/r8/errors/Unimplemented.java
new file mode 100644
index 0000000..f0acc65
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/Unimplemented.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.errors;
+
+/**
+ * Exception to signal that a not-yet-implemented code path has been hit.
+ */
+public class Unimplemented extends RuntimeException {
+
+ public Unimplemented() {
+ }
+
+ public Unimplemented(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/Unreachable.java b/src/main/java/com/android/tools/r8/errors/Unreachable.java
new file mode 100644
index 0000000..4b17eba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/Unreachable.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.errors;
+
+/**
+ * Exception to signal an expected unreachable code path.
+ */
+public class Unreachable extends InternalCompilerError {
+
+ public Unreachable() {
+ }
+
+ public Unreachable(String s) {
+ super(s);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
new file mode 100644
index 0000000..f5f22e7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -0,0 +1,337 @@
+// 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.graph;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiFunction;
+
+public class AppInfo {
+
+ public final DexItemFactory dexItemFactory;
+ final ImmutableMap<DexType, DexClassPromise> classMap;
+ private final ConcurrentHashMap<DexType, Map<Descriptor, KeyedDexItem>> definitions =
+ new ConcurrentHashMap<>();
+
+ public AppInfo(DexApplication application) {
+ this.dexItemFactory = application.dexItemFactory;
+ this.classMap = application.classMap;
+ }
+
+ protected AppInfo(AppInfo previous) {
+ this.dexItemFactory = previous.dexItemFactory;
+ this.classMap = previous.classMap;
+ this.definitions.putAll(previous.definitions);
+ }
+
+ protected AppInfo(AppInfo previous, GraphLense lense) {
+ // Do not rewrite basic structure, as the type information in the lense is about applied uses
+ // and not definitions.
+ this(previous);
+ }
+
+ private Map<Descriptor, KeyedDexItem> computeDefinitions(DexType type) {
+ Builder<Descriptor, KeyedDexItem> builder = ImmutableMap.builder();
+ DexClassPromise promise = classMap.get(type);
+ if (promise != null) {
+ DexClass clazz = promise.get();
+ registerDefinitions(builder, clazz.directMethods());
+ registerDefinitions(builder, clazz.virtualMethods());
+ registerDefinitions(builder, clazz.instanceFields());
+ registerDefinitions(builder, clazz.staticFields());
+ }
+ return builder.build();
+ }
+
+ private void registerDefinitions(Builder<Descriptor, KeyedDexItem> builder,
+ KeyedDexItem<? extends Descriptor>[] items) {
+ for (KeyedDexItem<? extends Descriptor> item : items) {
+ builder.put(item.getKey(), item);
+ }
+ }
+
+ public Iterable<DexProgramClass> classes() {
+ List<DexProgramClass> result = new ArrayList<>();
+ for (DexClassPromise promise : classMap.values()) {
+ if (promise.isProgramClass()) {
+ result.add(promise.get().asProgramClass());
+ }
+ }
+ return result;
+ }
+
+ public Iterable<DexLibraryClass> libraryClasses() {
+ List<DexLibraryClass> result = new ArrayList<>();
+ for (DexClassPromise promise : classMap.values()) {
+ if (promise.isLibraryClass()) {
+ result.add(promise.get().asLibraryClass());
+ }
+ }
+ return result;
+ }
+
+ public DexClass definitionFor(DexType type) {
+ DexClassPromise promise = classMap.get(type);
+ return promise == null ? null : promise.get();
+ }
+
+ public DexEncodedMethod definitionFor(DexMethod method) {
+ return (DexEncodedMethod) getDefinitions(method.getHolder()).get(method);
+ }
+
+ public DexEncodedField definitionFor(DexField field) {
+ return (DexEncodedField) getDefinitions(field.getHolder()).get(field);
+ }
+
+ private Map<Descriptor, KeyedDexItem> getDefinitions(DexType type) {
+ Map<Descriptor, KeyedDexItem> typeDefinitions = definitions.get(type);
+ if (typeDefinitions != null) {
+ return typeDefinitions;
+ }
+
+ typeDefinitions = computeDefinitions(type);
+ Map<Descriptor, KeyedDexItem> existing = definitions.putIfAbsent(type, typeDefinitions);
+ return existing != null ? existing : typeDefinitions;
+ }
+
+ private DexEncodedMethod lookupDirectStaticOrConstructorTarget(DexMethod method) {
+ assert method.holder.isClassType();
+ return lookupTargetAlongSuperChain(method.holder, method, DexClass::findDirectTarget);
+ }
+
+ /**
+ * Lookup static method following the super chain from the holder of {@code method}.
+ * <p>
+ * This method will lookup only static methods.
+ *
+ * @param method the method to lookup
+ * @return The actual target for {@code method} or {@code null} if none found.
+ */
+ public DexEncodedMethod lookupStaticTarget(DexMethod method) {
+ DexEncodedMethod target = lookupDirectStaticOrConstructorTarget(method);
+ return target == null || target.accessFlags.isStatic() ? target : null;
+ }
+
+ /**
+ * Lookup direct method following the super chain from the holder of {@code method}.
+ * <p>
+ * This method will lookup private and constructor methods.
+ *
+ * @param method the method to lookup
+ * @return The actual target for {@code method} or {@code null} if none found.
+ */
+ public DexEncodedMethod lookupDirectTarget(DexMethod method) {
+ DexEncodedMethod target = lookupDirectStaticOrConstructorTarget(method);
+ return target == null || !target.accessFlags.isStatic() ? target : null;
+ }
+
+ /**
+ * Lookup virtual method starting in type and following the super chain.
+ * <p>
+ * If the target cannot be found along the super-chain, look for a default implementation in one
+ * of the interfaces.
+ */
+ public DexEncodedMethod lookupVirtualTarget(DexType type, DexMethod method) {
+ assert type.isClassType();
+ DexEncodedMethod result
+ = lookupTargetAlongSuperChain(type, method, DexClass::findVirtualTarget);
+ if (result != null) {
+ return result;
+ }
+ return lookupTargetAlongInterfaceChain(type, method,
+ (dexClass, dexMethod) -> {
+ DexEncodedMethod virtualTarget = dexClass.findVirtualTarget(dexMethod);
+ return virtualTarget != null && virtualTarget.getCode() != null
+ ? virtualTarget
+ : null;
+ });
+ }
+
+ /**
+ * Lookup virtual method starting in type and following the super chain.
+ * <p>
+ * If the target cannot be found along the super-chain, look for a definition in one of
+ * the interfaces.
+ */
+ public DexEncodedMethod lookupVirtualDefinition(DexType type, DexMethod method) {
+ assert type.isClassType();
+ DexEncodedMethod result
+ = lookupTargetAlongSuperChain(type, method, DexClass::findVirtualTarget);
+ if (result != null) {
+ return result;
+ }
+ return lookupTargetAlongInterfaceChain(type, method, DexClass::findVirtualTarget);
+ }
+
+ /**
+ * Lookup instance field starting in type and following the super chain.
+ */
+ public DexEncodedField lookupInstanceTarget(DexType type, DexField field) {
+ assert type.isClassType();
+ return lookupTargetAlongSuperChain(type, field, DexClass::findInstanceTarget);
+ }
+
+ /**
+ * Lookup static field starting in type and following the super chain.
+ */
+ public DexEncodedField lookupStaticTarget(DexType type, DexField field) {
+ assert type.isClassType();
+ DexEncodedField target = lookupTargetAlongSuperChain(type, field, DexClass::findStaticTarget);
+ if (target == null) {
+ target = lookupTargetAlongInterfaceChain(type, field, DexClass::findStaticTarget);
+ }
+ return target;
+ }
+
+ /**
+ * Traverse along the super chain until lookup returns non-null value.
+ */
+ private <S extends DexItem, T extends Descriptor<S, T>> S lookupTargetAlongSuperChain(
+ DexType type,
+ T desc,
+ BiFunction<DexClass, T, S> lookup) {
+ assert type != null;
+ DexClass holder = definitionFor(type);
+ while (holder != null) {
+ S result = lookup.apply(holder, desc);
+ if (result != null) {
+ return result;
+ }
+ if (holder.superType == null) {
+ return null;
+ }
+ holder = definitionFor(holder.superType);
+ }
+ return null;
+ }
+
+ /**
+ * Traverse along the super chain and the interface chains until lookup returns non-null value.
+ */
+ private <S extends DexItem, T extends Descriptor<S, T>> S lookupTargetAlongSuperAndInterfaceChain(
+ DexType type,
+ T desc,
+ BiFunction<DexClass, T, S> lookup) {
+ DexClass holder = definitionFor(type);
+ if (holder == null) {
+ return null;
+ }
+ S result = lookup.apply(holder, desc);
+ if (result != null) {
+ return result;
+ }
+ if (holder.superType != null) {
+ result = lookupTargetAlongSuperAndInterfaceChain(holder.superType, desc, lookup);
+ if (result != null) {
+ return result;
+ }
+ }
+ for (DexType iface : holder.interfaces.values) {
+ result = lookupTargetAlongSuperAndInterfaceChain(iface, desc, lookup);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+ private boolean isDefaultMethod(DexItem dexItem) {
+ return dexItem != null && dexItem instanceof DexEncodedMethod &&
+ !((DexEncodedMethod) dexItem).accessFlags.isStatic() &&
+ ((DexEncodedMethod) dexItem).getCode() != null;
+ }
+
+ private void checkIfMethodIsAmbiguous(DexItem previousResult, DexItem newResult) {
+ if (previousResult != null
+ && previousResult != newResult
+ && isDefaultMethod(previousResult)
+ && isDefaultMethod(newResult)) {
+ throw new CompilationError("Duplicate default methods named "
+ + previousResult.toSourceString()
+ + " are inherited from the types "
+ + ((DexEncodedMethod) previousResult).method.holder.getName()
+ + " and "
+ + ((DexEncodedMethod) newResult).method.holder.getName());
+ }
+ }
+
+ /**
+ * Traverse along the interface chains until lookup returns non-null value.
+ */
+ private <S extends DexItem, T extends Descriptor<S, T>> S lookupTargetAlongInterfaceChain(
+ DexType type,
+ T desc,
+ BiFunction<DexClass, T, S> lookup) {
+ DexClass holder = definitionFor(type);
+ if (holder == null) {
+ // TODO(herhut): The subtype hierarchy is broken. Handle this case.
+ return null;
+ }
+ S result = null;
+ for (DexType iface : holder.interfaces.values) {
+ S localResult = lookupTargetAlongSuperAndInterfaceChain(iface, desc, lookup);
+ if (localResult != null) {
+ checkIfMethodIsAmbiguous(result, localResult);
+ // Return the first item found, we only continue to detect ambiguous method call.
+ if (result == null) {
+ result = localResult;
+ }
+ }
+ }
+ if (holder.superType != null) {
+ S localResult = lookupTargetAlongInterfaceChain(holder.superType, desc, lookup);
+ if (localResult != null) {
+ checkIfMethodIsAmbiguous(result, localResult);
+ // Return the first item found, we only continue to detect ambiguous method call.
+ if (result == null) {
+ result = localResult;
+ }
+ }
+ }
+ return result;
+ }
+
+ public DexEncodedMethod lookup(Type type, DexMethod target) {
+ DexEncodedMethod definition;
+ DexType holder = target.getHolder();
+ if (!holder.isClassType()) {
+ return null;
+ }
+ if (type == Type.VIRTUAL || type == Type.INTERFACE) {
+ definition = lookupVirtualDefinition(holder, target);
+ } else if (type == Type.DIRECT) {
+ definition = lookupDirectTarget(target);
+ } else if (type == Type.STATIC) {
+ definition = lookupStaticTarget(target);
+ } else if (type == Type.SUPER) {
+ definition = lookupVirtualTarget(holder, target);
+ } else {
+ return null;
+ }
+ return definition;
+ }
+
+ public boolean hasSubtyping() {
+ return false;
+ }
+
+ public AppInfoWithSubtyping withSubtyping() {
+ return null;
+ }
+
+ public boolean hasLiveness() {
+ return false;
+ }
+
+ public AppInfoWithLiveness withLiveness() {
+ return null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
new file mode 100644
index 0000000..17754cc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -0,0 +1,240 @@
+// 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.graph;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+public class AppInfoWithSubtyping extends AppInfo {
+
+ // Set of missing classes, discovered during subtypeMap computation.
+ private Set<DexType> missingClasses = Sets.newIdentityHashSet();
+ // Map from types to their subtypes.
+ private final Hashtable<DexType, ImmutableSet<DexType>> subtypeMap = new Hashtable<>();
+
+ public AppInfoWithSubtyping(DexApplication application) {
+ super(application);
+ populateSubtypeMap(application.classMap, application.dexItemFactory);
+ }
+
+ protected AppInfoWithSubtyping(AppInfoWithSubtyping previous) {
+ super(previous);
+ missingClasses.addAll(previous.missingClasses);
+ subtypeMap.putAll(previous.subtypeMap);
+ }
+
+ protected AppInfoWithSubtyping(AppInfoWithSubtyping previous, GraphLense lense) {
+ super(previous, lense);
+ // Recompute subtype map if we have modified the graph.
+ populateSubtypeMap(previous.classMap, dexItemFactory);
+ }
+
+ public Set<DexType> getMissingClasses() {
+ return Collections.unmodifiableSet(missingClasses);
+ }
+
+ public ImmutableSet<DexType> subtypes(DexType type) {
+ assert type.isClassType();
+ ImmutableSet<DexType> subtypes = subtypeMap.get(type);
+ return subtypes == null ? ImmutableSet.of() : subtypes;
+ }
+
+ private void populateSuperType(Hashtable<DexType, Set<DexType>> map, DexType superType,
+ DexClass baseClass, Function<DexType, DexClass> definitions) {
+ if (superType != null) {
+ Set<DexType> set = map.computeIfAbsent(superType, ignore -> new HashSet<>());
+ if (set.add(baseClass.type)) {
+ // Only continue recursion if type has been added to set.
+ populateAllSuperTypes(map, superType, baseClass, definitions);
+ }
+ }
+ }
+
+ private void populateAllSuperTypes(Hashtable<DexType, Set<DexType>> map, DexType holder,
+ DexClass baseClass, Function<DexType, DexClass> definitions) {
+ DexClass holderClass = definitions.apply(holder);
+ // Skip if no corresponding class is found.
+ if (holderClass != null) {
+ populateSuperType(map, holderClass.superType, baseClass, definitions);
+ if (holderClass.superType != null) {
+ holderClass.superType.addDirectSubtype(holder);
+ } else {
+ // We found java.lang.Object
+ assert dexItemFactory.objectType == holder;
+ }
+ for (DexType inter : holderClass.interfaces.values) {
+ populateSuperType(map, inter, baseClass, definitions);
+ inter.addInterfaceSubtype(holder);
+ }
+ } else {
+ if (!baseClass.isLibraryClass()) {
+ missingClasses.add(holder);
+ }
+ // The subtype chain is broken, at least make this type a subtype of Object.
+ if (holder != dexItemFactory.objectType) {
+ dexItemFactory.objectType.addDirectSubtype(holder);
+ }
+ }
+ }
+
+ private void populateSubtypeMap(
+ Map<DexType, DexClassPromise> classes, DexItemFactory dexItemFactory) {
+ dexItemFactory.clearSubtypeInformation();
+ dexItemFactory.objectType.tagAsSubtypeRoot();
+ Hashtable<DexType, Set<DexType>> map = new Hashtable<>();
+ Function<DexType, DexClass> typeToClass = type -> {
+ DexClassPromise promise = classes.get(type);
+ return promise == null ? null : promise.get();
+ };
+ for (Map.Entry<DexType, DexClassPromise> entry : classes.entrySet()) {
+ populateAllSuperTypes(map, entry.getKey(), entry.getValue().get(), typeToClass);
+ }
+ for (Map.Entry<DexType, Set<DexType>> entry : map.entrySet()) {
+ subtypeMap.put(entry.getKey(), ImmutableSet.copyOf(entry.getValue()));
+ }
+ assert DexType.validateLevelsAreCorrect(typeToClass, dexItemFactory);
+ }
+
+ // For mapping invoke virtual instruction to target methods.
+ public Set<DexEncodedMethod> lookupVirtualTargets(DexMethod method) {
+ Set<DexEncodedMethod> result = new HashSet<>();
+ // First add the target for receiver type method.type.
+ DexClass root = definitionFor(method.holder);
+ if (root == null) {
+ // type specified in method does not have a materialized class.
+ return null;
+ }
+ DexEncodedMethod topMethod = lookupVirtualTarget(method.holder, method);
+ // The top method might be absent if this is an abstract class.
+ if (topMethod != null) {
+ result.add(topMethod);
+ } else {
+ if (!holderIsAbstract(method)) {
+ // This method is missing and the program we have is invalid.
+ // TODO(herhut): Find a better way to handle missing targets.
+ return Collections.emptySet();
+ }
+ }
+ // Add all matching targets from the subclass hierarchy.
+ Set<DexType> set = subtypes(method.holder);
+ if (set != null) {
+ for (DexType type : set) {
+ DexClass clazz = definitionFor(type);
+ if (!clazz.isInterface()) {
+ DexEncodedMethod t = clazz.findVirtualTarget(method);
+ if (t != null) {
+ result.add(t);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * For mapping invoke virtual instruction to single target method.
+ */
+ public DexEncodedMethod lookupSingleVirtualTarget(DexMethod method) {
+ assert method != null;
+ if (method.isSingleVirtualMethodCached()) {
+ return method.getSingleVirtualMethodCache();
+ }
+ DexEncodedMethod result = null;
+ // First add the target for receiver type method.type.
+ DexClass root = definitionFor(method.holder);
+ if (root == null) {
+ // type specified in method does not have a materialized class.
+ return null;
+ }
+ DexEncodedMethod topMethod = lookupVirtualTarget(method.holder, method);
+ // The top method might be absent if this is an abstract class.
+ if (topMethod != null) {
+ result = topMethod;
+ } else {
+ if (!holderIsAbstract(method)) {
+ return null;
+ }
+ }
+ // Search for matching target in subtype hierarchy.
+ Set<DexType> set = subtypes(method.holder);
+ if (set != null) {
+ for (DexType type : set) {
+ DexClass clazz = definitionFor(type);
+ if (!clazz.isInterface()) {
+ DexEncodedMethod t = clazz.findVirtualTarget(method);
+ if (t != null) {
+ if (result != null) {
+ return null; // We have more than one target method.
+ } else {
+ result = t;
+ }
+ }
+ }
+ }
+ }
+ method.setSingleVirtualMethodCache(result);
+ return result;
+ }
+
+ private boolean holderIsAbstract(Descriptor desc) {
+ DexClass holder = definitionFor(desc.getHolder());
+ return holder.accessFlags.isAbstract();
+ }
+
+ // For mapping invoke interface instruction to target methods.
+ public Set<DexEncodedMethod> lookupInterfaceTargets(DexMethod method) {
+ Set<DexEncodedMethod> result = new HashSet<>();
+ Set<DexType> set = subtypes(method.holder);
+ if (set != null) {
+ for (DexType type : set) {
+ DexClass clazz = definitionFor(type);
+ if (!clazz.isInterface()) {
+ DexEncodedMethod targetMethod = lookupVirtualTarget(type, method);
+ if (targetMethod != null) {
+ result.add(targetMethod);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ public DexEncodedMethod lookupSingleInterfaceTarget(DexMethod method) {
+ assert method != null;
+ DexEncodedMethod result = null;
+ Set<DexType> set = subtypes(method.holder);
+ if (set != null) {
+ for (DexType type : set) {
+ DexClass clazz = definitionFor(type);
+ if (!clazz.isInterface()) {
+ DexEncodedMethod t = lookupVirtualTarget(type, method);
+ if (t != null) {
+ if (result != null) {
+ return null;
+ } else {
+ result = t;
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean hasSubtyping() {
+ return true;
+ }
+
+ @Override
+ public AppInfoWithSubtyping withSubtyping() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/CanonicalizedDexItem.java b/src/main/java/com/android/tools/r8/graph/CanonicalizedDexItem.java
new file mode 100644
index 0000000..7872801
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/CanonicalizedDexItem.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+/**
+ * DexItems of this kind have to be canonicalized for the whole application.
+ */
+public abstract class CanonicalizedDexItem extends DexItem {
+
+ private static final int NOT_COMPUTED_HASH_VALUE = -1;
+ private static final int SENTINEL_HASH_VALUE = 0;
+ private volatile int hash = NOT_COMPUTED_HASH_VALUE;
+
+ protected abstract int computeHashCode();
+
+ protected abstract boolean computeEquals(Object other);
+
+ @Override
+ public final int hashCode() {
+ int cache = hash;
+ if (cache == NOT_COMPUTED_HASH_VALUE) {
+ cache = computeHashCode();
+ if (cache == NOT_COMPUTED_HASH_VALUE) {
+ cache = SENTINEL_HASH_VALUE;
+ }
+ hash = cache;
+ }
+ return cache;
+ }
+
+ @Override
+ public void flushCachedValues() {
+ super.flushCachedValues();
+ hash = NOT_COMPUTED_HASH_VALUE;
+ }
+
+ @Override
+ public final boolean equals(Object other) {
+ return this == other || computeEquals(other);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java
new file mode 100644
index 0000000..e2e972c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+public abstract class ClassAndMemberPublicizer {
+
+ private static void publicizeAllMethods(DexEncodedMethod[] methods) {
+ for (DexEncodedMethod method : methods) {
+ method.accessFlags.promoteToPublic();
+ }
+ }
+
+ private static void publicizeAllFields(DexEncodedField[] fields) {
+ for (DexEncodedField field : fields) {
+ field.accessFlags.promoteToPublic();
+ }
+ }
+
+ /**
+ * Marks all package private and protected methods and fields as public.
+ * <p>
+ * This will destructively update the DexApplication passed in as argument.
+ */
+ public static DexApplication run(DexApplication application) {
+ for (DexClass clazz : application.classes()) {
+ clazz.accessFlags.promoteToPublic();
+ publicizeAllFields(clazz.staticFields());
+ publicizeAllFields(clazz.instanceFields());
+ publicizeAllMethods(clazz.directMethods());
+ publicizeAllMethods(clazz.virtualMethods());
+ }
+ return application;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
new file mode 100644
index 0000000..04c01be
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.optimize.Outliner.OutlineCode;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.InternalOptions;
+
+public abstract class Code extends CanonicalizedDexItem {
+
+ public abstract IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options);
+
+ public abstract void registerReachableDefinitions(UseRegistry registry);
+
+ public abstract String toString();
+
+ public abstract String toString(ClassNameMapper naming);
+
+ public boolean isDexCode() {
+ return false;
+ }
+
+ public boolean isJarCode() {
+ return false;
+ }
+
+ public boolean isOutlineCode() {
+ return false;
+ }
+
+ public DexCode asDexCode() {
+ throw new Unreachable();
+ }
+
+ public JarCode asJarCode() {
+ throw new Unreachable();
+ }
+
+ public OutlineCode asOutlineCode() {
+ throw new Unreachable();
+ }
+
+ @Override
+ void collectIndexedItems(IndexedItemCollection collection) {
+ throw new Unreachable();
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection collection) {
+ throw new Unreachable();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
new file mode 100644
index 0000000..4d7d120
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
@@ -0,0 +1,42 @@
+// 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.graph;
+
+public class DebugLocalInfo {
+ public final DexString name;
+ public final DexType type;
+ public final DexString signature;
+
+ public DebugLocalInfo(DexString name, DexType type, DexString signature) {
+ this.name = name;
+ this.type = type;
+ this.signature = signature;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof DebugLocalInfo)) {
+ return false;
+ }
+ DebugLocalInfo o = (DebugLocalInfo) other;
+ return name == o.name && type == o.type && signature == o.signature;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7 * name.hashCode() + 13 * type.hashCode();
+ if (signature != null) {
+ hash += 31 * signature.hashCode();
+ }
+ return hash;
+ }
+
+ @Override
+ public String toString() {
+ return name + ":" + type + (signature == null ? "" : signature);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/Descriptor.java b/src/main/java/com/android/tools/r8/graph/Descriptor.java
new file mode 100644
index 0000000..9065839
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/Descriptor.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+public abstract class Descriptor<T extends DexItem, S extends Descriptor>
+ extends IndexedDexItem implements PresortedComparable<S> {
+
+ public abstract boolean match(T entry);
+
+ public abstract DexType getHolder();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAccessFlags.java b/src/main/java/com/android/tools/r8/graph/DexAccessFlags.java
new file mode 100644
index 0000000..e145158
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexAccessFlags.java
@@ -0,0 +1,289 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.Constants;
+
+public class DexAccessFlags {
+
+ private static final String[] ACC_NAMES = {
+ "public",
+ "private",
+ "protected",
+ "static",
+ "final",
+ "synchronized",
+ "volatile(bridge)",
+ "transient(varargs)",
+ "native",
+ "interface",
+ "abstract",
+ "strictfp",
+ "synthetic",
+ "annotation",
+ "enum",
+ "<unused>",
+ "<init>",
+ "synchronized",
+ };
+
+ private int flags;
+
+ public DexAccessFlags(int flags) {
+ this.flags = flags;
+ }
+
+ public DexAccessFlags(int... flags) {
+ this(combineFlags(flags));
+ }
+
+ private static int combineFlags(int[] flags) {
+ int combined = 0;
+ for (int flag : flags) {
+ combined |= flag;
+ }
+ return combined;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof DexAccessFlags) {
+ return flags == ((DexAccessFlags) other).flags;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return get();
+ }
+
+ public int get() {
+ return flags;
+ }
+
+ public boolean containsAllOf(DexAccessFlags other) {
+ return (flags & other.get()) == other.get();
+ }
+
+ public boolean containsNoneOf(DexAccessFlags other) {
+ return (flags & other.get()) == 0;
+ }
+
+ public boolean isPublic() {
+ return isSet(Constants.ACC_PUBLIC);
+ }
+
+ public void setPublic() {
+ set(Constants.ACC_PUBLIC);
+ }
+
+ public boolean isPrivate() {
+ return isSet(Constants.ACC_PRIVATE);
+ }
+
+ public void setPrivate() {
+ set(Constants.ACC_PRIVATE);
+ }
+
+ public void unsetPrivate() {
+ unset(Constants.ACC_PRIVATE);
+ }
+
+ public boolean isProtected() {
+ return isSet(Constants.ACC_PROTECTED);
+ }
+
+ public void setProtected() {
+ set(Constants.ACC_PROTECTED);
+ }
+
+ public boolean isStatic() {
+ return isSet(Constants.ACC_STATIC);
+ }
+
+ public void setStatic() {
+ set(Constants.ACC_STATIC);
+ }
+
+ public boolean isFinal() {
+ return isSet(Constants.ACC_FINAL);
+ }
+
+ public void setFinal() {
+ set(Constants.ACC_FINAL);
+ }
+
+ public boolean isSynchronized() {
+ return isSet(Constants.ACC_SYNCHRONIZED);
+ }
+
+ public void setSynchronized() {
+ set(Constants.ACC_SYNCHRONIZED);
+ }
+
+ public void unsetSynchronized() {
+ unset(Constants.ACC_SYNCHRONIZED);
+ }
+
+ public boolean isVolatile() {
+ return isSet(Constants.ACC_VOLATILE);
+ }
+
+ public void setVolatile() {
+ set(Constants.ACC_VOLATILE);
+ }
+
+ public boolean isBridge() {
+ return isSet(Constants.ACC_BRIDGE);
+ }
+
+ public void setBridge() {
+ set(Constants.ACC_BRIDGE);
+ }
+
+ public void unsetBridge() {
+ unset(Constants.ACC_BRIDGE);
+ }
+
+ public boolean isTransient() {
+ return isSet(Constants.ACC_TRANSIENT);
+ }
+
+ public void setTransient() {
+ set(Constants.ACC_TRANSIENT);
+ }
+
+ public boolean isVarargs() {
+ return isSet(Constants.ACC_VARARGS);
+ }
+
+ public void setVarargs() {
+ set(Constants.ACC_VARARGS);
+ }
+
+ public boolean isNative() {
+ return isSet(Constants.ACC_NATIVE);
+ }
+
+ public void setNative() {
+ set(Constants.ACC_NATIVE);
+ }
+
+ public boolean isInterface() {
+ return isSet(Constants.ACC_INTERFACE);
+ }
+
+ public void setInterface() {
+ set(Constants.ACC_INTERFACE);
+ }
+
+ public void unsetInterface() {
+ unset(Constants.ACC_INTERFACE);
+ }
+
+ public boolean isAbstract() {
+ return isSet(Constants.ACC_ABSTRACT);
+ }
+
+ public void setAbstract() {
+ set(Constants.ACC_ABSTRACT);
+ }
+
+ public void unsetAbstract() {
+ unset(Constants.ACC_ABSTRACT);
+ }
+
+ public boolean isStrict() {
+ return isSet(Constants.ACC_STRICT);
+ }
+
+ public void setStrict() {
+ set(Constants.ACC_STRICT);
+ }
+
+ public boolean isSynthetic() {
+ return isSet(Constants.ACC_SYNTHETIC);
+ }
+
+ public void setSynthetic() {
+ set(Constants.ACC_SYNTHETIC);
+ }
+
+ public void unsetSynthetic() {
+ unset(Constants.ACC_SYNTHETIC);
+ }
+
+ public boolean isAnnotation() {
+ return isSet(Constants.ACC_ANNOTATION);
+ }
+
+ public void setAnnotation() {
+ set(Constants.ACC_ANNOTATION);
+ }
+
+ public boolean isEnum() {
+ return isSet(Constants.ACC_ENUM);
+ }
+
+ public void setEnum() {
+ set(Constants.ACC_ENUM);
+ }
+
+ public boolean isConstructor() {
+ return isSet(Constants.ACC_CONSTRUCTOR);
+ }
+
+ public void setConstructor() {
+ set(Constants.ACC_CONSTRUCTOR);
+ }
+
+ public boolean isDeclaredSynchronized() {
+ return isSet(Constants.ACC_DECLARED_SYNCHRONIZED);
+ }
+
+ public void setDeclaredSynchronized() {
+ set(Constants.ACC_DECLARED_SYNCHRONIZED);
+ }
+
+ public void promoteToPublic() {
+ if (!isPrivate()) {
+ flags &= ~Constants.ACC_PROTECTED;
+ flags |= Constants.ACC_PUBLIC;
+ }
+ }
+
+ private boolean isSet(int flag) {
+ return (flags & flag) != 0;
+ }
+
+ private void set(int flag) {
+ flags |= flag;
+ }
+
+ private void unset(int flag) {
+ flags &= ~flag;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ int flags = this.flags;
+ flags &= ~Constants.ACC_CONSTRUCTOR; // Don't include the constructor flag in the string.
+ for (int i = 0; i < ACC_NAMES.length && flags != 0; i++, flags >>= 1) {
+ if ((flags & 0x1) != 0) {
+ if (builder.length() > 0) {
+ builder.append(' ');
+ }
+ builder.append(ACC_NAMES[i]);
+ }
+ }
+ assert flags == 0;
+ return builder.toString();
+ }
+
+ public String toSmaliString() {
+ return toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
new file mode 100644
index 0000000..d355ffd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -0,0 +1,262 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
+import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.DexValue.DexValueInt;
+import com.android.tools.r8.graph.DexValue.DexValueMethod;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.DexValue.DexValueType;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DexAnnotation extends DexItem {
+ // Dex system annotations.
+ // See https://source.android.com/devices/tech/dalvik/dex-format.html#system-annotation
+ private static final String ANNOTATION_DEFAULT_DESCRIPTOR =
+ "Ldalvik/annotation/AnnotationDefault;";
+ private static final String ENCLOSING_CLASS_DESCRIPTOR = "Ldalvik/annotation/EnclosingClass;";
+ private static final String ENCLOSING_METHOD_DESCRIPTOR = "Ldalvik/annotation/EnclosingMethod;";
+ private static final String INNER_CLASS_DESCRIPTOR = "Ldalvik/annotation/InnerClass;";
+ private static final String MEMBER_CLASSES_DESCRIPTOR = "Ldalvik/annotation/MemberClasses;";
+ private static final String METHOD_PARAMETERS_DESCRIPTOR = "Ldalvik/annotation/MethodParameters;";
+ private static final String SIGNATURE_DESCRIPTOR = "Ldalvik/annotation/Signature;";
+ private static final String SOURCE_DEBUG_EXTENSION = "Ldalvik/annotation/SourceDebugExtension;";
+ private static final String THROWS_DESCRIPTOR = "Ldalvik/annotation/Throws;";
+
+ public static final int VISIBILITY_BUILD = 0x00;
+ public static final int VISIBILITY_RUNTIME = 0x01;
+ public static final int VISIBILITY_SYSTEM = 0x02;
+ public final int visibility;
+ public final DexEncodedAnnotation annotation;
+
+ public DexAnnotation(int visibility, DexEncodedAnnotation annotation) {
+ this.visibility = visibility;
+ this.annotation = annotation;
+ }
+
+ @Override
+ public int hashCode() {
+ return visibility + annotation.hashCode() * 3;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof DexAnnotation) {
+ DexAnnotation o = (DexAnnotation) other;
+ return (visibility == o.visibility) && annotation.equals(o.annotation);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return visibility + " " + annotation;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ annotation.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ mixedItems.add(this);
+ }
+
+ public static DexAnnotation createEnclosingClassAnnotation(DexType enclosingClass,
+ DexItemFactory factory) {
+ return createSystemValueAnnotation(ENCLOSING_CLASS_DESCRIPTOR, factory,
+ new DexValueType(enclosingClass));
+ }
+
+ public static DexAnnotation createEnclosingMethodAnnotation(DexMethod enclosingMethod,
+ DexItemFactory factory) {
+ return createSystemValueAnnotation(ENCLOSING_METHOD_DESCRIPTOR, factory,
+ new DexValueMethod(enclosingMethod));
+ }
+
+ public static boolean isEnclosingClassAnnotation(DexAnnotation annotation) {
+ return annotation.annotation.type.toDescriptorString().equals(ENCLOSING_CLASS_DESCRIPTOR);
+ }
+
+ public static boolean isEnclosingMethodAnnotation(DexAnnotation annotation) {
+ return annotation.annotation.type.toDescriptorString().equals(ENCLOSING_METHOD_DESCRIPTOR);
+ }
+
+ public static boolean isEnclosingAnnotation(DexAnnotation annotation) {
+ return isEnclosingClassAnnotation(annotation) || isEnclosingMethodAnnotation(annotation);
+ }
+
+ public static boolean isInnerClassesAnnotation(DexAnnotation annotation) {
+ return annotation.annotation.type.toDescriptorString().equals(MEMBER_CLASSES_DESCRIPTOR)
+ || annotation.annotation.type.toDescriptorString().equals(INNER_CLASS_DESCRIPTOR);
+ }
+
+ public static DexAnnotation createInnerClassAnnotation(String clazz, int access,
+ DexItemFactory factory) {
+ return new DexAnnotation(VISIBILITY_SYSTEM,
+ new DexEncodedAnnotation(factory.createType(INNER_CLASS_DESCRIPTOR),
+ new DexAnnotationElement[]{
+ new DexAnnotationElement(
+ factory.createString("accessFlags"),
+ DexValueInt.create(access)),
+ new DexAnnotationElement(
+ factory.createString("name"),
+ (clazz == null)
+ ? DexValue.NULL
+ : new DexValueString(factory.createString(clazz)))
+ }));
+ }
+
+ public static DexAnnotation createMemberClassesAnnotation(List<DexType> classes,
+ DexItemFactory factory) {
+ DexValue[] values = new DexValue[classes.size()];
+ for (int i = 0; i < classes.size(); i++) {
+ values[i] = new DexValueType(classes.get(i));
+ }
+ return createSystemValueAnnotation(MEMBER_CLASSES_DESCRIPTOR, factory,
+ new DexValueArray(values));
+ }
+
+ public static DexAnnotation createSourceDebugExtensionAnnotation(DexValue value,
+ DexItemFactory factory) {
+ return new DexAnnotation(VISIBILITY_SYSTEM,
+ new DexEncodedAnnotation(factory.createType(SOURCE_DEBUG_EXTENSION),
+ new DexAnnotationElement[] {
+ new DexAnnotationElement(factory.createString("value"), value)
+ }));
+ }
+
+ public static DexAnnotation createMethodParametersAnnotation(DexValue[] names,
+ DexValue[] accessFlags, DexItemFactory factory) {
+ assert names.length == accessFlags.length;
+ return new DexAnnotation(VISIBILITY_SYSTEM,
+ new DexEncodedAnnotation(factory.createType(METHOD_PARAMETERS_DESCRIPTOR),
+ new DexAnnotationElement[]{
+ new DexAnnotationElement(
+ factory.createString("names"),
+ new DexValueArray(names)),
+ new DexAnnotationElement(
+ factory.createString("accessFlags"),
+ new DexValueArray(accessFlags))
+ }));
+ }
+
+ public static DexAnnotation createAnnotationDefaultAnnotation(DexType type,
+ List<DexAnnotationElement> defaults, DexItemFactory factory) {
+ return createSystemValueAnnotation(ANNOTATION_DEFAULT_DESCRIPTOR, factory,
+ new DexValueAnnotation(
+ new DexEncodedAnnotation(type,
+ defaults.toArray(new DexAnnotationElement[defaults.size()])))
+ );
+ }
+
+ public static DexAnnotation createSignatureAnnotation(String signature, DexItemFactory factory) {
+ return createSystemValueAnnotation(SIGNATURE_DESCRIPTOR, factory,
+ compressSignature(signature, factory));
+ }
+
+ public static DexAnnotation createThrowsAnnotation(DexValue[] exceptions,
+ DexItemFactory factory) {
+ return createSystemValueAnnotation(THROWS_DESCRIPTOR, factory, new DexValueArray(exceptions));
+ }
+
+ private static DexAnnotation createSystemValueAnnotation(String desc, DexItemFactory factory,
+ DexValue value) {
+ return new DexAnnotation(VISIBILITY_SYSTEM,
+ new DexEncodedAnnotation(factory.createType(desc), new DexAnnotationElement[] {
+ new DexAnnotationElement(factory.createString("value"), value)
+ }));
+ }
+
+ public static boolean isThrowingAnnotation(DexAnnotation annotation) {
+ return annotation.annotation.type.toDescriptorString().equals(THROWS_DESCRIPTOR);
+ }
+
+ public static boolean isSignatureAnnotation(DexAnnotation annotation) {
+ return annotation.annotation.type.toDescriptorString().equals(SIGNATURE_DESCRIPTOR);
+ }
+
+
+ public static boolean isSourceDebugExtension(DexAnnotation annotation) {
+ return annotation.annotation.type.toDescriptorString().equals(SOURCE_DEBUG_EXTENSION);
+ }
+
+ /**
+ * As a simple heuristic for compressing a signature, we locate all starting points of qualified
+ * names and of inner classes (started by an L or $) and make them individual parts, potentially
+ * splitting the qualified name into its package prefix and class name components. All other
+ * parts of the signature are simply grouped and separate the names.
+ * For examples, "()Ljava/lang/List<Lfoo/bar/Baz;>;" splits into:
+ * <pre>
+ * ["()", "Ljava/lang/List<", "Lfoo/bar/", "Baz;", ">;"]
+ * </pre>
+ * We don't split classes in java and android since they are very frequent and the added string
+ * payload is outweighed by the reduced number of parts.
+ */
+ private static DexValue compressSignature(String signature, DexItemFactory factory) {
+ final int length = signature.length();
+ List<DexValue> parts = new ArrayList<>();
+ int previous = 0;
+ int index = 0;
+ while (index < length) {
+ char c = signature.charAt(index);
+ if (c == 'L' || c == '$') {
+ if (previous < index) {
+ parts.add(toDexValue(signature.substring(previous, index), factory));
+ previous = index;
+ }
+ int startOfClassName = index;
+ ++index;
+ while (index < length && isAlphaNumericPath(index, signature)) {
+ ++index;
+ // Record the last separator.
+ if (signature.charAt(index - 1) == '/') {
+ startOfClassName = index;
+ }
+ }
+ // Include the termination char in the part (this will typically be duplicated).
+ int lastChar = signature.charAt(index);
+ if (lastChar == ';' || lastChar == '<') {
+ ++index;
+ }
+ if (splitQualifiedName(previous, index, startOfClassName, signature)) {
+ parts.add(toDexValue(signature.substring(previous, startOfClassName), factory));
+ parts.add(toDexValue(signature.substring(startOfClassName, index), factory));
+ } else {
+ parts.add(toDexValue(signature.substring(previous, index), factory));
+ }
+ previous = index;
+ } else {
+ ++index;
+ }
+ }
+ if (previous < index) {
+ parts.add(toDexValue(signature.substring(previous, index), factory));
+ }
+ return new DexValueArray(parts.toArray(new DexValue[parts.size()]));
+ }
+
+ private static boolean isAlphaNumericPath(int position, String data) {
+ char c = data.charAt(position);
+ return c == '/' || Character.isLetterOrDigit(c);
+ }
+
+ private static boolean splitQualifiedName(int start, int end, int classStart, String signature) {
+ return start < classStart && classStart < end - 1
+ && !signature.startsWith("Landroid/", start)
+ && !signature.startsWith("Ljava/", start);
+ }
+
+ private static DexValue toDexValue(String string, DexItemFactory factory) {
+ return new DexValueString(factory.createString(string));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
new file mode 100644
index 0000000..c55ea05
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+
+public class DexAnnotationElement extends DexItem {
+
+ public final DexString name;
+ public final DexValue value;
+
+ public DexAnnotationElement(DexString name, DexValue value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode() + value.hashCode() * 3;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof DexAnnotationElement) {
+ DexAnnotationElement o = (DexAnnotationElement) other;
+ return name.equals(o.name) && value.equals(o.value);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return name + "=" + value;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ name.collectIndexedItems(indexedItems);
+ value.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ // Should never be visited.
+ assert false;
+ }
+
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
new file mode 100644
index 0000000..04e94d7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+
+public class DexAnnotationSet extends DexItem {
+
+ private static final int UNSORTED = 0;
+ private static final DexAnnotationSet THE_EMPTY_ANNOTATIONS_SET =
+ new DexAnnotationSet(new DexAnnotation[0]);
+
+ public final DexAnnotation[] annotations;
+ private int sorted = UNSORTED;
+
+ public DexAnnotationSet(DexAnnotation[] annotations) {
+ this.annotations = annotations;
+ }
+
+ public static DexAnnotationSet empty() {
+ return THE_EMPTY_ANNOTATIONS_SET;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(annotations);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof DexAnnotationSet) {
+ DexAnnotationSet o = (DexAnnotationSet) other;
+ return Arrays.equals(annotations, o.annotations);
+ }
+ return false;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ collectAll(indexedItems, annotations);
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ mixedItems.add(this);
+ collectAll(mixedItems, annotations);
+ }
+
+ public boolean isEmpty() {
+ return annotations.length == 0;
+ }
+
+ public void sort() {
+ if (sorted != UNSORTED) {
+ assert sorted == sortedHashCode();
+ return;
+ }
+ Arrays.sort(annotations, (a, b) -> a.annotation.type.compareTo(b.annotation.type));
+ for (DexAnnotation annotation : annotations) {
+ annotation.annotation.sort();
+ }
+ sorted = hashCode();
+ }
+
+ private int sortedHashCode() {
+ int hashCode = hashCode();
+ return hashCode == UNSORTED ? 1 : hashCode;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java
new file mode 100644
index 0000000..336eac5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+
+public class DexAnnotationSetRefList extends DexItem {
+
+ private static final DexAnnotationSetRefList theEmptyTypeList = new DexAnnotationSetRefList();
+
+ public final DexAnnotationSet[] values;
+
+ public static DexAnnotationSetRefList empty() {
+ return theEmptyTypeList;
+ }
+
+ private DexAnnotationSetRefList() {
+ this.values = new DexAnnotationSet[0];
+ }
+
+ public DexAnnotationSetRefList(DexAnnotationSet[] values) {
+ assert values != null && values.length > 0;
+ this.values = values;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(values);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof DexAnnotationSetRefList) {
+ return Arrays.equals(values, ((DexAnnotationSetRefList) other).values);
+ }
+ return false;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ collectAll(indexedItems, values);
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ // Collect values first so that the annotation sets have sorted themselves before adding this.
+ collectAll(mixedItems, values);
+ mixedItems.add(this);
+ }
+
+ public boolean isEmpty() {
+ return values.length == 0;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
new file mode 100644
index 0000000..b155cbb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -0,0 +1,382 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.ir.desugar.LambdaRewriter;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class DexApplication {
+
+ // Maps type into class promise, may be used concurrently.
+ final ImmutableMap<DexType, DexClassPromise> classMap;
+
+ public final ImmutableSet<DexType> mainDexList;
+
+ private final ClassNameMapper proguardMap;
+
+ public final Timing timing;
+
+ public final DexItemFactory dexItemFactory;
+
+ // Information on the lexicographically largest string referenced from code.
+ public final DexString highestSortingString;
+
+ /** Constructor should only be invoked by the DexApplication.Builder. */
+ private DexApplication(
+ ClassNameMapper proguardMap,
+ ImmutableMap<DexType, DexClassPromise> classMap,
+ ImmutableSet<DexType> mainDexList,
+ DexItemFactory dexItemFactory,
+ DexString highestSortingString,
+ Timing timing) {
+ this.proguardMap = proguardMap;
+ this.mainDexList = mainDexList;
+ this.classMap = classMap;
+ this.dexItemFactory = dexItemFactory;
+ this.highestSortingString = highestSortingString;
+ this.timing = timing;
+ }
+
+ public Iterable<DexProgramClass> classes() {
+ List<DexProgramClass> result = new ArrayList<>();
+ for (DexClassPromise promise : classMap.values()) {
+ if (promise.isProgramClass()) {
+ result.add(promise.get().asProgramClass());
+ }
+ }
+ return result;
+ }
+
+ public Iterable<DexLibraryClass> libraryClasses() {
+ List<DexLibraryClass> result = new ArrayList<>();
+ for (DexClassPromise promise : classMap.values()) {
+ if (promise.isLibraryClass()) {
+ result.add(promise.get().asLibraryClass());
+ }
+ }
+ return result;
+ }
+
+ public DexClass definitionFor(DexType type) {
+ DexClassPromise promise = classMap.get(type);
+ return promise == null ? null : promise.get();
+ }
+
+ public DexProgramClass programDefinitionFor(DexType type) {
+ DexClassPromise promise = classMap.get(type);
+ return (promise == null || !promise.isProgramClass()) ? null : promise.get().asProgramClass();
+ }
+
+ public String toString() {
+ return "Application (classes #" + classMap.size() + ")";
+ }
+
+ public ClassNameMapper getProguardMap() {
+ return proguardMap;
+ }
+
+ private void disassemble(DexEncodedMethod method, ClassNameMapper naming, Path outputDir) {
+ if (method.getCode() != null) {
+ PrintStream ps = System.out;
+ try {
+ String clazzName;
+ String methodName;
+ if (naming != null) {
+ clazzName = naming.originalNameOf(method.method.holder);
+ methodName = naming.originalSignatureOf(method.method).toString();
+ } else {
+ clazzName = method.method.holder.toSourceString();
+ methodName = method.method.name.toString();
+ }
+ if (outputDir != null) {
+ Path directory = outputDir.resolve(clazzName.replace('.', '/'));
+ String name = methodName + ".dump";
+ if (name.length() > 200) {
+ name = StringUtils.computeMD5Hash(name);
+ }
+ Files.createDirectories(directory);
+ ps = new PrintStream(Files.newOutputStream(directory.resolve(name)));
+ }
+ ps.println("Bytecode for");
+ ps.println("Class: '" + clazzName + "'");
+ ps.println("Method: '" + methodName + "':");
+ ps.println(method.getCode().toString(naming));
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (outputDir != null) {
+ ps.flush();
+ ps.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * Write disassembly for the application code in the provided directory.
+ *
+ * <p>If no directory is provided everything is written to System.out.
+ */
+ public void disassemble(Path outputDir, InternalOptions options) {
+ for (DexClass clazz : classes()) {
+ for (DexEncodedMethod method : clazz.virtualMethods()) {
+ if (options.methodMatchesFilter(method)) {
+ disassemble(method, getProguardMap(), outputDir);
+ }
+ }
+ for (DexEncodedMethod method : clazz.directMethods()) {
+ if (options.methodMatchesFilter(method)) {
+ disassemble(method, getProguardMap(), outputDir);
+ }
+ }
+ }
+ }
+
+ /** Return smali source for the application code. */
+ public String smali(InternalOptions options) {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(os);
+ smali(options, ps);
+ return new String(os.toByteArray(), StandardCharsets.UTF_8);
+ }
+
+ private void writeClassHeader(DexClass clazz, PrintStream ps) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".class ");
+ builder.append(clazz.accessFlags.toSmaliString());
+ builder.append(" ");
+ builder.append(clazz.type.toSmaliString());
+ builder.append("\n\n");
+ if (clazz.type != dexItemFactory.objectType) {
+ builder.append(".super ");
+ builder.append(clazz.superType.toSmaliString());
+ // TODO(sgjesse): Add implemented interfaces
+ builder.append("\n");
+ }
+ ps.append(builder.toString());
+ }
+
+ /** Write smali source for the application code on the provided PrintStream. */
+ public void smali(InternalOptions options, PrintStream ps) {
+ for (DexClass clazz : classes()) {
+ boolean classHeaderWritten = false;
+ for (DexEncodedMethod method : clazz.virtualMethods()) {
+ if (options.methodMatchesFilter(method)) {
+ if (!classHeaderWritten) {
+ writeClassHeader(clazz, ps);
+ classHeaderWritten = true;
+ }
+ ps.append("\n");
+ ps.append(method.toSmaliString(getProguardMap()));
+ }
+ }
+ for (DexEncodedMethod method : clazz.directMethods()) {
+ if (options.methodMatchesFilter(method)) {
+ if (!classHeaderWritten) {
+ writeClassHeader(clazz, ps);
+ classHeaderWritten = true;
+ }
+ ps.append("\n");
+ ps.append(method.toSmaliString(getProguardMap()));
+ }
+ }
+ }
+ }
+
+ public static class Builder {
+
+ public final Hashtable<DexType, DexClassPromise> classMap = new Hashtable<>();
+ public final Hashtable<DexCode, DexCode> codeItems = new Hashtable<>();
+
+ public final DexItemFactory dexItemFactory;
+ public ClassNameMapper proguardMap;
+ private final Timing timing;
+
+ public DexString highestSortingString;
+ private final Set<DexType> mainDexList = Sets.newIdentityHashSet();
+
+ public Builder(DexItemFactory dexItemFactory, Timing timing) {
+ this.dexItemFactory = dexItemFactory;
+ this.timing = timing;
+ }
+
+ public Builder(DexApplication application) {
+ this(application, application.classMap);
+ }
+
+ public Builder(DexApplication application, Map<DexType, DexClassPromise> classMap) {
+ this.classMap.putAll(classMap);
+ proguardMap = application.proguardMap;
+ timing = application.timing;
+ highestSortingString = application.highestSortingString;
+ dexItemFactory = application.dexItemFactory;
+ mainDexList.addAll(application.mainDexList);
+ }
+
+ public synchronized void setProguardMap(ClassNameMapper proguardMap) {
+ assert this.proguardMap == null;
+ this.proguardMap = proguardMap;
+ }
+
+ public synchronized void setHighestSortingString(DexString value) {
+ highestSortingString = value;
+ }
+
+ // Callback from FileReader when parsing a DexCode (multi-threaded).
+ public synchronized DexCode canonicalizeCode(DexCode code) {
+ DexCode result = codeItems.get(code);
+ if (result != null) {
+ return result;
+ }
+ codeItems.put(code, code);
+ return code;
+ }
+
+ public void addClassPromise(DexClassPromise promise) {
+ addClassPromise(promise, false);
+ }
+
+ public void addClassIgnoringLibraryDuplicates(DexClass clazz) {
+ addClass(clazz, true);
+ }
+
+ public void addSynthesizedClass(DexProgramClass synthesizedClass, boolean addToMainDexList) {
+ addClassPromise(synthesizedClass);
+ if (addToMainDexList && !mainDexList.isEmpty()) {
+ mainDexList.add(synthesizedClass.type);
+ }
+ }
+
+ // Callback from FileReader when parsing a DexProgramClass (multi-threaded).
+ private void addClass(DexClass clazz, boolean skipLibDups) {
+ addClassPromise(clazz, skipLibDups);
+ }
+
+ public synchronized void addClassPromise(DexClassPromise promise, boolean skipLibDups) {
+ assert promise != null;
+ DexType type = promise.getType();
+ DexClassPromise oldPromise = classMap.get(type);
+ if (oldPromise != null) {
+ promise = chooseClass(promise, oldPromise, skipLibDups);
+ }
+ if (oldPromise != promise) {
+ classMap.put(type, promise);
+ }
+ }
+
+ private DexClassPromise chooseClass(DexClassPromise a, DexClassPromise b, boolean skipLibDups) {
+ // NOTE: We assume that there should not be any conflicting names in user defined
+ // classes and/or linked jars. If we ever want to allow 'keep first'-like policy
+ // to resolve this kind of conflict between program and/or classpath classes, we'll
+ // need to make sure we choose the class we keep deterministically.
+ if (a.isProgramClass() && b.isProgramClass()) {
+ if (allowProgramClassConflict(a, b)) {
+ return a;
+ }
+ throw new CompilationError(
+ "Program type already present: " + a.getType().toSourceString());
+ }
+ if (a.isProgramClass()) {
+ return chooseBetweenProgramAndOtherClass(a, b);
+ }
+ if (b.isProgramClass()) {
+ return chooseBetweenProgramAndOtherClass(b, a);
+ }
+
+ if (a.isClasspathClass() && b.isClasspathClass()) {
+ throw new CompilationError(
+ "Classpath type already present: " + a.getType().toSourceString());
+ }
+ if (a.isClasspathClass()) {
+ return chooseBetweenClasspathAndLibraryClass(a, b);
+ }
+ if (b.isClasspathClass()) {
+ return chooseBetweenClasspathAndLibraryClass(b, a);
+ }
+
+ return chooseBetweenLibraryClasses(b, a, skipLibDups);
+ }
+
+ private boolean allowProgramClassConflict(DexClassPromise a, DexClassPromise b) {
+ // Currently only allow collapsing synthetic lambda classes.
+ return a.getOrigin() == DexClass.Origin.Dex
+ && b.getOrigin() == DexClass.Origin.Dex
+ && a.get().accessFlags.isSynthetic()
+ && b.get().accessFlags.isSynthetic()
+ && LambdaRewriter.hasLambdaClassPrefix(a.getType())
+ && LambdaRewriter.hasLambdaClassPrefix(b.getType());
+ }
+
+ private DexClassPromise chooseBetweenProgramAndOtherClass(
+ DexClassPromise selected, DexClassPromise ignored) {
+ assert selected.isProgramClass() && !ignored.isProgramClass();
+ if (ignored.isLibraryClass()) {
+ logIgnoredClass(ignored, "Class `%s` was specified as library and program type.");
+ }
+ // We don't log program/classpath class conflict since it is expected case.
+ return selected;
+ }
+
+ private DexClassPromise chooseBetweenClasspathAndLibraryClass(
+ DexClassPromise selected, DexClassPromise ignored) {
+ assert selected.isClasspathClass() && ignored.isLibraryClass();
+ logIgnoredClass(ignored, "Class `%s` was specified as library and classpath type.");
+ return selected;
+ }
+
+ private DexClassPromise chooseBetweenLibraryClasses(
+ DexClassPromise selected, DexClassPromise ignored, boolean skipDups) {
+ assert selected.isLibraryClass() && ignored.isLibraryClass();
+ if (!skipDups) {
+ throw new CompilationError(
+ "Library type already present: " + selected.getType().toSourceString());
+ }
+ logIgnoredClass(ignored, "Class `%s` was specified twice as a library type.");
+ return selected;
+ }
+
+ private void logIgnoredClass(DexClassPromise ignored, String message) {
+ if (Log.ENABLED) {
+ Log.warn(getClass(), message, ignored.getType().toSourceString());
+ }
+ }
+
+ public void addToMainDexList(Collection<DexType> mainDexList) {
+ this.mainDexList.addAll(mainDexList);
+ }
+
+ public DexApplication build() {
+ return new DexApplication(
+ proguardMap,
+ ImmutableMap.copyOf(classMap),
+ ImmutableSet.copyOf(mainDexList),
+ dexItemFactory,
+ highestSortingString,
+ timing);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
new file mode 100644
index 0000000..58bf764
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -0,0 +1,241 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
+import com.android.tools.r8.graph.DexValue.DexValueMethodType;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.google.common.io.BaseEncoding;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+
+public final class DexCallSite extends IndexedDexItem {
+
+ public final DexString methodName;
+ public final DexProto methodProto;
+
+ public final DexMethodHandle bootstrapMethod;
+ public final List<DexValue> bootstrapArgs;
+
+ private DexEncodedArray encodedArray = null;
+
+ DexCallSite(DexString methodName, DexProto methodProto,
+ DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
+ assert methodName != null;
+ assert methodProto != null;
+ assert bootstrapMethod != null;
+ assert bootstrapArgs != null;
+
+ this.methodName = methodName;
+ this.methodProto = methodProto;
+ this.bootstrapMethod = bootstrapMethod;
+ this.bootstrapArgs = bootstrapArgs;
+ }
+
+ public int computeHashCode() {
+ return methodName.hashCode()
+ + methodProto.hashCode() * 7
+ + bootstrapMethod.hashCode() * 31
+ + bootstrapArgs.hashCode() * 101;
+ }
+
+ public boolean computeEquals(Object other) {
+ if (other instanceof DexCallSite) {
+ DexCallSite o = (DexCallSite) other;
+ return methodName.equals(o.methodName)
+ && methodProto.equals(o.methodProto)
+ && bootstrapMethod.equals(o.bootstrapMethod)
+ && bootstrapArgs.equals(o.bootstrapArgs);
+ }
+ return false;
+ }
+
+ public String toString() {
+ StringBuilder builder =
+ new StringBuilder("CallSite: { Name: ").append(methodName.toSourceString())
+ .append(", Proto: ").append(methodProto.toSourceString())
+ .append(", ").append(bootstrapMethod.toSourceString());
+ String sep = ", Args: ";
+ for (DexItem arg : bootstrapArgs) {
+ builder.append(sep).append(arg.toSourceString());
+ sep = ", ";
+ }
+ builder.append('}');
+ return builder.toString();
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ if (indexedItems.addCallSite(this)) {
+ methodName.collectIndexedItems(indexedItems);
+ methodProto.collectIndexedItems(indexedItems);
+ bootstrapMethod.collectIndexedItems(indexedItems);
+ for (DexValue arg : bootstrapArgs) {
+ arg.collectIndexedItems(indexedItems);
+ }
+ }
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ mixedItems.add(getEncodedArray());
+ }
+
+ @Override
+ public int getOffset(ObjectToOffsetMapping mapping) {
+ return mapping.getOffsetFor(this);
+ }
+
+ // TODO(mikaelpeltier): Adapt syntax when invoke-custom will be available into smali.
+ public String toSmaliString() {
+ return toString();
+ }
+
+ public String getHash() {
+ return new HashBuilder().build();
+ }
+
+ private final class HashBuilder {
+ private ByteArrayOutputStream bytes;
+ private ObjectOutputStream out;
+
+ private void write(DexString string) throws IOException {
+ out.writeInt(string.size); // To avoid same-prefix problem
+ out.write(string.content);
+ }
+
+ private void write(DexType type) throws IOException {
+ write(type.descriptor);
+ }
+
+ private void write(DexMethodHandle methodHandle) throws IOException {
+ out.writeShort(methodHandle.type.getValue());
+ if (methodHandle.isFieldHandle()) {
+ write(methodHandle.asField());
+ } else {
+ write(methodHandle.asMethod());
+ }
+ }
+
+ private void write(DexProto proto) throws IOException {
+ write(proto.shorty);
+ write(proto.returnType);
+ DexType[] params = proto.parameters.values;
+ out.writeInt(params.length);
+ for (DexType param : params) {
+ write(param);
+ }
+ }
+
+ private void write(DexMethod method) throws IOException {
+ write(method.holder);
+ write(method.proto);
+ write(method.name);
+ }
+
+ private void write(DexField field) throws IOException {
+ write(field.clazz);
+ write(field.type);
+ write(field.name);
+ }
+
+ private void write(List<DexValue> args) throws IOException {
+ out.writeInt(args.size());
+ for (DexValue arg : args) {
+ // String, Class, Integer, Long, Float, Double, MethodHandle, MethodType
+ if (arg instanceof DexValue.DexValueString) {
+ out.writeByte(0);
+ write(((DexValue.DexValueString) arg).value);
+ continue;
+ }
+
+ if (arg instanceof DexValue.DexValueType) {
+ out.writeByte(1);
+ write(((DexValue.DexValueType) arg).value);
+ continue;
+ }
+
+ if (arg instanceof DexValue.DexValueInt) {
+ out.writeByte(2);
+ out.writeInt(((DexValue.DexValueInt) arg).value);
+ continue;
+ }
+
+ if (arg instanceof DexValue.DexValueLong) {
+ out.writeByte(3);
+ out.writeLong(((DexValue.DexValueLong) arg).value);
+ continue;
+ }
+
+ if (arg instanceof DexValue.DexValueFloat) {
+ out.writeByte(4);
+ out.writeFloat(((DexValue.DexValueFloat) arg).value);
+ continue;
+ }
+
+ if (arg instanceof DexValue.DexValueDouble) {
+ out.writeByte(5);
+ out.writeDouble(((DexValue.DexValueDouble) arg).value);
+ continue;
+ }
+
+ if (arg instanceof DexValue.DexValueMethodHandle) {
+ out.writeByte(6);
+ write(((DexValue.DexValueMethodHandle) arg).value);
+ continue;
+ }
+
+ assert arg instanceof DexValue.DexValueMethodType;
+ out.writeByte(7);
+ write(((DexValue.DexValueMethodType) arg).value);
+ }
+ }
+
+ String build() {
+ try {
+ bytes = new ByteArrayOutputStream();
+ out = new ObjectOutputStream(bytes);
+
+ // We will generate SHA-1 hash of the call site information based on call site
+ // attributes used in equality comparison, such that if the two call sites are
+ // different their hashes should also be different.
+ write(methodName);
+ write(methodProto);
+ write(bootstrapMethod);
+ write(bootstrapArgs);
+ out.close();
+
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ digest.update(bytes.toByteArray());
+ return BaseEncoding.base64Url().omitPadding().encode(digest.digest());
+ } catch (NoSuchAlgorithmException | IOException ex) {
+ throw new Unreachable("Cannot get SHA-1 message digest");
+ }
+ }
+ }
+
+ public DexEncodedArray getEncodedArray() {
+ if (encodedArray == null) {
+ // 3 is the fixed size of the call site
+ DexValue[] callSitesValues = new DexValue[3 + bootstrapArgs.size()];
+ int valuesIndex = 0;
+ callSitesValues[valuesIndex++] = new DexValueMethodHandle(bootstrapMethod);
+ callSitesValues[valuesIndex++] = new DexValueString(methodName);
+ callSitesValues[valuesIndex++] = new DexValueMethodType(methodProto);
+ for (DexValue extraArgValue : bootstrapArgs) {
+ callSitesValues[valuesIndex++] = extraArgValue;
+ }
+ encodedArray = new DexEncodedArray(callSitesValues);
+ }
+
+ return encodedArray;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
new file mode 100644
index 0000000..bff12e7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -0,0 +1,194 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.InternalResource;
+import com.google.common.base.MoreObjects;
+
+public abstract class DexClass extends DexItem implements DexClassPromise {
+ public interface Factory {
+ DexClass create(DexType type, Origin origin, DexAccessFlags accessFlags, DexType superType,
+ DexTypeList interfaces, DexString sourceFile, DexAnnotationSet annotations,
+ DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
+ DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods);
+ }
+
+ public enum Origin {
+ Dex, ClassFile, Synthetic
+ }
+
+ private static final DexEncodedMethod[] NO_METHODS = {};
+ private static final DexEncodedField[] NO_FIELDS = {};
+
+ public final Origin origin;
+ public final DexType type;
+ public final DexAccessFlags accessFlags;
+ public final DexType superType;
+ public final DexTypeList interfaces;
+ public final DexString sourceFile;
+ public DexEncodedField[] staticFields;
+ public DexEncodedField[] instanceFields;
+ public DexEncodedMethod[] directMethods;
+ public DexEncodedMethod[] virtualMethods;
+ public DexAnnotationSet annotations;
+
+ public DexClass(
+ DexString sourceFile, DexTypeList interfaces, DexAccessFlags accessFlags, DexType superType,
+ DexType type, DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
+ DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods,
+ DexAnnotationSet annotations, Origin origin) {
+ this.origin = origin;
+ this.sourceFile = sourceFile;
+ this.interfaces = interfaces;
+ this.accessFlags = accessFlags;
+ this.superType = superType;
+ this.type = type;
+ this.staticFields = staticFields;
+ this.instanceFields = instanceFields;
+ this.directMethods = directMethods;
+ this.virtualMethods = virtualMethods;
+ this.annotations = annotations;
+ if (type == superType) {
+ throw new CompilationError("Class " + type.toString() + " cannot extend itself");
+ }
+ for (DexType interfaceType : interfaces.values) {
+ if (type == interfaceType) {
+ throw new CompilationError("Interface " + type.toString() + " cannot implement itself");
+ }
+ }
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ throw new Unreachable();
+ }
+
+ public DexEncodedMethod[] directMethods() {
+ return MoreObjects.firstNonNull(directMethods, NO_METHODS);
+ }
+
+ public DexEncodedMethod[] virtualMethods() {
+ return MoreObjects.firstNonNull(virtualMethods, NO_METHODS);
+ }
+
+
+ public DexEncodedField[] staticFields() {
+ return MoreObjects.firstNonNull(staticFields, NO_FIELDS);
+ }
+
+ public DexEncodedField[] instanceFields() {
+ return MoreObjects.firstNonNull(instanceFields, NO_FIELDS);
+ }
+
+ /**
+ * Find direct method in this class matching method
+ */
+ public DexEncodedMethod findDirectTarget(DexMethod method) {
+ return findTarget(directMethods(), method);
+ }
+
+ /**
+ * Find static field in this class matching field
+ */
+ public DexEncodedField findStaticTarget(DexField field) {
+ return findTarget(staticFields(), field);
+ }
+
+ /**
+ * Find virtual method in this class matching method
+ */
+ public DexEncodedMethod findVirtualTarget(DexMethod method) {
+ return findTarget(virtualMethods(), method);
+ }
+
+ /**
+ * Find instance field in this class matching field
+ */
+ public DexEncodedField findInstanceTarget(DexField field) {
+ return findTarget(instanceFields(), field);
+ }
+
+ private <T extends DexItem, S extends Descriptor<T, S>> T findTarget(T[] items, S descriptor) {
+ for (T entry : items) {
+ if (descriptor.match(entry)) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ // Tells whether this is an interface.
+ public boolean isInterface() {
+ return accessFlags.isInterface();
+ }
+
+ public abstract void addDependencies(MixedSectionCollection collector);
+
+ @Override
+ public boolean isProgramClass() {
+ return false;
+ }
+
+ public DexProgramClass asProgramClass() {
+ return null;
+ }
+
+ @Override
+ public boolean isClasspathClass() {
+ return false;
+ }
+
+ public DexClasspathClass asClasspathClass() {
+ return null;
+ }
+
+ @Override
+ public boolean isLibraryClass() {
+ return false;
+ }
+
+ public DexLibraryClass asLibraryClass() {
+ return null;
+ }
+
+ public DexEncodedMethod getClassInitializer(DexItemFactory factory) {
+ for (DexEncodedMethod method : directMethods()) {
+ if (factory.isClassConstructor(method.method)) {
+ return method;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return this.origin;
+ }
+
+ @Override
+ public DexClass get() {
+ return this;
+ }
+
+ @Override
+ public DexType getType() {
+ return type;
+ }
+
+ /** Get a class factory for a particular resource kind */
+ public static Factory factoryForResourceKind(InternalResource.Kind kind) {
+ switch (kind) {
+ case PROGRAM:
+ return DexProgramClass::new;
+ case CLASSPATH:
+ return DexClasspathClass::new;
+ case LIBRARY:
+ return DexLibraryClass::new;
+ }
+ throw new Unreachable();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassPromise.java b/src/main/java/com/android/tools/r8/graph/DexClassPromise.java
new file mode 100644
index 0000000..243eadf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexClassPromise.java
@@ -0,0 +1,27 @@
+// 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.graph;
+
+/**
+ * Provides a way for delayed DexClass discovery.
+ *
+ * Provides minimal class details of the promised class and
+ * provides the class when asked by calling method get().
+ *
+ * Note that DexClass also implements this interface, since it
+ * represents a 'materialized' promise for a class.
+ */
+public interface DexClassPromise {
+ DexType getType();
+
+ DexClass.Origin getOrigin();
+
+ boolean isProgramClass();
+
+ boolean isClasspathClass();
+
+ boolean isLibraryClass();
+
+ DexClass get();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
new file mode 100644
index 0000000..0ec8cf7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -0,0 +1,45 @@
+// 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.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+
+public class DexClasspathClass extends DexClass {
+
+ public DexClasspathClass(DexType type, Origin origin, DexAccessFlags accessFlags,
+ DexType superType, DexTypeList interfaces, DexString sourceFile, DexAnnotationSet annotations,
+ DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
+ DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods) {
+ super(sourceFile, interfaces, accessFlags, superType, type,
+ staticFields, instanceFields, directMethods, virtualMethods, annotations, origin);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public String toString() {
+ return type.toString() + "(classpath class)";
+ }
+
+ @Override
+ public void addDependencies(MixedSectionCollection collector) {
+ // Should never happen but does not harm.
+ assert false;
+ }
+
+ @Override
+ public boolean isClasspathClass() {
+ return true;
+ }
+
+ @Override
+ public DexClasspathClass asClasspathClass() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
new file mode 100644
index 0000000..ebfd388
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -0,0 +1,448 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.SwitchPayload;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.conversion.DexSourceCode;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+// DexCode corresponds to code item in dalvik/dex-format.html
+public class DexCode extends Code {
+
+ public final int registerSize;
+ public final int incomingRegisterSize;
+ public final int outgoingRegisterSize;
+ public final Try[] tries;
+ public final TryHandler[] handlers;
+ public final Instruction[] instructions;
+
+ public final DexString highestSortingString;
+ private DexDebugInfo debugInfo;
+
+ public DexCode(
+ int registerSize,
+ int insSize,
+ int outsSize,
+ Instruction[] instructions,
+ Try[] tries,
+ TryHandler[] handlers,
+ DexDebugInfo debugInfo,
+ DexString highestSortingString) {
+ this.incomingRegisterSize = insSize;
+ this.registerSize = registerSize;
+ this.outgoingRegisterSize = outsSize;
+ this.instructions = instructions;
+ this.tries = tries;
+ this.handlers = handlers;
+ this.debugInfo = debugInfo;
+ this.highestSortingString = highestSortingString;
+ hashCode(); // Cache the hash code eagerly.
+ }
+
+ @Override
+ public boolean isDexCode() {
+ return true;
+ }
+
+ @Override
+ public DexCode asDexCode() {
+ return this;
+ }
+
+ public DexDebugInfo getDebugInfo() {
+ return debugInfo;
+ }
+
+ public void setDebugInfo(DexDebugInfo debugInfo) {
+ this.debugInfo = debugInfo;
+ }
+
+ public DexDebugInfo debugInfoWithAdditionalFirstParameter(DexString name) {
+ if (debugInfo == null) {
+ return null;
+ }
+ DexString[] parameters = debugInfo.parameters;
+ DexString[] newParameters = new DexString[parameters.length + 1];
+ newParameters[0] = name;
+ System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
+ return new DexDebugInfo(debugInfo.startLine, newParameters, debugInfo.events);
+ }
+
+ public int codeSizeInBytes() {
+ Instruction last = instructions[instructions.length - 1];
+ return last.getOffset() + last.getSize();
+ }
+
+ @Override
+ public int computeHashCode() {
+ return incomingRegisterSize * 2
+ + registerSize * 3
+ + outgoingRegisterSize * 5
+ + Arrays.hashCode(instructions) * 7
+ + ((debugInfo == null) ? 0 : debugInfo.hashCode()) * 11
+ + Arrays.hashCode(tries) * 13
+ + Arrays.hashCode(handlers) * 17;
+ }
+
+ @Override
+ public boolean computeEquals(Object other) {
+ if (other instanceof DexCode) {
+ DexCode o = (DexCode) other;
+ if (incomingRegisterSize != o.incomingRegisterSize) {
+ return false;
+ }
+ if (registerSize != o.registerSize) {
+ return false;
+ }
+ if (outgoingRegisterSize != o.outgoingRegisterSize) {
+ return false;
+ }
+ if (debugInfo == null) {
+ if (o.debugInfo != null) {
+ return false;
+ }
+ } else {
+ if (!debugInfo.equals(o.debugInfo)) {
+ return false;
+ }
+ }
+ if (!Arrays.equals(tries, o.tries)) {
+ return false;
+ }
+ if (!Arrays.equals(handlers, o.handlers)) {
+ return false;
+ }
+ // Save the most expensive operation to last.
+ return Arrays.equals(instructions, o.instructions);
+ }
+ return false;
+ }
+
+ @Override
+ public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
+ DexSourceCode source = new DexSourceCode(this, encodedMethod);
+ IRBuilder builder = new IRBuilder(encodedMethod, source, options);
+ return builder.build();
+ }
+
+ public IRCode buildIR(
+ DexEncodedMethod encodedMethod,
+ ValueNumberGenerator valueNumberGenerator,
+ InternalOptions options) {
+ DexSourceCode source = new DexSourceCode(this, encodedMethod);
+ IRBuilder builder = new IRBuilder(encodedMethod, source, valueNumberGenerator, options);
+ return builder.build();
+ }
+
+ @Override
+ public void registerReachableDefinitions(UseRegistry registry) {
+ for (Instruction insn : instructions) {
+ insn.registerUse(registry);
+ }
+ }
+
+ public String toString() {
+ return toString(null);
+ }
+
+ public String toString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("registers: ").append(registerSize);
+ builder.append(", inputs: ").append(incomingRegisterSize);
+ builder.append(", outputs: ").append(outgoingRegisterSize).append("\n");
+ builder.append("------------------------------------------------------------\n");
+ builder.append("inst# offset instruction arguments\n");
+ builder.append("------------------------------------------------------------\n");
+ int instructionNumber = 0;
+ for (Instruction insn : instructions) {
+ StringUtils.appendLeftPadded(builder, Integer.toString(instructionNumber++), 5);
+ builder.append(": ")
+ .append(insn.toString(naming))
+ .append('\n');
+ }
+ if (tries.length > 0) {
+ builder.append("Tries (numbers are offsets)\n");
+ for (Try atry : tries) {
+ builder.append(" ");
+ builder.append(atry.toString());
+ builder.append('\n');
+ }
+ if (handlers != null) {
+ builder.append("Handlers (numbers are offsets)\n");
+ for (int handlerIndex = 0; handlerIndex < handlers.length; handlerIndex++) {
+ TryHandler handler = handlers[handlerIndex];
+ builder.append(" ").append(handlerIndex).append(": ");
+ builder.append(handler.toString());
+ builder.append('\n');
+ }
+ }
+ }
+ return builder.toString();
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder();
+ // Find labeled targets.
+ Map<Integer, Instruction> payloadUsers = new HashMap<>();
+ Set<Integer> labledTargets = new HashSet<>();
+ // Collect payload users and labeled targets for non-payload instructions.
+ for (Instruction dex : instructions) {
+ int[] targets = dex.getTargets();
+ if (targets != Instruction.NO_TARGETS && targets != Instruction.EXIT_TARGET) {
+ assert targets.length <= 2;
+ // For if instructions the second target is the fallthrough, for which no label is needed.
+ labledTargets.add(dex.getOffset() + targets[0]);
+ } else if (dex.hasPayload()) {
+ labledTargets.add(dex.getOffset() + dex.getPayloadOffset());
+ payloadUsers.put(dex.getOffset() + dex.getPayloadOffset(), dex);
+ }
+ }
+ // Collect labeled targets for payload instructions.
+ for (Instruction dex : instructions) {
+ if (dex.isSwitchPayload()) {
+ Instruction payloadUser = payloadUsers.get(dex.getOffset());
+ if (dex instanceof SwitchPayload) {
+ SwitchPayload payload = (SwitchPayload) dex;
+ for (int target : payload.switchTargetOffsets()) {
+ labledTargets.add(payloadUser.getOffset() + target);
+ }
+ }
+ }
+ }
+ // Generate smali for all instructions.
+ for (Instruction dex : instructions) {
+ if (labledTargets.contains(dex.getOffset())) {
+ builder.append(" :label_");
+ builder.append(dex.getOffset());
+ builder.append("\n");
+ }
+ if (dex.isSwitchPayload()) {
+ Instruction payloadUser = payloadUsers.get(dex.getOffset());
+ builder.append(dex.toSmaliString(payloadUser)).append('\n');
+ } else {
+ builder.append(dex.toSmaliString(naming)).append('\n');
+ }
+ }
+ if (tries.length > 0) {
+ builder.append("Tries (numbers are offsets)\n");
+ for (Try atry : tries) {
+ builder.append(" ");
+ builder.append(atry.toString());
+ builder.append('\n');
+ }
+ if (handlers != null) {
+ builder.append("Handlers (numbers are offsets)\n");
+ for (TryHandler handler : handlers) {
+ builder.append(handler.toString());
+ builder.append('\n');
+ }
+ }
+ }
+ return builder.toString();
+ }
+
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ for (Instruction insn : instructions) {
+ insn.collectIndexedItems(indexedItems);
+ }
+ if (debugInfo != null) {
+ debugInfo.collectIndexedItems(indexedItems);
+ }
+ if (handlers != null) {
+ for (TryHandler handler : handlers) {
+ handler.collectIndexedItems(indexedItems);
+ }
+ }
+ }
+
+ public boolean usesExceptionHandling() {
+ return tries.length != 0;
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ if (mixedItems.add(this)) {
+ if (debugInfo != null) {
+ debugInfo.collectMixedSectionItems(mixedItems);
+ }
+ }
+ }
+
+ public static class Try extends DexItem {
+
+ public static final int NO_INDEX = -1;
+
+ private final int handlerOffset;
+ public /* offset */ int startAddress;
+ public /* offset */ int instructionCount;
+ public int handlerIndex;
+
+ public Try(int startAddress, int instructionCount, int handlerOffset) {
+ this.startAddress = startAddress;
+ this.instructionCount = instructionCount;
+ this.handlerOffset = handlerOffset;
+ this.handlerIndex = NO_INDEX;
+ }
+
+ public void setHandlerIndex(Hashtable<Integer, Integer> map) {
+ handlerIndex = map.get(handlerOffset);
+ }
+
+ public int hashCode() {
+ return startAddress * 2 + instructionCount * 3 + handlerIndex * 5;
+ }
+
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof Try) {
+ Try o = (Try) other;
+ if (startAddress != o.startAddress) {
+ return false;
+ }
+ if (instructionCount != o.instructionCount) {
+ return false;
+ }
+ return handlerIndex == o.handlerIndex;
+ }
+ return false;
+ }
+
+ public String toString() {
+ return "["
+ + startAddress
+ + " .. "
+ + (startAddress + instructionCount - 1)
+ + "] -> "
+ + handlerIndex;
+ }
+
+ @Override
+ void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // Intentionally left empty.
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ // Should never be visited.
+ assert false;
+ }
+
+ }
+
+ public static class TryHandler extends DexItem {
+
+ public static final int NO_HANDLER = -1;
+
+ public final TypeAddrPair[] pairs;
+ public /* offset */ int catchAllAddr;
+
+ public TryHandler(TypeAddrPair[] pairs, int catchAllAddr) {
+ this.pairs = pairs;
+ this.catchAllAddr = catchAllAddr;
+ }
+
+ public int hashCode() {
+ return catchAllAddr + Arrays.hashCode(pairs) * 7;
+ }
+
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof TryHandler) {
+ TryHandler o = (TryHandler) other;
+ if (catchAllAddr != o.catchAllAddr) {
+ return false;
+ }
+ return Arrays.equals(pairs, o.pairs);
+ }
+ return false;
+ }
+
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ collectAll(indexedItems, pairs);
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ // Should never be visited.
+ assert false;
+ }
+
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[\n");
+ for (TypeAddrPair pair : pairs) {
+ builder.append(" ");
+ builder.append(pair.type);
+ builder.append(" -> ");
+ builder.append(pair.addr);
+ builder.append("\n");
+ }
+ if (catchAllAddr != NO_HANDLER) {
+ builder.append(" default -> ");
+ builder.append(catchAllAddr);
+ builder.append("\n");
+ }
+ builder.append(" ]");
+ return builder.toString();
+ }
+
+ public static class TypeAddrPair extends DexItem {
+
+ public final DexType type;
+ public final /* offset */ int addr;
+ public final /* offset to the start of an encoded_catch_handler. */ int offset;
+
+ public TypeAddrPair(DexType type, int addr, int offset) {
+ this.type = type;
+ this.addr = addr;
+ this.offset = offset;
+ }
+
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ type.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ // Should never be visited.
+ assert false;
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode() * 7 + addr;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof TypeAddrPair) {
+ TypeAddrPair o = (TypeAddrPair) other;
+ return type.equals(o.type) && addr == o.addr;
+ }
+ return false;
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
new file mode 100644
index 0000000..de7b544
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableMap;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+public class DexDebugEntry {
+
+ public final int address;
+ public final int line;
+ public final DexString sourceFile;
+ public final boolean prologueEnd;
+ public final boolean epilogueBegin;
+ public final ImmutableMap<Integer, DebugLocalInfo> locals;
+
+ public DexDebugEntry(int address,
+ int line,
+ DexString sourceFile,
+ boolean prologueEnd,
+ boolean epilogueBegin,
+ ImmutableMap<Integer, DebugLocalInfo> locals) {
+ this.address = address;
+ this.line = line;
+ this.sourceFile = sourceFile;
+ this.prologueEnd = prologueEnd;
+ this.epilogueBegin = epilogueBegin;
+ this.locals = locals;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("pc 0x").append(StringUtils.hexString(address, 2));
+ builder.append(", line ").append(line);
+ if (sourceFile != null) {
+ builder.append(", file ").append(sourceFile);
+ }
+ if (prologueEnd) {
+ builder.append(", prologue_end = true");
+ }
+ if (epilogueBegin) {
+ builder.append(", epilogue_begin = true");
+ }
+ if (!locals.isEmpty()) {
+ builder.append(", locals: [");
+ SortedSet<Integer> keys = new TreeSet<>(locals.keySet());
+ boolean first = true;
+ for (Integer register : keys) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(", ");
+ }
+ builder.append(register).append(" -> ").append(locals.get(register));
+ }
+ builder.append("]");
+ }
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
new file mode 100644
index 0000000..f6d8fb4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -0,0 +1,170 @@
+// 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.graph;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Builder to construct a "per position" representation of the debug information.
+ *
+ * This builder is relatively relaxed about the stream of build operations and should accept
+ * any stream from any input file we expect to process correctly.
+ */
+public class DexDebugEntryBuilder {
+
+ private static class LocalEntry {
+ DebugLocalInfo current;
+ DebugLocalInfo last;
+
+ void set(DebugLocalInfo value) {
+ current = value;
+ last = value;
+ }
+
+ void unset() {
+ current = null;
+ }
+
+ void reset() {
+ current = last;
+ }
+ }
+
+ // The variables of the state machine.
+ private int currentPc = 0;
+ private int currentLine;
+ private DexString currentFile = null;
+ private boolean prologueEnd = false;
+ private boolean epilogueBegin = false;
+ private final Map<Integer, LocalEntry> locals = new HashMap<>();
+
+ // Delayed construction of an entry. Is finalized once locals information has been collected.
+ private DexDebugEntry pending = null;
+
+ // Canonicalization of locals (the IR/Dex builders assume identity of locals).
+ private final Map<DebugLocalInfo, DebugLocalInfo> canonicalizedLocals = new HashMap<>();
+
+ // Resulting debug entries.
+ private List<DexDebugEntry> entries = new ArrayList<>();
+
+ public DexDebugEntryBuilder(int startLine) {
+ currentLine = startLine;
+ }
+
+ public DexDebugEntryBuilder(DexEncodedMethod method, DexItemFactory factory) {
+ DexCode code = method.getCode().asDexCode();
+ DexDebugInfo info = code.getDebugInfo();
+ int argumentRegister = code.registerSize - code.incomingRegisterSize;
+ int argumentCount = code.incomingRegisterSize;
+ if (!method.accessFlags.isStatic()) {
+ --argumentCount;
+ DexString name = factory.thisName;
+ DexType type = method.method.getHolder();
+ startLocal(argumentRegister++, name, type, null);
+ }
+ DexType[] types = method.method.proto.parameters.values;
+ DexString[] names = info.parameters;
+ for (int i = 0; i < argumentCount; i++) {
+ // If null, the parameter has a parameterized type and the local is introduced in the stream.
+ if (names[i] != null) {
+ startLocal(argumentRegister++, names[i], types[i], null);
+ }
+ }
+ currentLine = info.startLine;
+ for (DexDebugEvent event : info.events) {
+ event.addToBuilder(this);
+ }
+ }
+
+ public void setFile(DexString file) {
+ currentFile = file;
+ }
+
+ public void advancePC(int pcDelta) {
+ assert pcDelta >= 0;
+ currentPc += pcDelta;
+ }
+
+ public void advanceLine(int line) {
+ currentLine += line;
+ }
+
+ public void endPrologue() {
+ prologueEnd = true;
+ }
+
+ public void beginEpilogue() {
+ epilogueBegin = true;
+ }
+
+ public void startLocal(int register, DexString name, DexType type, DexString signature) {
+ getEntry(register).set(canonicalize(name, type, signature));
+ }
+
+ public void endLocal(int register) {
+ getEntry(register).unset();
+ }
+
+ public void restartLocal(int register) {
+ getEntry(register).reset();
+ }
+
+ public void setPosition(int pcDelta, int lineDelta) {
+ assert pcDelta >= 0;
+ if (pending != null) {
+ // Local changes contribute to the pending position entry.
+ entries.add(new DexDebugEntry(
+ pending.address, pending.line, pending.sourceFile,
+ pending.prologueEnd, pending.epilogueBegin,
+ getLocals()));
+ }
+ currentPc += pcDelta;
+ currentLine += lineDelta;
+ pending = new DexDebugEntry(
+ currentPc, currentLine, currentFile, prologueEnd, epilogueBegin, null);
+ prologueEnd = false;
+ epilogueBegin = false;
+ }
+
+ public List<DexDebugEntry> build() {
+ // Flush any pending entry.
+ if (pending != null) {
+ setPosition(0, 0);
+ pending = null;
+ }
+ List<DexDebugEntry> result = entries;
+ entries = null;
+ return result;
+ }
+
+ private DebugLocalInfo canonicalize(DexString name, DexType type, DexString signature) {
+ DebugLocalInfo local = new DebugLocalInfo(name, type, signature);
+ DebugLocalInfo canonical = canonicalizedLocals.putIfAbsent(local, local);
+ return canonical != null ? canonical : local;
+ }
+
+ private LocalEntry getEntry(int register) {
+ LocalEntry entry = locals.get(register);
+ if (entry == null) {
+ entry = new LocalEntry();
+ locals.put(register, entry);
+ }
+ return entry;
+ }
+
+ private ImmutableMap<Integer, DebugLocalInfo> getLocals() {
+ ImmutableMap.Builder<Integer, DebugLocalInfo> builder = ImmutableMap.builder();
+ for (Integer register : locals.keySet()) {
+ LocalEntry entry = locals.get(register);
+ if (entry.current != null) {
+ builder.put(register, entry.current);
+ }
+ }
+ return builder.build();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
new file mode 100644
index 0000000..b8bab7e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -0,0 +1,367 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.DebugBytecodeWriter;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+
+abstract public class DexDebugEvent extends DexItem {
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection collection) {
+ // Empty by default.
+ }
+
+ @Override
+ public void collectMixedSectionItems(MixedSectionCollection collection) {
+ // Empty by default.
+ }
+
+ // Make sure all concrete subclasses implements toString, hashCode, and equals.
+ abstract public String toString();
+
+ abstract public int hashCode();
+
+ abstract public boolean equals(Object other);
+
+ public abstract void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping);
+
+ public abstract void addToBuilder(DexDebugEntryBuilder builder);
+
+
+ public static class AdvancePC extends DexDebugEvent {
+
+ final int delta;
+
+ public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ writer.putByte(Constants.DBG_ADVANCE_PC);
+ writer.putUleb128(delta);
+ }
+
+ public AdvancePC(int delta) {
+ this.delta = delta;
+ }
+
+ public void addToBuilder(DexDebugEntryBuilder builder) {
+ assert delta >= 0;
+ builder.advancePC(delta);
+ }
+
+
+ public String toString() {
+ return "ADVANCE_PC " + delta;
+ }
+
+ public int hashCode() {
+ return Constants.DBG_ADVANCE_PC
+ + delta * 7;
+ }
+
+ public boolean equals(Object other) {
+ return (other instanceof AdvancePC)
+ && (delta == ((AdvancePC) other).delta);
+ }
+ }
+
+ public static class SetPrologueEnd extends DexDebugEvent {
+
+ public SetPrologueEnd() {
+ }
+
+ public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ writer.putByte(Constants.DBG_SET_PROLOGUE_END);
+ }
+
+ public void addToBuilder(DexDebugEntryBuilder builder) {
+ builder.endPrologue();
+ }
+
+ public String toString() {
+ return "SET_PROLOGUE_END";
+ }
+
+
+ public int hashCode() {
+ return Constants.DBG_SET_PROLOGUE_END;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof SetPrologueEnd;
+ }
+ }
+
+
+ public static class SetEpilogueBegin extends DexDebugEvent {
+
+ public SetEpilogueBegin() {
+ }
+
+ public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ writer.putByte(Constants.DBG_SET_EPILOGUE_BEGIN);
+ }
+
+ public void addToBuilder(DexDebugEntryBuilder builder) {
+ builder.beginEpilogue();
+ }
+
+ public String toString() {
+ return "SET_EPILOGUE_BEGIN";
+ }
+
+ public int hashCode() {
+ return Constants.DBG_SET_EPILOGUE_BEGIN;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof SetEpilogueBegin;
+ }
+ }
+
+ public static class AdvanceLine extends DexDebugEvent {
+
+ final int delta;
+
+ public AdvanceLine(int delta) {
+ this.delta = delta;
+ }
+
+ public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ writer.putByte(Constants.DBG_ADVANCE_LINE);
+ writer.putSleb128(delta);
+ }
+
+ public void addToBuilder(DexDebugEntryBuilder builder) {
+ builder.advanceLine(delta);
+ }
+
+ public String toString() {
+ return "ADVANCE_LINE " + delta;
+ }
+
+ public int hashCode() {
+ return Constants.DBG_ADVANCE_LINE
+ + delta * 7;
+ }
+
+ public boolean equals(Object other) {
+ return (other instanceof AdvanceLine)
+ && (delta == ((AdvanceLine) other).delta);
+ }
+ }
+
+ static public class StartLocal extends DexDebugEvent {
+
+ final int registerNum;
+ final DexString name;
+ final DexType type;
+ final DexString signature;
+
+ public StartLocal(
+ int registerNum,
+ DexString name,
+ DexType type,
+ DexString signature) {
+ this.registerNum = registerNum;
+ this.name = name;
+ this.type = type;
+ this.signature = signature;
+ }
+
+ public StartLocal(int registerNum, DebugLocalInfo local) {
+ this(registerNum, local.name, local.type, local.signature);
+ }
+
+ public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ writer.putByte(signature == null
+ ? Constants.DBG_START_LOCAL
+ : Constants.DBG_START_LOCAL_EXTENDED);
+ writer.putUleb128(registerNum);
+ writer.putString(name);
+ writer.putType(type);
+ if (signature != null) {
+ writer.putString(signature);
+ }
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection collection) {
+ name.collectIndexedItems(collection);
+ type.collectIndexedItems(collection);
+ if (signature != null) {
+ signature.collectIndexedItems(collection);
+ }
+ }
+
+ public void addToBuilder(DexDebugEntryBuilder builder) {
+ builder.startLocal(registerNum, name, type, signature);
+ }
+
+ public String toString() {
+ return "START_LOCAL " + registerNum;
+ }
+
+ public int hashCode() {
+ return Constants.DBG_START_LOCAL
+ + registerNum * 7
+ + name.hashCode() * 13
+ + type.hashCode() * 17
+ + (signature == null ? 0 : signature.hashCode()) * 19;
+ }
+
+ public boolean equals(Object other) {
+ if (!(other instanceof StartLocal)) {
+ return false;
+ }
+ StartLocal o = (StartLocal) other;
+ if (registerNum != o.registerNum) {
+ return false;
+ }
+ if (!name.equals(o.name)) {
+ return false;
+ }
+ if (!type.equals(o.type)) {
+ return false;
+ }
+ return (signature == o.signature || signature.equals(o.signature));
+ }
+ }
+
+ public static class EndLocal extends DexDebugEvent {
+
+ final int registerNum;
+
+ public EndLocal(int registerNum) {
+ this.registerNum = registerNum;
+ }
+
+ public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ writer.putByte(Constants.DBG_END_LOCAL);
+ writer.putUleb128(registerNum);
+ }
+
+ public void addToBuilder(DexDebugEntryBuilder builder) {
+ builder.endLocal(registerNum);
+ }
+
+ public String toString() {
+ return "END_LOCAL " + registerNum;
+ }
+
+ public int hashCode() {
+ return Constants.DBG_END_LOCAL
+ + registerNum * 7;
+ }
+
+ public boolean equals(Object other) {
+ return (other instanceof EndLocal)
+ && (registerNum == ((EndLocal) other).registerNum);
+ }
+ }
+
+ public static class RestartLocal extends DexDebugEvent {
+
+ final int registerNum;
+
+ public RestartLocal(int registerNum) {
+ this.registerNum = registerNum;
+ }
+
+ public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ writer.putByte(Constants.DBG_RESTART_LOCAL);
+ writer.putUleb128(registerNum);
+ }
+
+ public void addToBuilder(DexDebugEntryBuilder builder) {
+ builder.restartLocal(registerNum);
+ }
+
+ public String toString() {
+ return "RESTART_LOCAL " + registerNum;
+ }
+
+ public int hashCode() {
+ return Constants.DBG_RESTART_LOCAL
+ + registerNum * 7;
+ }
+
+ public boolean equals(Object other) {
+ return (other instanceof RestartLocal)
+ && (registerNum == ((RestartLocal) other).registerNum);
+ }
+ }
+
+ public static class SetFile extends DexDebugEvent {
+
+ final DexString fileName;
+
+ public SetFile(DexString fileName) {
+ this.fileName = fileName;
+ }
+
+ public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ writer.putByte(Constants.DBG_SET_FILE);
+ writer.putString(fileName);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection collection) {
+ fileName.collectIndexedItems(collection);
+ }
+
+ public void addToBuilder(DexDebugEntryBuilder builder) {
+ builder.setFile(fileName);
+ }
+
+ public String toString() {
+ return "SET_FILE " + fileName.toString();
+ }
+
+ public int hashCode() {
+ return Constants.DBG_SET_FILE
+ + fileName.hashCode() * 7;
+ }
+
+ public boolean equals(Object other) {
+ return (other instanceof SetFile)
+ && fileName.equals(((SetFile) other).fileName);
+ }
+ }
+
+ public static class Default extends DexDebugEvent {
+
+ final int value;
+
+ public Default(int value) {
+ assert (value >= Constants.DBG_FIRST_SPECIAL) && (value <= Constants.DBG_LAST_SPECIAL);
+ this.value = value;
+ }
+
+ public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ writer.putByte(value);
+ }
+
+ public void addToBuilder(DexDebugEntryBuilder builder) {
+ int adjustedOpcode = value - Constants.DBG_FIRST_SPECIAL;
+ int line = Constants.DBG_LINE_BASE + (adjustedOpcode % Constants.DBG_LINE_RANGE);
+ int address = adjustedOpcode / Constants.DBG_LINE_RANGE;
+ builder.setPosition(address, line);
+ }
+
+ public String toString() {
+ return "DEFAULT " + value;
+ }
+
+ public int hashCode() {
+ return Constants.DBG_FIRST_SPECIAL
+ + value * 7;
+ }
+
+ public boolean equals(Object other) {
+ return (other instanceof Default)
+ && (value == ((Default) other).value);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
new file mode 100644
index 0000000..5295c55
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -0,0 +1,200 @@
+// 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.graph;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
+import com.android.tools.r8.graph.DexDebugEvent.AdvancePC;
+import com.android.tools.r8.graph.DexDebugEvent.Default;
+import com.android.tools.r8.graph.DexDebugEvent.EndLocal;
+import com.android.tools.r8.graph.DexDebugEvent.RestartLocal;
+import com.android.tools.r8.graph.DexDebugEvent.SetFile;
+import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
+import com.android.tools.r8.ir.code.DebugPosition;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Builder for constructing a list of debug events suitable for DexDebugInfo.
+ *
+ * This builder is intended to be very pedantic and ensure a well-formed structure of the resulting
+ * event stream.
+ */
+public class DexDebugEventBuilder {
+
+ private static final int NO_PC_INFO = -1;
+ private static final int NO_LINE_INFO = -1;
+
+ private static class PositionState {
+ int pc = NO_PC_INFO;
+ int line = NO_LINE_INFO;
+ DexString file = null;
+ ImmutableMap<Integer, DebugLocalInfo> locals = null;
+ }
+
+ private final DexMethod method;
+
+ // Previous and current position info to delay emitting position changes.
+ private final PositionState previous;
+ private final PositionState current;
+
+ // In order list of non-this argument locals.
+ private int lastArgumentRegister = -1;
+ private final List<DebugLocalInfo> arguments;
+
+ // Mapping from register to local for currently open/visible locals.
+ private final Map<Integer, DebugLocalInfo> openLocals = new HashMap<>();
+
+ // Mapping from register to the last known local in that register (See DBG_RESTART_LOCAL).
+ private final Map<Integer, DebugLocalInfo> lastKnownLocals = new HashMap<>();
+
+ // Flushed events.
+ private final List<DexDebugEvent> events = new ArrayList<>();
+
+ private int startLine = NO_LINE_INFO;
+
+ public DexDebugEventBuilder(DexMethod method) {
+ this.method = method;
+ arguments = new ArrayList<>(method.proto.parameters.values.length);
+ current = new PositionState();
+ previous = new PositionState();
+ }
+
+ public void startArgument(int register, DebugLocalInfo local, boolean isThis) {
+ // Verify that arguments are started in order.
+ assert register > lastArgumentRegister;
+ lastArgumentRegister = register;
+ // If this is an actual argument record it for header information.
+ if (!isThis) {
+ arguments.add(local);
+ }
+ // If the argument does not have a parametrized type, implicitly open it.
+ if (local != null && local.signature == null) {
+ openLocals.put(register, local);
+ lastKnownLocals.put(register, local);
+ }
+ }
+
+ /** Emits a positions entry if the position has changed and associates any local changes. */
+ public void setPosition(int pc, DebugPosition position) {
+ setPosition(pc, position.line, position.file, position.getLocals());
+ }
+
+ public void setPosition(
+ int pc, int line, DexString file, ImmutableMap<Integer, DebugLocalInfo> locals) {
+ // If we have a pending position and the next differs from it flush the pending one.
+ if (previous.pc != current.pc && positionChanged(current, pc, line, file)) {
+ flushCurrentPosition();
+ }
+ current.pc = pc;
+ current.line = line;
+ current.file = file;
+ current.locals = locals;
+ }
+
+ private void flushCurrentPosition() {
+ // If this is the first emitted possition, initialize previous state: start-line is forced to be
+ // the first actual line, in-effect, causing the first position to be a zero-delta line change.
+ if (startLine == NO_LINE_INFO) {
+ assert events.isEmpty();
+ assert previous.pc == NO_PC_INFO;
+ assert previous.line == NO_LINE_INFO;
+ startLine = current.line;
+ previous.line = current.line;
+ previous.pc = 0;
+ }
+ // Emit position change (which might result in additional advancement events).
+ emitAdvancementEvents();
+ // Emit local changes for new current position (they relate to the already emitted position).
+ // Locals are either defined on all positions or on none.
+ assert current.locals != null || previous.locals == null;
+ if (current.locals != null) {
+ emitLocalChanges();
+ }
+ }
+
+ /** Build the resulting DexDebugInfo object. */
+ public DexDebugInfo build() {
+ if (previous.pc != current.pc) {
+ flushCurrentPosition();
+ }
+ if (startLine == NO_LINE_INFO) {
+ return null;
+ }
+ DexString[] params = new DexString[method.proto.parameters.values.length];
+ assert arguments.isEmpty() || params.length == arguments.size();
+ for (int i = 0; i < arguments.size(); i++) {
+ DebugLocalInfo local = arguments.get(i);
+ params[i] = (local == null || local.signature != null) ? null : local.name;
+ }
+ return new DexDebugInfo(startLine, params, events.toArray(new DexDebugEvent[events.size()]));
+ }
+
+ private static boolean positionChanged(
+ PositionState current, int nextPc, int nextLine, DexString nextFile) {
+ return nextPc != current.pc && (nextLine != current.line || nextFile != current.file);
+ }
+
+ private void emitAdvancementEvents() {
+ int pcDelta = current.pc - previous.pc;
+ int lineDelta = current.line - previous.line;
+ assert pcDelta >= 0;
+ if (current.file != previous.file) {
+ assert current.file == null || !current.file.equals(previous.file);
+ events.add(new SetFile(current.file));
+ }
+ if (lineDelta < Constants.DBG_LINE_BASE
+ || lineDelta - Constants.DBG_LINE_BASE >= Constants.DBG_LINE_RANGE) {
+ events.add(new AdvanceLine(lineDelta));
+ // TODO(herhut): To be super clever, encode only the part that is above limit.
+ lineDelta = 0;
+ }
+ if (pcDelta >= Constants.DBG_ADDRESS_RANGE) {
+ events.add(new AdvancePC(pcDelta));
+ pcDelta = 0;
+ }
+ // TODO(herhut): Maybe only write this one if needed (would differ from DEX).
+ int specialOpcode =
+ 0x0a + (lineDelta - Constants.DBG_LINE_BASE) + Constants.DBG_LINE_RANGE * pcDelta;
+ assert specialOpcode >= 0x0a;
+ assert specialOpcode <= 0xff;
+ events.add(new Default(specialOpcode));
+ previous.pc = current.pc;
+ previous.line = current.line;
+ previous.file = current.file;
+ }
+
+ private void emitLocalChanges() {
+ if (previous.locals == current.locals) {
+ return;
+ }
+ SortedSet<Integer> currentRegisters = new TreeSet<>(openLocals.keySet());
+ SortedSet<Integer> positionRegisters = new TreeSet<>(current.locals.keySet());
+ for (Integer register : currentRegisters) {
+ if (!positionRegisters.contains(register)) {
+ events.add(new EndLocal(register));
+ openLocals.put(register, null);
+ }
+ }
+ for (Integer register : positionRegisters) {
+ DebugLocalInfo positionLocal = current.locals.get(register);
+ DebugLocalInfo currentLocal = openLocals.get(register);
+ if (currentLocal != positionLocal) {
+ openLocals.put(register, positionLocal);
+ if (currentLocal == null && lastKnownLocals.get(register) == positionLocal) {
+ events.add(new RestartLocal(register));
+ } else {
+ events.add(new StartLocal(register, positionLocal));
+ lastKnownLocals.put(register, positionLocal);
+ }
+ }
+ }
+ previous.locals = current.locals;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
new file mode 100644
index 0000000..f82fe2a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+import java.util.List;
+
+public class DexDebugInfo extends CanonicalizedDexItem {
+
+ public final int startLine;
+ public final DexString[] parameters;
+ public final DexDebugEvent[] events;
+
+ public DexDebugInfo(int startLine, DexString[] parameters, DexDebugEvent[] events) {
+ assert startLine >= 0;
+ this.startLine = startLine;
+ this.parameters = parameters;
+ this.events = events;
+ // This call to hashCode is just an optimization to speedup equality when
+ // canonicalizing DexDebugInfo objects inside a synchronize method.
+ hashCode();
+ }
+
+ public List<DexDebugEntry> computeEntries() {
+ DexDebugEntryBuilder builder = new DexDebugEntryBuilder(startLine);
+ for (DexDebugEvent event : events) {
+ event.addToBuilder(builder);
+ }
+ return builder.build();
+ }
+
+ public int computeHashCode() {
+ return startLine
+ + Arrays.hashCode(parameters) * 7
+ + Arrays.hashCode(events) * 13;
+ }
+
+ public boolean computeEquals(Object other) {
+ if (other instanceof DexDebugInfo) {
+ DexDebugInfo o = (DexDebugInfo) other;
+ if (startLine != o.startLine) {
+ return false;
+ }
+ if (!Arrays.equals(parameters, o.parameters)) {
+ return false;
+ }
+ return Arrays.equals(events, o.events);
+ }
+ return false;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection collection) {
+ collectAll(collection, parameters);
+ collectAll(collection, events);
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection collection) {
+ collection.add(this);
+ }
+
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("DebugInfo (line " + startLine + ") events: [\n");
+ for (DexDebugEvent event : events) {
+ builder.append(" ").append(event).append("\n");
+ }
+ builder.append(" END_SEQUENCE\n");
+ builder.append("]\n");
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
new file mode 100644
index 0000000..b421a9c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+
+public class DexEncodedAnnotation extends DexItem {
+
+ private static final int UNSORTED = 0;
+
+ public final DexType type;
+ public final DexAnnotationElement[] elements;
+
+ private int sorted = UNSORTED;
+
+ public DexEncodedAnnotation(DexType type, DexAnnotationElement[] elements) {
+ this.type = type;
+ this.elements = elements;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ type.collectIndexedItems(indexedItems);
+ collectAll(indexedItems, elements);
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ // Should never be called.
+ assert false;
+ }
+
+ public String toString() {
+ return "Encoded annotation " + type + " " + Arrays.toString(elements);
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode() * 7 + Arrays.hashCode(elements);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof DexEncodedAnnotation) {
+ DexEncodedAnnotation that = (DexEncodedAnnotation) other;
+ return that.type.equals(type) && Arrays.equals(that.elements, elements);
+ }
+ return false;
+ }
+
+ public void sort() {
+ if (sorted != UNSORTED) {
+ assert sorted == sortedHashCode();
+ return;
+ }
+ Arrays.sort(elements, (a, b) -> a.name.compareTo(b.name));
+ for (DexAnnotationElement element : elements) {
+ element.value.sort();
+ }
+ sorted = sortedHashCode();
+ }
+
+ private int sortedHashCode() {
+ int hashCode = hashCode();
+ return hashCode == UNSORTED ? 1 : hashCode;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedArray.java b/src/main/java/com/android/tools/r8/graph/DexEncodedArray.java
new file mode 100644
index 0000000..8d7eba9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedArray.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+
+public class DexEncodedArray extends DexItem {
+
+ public final DexValue[] values;
+
+ public DexEncodedArray(DexValue[] values) {
+ this.values = values;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ collectAll(indexedItems, values);
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ mixedItems.add(this);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(values);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ return (o instanceof DexEncodedArray) && (Arrays.equals(((DexEncodedArray) o).values, values));
+ }
+
+ @Override
+ public String toString() {
+ return "EncodedArray " + Arrays.toString(values);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
new file mode 100644
index 0000000..06add73
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+
+public class DexEncodedField extends KeyedDexItem<DexField> {
+
+ public static final DexEncodedField[] EMPTY_ARRAY = new DexEncodedField[]{};
+
+ public final DexField field;
+ public final DexAccessFlags accessFlags;
+ public DexAnnotationSet annotations;
+ public final DexValue staticValue;
+
+ public DexEncodedField(DexField field, DexAccessFlags accessFlags, DexAnnotationSet annotations,
+ DexValue staticValue) {
+ assert !accessFlags.isStatic() || staticValue != null;
+ this.field = field;
+ this.accessFlags = accessFlags;
+ this.annotations = annotations;
+ this.staticValue = staticValue;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ field.collectIndexedItems(indexedItems);
+ annotations.collectIndexedItems(indexedItems);
+ if (staticValue != null) {
+ staticValue.collectIndexedItems(indexedItems);
+ }
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ annotations.collectMixedSectionItems(mixedItems);
+ }
+
+ @Override
+ public String toString() {
+ return "Encoded field " + field;
+ }
+
+ @Override
+ public String toSmaliString() {
+ return field.toSmaliString();
+ }
+
+ @Override
+ public String toSourceString() {
+ return field.toSourceString();
+ }
+
+ @Override
+ public DexField getKey() {
+ return field;
+ }
+
+ public boolean hasAnnotation() {
+ return !annotations.isEmpty();
+ }
+
+ // Returns a const instructions if this field is a compile time final const.
+ public Instruction valueAsConstInstruction(AppInfo appInfo, Value dest) {
+ // The only way to figure out whether the DexValue contains the final value
+ // is ensure the value is not the default or check <clinit> is not present.
+ if (accessFlags.isStatic() && accessFlags.isPublic() && accessFlags.isFinal()) {
+ DexClass clazz = appInfo.definitionFor(field.getHolder());
+ assert clazz != null : "Class for the field must be present";
+ return staticValue.asConstInstruction(
+ clazz.getClassInitializer(appInfo.dexItemFactory) != null, dest);
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
new file mode 100644
index 0000000..f041e52
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -0,0 +1,377 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.code.Const;
+import com.android.tools.r8.code.ConstString;
+import com.android.tools.r8.code.ConstStringJumbo;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.InvokeStatic;
+import com.android.tools.r8.code.NewInstance;
+import com.android.tools.r8.code.Throw;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class DexEncodedMethod extends KeyedDexItem<DexMethod> {
+
+ public enum CompilationState {
+ NOT_PROCESSED,
+ PROCESSED_NOT_INLINING_CANDIDATE,
+ // Code only contains instructions that access public entities.
+ PROCESSED_INLINING_CANDIDATE_PUBLIC,
+ // Code only contains instructions that access public and package private entities.
+ PROCESSED_INLINING_CANDIDATE_PACKAGE_PRIVATE,
+ // Code also contains instructions that access public entities.
+ PROCESSED_INLINING_CANDIDATE_PRIVATE,
+ }
+
+ public static final DexEncodedMethod[] EMPTY_ARRAY = new DexEncodedMethod[]{};
+ public static final DexEncodedMethod SENTINEL =
+ new DexEncodedMethod(null, null, null, null, null);
+
+ public final DexMethod method;
+ public final DexAccessFlags accessFlags;
+ public DexAnnotationSet annotations;
+ public DexAnnotationSetRefList parameterAnnotations;
+ private Code code;
+ private CompilationState compilationState = CompilationState.NOT_PROCESSED;
+ private OptimizationInfo optimizationInfo = DefaultOptimizationInfo.DEFAULT;
+
+ public DexEncodedMethod(DexMethod method, DexAccessFlags accessFlags,
+ DexAnnotationSet annotations, DexAnnotationSetRefList parameterAnnotations, Code code) {
+ this.method = method;
+ this.accessFlags = accessFlags;
+ this.annotations = annotations;
+ this.parameterAnnotations = parameterAnnotations;
+ this.code = code;
+ }
+
+ public boolean isProcessed() {
+ return compilationState != CompilationState.NOT_PROCESSED;
+ }
+
+ public boolean isInliningCandidate(DexEncodedMethod container, boolean alwaysInline) {
+ if (alwaysInline && (compilationState != CompilationState.NOT_PROCESSED)) {
+ // Inline iff holder classes are equal.
+ // Interestingly, alwaysInline is true for some constructors.
+ // (accessFlags.isConstructor())
+ return container.method.getHolder() == method.getHolder();
+ }
+ switch (compilationState) {
+ case PROCESSED_INLINING_CANDIDATE_PUBLIC:
+ return true;
+ case PROCESSED_INLINING_CANDIDATE_PACKAGE_PRIVATE:
+ return container.method.getHolder().isSamePackage(method.getHolder());
+ // TODO(bak): Expand check for package private access:
+ case PROCESSED_INLINING_CANDIDATE_PRIVATE:
+ return container.method.getHolder() == method.getHolder();
+ default:
+ return false;
+ }
+ }
+
+ public void markProcessed(InliningConstraint state) {
+ switch (state) {
+ case ALWAYS:
+ compilationState = CompilationState.PROCESSED_INLINING_CANDIDATE_PUBLIC;
+ break;
+ case PACKAGE:
+ compilationState = CompilationState.PROCESSED_INLINING_CANDIDATE_PACKAGE_PRIVATE;
+ break;
+ case PRIVATE:
+ compilationState = CompilationState.PROCESSED_INLINING_CANDIDATE_PRIVATE;
+ break;
+ case NEVER:
+ compilationState = CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
+ break;
+ }
+ }
+
+ public void markNotProcessed() {
+ compilationState = CompilationState.NOT_PROCESSED;
+ }
+
+ public IRCode buildIR(InternalOptions options) {
+ return code == null ? null : code.buildIR(this, options);
+ }
+
+ public IRCode buildIR(ValueNumberGenerator valueNumberGenerator, InternalOptions options) {
+ return code == null
+ ? null
+ : code.asDexCode().buildIR(this, valueNumberGenerator, options);
+ }
+
+ public void setCode(IRCode ir, RegisterAllocator registerAllocator) {
+ final DexBuilder builder = new DexBuilder(ir, registerAllocator);
+ code = builder.build(method.proto.parameters.values.length);
+ }
+
+ // Replaces the dex code in the method by setting code to result of compiling the IR.
+ public void setCode(IRCode ir, RegisterAllocator registerAllocator,
+ DexString firstJumboString) {
+ final DexBuilder builder = new DexBuilder(ir, registerAllocator, firstJumboString);
+ code = builder.build(method.proto.parameters.values.length);
+ }
+
+ public String toString() {
+ return "Encoded method " + method;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ method.collectIndexedItems(indexedItems);
+ if (code != null) {
+ code.collectIndexedItems(indexedItems);
+ }
+ annotations.collectIndexedItems(indexedItems);
+ parameterAnnotations.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ if (code != null) {
+ code.collectMixedSectionItems(mixedItems);
+ }
+ annotations.collectMixedSectionItems(mixedItems);
+ parameterAnnotations.collectMixedSectionItems(mixedItems);
+ }
+
+ public Code getCode() {
+ return code;
+ }
+
+ public void removeCode() {
+ code = null;
+ }
+
+ public String qualifiedName() {
+ return method.qualifiedName();
+ }
+
+ public String descriptor() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("(");
+ for (DexType type : method.proto.parameters.values) {
+ builder.append(type.descriptor.toString());
+ }
+ builder.append(")");
+ builder.append(method.proto.returnType.descriptor.toString());
+ return builder.toString();
+ }
+
+ public String toSmaliString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".method ");
+ builder.append(accessFlags.toSmaliString());
+ builder.append(" ");
+ builder.append(method.name.toSmaliString());
+ builder.append(method.proto.toSmaliString());
+ builder.append("\n");
+ if (code != null) {
+ DexCode dexCode = code.asDexCode();
+ builder.append(" .registers ");
+ builder.append(dexCode.registerSize);
+ builder.append("\n\n");
+ builder.append(dexCode.toSmaliString(naming));
+ }
+ builder.append(".end method\n");
+ return builder.toString();
+ }
+
+ @Override
+ public String toSourceString() {
+ return method.toSourceString();
+ }
+
+ public DexEncodedMethod toAbstractMethod() {
+ accessFlags.setAbstract();
+ this.code = null;
+ return this;
+ }
+
+ /**
+ * Generates a {@link DexCode} object for the given instructions.
+ * <p>
+ * As the code object is produced outside of the normal compilation cycle, it has to use
+ * {@link ConstStringJumbo} to reference string constants. Hence, code produced form these
+ * templates might incur a size overhead.
+ */
+ private DexCode generateCodeFromTemplate(
+ int numberOfRegisters, int outRegisters, Instruction[] instructions) {
+ int offset = 0;
+ for (Instruction instruction : instructions) {
+ assert !(instruction instanceof ConstString);
+ instruction.setOffset(offset);
+ offset += instruction.getSize();
+ }
+ int requiredArgRegisters = accessFlags.isStatic() ? 0 : 1;
+ for (DexType type : method.proto.parameters.values) {
+ requiredArgRegisters += MoveType.fromDexType(type).requiredRegisters();
+ }
+ // Passing null as highestSortingString is save, as ConstString instructions are not allowed.
+ return new DexCode(Math.max(numberOfRegisters, requiredArgRegisters), requiredArgRegisters,
+ outRegisters, instructions, new DexCode.Try[0], new DexCode.TryHandler[0], null, null);
+ }
+
+ public DexEncodedMethod toEmptyThrowingMethod() {
+ Instruction insn[] = {new Const(0, 0), new Throw(0)};
+ code = generateCodeFromTemplate(1, 0, insn);
+ return this;
+ }
+
+ public DexEncodedMethod toMethodThatLogsError(DexItemFactory itemFactory) {
+ Signature signature = MethodSignature.fromDexMethod(this.method);
+ // TODO(herhut): Construct this out of parts to enable reuse, maybe even using descriptors.
+ DexString message = itemFactory.createString(
+ "Shaking error: Missing method in " + method.holder.toSourceString() + ": "
+ + signature);
+ DexString tag = itemFactory.createString("TOIGHTNESS");
+ DexType[] args = {itemFactory.stringType, itemFactory.stringType};
+ DexProto proto = itemFactory.createProto(itemFactory.intType, args);
+ DexMethod logMethod = itemFactory
+ .createMethod(itemFactory.createType("Landroid/util/Log;"), proto,
+ itemFactory.createString("e"));
+ DexType exceptionType = itemFactory.createType("Ljava/lang/RuntimeException;");
+ DexType[] exceptionArgs = {exceptionType, itemFactory.stringType};
+ DexMethod initMethod = itemFactory
+ .createMethod(exceptionType, itemFactory.createProto(itemFactory.voidType, exceptionArgs),
+ itemFactory.constructorMethodName);
+ // These methods might not get registered for jumbo string processing, therefore we always
+ // use the jumbo string encoding for the const string instruction.
+ Instruction insn[] = {
+ new ConstStringJumbo(0, tag),
+ new ConstStringJumbo(1, message),
+ new InvokeStatic(2, logMethod, 0, 1, 0, 0, 0),
+ new NewInstance(0, exceptionType),
+ new InvokeStatic(2, initMethod, 0, 1, 0, 0, 0),
+ new Throw(0)
+ };
+ code = generateCodeFromTemplate(2, 2, insn);
+ return this;
+ }
+
+ public String codeToString() {
+ return code == null ? "<no code>" : code.toString();
+ }
+
+ @Override
+ public DexMethod getKey() {
+ return method;
+ }
+
+ public boolean hasAnnotation() {
+ return !annotations.isEmpty() || !parameterAnnotations.isEmpty();
+ }
+
+ public void registerReachableDefinitions(UseRegistry registry) {
+ if (code != null) {
+ if (Log.ENABLED) {
+ Log.verbose((Class) getClass(), "Registering definitions reachable from `%s`.", method);
+ }
+ code.registerReachableDefinitions(registry);
+ }
+ }
+
+ public static class OptimizationInfo {
+ private int returnedArgument = -1;
+ private boolean neverReturnsNull = false;
+ private boolean returnsConstant = false;
+ private long returnedConstant = 0;
+ private boolean forceInline = false;
+
+ private OptimizationInfo() {
+ // Intentionally left empty.
+ }
+
+ public boolean returnsArgument() {
+ return returnedArgument != -1;
+ }
+
+ public int getReturnedArgument() {
+ assert returnsArgument();
+ return returnedArgument;
+ }
+
+ public boolean neverReturnsNull() {
+ return neverReturnsNull;
+ }
+
+ public boolean returnsConstant() {
+ return returnsConstant;
+ }
+
+ public long getReturnedConstant() {
+ assert returnsConstant();
+ return returnedConstant;
+ }
+
+ public boolean forceInline() {
+ return forceInline;
+ }
+
+ private void markReturnsArgument(int argument) {
+ assert argument >= 0;
+ assert returnedArgument == -1 || returnedArgument == argument;
+ returnedArgument = argument;
+ }
+
+ private void markNeverReturnsNull() {
+ neverReturnsNull = true;
+ }
+
+ private void markReturnsConstant(long value) {
+ assert !returnsConstant || returnedConstant == value;
+ returnsConstant = true;
+ returnedConstant = value;
+ }
+
+ private void markForceInline() {
+ forceInline = true;
+ }
+ }
+
+ private static class DefaultOptimizationInfo extends OptimizationInfo {
+ public static final OptimizationInfo DEFAULT = new DefaultOptimizationInfo();
+
+ private DefaultOptimizationInfo() {}
+ }
+
+ synchronized private OptimizationInfo ensureMutableOI() {
+ if (optimizationInfo == DefaultOptimizationInfo.DEFAULT) {
+ optimizationInfo = new OptimizationInfo();
+ }
+ return optimizationInfo;
+ }
+
+ synchronized public void markReturnsArgument(int argument) {
+ ensureMutableOI().markReturnsArgument(argument);
+ }
+
+ synchronized public void markNeverReturnsNull() {
+ ensureMutableOI().markNeverReturnsNull();
+ }
+
+ synchronized public void markReturnsConstant(long value) {
+ ensureMutableOI().markReturnsConstant(value);
+ }
+
+ synchronized public void markForceInline() {
+ ensureMutableOI().markForceInline();
+ }
+
+ public OptimizationInfo getOptimizationInfo() {
+ return optimizationInfo;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
new file mode 100644
index 0000000..f97c147
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.naming.NamingLens;
+
+public class DexField extends Descriptor<DexEncodedField, DexField> implements
+ PresortedComparable<DexField> {
+
+ public final DexType clazz;
+ public final DexType type;
+ public final DexString name;
+
+ DexField(DexType clazz, DexType type, DexString name) {
+ this.clazz = clazz;
+ this.type = type;
+ this.name = name;
+ }
+
+ @Override
+ public int computeHashCode() {
+ return clazz.hashCode()
+ + type.hashCode() * 7
+ + name.hashCode() * 31;
+ }
+
+ @Override
+ public boolean computeEquals(Object other) {
+ if (other instanceof DexField) {
+ DexField o = (DexField) other;
+ return clazz.equals(o.clazz)
+ && type.equals(o.type)
+ && name.equals(o.name);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "Field " + type + " " + clazz + "." + name;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ if (indexedItems.addField(this)) {
+ clazz.collectIndexedItems(indexedItems);
+ type.collectIndexedItems(indexedItems);
+ indexedItems.getRenamedName(this).collectIndexedItems(indexedItems);
+ }
+ }
+
+ @Override
+ public int getOffset(ObjectToOffsetMapping mapping) {
+ return mapping.getOffsetFor(this);
+ }
+
+ @Override
+ public int compareTo(DexField other) {
+ return sortedCompareTo(other.getSortedIndex());
+ }
+
+ @Override
+ public int slowCompareTo(DexField other) {
+ int result = clazz.slowCompareTo(other.clazz);
+ if (result != 0) {
+ return result;
+ }
+ result = name.slowCompareTo(other.name);
+ if (result != 0) {
+ return result;
+ }
+ return type.slowCompareTo(other.type);
+ }
+
+ @Override
+ public int slowCompareTo(DexField other, NamingLens namingLens) {
+ int result = clazz.slowCompareTo(other.clazz, namingLens);
+ if (result != 0) {
+ return result;
+ }
+ result = namingLens.lookupName(this).slowCompareTo(namingLens.lookupName(other));
+ if (result != 0) {
+ return result;
+ }
+ return type.slowCompareTo(other.type, namingLens);
+ }
+
+ @Override
+ public int layeredCompareTo(DexField other, NamingLens namingLens) {
+ int result = clazz.compareTo(other.clazz);
+ if (result != 0) {
+ return result;
+ }
+ result = namingLens.lookupName(this).compareTo(namingLens.lookupName(other));
+ if (result != 0) {
+ return result;
+ }
+ return type.compareTo(other.type);
+ }
+
+ @Override
+ public boolean match(DexEncodedField entry) {
+ return entry.field.name == name && entry.field.type == type;
+ }
+
+ @Override
+ public DexType getHolder() {
+ return clazz;
+ }
+
+ public String toSmaliString() {
+ return clazz.toSmaliString() + "->" + name + ":" + type.toSmaliString();
+ }
+
+ @Override
+ public String toSourceString() {
+ return type.toSourceString() + " " + clazz.toSourceString() + "." + name.toSourceString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItem.java b/src/main/java/com/android/tools/r8/graph/DexItem.java
new file mode 100644
index 0000000..c0df752
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexItem.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.function.Consumer;
+
+public abstract class DexItem {
+
+ static <T extends DexItem> void collectAll(IndexedItemCollection indexedItems, T[] items) {
+ consumeArray(items, (T item) -> item.collectIndexedItems(indexedItems));
+ }
+
+ public static <T extends DexItem> void collectAll(MixedSectionCollection mixedItems, T[] items) {
+ consumeArray(items, (T item) -> item.collectMixedSectionItems(mixedItems));
+ }
+
+ /**
+ * Helper method to iterate over elements in an array.
+ * Handles the case where the array is null.
+ */
+ private static <T extends DexItem> void consumeArray(T[] items, Consumer<T> consumer) {
+ if (items == null) {
+ return;
+ }
+ for (T item : items) {
+ if (item != null) {
+ consumer.accept(item);
+ }
+ }
+ }
+
+ abstract void collectIndexedItems(IndexedItemCollection collection);
+
+ abstract void collectMixedSectionItems(MixedSectionCollection collection);
+
+ protected void flushCachedValues() {
+ // Overwritten in subclasses.
+ }
+
+ public String toSmaliString() {
+ return toString();
+ }
+
+ public String toSourceString() {
+ return toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
new file mode 100644
index 0000000..ce974ce
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -0,0 +1,367 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.naming.NamingLens;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public class DexItemFactory {
+
+ private final Map<DexString, DexString> strings = new HashMap<>();
+ private final Map<DexType, DexType> types = new HashMap<>();
+ private final Map<DexField, DexField> fields = new HashMap<>();
+ private final Map<DexProto, DexProto> protos = new HashMap<>();
+ private final Map<DexMethod, DexMethod> methods = new HashMap<>();
+ private final Map<DexCallSite, DexCallSite> callSites = new HashMap<>();
+ private final Map<DexMethodHandle, DexMethodHandle> methodHandles = new HashMap<>();
+
+ boolean sorted = false;
+
+ public static final DexType catchAllType = new DexType(new DexString("CATCH_ALL"));
+ private static final Set<DexItem> internalSentinels = ImmutableSet.of(catchAllType);
+
+ public DexString booleanDescriptor = createString("Z");
+ public DexString byteDescriptor = createString("B");
+ public DexString charDescriptor = createString("C");
+ public DexString doubleDescriptor = createString("D");
+ public DexString floatDescriptor = createString("F");
+ public DexString intDescriptor = createString("I");
+ public DexString longDescriptor = createString("J");
+ public DexString shortDescriptor = createString("S");
+ public DexString voidDescriptor = createString("V");
+
+ public DexString boxedBooleanDescriptor = createString("Ljava/lang/Boolean;");
+ public DexString boxedByteDescriptor = createString("Ljava/lang/Byte;");
+ public DexString boxedCharDescriptor = createString("Ljava/lang/Character;");
+ public DexString boxedDoubleDescriptor = createString("Ljava/lang/Double;");
+ public DexString boxedFloatDescriptor = createString("Ljava/lang/Float;");
+ public DexString boxedIntDescriptor = createString("Ljava/lang/Integer;");
+ public DexString boxedLongDescriptor = createString("Ljava/lang/Long;");
+ public DexString boxedShortDescriptor = createString("Ljava/lang/Short;");
+ public DexString boxedNumberDescriptor = createString("Ljava/lang/Number;");
+
+ public DexString unboxBooleanMethodName = createString("booleanValue");
+ public DexString unboxByteMethodName = createString("byteValue");
+ public DexString unboxCharMethodName = createString("charValue");
+ public DexString unboxShortMethodName = createString("shortValue");
+ public DexString unboxIntMethodName = createString("intValue");
+ public DexString unboxLongMethodName = createString("longValue");
+ public DexString unboxFloatMethodName = createString("floatValue");
+ public DexString unboxDoubleMethodName = createString("doubleValue");
+
+ public DexString valueOfMethodName = createString("valueOf");
+
+ public DexString getClassMethodName = createString("getClass");
+
+ public DexString stringDescriptor = createString("Ljava/lang/String;");
+ public DexString objectDescriptor = createString("Ljava/lang/Object;");
+ public DexString classDescriptor = createString("Ljava/lang/Class;");
+ public DexString objectsDescriptor = createString("Ljava/util/Objects;");
+
+ public DexString constructorMethodName = createString(Constants.INSTANCE_INITIALIZER_NAME);
+ public DexString classConstructorMethodName = createString(Constants.CLASS_INITIALIZER_NAME);
+
+ public DexString thisName = createString("this");
+
+ private DexString charArrayDescriptor = createString("[C");
+
+ public DexType booleanType = createType(booleanDescriptor);
+ public DexType byteType = createType(byteDescriptor);
+ public DexType charType = createType(charDescriptor);
+ public DexType doubleType = createType(doubleDescriptor);
+ public DexType floatType = createType(floatDescriptor);
+ public DexType intType = createType(intDescriptor);
+ public DexType longType = createType(longDescriptor);
+ public DexType shortType = createType(shortDescriptor);
+ public DexType voidType = createType(voidDescriptor);
+
+ public DexType boxedBooleanType = createType(boxedBooleanDescriptor);
+ public DexType boxedByteType = createType(boxedByteDescriptor);
+ public DexType boxedCharType = createType(boxedCharDescriptor);
+ public DexType boxedDoubleType = createType(boxedDoubleDescriptor);
+ public DexType boxedFloatType = createType(boxedFloatDescriptor);
+ public DexType boxedIntType = createType(boxedIntDescriptor);
+ public DexType boxedLongType = createType(boxedLongDescriptor);
+ public DexType boxedShortType = createType(boxedShortDescriptor);
+ public DexType boxedNumberType = createType(boxedNumberDescriptor);
+
+ public DexType stringType = createType(stringDescriptor);
+ public DexType objectType = createType(objectDescriptor);
+
+ public StringBuildingMethods stringBuilderMethods =
+ new StringBuildingMethods(createString("Ljava/lang/StringBuilder;"));
+ public StringBuildingMethods stringBufferMethods =
+ new StringBuildingMethods(createString("Ljava/lang/StringBuffer;"));
+ public ObjectsMethods objectsMethods = new ObjectsMethods();
+ public ObjectMethods objectMethods = new ObjectMethods();
+ public LongMethods longMethods = new LongMethods();
+
+ public void clearSubtypeInformation() {
+ types.values().forEach(DexType::clearSubtypeInformation);
+ }
+
+ public class LongMethods {
+ public DexMethod compare;
+
+ private LongMethods() {
+ compare = createMethod(boxedLongDescriptor,
+ createString("compare"), intDescriptor, new DexString[]{longDescriptor, longDescriptor});
+ }
+ }
+
+ public class ObjectMethods {
+ public DexMethod getClass;
+
+ private ObjectMethods() {
+ getClass = createMethod(objectsDescriptor,
+ getClassMethodName, classDescriptor, new DexString[]{});
+ }
+ }
+
+ public class ObjectsMethods {
+ public DexMethod requireNonNull;
+
+ private ObjectsMethods() {
+ requireNonNull = createMethod(objectsDescriptor,
+ createString("requireNonNull"), objectDescriptor, new DexString[]{objectDescriptor});
+ }
+ }
+
+ public class StringBuildingMethods {
+ public DexMethod appendBoolean;
+ public DexMethod appendChar;
+ public DexMethod appendCharArray;
+ public DexMethod appendSubCharArray;
+ public DexMethod appendCharSequence;
+ public DexMethod appendSubCharSequence;
+ public DexMethod appendInt;
+ public DexMethod appendDouble;
+ public DexMethod appendFloat;
+ public DexMethod appendLong;
+ public DexMethod appendObject;
+ public DexMethod appendString;
+ public DexMethod appendStringBuffer;
+ public DexMethod toString;
+
+ private StringBuildingMethods(DexString receiver) {
+ DexString sbuf = createString("Ljava/lang/StringBuffer;");
+ DexString charSequence = createString("Ljava/lang/CharSequence;");
+ DexString append = createString("append");
+ DexString toStringMethodName = createString("toString");
+
+ appendBoolean = createMethod(receiver, append, receiver, new DexString[]{booleanDescriptor});
+ appendChar = createMethod(receiver, append, receiver, new DexString[]{charDescriptor});
+ appendCharArray = createMethod(receiver, append, receiver, new DexString[]{
+ charArrayDescriptor});
+ appendSubCharArray = createMethod(receiver, append, receiver,
+ new DexString[]{charArrayDescriptor, intDescriptor, intDescriptor});
+ appendCharSequence = createMethod(receiver, append, receiver,
+ new DexString[]{charSequence});
+ appendSubCharSequence = createMethod(receiver, append, receiver,
+ new DexString[]{charSequence, intDescriptor, intDescriptor});
+ appendInt = createMethod(receiver, append, receiver, new DexString[]{intDescriptor});
+ appendDouble = createMethod(receiver, append, receiver, new DexString[]{doubleDescriptor});
+ appendFloat = createMethod(receiver, append, receiver, new DexString[]{floatDescriptor});
+ appendLong = createMethod(receiver, append, receiver, new DexString[]{longDescriptor});
+ appendObject = createMethod(receiver, append, receiver, new DexString[]{objectDescriptor});
+ appendString = createMethod(receiver, append, receiver, new DexString[]{stringDescriptor});
+ appendStringBuffer = createMethod(receiver, append, receiver, new DexString[]{sbuf});
+ toString = createMethod(receiver, toStringMethodName, stringDescriptor,
+ DexString.EMPTY_ARRAY);
+ }
+
+ public void forEeachAppendMethod(Consumer<DexMethod> consumer) {
+ consumer.accept(appendBoolean);
+ consumer.accept(appendChar);
+ consumer.accept(appendCharArray);
+ consumer.accept(appendSubCharArray);
+ consumer.accept(appendCharSequence);
+ consumer.accept(appendSubCharSequence);
+ consumer.accept(appendInt);
+ consumer.accept(appendDouble);
+ consumer.accept(appendFloat);
+ consumer.accept(appendLong);
+ consumer.accept(appendObject);
+ consumer.accept(appendString);
+ consumer.accept(appendStringBuffer);
+ consumer.accept(appendBoolean);
+ }
+ }
+
+ synchronized private static <T extends DexItem> T canonicalize(Map<T, T> map, T item) {
+ assert item != null;
+ assert !internalSentinels.contains(item);
+ T previous = map.putIfAbsent(item, item);
+ return previous == null ? item : previous;
+ }
+
+ public DexString createString(int size, byte[] content) {
+ assert !sorted;
+ DexString string = new DexString(size, content);
+ return canonicalize(strings, string);
+ }
+
+ public DexString createString(String source) {
+ assert !sorted;
+ DexString string = new DexString(source);
+ return canonicalize(strings, string);
+ }
+
+ public DexString lookupString(String source) {
+ return strings.get(new DexString(source));
+ }
+
+ public DexType createType(DexString descriptor) {
+ assert !sorted;
+ DexType type = new DexType(descriptor);
+ return canonicalize(types, type);
+ }
+
+ public DexType createType(String descriptor) {
+ return createType(createString(descriptor));
+ }
+
+ public DexType lookupType(String descriptor) {
+ DexString string = lookupString(descriptor);
+ if (string != null) {
+ return types.get(new DexType(string));
+ }
+ return null;
+ }
+
+ public DexField createField(DexType clazz, DexType type, DexString name) {
+ assert !sorted;
+ DexField field = new DexField(clazz, type, name);
+ return canonicalize(fields, field);
+ }
+
+ public DexProto createProto(DexString shorty, DexType type, DexTypeList parameters) {
+ assert !sorted;
+ DexProto proto = new DexProto(shorty, type, parameters);
+ return canonicalize(protos, proto);
+ }
+
+ public DexProto createProto(DexString shorty, DexType type, DexType[] parameters) {
+ assert !sorted;
+ return createProto(shorty, type,
+ parameters.length == 0 ? DexTypeList.empty() : new DexTypeList(parameters));
+ }
+
+ public DexProto createProto(DexType type, DexType[] parameters) {
+ return createProto(createShorty(type, parameters), type, parameters);
+ }
+
+ public DexString createShorty(DexType returnType, DexType[] argumentTypes) {
+ StringBuilder shortyBuilder = new StringBuilder();
+ shortyBuilder.append(returnType.toShorty());
+ for (DexType argumentType : argumentTypes) {
+ shortyBuilder.append(argumentType.toShorty());
+ }
+ return createString(shortyBuilder.toString());
+ }
+
+ public DexMethod createMethod(DexType holder, DexProto proto, DexString name) {
+ assert !sorted;
+ DexMethod method = new DexMethod(holder, proto, name);
+ return canonicalize(methods, method);
+ }
+
+ public DexMethodHandle createMethodHandle(
+ MethodHandleType type, Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod) {
+ assert !sorted;
+ DexMethodHandle methodHandle = new DexMethodHandle(type, fieldOrMethod);
+ return canonicalize(methodHandles, methodHandle);
+ }
+
+ public DexCallSite createCallSite(
+ DexString methodName, DexProto methodProto,
+ DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
+ assert !sorted;
+ DexCallSite callSite = new DexCallSite(methodName, methodProto, bootstrapMethod, bootstrapArgs);
+ return canonicalize(callSites, callSite);
+ }
+
+ public DexMethod createMethod(DexString clazzDescriptor, DexString name,
+ DexString returnTypeDescriptor,
+ DexString[] parameterDescriptors) {
+ assert !sorted;
+ DexType clazz = createType(clazzDescriptor);
+ DexType returnType = createType(returnTypeDescriptor);
+ DexType[] parameterTypes = new DexType[parameterDescriptors.length];
+ for (int i = 0; i < parameterDescriptors.length; i++) {
+ parameterTypes[i] = createType(parameterDescriptors[i]);
+ }
+ DexProto proto = createProto(shorty(returnType, parameterTypes), returnType, parameterTypes);
+
+ return createMethod(clazz, proto, name);
+ }
+
+ public boolean isConstructor(DexMethod method) {
+ return method.name == constructorMethodName;
+ }
+
+ public boolean isClassConstructor(DexMethod method) {
+ return method.name == classConstructorMethodName;
+ }
+
+ private DexString shorty(DexType returnType, DexType[] parameters) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(returnType.toDescriptorString().charAt(0));
+ for (DexType parameter : parameters) {
+ String descriptor = parameter.toDescriptorString();
+ if (descriptor.charAt(0) == '[') {
+ builder.append('L');
+ } else {
+ builder.append(descriptor.charAt(0));
+ }
+ }
+ return createString(builder.toString());
+ }
+
+ private static <S extends PresortedComparable<S>> void assignSortedIndices(Collection<S> items,
+ NamingLens namingLens) {
+ List<S> sorted = new ArrayList<>(items);
+ sorted.sort((a, b) -> a.layeredCompareTo(b, namingLens));
+ int i = 0;
+ for (S value : sorted) {
+ value.setSortedIndex(i++);
+ }
+ }
+
+ synchronized public void sort(NamingLens namingLens) {
+ assert !sorted;
+ assignSortedIndices(strings.values(), namingLens);
+ assignSortedIndices(types.values(), namingLens);
+ assignSortedIndices(fields.values(), namingLens);
+ assignSortedIndices(protos.values(), namingLens);
+ assignSortedIndices(methods.values(), namingLens);
+ sorted = true;
+ }
+
+ synchronized public void resetSortedIndices() {
+ if (!sorted) {
+ return;
+ }
+ // Only used for asserting that we don't use the sorted index after we build the graph.
+ strings.values().forEach(IndexedDexItem::resetSortedIndex);
+ types.values().forEach(IndexedDexItem::resetSortedIndex);
+ fields.values().forEach(IndexedDexItem::resetSortedIndex);
+ protos.values().forEach(IndexedDexItem::resetSortedIndex);
+ methods.values().forEach(IndexedDexItem::resetSortedIndex);
+ sorted = false;
+ }
+
+ synchronized public void forAllTypes(Consumer<DexType> f) {
+ new ArrayList<>(types.values()).forEach(f);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
new file mode 100644
index 0000000..f0ca9b5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+
+public class DexLibraryClass extends DexClass {
+
+ public DexLibraryClass(DexType type, Origin origin, DexAccessFlags accessFlags, DexType superType,
+ DexTypeList interfaces, DexString sourceFile, DexAnnotationSet annotations,
+ DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
+ DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods) {
+ super(sourceFile, interfaces, accessFlags, superType, type,
+ staticFields, instanceFields, directMethods, virtualMethods, annotations, origin);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public String toString() {
+ return type.toString() + "(library class)";
+ }
+
+ @Override
+ public void addDependencies(MixedSectionCollection collector) {
+ // Should never happen but does not harm.
+ assert false;
+ }
+
+ @Override
+ public boolean isLibraryClass() {
+ return true;
+ }
+
+ @Override
+ public DexLibraryClass asLibraryClass() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java
new file mode 100644
index 0000000..11a9396
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+
+public class DexMemberAnnotation<T extends Descriptor, S extends DexItem> extends DexItem {
+
+ public final T item;
+ public final S annotations;
+
+ public DexMemberAnnotation(T item, S annotations) {
+ this.item = item;
+ this.annotations = annotations;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ item.collectIndexedItems(indexedItems);
+ annotations.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ annotations.collectMixedSectionItems(mixedItems);
+ }
+
+ @Override
+ public int hashCode() {
+ return item.hashCode() * 7 + annotations.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof DexMemberAnnotation) {
+ DexMemberAnnotation otherMember = (DexMemberAnnotation) other;
+ return item.equals(otherMember.item) && annotations.equals(otherMember.annotations);
+ }
+ return false;
+ }
+
+ public static class DexFieldAnnotation extends DexMemberAnnotation<DexField, DexAnnotationSet> {
+
+ public DexFieldAnnotation(DexField item, DexAnnotationSet annotations) {
+ super(item, annotations);
+ }
+ }
+
+ public static class DexMethodAnnotation extends DexMemberAnnotation<DexMethod, DexAnnotationSet> {
+
+ public DexMethodAnnotation(DexMethod item, DexAnnotationSet annotations) {
+ super(item, annotations);
+ }
+ }
+
+ public static class DexParameterAnnotation extends
+ DexMemberAnnotation<DexMethod, DexAnnotationSetRefList> {
+
+ public DexParameterAnnotation(DexMethod item, DexAnnotationSetRefList annotations) {
+ super(item, annotations);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
new file mode 100644
index 0000000..de536f6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -0,0 +1,152 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.naming.NamingLens;
+
+public class DexMethod extends Descriptor<DexEncodedMethod, DexMethod>
+ implements PresortedComparable<DexMethod> {
+
+ public final DexType holder;
+ public final DexProto proto;
+ public final DexString name;
+
+ // Caches used during processing.
+ private DexEncodedMethod singleTargetCache;
+
+ DexMethod(DexType holder, DexProto proto, DexString name) {
+ this.holder = holder;
+ this.proto = proto;
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "Method " + holder + "." + name + " " + proto.toString();
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ if (indexedItems.addMethod(this)) {
+ holder.collectIndexedItems(indexedItems);
+ proto.collectIndexedItems(indexedItems);
+ indexedItems.getRenamedName(this).collectIndexedItems(indexedItems);
+ }
+ }
+
+ @Override
+ public int getOffset(ObjectToOffsetMapping mapping) {
+ return mapping.getOffsetFor(this);
+ }
+
+ public int computeHashCode() {
+ return holder.hashCode()
+ + proto.hashCode() * 7
+ + name.hashCode() * 31;
+ }
+
+ public boolean computeEquals(Object other) {
+ if (other instanceof DexMethod) {
+ DexMethod o = (DexMethod) other;
+ return holder.equals(o.holder)
+ && name.equals(o.name)
+ && proto.equals(o.proto);
+ }
+ return false;
+ }
+
+ @Override
+ public int compareTo(DexMethod other) {
+ return sortedCompareTo(other.getSortedIndex());
+ }
+
+ @Override
+ public int slowCompareTo(DexMethod other) {
+ int result = holder.slowCompareTo(other.holder);
+ if (result != 0) {
+ return result;
+ }
+ result = name.slowCompareTo(other.name);
+ if (result != 0) {
+ return result;
+ }
+ return proto.slowCompareTo(other.proto);
+ }
+
+ @Override
+ public int slowCompareTo(DexMethod other, NamingLens namingLens) {
+ int result = holder.slowCompareTo(other.holder, namingLens);
+ if (result != 0) {
+ return result;
+ }
+ result = namingLens.lookupName(this).slowCompareTo(namingLens.lookupName(other));
+ if (result != 0) {
+ return result;
+ }
+ return proto.slowCompareTo(other.proto, namingLens);
+ }
+
+ @Override
+ public int layeredCompareTo(DexMethod other, NamingLens namingLens) {
+ int result = holder.compareTo(other.holder);
+ if (result != 0) {
+ return result;
+ }
+ result = namingLens.lookupName(this).compareTo(namingLens.lookupName(other));
+ if (result != 0) {
+ return result;
+ }
+ return proto.compareTo(other.proto);
+ }
+
+ @Override
+ public boolean match(DexEncodedMethod entry) {
+ return entry.method.name == name && entry.method.proto == proto;
+ }
+
+ @Override
+ public DexType getHolder() {
+ return holder;
+ }
+
+ public String qualifiedName() {
+ return holder + "." + name;
+ }
+
+ public String toSmaliString() {
+ return holder.toSmaliString() + "->" + name + proto.toSmaliString();
+ }
+
+ public String toSourceString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(proto.returnType.toSourceString());
+ builder.append(" ");
+ builder.append(holder.toSourceString());
+ builder.append(".");
+ builder.append(name);
+ builder.append("(");
+ for (int i = 0; i < proto.parameters.values.length; i++) {
+ if (i != 0) {
+ builder.append(", ");
+ }
+ builder.append(proto.parameters.values[i].toSourceString());
+ }
+ builder.append(")");
+ return builder.toString();
+ }
+
+ synchronized public void setSingleVirtualMethodCache(DexEncodedMethod method) {
+ singleTargetCache = method == null ? DexEncodedMethod.SENTINEL : method;
+ }
+
+ synchronized public boolean isSingleVirtualMethodCached() {
+ return singleTargetCache != null;
+ }
+
+ synchronized public DexEncodedMethod getSingleVirtualMethodCache() {
+ assert isSingleVirtualMethodCached();
+ return singleTargetCache == DexEncodedMethod.SENTINEL ? null : singleTargetCache;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
new file mode 100644
index 0000000..e59e7c9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -0,0 +1,187 @@
+// 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.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+
+public class DexMethodHandle extends IndexedDexItem {
+
+ public enum MethodHandleType {
+ // Method handle dex type.
+ STATIC_PUT((short) 0x00),
+ STATIC_GET((short) 0x01),
+ INSTANCE_PUT((short) 0x02),
+ INSTANCE_GET((short) 0x03),
+ INVOKE_STATIC((short) 0x04),
+ INVOKE_INSTANCE((short) 0x05),
+ // Upcoming method handle dex type.
+ INVOKE_CONSTRUCTOR((short) 0x06),
+ // Internal method handle needed by lambda desugaring.
+ INVOKE_INTERFACE((short) 0x07),
+ INVOKE_SUPER((short) 0x08);
+
+ private final short value;
+
+ MethodHandleType(short value) {
+ this.value = value;
+ }
+
+ public short getValue() {
+ return value;
+ }
+
+ public static MethodHandleType getKind(int value) {
+ MethodHandleType kind;
+
+ switch (value) {
+ case 0x00:
+ kind = STATIC_PUT;
+ break;
+ case 0x01:
+ kind = STATIC_GET;
+ break;
+ case 0x02:
+ kind = INSTANCE_PUT;
+ break;
+ case 0x03:
+ kind = INSTANCE_GET;
+ break;
+ case 0x04:
+ kind = INVOKE_STATIC;
+ break;
+ case 0x05:
+ kind = INVOKE_INSTANCE;
+ break;
+ case 0x06:
+ kind = INVOKE_CONSTRUCTOR;
+ break;
+ case 0x07:
+ kind = INVOKE_INTERFACE;
+ break;
+ case 0x08:
+ kind = INVOKE_SUPER;
+ break;
+ default:
+ throw new AssertionError();
+ }
+
+ assert kind.getValue() == value;
+ return kind;
+ }
+
+ public boolean isFieldType() {
+ return isStaticPut() || isStaticGet() || isInstancePut() || isInstanceGet();
+ }
+
+ public boolean isMethodType() {
+ return isInvokeStatic() || isInvokeInstance() || isInvokeInterface() || isInvokeSuper()
+ || isInvokeConstructor();
+ }
+
+ public boolean isStaticPut() {
+ return this == MethodHandleType.STATIC_PUT;
+ }
+
+ public boolean isStaticGet() {
+ return this == MethodHandleType.STATIC_GET;
+ }
+
+ public boolean isInstancePut() {
+ return this == MethodHandleType.INSTANCE_PUT;
+ }
+
+ public boolean isInstanceGet() {
+ return this == MethodHandleType.INSTANCE_GET;
+ }
+
+ public boolean isInvokeStatic() {
+ return this == MethodHandleType.INVOKE_STATIC;
+ }
+
+ public boolean isInvokeInstance() {
+ return this == MethodHandleType.INVOKE_INSTANCE;
+ }
+
+ public boolean isInvokeInterface() {
+ return this == MethodHandleType.INVOKE_INTERFACE;
+ }
+
+ public boolean isInvokeSuper() {
+ return this == MethodHandleType.INVOKE_SUPER;
+ }
+
+ public boolean isInvokeConstructor() {
+ return this == MethodHandleType.INVOKE_CONSTRUCTOR;
+ }
+ }
+
+ public MethodHandleType type;
+ public Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod;
+
+ public DexMethodHandle(
+ MethodHandleType type, Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod) {
+ this.type = type;
+ this.fieldOrMethod = fieldOrMethod;
+ }
+
+ public int computeHashCode() {
+ return type.hashCode() + fieldOrMethod.computeHashCode() * 7;
+ }
+
+ public boolean computeEquals(Object other) {
+ if (other instanceof DexMethodHandle) {
+ DexMethodHandle o = (DexMethodHandle) other;
+ return type.equals(o.type) && fieldOrMethod.computeEquals(o.fieldOrMethod);
+ }
+ return false;
+ }
+
+ public String toString() {
+ StringBuilder builder = new StringBuilder("MethodHandle: {")
+ .append(type)
+ .append(", ")
+ .append(fieldOrMethod.toSourceString())
+ .append("}");
+ return builder.toString();
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ if (indexedItems.addMethodHandle(this)) {
+ fieldOrMethod.collectIndexedItems(indexedItems);
+ }
+ }
+
+ @Override
+ public int getOffset(ObjectToOffsetMapping mapping) {
+ return mapping.getOffsetFor(this);
+ }
+
+ // TODO(mikaelpeltier): Adapt syntax when invoke-custom will be available into smali.
+ public String toSmaliString() {
+ return toString();
+ }
+
+ public boolean isFieldHandle() {
+ return type.isFieldType();
+ }
+
+ public boolean isMethodHandle() {
+ return type.isMethodType();
+ }
+
+ public boolean isStaticHandle() {
+ return type.isStaticPut() || type.isStaticGet() || type.isInvokeStatic();
+ }
+
+ public DexMethod asMethod() {
+ assert isMethodHandle();
+ return (DexMethod) fieldOrMethod;
+ }
+
+ public DexField asField() {
+ assert isFieldHandle();
+ return (DexField) fieldOrMethod;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
new file mode 100644
index 0000000..2fc73bc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+
+public class DexProgramClass extends DexClass {
+
+ private DexEncodedArray staticValues;
+
+ public DexProgramClass(DexType type,
+ Origin origin,
+ DexAccessFlags accessFlags,
+ DexType superType,
+ DexTypeList interfaces,
+ DexString sourceFile,
+ DexAnnotationSet classAnnotations,
+ DexEncodedField[] staticFields,
+ DexEncodedField[] instanceFields,
+ DexEncodedMethod[] directMethods,
+ DexEncodedMethod[] virtualMethods) {
+ super(sourceFile, interfaces, accessFlags, superType, type, staticFields,
+ instanceFields, directMethods, virtualMethods, classAnnotations, origin);
+ assert classAnnotations != null;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ if (indexedItems.addClass(this)) {
+ type.collectIndexedItems(indexedItems);
+ if (superType != null) {
+ superType.collectIndexedItems(indexedItems);
+ } else {
+ assert type.toDescriptorString().equals("Ljava/lang/Object;");
+ }
+ if (sourceFile != null) {
+ sourceFile.collectIndexedItems(indexedItems);
+ }
+ if (annotations != null) {
+ annotations.collectIndexedItems(indexedItems);
+ }
+ if (interfaces != null) {
+ interfaces.collectIndexedItems(indexedItems);
+ }
+ collectAll(indexedItems, staticFields);
+ collectAll(indexedItems, instanceFields);
+ collectAll(indexedItems, directMethods);
+ collectAll(indexedItems, virtualMethods);
+ }
+ }
+
+ public void addDependencies(MixedSectionCollection collector) {
+ // We only have a class data item if there are methods or fields.
+ if (hasMethodsOrFields()) {
+ collector.add(this);
+ collectAll(collector, directMethods);
+ collectAll(collector, virtualMethods);
+ collectAll(collector, staticFields);
+ collectAll(collector, instanceFields);
+ }
+ if (annotations != null) {
+ annotations.collectMixedSectionItems(collector);
+ }
+ if (interfaces != null) {
+ interfaces.collectMixedSectionItems(collector);
+ }
+ annotations.collectMixedSectionItems(collector);
+ }
+
+ @Override
+ public String toString() {
+ return type.toString();
+ }
+
+ @Override
+ public boolean isProgramClass() {
+ return true;
+ }
+
+ @Override
+ public DexProgramClass asProgramClass() {
+ return this;
+ }
+
+ public boolean hasMethodsOrFields() {
+ int numberOfFields = staticFields().length + instanceFields().length;
+ int numberOfMethods = directMethods().length + virtualMethods().length;
+ return numberOfFields + numberOfMethods > 0;
+ }
+
+ public boolean hasAnnotations() {
+ return !annotations.isEmpty()
+ || hasAnnotations(virtualMethods)
+ || hasAnnotations(directMethods)
+ || hasAnnotations(staticFields)
+ || hasAnnotations(instanceFields);
+ }
+
+ private boolean hasAnnotations(DexEncodedField[] fields) {
+ return fields != null && Arrays.stream(fields).anyMatch(DexEncodedField::hasAnnotation);
+ }
+
+ private boolean hasAnnotations(DexEncodedMethod[] methods) {
+ return methods != null && Arrays.stream(methods).anyMatch(DexEncodedMethod::hasAnnotation);
+ }
+
+ public void setStaticValues(DexEncodedArray staticValues) {
+ this.staticValues = staticValues;
+ }
+
+ public DexEncodedArray getStaticValues() {
+ return staticValues;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java
new file mode 100644
index 0000000..946eb6a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.naming.NamingLens;
+
+public class DexProto extends IndexedDexItem implements PresortedComparable<DexProto> {
+
+ public final DexString shorty;
+ public final DexType returnType;
+ public final DexTypeList parameters;
+
+ DexProto(DexString shorty, DexType returnType, DexTypeList parameters) {
+ this.shorty = shorty;
+ this.returnType = returnType;
+ this.parameters = parameters;
+ }
+
+ public int computeHashCode() {
+ return shorty.hashCode()
+ + returnType.hashCode() * 7
+ + parameters.hashCode() * 31;
+ }
+
+ public boolean computeEquals(Object other) {
+ if (other instanceof DexProto) {
+ DexProto o = (DexProto) other;
+ return shorty.equals(o.shorty)
+ && returnType.equals(o.returnType)
+ && parameters.equals(o.parameters);
+ }
+ return false;
+ }
+
+ public String toString() {
+ return "Proto " + shorty + " " + returnType + " " + parameters;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ if (indexedItems.addProto(this)) {
+ shorty.collectIndexedItems(indexedItems);
+ returnType.collectIndexedItems(indexedItems);
+ parameters.collectIndexedItems(indexedItems);
+ }
+ }
+
+ @Override
+ public int getOffset(ObjectToOffsetMapping mapping) {
+ return mapping.getOffsetFor(this);
+ }
+
+ @Override
+ public int compareTo(DexProto other) {
+ return sortedCompareTo(other.getSortedIndex());
+ }
+
+ @Override
+ public int slowCompareTo(DexProto other) {
+ int result = returnType.slowCompareTo(other.returnType);
+ if (result == 0) {
+ result = parameters.slowCompareTo(other.parameters);
+ }
+ return result;
+ }
+
+ @Override
+ public int slowCompareTo(DexProto other, NamingLens namingLens) {
+ int result = returnType.slowCompareTo(other.returnType, namingLens);
+ if (result == 0) {
+ result = parameters.slowCompareTo(other.parameters, namingLens);
+ }
+ return result;
+ }
+
+ @Override
+ public int layeredCompareTo(DexProto other, NamingLens namingLens) {
+ int result = returnType.compareTo(other.returnType);
+ if (result == 0) {
+ result = parameters.compareTo(other.parameters);
+ }
+ return result;
+ }
+
+ @Override
+ public String toSmaliString() {
+ return toDescriptorString();
+ }
+
+ public String toDescriptorString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("(");
+ for (int i = 0; i < parameters.values.length; i++) {
+ builder.append(parameters.values[i].toDescriptorString());
+ }
+ builder.append(")");
+ builder.append(returnType.toDescriptorString());
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
new file mode 100644
index 0000000..e9b687e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -0,0 +1,201 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.naming.NamingLens;
+import java.io.UTFDataFormatException;
+import java.util.Arrays;
+
+public class DexString extends IndexedDexItem implements PresortedComparable<DexString> {
+
+ public static final DexString[] EMPTY_ARRAY = new DexString[]{};
+
+ public final int size; // size of this string, in UTF-16
+ public final byte[] content;
+
+ DexString(int size, byte[] content) {
+ this.size = size;
+ this.content = content;
+ }
+
+ DexString(String string) {
+ this.size = string.length();
+ this.content = encode(string);
+ }
+
+ public int computeHashCode() {
+ return size * 7 + Arrays.hashCode(content);
+ }
+
+ public boolean computeEquals(Object other) {
+ if (other instanceof DexString) {
+ DexString o = (DexString) other;
+ return size == o.size && Arrays.equals(content, o.content);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ try {
+ return decode();
+ } catch (UTFDataFormatException e) {
+ throw new RuntimeException("Bad format", e);
+ }
+ }
+
+ public int numberOfLeadingSquareBrackets() {
+ int result = 0;
+ while (content.length > result && content[result] == ((byte) '[')) {
+ result++;
+ }
+ return result;
+ }
+
+ // Inspired from /dex/src/main/java/com/android/dex/Mutf8.java
+ private String decode() throws UTFDataFormatException {
+ int s = 0;
+ int p = 0;
+ char[] out = new char[size];
+ while (true) {
+ char a = (char) (content[p++] & 0xff);
+ if (a == 0) {
+ return new String(out, 0, s);
+ }
+ out[s] = a;
+ if (a < '\u0080') {
+ s++;
+ } else if ((a & 0xe0) == 0xc0) {
+ int b = content[p++] & 0xff;
+ if ((b & 0xC0) != 0x80) {
+ throw new UTFDataFormatException("bad second byte");
+ }
+ out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F));
+ } else if ((a & 0xf0) == 0xe0) {
+ int b = content[p++] & 0xff;
+ int c = content[p++] & 0xff;
+ if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
+ throw new UTFDataFormatException("bad second or third byte");
+ }
+ out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
+ } else {
+ throw new UTFDataFormatException("bad byte");
+ }
+ }
+ }
+
+ // Inspired from /dex/src/main/java/com/android/dex/Mutf8.java
+ private static int countBytes(String string) {
+ int result = 0;
+ for (int i = 0; i < string.length(); ++i) {
+ char ch = string.charAt(i);
+ if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
+ ++result;
+ } else if (ch <= 2047) {
+ result += 2;
+ } else {
+ result += 3;
+ }
+ assert result > 0;
+ }
+ // We need an extra byte for the terminating '0'.
+ return result + 1;
+ }
+
+ // Inspired from /dex/src/main/java/com/android/dex/Mutf8.java
+ private static byte[] encode(String string) {
+ byte[] result = new byte[countBytes(string)];
+ int offset = 0;
+ for (int i = 0; i < string.length(); i++) {
+ char ch = string.charAt(i);
+ if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
+ result[offset++] = (byte) ch;
+ } else if (ch <= 2047) {
+ result[offset++] = (byte) (0xc0 | (0x1f & (ch >> 6)));
+ result[offset++] = (byte) (0x80 | (0x3f & ch));
+ } else {
+ result[offset++] = (byte) (0xe0 | (0x0f & (ch >> 12)));
+ result[offset++] = (byte) (0x80 | (0x3f & (ch >> 6)));
+ result[offset++] = (byte) (0x80 | (0x3f & ch));
+ }
+ }
+ result[offset] = 0;
+ return result;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ indexedItems.addString(this);
+ }
+
+ @Override
+ public int getOffset(ObjectToOffsetMapping mapping) {
+ return mapping.getOffsetFor(this);
+ }
+
+ @Override
+ public int compareTo(DexString other) {
+ return sortedCompareTo(other.getSortedIndex());
+ }
+
+ @Override
+ public int slowCompareTo(DexString other) {
+ // Compare the bytes, as comparing UTF-8 encoded strings as strings of unsigned bytes gives
+ // the same result as comparing the corresponding Unicode strings lexicographically by
+ // codepoint. The only complication is the MUTF-8 encoding have the two byte encoding c0 80 of
+ // the null character (U+0000) to allow embedded null characters.
+ // Supplementary characters (unicode code points above U+FFFF) are always represented as
+ // surrogate pairs and are compared using UTF-16 code units as per Java string semantics.
+ int index = 0;
+ while (true) {
+ char b1 = (char) (content[index] & 0xff);
+ char b2 = (char) (other.content[index] & 0xff);
+ int diff = b1 - b2;
+ if (diff != 0) {
+ // Check if either string ends here.
+ if (b1 == 0 || b2 == 0) {
+ return diff;
+ }
+ // If either of the strings have the null character starting here, the null character
+ // sort lowest.
+ if (b1 == 0xc0 && (content[index + 1] & 0xff) == 0x80 ||
+ b2 == 0xc0 && (other.content[index + 1] & 0xff) == 0x80) {
+ return b1 == 0xc0 && (content[index + 1] & 0xff) == 0x80 ? -1 : 1;
+ }
+ return diff;
+ } else if (b1 == 0) {
+ // Reached the end in both strings.
+ return 0;
+ }
+ index++;
+ }
+ }
+
+ @Override
+ public int slowCompareTo(DexString other, NamingLens lens) {
+ // The naming lens cannot affect strings.
+ return slowCompareTo(other);
+ }
+
+ @Override
+ public int layeredCompareTo(DexString other, NamingLens lens) {
+ // Strings have no subparts that are already sorted.
+ return slowCompareTo(other);
+ }
+
+ public String dump() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(toString());
+ builder.append(" [");
+ for (int i = 0; i < content.length; i++) {
+ if (i > 0) {
+ builder.append(" ");
+ }
+ builder.append(Integer.toHexString(content[i] & 0xff));
+ }
+ builder.append("]");
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
new file mode 100644
index 0000000..1b70497
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -0,0 +1,424 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class DexType extends IndexedDexItem implements PresortedComparable<DexType> {
+
+ private final static int ROOT_LEVEL = 0;
+ private final static int UNKNOWN_LEVEL = -1;
+ private final static int INTERFACE_LEVEL = -2;
+
+ // Since most Java types has no sub-types, we can just share an empty immutable set until we need
+ // to add to it.
+ private final static Set<DexType> NO_DIRECT_SUBTYPE = ImmutableSet.of();
+
+ public final DexString descriptor;
+ private String toStringCache = null;
+ private int hierarchyLevel = UNKNOWN_LEVEL;
+
+ private Set<DexType> directSubtypes = NO_DIRECT_SUBTYPE;
+
+ DexType(DexString descriptor) {
+ assert !descriptor.toString().contains(".");
+ this.descriptor = descriptor;
+ }
+
+ public int computeHashCode() {
+ return descriptor.hashCode();
+ }
+
+ public boolean computeEquals(Object other) {
+ if (other instanceof DexType) {
+ return descriptor.equals(((DexType) other).descriptor);
+ }
+ return false;
+ }
+
+ private void ensureDirectSubTypeSet() {
+ if (directSubtypes == NO_DIRECT_SUBTYPE) {
+ directSubtypes = Sets.newIdentityHashSet();
+ }
+ }
+
+ private void setLevel(int level) {
+ if (level == hierarchyLevel) {
+ return;
+ }
+ if (hierarchyLevel == INTERFACE_LEVEL) {
+ assert level == ROOT_LEVEL + 1;
+ } else if (level == INTERFACE_LEVEL) {
+ assert hierarchyLevel == ROOT_LEVEL + 1 || hierarchyLevel == UNKNOWN_LEVEL;
+ hierarchyLevel = INTERFACE_LEVEL;
+ } else {
+ assert hierarchyLevel == UNKNOWN_LEVEL;
+ hierarchyLevel = level;
+ }
+ }
+
+ public void addDirectSubtype(DexType type) {
+ assert hierarchyLevel != UNKNOWN_LEVEL;
+ ensureDirectSubTypeSet();
+ directSubtypes.add(type);
+ type.setLevel(hierarchyLevel + 1);
+ }
+
+ public void tagAsSubtypeRoot() {
+ setLevel(ROOT_LEVEL);
+ }
+
+ public boolean isInterface() {
+ assert isClassType() && hierarchyLevel != UNKNOWN_LEVEL;
+ return hierarchyLevel == INTERFACE_LEVEL;
+ }
+
+ public void addInterfaceSubtype(DexType type) {
+ // Interfaces all inherit from java.lang.Object. However, we assign a special level to
+ // identify them later on.
+ setLevel(INTERFACE_LEVEL);
+ ensureDirectSubTypeSet();
+ directSubtypes.add(type);
+ }
+
+ static void clearSubtypeInformation(DexType type) {
+ type.hierarchyLevel = UNKNOWN_LEVEL;
+ type.directSubtypes = NO_DIRECT_SUBTYPE;
+ }
+
+ public boolean isSubtypeOf(DexType other, AppInfo appInfo) {
+ if (this == other) {
+ return true;
+ }
+ // Treat the object class special as it is always the supertype, even in the case of broken
+ // subtype chains.
+ if (this == appInfo.dexItemFactory.objectType) {
+ return false;
+ }
+ if (other == appInfo.dexItemFactory.objectType) {
+ return true;
+ }
+ if (this.hierarchyLevel == INTERFACE_LEVEL) {
+ return isInterfaceSubtypeOf(this, other, appInfo);
+ }
+ if (other.hierarchyLevel == INTERFACE_LEVEL) {
+ return other.directSubtypes.stream().anyMatch(subtype -> this.isSubtypeOf(subtype,
+ appInfo));
+ }
+ return isSubtypeOfClass(other, appInfo);
+ }
+
+ private boolean isInterfaceSubtypeOf(DexType candidate, DexType other, AppInfo appInfo) {
+ if (candidate == other || other == appInfo.dexItemFactory.objectType) {
+ return true;
+ }
+ DexClass candidateHolder = appInfo.definitionFor(candidate);
+ if (candidateHolder == null) {
+ return false;
+ }
+ for (DexType iface : candidateHolder.interfaces.values) {
+ assert iface.hierarchyLevel == INTERFACE_LEVEL;
+ if (isInterfaceSubtypeOf(iface, other, appInfo)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isSubtypeOfClass(DexType other, AppInfo appInfo) {
+ DexType self = this;
+ while (other.hierarchyLevel < self.hierarchyLevel) {
+ DexClass holder = appInfo.definitionFor(self);
+ assert holder != null && !holder.isInterface();
+ self = holder.superType;
+ }
+ return self == other;
+ }
+
+ /**
+ * Apply the given function to all classes that directly extend this class.
+ *
+ * If this class is an interface, then this method will visit all sub-interfaces. This deviates
+ * from the dex-file encoding, where subinterfaces "implement" their super interfaces. However,
+ * it is consistent with the source language.
+ */
+ public void forAllExtendsSubtypes(Consumer<DexType> f) {
+ assert hierarchyLevel != UNKNOWN_LEVEL;
+ if (hierarchyLevel == INTERFACE_LEVEL) {
+ for (DexType subtype : directSubtypes) {
+ // Other interfaces that extend this interface.
+ if (subtype.hierarchyLevel == INTERFACE_LEVEL) {
+ f.accept(subtype);
+ }
+ }
+ } else if (hierarchyLevel == ROOT_LEVEL) {
+ // This is the object type. Filter out interfaces
+ for (DexType subtype : directSubtypes) {
+ // Other interfaces that extend this interface.
+ if (subtype.hierarchyLevel != INTERFACE_LEVEL) {
+ f.accept(subtype);
+ }
+ }
+ } else {
+ directSubtypes.forEach(f);
+ }
+ }
+
+ /**
+ * Apply the given function to all classes that directly implement this interface.
+ *
+ * The implementation does not consider how the hierarchy is encoded in the dex file, where
+ * interfaces "implement" their super interfaces. Instead it takes the view of the source
+ * language, where interfaces "extend" their superinterface.
+ */
+ public void forAllImplementsSubtypes(Consumer<DexType> f) {
+ if (hierarchyLevel != INTERFACE_LEVEL) {
+ return;
+ }
+ for (DexType subtype : directSubtypes) {
+ // Filter out other interfaces.
+ if (subtype.hierarchyLevel != INTERFACE_LEVEL) {
+ f.accept(subtype);
+ }
+ }
+ }
+
+ public static void forAllInterfaces(DexItemFactory factory, Consumer<DexType> f) {
+ DexType object = factory.objectType;
+ assert object.hierarchyLevel == ROOT_LEVEL;
+ for (DexType subtype : object.directSubtypes) {
+ if (subtype.isInterface()) {
+ f.accept(subtype);
+ }
+ }
+ }
+
+ public boolean isSamePackage(DexType other) {
+ return getPackageDescriptor().equals(other.getPackageDescriptor());
+ }
+
+ public String toDescriptorString() {
+ return descriptor.toString();
+ }
+
+ public String toSourceString() {
+ if (toStringCache == null) {
+ // TODO(ager): Pass in a ProguardMapReader to map names back to original names.
+ if (this == DexItemFactory.catchAllType) {
+ toStringCache = "CATCH_ALL";
+ } else {
+ toStringCache = DescriptorUtils.descriptorToJavaType(toDescriptorString());
+ }
+ }
+ return toStringCache;
+ }
+
+ public char toShorty() {
+ char c = (char) descriptor.content[0];
+ return c == '[' ? 'L' : c;
+ }
+
+ @Override
+ public String toSmaliString() {
+ return toDescriptorString();
+ }
+
+ @Override
+ public String toString() {
+ return toSourceString();
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection collection) {
+ if (collection.addType(this)) {
+ collection.getRenamedDescriptor(this).collectIndexedItems(collection);
+ }
+ }
+
+ @Override
+ public void flushCachedValues() {
+ super.flushCachedValues();
+ toStringCache = null;
+ }
+
+ @Override
+ public int getOffset(ObjectToOffsetMapping mapping) {
+ return mapping.getOffsetFor(this);
+ }
+
+ @Override
+ public int compareTo(DexType other) {
+ return sortedCompareTo(other.getSortedIndex());
+ }
+
+ @Override
+ public int slowCompareTo(DexType other) {
+ return descriptor.slowCompareTo(other.descriptor);
+ }
+
+ @Override
+ public int slowCompareTo(DexType other, NamingLens namingLens) {
+ DexString thisDescriptor = namingLens.lookupDescriptor(this);
+ DexString otherDescriptor = namingLens.lookupDescriptor(other);
+ return thisDescriptor.slowCompareTo(otherDescriptor);
+ }
+
+ @Override
+ public int layeredCompareTo(DexType other, NamingLens namingLens) {
+ DexString thisDescriptor = namingLens.lookupDescriptor(this);
+ DexString otherDescriptor = namingLens.lookupDescriptor(other);
+ return thisDescriptor.compareTo(otherDescriptor);
+ }
+
+ public boolean isPrimitiveType() {
+ char firstChar = (char) descriptor.content[0];
+ return firstChar != 'L' && firstChar != '[';
+ }
+
+ public boolean isVoidType() {
+ return (char) descriptor.content[0] == 'V';
+ }
+
+ public boolean isArrayType() {
+ char firstChar = (char) descriptor.content[0];
+ return firstChar == '[';
+ }
+
+ public boolean isClassType() {
+ char firstChar = (char) descriptor.content[0];
+ return firstChar == 'L';
+ }
+
+ public boolean isPrimitiveArrayType() {
+ if (!isArrayType()) {
+ return false;
+ }
+ switch (descriptor.content[1]) {
+ case 'Z': // boolean
+ case 'B': // byte
+ case 'S': // short
+ case 'C': // char
+ case 'I': // int
+ case 'F': // float
+ case 'J': // long
+ case 'D': // double
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public int elementSizeForPrimitiveArrayType() {
+ assert isPrimitiveArrayType();
+ switch (descriptor.content[1]) {
+ case 'Z': // boolean
+ case 'B': // byte
+ return 1;
+ case 'S': // short
+ case 'C': // char
+ return 2;
+ case 'I': // int
+ case 'F': // float
+ return 4;
+ case 'J': // long
+ case 'D': // double
+ return 8;
+ default:
+ throw new Unreachable("Not array of primitives '" + descriptor + "'");
+ }
+ }
+
+ public DexType toBaseType(DexItemFactory dexItemFactory) {
+ int leadingSquareBrackets = 0;
+ while (descriptor.content[leadingSquareBrackets] == '[') {
+ leadingSquareBrackets++;
+ }
+ if (leadingSquareBrackets == 0) {
+ return this;
+ }
+ DexString newDesc = dexItemFactory.createString(descriptor.size - leadingSquareBrackets,
+ Arrays.copyOfRange(descriptor.content, leadingSquareBrackets, descriptor.content.length));
+ return dexItemFactory.createType(newDesc);
+ }
+
+ public DexType toArrayElementType(DexItemFactory dexItemFactory) {
+ assert this.isArrayType();
+ DexString newDesc = dexItemFactory.createString(descriptor.size - 1,
+ Arrays.copyOfRange(descriptor.content, 1, descriptor.content.length));
+ return dexItemFactory.createType(newDesc);
+ }
+
+ static boolean validateLevelsAreCorrect(Function<DexType, DexClass> definitions,
+ DexItemFactory dexItemFactory) {
+ Set<DexType> seenTypes = Sets.newIdentityHashSet();
+ Deque<DexType> worklist = new ArrayDeque<>();
+ DexType objectType = dexItemFactory.objectType;
+ worklist.add(objectType);
+ while (!worklist.isEmpty()) {
+ DexType next = worklist.pop();
+ DexClass nextHolder = definitions.apply(next);
+ DexType superType;
+ if (nextHolder == null) {
+ // We might lack the definition of Object, so guard against that.
+ superType = next == dexItemFactory.objectType ? null : dexItemFactory.objectType;
+ } else {
+ superType = nextHolder.superType;
+ }
+ assert !seenTypes.contains(next);
+ seenTypes.add(next);
+ if (superType == null) {
+ assert next.hierarchyLevel == ROOT_LEVEL;
+ } else {
+ assert superType.hierarchyLevel == next.hierarchyLevel - 1
+ || superType.hierarchyLevel == ROOT_LEVEL && next.hierarchyLevel == INTERFACE_LEVEL;
+ assert superType.directSubtypes.contains(next);
+ }
+ if (next.hierarchyLevel != INTERFACE_LEVEL) {
+ // Only traverse the class hierarchy subtypes, not interfaces.
+ worklist.addAll(next.directSubtypes);
+ } else if (nextHolder != null) {
+ // Test that the interfaces of this class are interfaces and have this class as subtype.
+ for (DexType iface : nextHolder.interfaces.values) {
+ assert iface.directSubtypes.contains(next);
+ assert iface.hierarchyLevel == INTERFACE_LEVEL;
+ }
+ }
+ }
+ return true;
+ }
+
+ private String getPackageOrName(boolean packagePart) {
+ assert isClassType();
+ String descriptor = toDescriptorString();
+ int lastSeparator = descriptor.lastIndexOf('/');
+ if (lastSeparator == -1) {
+ return packagePart ? "" : descriptor.substring(1, descriptor.length() - 1);
+ } else {
+ return packagePart ? descriptor.substring(1, lastSeparator)
+ : descriptor.substring(lastSeparator + 1, descriptor.length() - 1);
+ }
+ }
+
+ public String getPackageDescriptor() {
+ return getPackageOrName(true);
+ }
+
+ public String getName() {
+ if (isPrimitiveType()) {
+ return toSourceString();
+ }
+ return getPackageOrName(false);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
new file mode 100644
index 0000000..39226d8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.naming.NamingLens;
+import java.util.Arrays;
+
+public class DexTypeList extends DexItem implements Comparable<DexTypeList> {
+
+ private static final DexTypeList theEmptyTypeList = new DexTypeList();
+
+ public final DexType[] values;
+
+ public static DexTypeList empty() {
+ return theEmptyTypeList;
+ }
+
+ private DexTypeList() {
+ this.values = new DexType[0];
+ }
+
+ public DexTypeList(DexType[] values) {
+ assert values != null && values.length > 0;
+ this.values = values;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(values);
+ }
+
+ @Override
+ void collectIndexedItems(IndexedItemCollection indexedItems) {
+ for (DexType type : values) {
+ type.collectIndexedItems(indexedItems);
+ }
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ mixedItems.add(this);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ return (other instanceof DexTypeList)
+ && Arrays.equals(values, ((DexTypeList) other).values);
+ }
+
+ public boolean isEmpty() {
+ return values.length == 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ for (DexType type : values) {
+ builder.append(' ').append(type);
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public int compareTo(DexTypeList other) {
+ for (int i = 0; i <= Math.min(values.length, other.values.length); i++) {
+ if (i == values.length) {
+ return i == other.values.length ? 0 : -1;
+ } else if (i == other.values.length) {
+ return 1;
+ } else {
+ int result = values[i].compareTo(other.values[i]);
+ if (result != 0) {
+ return result;
+ }
+ }
+ }
+ throw new Unreachable();
+ }
+
+ public int slowCompareTo(DexTypeList other) {
+ for (int i = 0; i <= Math.min(values.length, other.values.length); i++) {
+ if (i == values.length) {
+ return i == other.values.length ? 0 : -1;
+ } else if (i == other.values.length) {
+ return 1;
+ } else {
+ int result = values[i].slowCompareTo(other.values[i]);
+ if (result != 0) {
+ return result;
+ }
+ }
+ }
+ throw new Unreachable();
+ }
+
+ public int slowCompareTo(DexTypeList other, NamingLens namingLens) {
+ for (int i = 0; i <= Math.min(values.length, other.values.length); i++) {
+ if (i == values.length) {
+ return i == other.values.length ? 0 : -1;
+ } else if (i == other.values.length) {
+ return 1;
+ } else {
+ int result = values[i].slowCompareTo(other.values[i], namingLens);
+ if (result != 0) {
+ return result;
+ }
+ }
+ }
+ throw new Unreachable();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
new file mode 100644
index 0000000..5430d6e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -0,0 +1,783 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.DexOutputBuffer;
+import com.android.tools.r8.dex.FileWriter;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.EncodedValueUtils;
+import java.util.Arrays;
+
+public abstract class DexValue extends DexItem {
+
+ public static final byte VALUE_BYTE = 0x00;
+ public static final byte VALUE_SHORT = 0x02;
+ public static final byte VALUE_CHAR = 0x03;
+ public static final byte VALUE_INT = 0x04;
+ public static final byte VALUE_LONG = 0x06;
+ public static final byte VALUE_FLOAT = 0x10;
+ public static final byte VALUE_DOUBLE = 0x11;
+ public static final byte VALUE_METHOD_TYPE = 0x15;
+ public static final byte VALUE_METHOD_HANDLE = 0x16;
+ public static final byte VALUE_STRING = 0x17;
+ public static final byte VALUE_TYPE = 0x18;
+ public static final byte VALUE_FIELD = 0x19;
+ public static final byte VALUE_METHOD = 0x1a;
+ public static final byte VALUE_ENUM = 0x1b;
+ public static final byte VALUE_ARRAY = 0x1c;
+ public static final byte VALUE_ANNOTATION = 0x1d;
+ public static final byte VALUE_NULL = 0x1e;
+ public static final byte VALUE_BOOLEAN = 0x1f;
+
+ public static final DexValue NULL = new DexValueNull();
+
+ private static void writeHeader(byte type, int arg, DexOutputBuffer dest) {
+ dest.putByte((byte) ((arg << 5) | type));
+ }
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ // Should never be visited.
+ assert false;
+ }
+
+ public abstract void sort();
+
+ public abstract void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping);
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object other);
+
+ @Override
+ public abstract String toString();
+
+ public static DexValue defaultForType(DexType type, DexItemFactory factory) {
+ if (type == factory.booleanType) {
+ return DexValueBoolean.DEFAULT;
+ }
+ if (type == factory.byteType) {
+ return DexValueByte.DEFAULT;
+ }
+ if (type == factory.charType) {
+ return DexValueChar.DEFAULT;
+ }
+ if (type == factory.shortType) {
+ return DexValueShort.DEFAULT;
+ }
+ if (type == factory.intType) {
+ return DexValueInt.DEFAULT;
+ }
+ if (type == factory.longType) {
+ return DexValueLong.DEFAULT;
+ }
+ if (type == factory.floatType) {
+ return DexValueFloat.DEFAULT;
+ }
+ if (type == factory.doubleType) {
+ return DexValueDouble.DEFAULT;
+ }
+ if (type.isArrayType() || type.isClassType()) {
+ return DexValue.NULL;
+ }
+ throw new Unreachable("No default value for unexpected type " + type);
+ }
+
+ // Returns a const instruction for the non default value.
+ public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+ return null;
+ }
+
+ public boolean isDefault(DexType type, DexItemFactory factory) {
+ return this == defaultForType(type, factory);
+ }
+
+ static private abstract class SimpleDexValue extends DexValue {
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ // Intentionally left empty
+ }
+
+ @Override
+ public void sort() {
+ // Intentionally empty
+ }
+
+ protected static void writeIntegerTo(byte type, long value, int expected,
+ DexOutputBuffer dest) {
+ // Leave space for header.
+ dest.forward(1);
+ int length = dest.putSignedEncodedValue(value, expected);
+ dest.rewind(length + 1);
+ writeHeader(type, length - 1, dest);
+ dest.forward(length);
+ }
+
+ }
+
+ static public class DexValueByte extends SimpleDexValue {
+
+ public static final DexValueByte DEFAULT = new DexValueByte((byte) 0);
+
+ final byte value;
+
+ private DexValueByte(byte value) {
+ this.value = value;
+ }
+
+ public static DexValueByte create(byte value) {
+ return value == DEFAULT.value ? DEFAULT : new DexValueByte(value);
+ }
+
+ @Override
+ public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+ writeHeader(VALUE_BYTE, 0, dest);
+ dest.putSignedEncodedValue(value, 1);
+ }
+
+ @Override
+ public int hashCode() {
+ return value * 3;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ return other instanceof DexValueByte && value == ((DexValueByte) other).value;
+ }
+
+ @Override
+ public String toString() {
+ return "Byte " + value;
+ }
+
+ @Override
+ public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+ return (this == DEFAULT && hasClassInitializer)
+ ? null
+ : new ConstNumber(ConstType.INT, dest, value);
+ }
+ }
+
+ static public class DexValueShort extends SimpleDexValue {
+
+ public static final DexValueShort DEFAULT = new DexValueShort((short) 0);
+ final short value;
+
+ private DexValueShort(short value) {
+ this.value = value;
+ }
+
+ public static DexValueShort create(short value) {
+ return value == DEFAULT.value ? DEFAULT : new DexValueShort(value);
+ }
+
+ @Override
+ public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+ writeIntegerTo(VALUE_SHORT, value, Short.BYTES, dest);
+ }
+
+ @Override
+ public int hashCode() {
+ return value * 7;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ return other instanceof DexValueShort && value == ((DexValueShort) other).value;
+ }
+
+ @Override
+ public String toString() {
+ return "Short " + value;
+ }
+
+ @Override
+ public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+ return (this == DEFAULT && hasClassInitializer)
+ ? null
+ : new ConstNumber(ConstType.INT, dest, value);
+ }
+ }
+
+ static public class DexValueChar extends SimpleDexValue {
+
+ public static final DexValueChar DEFAULT = new DexValueChar((char) 0);
+ final char value;
+
+ private DexValueChar(char value) {
+ this.value = value;
+ }
+
+ public static DexValueChar create(char value) {
+ return value == DEFAULT.value ? DEFAULT : new DexValueChar(value);
+ }
+
+ @Override
+ public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+ dest.forward(1);
+ int length = dest.putUnsignedEncodedValue(value, 2);
+ dest.rewind(length + 1);
+ writeHeader(VALUE_CHAR, length - 1, dest);
+ dest.forward(length);
+ }
+
+ @Override
+ public int hashCode() {
+ return value * 5;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ return other instanceof DexValueChar && value == ((DexValueChar) other).value;
+ }
+
+ @Override
+ public String toString() {
+ return "Char " + value;
+ }
+
+ @Override
+ public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+ return (this == DEFAULT && hasClassInitializer)
+ ? null
+ : new ConstNumber(ConstType.INT, dest, value);
+ }
+ }
+
+ static public class DexValueInt extends SimpleDexValue {
+
+ public static final DexValueInt DEFAULT = new DexValueInt(0);
+ public final int value;
+
+ private DexValueInt(int value) {
+ this.value = value;
+ }
+
+ public static DexValueInt create(int value) {
+ return value == DEFAULT.value ? DEFAULT : new DexValueInt(value);
+ }
+
+ @Override
+ public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+ writeIntegerTo(VALUE_INT, value, Integer.BYTES, dest);
+ }
+
+ @Override
+ public int hashCode() {
+ return value * 11;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ return other instanceof DexValueInt && value == ((DexValueInt) other).value;
+ }
+
+ @Override
+ public String toString() {
+ return "Int " + value;
+ }
+
+ @Override
+ public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+ return (this == DEFAULT && hasClassInitializer)
+ ? null
+ : new ConstNumber(ConstType.INT, dest, value);
+ }
+ }
+
+ static public class DexValueLong extends SimpleDexValue {
+
+ public static final DexValueLong DEFAULT = new DexValueLong(0);
+ final long value;
+
+ private DexValueLong(long value) {
+ this.value = value;
+ }
+
+ public static DexValueLong create(long value) {
+ return value == DEFAULT.value ? DEFAULT : new DexValueLong(value);
+ }
+
+ @Override
+ public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+ writeIntegerTo(VALUE_LONG, value, Long.BYTES, dest);
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) value * 13;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ return other instanceof DexValueLong && value == ((DexValueLong) other).value;
+ }
+
+ @Override
+ public String toString() {
+ return "Long " + value;
+ }
+
+ @Override
+ public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+ return (this == DEFAULT && hasClassInitializer)
+ ? null
+ : new ConstNumber(ConstType.LONG, dest, value);
+ }
+ }
+
+ static public class DexValueFloat extends SimpleDexValue {
+
+ public static final DexValueFloat DEFAULT = new DexValueFloat(0);
+ final float value;
+
+ private DexValueFloat(float value) {
+ this.value = value;
+ }
+
+ public static DexValueFloat create(float value) {
+ return Float.compare(value, DEFAULT.value) == 0 ? DEFAULT : new DexValueFloat(value);
+ }
+
+ @Override
+ public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+ dest.forward(1);
+ int length = EncodedValueUtils.putFloat(dest, value);
+ dest.rewind(length + 1);
+ writeHeader(VALUE_FLOAT, length - 1, dest);
+ dest.forward(length);
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) value * 19;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ return (other instanceof DexValueFloat) &&
+ (Float.compare(value, ((DexValueFloat) other).value) == 0);
+ }
+
+ @Override
+ public String toString() {
+ return "Float " + value;
+ }
+
+ }
+
+ static public class DexValueDouble extends SimpleDexValue {
+
+ public static final DexValueDouble DEFAULT = new DexValueDouble(0);
+
+ final double value;
+
+ private DexValueDouble(double value) {
+ this.value = value;
+ }
+
+ public static DexValueDouble create(double value) {
+ return Double.compare(value, DEFAULT.value) == 0 ? DEFAULT : new DexValueDouble(value);
+ }
+
+ @Override
+ public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+ dest.forward(1);
+ int length = EncodedValueUtils.putDouble(dest, value);
+ dest.rewind(length + 1);
+ writeHeader(VALUE_DOUBLE, length - 1, dest);
+ dest.forward(length);
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) value * 29;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ return (other instanceof DexValueDouble) &&
+ (Double.compare(value, ((DexValueDouble) other).value) == 0);
+ }
+
+ @Override
+ public String toString() {
+ return "Double " + value;
+ }
+ }
+
+ static private abstract class NestedDexValue<T extends IndexedDexItem> extends DexValue {
+
+ public final T value;
+
+ private NestedDexValue(T value) {
+ this.value = value;
+ }
+
+ protected abstract byte getValueKind();
+
+ @Override
+ public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+ int offset = value.getOffset(mapping);
+ dest.forward(1);
+ int length = dest.putUnsignedEncodedValue(offset, 4);
+ dest.rewind(length + 1);
+ writeHeader(getValueKind(), length - 1, dest);
+ dest.forward(length);
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ value.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ public void sort() {
+ // Intentionally empty.
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode() * 7 + getValueKind();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (other instanceof NestedDexValue) {
+ NestedDexValue<?> that = (NestedDexValue<?>) other;
+ return that.getValueKind() == getValueKind() && that.value.equals(value);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "Item " + getValueKind() + " " + value;
+ }
+ }
+
+ static public class DexValueString extends NestedDexValue<DexString> {
+
+ public DexValueString(DexString value) {
+ super(value);
+ }
+
+ @Override
+ protected byte getValueKind() {
+ return VALUE_STRING;
+ }
+ }
+
+ static public class DexValueType extends NestedDexValue<DexType> {
+
+ public DexValueType(DexType value) {
+ super(value);
+ }
+
+ @Override
+ protected byte getValueKind() {
+ return VALUE_TYPE;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ value.collectIndexedItems(indexedItems);
+ }
+ }
+
+ static public class DexValueField extends NestedDexValue<DexField> {
+
+ public DexValueField(DexField value) {
+ super(value);
+ }
+
+ @Override
+ protected byte getValueKind() {
+ return VALUE_FIELD;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ value.collectIndexedItems(indexedItems);
+ }
+ }
+
+ static public class DexValueMethod extends NestedDexValue<DexMethod> {
+
+ public DexValueMethod(DexMethod value) {
+ super(value);
+ }
+
+ @Override
+ protected byte getValueKind() {
+ return VALUE_METHOD;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ value.collectIndexedItems(indexedItems);
+ }
+ }
+
+ static public class DexValueEnum extends NestedDexValue<DexField> {
+
+ public DexValueEnum(DexField value) {
+ super(value);
+ }
+
+ @Override
+ protected byte getValueKind() {
+ return VALUE_ENUM;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ value.collectIndexedItems(indexedItems);
+ }
+ }
+
+ static public class DexValueMethodType extends NestedDexValue<DexProto> {
+
+ public DexValueMethodType(DexProto value) {
+ super(value);
+ }
+
+ @Override
+ protected byte getValueKind() {
+ return VALUE_METHOD_TYPE;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ value.collectIndexedItems(indexedItems);
+ }
+ }
+
+ static public class DexValueArray extends DexValue {
+
+ final DexValue[] values;
+
+ public DexValueArray(DexValue[] values) {
+ this.values = values;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ collectAll(indexedItems, values);
+ }
+
+ @Override
+ public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+ writeHeader(VALUE_ARRAY, 0, dest);
+ dest.putUleb128(values.length);
+ for (DexValue value : values) {
+ value.writeTo(dest, mapping);
+ }
+ }
+
+ @Override
+ public void sort() {
+ for (DexValue value : values) {
+ value.sort();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(values);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (other instanceof DexValueArray) {
+ DexValueArray that = (DexValueArray) other;
+ return Arrays.equals(that.values, values);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "Array " + Arrays.toString(values);
+ }
+ }
+
+ static public class DexValueAnnotation extends DexValue {
+
+ public final DexEncodedAnnotation value;
+
+ public DexValueAnnotation(DexEncodedAnnotation value) {
+ this.value = value;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ value.collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+ writeHeader(VALUE_ANNOTATION, 0, dest);
+ FileWriter.writeEncodedAnnotation(value, dest, mapping);
+ }
+
+ @Override
+ public void sort() {
+ value.sort();
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode() * 7;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (other instanceof DexValueAnnotation) {
+ DexValueAnnotation that = (DexValueAnnotation) other;
+ return that.value.equals(value);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "Annotation " + value;
+ }
+ }
+
+ static public class DexValueNull extends SimpleDexValue {
+
+ // See DexValue.NULL
+ private DexValueNull() {
+ }
+
+ @Override
+ public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+ writeHeader(VALUE_NULL, 0, dest);
+ }
+
+ @Override
+ public int hashCode() {
+ return 42;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ return (other instanceof DexValueNull);
+ }
+
+ @Override
+ public String toString() {
+ return "Null";
+ }
+ }
+
+ static public class DexValueBoolean extends SimpleDexValue {
+
+ private static final DexValueBoolean TRUE = new DexValueBoolean(true);
+ private static final DexValueBoolean FALSE = new DexValueBoolean(false);
+ // Use a separate instance for the default value to distinguish it from an explicit false value.
+ private static final DexValueBoolean DEFAULT = new DexValueBoolean(false);
+
+ final boolean value;
+
+ private DexValueBoolean(boolean value) {
+ this.value = value;
+ }
+
+ public static DexValueBoolean create(boolean value) {
+ return value ? TRUE : FALSE;
+ }
+
+ @Override
+ public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+ writeHeader(VALUE_BOOLEAN, value ? 1 : 0, dest);
+ }
+
+ @Override
+ public int hashCode() {
+ return value ? 1234 : 4321;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ return (other instanceof DexValueBoolean) && ((DexValueBoolean) other).value == value;
+ }
+
+ @Override
+ public String toString() {
+ return value ? "True" : "False";
+ }
+
+ @Override
+ public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+ return (this == DEFAULT && hasClassInitializer)
+ ? null
+ : new ConstNumber(ConstType.INT, dest, value ? 1 : 0);
+ }
+ }
+
+ static public class DexValueMethodHandle extends NestedDexValue<DexMethodHandle> {
+
+ public DexValueMethodHandle(DexMethodHandle value) {
+ super(value);
+ }
+
+ @Override
+ protected byte getValueKind() {
+ return VALUE_METHOD_HANDLE;
+ }
+
+ @Override
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ value.collectIndexedItems(indexedItems);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
new file mode 100644
index 0000000..42f7c6e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -0,0 +1,117 @@
+// 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.graph;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public abstract class GraphLense {
+
+ public static class Builder {
+
+ private final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
+ private final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
+ private final Map<DexField, DexField> fieldMap = new IdentityHashMap<>();
+
+ public void map(DexType from, DexType to) {
+ typeMap.put(from, to);
+ }
+
+ public void map(DexMethod from, DexMethod to) {
+ methodMap.put(from, to);
+ }
+
+ public void map(DexField from, DexField to) {
+ fieldMap.put(from, to);
+ }
+
+ public GraphLense build() {
+ return build(new IdentityGraphLense());
+ }
+
+ public GraphLense build(GraphLense previousLense) {
+ return new NestedGraphLense(typeMap, methodMap, fieldMap, previousLense);
+ }
+
+ }
+
+ public abstract DexType lookupType(DexType type, DexEncodedMethod context);
+
+ public abstract DexMethod lookupMethod(DexMethod method, DexEncodedMethod context);
+
+ public abstract DexField lookupField(DexField field, DexEncodedMethod context);
+
+ public abstract boolean isContextFree();
+
+ public static GraphLense getIdentityLense() {
+ return new IdentityGraphLense();
+ }
+
+ public final boolean isIdentityLense() {
+ return this instanceof IdentityGraphLense;
+ }
+
+ private static class IdentityGraphLense extends GraphLense {
+
+ @Override
+ public DexType lookupType(DexType type, DexEncodedMethod context) {
+ return type;
+ }
+
+ @Override
+ public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context) {
+ return method;
+ }
+
+ @Override
+ public DexField lookupField(DexField field, DexEncodedMethod context) {
+ return field;
+ }
+
+ @Override
+ public boolean isContextFree() {
+ return true;
+ }
+ }
+
+ private static class NestedGraphLense extends GraphLense {
+
+ private final GraphLense previousLense;
+
+ private final Map<DexType, DexType> typeMap;
+ private final Map<DexMethod, DexMethod> methodMap;
+ private final Map<DexField, DexField> fieldMap;
+
+ private NestedGraphLense(Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap,
+ Map<DexField, DexField> fieldMap, GraphLense previousLense) {
+ this.typeMap = typeMap;
+ this.methodMap = methodMap;
+ this.fieldMap = fieldMap;
+ this.previousLense = previousLense;
+ }
+
+ @Override
+ public DexType lookupType(DexType type, DexEncodedMethod context) {
+ DexType previous = previousLense.lookupType(type, context);
+ return typeMap.getOrDefault(previous, previous);
+ }
+
+ @Override
+ public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context) {
+ DexMethod previous = previousLense.lookupMethod(method, context);
+ return methodMap.getOrDefault(previous, previous);
+ }
+
+ @Override
+ public DexField lookupField(DexField field, DexEncodedMethod context) {
+ DexField previous = previousLense.lookupField(field, context);
+ return fieldMap.getOrDefault(previous, previous);
+ }
+
+ @Override
+ public boolean isContextFree() {
+ return previousLense.isContextFree();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java b/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
new file mode 100644
index 0000000..6c7f3b3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
@@ -0,0 +1,159 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+
+/**
+ * Subset of dex items that are referenced by some table index.
+ */
+public abstract class IndexedDexItem extends CanonicalizedDexItem implements Presorted {
+
+ private static final int SORTED_INDEX_UNKNOWN = -1;
+ private int sortedIndex = SORTED_INDEX_UNKNOWN; // assigned globally after reading.
+ /**
+ * Contains the indexes assigned to this item for the various virtual output files.
+ *
+ * <p>One DexItem might be assigned to multiple virtual files.
+ *
+ * <p>For a certain virtual file this DexItem has the value:
+ * <ul>
+ * <li>{@link #UNASSOCIATED_VALUE}, when not associated with the virtual file.
+ * <li>{@link #ASSOCIATED_VALUE}, when associated with the virtual file but no index allocated.
+ * <li>A zero or greater value when this item has been associated by the virtual file
+ * and the value denotes the assigned index.
+ * </ul>
+ * <p> Note that, in case of multiple files, for a specific IndexedDexItem, we may not have
+ * as many entries in the index as there are files (we only expand when we need to). If we lookup
+ * the value of an entry that is out of bounds it is equivalent to {@link #UNASSOCIATED_VALUE}
+ *
+ * <p>This field is initialized on first write in {@link #updateVirtualFileData(int, int)}}. It
+ * is assumed that multiple files are processed concurrently and thus the allocation of the
+ * array is synchronized. However, for any a given file id, sequential access is assumed.
+ */
+ private int[] virtualFileIndexes;
+
+ public abstract void collectIndexedItems(IndexedItemCollection indexedItems);
+
+ @Override
+ void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ // Should never be visited.
+ assert false;
+ }
+
+ public abstract int getOffset(ObjectToOffsetMapping mapping);
+
+ /**
+ * Constants used inside virtualFileIndexes.
+ */
+ public static final int UNASSOCIATED_VALUE = -2;
+ public static final int ASSOCIATED_VALUE = -1;
+ public static final int MIN_VALID_VALUE = 0;
+
+ /**
+ * Returns whether this item is assigned to the given file id.
+ */
+ public boolean hasVirtualFileData(int virtualFileId) {
+ return getVirtualFileIndex(virtualFileId) != UNASSOCIATED_VALUE;
+ }
+
+ /**
+ * Assigns this item to the given file id if it has not been assigned previously.
+ *
+ * <p>This method returns 'true' if the item was newly assigned, i.e., it was not previously
+ * assigned to the file id.
+ */
+ public boolean assignToVirtualFile(int virtualFileId) {
+ // Fast lock-free check whether already assigned.
+ if (hasVirtualFileData(virtualFileId)) {
+ return false;
+ }
+ return updateVirtualFileData(virtualFileId);
+ }
+
+ /**
+ * Assigns this item to the given file id.
+ *
+ * <p>As a side effect, the {@link #virtualFileIndexes} field might be initialized or expanded.
+ * Hence this method is synchronized. Note that the field is queried without synchronization.
+ * Therefor it has to remain in a valid state at all times and must transition atomically from
+ * null to an initialized allocated value.
+ */
+ private synchronized boolean updateVirtualFileData(int virtualFileId) {
+ if (virtualFileIndexes == null) {
+ int[] fileIndices = new int[virtualFileId + 1];
+ Arrays.fill(fileIndices, UNASSOCIATED_VALUE);
+ // This has to be an atomic transition from null to an initialized array.
+ virtualFileIndexes = fileIndices;
+ }
+ // We increased the number of files, increase the index size.
+ if (virtualFileId >= virtualFileIndexes.length) {
+ int oldLength = virtualFileIndexes.length;
+ int[] fileIndices = Arrays.copyOf(virtualFileIndexes, virtualFileId + 1);
+ Arrays.fill(fileIndices, oldLength, virtualFileId + 1, UNASSOCIATED_VALUE);
+ virtualFileIndexes = fileIndices;
+ }
+ assert virtualFileId < virtualFileIndexes.length;
+ boolean wasAdded = virtualFileIndexes[virtualFileId] == UNASSOCIATED_VALUE;
+ virtualFileIndexes[virtualFileId] = ASSOCIATED_VALUE;
+ return wasAdded;
+ }
+
+ /**
+ * Assigns an actual index for this item in the given file.
+ *
+ * <p>May only be used after this item has been assigned to the file using {@link
+ * #assignToVirtualFile(int, int)}.
+ */
+ public void assignVirtualFileIndex(int virtualFileId, int index) {
+ assert virtualFileIndexes != null;
+ assert virtualFileIndexes[virtualFileId] < MIN_VALID_VALUE;
+ virtualFileIndexes[virtualFileId] = index;
+ }
+
+ /**
+ * Returns the index associated with this item for the given file id or {@link
+ * #UNASSOCIATED_VALUE} if the item is not associated to the given file id.
+ */
+ public int getVirtualFileIndex(int virtualFileId) {
+ if (virtualFileIndexes == null) {
+ return UNASSOCIATED_VALUE;
+ }
+ // If more files were added, but this entry not associated with it, we would not have extended
+ // the size of the array. So if the {@link virtualFileId} is out of bounds, it means
+ // {@link #UNASSOCIATED_VALUE}
+ return virtualFileIndexes.length > virtualFileId
+ ? virtualFileIndexes[virtualFileId]
+ : UNASSOCIATED_VALUE;
+ }
+
+ // Partial implementation of PresortedComparable.
+
+ final public void setSortedIndex(int sortedIndex) {
+ assert sortedIndex > SORTED_INDEX_UNKNOWN;
+ assert this.sortedIndex == SORTED_INDEX_UNKNOWN;
+ this.sortedIndex = sortedIndex;
+ }
+
+ final public int getSortedIndex() {
+ return sortedIndex;
+ }
+
+ final public int sortedCompareTo(int other) {
+ assert sortedIndex > SORTED_INDEX_UNKNOWN;
+ assert other > SORTED_INDEX_UNKNOWN;
+ return Integer.compare(sortedIndex, other);
+ }
+
+ public void flushCachedValues() {
+ super.flushCachedValues();
+ resetSortedIndex();
+ }
+
+ public void resetSortedIndex() {
+ sortedIndex = SORTED_INDEX_UNKNOWN;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java
new file mode 100644
index 0000000..b28ffb5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java
@@ -0,0 +1,135 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
+import org.objectweb.asm.Type;
+
+/**
+ * Common structures used while reading in a Java application from jar files.
+ *
+ * The primary use of this class is to canonicalize dex items during read.
+ * The addition of classes to the builder also takes place through this class.
+ * It does not currently support multithreaded reading.
+ */
+public class JarApplicationReader {
+ public final InternalOptions options;
+
+ public JarApplicationReader(InternalOptions options) {
+ this.options = options;
+ }
+
+ public DexItemFactory getFactory() {
+ return options.itemFactory;
+ }
+
+ public DexString getString(String string) {
+ return options.itemFactory.createString(string);
+ }
+
+ public DexType getType(Type type) {
+ return getTypeFromDescriptor(type.getDescriptor());
+ }
+
+ public DexType getTypeFromName(String name) {
+ assert isValidInternalName(name);
+ return getType(Type.getObjectType(name));
+ }
+
+ public DexType getTypeFromDescriptor(String desc) {
+ assert isValidDescriptor(desc);
+ return options.itemFactory.createType(getString(desc));
+ }
+
+ public DexTypeList getTypeListFromNames(String[] names) {
+ if (names.length == 0) {
+ return DexTypeList.empty();
+ }
+ DexType[] types = new DexType[names.length];
+ for (int i = 0; i < names.length; i++) {
+ types[i] = getTypeFromName(names[i]);
+ }
+ return new DexTypeList(types);
+ }
+
+ public DexTypeList getTypeListFromDescriptors(String[] descriptors) {
+ if (descriptors.length == 0) {
+ return DexTypeList.empty();
+ }
+ DexType[] types = new DexType[descriptors.length];
+ for (int i = 0; i < descriptors.length; i++) {
+ types[i] = getTypeFromDescriptor(descriptors[i]);
+ }
+ return new DexTypeList(types);
+ }
+
+ public DexField getField(String owner, String name, String desc) {
+ return getField(getTypeFromName(owner), name, desc);
+ }
+
+ public DexField getField(DexType owner, String name, String desc) {
+ return options.itemFactory.createField(owner, getTypeFromDescriptor(desc), getString(name));
+ }
+
+ public DexMethod getMethod(String owner, String name, String desc) {
+ return getMethod(getTypeFromName(owner), name, desc);
+ }
+
+ public DexMethod getMethod(DexType owner, String name, String desc) {
+ return options.itemFactory.createMethod(owner, getProto(desc), getString(name));
+ }
+
+ public DexCallSite getCallSite(String methodName, String methodProto,
+ DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
+ return options.itemFactory.createCallSite(
+ getString(methodName), getProto(methodProto), bootstrapMethod, bootstrapArgs);
+ }
+
+ public DexMethodHandle getMethodHandle(
+ MethodHandleType type, Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod) {
+ return options.itemFactory.createMethodHandle(type, fieldOrMethod);
+ }
+
+ public DexProto getProto(String desc) {
+ assert isValidDescriptor(desc);
+ Type returnType = Type.getReturnType(desc);
+ Type[] arguments = Type.getArgumentTypes(desc);
+
+ StringBuilder shortyDescriptor = new StringBuilder();
+ String[] argumentDescriptors = new String[arguments.length];
+ shortyDescriptor.append(getShortyDescriptor(returnType));
+ for (int i = 0; i < arguments.length; i++) {
+ shortyDescriptor.append(getShortyDescriptor(arguments[i]));
+ argumentDescriptors[i] = arguments[i].getDescriptor();
+ }
+ DexProto proto = options.itemFactory.createProto(
+ getString(shortyDescriptor.toString()),
+ getTypeFromDescriptor(returnType.getDescriptor()),
+ getTypeListFromDescriptors(argumentDescriptors));
+ return proto;
+ }
+
+ private static String getShortyDescriptor(Type type) {
+ switch (type.getSort()) {
+ case Type.METHOD:
+ throw new InternalCompilerError("Cannot produce a shorty decriptor for methods");
+ case Type.ARRAY:
+ case Type.OBJECT:
+ return "L";
+ default:
+ return type.getDescriptor();
+ }
+ }
+
+ private boolean isValidDescriptor(String desc) {
+ return Type.getType(desc).getDescriptor().equals(desc);
+ }
+
+ private boolean isValidInternalName(String name) {
+ return Type.getObjectType(name).getInternalName().equals(name);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
new file mode 100644
index 0000000..07a7231
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -0,0 +1,711 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
+import static org.objectweb.asm.Opcodes.ACC_DEPRECATED;
+import static org.objectweb.asm.Opcodes.ASM5;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
+import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.DexValue.DexValueBoolean;
+import com.android.tools.r8.graph.DexValue.DexValueByte;
+import com.android.tools.r8.graph.DexValue.DexValueChar;
+import com.android.tools.r8.graph.DexValue.DexValueDouble;
+import com.android.tools.r8.graph.DexValue.DexValueEnum;
+import com.android.tools.r8.graph.DexValue.DexValueFloat;
+import com.android.tools.r8.graph.DexValue.DexValueInt;
+import com.android.tools.r8.graph.DexValue.DexValueLong;
+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.utils.InternalResource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+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;
+
+/**
+ * Java/Jar class reader for constructing dex/graph structure.
+ */
+public class JarClassFileReader {
+
+ // Hidden ASM "synthetic attribute" bit we need to clear.
+ private static int ACC_SYNTHETIC_ATTRIBUTE = 0x40000;
+
+ private final JarApplicationReader application;
+ private final Consumer<DexClass> classConsumer;
+
+ public JarClassFileReader(
+ JarApplicationReader application, Consumer<DexClass> classConsumer) {
+ this.application = application;
+ this.classConsumer = classConsumer;
+ }
+
+ public void read(String file, InternalResource.Kind kind, InputStream input) throws IOException {
+ ClassReader reader = new ClassReader(input);
+ reader.accept(new CreateDexClassVisitor(
+ file, kind, reader.b, application, classConsumer), SKIP_FRAMES);
+ }
+
+ private static DexAccessFlags createAccessFlags(int access) {
+ // Clear the "synthetic attribute" and "deprecated" flags if preset.
+ return new DexAccessFlags(access & ~ACC_SYNTHETIC_ATTRIBUTE & ~ACC_DEPRECATED);
+ }
+
+ private static AnnotationVisitor createAnnotationVisitor(String desc, boolean visible,
+ AnnotationVisitor annotationVisitor, List<DexAnnotation> annotations,
+ JarApplicationReader application) {
+ assert annotations != null;
+ int visiblity = visible ? DexAnnotation.VISIBILITY_RUNTIME : DexAnnotation.VISIBILITY_BUILD;
+ return new CreateAnnotationVisitor(annotationVisitor, application, (names, values) ->
+ annotations.add(new DexAnnotation(visiblity,
+ createEncodedAnnotation(desc, names, values, application))));
+ }
+
+ private static DexEncodedAnnotation createEncodedAnnotation(String desc,
+ List<DexString> names, List<DexValue> values, JarApplicationReader application) {
+ assert (names == null && values.isEmpty())
+ || (names != null && !names.isEmpty() && names.size() == values.size());
+ DexAnnotationElement[] elements = new DexAnnotationElement[values.size()];
+ for (int i = 0; i < values.size(); i++) {
+ elements[i] = new DexAnnotationElement(names.get(i), values.get(i));
+ }
+ return new DexEncodedAnnotation(application.getTypeFromDescriptor(desc), elements);
+ }
+
+ private static class CreateDexClassVisitor extends ClassVisitor {
+ private final String file;
+ private final InternalResource.Kind kind;
+ private final JarApplicationReader application;
+ private final Consumer<DexClass> classConsumer;
+ private final ReparseContext context = new ReparseContext();
+
+ // DexClass data.
+ private DexType type;
+ private DexAccessFlags accessFlags;
+ private DexType superType;
+ private DexTypeList interfaces;
+ private DexString sourceFile;
+ private List<DexType> memberClasses = null;
+ private List<DexAnnotation> annotations = null;
+ private List<DexAnnotationElement> defaultAnnotations = null;
+ private DexAnnotation innerClassAnnotation = null;
+ private DexAnnotation enclosingAnnotation = null;
+ private final List<DexEncodedField> staticFields = new ArrayList<>();
+ private final List<DexEncodedField> instanceFields = new ArrayList<>();
+ private final List<DexEncodedMethod> directMethods = new ArrayList<>();
+ private final List<DexEncodedMethod> virtualMethods = new ArrayList<>();
+
+ public CreateDexClassVisitor(
+ String file,
+ InternalResource.Kind kind,
+ byte[] classCache,
+ JarApplicationReader application,
+ Consumer<DexClass> classConsumer) {
+ super(ASM5);
+ this.file = file;
+ this.kind = kind;
+ this.classConsumer = classConsumer;
+ this.context.classCache = classCache;
+ this.application = application;
+ }
+
+ @Override
+ public void visitInnerClass(String name, String outerName, String innerName, int access) {
+ if (type == application.getTypeFromName(name)) {
+ // If the inner class is this class, record its original access flags and name in its
+ // InnerClass annotation. We defer storing the actual annotation until we have found
+ // a matching enclosing annotation.
+ assert innerClassAnnotation == null;
+ innerClassAnnotation = DexAnnotation.createInnerClassAnnotation(
+ innerName, access, application.getFactory());
+ // If this is a named inner class (in which case outerName and innerName are defined)
+ // record the outer class in its EnclosingClass annotation.
+ if (outerName != null && innerName != null) {
+ assert enclosingAnnotation == null;
+ enclosingAnnotation = DexAnnotation.createEnclosingClassAnnotation(
+ application.getTypeFromName(outerName),
+ application.getFactory());
+ }
+ } else if (outerName != null && innerName != null
+ && type == application.getTypeFromName(outerName)) {
+ // If the inner class is a member of this class, record it for the MemberClasses annotation.
+ if (memberClasses == null) {
+ memberClasses = new ArrayList<>();
+ }
+ memberClasses.add(application.getTypeFromName(name));
+ }
+ }
+
+ @Override
+ public void visitOuterClass(String owner, String name, String desc) {
+ assert enclosingAnnotation == null;
+ // This is called for anonymous inner classes defined in classes or in methods.
+ DexType ownerType = application.getTypeFromName(owner);
+ if (name == null) {
+ enclosingAnnotation = DexAnnotation.createEnclosingClassAnnotation(
+ ownerType, application.getFactory());
+ } else {
+ enclosingAnnotation = DexAnnotation.createEnclosingMethodAnnotation(
+ application.getMethod(ownerType, name, desc), application.getFactory());
+ }
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName,
+ String[] interfaces) {
+ accessFlags = createAccessFlags(access);
+ // Unset the (in dex) non-existent ACC_SUPER flag on the class.
+ assert Constants.ACC_SYNCHRONIZED == Opcodes.ACC_SUPER;
+ accessFlags.unsetSynchronized();
+ type = application.getTypeFromName(name);
+ assert superName != null || name.equals(Constants.JAVA_LANG_OBJECT_NAME);
+ superType = superName == null ? null : application.getTypeFromName(superName);
+ this.interfaces = application.getTypeListFromNames(interfaces);
+ if (signature != null && !signature.isEmpty()) {
+ addAnnotation(DexAnnotation.createSignatureAnnotation(signature, application.getFactory()));
+ }
+ }
+
+ @Override
+ public void visitSource(String source, String debug) {
+ if (source != null) {
+ sourceFile = application.getString(source);
+ }
+ if (debug != null) {
+ getAnnotations().add(
+ DexAnnotation.createSourceDebugExtensionAnnotation(
+ new DexValueString(application.getString(debug)), application.getFactory()));
+ }
+ }
+
+ @Override
+ public FieldVisitor visitField(
+ int access, String name, String desc, String signature, Object value) {
+ return new CreateFieldVisitor(this, access, name, desc, signature, value);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String desc, String signature, String[] exceptions) {
+ return new CreateMethodVisitor(access, name, desc, signature, exceptions, this);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return createAnnotationVisitor(desc, visible, null, getAnnotations(), application);
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
+ boolean visible) {
+ // Java 8 type annotations are not supported by Dex, thus ignore them.
+ return null;
+ }
+
+ @Override
+ public void visitAttribute(Attribute attr) {
+ // Unknown attribute must only be ignored
+ }
+
+ @Override
+ public void visitEnd() {
+ if (memberClasses != null) {
+ assert !memberClasses.isEmpty();
+ addAnnotation(DexAnnotation.createMemberClassesAnnotation(
+ memberClasses, application.getFactory()));
+ }
+ if (innerClassAnnotation != null) {
+ if (enclosingAnnotation == null) {
+ System.out.println("InnerClass annotation is missing a corresponding EnclosingMember " +
+ "annotation. This is typically a sign of using an outdated Java toolchain. To fix, " +
+ "recompile the source with an updated toolchain. The InnerClass annotation will be " +
+ "ignored.");
+ } else {
+ addAnnotation(innerClassAnnotation);
+ addAnnotation(enclosingAnnotation);
+ }
+ }
+ if (defaultAnnotations != null) {
+ addAnnotation(DexAnnotation.createAnnotationDefaultAnnotation(
+ type, defaultAnnotations, application.getFactory()));
+ }
+ DexClass clazz = DexClass.factoryForResourceKind(kind).create(
+ type,
+ DexClass.Origin.ClassFile,
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ createAnnotationSet(annotations),
+ staticFields.toArray(new DexEncodedField[staticFields.size()]),
+ instanceFields.toArray(new DexEncodedField[instanceFields.size()]),
+ directMethods.toArray(new DexEncodedMethod[directMethods.size()]),
+ virtualMethods.toArray(new DexEncodedMethod[virtualMethods.size()]));
+ if (kind == InternalResource.Kind.PROGRAM) {
+ context.owner = clazz.asProgramClass();
+ }
+ classConsumer.accept(clazz);
+ }
+
+ private void addDefaultAnnotation(String name, DexValue value) {
+ if (defaultAnnotations == null) {
+ defaultAnnotations = new ArrayList<>();
+ }
+ defaultAnnotations.add(new DexAnnotationElement(application.getString(name), value));
+ }
+
+ private void addAnnotation(DexAnnotation annotation) {
+ getAnnotations().add(annotation);
+ }
+
+ private List<DexAnnotation> getAnnotations() {
+ if (annotations == null) {
+ annotations = new ArrayList<>();
+ }
+ return annotations;
+ }
+ }
+
+ private static DexAnnotationSet createAnnotationSet(List<DexAnnotation> annotations) {
+ return annotations == null || annotations.isEmpty()
+ ? DexAnnotationSet.empty()
+ : new DexAnnotationSet(annotations.toArray(new DexAnnotation[annotations.size()]));
+ }
+
+ private static class CreateFieldVisitor extends FieldVisitor {
+ private final CreateDexClassVisitor parent;
+ private final int access;
+ private final String name;
+ private final String desc;
+ private final Object value;
+ private List<DexAnnotation> annotations = null;
+
+ public CreateFieldVisitor(CreateDexClassVisitor parent,
+ int access, String name, String desc, String signature, Object value) {
+ super(ASM5);
+ this.parent = parent;
+ this.access = access;
+ this.name = name;
+ this.desc = desc;
+ this.value = value;
+ if (signature != null && !signature.isEmpty()) {
+ addAnnotation(DexAnnotation.createSignatureAnnotation(
+ signature, parent.application.getFactory()));
+ }
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return createAnnotationVisitor(desc, visible, null, getAnnotations(), parent.application);
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
+ boolean visible) {
+ // Java 8 type annotations are not supported by Dex, thus ignore them.
+ return null;
+ }
+
+ @Override
+ public void visitEnd() {
+ DexAccessFlags flags = createAccessFlags(access);
+ DexField dexField = parent.application.getField(parent.type, name, desc);
+ DexAnnotationSet annotationSet = createAnnotationSet(annotations);
+ DexValue staticValue = flags.isStatic() ? getStaticValue(value, dexField.type) : null;
+ DexEncodedField field = new DexEncodedField(dexField, flags, annotationSet, staticValue);
+ if (flags.isStatic()) {
+ parent.staticFields.add(field);
+ } else {
+ parent.instanceFields.add(field);
+ }
+ }
+
+ private DexValue getStaticValue(Object value, DexType type) {
+ if (value == null) {
+ return DexValue.defaultForType(type, parent.application.getFactory());
+ }
+ DexItemFactory factory = parent.application.getFactory();
+ if (type == factory.booleanType) {
+ int i = (Integer) value;
+ assert 0 <= i && i <= 1;
+ return DexValueBoolean.create(i == 1);
+ }
+ if (type == factory.byteType) {
+ return DexValueByte.create(((Integer) value).byteValue());
+ }
+ if (type == factory.shortType) {
+ return DexValueShort.create(((Integer) value).shortValue());
+ }
+ if (type == factory.charType) {
+ return DexValueChar.create((char) ((Integer) value).intValue());
+ }
+ if (type == factory.intType) {
+ return DexValueInt.create((Integer) value);
+ }
+ if (type == factory.floatType) {
+ return DexValueFloat.create((Float) value);
+ }
+ if (type == factory.longType) {
+ return DexValueLong.create((Long) value);
+ }
+ if (type == factory.doubleType) {
+ return DexValueDouble.create((Double) value);
+ }
+ if (type == factory.stringType) {
+ return new DexValueString(factory.createString((String) value));
+ }
+ throw new Unreachable("Unexpected static-value type " + type);
+ }
+
+ private void addAnnotation(DexAnnotation annotation) {
+ getAnnotations().add(annotation);
+ }
+
+ private List<DexAnnotation> getAnnotations() {
+ if (annotations == null) {
+ annotations = new ArrayList<>();
+ }
+ return annotations;
+ }
+ }
+
+ private static class CreateMethodVisitor extends MethodVisitor {
+ private final int access;
+ private final String name;
+ private final String desc;
+ private final CreateDexClassVisitor parent;
+ private final int parameterCount;
+ private List<DexAnnotation> annotations = null;
+ private DexValue defaultAnnotation = null;
+ private List<List<DexAnnotation>> parameterAnnotations = null;
+ private List<DexValue> parameterNames = null;
+ private List<DexValue> parameterFlags = null;
+
+ public CreateMethodVisitor(int access, String name, String desc, String signature,
+ String[] exceptions, CreateDexClassVisitor parent) {
+ super(ASM5);
+ this.access = access;
+ this.name = name;
+ this.desc = desc;
+ this.parent = parent;
+ parameterCount = Type.getArgumentTypes(desc).length;
+ if (exceptions != null && exceptions.length > 0) {
+ DexValue[] values = new DexValue[exceptions.length];
+ for (int i = 0; i < exceptions.length; i++) {
+ values[i] = new DexValueType(parent.application.getTypeFromName(exceptions[i]));
+ }
+ addAnnotation(DexAnnotation.createThrowsAnnotation(
+ values, parent.application.getFactory()));
+ }
+ if (signature != null && !signature.isEmpty()) {
+ addAnnotation(DexAnnotation.createSignatureAnnotation(
+ signature, parent.application.getFactory()));
+ }
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return createAnnotationVisitor(desc, visible,
+ mv == null ? null : mv.visitAnnotation(desc, visible),
+ getAnnotations(), parent.application);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotationDefault() {
+ return new CreateAnnotationVisitor(mv == null ? null : mv.visitAnnotationDefault(),
+ parent.application, (names, elements) -> {
+ assert elements.size() == 1;
+ defaultAnnotation = elements.get(0);
+ });
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
+ boolean visible) {
+ // Java 8 type annotations are not supported by Dex, thus ignore them.
+ return null;
+ }
+
+ @Override
+ public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
+ if (parameterAnnotations == null) {
+ parameterAnnotations = new ArrayList<>(parameterCount);
+ for (int i = 0; i < parameterCount; i++) {
+ parameterAnnotations.add(new ArrayList<>());
+ }
+ }
+ return createAnnotationVisitor(desc, visible,
+ mv == null ? null : mv.visitParameterAnnotation(parameter, desc, visible),
+ parameterAnnotations.get(parameter), parent.application);
+ }
+
+ @Override
+ public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc,
+ boolean visible) {
+ // Java 8 type annotations are not supported by Dex, thus ignore them.
+ return null;
+ }
+
+ @Override
+ public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath,
+ Label[] start, Label[] end, int[] index, String desc, boolean visible) {
+ // Java 8 type annotations are not supported by Dex, thus ignore them.
+ return null;
+ }
+
+ @Override
+ public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc,
+ boolean visible) {
+ // Java 8 type annotations are not supported by Dex, thus ignore them.
+ return null;
+ }
+
+ @Override
+ public void visitParameter(String name, int access) {
+ if (parameterNames == null) {
+ assert parameterFlags == null;
+ parameterNames = new ArrayList<>(parameterCount);
+ parameterFlags = new ArrayList<>(parameterCount);
+ }
+ parameterNames.add(new DexValueString(parent.application.getFactory().createString(name)));
+ parameterFlags.add(DexValueInt.create(access));
+ super.visitParameter(name, access);
+ }
+
+ @Override
+ public void visitEnd() {
+ DexMethod method = parent.application.getMethod(parent.type, name, desc);
+ DexAccessFlags flags = createMethodAccessFlags(access);
+ Code code = null;
+ if (!flags.isAbstract()
+ && !flags.isNative()
+ && parent.kind == InternalResource.Kind.PROGRAM) {
+ code = new JarCode(method, parent.context, parent.application);
+ }
+ DexAnnotationSetRefList parameterAnnotationSets;
+ if (parameterAnnotations == null) {
+ parameterAnnotationSets = DexAnnotationSetRefList.empty();
+ } else {
+ DexAnnotationSet[] sets = new DexAnnotationSet[parameterAnnotations.size()];
+ for (int i = 0; i < parameterAnnotations.size(); i++) {
+ sets[i] = createAnnotationSet(parameterAnnotations.get(i));
+ }
+ parameterAnnotationSets = new DexAnnotationSetRefList(sets);
+ }
+ if (parameterNames != null) {
+ assert parameterFlags != null;
+ if (parameterNames.size() != parameterCount) {
+ parent.application.options.warningInvalidParameterAnnotations =
+ "Invalid parameter count in MethodParameters attributes of "
+ + method.toSourceString() + " from '" + parent.file + "'. Found "
+ + parameterNames.size() + " while expecting " + parameterCount + "."
+ + " This is likely due to proguard having removed a parameter.";
+ }
+ getAnnotations().add(DexAnnotation.createMethodParametersAnnotation(
+ parameterNames.toArray(new DexValue[parameterNames.size()]),
+ parameterFlags.toArray(new DexValue[parameterFlags.size()]),
+ parent.application.getFactory()));
+ }
+ DexEncodedMethod dexMethod = new DexEncodedMethod(method, flags,
+ createAnnotationSet(annotations), parameterAnnotationSets, code);
+ if (flags.isStatic() || flags.isConstructor() || flags.isPrivate()) {
+ parent.directMethods.add(dexMethod);
+ } else {
+ parent.virtualMethods.add(dexMethod);
+ }
+ if (defaultAnnotation != null) {
+ parent.addDefaultAnnotation(name, defaultAnnotation);
+ }
+ }
+
+ private List<DexAnnotation> getAnnotations() {
+ if (annotations == null) {
+ annotations = new ArrayList<>();
+ }
+ return annotations;
+ }
+
+ private void addAnnotation(DexAnnotation annotation) {
+ getAnnotations().add(annotation);
+ }
+
+ private DexAccessFlags createMethodAccessFlags(int access) {
+ DexAccessFlags flags = createAccessFlags(access);
+ // Set just the dex specific declared-synchronized flag if the method is synchronized.
+ // TODO(zerny): Should declared sync also be set if it is native?
+ if (flags.isSynchronized() && !flags.isNative()) {
+ flags.unsetSynchronized();
+ flags.setDeclaredSynchronized();
+ }
+ // Set the constructor bit on instance and class initializers.
+ if (name.equals(Constants.INSTANCE_INITIALIZER_NAME) || name.equals(
+ Constants.CLASS_INITIALIZER_NAME)) {
+ flags.setConstructor();
+ }
+ return flags;
+ }
+ }
+
+ private static class CreateAnnotationVisitor extends AnnotationVisitor {
+ private final JarApplicationReader application;
+ private final BiConsumer<List<DexString>, List<DexValue>> onVisitEnd;
+ private List<DexString> names = null;
+ private final List<DexValue> values = new ArrayList<>();
+
+ public CreateAnnotationVisitor(AnnotationVisitor annotationVisitor,
+ JarApplicationReader application,
+ BiConsumer<List<DexString>, List<DexValue>> onVisitEnd) {
+ super(ASM5, annotationVisitor);
+ this.application = application;
+ this.onVisitEnd = onVisitEnd;
+ }
+
+ @Override
+ public void visit(String name, Object value) {
+ addElement(name, getDexValue(value));
+ }
+
+ @Override
+ public void visitEnum(String name, String desc, String value) {
+ DexType owner = application.getTypeFromDescriptor(desc);
+ addElement(name, new DexValueEnum(application.getField(owner, value, desc)));
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String name, String desc) {
+ return new CreateAnnotationVisitor(av, application, (names, values) ->
+ addElement(name, new DexValueAnnotation(
+ createEncodedAnnotation(desc, names, values, application))));
+ }
+
+ @Override
+ public AnnotationVisitor visitArray(String name) {
+ return new CreateAnnotationVisitor(av, application, (names, values) -> {
+ assert names == null;
+ addElement(name, new DexValueArray(values.toArray(new DexValue[values.size()])));
+ });
+ }
+
+ @Override
+ public void visitEnd() {
+ onVisitEnd.accept(names, values);
+ }
+
+ private void addElement(String name, DexValue value) {
+ if (name != null) {
+ if (names == null){
+ names = new ArrayList<>();
+ }
+ names.add(application.getString(name));
+ }
+ values.add(value);
+ }
+
+ private static DexValueArray getDexValueArray(Object value) {
+ if (value instanceof byte[]) {
+ byte[] values = (byte[]) value;
+ DexValue[] elements = new DexValue[values.length];
+ for (int i = 0; i < values.length; i++) {
+ elements[i] = DexValueByte.create(values[i]);
+ }
+ return new DexValueArray(elements);
+ } else if (value instanceof boolean[]) {
+ boolean[] values = (boolean[]) value;
+ DexValue[] elements = new DexValue[values.length];
+ for (int i = 0; i < values.length; i++) {
+ elements[i] = DexValueBoolean.create(values[i]);
+ }
+ return new DexValueArray(elements);
+ } else if (value instanceof char[]) {
+ char[] values = (char[]) value;
+ DexValue[] elements = new DexValue[values.length];
+ for (int i = 0; i < values.length; i++) {
+ elements[i] = DexValueChar.create(values[i]);
+ }
+ return new DexValueArray(elements);
+ } else if (value instanceof short[]) {
+ short[] values = (short[]) value;
+ DexValue[] elements = new DexValue[values.length];
+ for (int i = 0; i < values.length; i++) {
+ elements[i] = DexValueShort.create(values[i]);
+ }
+ return new DexValueArray(elements);
+ } else if (value instanceof int[]) {
+ int[] values = (int[]) value;
+ DexValue[] elements = new DexValue[values.length];
+ for (int i = 0; i < values.length; i++) {
+ elements[i] = DexValueInt.create(values[i]);
+ }
+ return new DexValueArray(elements);
+ } else if (value instanceof long[]) {
+ long[] values = (long[]) value;
+ DexValue[] elements = new DexValue[values.length];
+ for (int i = 0; i < values.length; i++) {
+ elements[i] = DexValueLong.create(values[i]);
+ }
+ return new DexValueArray(elements);
+ } else if (value instanceof float[]) {
+ float[] values = (float[]) value;
+ DexValue[] elements = new DexValue[values.length];
+ for (int i = 0; i < values.length; i++) {
+ elements[i] = DexValueFloat.create(values[i]);
+ }
+ return new DexValueArray(elements);
+ } else if (value instanceof double[]) {
+ double[] values = (double[]) value;
+ DexValue[] elements = new DexValue[values.length];
+ for (int i = 0; i < values.length; i++) {
+ elements[i] = DexValueDouble.create(values[i]);
+ }
+ return new DexValueArray(elements);
+ }
+ throw new Unreachable("Unexpected type of annotation value: " + value);
+ }
+
+ private DexValue getDexValue(Object value) {
+ if (value == null) {
+ return DexValue.NULL;
+ }
+ if (value instanceof Byte) {
+ return DexValueByte.create((Byte) value);
+ } else if (value instanceof Boolean) {
+ return DexValueBoolean.create((Boolean) value);
+ } else if (value instanceof Character) {
+ return DexValueChar.create((Character) value);
+ } else if (value instanceof Short) {
+ return DexValueShort.create((Short) value);
+ } else if (value instanceof Integer) {
+ return DexValueInt.create((Integer) value);
+ } else if (value instanceof Long) {
+ return DexValueLong.create((Long) value);
+ } else if (value instanceof Float) {
+ return DexValueFloat.create((Float) value);
+ } else if (value instanceof Double) {
+ return DexValueDouble.create((Double) value);
+ } else if (value instanceof String) {
+ return new DexValueString(application.getString((String) value));
+ } else if (value instanceof Type) {
+ return new DexValueType(application.getTypeFromDescriptor(((Type) value).getDescriptor()));
+ }
+ return getDexValueArray(value);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
new file mode 100644
index 0000000..b40f6ef
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -0,0 +1,168 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.JarSourceCode;
+import com.android.tools.r8.jar.JarRegisterEffectsVisitor;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.InternalOptions;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.IdentityHashMap;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.JSRInlinerAdapter;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.util.Textifier;
+import org.objectweb.asm.util.TraceMethodVisitor;
+
+public class JarCode extends Code {
+
+ 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 IdentityHashMap<DexMethod, JarCode> lookupMap = new IdentityHashMap<>();
+ }
+
+ private final DexType clazz;
+ private MethodNode node;
+ private ReparseContext context;
+
+ private final JarApplicationReader application;
+
+ public JarCode(DexMethod method, ReparseContext context, JarApplicationReader application) {
+ this.clazz = method.getHolder();
+ this.context = context;
+ this.application = application;
+ context.lookupMap.put(method, this);
+ }
+
+ @Override
+ public boolean isJarCode() {
+ return true;
+ }
+
+ @Override
+ public JarCode asJarCode() {
+ return this;
+ }
+
+ @Override
+ protected int computeHashCode() {
+ triggerDelayedParsingIfNeccessary();
+ return node.hashCode();
+ }
+
+ @Override
+ protected boolean computeEquals(Object other) {
+ triggerDelayedParsingIfNeccessary();
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof JarCode) {
+ JarCode o = (JarCode) other;
+ o.triggerDelayedParsingIfNeccessary();
+ // TODO(zerny): This amounts to object equality.
+ return node.equals(o.node);
+ }
+ return false;
+ }
+
+ @Override
+ public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
+ assert clazz == encodedMethod.method.getHolder();
+ triggerDelayedParsingIfNeccessary();
+ JarSourceCode source = new JarSourceCode(clazz, node, application);
+ IRBuilder builder = new IRBuilder(encodedMethod, source, options);
+ return builder.build();
+ }
+
+ @Override
+ public void registerReachableDefinitions(UseRegistry registry) {
+ triggerDelayedParsingIfNeccessary();
+ node.instructions.accept(new JarRegisterEffectsVisitor(clazz, registry, application));
+ }
+
+ @Override
+ public String toString() {
+ triggerDelayedParsingIfNeccessary();
+ TraceMethodVisitor visitor = new TraceMethodVisitor(new Textifier());
+ node.accept(visitor);
+ StringWriter writer = new StringWriter();
+ visitor.p.print(new PrintWriter(writer));
+ return writer.toString();
+ }
+
+ @Override
+ public String toString(ClassNameMapper naming) {
+ return toString();
+ }
+
+ private void triggerDelayedParsingIfNeccessary() {
+ if (context != null) {
+ // The SecondVistor is in charge of setting the context to null.
+ DexProgramClass owner = context.owner;
+ new ClassReader(context.classCache).accept(new SecondVisitor(context, application),
+ ClassReader.SKIP_FRAMES);
+ assert verifyNoReparseContext(owner);
+ }
+ }
+
+ /**
+ * Fills the MethodNodes of all the methods in the class and removes the ReparseContext.
+ */
+ private static class SecondVisitor extends ClassVisitor {
+
+ private final ReparseContext context;
+ private final JarApplicationReader application;
+
+ public SecondVisitor(ReparseContext context, JarApplicationReader application) {
+ super(Opcodes.ASM5);
+ this.context = context;
+ this.application = application;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+ String[] exceptions) {
+ MethodNode node = new JSRInlinerAdapter(null, access, name, desc, signature, exceptions);
+ JarCode code = context.lookupMap.get(application.getMethod(context.owner.type, name, desc));
+ if (code != null) {
+ code.context = null;
+ code.node = node;
+ return node;
+ }
+ return null;
+ }
+ }
+
+ private static boolean verifyNoReparseContext(DexProgramClass owner) {
+ for (DexEncodedMethod method : owner.virtualMethods()) {
+ Code code = method.getCode();
+ if (code != null && code.isJarCode()) {
+ if (code.asJarCode().context != null) {
+ return false;
+ }
+ }
+ }
+
+ for (DexEncodedMethod method : owner.directMethods()) {
+ Code code = method.getCode();
+ if (code != null && code.isJarCode()) {
+ if (code.asJarCode().context != null) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/KeyedDexItem.java b/src/main/java/com/android/tools/r8/graph/KeyedDexItem.java
new file mode 100644
index 0000000..ad852eb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/KeyedDexItem.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+public abstract class KeyedDexItem<T extends PresortedComparable<T>> extends DexItem {
+
+ public abstract T getKey();
+
+ @Override
+ public final boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ return (other.getClass() == getClass()) && ((KeyedDexItem) other).getKey().equals(getKey());
+ }
+
+ @Override
+ public final int hashCode() {
+ return getKey().hashCode();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/LazyClassFileLoader.java b/src/main/java/com/android/tools/r8/graph/LazyClassFileLoader.java
new file mode 100644
index 0000000..01bf132
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/LazyClassFileLoader.java
@@ -0,0 +1,99 @@
+// 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.graph;
+
+import static com.android.tools.r8.utils.FileUtils.DEFAULT_DEX_FILENAME;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.InternalResource;
+import com.google.common.io.Closer;
+import java.io.IOException;
+
+// Lazily loads a class file represented by resource.
+public final class LazyClassFileLoader implements DexClassPromise {
+ // Resource representing file definition.
+ private final InternalResource resource;
+
+ // Application reader to be used. Note that the reader may be reused in
+ // many loaders and may be used concurrently, it is considered to be
+ // thread-safe since its only state is internal options which we
+ // consider immutable after they are initialized (barring dex factory
+ // which is thread-safe).
+ private final JarApplicationReader reader;
+
+ // Dex type of the class to be created.
+ private final DexType type;
+
+ // Cached loaded class if get(...) method has already been called, this
+ // field is only accessed in context synchronized on `this`.
+ private DexClass loadedClass = null;
+
+ public LazyClassFileLoader(DexType type, InternalResource resource, JarApplicationReader reader) {
+ this.resource = resource;
+ this.reader = reader;
+ this.type = type;
+ }
+
+ // Callback method for JarClassFileReader, is always called in synchronized context.
+ private void addClass(DexClass clazz) {
+ assert clazz != null;
+ assert loadedClass == null;
+ loadedClass = clazz;
+ }
+
+ @Override
+ public DexType getType() {
+ return type;
+ }
+
+ @Override
+ public DexClass.Origin getOrigin() {
+ return DexClass.Origin.ClassFile;
+ }
+
+ @Override
+ public boolean isProgramClass() {
+ return resource.kind == InternalResource.Kind.PROGRAM;
+ }
+
+ @Override
+ public boolean isClasspathClass() {
+ return resource.kind == InternalResource.Kind.CLASSPATH;
+ }
+
+ @Override
+ public boolean isLibraryClass() {
+ return resource.kind == InternalResource.Kind.LIBRARY;
+ }
+
+ // Loads the class from the resource. Synchronized on `this` to avoid
+ // unnecessary complications, thus all threads trying to load a class with
+ // this loader will wait for the first load to finish.
+ @Override
+ public synchronized DexClass get() {
+ if (loadedClass != null) {
+ return loadedClass;
+ }
+
+ try (Closer closer = Closer.create()) {
+ JarClassFileReader reader = new JarClassFileReader(this.reader, this::addClass);
+ reader.read(DEFAULT_DEX_FILENAME, resource.kind, resource.getStream(closer));
+ } catch (IOException e) {
+ throw new CompilationError("Failed to load class: " + resource.getClassDescriptor(), e);
+ }
+
+ if (loadedClass == null) {
+ throw new Unreachable("Class is supposed to be loaded: " + resource.getClassDescriptor());
+ }
+
+ if (loadedClass.type != type) {
+ throw new CompilationError("Class content provided for type descriptor "
+ + type.toSourceString() + " actually defines class " + loadedClass.type.toSourceString());
+ }
+
+ return loadedClass;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
new file mode 100644
index 0000000..1d5f38c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -0,0 +1,208 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.Constants;
+import com.google.common.collect.Sets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+
+public class ObjectToOffsetMapping {
+
+ private final int virtualFileId;
+
+ private final DexProgramClass[] classes;
+ private final DexProto[] protos;
+ private final DexType[] types;
+ private final DexMethod[] methods;
+ private final DexField[] fields;
+ private final DexString[] strings;
+ private final DexCallSite[] callSites;
+ private final DexMethodHandle[] methodHandles;
+ private DexString firstJumboString;
+
+ public ObjectToOffsetMapping(
+ int virtualFileId,
+ DexApplication application,
+ DexProgramClass[] classes,
+ DexProto[] protos,
+ DexType[] types,
+ DexMethod[] methods,
+ DexField[] fields,
+ DexString[] strings,
+ DexCallSite[] callSites,
+ DexMethodHandle[] methodHandles) {
+ assert application != null;
+ assert classes != null;
+ assert protos != null;
+ assert types != null;
+ assert methods != null;
+ assert fields != null;
+ assert strings != null;
+ assert callSites != null;
+ assert methodHandles != null;
+
+ this.virtualFileId = virtualFileId;
+ this.classes = sortClasses(application, classes);
+ this.protos = protos;
+ this.types = types;
+ this.methods = methods;
+ this.fields = fields;
+ this.strings = strings;
+ this.callSites = callSites;
+ this.methodHandles = methodHandles;
+
+ Arrays.sort(protos);
+ setIndexes(protos);
+
+ Arrays.sort(types);
+ setIndexes(types);
+
+ Arrays.sort(methods);
+ setIndexes(methods);
+
+ Arrays.sort(fields);
+ setIndexes(fields);
+
+ Arrays.sort(strings);
+ setIndexes(strings);
+
+ // No need to sort CallSite, they will be written in data section in the callSites order,
+ // consequently offset of call site used into the call site section will be in ascending order.
+ setIndexes(callSites);
+
+ // No need to sort method handle
+ setIndexes(methodHandles);
+ }
+
+ private static DexProgramClass[] sortClasses(
+ DexApplication application, DexProgramClass[] classes) {
+ SortingProgramClassVisitor classVisitor = new SortingProgramClassVisitor(application, classes);
+ classVisitor.run(classes);
+ return classVisitor.getSortedClasses();
+ }
+
+ private void setIndexes(IndexedDexItem[] items) {
+ int index = 0;
+ for (IndexedDexItem item : items) {
+ item.assignVirtualFileIndex(virtualFileId, index++);
+ // For strings collect the first jumbo string (if any).
+ if (index > Constants.MAX_NON_JUMBO_INDEX) {
+ assert item instanceof DexString;
+ if (index == Constants.FIRST_JUMBO_INDEX) {
+ firstJumboString = (DexString) item;
+ }
+ }
+ }
+ }
+
+ public DexMethod[] getMethods() {
+ return methods;
+ }
+
+ public DexProgramClass[] getClasses() {
+ return classes;
+ }
+
+ public DexType[] getTypes() {
+ return types;
+ }
+
+ public DexProto[] getProtos() {
+ return protos;
+ }
+
+ public DexField[] getFields() {
+ return fields;
+ }
+
+ public DexString[] getStrings() {
+ return strings;
+ }
+
+ public DexCallSite[] getCallSites() {
+ return callSites;
+ }
+
+ public DexMethodHandle[] getMethodHandles() {
+ return methodHandles;
+ }
+
+ public boolean hasJumboStrings() {
+ return firstJumboString != null;
+ }
+
+ public DexString getFirstJumboString() {
+ return firstJumboString;
+ }
+
+ private boolean isContainedInMapping(IndexedDexItem item) {
+ return item.getVirtualFileIndex(virtualFileId) != IndexedDexItem.UNASSOCIATED_VALUE;
+ }
+
+ public int getOffsetFor(DexProto proto) {
+ assert isContainedInMapping(proto) : "Missing dependency: " + proto;
+ return proto.getVirtualFileIndex(virtualFileId);
+ }
+
+ public int getOffsetFor(DexField field) {
+ assert isContainedInMapping(field) : "Missing dependency: " + field;
+ return field.getVirtualFileIndex(virtualFileId);
+ }
+
+ public int getOffsetFor(DexMethod method) {
+ assert isContainedInMapping(method) : "Missing dependency: " + method;
+ return method.getVirtualFileIndex(virtualFileId);
+ }
+
+ public int getOffsetFor(DexString string) {
+ assert isContainedInMapping(string) : "Missing dependency: " + string;
+ return string.getVirtualFileIndex(virtualFileId);
+ }
+
+ public int getOffsetFor(DexType type) {
+ assert isContainedInMapping(type) : "Missing dependency: " + type;
+ return type.getVirtualFileIndex(virtualFileId);
+ }
+
+ public int getOffsetFor(DexCallSite callSite) {
+ assert isContainedInMapping(callSite) : "Missing dependency: " + callSite;
+ return callSite.getVirtualFileIndex(virtualFileId);
+ }
+
+ public int getOffsetFor(DexMethodHandle methodHandle) {
+ assert isContainedInMapping(methodHandle) : "Missing dependency: " + methodHandle;
+ return methodHandle.getVirtualFileIndex(virtualFileId);
+ }
+
+ private static class SortingProgramClassVisitor extends ProgramClassVisitor {
+ private final Set<DexClass> classSet = Sets.newIdentityHashSet();
+ private final DexProgramClass[] sortedClasses;
+
+ private int index = 0;
+
+ public SortingProgramClassVisitor(DexApplication application, DexProgramClass[] classes) {
+ super(application);
+ this.sortedClasses = new DexProgramClass[classes.length];
+ Collections.addAll(classSet, classes);
+ }
+
+ @Override
+ public void visit(DexType type) {}
+
+ @Override
+ public void visit(DexClass clazz) {
+ if (classSet.contains(clazz)) {
+ assert index < sortedClasses.length;
+ sortedClasses[index++] = (DexProgramClass) clazz;
+ }
+ }
+
+ public DexProgramClass[] getSortedClasses() {
+ assert index == sortedClasses.length;
+ return sortedClasses;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/OffsetToObjectMapping.java b/src/main/java/com/android/tools/r8/graph/OffsetToObjectMapping.java
new file mode 100644
index 0000000..5e42113
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/OffsetToObjectMapping.java
@@ -0,0 +1,176 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+public class OffsetToObjectMapping {
+
+ private DexProgramClass[] classes;
+ private DexMethod[] methods;
+ private DexProto[] protos;
+ private DexField[] fields;
+ private DexType[] types;
+ private DexString[] strings;
+ private DexCallSite[] callSites;
+ private DexMethodHandle[] methodHandles;
+
+ public void initializeClasses(int length) {
+ assert classes == null;
+ classes = new DexProgramClass[length];
+ }
+
+ public void initializeMethods(int length) {
+ assert methods == null;
+ methods = new DexMethod[length];
+ }
+
+ public void initializeProtos(int length) {
+ assert protos == null;
+ protos = new DexProto[length];
+ }
+
+ public void initializeFields(int length) {
+ assert fields == null;
+ fields = new DexField[length];
+ }
+
+ public void initializeTypes(int length) {
+ assert types == null;
+ types = new DexType[length];
+ }
+
+ public void initializeStrings(int length) {
+ assert strings == null;
+ strings = new DexString[length];
+ }
+
+ public void initializeCallSites(int length) {
+ assert callSites == null;
+ callSites = new DexCallSite[length];
+ }
+
+ public void initializeMethodHandles(int length) {
+ assert methodHandles == null;
+ methodHandles = new DexMethodHandle[length];
+ }
+
+ public DexProgramClass[] getClassMap() {
+ assert classes != null;
+ return classes;
+ }
+
+ public DexMethod[] getMethodMap() {
+ assert methods != null;
+ return methods;
+ }
+
+ public DexProto[] getProtosMap() {
+ assert protos != null;
+ return protos;
+ }
+
+ public DexField[] getFieldMap() {
+ assert fields != null;
+ return fields;
+ }
+
+ public DexType[] getTypeMap() {
+ assert types != null;
+ return types;
+ }
+
+ public DexString[] getStringMap() {
+ assert strings != null;
+ return strings;
+ }
+
+ public DexCallSite[] getCallSiteMap() {
+ assert callSites != null;
+ return callSites;
+ }
+
+ public DexMethodHandle[] getMethodHandleMap() {
+ assert methodHandles != null;
+ return methodHandles;
+ }
+
+ public DexProgramClass getClass(int index) {
+ assert classes[index] != null;
+ return classes[index];
+ }
+
+ public DexMethod getMethod(int index) {
+ assert methods[index] != null;
+ return methods[index];
+ }
+
+ public DexProto getProto(int index) {
+ assert protos[index] != null;
+ return protos[index];
+ }
+
+ public DexField getField(int index) {
+ assert fields[index] != null;
+ return fields[index];
+ }
+
+ public DexType getType(int index) {
+ assert types[index] != null;
+ return types[index];
+ }
+
+ public DexString getString(int index) {
+ assert strings[index] != null;
+ return strings[index];
+ }
+
+ public DexCallSite getCallSite(int index) {
+ assert callSites[index] != null;
+ return callSites[index];
+ }
+
+ public DexMethodHandle getMethodHandle(int index) {
+ assert methodHandles[index] != null;
+ return methodHandles[index];
+ }
+
+ public void setClass(int index, DexProgramClass clazz) {
+ assert classes[index] == null;
+ classes[index] = clazz;
+ }
+
+ public void setProto(int index, DexProto proto) {
+ assert protos[index] == null;
+ protos[index] = proto;
+ }
+
+ public void setMethod(int index, DexMethod method) {
+ assert methods[index] == null;
+ methods[index] = method;
+ }
+
+ public void setField(int index, DexField field) {
+ assert fields[index] == null;
+ fields[index] = field;
+ }
+
+ public void setType(int index, DexType type) {
+ assert types[index] == null;
+ types[index] = type;
+ }
+
+ public void setString(int index, DexString string) {
+ assert strings[index] == null;
+ strings[index] = string;
+ }
+
+ public void setCallSites(int index, DexCallSite callSite) {
+ assert callSites[index] == null;
+ callSites[index] = callSite;
+ }
+
+ public void setMethodHandle(int index, DexMethodHandle methodHandle) {
+ assert methodHandles[index] == null;
+ methodHandles[index] = methodHandle;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/Presorted.java b/src/main/java/com/android/tools/r8/graph/Presorted.java
new file mode 100644
index 0000000..475da13
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/Presorted.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+/**
+ * Interface for capturing presorted behavior for Dex items.
+ */
+public interface Presorted {
+
+ void setSortedIndex(int sortedIndex);
+
+ int getSortedIndex();
+
+ int sortedCompareTo(int other);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/PresortedComparable.java b/src/main/java/com/android/tools/r8/graph/PresortedComparable.java
new file mode 100644
index 0000000..3caf8f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/PresortedComparable.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.naming.NamingLens;
+
+public interface PresortedComparable<T> extends Presorted, Comparable<T> {
+ // Slow comparison methods that make no use of indices for comparisons. These are used
+ // for sorting operations when reading dex files.
+ int slowCompareTo(T other);
+ int slowCompareTo(T other, NamingLens namingLens);
+ // Layered comparison methods that make use of indices for subpart comparisons. These rely
+ // on subparts already being sorted and having indices assigned.
+ int layeredCompareTo(T other, NamingLens namingLens);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramClassVisitor.java b/src/main/java/com/android/tools/r8/graph/ProgramClassVisitor.java
new file mode 100644
index 0000000..826e260
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ProgramClassVisitor.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Implements traversal of the class hierarchy in topological order. A class is visited after its
+ * super class and its interfaces are visited. Only visits program classes and does NOT visit
+ * classpath, nor library classes.
+ *
+ * NOTE: The visiting is processed by traversing program classes only, which means that
+ * in presence of classpath it is NOT guaranteed that class C is visited before class D
+ * if there exists a classpath class X in class hierarchy between C and D, like:
+ *
+ * <pre>
+ * class ProgramClassS {}
+ * class ClasspathClassX extends ProgramClassS {}
+ * class ProgramClassD extends ClasspathClassX {}
+ * </pre>
+ *
+ * The above consideration does not apply to library classes, since we assume library
+ * classes never extend or implement program/classpath class.
+ */
+public abstract class ProgramClassVisitor {
+
+ final DexApplication application;
+ private final Set<DexItem> visited = new HashSet<>();
+
+ protected ProgramClassVisitor(DexApplication application) {
+ this.application = application;
+ }
+
+ private void accept(DexType type) {
+ if (type == null || visited.contains(type)) {
+ return;
+ }
+ DexClass clazz = application.programDefinitionFor(type);
+ if (clazz != null) {
+ accept(clazz);
+ return;
+ }
+ visit(type);
+ visited.add(type);
+ }
+
+ private void accept(DexTypeList types) {
+ for (DexType type : types.values) {
+ accept(type);
+ }
+ }
+
+ private void accept(DexClass clazz) {
+ if (visited.contains(clazz)) {
+ return;
+ }
+ accept(clazz.superType);
+ accept(clazz.interfaces);
+ visit(clazz);
+ visited.add(clazz);
+ }
+
+ public void run(DexProgramClass[] classes) {
+ for (DexProgramClass clazz : classes) {
+ accept(clazz);
+ }
+ }
+
+ public void run() {
+ for (DexProgramClass clazz : application.classes()) {
+ accept(clazz);
+ }
+ }
+
+ /**
+ * Called for each library class used in the class hierarchy. A library class is a class that is
+ * not present in the application.
+ */
+ public abstract void visit(DexType type);
+
+ /**
+ * Called for each class defined in the application.
+ */
+ public abstract void visit(DexClass clazz);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
new file mode 100644
index 0000000..45824c2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -0,0 +1,29 @@
+// 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.graph;
+
+public abstract class UseRegistry {
+
+ public abstract boolean registerInvokeVirtual(DexMethod method);
+
+ public abstract boolean registerInvokeDirect(DexMethod method);
+
+ public abstract boolean registerInvokeStatic(DexMethod method);
+
+ public abstract boolean registerInvokeInterface(DexMethod method);
+
+ public abstract boolean registerInvokeSuper(DexMethod method);
+
+ public abstract boolean registerInstanceFieldWrite(DexField field);
+
+ public abstract boolean registerInstanceFieldRead(DexField field);
+
+ public abstract boolean registerNewInstance(DexType type);
+
+ public abstract boolean registerStaticFieldRead(DexField field);
+
+ public abstract boolean registerStaticFieldWrite(DexField field);
+
+ public abstract boolean registerTypeReference(DexType type);
+}
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
new file mode 100644
index 0000000..ba11abb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Add.java
@@ -0,0 +1,114 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.AddDouble;
+import com.android.tools.r8.code.AddDouble2Addr;
+import com.android.tools.r8.code.AddFloat;
+import com.android.tools.r8.code.AddFloat2Addr;
+import com.android.tools.r8.code.AddInt;
+import com.android.tools.r8.code.AddInt2Addr;
+import com.android.tools.r8.code.AddIntLit16;
+import com.android.tools.r8.code.AddIntLit8;
+import com.android.tools.r8.code.AddLong;
+import com.android.tools.r8.code.AddLong2Addr;
+
+public class Add extends ArithmeticBinop {
+
+ public Add(NumericType type, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ }
+
+ @Override
+ public boolean isCommutative() {
+ return true;
+ }
+
+ public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+ return new AddInt(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+ // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+ // the first part of the result long before reading the second part of the input longs.
+ // Therefore, there can be no overlap of the second part of an input long and the first
+ // part of the output long.
+ assert dest != left + 1;
+ assert dest != right + 1;
+ return new AddLong(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateFloat(int dest, int left, int right) {
+ return new AddFloat(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateDouble(int dest, int left, int right) {
+ return new AddDouble(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+ return new AddInt2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+ return new AddLong2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateFloat2Addr(int left, int right) {
+ return new AddFloat2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateDouble2Addr(int left, int right) {
+ return new AddDouble2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+ return new AddIntLit8(dest, left, constant);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+ return new AddIntLit16(dest, left, constant);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asAdd().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asAdd().type.ordinal();
+ }
+
+ @Override
+ int foldIntegers(int left, int right) {
+ return left + right;
+ }
+
+ @Override
+ long foldLongs(long left, long right) {
+ return left + right;
+ }
+
+ @Override
+ float foldFloat(float left, float right) {
+ return left + right;
+ }
+
+ @Override
+ double foldDouble(double left, double right) {
+ return left + right;
+ }
+
+ @Override
+ public boolean isAdd() {
+ return true;
+ }
+
+ @Override
+ public Add asAdd() {
+ return this;
+ }
+}
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
new file mode 100644
index 0000000..a6e71ae
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/And.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+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;
+
+public class And extends LogicalBinop {
+
+ public And(NumericType type, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ }
+
+ @Override
+ public boolean isAnd() {
+ return true;
+ }
+
+ @Override
+ public And asAnd() {
+ return this;
+ }
+
+ @Override
+ public boolean isCommutative() {
+ return true;
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+ return new AndInt(dest, left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+ // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+ // the first part of the result long before reading the second part of the input longs.
+ // Therefore, there can be no overlap of the second part of an input long and the first
+ // part of the output long.
+ assert dest != left + 1;
+ assert dest != right + 1;
+ return new AndLong(dest, left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+ return new AndInt2Addr(left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+ return new AndLong2Addr(left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+ return new AndIntLit8(dest, left, constant);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+ return new AndIntLit16(dest, left, constant);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asAnd().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asAnd().type.ordinal();
+ }
+
+ @Override
+ int foldIntegers(int left, int right) {
+ return left & right;
+ }
+
+ @Override
+ long foldLongs(long left, long right) {
+ return left & right;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Argument.java b/src/main/java/com/android/tools/r8/ir/code/Argument.java
new file mode 100644
index 0000000..94c415e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Argument.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+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.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import com.android.tools.r8.utils.InternalOptions;
+
+/**
+ * Argument pseudo instruction used to introduce values for all arguments for SSA conversion.
+ */
+public class Argument extends Instruction {
+
+ public Argument(Value outValue) {
+ super(outValue);
+ outValue.markAsArgument();;
+ }
+
+ @Override
+ public boolean canBeDeadCode(InternalOptions options) {
+ // Never remove argument instructions. That would change the signature of the method.
+ // TODO(ager): If we can tell that a method never uses an argument we might be able to
+ // rewrite the signature and call-sites.
+ return false;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ assert false : "Argument has no register arguments.";
+ return 0;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U16BIT_MAX;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ builder.addArgument(this);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ assert other.isArgument();
+ return true;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ assert other.isArgument();
+ return 0;
+ }
+
+ @Override
+ public boolean isArgument() {
+ return true;
+ }
+
+ @Override
+ public Argument asArgument() {
+ return this;
+ }
+
+ @Override
+ public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+ return InliningConstraint.ALWAYS;
+ }
+}
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
new file mode 100644
index 0000000..e793190
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
@@ -0,0 +1,150 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public abstract class ArithmeticBinop extends Binop {
+
+ public ArithmeticBinop(NumericType type, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ }
+
+ public abstract com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right);
+
+ public abstract Instruction CreateLong(int dest, int left, int right);
+
+ public abstract Instruction CreateFloat(int dest, int left, int right);
+
+ public abstract Instruction CreateDouble(int dest, int left, int right);
+
+ public abstract Instruction CreateInt2Addr(int left, int right);
+
+ public abstract Instruction CreateLong2Addr(int left, int right);
+
+ public abstract Instruction CreateFloat2Addr(int left, int right);
+
+ public abstract Instruction CreateDouble2Addr(int left, int right);
+
+ public abstract Instruction CreateIntLit8(int dest, int left, int constant);
+
+ public abstract Instruction CreateIntLit16(int dest, int left, int constant);
+
+ @Override
+ public boolean canBeFolded() {
+ return (type == NumericType.INT || type == NumericType.LONG || type == NumericType.FLOAT
+ || type == NumericType.DOUBLE)
+ && leftValue().isConstant() && rightValue().isConstant();
+ }
+
+ public boolean needsValueInRegister(Value value) {
+ assert !isSub(); // Constants in instructions for sub must be handled in subclass Sub.
+ // Always require the left value in a register. If left and right are the same value, then
+ // both will use its register.
+ if (value == leftValue()) {
+ return true;
+ }
+ assert value == rightValue();
+ return !fitsInDexInstruction(value);
+ }
+
+ public ConstInstruction fold(ValueNumberGenerator valueNumberGenerator) {
+ assert canBeFolded();
+ if (type == NumericType.INT) {
+ int left = leftValue().getConstInstruction().asConstNumber().getIntValue();
+ int right = rightValue().getConstInstruction().asConstNumber().getIntValue();
+ int result = foldIntegers(left, right);
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+ return new ConstNumber(ConstType.INT, value, result);
+ } else if (type == NumericType.LONG) {
+ long left = leftValue().getConstInstruction().asConstNumber().getLongValue();
+ long right = rightValue().getConstInstruction().asConstNumber().getLongValue();
+ long result = foldLongs(left, right);
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.WIDE, getDebugInfo());
+ return new ConstNumber(ConstType.LONG, value, result);
+ } else if (type == NumericType.FLOAT) {
+ float left = leftValue().getConstInstruction().asConstNumber().getFloatValue();
+ float right = rightValue().getConstInstruction().asConstNumber().getFloatValue();
+ float result = foldFloat(left, right);
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+ return new ConstNumber(ConstType.FLOAT, value, Float.floatToIntBits(result));
+ } else {
+ assert type == NumericType.DOUBLE;
+ double left = leftValue().getConstInstruction().asConstNumber().getDoubleValue();
+ double right = rightValue().getConstInstruction().asConstNumber().getDoubleValue();
+ double result = foldDouble(left, right);
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.WIDE, getDebugInfo());
+ return new ConstNumber(ConstType.DOUBLE, value, Double.doubleToLongBits(result));
+ }
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ // Method needsValueInRegister ensures that left value has an allocated register.
+ int left = builder.allocatedRegister(leftValue(), getNumber());
+ int dest = builder.allocatedRegister(outValue, getNumber());
+ Instruction instruction = null;
+ if (isTwoAddr(builder)) {
+ int right = builder.allocatedRegister(rightValue(), getNumber());
+ if (left != dest) {
+ assert isCommutative();
+ assert right == dest;
+ right = left;
+ }
+ switch (type) {
+ case DOUBLE:
+ instruction = CreateDouble2Addr(dest, right);
+ break;
+ case FLOAT:
+ instruction = CreateFloat2Addr(dest, right);
+ break;
+ case INT:
+ instruction = CreateInt2Addr(dest, right);
+ break;
+ case LONG:
+ instruction = CreateLong2Addr(dest, right);
+ break;
+ }
+ } else if (!rightValue().needsRegister()) {
+ assert !isSub(); // Constants in instructions for sub must be handled in subclass Sub.
+ assert fitsInDexInstruction(rightValue());
+ ConstNumber right = rightValue().getConstInstruction().asConstNumber();
+ if (right.is8Bit()) {
+ instruction = CreateIntLit8(dest, left, right.getIntValue());
+ } else {
+ assert right.is16Bit();
+ instruction = CreateIntLit16(dest, left, right.getIntValue());
+ }
+ } else {
+ int right = builder.allocatedRegister(rightValue(), getNumber());
+ switch (type) {
+ case DOUBLE:
+ instruction = CreateDouble(dest, left, right);
+ break;
+ case FLOAT:
+ instruction = CreateFloat(dest, left, right);
+ break;
+ case INT:
+ instruction = CreateInt(dest, left, right);
+ break;
+ case LONG:
+ instruction = CreateLong(dest, left, right);
+ break;
+ }
+ }
+ builder.add(this, instruction);
+ }
+
+ @Override
+ public boolean isArithmeticBinop() {
+ return true;
+ }
+
+ @Override
+ public ArithmeticBinop asArithmeticBinop() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
new file mode 100644
index 0000000..c2cd2ea
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Aget;
+import com.android.tools.r8.code.AgetBoolean;
+import com.android.tools.r8.code.AgetByte;
+import com.android.tools.r8.code.AgetChar;
+import com.android.tools.r8.code.AgetObject;
+import com.android.tools.r8.code.AgetShort;
+import com.android.tools.r8.code.AgetWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import java.util.Arrays;
+
+public class ArrayGet extends Instruction {
+
+ private final MemberType type;
+
+ public ArrayGet(MemberType type, Value dest, Value array, Value index) {
+ super(dest, Arrays.asList(array, index));
+ this.type = type;
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ public Value array() {
+ return inValues.get(0);
+ }
+
+ public Value index() {
+ return inValues.get(1);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int dest = builder.allocatedRegister(dest(), getNumber());
+ int array = builder.allocatedRegister(array(), getNumber());
+ int index = builder.allocatedRegister(index(), getNumber());
+ com.android.tools.r8.code.Instruction instruction;
+ switch (type) {
+ case SINGLE:
+ instruction = new Aget(dest, array, index);
+ break;
+ case WIDE:
+ instruction = new AgetWide(dest, array, index);
+ break;
+ case OBJECT:
+ instruction = new AgetObject(dest, array, index);
+ break;
+ case BOOLEAN:
+ instruction = new AgetBoolean(dest, array, index);
+ break;
+ case BYTE:
+ instruction = new AgetByte(dest, array, index);
+ break;
+ case CHAR:
+ instruction = new AgetChar(dest, array, index);
+ break;
+ case SHORT:
+ instruction = new AgetShort(dest, array, index);
+ break;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ builder.add(this, instruction);
+ }
+
+ @Override
+ public boolean identicalAfterRegisterAllocation(Instruction other, RegisterAllocator allocator) {
+ // We cannot share ArrayGet instructions without knowledge of the type of the array input.
+ // If multiple primitive array types flow to the same ArrayGet instruction the art verifier
+ // gets confused.
+ return false;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asArrayGet().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asArrayGet().type.ordinal();
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ // TODO: Determine if the array index out-of-bounds exception cannot happen.
+ return true;
+ }
+
+ @Override
+ public boolean isArrayGet() {
+ return true;
+ }
+
+ @Override
+ public ArrayGet asArrayGet() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
new file mode 100644
index 0000000..40bd443
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+
+public class ArrayLength extends Instruction {
+
+ public ArrayLength(Value dest, Value array) {
+ super(dest, array);
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ public Value array() {
+ return inValues.get(0);
+ }
+
+ @Override
+ public boolean isArrayLength() {
+ return true;
+ }
+
+ @Override
+ public ArrayLength asArrayLength() {
+ return this;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int dest = builder.allocatedRegister(dest(), getNumber());
+ int array = builder.allocatedRegister(array(), getNumber());
+ builder.add(this, new com.android.tools.r8.code.ArrayLength(dest, array));
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U4BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U4BIT_MAX;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return true;
+ }
+
+ @Override
+ public boolean identicalAfterRegisterAllocation(Instruction other, RegisterAllocator allocator) {
+ if (super.identicalAfterRegisterAllocation(other, allocator)) {
+ // The array length instruction doesn't carry the element type. The art verifier doesn't
+ // allow an array length instruction into which arrays of two different base types can
+ // flow. Therefore, as a safe approximation we only consider array length instructions
+ // equal when they have the same inflowing SSA value.
+ // TODO(ager): We could perform conservative type propagation earlier in the pipeline and
+ // add a member type to array length instructions.
+ return array() == other.asArrayLength().array();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ assert other.isArrayLength();
+ return true;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ assert other.isArrayLength();
+ return 0;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
new file mode 100644
index 0000000..5e1ff16
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -0,0 +1,138 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Aput;
+import com.android.tools.r8.code.AputBoolean;
+import com.android.tools.r8.code.AputByte;
+import com.android.tools.r8.code.AputChar;
+import com.android.tools.r8.code.AputObject;
+import com.android.tools.r8.code.AputShort;
+import com.android.tools.r8.code.AputWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
+
+public class ArrayPut extends Instruction {
+
+ private final MemberType type;
+
+ public ArrayPut(MemberType type, List<Value> ins) {
+ super(null, ins);
+ assert type != null;
+ this.type = type;
+ }
+
+ public Value source() {
+ return inValues.get(0);
+ }
+
+ public Value array() {
+ return inValues.get(1);
+ }
+
+ public Value index() {
+ return inValues.get(2);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int source = builder.allocatedRegister(source(), getNumber());
+ int array = builder.allocatedRegister(array(), getNumber());
+ int index = builder.allocatedRegister(index(), getNumber());
+ com.android.tools.r8.code.Instruction instruction;
+ switch (type) {
+ case SINGLE:
+ instruction = new Aput(source, array, index);
+ break;
+ case WIDE:
+ instruction = new AputWide(source, array, index);
+ break;
+ case OBJECT:
+ instruction = new AputObject(source, array, index);
+ break;
+ case BOOLEAN:
+ instruction = new AputBoolean(source, array, index);
+ break;
+ case BYTE:
+ instruction = new AputByte(source, array, index);
+ break;
+ case CHAR:
+ instruction = new AputChar(source, array, index);
+ break;
+ case SHORT:
+ instruction = new AputShort(source, array, index);
+ break;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ builder.add(this, instruction);
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ assert false : "ArrayPut instructions define no values.";
+ return 0;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return true;
+ }
+
+ @Override
+ public boolean instructionInstanceCanThrow() {
+ if (index().isConstant() && !array().isPhi() && array().definition.isNewArrayEmpty()) {
+ Value newArraySizeValue = array().definition.asNewArrayEmpty().size();
+ if (newArraySizeValue.isConstant()) {
+ int newArraySize = newArraySizeValue.getConstInstruction().asConstNumber().getIntValue();
+ int index = index().getConstInstruction().asConstNumber().getIntValue();
+ return newArraySize <= 0 || index < 0 || newArraySize <= index;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean canBeDeadCode(InternalOptions options) {
+ // ArrayPut has side-effects on input values.
+ return false;
+ }
+
+ @Override
+ public boolean identicalAfterRegisterAllocation(Instruction other, RegisterAllocator allocator) {
+ // We cannot share ArrayPut instructions without knowledge of the type of the array input.
+ // If multiple primitive array types flow to the same ArrayPut instruction the art verifier
+ // gets confused.
+ return false;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asArrayPut().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asArrayPut().type.ordinal();
+ }
+
+ @Override
+ public boolean isArrayPut() {
+ return true;
+ }
+
+ @Override
+ public ArrayPut asArrayPut() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
new file mode 100644
index 0000000..db44f9a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -0,0 +1,1190 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Basic block abstraction.
+ */
+public class BasicBlock {
+
+ public enum ThrowingInfo {
+ NO_THROW, CAN_THROW
+ }
+
+ public enum EdgeType {
+ NON_EDGE,
+ NORMAL,
+ EXCEPTIONAL
+ }
+
+ public static class Pair implements Comparable<Pair> {
+
+ public BasicBlock first;
+ public BasicBlock second;
+
+ public Pair(BasicBlock first, BasicBlock second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ @Override
+ public int compareTo(Pair o) {
+ if (first != o.first) {
+ return first.getNumber() - o.first.getNumber();
+ }
+ if (second != o.second) {
+ return second.getNumber() - o.second.getNumber();
+ }
+ return 0;
+ }
+ }
+
+ private final List<BasicBlock> successors = new ArrayList<>();
+ private final List<BasicBlock> predecessors = new ArrayList<>();
+
+ // Catch handler information about which successors are catch handlers and what their guards are.
+ private CatchHandlers<Integer> catchHandlers = CatchHandlers.EMPTY_INDICES;
+
+ private LinkedList<Instruction> instructions = new LinkedList<>();
+ private int number = -1;
+ private List<Phi> phis = new ArrayList<>();
+
+ // State used during SSA construction. The SSA construction is based on the paper:
+ //
+ // "Simple and Efficient Construction of Static Single Assignment Form"
+ // http://compilers.cs.uni-saarland.de/papers/bbhlmz13cc.pdf
+ //
+ // A basic block is filled when local value numbering is complete for that block.
+ // A basic block is sealed when all predecessor blocks have been filled.
+ //
+ // Therefore, for a sealed block we can always search backwards to find reaching values
+ // in predecessor blocks.
+ private boolean filled = false;
+ private boolean sealed = false;
+ private Map<Integer, Phi> incompletePhis = new HashMap<>();
+ private int estimatedPredecessorsCount = 0;
+ private int unfilledPredecessorsCount = 0;
+
+ // State used for basic block sorting and tracing.
+ private int color = 0;
+
+ // Map of registers to current SSA value. Used during SSA numbering and cleared once filled.
+ private Map<Integer, Value> currentDefinitions = new HashMap<>();
+
+ public List<BasicBlock> getSuccessors() {
+ return successors;
+ }
+
+ public List<BasicBlock> getNormalSucessors() {
+ if (!hasCatchHandlers()) {
+ return successors;
+ }
+ Set<Integer> handlers = catchHandlers.getUniqueTargets();
+ ImmutableList.Builder<BasicBlock> normals = ImmutableList.builder();
+ for (int i = 0; i < successors.size(); i++) {
+ if (!handlers.contains(i)) {
+ normals.add(successors.get(i));
+ }
+ }
+ return normals.build();
+ }
+
+ public List<BasicBlock> getPredecessors() {
+ return predecessors;
+ }
+
+ public List<BasicBlock> getNormalPredecessors() {
+ ImmutableList.Builder<BasicBlock> normals = ImmutableList.builder();
+ for (BasicBlock predecessor : predecessors) {
+ if (!predecessor.isCatchSuccessor(this)) {
+ normals.add(predecessor);
+ }
+ }
+ return normals.build();
+ }
+
+ public void removeSuccessor(BasicBlock block) {
+ int index = successors.indexOf(block);
+ assert index >= 0 : "removeSuccessor did not find the successor to remove";
+ removeSuccessorsByIndex(Arrays.asList(index));
+ }
+
+ public void removePredecessor(BasicBlock block) {
+ int index = predecessors.indexOf(block);
+ assert index >= 0 : "removePredecessor did not find the predecessor to remove";
+ predecessors.remove(index);
+ if (phis != null) {
+ for (Phi phi : getPhis()) {
+ phi.removeOperand(index);
+ }
+ // Collect and remove trivial phis after block removal.
+ List<Phi> trivials = new ArrayList<>();
+ for (Phi phi : getPhis()) {
+ if (phi.isTrivialPhi()) {
+ trivials.add(phi);
+ }
+ }
+ for (Phi phi : trivials) {
+ phi.removeTrivialPhi();
+ }
+ }
+ }
+
+ private void swapSuccessors(int x, int y) {
+ assert x != y;
+ if (hasCatchHandlers()) {
+ List<Integer> targets = new ArrayList<>(catchHandlers.getAllTargets());
+ for (int i = 0; i < targets.size(); i++) {
+ if (targets.get(i) == x) {
+ targets.set(i, y);
+ } else if (targets.get(i) == y) {
+ targets.set(i, x);
+ }
+ }
+ catchHandlers = new CatchHandlers<>(catchHandlers.getGuards(), targets);
+ }
+ BasicBlock tmp = successors.get(x);
+ successors.set(x, successors.get(y));
+ successors.set(y, tmp);
+ }
+
+ public void replaceSuccessor(BasicBlock block, BasicBlock newBlock) {
+ assert successors.contains(block) : "attempt to replace non-existent successor";
+
+ if (successors.contains(newBlock)) {
+ int indexOfOldBlock = successors.indexOf(block);
+ int indexOfNewBlock = successors.indexOf(newBlock);
+
+ // Always rewrite catch handlers.
+ if (hasCatchHandlers()) {
+ List<Integer> targets = new ArrayList<>(catchHandlers.getAllTargets());
+ for (int i = 0; i < targets.size(); i++) {
+ if (targets.get(i) == indexOfOldBlock) {
+ targets.set(i, indexOfNewBlock);
+ }
+ if (targets.get(i) > indexOfOldBlock) {
+ targets.set(i, targets.get(i) - 1);
+ }
+ }
+ catchHandlers = new CatchHandlers<>(catchHandlers.getGuards(), targets);
+ }
+
+ // Check if the replacement influences jump targets and rewrite as needed.
+ if (exit().isGoto()) {
+ if (indexOfOldBlock == successors.size() - 1 && indexOfNewBlock != successors.size() - 2) {
+ // Replacing the goto target and the new block will not become the goto target.
+ // We perform a swap to get the new block into the goto target position.
+ swapSuccessors(indexOfOldBlock - 1, indexOfNewBlock);
+ }
+ } else if (exit().isIf()) {
+ if (indexOfNewBlock >= successors.size() - 2 && indexOfOldBlock >= successors.size() - 2) {
+ // New and old are true target and fallthrough, replace last instruction with a goto.
+ Instruction instruction = getInstructions().removeLast();
+ for (Value value : instruction.inValues()) {
+ if (value.hasUsersInfo()) {
+ value.removeUser(instruction);
+ }
+ }
+ Instruction exit = new Goto();
+ exit.setBlock(this);
+ getInstructions().addLast(exit);
+ } else if (indexOfOldBlock >= successors.size() - 2) {
+ // Old is either true or fallthrough and we need to swap the new block into the right
+ // position to become that target.
+ swapSuccessors(indexOfOldBlock - 1, indexOfNewBlock);
+ }
+ } else if (exit().isSwitch()) {
+ // Rewrite fallthrough and case target indices.
+ Switch exit = exit().asSwitch();
+ if (exit.getFallthroughBlockIndex() == indexOfOldBlock) {
+ exit.setFallthroughBlockIndex(indexOfNewBlock);
+ }
+ if (exit.getFallthroughBlockIndex() > indexOfOldBlock) {
+ exit.setFallthroughBlockIndex(exit.getFallthroughBlockIndex() - 1);
+ }
+ int[] indices = exit.targetBlockIndices();
+ for (int i = 0; i < indices.length; i++) {
+ if (indices[i] == indexOfOldBlock) {
+ indices[i] = indexOfNewBlock;
+ }
+ if (indices[i] > indexOfOldBlock) {
+ indices[i] = indices[i] - 1;
+ }
+ }
+ }
+
+ // Remove the replaced successor.
+ boolean removed = successors.remove(block);
+ assert removed;
+ } else {
+ // If the new block is not a successor we don't have to rewrite indices or instructions
+ // and we can just replace the old successor with the new one.
+ for (int i = 0; i < successors.size(); i++) {
+ if (successors.get(i) == block) {
+ successors.set(i, newBlock);
+ return;
+ }
+ }
+ }
+ }
+
+ public void replacePredecessor(BasicBlock block, BasicBlock newBlock) {
+ for (int i = 0; i < predecessors.size(); i++) {
+ if (predecessors.get(i) == block) {
+ predecessors.set(i, newBlock);
+ return;
+ }
+ }
+ assert false : "replaceSuccessor did not find the predecessor to replace";
+ }
+
+ public void removeSuccessorsByIndex(List<Integer> successorsToRemove) {
+ if (successorsToRemove.isEmpty()) {
+ return;
+ }
+ List<BasicBlock> copy = new ArrayList<>(successors);
+ successors.clear();
+ int current = 0;
+ for (int i : successorsToRemove) {
+ successors.addAll(copy.subList(current, i));
+ current = i + 1;
+ }
+ successors.addAll(copy.subList(current, copy.size()));
+
+ if (hasCatchHandlers()) {
+ int size = catchHandlers.size();
+ List<DexType> guards = new ArrayList<>(size);
+ List<Integer> targets = new ArrayList<>(size);
+ current = 0;
+ for (int i = 0; i < catchHandlers.getAllTargets().size(); i++) {
+ if (successorsToRemove.contains(catchHandlers.getAllTargets().get(i))) {
+ guards.addAll(catchHandlers.getGuards().subList(current, i));
+ targets.addAll(catchHandlers.getAllTargets().subList(current, i));
+ current = i + 1;
+ }
+ }
+ if (guards.isEmpty()) {
+ catchHandlers = CatchHandlers.EMPTY_INDICES;
+ } else {
+ catchHandlers = new CatchHandlers<>(guards, targets);
+ }
+ }
+ }
+
+ public void removePredecessorsByIndex(List<Integer> predecessorsToRemove) {
+ if (predecessorsToRemove.isEmpty()) {
+ return;
+ }
+ List<BasicBlock> copy = new ArrayList<>(predecessors);
+ predecessors.clear();
+ int current = 0;
+ for (int i : predecessorsToRemove) {
+ predecessors.addAll(copy.subList(current, i));
+ current = i + 1;
+ }
+ predecessors.addAll(copy.subList(current, copy.size()));
+ }
+
+ public void removePhisByIndex(List<Integer> predecessorsToRemove) {
+ for (Phi phi : phis) {
+ phi.removeOperandsByIndex(predecessorsToRemove);
+ }
+ }
+
+ public List<Phi> getPhis() {
+ return phis;
+ }
+
+ public void setPhis(List<Phi> phis) {
+ this.phis = phis;
+ }
+
+ public boolean isFilled() {
+ return filled;
+ }
+
+ void setFilledForTesting() {
+ filled = true;
+ }
+
+ public boolean hasCatchHandlers() {
+ assert catchHandlers != null;
+ return !catchHandlers.isEmpty();
+ }
+
+ public int getNumber() {
+ assert number >= 0;
+ return number;
+ }
+
+ public void setNumber(int number) {
+ assert number >= 0;
+ this.number = number;
+ }
+
+ public LinkedList<Instruction> getInstructions() {
+ return instructions;
+ }
+
+ public Instruction entry() {
+ return instructions.get(0);
+ }
+
+ public JumpInstruction exit() {
+ assert filled;
+ assert instructions.get(instructions.size() - 1).isJumpInstruction();
+ return instructions.get(instructions.size() - 1).asJumpInstruction();
+ }
+
+ public void clearUserInfo() {
+ phis = null;
+ instructions.forEach(Instruction::clearUserInfo);
+ }
+
+ public void buildDex(DexBuilder builder) {
+ for (Instruction instruction : instructions) {
+ instruction.buildDex(builder);
+ }
+ }
+
+ public void mark() {
+ assert color == 0;
+ color = 1;
+ }
+
+ public void clearMark() {
+ color = 0;
+ }
+
+ public boolean isMarked() {
+ return color == 1;
+ }
+
+ public void setColor(int color) {
+ this.color = color;
+ }
+
+ public int getColor() {
+ return color;
+ }
+
+ public boolean hasColor(int color) {
+ return this.color == color;
+ }
+
+ public void incrementUnfilledPredecessorCount() {
+ ++unfilledPredecessorsCount;
+ ++estimatedPredecessorsCount;
+ }
+
+ public void decrementUnfilledPredecessorCount(int n) {
+ unfilledPredecessorsCount -= n;
+ estimatedPredecessorsCount -= n;
+ }
+
+ public void decrementUnfilledPredecessorCount() {
+ --unfilledPredecessorsCount;
+ --estimatedPredecessorsCount;
+ }
+
+ public boolean verifyFilledPredecessors() {
+ assert estimatedPredecessorsCount == predecessors.size();
+ assert unfilledPredecessorsCount == 0;
+ return true;
+ }
+
+ public void addPhi(Phi phi) {
+ phis.add(phi);
+ }
+
+ public void removePhi(Phi phi) {
+ phis.remove(phi);
+ }
+
+ public void add(Instruction next) {
+ assert !isFilled();
+ instructions.add(next);
+ next.setBlock(this);
+ }
+
+ public void close(IRBuilder builder) {
+ assert !isFilled();
+ assert !instructions.isEmpty();
+ filled = true;
+ sealed = unfilledPredecessorsCount == 0;
+ assert exit().isJumpInstruction();
+ assert verifyNoValuesAfterThrowingInstruction();
+ for (BasicBlock successor : successors) {
+ successor.filledPredecessor(builder);
+ }
+ }
+
+ public void link(BasicBlock successor) {
+ assert !successors.contains(successor);
+ assert !successor.predecessors.contains(this);
+ successors.add(successor);
+ successor.predecessors.add(this);
+ }
+
+ private boolean allPredecessorsDominated(BasicBlock block, DominatorTree dominator) {
+ for (BasicBlock pred : block.predecessors) {
+ if (!dominator.dominatedBy(pred, block)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean blocksClean(List<BasicBlock> blocks) {
+ blocks.forEach((b) -> {
+ assert b.predecessors.size() == 0;
+ assert b.successors.size() == 0;
+ });
+ return true;
+ }
+
+ /**
+ * Unlinks this block from a single predecessor.
+ *
+ * @return returns the unlinked predecessor.
+ */
+ public BasicBlock unlinkSinglePredecessor() {
+ assert predecessors.size() == 1;
+ assert predecessors.get(0).successors.size() == 1;
+ BasicBlock unlinkedBlock = predecessors.get(0);
+ predecessors.get(0).successors.clear();
+ predecessors.clear();
+ return unlinkedBlock;
+ }
+
+ /**
+ * Unlinks this block from a single normal successor.
+ *
+ * @return Returns the unlinked successor.
+ */
+ public BasicBlock unlinkSingleSuccessor() {
+ assert !hasCatchHandlers();
+ assert successors.size() == 1;
+ assert successors.get(0).predecessors.size() == 1;
+ BasicBlock unlinkedBlock = successors.get(0);
+ successors.get(0).predecessors.clear();
+ successors.clear();
+ return unlinkedBlock;
+ }
+
+ /**
+ * Unlinks this block from a single predecessor and successor.
+ *
+ * @return Returns the unlinked successor
+ */
+ public BasicBlock unlinkSingle() {
+ unlinkSinglePredecessor();
+ return unlinkSingleSuccessor();
+ }
+
+ public List<BasicBlock> unlink(BasicBlock successor, DominatorTree dominator) {
+ assert successors.contains(successor);
+ assert successor.predecessors.contains(this);
+ List<BasicBlock> removedBlocks = new ArrayList<>();
+ TreeSet<Pair> worklist = new TreeSet<>();
+ worklist.add(new Pair(this, successor));
+ while (!worklist.isEmpty()) {
+ Pair pair = worklist.pollFirst();
+ BasicBlock pred = pair.first;
+ BasicBlock succ = pair.second;
+ assert pred.successors.contains(succ);
+ assert succ.predecessors.contains(pred);
+ int size = pred.successors.size();
+ pred.removeSuccessor(succ);
+ assert size == pred.successors.size() + 1;
+ size = succ.predecessors.size();
+ succ.removePredecessor(pred);
+ assert size == succ.predecessors.size() + 1;
+ // A predecessor has been removed. If all remaining predecessors are dominated by this block
+ // schedule it for removal, as it is no longer reachable.
+ if (allPredecessorsDominated(succ, dominator)) {
+ removedBlocks.add(succ);
+ for (BasicBlock block : succ.successors) {
+ worklist.add(new Pair(succ, block));
+ }
+ for (Instruction instruction : succ.getInstructions()) {
+ for (Value value : instruction.inValues) {
+ value.removeUser(instruction);
+ }
+ Value previousLocalValue = instruction.getPreviousLocalValue();
+ if (previousLocalValue != null) {
+ previousLocalValue.removeDebugUser(instruction);
+ }
+ }
+ }
+ }
+ assert blocksClean(removedBlocks);
+ return removedBlocks;
+ }
+
+ public void linkCatchSuccessors(List<DexType> guards, List<BasicBlock> targets) {
+ List<Integer> successorIndexes = new ArrayList<>(targets.size());
+ for (BasicBlock target : targets) {
+ int index = successors.indexOf(target);
+ if (index < 0) {
+ index = successors.size();
+ link(target);
+ }
+ successorIndexes.add(index);
+ }
+ catchHandlers = new CatchHandlers<>(guards, successorIndexes);
+ }
+
+ public void clearCurrentDefinitions() {
+ currentDefinitions = null;
+ for (Phi phi : getPhis()) {
+ phi.clearDefinitionsUsers();
+ }
+ }
+
+ // The proper incoming register for a catch successor (that is otherwise shadowed by the out-value
+ // of a throwing instruction) is stored at the negative register-index in the definitions map.
+ // (See readCurrentDefinition/writeCurrentDefinition/updateCurrentDefinition).
+ private int onThrowValueRegister(int register) {
+ return -(register + 1);
+ }
+
+ private Value readOnThrowValue(int register, EdgeType readingEdge) {
+ if (readingEdge == EdgeType.EXCEPTIONAL) {
+ return currentDefinitions.get(onThrowValueRegister(register));
+ }
+ return null;
+ }
+
+ private boolean isOnThrowValue(int register, EdgeType readingEdge) {
+ return readOnThrowValue(register, readingEdge) != null;
+ }
+
+ public Value readCurrentDefinition(int register, EdgeType readingEdge) {
+ // If the block reading the current definition is a catch successor, then we must return the
+ // previous value of the throwing-instructions outgoing register if any.
+ Value result = readOnThrowValue(register, readingEdge);
+ if (result != null) {
+ return result == Value.UNDEFINED ? null : result;
+ }
+ return currentDefinitions.get(register);
+ }
+
+ public void updateCurrentDefinition(int register, Value value, EdgeType readingEdge) {
+ // If the reading/writing block is a catch successor, possibly update the on-throw value.
+ if (isOnThrowValue(register, readingEdge)) {
+ register = onThrowValueRegister(register);
+ }
+ // We keep track of all users of phis so that we can update all users during
+ // trivial phi elimination. We only rewrite phi values during IR construction, so
+ // we only need to record definition users for phis.
+ Value previousValue = currentDefinitions.get(register);
+ if (value.isPhi()) {
+ value.asPhi().addDefinitionsUser(currentDefinitions);
+ }
+ assert verifyOnThrowWrite(register);
+ currentDefinitions.put(register, value);
+ // We have replaced one occurrence of value in currentDefinitions. There could be
+ // other occurrences. We only remove currentDefinitions from the set of users
+ // of the phi if we have removed all occurrences.
+ if (previousValue != null &&
+ previousValue.isPhi() &&
+ !currentDefinitions.values().contains(previousValue)) {
+ previousValue.asPhi().removeDefinitionsUser(currentDefinitions);
+ }
+ }
+
+ public void writeCurrentDefinition(int register, Value value, ThrowingInfo throwing) {
+ // If this write is dependent on not throwing, we move the existing value to its negative index
+ // so that it can be read by catch successors.
+ if (throwing == ThrowingInfo.CAN_THROW) {
+ Value previous = currentDefinitions.get(register);
+ assert verifyOnThrowWrite(register);
+ currentDefinitions.put(onThrowValueRegister(register),
+ previous == null ? Value.UNDEFINED : previous);
+ }
+ updateCurrentDefinition(register, value, EdgeType.NON_EDGE);
+ }
+
+ public void filledPredecessor(IRBuilder builder) {
+ assert unfilledPredecessorsCount > 0;
+ if (--unfilledPredecessorsCount == 0) {
+ assert estimatedPredecessorsCount == predecessors.size();
+ for (Phi phi : incompletePhis.values()) {
+ phi.addOperands(builder);
+ }
+ sealed = true;
+ incompletePhis.clear();
+ }
+ }
+
+ public EdgeType getEdgeType(BasicBlock successor) {
+ assert successors.indexOf(successor) >= 0;
+ return isCatchSuccessor(successor) ? EdgeType.EXCEPTIONAL : EdgeType.NORMAL;
+ }
+
+ public boolean isCatchSuccessor(BasicBlock block) {
+ if (!hasCatchHandlers()) {
+ return false;
+ }
+ return catchHandlers.getAllTargets().contains(successors.indexOf(block));
+ }
+
+ public int guardsForCatchSuccessor(BasicBlock block) {
+ assert isCatchSuccessor(block);
+ int index = successors.indexOf(block);
+ int count = 0;
+ for (int handler : catchHandlers.getAllTargets()) {
+ if (handler == index) {
+ count++;
+ }
+ }
+ assert count > 0;
+ return count;
+ }
+
+ public boolean isSealed() {
+ return sealed;
+ }
+
+ public void addIncompletePhi(int register, Phi phi, EdgeType readingEdge) {
+ if (isOnThrowValue(register, readingEdge)) {
+ register = onThrowValueRegister(register);
+ }
+ assert !incompletePhis.containsKey(register);
+ incompletePhis.put(register, phi);
+ }
+
+ public boolean hasIncompletePhis() {
+ return !incompletePhis.isEmpty();
+ }
+
+ public Collection<Integer> getIncompletePhiRegisters() {
+ return incompletePhis.keySet();
+ }
+
+ private void appendBasicBlockList(
+ StringBuilder builder, List<BasicBlock> list, Function<BasicBlock, String> postfix) {
+ if (list.size() > 0) {
+ for (BasicBlock block : list) {
+ builder.append(block.getNumber());
+ builder.append(postfix.apply(block));
+ builder.append(" ");
+ }
+ } else {
+ builder.append("-");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return toDetailedString();
+ }
+
+ public String toSimpleString() {
+ return number < 0 ? super.toString() : ("block " + number);
+ }
+
+ private String predecessorPostfix(BasicBlock block) {
+ if (isCatchSuccessor(block)) {
+ return new String(new char[guardsForCatchSuccessor(block)]).replace("\0", "*");
+ }
+ return "";
+ }
+
+ public String toDetailedString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("block ");
+ builder.append(number);
+ builder.append(" (");
+ builder.append(System.identityHashCode(this));
+ builder.append(")");
+ builder.append(", pred-counts: " + predecessors.size());
+ if (unfilledPredecessorsCount > 0) {
+ builder.append(" (" + unfilledPredecessorsCount + " unfilled)");
+ }
+ builder.append(", succ-count: " + successors.size());
+ builder.append(", filled: " + isFilled());
+ builder.append(", sealed: " + isSealed());
+ builder.append("\n");
+ builder.append("predecessors: ");
+ appendBasicBlockList(builder, predecessors, b -> "");
+ builder.append("\n");
+ builder.append("successors: ");
+ appendBasicBlockList(builder, successors, this::predecessorPostfix);
+ if (successors.size() > 0) {
+ builder.append(" (");
+ if (hasCatchHandlers()) {
+ builder.append(catchHandlers.size());
+ } else {
+ builder.append("no");
+ }
+ builder.append(" try/catch successors)");
+ }
+ builder.append("\n");
+ if (phis != null && phis.size() > 0) {
+ for (Phi phi : phis) {
+ builder.append(phi.printPhi());
+ if (incompletePhis.values().contains(phi)) {
+ builder.append(" (incomplete)");
+ }
+ builder.append("\n");
+ }
+ } else {
+ builder.append("no phis\n");
+ }
+ for (Instruction instruction : instructions) {
+ StringUtils.appendLeftPadded(builder, Integer.toString(instruction.getNumber()), 6);
+ builder.append(": ");
+ StringUtils.appendRightPadded(builder, instruction.toString(), 20);
+ builder.append("\n");
+ }
+ return builder.toString();
+ }
+
+ public void print(CfgPrinter printer) {
+ printer.begin("block");
+ printer.print("name \"B").append(number).append("\"\n");
+ printer.print("from_bci -1\n");
+ printer.print("to_bci -1\n");
+ printer.print("predecessors");
+ printBlockList(printer, predecessors);
+ printer.ln();
+ printer.print("successors");
+ printBlockList(printer, successors);
+ printer.ln();
+ printer.print("xhandlers\n");
+ printer.print("flags\n");
+ printer.print("first_lir_id ").print(instructions.get(0).getNumber()).ln();
+ printer.print("last_lir_id ").print(instructions.get(instructions.size() - 1).getNumber()).ln();
+ printer.begin("HIR");
+ if (phis != null) {
+ for (Phi phi : phis) {
+ phi.print(printer);
+ printer.append(" <|@\n");
+ }
+ }
+ for (Instruction instruction : instructions) {
+ instruction.print(printer);
+ printer.append(" <|@\n");
+ }
+ printer.end("HIR");
+ printer.begin("LIR");
+ for (Instruction instruction : instructions) {
+ instruction.printLIR(printer);
+ printer.append(" <|@\n");
+ }
+ printer.end("LIR");
+ printer.end("block");
+ }
+
+ private static void printBlockList(CfgPrinter printer, List<BasicBlock> blocks) {
+ for (BasicBlock block : blocks) {
+ printer.append(" \"B").append(block.number).append("\"");
+ }
+ }
+
+ public void addPhiMove(Move move) {
+ // TODO(ager): Consider this more, is it always the case that we should add it before the
+ // exit instruction?
+ Instruction branch = exit();
+ instructions.set(instructions.size() - 1, move);
+ instructions.add(branch);
+ }
+
+ public void setInstructions(LinkedList<Instruction> instructions) {
+ this.instructions = instructions;
+ }
+
+ /**
+ * Remove a number of instructions. The instructions to remove are given as indexes in the
+ * instruction stream.
+ */
+ public void removeInstructions(List<Integer> toRemove) {
+ if (!toRemove.isEmpty()) {
+ LinkedList<Instruction> newInstructions = new LinkedList<>();
+ int nextIndex = 0;
+ for (Integer index : toRemove) {
+ assert index >= nextIndex; // Indexes in toRemove must be sorted ascending.
+ newInstructions.addAll(instructions.subList(nextIndex, index));
+ instructions.get(index).clearBlock();
+ nextIndex = index + 1;
+ }
+ if (nextIndex < instructions.size()) {
+ newInstructions.addAll(instructions.subList(nextIndex, instructions.size()));
+ }
+ assert instructions.size() == newInstructions.size() + toRemove.size();
+ setInstructions(newInstructions);
+ }
+ }
+
+ /**
+ * Create a new basic block with a single goto instruction.
+ *
+ * <p>The constructed basic block has no predecessors and has one
+ * successors which is the target block.
+ *
+ * @param target the target of the goto block
+ * @param blockNumber the block number of the goto block
+ */
+ public static BasicBlock createGotoBlock(BasicBlock target, int blockNumber) {
+ BasicBlock block = createGotoBlock(blockNumber);
+ block.getSuccessors().add(target);
+ return block;
+ }
+
+ /**
+ * Create a new basic block with a single goto instruction.
+ *
+ * <p>The constructed basic block has no predecessors and no successors.
+ *
+ * @param blockNumber the block number of the goto block
+ */
+ public static BasicBlock createGotoBlock(int blockNumber) {
+ BasicBlock block = new BasicBlock();
+ block.add(new Goto());
+ block.close(null);
+ block.setNumber(blockNumber);
+ return block;
+ }
+
+ public boolean isTrivialGoto() {
+ return instructions.size() == 1 && exit().isGoto();
+ }
+
+ public boolean hasOneNormalExit() {
+ return successors.size() == 1 && exit().isGoto();
+ }
+
+ public CatchHandlers<BasicBlock> getCatchHandlers() {
+ if (!hasCatchHandlers()) {
+ return CatchHandlers.EMPTY_BASIC_BLOCK;
+ }
+ List<BasicBlock> targets = ListUtils.map(catchHandlers.getAllTargets(), successors::get);
+ return new CatchHandlers<>(catchHandlers.getGuards(), targets);
+ }
+
+ public CatchHandlers<Integer> getCatchHandlersWithSuccessorIndexes() {
+ return catchHandlers;
+ }
+
+ public void clearCatchHandlers() {
+ catchHandlers = CatchHandlers.EMPTY_INDICES;
+ }
+
+ public void transferCatchHandlers(BasicBlock other) {
+ catchHandlers = other.catchHandlers;
+ other.catchHandlers = CatchHandlers.EMPTY_INDICES;
+ }
+
+ public boolean canThrow() {
+ for (Instruction instruction : instructions) {
+ if (instruction.instructionTypeCanThrow()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // A block can have at most one "on throw" value.
+ private boolean verifyOnThrowWrite(int register) {
+ if (register >= 0) {
+ return true;
+ }
+ for (Integer other : currentDefinitions.keySet()) {
+ assert other >= 0 || other == register;
+ }
+ return true;
+ }
+
+ // Verify that if this block has a throwing instruction none of the following instructions may
+ // introduce an SSA value. Such values can lead to invalid uses since the values are not actually
+ // visible to exceptional successors.
+ private boolean verifyNoValuesAfterThrowingInstruction() {
+ if (hasCatchHandlers()) {
+ ListIterator<Instruction> iterator = listIterator(instructions.size());
+ while (iterator.hasPrevious()) {
+ Instruction instruction = iterator.previous();
+ if (instruction.instructionTypeCanThrow()) {
+ return true;
+ }
+ assert instruction.outValue() == null;
+ }
+ }
+ return true;
+ }
+
+ public InstructionIterator iterator() {
+ return new BasicBlockInstructionIterator(this);
+ }
+
+ public InstructionListIterator listIterator() {
+ return new BasicBlockInstructionIterator(this);
+ }
+
+ public InstructionListIterator listIterator(int index) {
+ return new BasicBlockInstructionIterator(this, index);
+ }
+
+ /**
+ * Creates a new empty block as a successor for this block.
+ *
+ * The new block will have all the normal successors of the original block.
+ *
+ * The catch successors are either on the original block or the new block depending on the
+ * value of <code>keepCatchHandlers</code>.
+ *
+ * The current block still has all the instructions, and the new block is empty instruction-wise.
+ *
+ * @param blockNumber block number for new block
+ * @param keepCatchHandlers keep catch successors on the original block
+ * @return the new block
+ */
+ BasicBlock createSplitBlock(int blockNumber, boolean keepCatchHandlers) {
+ boolean hadCatchHandlers = hasCatchHandlers();
+ BasicBlock newBlock = new BasicBlock();
+ newBlock.setNumber(blockNumber);
+
+ // Copy all successors including catch handlers to the new block, and update predecessors.
+ successors.forEach(newBlock.successors::add);
+ for (BasicBlock successor : newBlock.getSuccessors()) {
+ successor.replacePredecessor(this, newBlock);
+ }
+ successors.clear();
+ newBlock.catchHandlers = catchHandlers;
+ catchHandlers = CatchHandlers.EMPTY_INDICES;
+
+ // If the catch handlers should be kept on the original block move them back.
+ if (keepCatchHandlers && hadCatchHandlers) {
+ moveCatchHandlers(newBlock);
+ }
+
+ // Link the two blocks
+ link(newBlock);
+
+ // Mark the new block filled and sealed.
+ newBlock.filled = true;
+ newBlock.sealed = true;
+
+ return newBlock;
+ }
+
+ /**
+ * Moves catch successors from `fromBlock` into this block.
+ */
+ public void moveCatchHandlers(BasicBlock fromBlock) {
+ List<BasicBlock> catchSuccessors = appendCatchHandlers(fromBlock);
+ for (BasicBlock successor : catchSuccessors) {
+ fromBlock.successors.remove(successor);
+ successor.removePredecessor(fromBlock);
+ }
+ fromBlock.catchHandlers = CatchHandlers.EMPTY_INDICES;
+ }
+
+ /**
+ * Clone catch successors from `fromBlock` into this block.
+ */
+ public void copyCatchHandlers(
+ IRCode code, ListIterator<BasicBlock> blockIterator, BasicBlock fromBlock) {
+ if (catchHandlers != null && catchHandlers.hasCatchAll()) {
+ return;
+ }
+ List<BasicBlock> catchSuccessors = appendCatchHandlers(fromBlock);
+
+ // After cloning is done all catch handler targets are referenced from both the
+ // original and the newly created catch handlers. Thus, since we keep both of
+ // them, we need to split appropriate edges to make sure every catch handler
+ // target block has only one predecessor.
+ //
+ // Note that for each catch handler block target block we actually create two new blocks:
+ // a copy of the original block and a new block to serve as a merging point for
+ // the original and its copy. This actually simplifies things since we only need
+ // one new phi to merge the two exception values, and all other phis don't need
+ // to be changed.
+ for (BasicBlock catchSuccessor : catchSuccessors) {
+ catchSuccessor.splitCriticalExceptioEdges(
+ code.valueNumberGenerator,
+ newBlock -> {
+ newBlock.setNumber(code.blocks.size());
+ blockIterator.add(newBlock);
+ });
+ }
+ }
+
+ private boolean allPredecessorsHaveCatchEdges() {
+ for (BasicBlock predecessor : getPredecessors()) {
+ if (!predecessor.isCatchSuccessor(this)) {
+ assert false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Assumes that `this` block is a catch handler target (note that it does not have to
+ * start with MoveException instruction, since the instruction can be removed by
+ * optimizations like dead code remover.
+ *
+ * Introduces new blocks on all incoming edges and clones MoveException instruction to
+ * these blocks if it exists. All exception values introduced in newly created blocks
+ * are combined in a phi added to `this` block.
+ *
+ * Note that if there are any other phis defined on this block, they remain valid, since
+ * this method does not affect incoming edges in any way, and just adds new blocks with
+ * MoveException and Goto.
+ *
+ * NOTE: onNewBlock must assign block number to the newly created block.
+ */
+ public void splitCriticalExceptioEdges(
+ ValueNumberGenerator valueNumberGenerator, Consumer<BasicBlock> onNewBlock) {
+ assert allPredecessorsHaveCatchEdges();
+
+ List<BasicBlock> predecessors = this.getPredecessors();
+ boolean hasMoveException = entry().isMoveException();
+ MoveException move = null;
+ if (hasMoveException) {
+ // Remove the move-exception instruction.
+ move = entry().asMoveException();
+ assert move.getPreviousLocalValue() == null;
+ this.getInstructions().remove(0);
+ }
+ // Create new predecessor blocks.
+ List<BasicBlock> newPredecessors = new ArrayList<>();
+ List<Value> values = new ArrayList<>(predecessors.size());
+ for (BasicBlock predecessor : predecessors) {
+ BasicBlock newBlock = new BasicBlock();
+ newPredecessors.add(newBlock);
+ if (hasMoveException) {
+ Value value = new Value(
+ valueNumberGenerator.next(), -1, MoveType.OBJECT, move.getDebugInfo());
+ values.add(value);
+ newBlock.add(new MoveException(value));
+ }
+ newBlock.add(new Goto());
+ newBlock.close(null);
+ newBlock.getSuccessors().add(this);
+ newBlock.getPredecessors().add(predecessor);
+ predecessor.replaceSuccessor(this, newBlock);
+ onNewBlock.accept(newBlock);
+ assert newBlock.getNumber() >= 0 : "Number must be assigned by `onNewBlock`";
+ }
+ // Replace the blocks predecessors with the new ones.
+ predecessors.clear();
+ predecessors.addAll(newPredecessors);
+ // Insert a phi for the move-exception value.
+ if (hasMoveException) {
+ Phi phi = new Phi(valueNumberGenerator.next(),
+ -1, this, MoveType.OBJECT, move.getLocalInfo());
+ phi.addOperands(values);
+ move.outValue().replaceUsers(phi);
+ }
+ }
+
+ /** Append catch handlers from another block <code>fromBlock</code> (which must have catch
+ * handlers) to the catch handlers of this block.
+ *
+ * Note that after appending catch handlers their targets are referenced by both
+ * <code>fromBlock</code> and <code>this</code> block, but no phis are inserted. For this reason
+ * this method should only be called from either {@link #moveCatchHandlers} or
+ * {@link #copyCatchHandlers} which know how to handle phis.
+ *
+ * @returns the catch successors that are reused in both blocks after appending.
+ */
+ private List<BasicBlock> appendCatchHandlers(BasicBlock fromBlock) {
+ assert fromBlock.hasCatchHandlers();
+
+ List<Integer> prevCatchTargets = fromBlock.catchHandlers.getAllTargets();
+ List<DexType> prevCatchGuards = fromBlock.catchHandlers.getGuards();
+ List<BasicBlock> catchSuccessors = new ArrayList<>();
+ List<DexType> newCatchGuards = new ArrayList<>();
+ List<Integer> newCatchTargets = new ArrayList<>();
+
+ // First add existing catch handlers to the catch handler list.
+ if (hasCatchHandlers()) {
+ newCatchGuards.addAll(catchHandlers.getGuards());
+ newCatchTargets.addAll(catchHandlers.getAllTargets());
+ for (int newCatchTarget : newCatchTargets) {
+ BasicBlock catchSuccessor = successors.get(newCatchTarget);
+ if (!catchSuccessors.contains(catchSuccessor)) {
+ catchSuccessors.add(catchSuccessor);
+ }
+ int index = catchSuccessors.indexOf(catchSuccessor);
+ assert index == newCatchTarget;
+ }
+ }
+
+ // This is the number of catch handlers which are already successors of this block.
+ int formerCatchHandlersCount = catchSuccessors.size();
+
+ // Then add catch handlers from the other block to the catch handler list.
+ for (int i = 0; i < prevCatchTargets.size(); i++) {
+ int prevCatchTarget = prevCatchTargets.get(i);
+ DexType prevCatchGuard = prevCatchGuards.get(i);
+ // TODO(sgjesse): Check sub-types of guards. Will require AppInfoWithSubtyping.
+ BasicBlock catchSuccessor = fromBlock.successors.get(prevCatchTarget);
+ // We assume that all the catch handlers targets has only one
+ // predecessor and, thus, no phis.
+ assert catchSuccessor.getPredecessors().size() == 1;
+ assert catchSuccessor.getPhis().isEmpty();
+
+ int index = catchSuccessors.indexOf(catchSuccessor);
+ if (index == -1) {
+ catchSuccessors.add(catchSuccessor);
+ index = catchSuccessors.size() - 1;
+ }
+ newCatchGuards.add(prevCatchGuard);
+ newCatchTargets.add(index);
+ }
+
+ // Create the new successors list and link things up.
+ List<BasicBlock> formerSuccessors = new ArrayList<>(successors);
+ successors.clear();
+ List<BasicBlock> sharedCatchSuccessors = new ArrayList<>();
+ for (int i = 0; i < catchSuccessors.size(); i++) {
+ if (i < formerCatchHandlersCount) {
+ // Former catch successors are just copied, as they are already linked.
+ assert catchSuccessors.get(i).getPredecessors().contains(this);
+ successors.add(catchSuccessors.get(i));
+ } else {
+ // New catch successors are linked properly.
+ assert !catchSuccessors.get(i).getPredecessors().contains(this);
+ link(catchSuccessors.get(i));
+ sharedCatchSuccessors.add(catchSuccessors.get(i));
+ }
+ }
+ catchHandlers = new CatchHandlers<>(newCatchGuards, newCatchTargets);
+
+ // Finally add the normal successor if any.
+ int catchSuccessorsCount = successors.size();
+ for (BasicBlock formerSuccessor : formerSuccessors) {
+ if (!successors.contains(formerSuccessor)) {
+ assert !exit().isThrow();
+ successors.add(formerSuccessor);
+ }
+ }
+ assert successors.size() == catchSuccessorsCount || !exit().isThrow();
+
+ return sharedCatchSuccessors;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
new file mode 100644
index 0000000..2ab2a4f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -0,0 +1,397 @@
+// 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.ir.code;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+public class BasicBlockInstructionIterator implements InstructionIterator, InstructionListIterator {
+
+ protected final BasicBlock block;
+ protected final ListIterator<Instruction> listIterator;
+ protected Instruction current;
+
+ protected BasicBlockInstructionIterator(BasicBlock block) {
+ this.block = block;
+ this.listIterator = block.getInstructions().listIterator();
+ }
+
+ protected BasicBlockInstructionIterator(BasicBlock block, int index) {
+ this.block = block;
+ this.listIterator = block.getInstructions().listIterator(index);
+ }
+
+ @Override
+ public boolean hasNext() {
+ return listIterator.hasNext();
+ }
+
+ @Override
+ public Instruction next() {
+ current = listIterator.next();
+ return current;
+ }
+
+ @Override
+ public int nextIndex() {
+ return listIterator.nextIndex();
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ return listIterator.hasPrevious();
+ }
+
+ @Override
+ public Instruction previous() {
+ current = listIterator.previous();
+ return current;
+ }
+
+ @Override
+ public int previousIndex() {
+ return listIterator.previousIndex();
+ }
+
+ /**
+ * Adds an instruction to the block. The instruction will be added just before the current
+ * cursor position.
+ *
+ * The instruction will be assigned to the block it is added to.
+ *
+ * @param instruction The instruction to add.
+ */
+ @Override
+ public void add(Instruction instruction) {
+ instruction.setBlock(block);
+ assert instruction.getBlock() == block;
+ listIterator.add(instruction);
+ }
+
+ /**
+ * Replaces the last instruction returned by {@link #next} or {@link #previous} with the
+ * specified instruction.
+ *
+ * The instruction will be assigned to the block it is added to.
+ *
+ * @param instruction The instruction to replace with.
+ */
+ @Override
+ public void set(Instruction instruction) {
+ instruction.setBlock(block);
+ assert instruction.getBlock() == block;
+ listIterator.set(instruction);
+ }
+
+ /**
+ * Remove the current instruction (aka the {@link Instruction} returned by the previous call to
+ * {@link #next}.
+ *
+ * The current instruction will be completely detached from the instruction stream with uses
+ * of its in-values removed.
+ *
+ * If the current instruction produces an out-value this out value must not have any users.
+ */
+ @Override
+ public void remove() {
+ if (current == null) {
+ throw new IllegalStateException();
+ }
+ assert current.outValue() == null || current.outValue().numberOfAllUsers() == 0;
+ for (int i = 0; i < current.inValues().size(); i++) {
+ Value value = current.inValues().get(i);
+ value.removeUser(current);
+ }
+ Value previousLocalValue = current.getPreviousLocalValue();
+ if (previousLocalValue != null) {
+ previousLocalValue.removeDebugUser(current);
+ }
+ listIterator.remove();
+ current = null;
+ }
+
+ @Override
+ public void detach() {
+ if (current == null) {
+ throw new IllegalStateException();
+ }
+ listIterator.remove();
+ current = null;
+ }
+
+ @Override
+ public void replaceCurrentInstruction(Instruction newInstruction) {
+ if (current == null) {
+ throw new IllegalStateException();
+ }
+ for (Value value : current.inValues()) {
+ value.removeUser(current);
+ }
+ if (current.outValue() != null) {
+ assert newInstruction.outValue() != null;
+ current.outValue().replaceUsers(newInstruction.outValue());
+ }
+ newInstruction.setBlock(block);
+ listIterator.remove();
+ listIterator.add(newInstruction);
+ current.clearBlock();
+ }
+
+ private BasicBlock peekPrevious(ListIterator<BasicBlock> blocksIterator) {
+ BasicBlock block = blocksIterator.previous();
+ blocksIterator.next();
+ return block;
+ }
+
+ public BasicBlock split(IRCode code, ListIterator<BasicBlock> blocksIterator) {
+ List<BasicBlock> blocks = code.blocks;
+ assert blocksIterator == null || peekPrevious(blocksIterator) == block;
+
+ int blockNumber = blocks.size();
+ BasicBlock newBlock;
+
+ // Don't allow splitting after the last instruction.
+ assert hasNext();
+
+ // Prepare the new block, placing the exception handlers on the block with the throwing
+ // instruction.
+ boolean keepCatchHandlers = hasPrevious() && peekPrevious().instructionTypeCanThrow();
+ newBlock = block.createSplitBlock(blockNumber, keepCatchHandlers);
+
+ // Add a goto instruction.
+ Goto newGoto = new Goto(block);
+ listIterator.add(newGoto);
+
+ // Move all remaining instructions to the new block.
+ while (listIterator.hasNext()) {
+ Instruction instruction = listIterator.next();
+ newBlock.getInstructions().addLast(instruction);
+ instruction.setBlock(newBlock);
+ listIterator.remove();
+ }
+
+ // If splitting the normal exit block, the new block is now the normal exit block.
+ if (code.getNormalExitBlock() == block) {
+ code.setNormalExitBlock(newBlock);
+ }
+
+ // Insert the new block in the block list right after the current block.
+ if (blocksIterator == null) {
+ blocks.add(blocks.indexOf(block) + 1, newBlock);
+ } else {
+ blocksIterator.add(newBlock);
+ }
+
+ return newBlock;
+ }
+
+ public BasicBlock split(int instructions, IRCode code, ListIterator<BasicBlock> blocksIterator) {
+ // Split at the current cursor position.
+ BasicBlock newBlock = split(code, blocksIterator);
+ assert blocksIterator == null || peekPrevious(blocksIterator) == newBlock;
+ // Skip the requested number of instructions and split again.
+ InstructionListIterator iterator = newBlock.listIterator();
+ for (int i = 0; i < instructions; i++) {
+ iterator.next();
+ }
+ iterator.split(code, blocksIterator);
+ // Return the first split block.
+ return newBlock;
+ }
+
+ private boolean canThrow(IRCode code) {
+ Iterator<Instruction> iterator = code.instructionIterator();
+ while (iterator.hasNext()) {
+ boolean throwing = iterator.next().instructionTypeCanThrow();
+ if (throwing) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void splitBlockAndCopyCatchHandlers(IRCode code, BasicBlock invokeBlock,
+ BasicBlock inlinedBlock, ListIterator<BasicBlock> blocksIterator) {
+ // Iterate through the instructions in the inlined block and split into blocks with only
+ // one throwing instruction in each block.
+ // NOTE: This iterator is replaced in the loop below, so that the iteration continues in
+ // the new block after the iterated block is split.
+ InstructionListIterator instructionsIterator = inlinedBlock.listIterator();
+ BasicBlock currentBlock = inlinedBlock;
+ while (currentBlock != null && instructionsIterator.hasNext()) {
+ assert !currentBlock.hasCatchHandlers();
+ Instruction throwingInstruction =
+ instructionsIterator.nextUntil(Instruction::instructionTypeCanThrow);
+ BasicBlock nextBlock;
+ if (throwingInstruction != null) {
+ // If a throwing instruction was found split the block.
+ if (instructionsIterator.hasNext()) {
+ // TODO(sgjesse): No need to split if this is the last non-debug, non-jump
+ // instruction in the block.
+ nextBlock = instructionsIterator.split(code, blocksIterator);
+ assert nextBlock.getPredecessors().size() == 1;
+ assert currentBlock == nextBlock.getPredecessors().get(0);
+ // Back up to before the split before inserting catch handlers.
+ BasicBlock b = blocksIterator.previous();
+ assert b == nextBlock;
+ } else {
+ nextBlock = null;
+ }
+ currentBlock.copyCatchHandlers(code, blocksIterator, invokeBlock);
+ if (nextBlock != null) {
+ BasicBlock b = blocksIterator.next();
+ assert b == nextBlock;
+ // Switch iteration to the split block.
+ instructionsIterator = nextBlock.listIterator();
+ } else {
+ instructionsIterator = null;
+ }
+ currentBlock = nextBlock;
+ } else {
+ assert !instructionsIterator.hasNext();
+ instructionsIterator = null;
+ currentBlock = null;
+ }
+ }
+ }
+
+ private void appendCatchHandlers(IRCode code, BasicBlock invokeBlock,
+ IRCode inlinee, ListIterator<BasicBlock> blocksIterator) {
+ BasicBlock inlineeBlock = null;
+ // Move back through the inlinee blocks added (they are now in the basic blocks list).
+ for (int i = 0; i < inlinee.blocks.size(); i++) {
+ inlineeBlock = blocksIterator.previous();
+ }
+ assert inlineeBlock == inlinee.blocks.getFirst();
+ // Position right after the empty invoke block.
+ inlineeBlock = blocksIterator.next();
+ assert inlineeBlock == inlinee.blocks.getFirst();
+
+ // Iterate through the inlined blocks (they are now in the basic blocks list).
+ Iterator<BasicBlock> inlinedBlocksIterator = inlinee.blocks.iterator();
+ while (inlinedBlocksIterator.hasNext()) {
+ BasicBlock inlinedBlock = inlinedBlocksIterator.next();
+ assert inlineeBlock == inlinedBlock; // Iterators must be in sync.
+ if (inlinedBlock.hasCatchHandlers()) {
+ // The block already has catch handlers, so it has only one throwing instruction, and no
+ // splitting is required.
+ inlinedBlock.copyCatchHandlers(code, blocksIterator, invokeBlock);
+ } else {
+ // The block does not have catch handlers, so it can have several throwing instructions.
+ // Therefore the block must be split after each throwing instruction, and the catch
+ // handlers must be added to each of these blocks.
+ splitBlockAndCopyCatchHandlers(code, invokeBlock, inlinedBlock, blocksIterator);
+ }
+ // Iterate to the next inlined block (if more inlined blocks).
+ inlineeBlock = blocksIterator.next();
+ }
+ }
+
+ private void removeArgumentInstructions(IRCode inlinee) {
+ int index = 0;
+ InstructionListIterator inlineeIterator = inlinee.blocks.getFirst().listIterator();
+ List<Value> arguments = inlinee.collectArguments();
+ while (inlineeIterator.hasNext()) {
+ Instruction instruction = inlineeIterator.next();
+ if (instruction.isArgument()) {
+ assert instruction.outValue().numberOfAllUsers() == 0;
+ assert instruction.outValue() == arguments.get(index++);
+ inlineeIterator.remove();
+ }
+ }
+ }
+
+ public BasicBlock inlineInvoke(
+ IRCode code, IRCode inlinee, ListIterator<BasicBlock> blocksIterator,
+ List<BasicBlock> blocksToRemove) {
+ assert blocksToRemove != null;
+ boolean inlineeCanThrow = canThrow(inlinee);
+ BasicBlock invokeBlock = split(1, code, blocksIterator);
+ assert invokeBlock.getInstructions().size() == 2;
+ assert invokeBlock.getInstructions().getFirst().isInvoke();
+
+ // Split the invoke instruction into a separate block.
+ Invoke invoke = invokeBlock.getInstructions().getFirst().asInvoke();
+ BasicBlock invokePredecessor = invokeBlock.getPredecessors().get(0);
+ BasicBlock invokeSuccessor = invokeBlock.getSuccessors().get(0);
+
+ // Map all argument values, and remove the arguments instructions in the inlinee.
+ List<Value> arguments = inlinee.collectArguments();
+ assert invoke.inValues().size() == arguments.size();
+ for (int i = 0; i < invoke.inValues().size(); i++) {
+ arguments.get(i).replaceUsers(invoke.inValues().get(i));
+ }
+ removeArgumentInstructions(inlinee);
+
+ // The inline entry is the first block now the argument instructions are gone.
+ BasicBlock inlineEntry = inlinee.blocks.getFirst();
+
+ BasicBlock inlineExit = null;
+ if (inlinee.getNormalExitBlock() == null) {
+ assert inlineeCanThrow;
+ // TODO(sgjesse): Remove this restriction.
+ assert !invokeBlock.hasCatchHandlers();
+ blocksToRemove.addAll(
+ invokePredecessor.unlink(invokeBlock, new DominatorTree(code, blocksToRemove)));
+ } else {
+ // Locate inlinee return.
+ InstructionListIterator inlineeIterator = inlinee.getNormalExitBlock().listIterator();
+ inlineeIterator.nextUntil(Instruction::isReturn);
+ Return ret = inlineeIterator.previous().asReturn();
+
+ // Map return value if used.
+ if (invoke.outValue() != null) {
+ assert !ret.isReturnVoid();
+ invoke.outValue().replaceUsers(ret.returnValue());
+ }
+
+ // Split before return and unlink return.
+ BasicBlock returnBlock = inlineeIterator.split(inlinee);
+ inlineExit = returnBlock.unlinkSinglePredecessor();
+ InstructionListIterator returnBlockIterator = returnBlock.listIterator();
+ returnBlockIterator.next();
+ returnBlockIterator.remove(); // This clears out the users from the return.
+ assert !returnBlockIterator.hasNext();
+ inlinee.blocks.remove(returnBlock);
+
+ // Leaving the invoke block in the graph as an empty block. Still unlink its predecessor as
+ // the exit block of the inlinee will become its new predecessor.
+ invokeBlock.unlinkSinglePredecessor();
+ InstructionListIterator invokeBlockIterator = invokeBlock.listIterator();
+ invokeBlockIterator.next();
+ invokeBlockIterator.remove();
+ invokeSuccessor = invokeBlock;
+ }
+
+ // Link the inlinee into the graph.
+ invokePredecessor.link(inlineEntry);
+ if (inlineExit != null) {
+ inlineExit.link(invokeSuccessor);
+ }
+
+ // Position the block iterator cursor just after the invoke block.
+ if (blocksIterator == null) {
+ // If no block iterator was passed create one for the insertion of the inlinee blocks.
+ blocksIterator = code.blocks.listIterator(code.blocks.indexOf(invokeBlock));
+ blocksIterator.next();
+ } else {
+ // If a blocks iterator was passed, back up to the block with the invoke instruction and
+ // remove it.
+ blocksIterator.previous();
+ blocksIterator.previous();
+ }
+
+ // Insert inlinee blocks into the IR code.
+ inlinee.blocks.forEach(blocksIterator::add);
+
+ // If the invoke block had catch handlers copy those down to all inlined blocks.
+ if (invokeBlock.hasCatchHandlers()) {
+ appendCatchHandlers(code, invokeBlock, inlinee, blocksIterator);
+ }
+
+ return invokeSuccessor;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
new file mode 100644
index 0000000..d9ce088
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
@@ -0,0 +1,93 @@
+// 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.ir.code;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+
+public class BasicBlockIterator implements ListIterator<BasicBlock> {
+
+ protected final IRCode code;
+ protected final ListIterator<BasicBlock> listIterator;
+ protected BasicBlock current;
+
+ protected BasicBlockIterator(IRCode code) {
+ this.code = code;
+ this.listIterator = code.blocks.listIterator();
+ }
+
+ protected BasicBlockIterator(IRCode code, int index) {
+ this.code = code;
+ this.listIterator = code.blocks.listIterator(index);
+ }
+
+ @Override
+ public boolean hasNext() {
+ return listIterator.hasNext();
+ }
+
+ @Override
+ public BasicBlock next() {
+ current = listIterator.next();
+ return current;
+ }
+
+ @Override
+ public int nextIndex() {
+ return listIterator.nextIndex();
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ return listIterator.hasPrevious();
+ }
+
+ @Override
+ public BasicBlock previous() {
+ current = listIterator.previous();
+ return current;
+ }
+
+ @Override
+ public int previousIndex() {
+ return listIterator.previousIndex();
+ }
+
+ @Override
+ public void add(BasicBlock block) {
+ listIterator.add(block);
+ }
+
+ @Override
+ public void set(BasicBlock block) {
+ listIterator.set(block);
+ }
+
+ /**
+ * Remove the last {@link BasicBlock} that was returned by {@link #next()} or {@link #previous()}.
+ * This call can only be made once per call to {@code next} or {@code previous}.
+ *
+ * All instructions in the block will be completely detached from the instruction stream. Each
+ * instruction will have all uses of its in-values removed. If any instructions in the block
+ * produces an out-value these out values must not have any users.
+ */
+ @Override
+ public void remove() {
+ if (current == null) {
+ throw new IllegalStateException();
+ }
+ // Remove all instructions from the block before removing the block.
+ Iterator<Instruction> iterator = current.iterator();
+ while (iterator.hasNext()) {
+ iterator.next();
+ iterator.remove();
+ }
+ listIterator.remove();
+ if (current == code.getNormalExitBlock()) {
+ code.setNormalExitBlock(null);
+ }
+ current = null;
+ }
+}
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
new file mode 100644
index 0000000..83240bb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Binop.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import static com.android.tools.r8.dex.Constants.U4BIT_MAX;
+import static com.android.tools.r8.dex.Constants.U8BIT_MAX;
+
+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.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+
+public abstract class Binop extends Instruction {
+
+ protected final NumericType type;
+
+ public Binop(NumericType type, Value dest, Value left, Value right) {
+ super(dest);
+ this.type = type;
+ addInValue(left);
+ addInValue(right);
+ }
+
+ public NumericType getNumericType() {
+ return type;
+ }
+
+ public Value leftValue() {
+ return inValues.get(0);
+ }
+
+ public Value rightValue() {
+ return inValues.get(1);
+ }
+
+ public abstract boolean isCommutative();
+
+ public boolean isTwoAddr(DexBuilder builder) {
+ if (rightValue().needsRegister() && leftValue().needsRegister()) {
+ int leftRegister = builder.allocatedRegister(leftValue(), getNumber());
+ int rightRegister = builder.allocatedRegister(rightValue(), getNumber());
+ int destRegister = builder.allocatedRegister(outValue, getNumber());
+ return ((leftRegister == destRegister) ||
+ (isCommutative() && rightRegister == destRegister)) &&
+ leftRegister <= U4BIT_MAX &&
+ rightRegister <= U4BIT_MAX;
+ }
+ return false;
+ }
+
+ boolean fitsInDexInstruction(Value value) {
+ return fitsInLit16Instruction(value);
+ }
+
+ boolean fitsInLit16Instruction(Value value) {
+ return type == NumericType.INT &&
+ value.isConstant() &&
+ value.getConstInstruction().asConstNumber().is16Bit();
+ }
+
+ boolean fitsInLit8Instruction(Value value) {
+ return type == NumericType.INT &&
+ value.isConstant() &&
+ value.getConstInstruction().asConstNumber().is8Bit();
+ }
+
+ // The in and out register sizes are the same depending on the size of the literal
+ // involved (is any).
+ int maxInOutValueRegisterSize() {
+ if (fitsInDexInstruction(rightValue())) {
+ return rightValue().getConstInstruction().asConstNumber().is8Bit() ? U8BIT_MAX : U4BIT_MAX;
+ }
+ return U8BIT_MAX;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return maxInOutValueRegisterSize();
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return maxInOutValueRegisterSize();
+ }
+
+ int foldIntegers(int left, int right) {
+ throw new Unreachable("Unsupported integer folding for " + this);
+ }
+
+ long foldLongs(long left, long right) {
+ throw new Unreachable("Unsupported long folding for " + this);
+ }
+
+ float foldFloat(float left, float right) {
+ throw new Unreachable("Unsupported float folding for " + this);
+ }
+
+ double foldDouble(double left, double right) {
+ throw new Unreachable("Unsupported float folding for " + this);
+ }
+
+ @Override
+ public boolean isBinop() {
+ return true;
+ }
+
+ @Override
+ public Binop asBinop() {
+ return this;
+ }
+
+ @Override
+ public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+ return InliningConstraint.ALWAYS;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
new file mode 100644
index 0000000..d67c359
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.List;
+import java.util.Set;
+
+public class CatchHandlers<T> {
+
+ private final List<DexType> guards;
+ private final List<T> targets;
+ private Set<T> uniqueTargets;
+
+ public static final CatchHandlers<Integer> EMPTY_INDICES = new CatchHandlers<>();
+ public static final CatchHandlers<BasicBlock> EMPTY_BASIC_BLOCK = new CatchHandlers<>();
+
+ private CatchHandlers() {
+ guards = ImmutableList.of();
+ targets = ImmutableList.of();
+ }
+
+ public CatchHandlers(List<DexType> guards, List<T> targets) {
+ assert !guards.isEmpty();
+ assert guards.size() == targets.size();
+ // Guava ImmutableList does not support null elements.
+ this.guards = ImmutableList.copyOf(guards);
+ this.targets = ImmutableList.copyOf(targets);
+ }
+
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ public int size() {
+ assert guards.size() == targets.size();
+ return guards.size();
+ }
+
+ public List<DexType> getGuards() {
+ return guards;
+ }
+
+ public List<T> getAllTargets() {
+ return targets;
+ }
+
+ public Set<T> getUniqueTargets() {
+ if (uniqueTargets == null) {
+ uniqueTargets = ImmutableSet.copyOf(targets);
+ }
+ return uniqueTargets;
+ }
+
+ public boolean hasCatchAll() {
+ return getGuards().size() > 0 &&
+ getGuards().get(getGuards().size() - 1) == DexItemFactory.catchAllType;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof CatchHandlers)) {
+ return false;
+ }
+ CatchHandlers that = (CatchHandlers) o;
+ return guards.equals(that.guards) && targets.equals(that.targets);
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * guards.hashCode() + targets.hashCode();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
new file mode 100644
index 0000000..7963357
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.MoveObject;
+import com.android.tools.r8.code.MoveObjectFrom16;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class CheckCast extends Instruction {
+
+ private final DexType type;
+
+ // A CheckCast dex instruction takes only one register containing a value and changes
+ // the associated type information for that value. In the IR we let the CheckCast
+ // instruction define a new value. During register allocation we then need to arrange it
+ // so that the source and destination are assigned the same register.
+ public CheckCast(Value dest, Value value, DexType type) {
+ super(dest, value);
+ if (value.isNeverNull()) {
+ dest.markNeverNull();
+ }
+ this.type = type;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ // The check cast instruction in dex doesn't write a new register. Therefore,
+ // if the register allocator could not put input and output in the same register
+ // we have to insert a move before the check cast instruction.
+ int inRegister = builder.allocatedRegister(inValues.get(0), getNumber());
+ int outRegister = builder.allocatedRegister(outValue, getNumber());
+ if (inRegister == outRegister) {
+ builder.add(this, new com.android.tools.r8.code.CheckCast(outRegister, type));
+ } else {
+ com.android.tools.r8.code.CheckCast cast = new com.android.tools.r8.code.CheckCast(outRegister, type);
+ if (outRegister <= Constants.U4BIT_MAX && inRegister <= Constants.U4BIT_MAX) {
+ builder.add(this, new com.android.tools.r8.code.Instruction[]{
+ new MoveObject(outRegister, inRegister), cast});
+ } else {
+ builder.add(this, new com.android.tools.r8.code.Instruction[]{
+ new MoveObjectFrom16(outRegister, inRegister), cast});
+ }
+ }
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asCheckCast().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.slowCompareTo(other.asCheckCast().type);
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return true;
+ }
+
+ @Override
+ public boolean isCheckCast() {
+ return true;
+ }
+
+ @Override
+ public CheckCast asCheckCast() {
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "; " + type;
+ }
+}
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
new file mode 100644
index 0000000..c5cc82f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
@@ -0,0 +1,196 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.CmpLong;
+import com.android.tools.r8.code.CmpgDouble;
+import com.android.tools.r8.code.CmpgFloat;
+import com.android.tools.r8.code.CmplDouble;
+import com.android.tools.r8.code.CmplFloat;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+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;
+
+public class Cmp extends Binop {
+
+ public enum Bias {
+ NONE, GT, LT
+ }
+
+ private final Bias bias;
+
+ public Cmp(NumericType type, Bias bias, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ this.bias = bias;
+ }
+
+ @Override
+ public boolean isCommutative() {
+ return false;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int dest = builder.allocatedRegister(outValue, getNumber());
+ int left = builder.allocatedRegister(leftValue(), getNumber());
+ int right = builder.allocatedRegister(rightValue(), getNumber());
+ switch (type) {
+ case DOUBLE:
+ assert bias != Bias.NONE;
+ if (bias == Bias.GT) {
+ instruction = new CmpgDouble(dest, left, right);
+ } else {
+ assert bias == Bias.LT;
+ instruction = new CmplDouble(dest, left, right);
+ }
+ break;
+ case FLOAT:
+ assert bias != Bias.NONE;
+ if (bias == Bias.GT) {
+ instruction = new CmpgFloat(dest, left, right);
+ } else {
+ assert bias == Bias.LT;
+ instruction = new CmplFloat(dest, left, right);
+ }
+ break;
+ case LONG:
+ assert bias == Bias.NONE;
+ instruction = new CmpLong(dest, left, right);
+ break;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ builder.add(this, instruction);
+ }
+
+ private String biasToString(Bias bias) {
+ switch (bias) {
+ case NONE:
+ return "none";
+ case GT:
+ return "gt";
+ case LT:
+ return "lt";
+ default:
+ throw new Unreachable("Unexpected bias " + bias);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(getClass().getSimpleName());
+ builder.append(" (");
+ switch (type) {
+ case DOUBLE:
+ builder.append("double, ");
+ builder.append(biasToString(bias));
+ break;
+ case FLOAT:
+ builder.append("float, ");
+ builder.append(biasToString(bias));
+ break;
+ case LONG:
+ builder.append("long");
+ break;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ builder.append(")");
+ for (int i = builder.length(); i < 20; i++) {
+ builder.append(" ");
+ }
+ if (outValue != null) {
+ builder.append(outValue);
+ builder.append(" <- ");
+ }
+ StringUtils.append(builder, inValues, ", ", BraceType.NONE);
+ return builder.toString();
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asCmp().bias == bias;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return bias.ordinal() - other.asCmp().bias.ordinal();
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ private boolean nonOverlapingRanges() {
+ return type == NumericType.LONG
+ && leftValue().hasValueRange()
+ && rightValue().hasValueRange()
+ && leftValue().getValueRange().doesntOverlapWith(rightValue().getValueRange());
+ }
+
+ @Override
+ public boolean canBeFolded() {
+ return (leftValue().isConstant() && rightValue().isConstant()) || nonOverlapingRanges();
+ }
+
+ @Override
+ public ConstInstruction fold(ValueNumberGenerator valueNumberGenerator) {
+ assert canBeFolded();
+ int result;
+ if (type == NumericType.LONG) {
+ if (leftValue().isConstant() && rightValue().isConstant()) {
+ long left = leftValue().getConstInstruction().asConstNumber().getLongValue();
+ long right = rightValue().getConstInstruction().asConstNumber().getLongValue();
+ result = Integer.signum(Long.compare(left, right));
+ } else {
+ assert nonOverlapingRanges();
+ LongInterval leftRange = leftValue().getValueRange();
+ LongInterval rightRange = rightValue().getValueRange();
+ result = Integer.signum(Long.compare(leftRange.getMin(), rightRange.getMin()));
+ }
+ } else if (type == NumericType.FLOAT) {
+ float left = leftValue().getConstInstruction().asConstNumber().getFloatValue();
+ float right = rightValue().getConstInstruction().asConstNumber().getFloatValue();
+ if (Float.isNaN(left) || Float.isNaN(right)) {
+ result = bias == Bias.GT ? 1 : -1;
+ } else {
+ result = (int) Math.signum(left - right);
+ }
+ } else {
+ assert type == NumericType.DOUBLE;
+ double left = leftValue().getConstInstruction().asConstNumber().getDoubleValue();
+ double right = rightValue().getConstInstruction().asConstNumber().getDoubleValue();
+ if (Double.isNaN(left) || Double.isNaN(right)) {
+ result = bias == Bias.GT ? 1 : -1;
+ } else {
+ result = (int) Math.signum(left - right);
+ }
+ }
+ assert result == -1 || result == 0 || result == 1;
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+ return new ConstNumber(ConstType.INT, value, result);
+ }
+
+ @Override
+ public boolean isCmp() {
+ return true;
+ }
+
+ @Override
+ public Cmp asCmp() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
new file mode 100644
index 0000000..9b565a6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class ConstClass extends ConstInstruction {
+
+ private final DexType clazz;
+
+ public ConstClass(Value dest, DexType clazz) {
+ super(dest);
+ this.clazz = clazz;
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ public DexType getValue() {
+ return clazz;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int dest = builder.allocatedRegister(dest(), getNumber());
+ builder.add(this, new com.android.tools.r8.code.ConstClass(dest, clazz));
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ assert false : "ConstClass has no register arguments.";
+ return 0;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return true;
+ }
+
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asConstClass().clazz == clazz;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return clazz.slowCompareTo(other.asConstClass().clazz);
+ }
+
+ @Override
+ public boolean isConstClass() {
+ return true;
+ }
+
+ @Override
+ public ConstClass asConstClass() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java b/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java
new file mode 100644
index 0000000..657faf7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+public abstract class ConstInstruction extends Instruction {
+
+ public ConstInstruction(Value out) {
+ super(out);
+ }
+
+ @Override
+ public ConstInstruction getOutConstantConstInstruction() {
+ return this;
+ }
+
+ @Override
+ public boolean isConstInstruction() {
+ return true;
+ }
+
+ @Override
+ public ConstInstruction asConstInstruction() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
new file mode 100644
index 0000000..8aff14a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -0,0 +1,195 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Const;
+import com.android.tools.r8.code.Const16;
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.ConstHigh16;
+import com.android.tools.r8.code.ConstWide;
+import com.android.tools.r8.code.ConstWide16;
+import com.android.tools.r8.code.ConstWide32;
+import com.android.tools.r8.code.ConstWideHigh16;
+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.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import com.android.tools.r8.utils.NumberUtils;
+
+public class ConstNumber extends ConstInstruction {
+
+ public final ConstType type;
+ private final long value;
+
+ public ConstNumber(ConstType type, Value dest, long value) {
+ super(dest);
+ // We create const numbers after register allocation for rematerialization of values. Those
+ // are all for fixed register values. All other values that are used as the destination for
+ // const number instructions should be marked as constants.
+ assert dest.isFixedRegisterValue() || dest.isConstant();
+ assert type != ConstType.OBJECT;
+ this.type = type;
+ this.value = value;
+ }
+
+ public static ConstNumber copyOf(IRCode code, ConstNumber original) {
+ Value newValue =
+ new Value(
+ code.valueNumberGenerator.next(),
+ original.outValue().getOriginalRegister(),
+ original.outType(),
+ original.getDebugInfo());
+ return new ConstNumber(original.type, newValue, original.getRawValue());
+ }
+
+ private boolean preciseTypeUnknown() {
+ return type == ConstType.INT_OR_FLOAT || type == ConstType.LONG_OR_DOUBLE;
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ public int getIntValue() {
+ assert type == ConstType.INT || type == ConstType.INT_OR_FLOAT;
+ return (int) value;
+ }
+
+ public long getLongValue() {
+ assert type == ConstType.LONG || type == ConstType.LONG_OR_DOUBLE;
+ return value;
+ }
+
+ public float getFloatValue() {
+ assert type == ConstType.FLOAT || type == ConstType.INT_OR_FLOAT;
+ return Float.intBitsToFloat((int) value);
+ }
+
+ public double getDoubleValue() {
+ assert type == ConstType.DOUBLE || type == ConstType.LONG_OR_DOUBLE;
+ return Double.longBitsToDouble(value);
+ }
+
+ public long getRawValue() {
+ return value;
+ }
+
+ public boolean isZero() {
+ return value == 0;
+ }
+
+ public boolean isIntegerNegativeOne(NumericType type) {
+ assert type == NumericType.INT || type == NumericType.LONG;
+ if (type == NumericType.INT) {
+ return getIntValue() == -1;
+ }
+ return getLongValue() == -1;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ if (!dest().needsRegister()) {
+ builder.addNop(this);
+ return;
+ }
+
+ int register = builder.allocatedRegister(dest(), getNumber());
+ if (MoveType.fromConstType(type) == MoveType.SINGLE) {
+ assert NumberUtils.is32Bit(value);
+ if ((register & 0xf) == register && NumberUtils.is4Bit(value)) {
+ builder.add(this, new Const4(register, (int) value));
+ } else if (NumberUtils.is16Bit(value)) {
+ builder.add(this, new Const16(register, (int) value));
+ } else if ((value & 0x0000ffffL) == 0) {
+ builder.add(this, new ConstHigh16(register, ((int) value) >>> 16));
+ } else {
+ builder.add(this, new Const(register, (int) value));
+ }
+ } else {
+ assert MoveType.fromConstType(type) == MoveType.WIDE;
+ if (NumberUtils.is16Bit(value)) {
+ builder.add(this, new ConstWide16(register, (int) value));
+ } else if ((value & 0x0000ffffffffffffL) == 0) {
+ builder.add(this, new ConstWideHigh16(register, (int) (value >>> 48)));
+ } else if (NumberUtils.is32Bit(value)) {
+ builder.add(this, new ConstWide32(register, (int) value));
+ } else {
+ builder.add(this, new ConstWide(register, value));
+ }
+ }
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ assert false : "Const has no register arguments.";
+ return 0;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " " + value + " (" + type + ")";
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ if (preciseTypeUnknown()) {
+ return false;
+ }
+ ConstNumber o = other.asConstNumber();
+ return o.type == type && o.value == value;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ ConstNumber o = other.asConstNumber();
+ int result;
+ result = type.ordinal() - o.type.ordinal();
+ if (result != 0) {
+ return result;
+ }
+ return Long.signum(value - o.value);
+ }
+
+ public boolean is8Bit() {
+ return NumberUtils.is8Bit(value);
+ }
+
+ public boolean negativeIs8Bit() {
+ return NumberUtils.negativeIs8Bit(value);
+ }
+
+ public boolean is16Bit() {
+ return NumberUtils.is16Bit(value);
+ }
+
+ public boolean negativeIs16Bit() {
+ return NumberUtils.negativeIs16Bit(value);
+ }
+
+ @Override
+ public boolean isOutConstant() {
+ return true;
+ }
+
+ @Override
+ public boolean isConstNumber() {
+ return true;
+ }
+
+ @Override
+ public ConstNumber asConstNumber() {
+ return this;
+ }
+
+ @Override
+ public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+ return InliningConstraint.ALWAYS;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
new file mode 100644
index 0000000..82db97a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.ConstStringJumbo;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class ConstString extends ConstInstruction {
+
+ private final DexString value;
+
+ public ConstString(Value dest, DexString value) {
+ super(dest);
+ dest.markNeverNull();
+ this.value = value;
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ public DexString getValue() {
+ return value;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ builder.registerStringReference(value);
+ int dest = builder.allocatedRegister(dest(), getNumber());
+ if (builder.isJumboString(value)) {
+ builder.add(this, new ConstStringJumbo(dest, value));
+ } else {
+ builder.add(this, new com.android.tools.r8.code.ConstString(dest, value));
+ }
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asConstString().value == value;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return value.slowCompareTo(other.asConstString().value);
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ assert false : "ConstString has no register arguments.";
+ return 0;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " \"" + value + "\"";
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return true;
+ }
+
+ @Override
+ public boolean isConstString() {
+ return true;
+ }
+
+ @Override
+ public ConstString asConstString() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstType.java b/src/main/java/com/android/tools/r8/ir/code/ConstType.java
new file mode 100644
index 0000000..c6f9d73
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstType.java
@@ -0,0 +1,48 @@
+// 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.ir.code;
+
+import com.android.tools.r8.errors.Unreachable;
+
+public enum ConstType {
+ INT,
+ LONG,
+ FLOAT,
+ DOUBLE,
+ OBJECT,
+ INT_OR_FLOAT,
+ LONG_OR_DOUBLE;
+
+ public static ConstType fromNumericType(NumericType type) {
+ switch (type) {
+ case BYTE:
+ case CHAR:
+ case SHORT:
+ case INT:
+ return INT;
+ case LONG:
+ return LONG;
+ case FLOAT:
+ return FLOAT;
+ case DOUBLE:
+ return DOUBLE;
+ default:
+ throw new Unreachable("Invalid numeric type '" + type + "'");
+ }
+ }
+
+ public static ConstType fromMoveType(MoveType moveType) {
+ switch (moveType) {
+ case SINGLE:
+ return INT_OR_FLOAT;
+ case WIDE:
+ return LONG_OR_DOUBLE;
+ case OBJECT:
+ // Currently constants never have type OBJECT even when it is the null object.
+ return INT;
+ default:
+ throw new Unreachable("Invalid move type '" + moveType + "'");
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
new file mode 100644
index 0000000..ee8a279
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
@@ -0,0 +1,79 @@
+// 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.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.InternalOptions;
+
+/**
+ * Instruction reading an SSA value with attached local information.
+ *
+ * This instruction ensures that a value with a local is kept alive until at least this read.
+ *
+ * This instruction must not be considered dead until live-ranges have been computed for locals
+ * after which it will be removed.
+ */
+public class DebugLocalRead extends Instruction {
+
+ public DebugLocalRead(Value src) {
+ super(null, src);
+ assert src.getLocalInfo() != null;
+ }
+
+ public Value src() {
+ return inValues.get(0);
+ }
+
+ @Override
+ public boolean isDebugLocalRead() {
+ return true;
+ }
+
+ @Override
+ public DebugLocalRead asDebugLocalRead() {
+ return this;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ assert other.isDebugLocalRead();
+ return true;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ assert other.isDebugLocalRead();
+ return 0;
+ }
+
+ @Override
+ public boolean canBeDeadCode(InternalOptions options) {
+ return false;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U16BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ throw new Unreachable();
+ }
+
+ public void addDebugLocalStart() {
+ src().addDebugLocalStart(this);
+ }
+
+ public void addDebugLocalEnd() {
+ src().addDebugLocalEnd(this);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalUninitialized.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalUninitialized.java
new file mode 100644
index 0000000..beaf508
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalUninitialized.java
@@ -0,0 +1,33 @@
+// 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.ir.code;
+
+/**
+ * Instruction representing the SSA value for an uninitialized local.
+ *
+ * This instruction allows the local information to not necessarily be properly block structured.
+ * For example, Javac will emit local-end labels in blocks that are not dominated by the
+ * introduction of the same local, causing our SSA graph to attempt to reference a local value that
+ * does not exist in all predecessors (eg, for a local declared in an unscoped switch-case).
+ * By emitting this "uninitialized" value in the method prelude, such a reference will instead
+ * become a phi of the uninitialized value and the local.
+ *
+ * This instruction may (and almost always will) be considered dead code and removed.
+ */
+public class DebugLocalUninitialized extends ConstNumber {
+
+ public DebugLocalUninitialized(ConstType type, Value value) {
+ super(type == ConstType.OBJECT ? ConstType.INT : type, value, 0);
+ }
+
+ @Override
+ public boolean isDebugLocalUninitialized() {
+ return true;
+ }
+
+ @Override
+ public DebugLocalUninitialized asDebugLocalUninitialized() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
new file mode 100644
index 0000000..442fb6d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -0,0 +1,45 @@
+// 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.ir.code;
+
+/**
+ * Instruction introducing an SSA value with attached local information.
+ *
+ * All instructions may have attached local information (defined as the local information of their
+ * outgoing value). This instruction is needed to mark a transition of an existing value (with a
+ * possible local attached) to a new value that has a local (possibly the same one). If all ingoing
+ * values end up having the same local this can be safely removed.
+ *
+ * For valid debug info, this instruction should have at least one user, namely a DebugLocalRead
+ * denoting the end of its range, and thus it should be live.
+ */
+public class DebugLocalWrite extends Move {
+
+ public DebugLocalWrite(Value dest, Value src) {
+ super(dest, src);
+ assert dest.getLocalInfo() != null;
+ assert dest.getLocalInfo() != src.getLocalInfo() || src.isPhi();
+ }
+
+ @Override
+ public boolean isDebugLocalWrite() {
+ return true;
+ }
+
+ @Override
+ public DebugLocalWrite asDebugLocalWrite() {
+ return this;
+ }
+
+ @Override
+ public boolean isOutConstant() {
+ return false;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ assert other.isDebugLocalWrite();
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
new file mode 100644
index 0000000..4e078a5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -0,0 +1,89 @@
+// 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.ir.code;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableMap;
+
+public class DebugPosition extends Instruction {
+
+ public final int line;
+ public final DexString file;
+ private ImmutableMap<Integer, DebugLocalInfo> locals;
+
+ public DebugPosition(int line, DexString file) {
+ super(null);
+ this.line = line;
+ this.file = file;
+ }
+
+ @Override
+ public boolean isDebugPosition() {
+ return true;
+ }
+
+ @Override
+ public DebugPosition asDebugPosition() {
+ return this;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ builder.addNop(this);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ assert other.isDebugPosition();
+ return false;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ assert other.isDebugPosition();
+ return 0;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public boolean canBeDeadCode(InternalOptions options) {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(super.toString());
+ if (file != null) {
+ builder.append(file).append(":");
+ }
+ builder.append(line);
+ if (locals != null && !locals.isEmpty()) {
+ builder.append(", locals: ");
+ StringUtils.append(builder, locals.values());
+ }
+ return builder.toString();
+ }
+
+ public void setLocals(ImmutableMap<Integer, DebugLocalInfo> locals) {
+ this.locals = locals;
+ }
+
+ public ImmutableMap<Integer, DebugLocalInfo> getLocals() {
+ return locals;
+ }
+}
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
new file mode 100644
index 0000000..c8c1c32
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Div.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.DivDouble;
+import com.android.tools.r8.code.DivDouble2Addr;
+import com.android.tools.r8.code.DivFloat;
+import com.android.tools.r8.code.DivFloat2Addr;
+import com.android.tools.r8.code.DivInt;
+import com.android.tools.r8.code.DivInt2Addr;
+import com.android.tools.r8.code.DivIntLit16;
+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.graph.AppInfo;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+
+public class Div extends ArithmeticBinop {
+
+ public Div(NumericType type, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ }
+
+ @Override
+ public boolean isDiv() {
+ return true;
+ }
+
+ @Override
+ public Div asDiv() {
+ return this;
+ }
+
+ @Override
+ public boolean isCommutative() {
+ return false;
+ }
+
+ public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+ return new DivInt(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+ return new DivLong(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateFloat(int dest, int left, int right) {
+ return new DivFloat(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateDouble(int dest, int left, int right) {
+ return new DivDouble(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+ return new DivInt2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+ return new DivLong2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateFloat2Addr(int left, int right) {
+ return new DivFloat2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateDouble2Addr(int left, int right) {
+ return new DivDouble2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+ return new DivIntLit8(dest, left, constant);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+ return new DivIntLit16(dest, left, constant);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asDiv().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asDiv().type.ordinal();
+ }
+
+ @Override
+ public boolean canBeFolded() {
+ return super.canBeFolded() && !rightValue().getConstInstruction().asConstNumber().isZero();
+ }
+
+ @Override
+ int foldIntegers(int left, int right) {
+ return left / right;
+ }
+
+ @Override
+ long foldLongs(long left, long right) {
+ return left / right;
+ }
+
+ @Override
+ float foldFloat(float left, float right) {
+ return left / right;
+ }
+
+ @Override
+ double foldDouble(double left, double right) {
+ return left / right;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return type != NumericType.DOUBLE && type != NumericType.FLOAT;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
new file mode 100644
index 0000000..e814dbe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
@@ -0,0 +1,175 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+public class DominatorTree {
+
+ IRCode code;
+ private BasicBlock[] sorted;
+ private BasicBlock[] doms;
+
+ public DominatorTree(IRCode code) {
+ this(code, Collections.emptyList());
+ }
+
+ // TODO(sgjesse) Get rid of this constructor and blocksToIgnore.
+ DominatorTree(IRCode code, List<BasicBlock> blocksToIgnore) {
+ this.code = code;
+ this.sorted = code.topologicallySortedBlocks(blocksToIgnore);
+ numberBlocks();
+ build();
+ }
+
+ /**
+ * Get the immediate dominator block for a block.
+ */
+ public BasicBlock immediateDominator(BasicBlock block) {
+ return doms[block.getNumber()];
+ }
+
+ /**
+ * Check if one basic block is dominated by another basic block.
+ *
+ * @param subject subject to check for domination by {@code dominator}
+ * @param dominator dominator to check against
+ * @return wether {@code subject} is dominated by {@code dominator}
+ */
+ public boolean dominatedBy(BasicBlock subject, BasicBlock dominator) {
+ if (subject == dominator) {
+ return true;
+ }
+ return strictlyDominatedBy(subject, dominator);
+ }
+
+ /**
+ * Check if one basic block is strictly dominated by another basic block.
+ *
+ * @param subject subject to check for domination by {@code dominator}
+ * @param dominator dominator to check against
+ * @return wether {@code subject} is strictly dominated by {@code dominator}
+ */
+ public boolean strictlyDominatedBy(BasicBlock subject, BasicBlock dominator) {
+ if (subject.getNumber() == 0) {
+ return false;
+ }
+ while (true) {
+ BasicBlock idom = immediateDominator(subject);
+ if (idom.getNumber() < dominator.getNumber()) {
+ return false;
+ }
+ if (idom == dominator) {
+ return true;
+ }
+ subject = idom;
+ }
+ }
+
+ /**
+ * Use the dominator tree to find the dominating block that is closest to a set of blocks.
+ *
+ * @param blocks the block for which to find a dominator
+ * @return the closest dominator for the collection of blocks
+ */
+ public BasicBlock closestDominator(Collection<BasicBlock> blocks) {
+ if (blocks.size() == 0) {
+ return null;
+ }
+ Iterator<BasicBlock> it = blocks.iterator();
+ BasicBlock dominator = it.next();
+ while (it.hasNext()) {
+ dominator = intersect(dominator, it.next());
+ }
+ return dominator;
+ }
+
+ public BasicBlock[] getSortedBlocks() {
+ return sorted;
+ }
+
+ private void numberBlocks() {
+ for (int i = 0; i < sorted.length; i++) {
+ sorted[i].setNumber(i);
+ }
+ }
+
+ private boolean postorderCompareLess(BasicBlock b1, BasicBlock b2) {
+ // The topological sort is reverse postorder.
+ return b1.getNumber() > b2.getNumber();
+ }
+
+ // Build dominator tree based on the algorithm described in this paper:
+ //
+ // A Simple, Fast Dominance Algorithm
+ // Cooper, Keith D.; Harvey, Timothy J.; and Kennedy, Ken (2001).
+ // http://www.cs.rice.edu/~keith/EMBED/dom.pdf
+ private void build() {
+ doms = new BasicBlock[sorted.length];
+ doms[0] = sorted[0];
+ boolean changed = true;
+ while (changed) {
+ changed = false;
+ // Run through all nodes in reverse postorder (except start node).
+ for (int i = 1; i < sorted.length; i++) {
+ BasicBlock b = sorted[i];
+ // Pick one processed predecessor.
+ BasicBlock newIDom = null;
+ int picked = -1;
+ for (int j = 0; newIDom == null && j < b.getPredecessors().size(); j++) {
+ BasicBlock p = b.getPredecessors().get(j);
+ if (doms[p.getNumber()] != null) {
+ picked = j;
+ newIDom = p;
+ }
+ }
+ // Run through all other predecessors.
+ for (int j = 0; j < b.getPredecessors().size(); j++) {
+ BasicBlock p = b.getPredecessors().get(j);
+ if (j == picked) {
+ continue;
+ }
+ if (doms[p.getNumber()] != null) {
+ newIDom = intersect(p, newIDom);
+ }
+ }
+ if (doms[b.getNumber()] != newIDom) {
+ doms[b.getNumber()] = newIDom;
+ changed = true;
+ }
+ }
+ }
+ }
+
+ private BasicBlock intersect(BasicBlock b1, BasicBlock b2) {
+ BasicBlock finger1 = b1;
+ BasicBlock finger2 = b2;
+ while (finger1 != finger2) {
+ while (postorderCompareLess(finger1, finger2)) {
+ finger1 = doms[finger1.getNumber()];
+ }
+ while (postorderCompareLess(finger2, finger1)) {
+ finger2 = doms[finger2.getNumber()];
+ }
+ }
+ return finger1;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Dominators\n");
+ for (BasicBlock block : sorted) {
+ builder.append(block.getNumber());
+ builder.append(": ");
+ builder.append(doms[block.getNumber()].getNumber());
+ builder.append("\n");
+ }
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
new file mode 100644
index 0000000..e93f3a5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -0,0 +1,68 @@
+// 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.ir.code;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.optimize.Inliner;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import java.util.List;
+
+abstract class FieldInstruction extends Instruction {
+
+ protected final MemberType type;
+ protected final DexField field;
+
+ protected FieldInstruction(MemberType type, DexField field, Value dest, Value object) {
+ super(dest, object);
+ assert type != null;
+ assert field != null;
+ this.type = type;
+ this.field = field;
+ }
+
+ protected FieldInstruction(MemberType type, DexField field, Value dest, List<Value> values) {
+ super(dest, values);
+ assert type != null;
+ assert field != null;
+ this.type = type;
+ this.field = field;
+ }
+
+ public MemberType getType() {
+ return type;
+ }
+
+ public DexField getField() {
+ return field;
+ }
+
+ @Override
+ public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+ // Resolve the field if possible and decide whether the instruction can inlined.
+ DexType fieldHolder = field.getHolder();
+ DexEncodedField target = info.lookupInstanceTarget(fieldHolder, field);
+ DexClass fieldClass = info.definitionFor(fieldHolder);
+ if ((target != null) && (fieldClass != null) && !fieldClass.isLibraryClass()) {
+ if (isInstanceGet() || isInstancePut()) {
+ return Inliner.InliningConstraint.PRIVATE;
+ }
+ DexAccessFlags flags = target.accessFlags;
+ if (flags.isPublic()) {
+ return Inliner.InliningConstraint.ALWAYS;
+ }
+ if (flags.isPrivate() && (fieldHolder == holder)) {
+ return Inliner.InliningConstraint.PRIVATE;
+ }
+ if (flags.isProtected() && (fieldHolder.isSamePackage(holder))) {
+ return Inliner.InliningConstraint.PACKAGE;
+ }
+ }
+ return Inliner.InliningConstraint.NEVER;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FixedRegisterValue.java b/src/main/java/com/android/tools/r8/ir/code/FixedRegisterValue.java
new file mode 100644
index 0000000..c63d323
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/FixedRegisterValue.java
@@ -0,0 +1,41 @@
+// 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.ir.code;
+
+// Value that has a fixed register allocated. These are used for inserting spill, restore, and phi
+// moves in the spilling register allocator.
+public class FixedRegisterValue extends Value {
+ private final int register;
+
+ public FixedRegisterValue(MoveType type, int register) {
+ // Set local info to null since these values are never representatives of live-ranges.
+ super(-1, -1, type, null);
+ setNeedsRegister(true);
+ this.register = register;
+ }
+
+ public int getRegister() {
+ return register;
+ }
+
+ @Override
+ public boolean isFixedRegisterValue() {
+ return true;
+ }
+
+ @Override
+ public FixedRegisterValue asFixedRegisterValue() {
+ return this;
+ }
+
+ @Override
+ public boolean isConstant() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "r" + register;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Goto.java b/src/main/java/com/android/tools/r8/ir/code/Goto.java
new file mode 100644
index 0000000..5ca9199
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Goto.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.CfgPrinter;
+import java.util.List;
+
+public class Goto extends JumpInstruction {
+
+ public Goto() {
+ super(null);
+ }
+
+ public Goto(BasicBlock block) {
+ super(null);
+ setBlock(block);
+ }
+
+ public BasicBlock getTarget() {
+ assert getBlock().exit() == this;
+ List<BasicBlock> successors = getBlock().getSuccessors();
+ assert successors.size() >= 1;
+ return successors.get(successors.size() - 1);
+ }
+
+ public void setTarget(BasicBlock nextBlock) {
+ assert getBlock().exit() == this;
+ List<BasicBlock> successors = getBlock().getSuccessors();
+ assert successors.size() >= 1;
+ BasicBlock target = successors.get(successors.size() - 1);
+ target.getPredecessors().remove(getBlock());
+ successors.set(successors.size() - 1, nextBlock);
+ nextBlock.getPredecessors().add(getBlock());
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ builder.addGoto(this);
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ assert false : "Goto has no register arguments.";
+ return 0;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ assert false : "Goto defines no values.";
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ if (getBlock() != null && !getBlock().getSuccessors().isEmpty()) {
+ return super.toString() + "block " + getTarget().getNumber();
+ }
+ return super.toString() + "block <unknown>";
+ }
+
+ public void print(CfgPrinter printer) {
+ super.print(printer);
+ printer.append(" B").append(getTarget().getNumber());
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asGoto().getTarget() == getTarget();
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ assert other.isGoto();
+ assert false : "Not supported";
+ return 0;
+ }
+
+ @Override
+ public boolean isGoto() {
+ return true;
+ }
+
+ @Override
+ public Goto asGoto() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
new file mode 100644
index 0000000..94eeb55
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -0,0 +1,350 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
+import com.android.tools.r8.utils.CfgPrinter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+
+public class IRCode {
+
+ public final DexEncodedMethod method;
+
+ public LinkedList<BasicBlock> blocks;
+ public final ValueNumberGenerator valueNumberGenerator;
+
+ private BasicBlock normalExitBlock;
+ private boolean numbered = false;
+ private int nextInstructionNumber = 0;
+
+ public IRCode(
+ DexEncodedMethod method,
+ LinkedList<BasicBlock> blocks,
+ BasicBlock normalExitBlock,
+ ValueNumberGenerator valueNumberGenerator) {
+ this.method = method;
+ this.blocks = blocks;
+ this.normalExitBlock = normalExitBlock;
+ this.valueNumberGenerator = valueNumberGenerator;
+ }
+
+ private void ensureBlockNumbering() {
+ if (!numbered) {
+ numbered = true;
+ BasicBlock[] sorted = topologicallySortedBlocks();
+ for (int i = 0; i < sorted.length; i++) {
+ sorted[i].setNumber(i);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ ensureBlockNumbering();
+ StringBuilder builder = new StringBuilder();
+ builder.append("blocks:\n");
+ for (BasicBlock block : blocks) {
+ builder.append(block.toDetailedString());
+ builder.append("\n");
+ }
+ return builder.toString();
+ }
+
+ public void clearMarks() {
+ for (BasicBlock block : blocks) {
+ block.clearMark();
+ }
+ }
+
+ public void removeMarkedBlocks() {
+ ListIterator<BasicBlock> blockIterator = listIterator();
+ while (blockIterator.hasNext()) {
+ BasicBlock block = blockIterator.next();
+ if (block.isMarked()) {
+ blockIterator.remove();
+ if (block == normalExitBlock) {
+ normalExitBlock = null;
+ }
+ }
+ }
+ }
+
+ public void removeBlocks(List<BasicBlock> blocksToRemove) {
+ blocks.removeAll(blocksToRemove);
+ if (blocksToRemove.contains(normalExitBlock)) {
+ normalExitBlock = null;
+ }
+ }
+
+ /**
+ * Compute quasi topologically sorted list of the basic blocks using depth first search.
+ *
+ * TODO(ager): We probably want to compute strongly connected components and topologically
+ * sort strongly connected components instead. However, this is much better than having
+ * no sorting.
+ */
+ public BasicBlock[] topologicallySortedBlocks() {
+ return topologicallySortedBlocks(Collections.emptyList());
+ }
+
+ public BasicBlock[] topologicallySortedBlocks(List<BasicBlock> blocksToIgnore) {
+ clearMarks();
+ int reachableBlocks = blocks.size() - blocksToIgnore.size();
+ BasicBlock[] sorted = new BasicBlock[reachableBlocks];
+ BasicBlock entryBlock = blocks.getFirst();
+ int index = depthFirstSorting(entryBlock, sorted, reachableBlocks - 1);
+ assert index == -1;
+ return sorted;
+ }
+
+ private int depthFirstSorting(BasicBlock block, BasicBlock[] sorted, int index) {
+ if (!block.isMarked()) {
+ block.mark();
+ for (BasicBlock succ : block.getSuccessors()) {
+ index = depthFirstSorting(succ, sorted, index);
+ }
+ assert sorted[index] == null;
+ sorted[index] = block;
+ return index - 1;
+ }
+ return index;
+ }
+
+ public void print(CfgPrinter printer) {
+ ensureBlockNumbering();
+ for (BasicBlock block : blocks) {
+ block.print(printer);
+ }
+ }
+
+ public boolean isConsistentSSA() {
+ assert isConsistentGraph() && consistentDefUseChains() && validThrowingInstructions();
+ return true;
+ }
+
+ public boolean isConsistentGraph() {
+ assert consistentPredecessorSuccessors();
+ assert consistentCatchHandlers();
+ assert normalExitBlock == null || normalExitBlock.exit().isReturn();
+ return true;
+ }
+
+ private boolean consistentDefUseChains() {
+ Set<Value> values = new HashSet<>();
+
+ for (BasicBlock block : blocks) {
+ int predecessorCount = block.getPredecessors().size();
+ // Check that all phi uses are consistent.
+ for (Phi phi : block.getPhis()) {
+ assert phi.getOperands().size() == predecessorCount;
+ values.add(phi);
+ for (Value value : phi.getOperands()) {
+ values.add(value);
+ if (value.isPhi()) {
+ Phi phiOperand = value.asPhi();
+ assert phiOperand.getBlock().getPhis().contains(phiOperand);
+ assert phiOperand.uniquePhiUsers().contains(phi);
+ } else {
+ Instruction definition = value.definition;
+ assert definition.outValue() == value;
+ }
+ }
+ }
+ for (Instruction instruction : block.getInstructions()) {
+ assert instruction.getBlock() == block;
+ Value outValue = instruction.outValue();
+ if (outValue != null) {
+ values.add(outValue);
+ assert outValue.definition == instruction;
+ Value previousLocalValue = outValue.getPreviousLocalValue();
+ if (previousLocalValue != null) {
+ values.add(previousLocalValue);
+ assert previousLocalValue.debugUsers().contains(instruction);
+ }
+ }
+ for (Value value : instruction.inValues()) {
+ values.add(value);
+ assert value.uniqueUsers().contains(instruction);
+ if (value.isPhi()) {
+ Phi phi = value.asPhi();
+ assert phi.getBlock().getPhis().contains(phi);
+ } else {
+ Instruction definition = value.definition;
+ assert definition.outValue() == value;
+ }
+ }
+ }
+ }
+
+ for (Value value : values) {
+ assert consistentValueUses(value);
+ }
+
+ return true;
+ }
+
+ private boolean consistentValueUses(Value value) {
+ for (Instruction user : value.uniqueUsers()) {
+ assert user.inValues().contains(value);
+ }
+ for (Phi phiUser : value.uniquePhiUsers()) {
+ assert phiUser.getOperands().contains(value);
+ assert phiUser.getBlock().getPhis().contains(phiUser);
+ }
+ if (value.debugUsers() != null) {
+ for (Instruction debugUser : value.debugUsers()) {
+ assert debugUser.getPreviousLocalValue() == value;
+ }
+ }
+ return true;
+ }
+
+ private boolean consistentPredecessorSuccessors() {
+ for (BasicBlock block : blocks) {
+ // Check that all successors are distinct.
+ assert new HashSet<>(block.getSuccessors()).size() == block.getSuccessors().size();
+ for (BasicBlock succ : block.getSuccessors()) {
+ // Check that successors are in the block list.
+ assert blocks.contains(succ);
+ // Check that successors have this block as a predecessor.
+ assert succ.getPredecessors().contains(block);
+ }
+ // Check that all predecessors are distinct.
+ assert new HashSet<>(block.getPredecessors()).size() == block.getPredecessors().size();
+ for (BasicBlock pred : block.getPredecessors()) {
+ // Check that predecessors are in the block list.
+ assert blocks.contains(pred);
+ // Check that predecessors have this block as a successor.
+ assert pred.getSuccessors().contains(block);
+ }
+ }
+ return true;
+ }
+
+ private boolean consistentCatchHandlers() {
+ for (BasicBlock block : blocks) {
+ // Check that catch handlers are always the first successors of a block.
+ if (block.hasCatchHandlers()) {
+ assert block.exit().isGoto() || block.exit().isThrow();
+ CatchHandlers<Integer> catchHandlers = block.getCatchHandlersWithSuccessorIndexes();
+ // If there is a catch-all guard it must be the last.
+ List<DexType> guards = catchHandlers.getGuards();
+ int lastGuardIndex = guards.size() - 1;
+ for (int i = 0; i < guards.size(); i++) {
+ assert guards.get(i) != DexItemFactory.catchAllType || i == lastGuardIndex;
+ }
+ // Check that all successors except maybe the last are catch successors.
+ List<Integer> sortedHandlerIndices = new ArrayList<>(catchHandlers.getAllTargets());
+ sortedHandlerIndices.sort(Comparator.naturalOrder());
+ int firstIndex = sortedHandlerIndices.get(0);
+ int lastIndex = sortedHandlerIndices.get(sortedHandlerIndices.size() - 1);
+ assert firstIndex == 0;
+ assert lastIndex < sortedHandlerIndices.size();
+ int lastSuccessorIndex = block.getSuccessors().size() - 1;
+ assert lastIndex == lastSuccessorIndex // All successors are catch successors.
+ || lastIndex == lastSuccessorIndex - 1; // All but one successors are catch successors.
+ assert lastIndex == lastSuccessorIndex || !block.exit().isThrow();
+ }
+ }
+ return true;
+ }
+
+ private boolean validThrowingInstructions() {
+ for (BasicBlock block : blocks) {
+ if (block.hasCatchHandlers()) {
+ boolean seenThrowing = false;
+ for (Instruction instruction : block.getInstructions()) {
+ if (instruction.instructionTypeCanThrow()) {
+ assert !seenThrowing;
+ seenThrowing = true;
+ continue;
+ }
+ // After the throwing instruction only debug instructions an the final jump
+ // instruction is allowed.
+ // TODO(ager): For now allow const instructions due to the way consts are pushed
+ // towards their use
+ if (seenThrowing) {
+ assert instruction.isDebugInstruction()
+ || instruction.isJumpInstruction()
+ || instruction.isConstInstruction()
+ || instruction.isNewArrayFilledData();
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ public InstructionIterator instructionIterator() {
+ return new IRCodeInstructionsIterator(this);
+ }
+
+ void setNormalExitBlock(BasicBlock block) {
+ normalExitBlock = block;
+ }
+
+ public BasicBlock getNormalExitBlock() {
+ return normalExitBlock;
+ }
+
+ public ListIterator<BasicBlock> listIterator() {
+ return new BasicBlockIterator(this);
+ }
+
+ public ListIterator<BasicBlock> listIterator(int index) {
+ return new BasicBlockIterator(this, index);
+ }
+
+ public BasicBlock[] numberInstructions() {
+ BasicBlock[] blocks = topologicallySortedBlocks();
+ for (BasicBlock block : blocks) {
+ for (Instruction instruction : block.getInstructions()) {
+ instruction.setNumber(nextInstructionNumber);
+ nextInstructionNumber += LinearScanRegisterAllocator.INSTRUCTION_NUMBER_DELTA;
+ }
+ }
+ return blocks;
+ }
+
+ public int numberRemainingInstructions() {
+ InstructionIterator it = instructionIterator();
+ while (it.hasNext()) {
+ Instruction i = it.next();
+ if (i.getNumber() == -1) {
+ i.setNumber(nextInstructionNumber);
+ nextInstructionNumber += LinearScanRegisterAllocator.INSTRUCTION_NUMBER_DELTA;
+ }
+ }
+ return nextInstructionNumber;
+ }
+
+ public int getNextInstructionNumber() {
+ return nextInstructionNumber;
+ }
+
+ public List<Value> collectArguments() {
+ final List<Value> arguments = new ArrayList<>();
+ Iterator<Instruction> iterator = blocks.get(0).iterator();
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ if (instruction.isArgument()) {
+ arguments.add(instruction.asArgument().outValue());
+ }
+ }
+ assert arguments.size()
+ == method.method.proto.parameters.values.length + (method.accessFlags.isStatic() ? 0 : 1);
+ return arguments;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java
new file mode 100644
index 0000000..760a7ed
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java
@@ -0,0 +1,54 @@
+// 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.ir.code;
+
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
+public class IRCodeInstructionsIterator implements InstructionIterator {
+
+ private ListIterator<BasicBlock> blockIterator;
+ private InstructionListIterator instructionIterator;
+
+ public IRCodeInstructionsIterator(IRCode code) {
+ blockIterator = code.blocks.listIterator();
+ instructionIterator = blockIterator.next().listIterator();
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (instructionIterator.hasNext()) {
+ return true;
+ }
+ return blockIterator.hasNext();
+ }
+
+ @Override
+ public Instruction next() {
+ if (instructionIterator.hasNext()) {
+ return instructionIterator.next();
+ }
+ if (!blockIterator.hasNext()) {
+ throw new NoSuchElementException();
+ }
+ instructionIterator = blockIterator.next().listIterator();
+ assert instructionIterator.hasNext();
+ return instructionIterator.next();
+ }
+
+ public void add(Instruction instruction) {
+ instructionIterator.add(instruction);
+ }
+
+ @Override
+ public void remove() {
+ instructionIterator.remove();
+ }
+
+ @Override
+ public void replaceCurrentInstruction(Instruction newInstruction) {
+ instructionIterator.replaceCurrentInstruction(newInstruction);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java
new file mode 100644
index 0000000..f621b8c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -0,0 +1,167 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import static com.android.tools.r8.dex.Constants.U4BIT_MAX;
+import static com.android.tools.r8.dex.Constants.U8BIT_MAX;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.CfgPrinter;
+import java.util.List;
+
+public class If extends JumpInstruction {
+
+ public enum Type {
+ EQ, GE, GT, LE, LT, NE
+ }
+
+ private Type type;
+
+ public If(Type type, Value value) {
+ super(null, value);
+ this.type = type;
+ }
+
+ public If(Type type, List<Value> values) {
+ super(null, values);
+ this.type = type;
+ }
+
+ public boolean isZeroTest() {
+ return inValues.size() == 1;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public void invert() {
+ BasicBlock tmp = getTrueTarget();
+ setTrueTarget(fallthroughBlock());
+ setFallthroughBlock(tmp);
+ switch (type) {
+ case EQ:
+ type = Type.NE;
+ break;
+ case GE:
+ type = Type.LT;
+ break;
+ case GT:
+ type = Type.LE;
+ break;
+ case LE:
+ type = Type.GT;
+ break;
+ case LT:
+ type = Type.GE;
+ break;
+ case NE:
+ type = Type.EQ;
+ break;
+ default:
+ throw new Unreachable("Unknown if condition type.");
+ }
+ }
+
+ public BasicBlock getTrueTarget() {
+ assert getBlock().exit() == this;
+ List<BasicBlock> successors = getBlock().getSuccessors();
+ assert successors.size() >= 2;
+ return successors.get(successors.size() - 2);
+ }
+
+ public void setTrueTarget(BasicBlock block) {
+ assert getBlock().exit() == this;
+ List<BasicBlock> successors = getBlock().getSuccessors();
+ assert successors.size() >= 2;
+ successors.set(successors.size() - 2, block);
+ }
+
+ @Override
+ public BasicBlock fallthroughBlock() {
+ assert getBlock().exit() == this;
+ List<BasicBlock> successors = getBlock().getSuccessors();
+ assert successors.size() >= 2;
+ return successors.get(successors.size() - 1);
+ }
+
+ @Override
+ public void setFallthroughBlock(BasicBlock block) {
+ List<BasicBlock> successors = getBlock().getSuccessors();
+ successors.set(successors.size() - 1, block);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ builder.addIf(this);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " " + type + " block " + getTrueTarget().getNumber()
+ + " (fallthrough " + fallthroughBlock().getNumber() + ")";
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return isZeroTest() ? U8BIT_MAX : U4BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ assert false : "If instructions define no values.";
+ return 0;
+ }
+
+ @Override
+ public void print(CfgPrinter printer) {
+ super.print(printer);
+ printer.append(" B").append(getTrueTarget().getNumber());
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ If o = other.asIf();
+ return o.getTrueTarget() == getTrueTarget()
+ && o.fallthroughBlock() == fallthroughBlock()
+ && o.type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ assert other.isIf();
+ assert false : "Not supported";
+ return 0;
+ }
+
+
+ public BasicBlock targetFromCondition(int cond) {
+ switch (type) {
+ case EQ:
+ return cond == 0 ? getTrueTarget() : fallthroughBlock();
+ case NE:
+ return cond != 0 ? getTrueTarget() : fallthroughBlock();
+ case GE:
+ return cond >= 0 ? getTrueTarget() : fallthroughBlock();
+ case GT:
+ return cond > 0 ? getTrueTarget() : fallthroughBlock();
+ case LE:
+ return cond <= 0 ? getTrueTarget() : fallthroughBlock();
+ case LT:
+ return cond < 0 ? getTrueTarget() : fallthroughBlock();
+ }
+ throw new Unreachable("Unexpected condition type " + type);
+ }
+
+ @Override
+ public boolean isIf() {
+ return true;
+ }
+
+ @Override
+ public If asIf() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
new file mode 100644
index 0000000..9e5c972
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Iget;
+import com.android.tools.r8.code.IgetBoolean;
+import com.android.tools.r8.code.IgetByte;
+import com.android.tools.r8.code.IgetChar;
+import com.android.tools.r8.code.IgetObject;
+import com.android.tools.r8.code.IgetShort;
+import com.android.tools.r8.code.IgetWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import java.util.Arrays;
+
+public class InstanceGet extends FieldInstruction {
+
+ public InstanceGet(MemberType type, Value dest, Value object, DexField field) {
+ super(type, field, dest, object);
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ public Value object() {
+ assert inValues.size() == 1;
+ return inValues.get(0);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int destRegister = builder.allocatedRegister(dest(), getNumber());
+ int objectRegister = builder.allocatedRegister(object(), getNumber());
+ com.android.tools.r8.code.Instruction instruction;
+ switch (type) {
+ case SINGLE:
+ instruction = new Iget(destRegister, objectRegister, field);
+ break;
+ case WIDE:
+ instruction = new IgetWide(destRegister, objectRegister, field);
+ break;
+ case OBJECT:
+ instruction = new IgetObject(destRegister, objectRegister, field);
+ break;
+ case BOOLEAN:
+ instruction = new IgetBoolean(destRegister, objectRegister, field);
+ break;
+ case BYTE:
+ instruction = new IgetByte(destRegister, objectRegister, field);
+ break;
+ case CHAR:
+ instruction = new IgetChar(destRegister, objectRegister, field);
+ break;
+ case SHORT:
+ instruction = new IgetShort(destRegister, objectRegister, field);
+ break;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ builder.add(this, instruction);
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return true;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U4BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U4BIT_MAX;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ InstanceGet o = other.asInstanceGet();
+ return o.field == field && o.type == type;
+ }
+
+ public int compareNonValueParts(Instruction other) {
+ InstanceGet o = other.asInstanceGet();
+ int result = field.slowCompareTo(o.field);
+ if (result != 0) {
+ return result;
+ }
+ return type.ordinal() - o.type.ordinal();
+ }
+
+ @Override
+ public boolean isInstanceGet() {
+ return true;
+ }
+
+ @Override
+ public InstanceGet asInstanceGet() {
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "; field: " + field.toSourceString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
new file mode 100644
index 0000000..3fe498d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+
+public class InstanceOf extends Instruction {
+
+ private final DexType type;
+
+ public InstanceOf(Value dest, Value value, DexType type) {
+ super(dest, value);
+ this.type = type;
+ }
+
+ public DexType type() {
+ return type;
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ public Value value() {
+ return inValues.get(0);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int dest = builder.allocatedRegister(dest(), getNumber());
+ int value = builder.allocatedRegister(value(), getNumber());
+ builder.add(this, new com.android.tools.r8.code.InstanceOf(dest, value, type));
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U4BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U4BIT_MAX;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return true;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asInstanceOf().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.slowCompareTo(other.asInstanceOf().type);
+ }
+
+ @Override
+ public boolean isInstanceOf() {
+ return true;
+ }
+
+ @Override
+ public InstanceOf asInstanceOf() {
+ return this;
+ }
+
+ @Override
+ public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+ DexClass targetClass = info.definitionFor(type());
+ if (targetClass == null) {
+ return InliningConstraint.NEVER;
+ }
+ if (targetClass.accessFlags.isPublic()) {
+ return InliningConstraint.ALWAYS;
+ }
+ return InliningConstraint.NEVER;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
new file mode 100644
index 0000000..7ec8eaf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Iput;
+import com.android.tools.r8.code.IputBoolean;
+import com.android.tools.r8.code.IputByte;
+import com.android.tools.r8.code.IputChar;
+import com.android.tools.r8.code.IputObject;
+import com.android.tools.r8.code.IputShort;
+import com.android.tools.r8.code.IputWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import java.util.List;
+
+public class InstancePut extends FieldInstruction {
+
+ public InstancePut(MemberType type, List<Value> values, DexField field) {
+ super(type, field, null, values);
+ }
+
+ public Value value() {
+ return inValues.get(0);
+ }
+
+ public Value object() {
+ return inValues.get(1);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int valueRegister = builder.allocatedRegister(value(), getNumber());
+ int objectRegister = builder.allocatedRegister(object(), getNumber());
+ switch (type) {
+ case SINGLE:
+ instruction = new Iput(valueRegister, objectRegister, field);
+ break;
+ case WIDE:
+ instruction = new IputWide(valueRegister, objectRegister, field);
+ break;
+ case OBJECT:
+ instruction = new IputObject(valueRegister, objectRegister, field);
+ break;
+ case BOOLEAN:
+ instruction = new IputBoolean(valueRegister, objectRegister, field);
+ break;
+ case BYTE:
+ instruction = new IputByte(valueRegister, objectRegister, field);
+ break;
+ case CHAR:
+ instruction = new IputChar(valueRegister, objectRegister, field);
+ break;
+ case SHORT:
+ instruction = new IputShort(valueRegister, objectRegister, field);
+ break;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ builder.add(this, instruction);
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return true;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ InstancePut o = other.asInstancePut();
+ return o.field == field && o.type == type;
+ }
+
+ public int compareNonValueParts(Instruction other) {
+ InstancePut o = other.asInstancePut();
+ int result = field.slowCompareTo(o.field);
+ if (result != 0) {
+ return result;
+ }
+ return type.ordinal() - o.type.ordinal();
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U4BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ assert false : "InstancePut instructions define no values.";
+ return 0;
+ }
+
+ @Override
+ public boolean isInstancePut() {
+ return true;
+ }
+
+ @Override
+ public InstancePut asInstancePut() {
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "; field: " + field.toSourceString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
new file mode 100644
index 0000000..d880c72
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -0,0 +1,795 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Value.DebugInfo;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class Instruction {
+
+ protected Value outValue = null;
+ protected final List<Value> inValues = new ArrayList<>();
+ private BasicBlock block = null;
+ private int number = -1;
+
+ protected Instruction(Value outValue) {
+ setOutValue(outValue);
+ }
+
+ protected Instruction(Value outValue, Value inValue) {
+ addInValue(inValue);
+ setOutValue(outValue);
+ }
+
+ protected Instruction(Value outValue, List<? extends Value> inValues) {
+ if (inValues != null) {
+ for (Value v : inValues) {
+ addInValue(v);
+ }
+ }
+ setOutValue(outValue);
+ }
+
+ public List<Value> inValues() {
+ return inValues;
+ }
+
+ protected void addInValue(Value value) {
+ if (value != null) {
+ inValues.add(value);
+ value.addUser(this);
+ }
+ }
+
+ public Value outValue() {
+ return outValue;
+ }
+
+ public void setOutValue(Value value) {
+ assert outValue == null || !outValue.hasUsersInfo() || outValue.numberOfAllUsers() == 0;
+ outValue = value;
+ if (outValue != null) {
+ outValue.definition = this;
+ Value previousLocalValue = getPreviousLocalValue();
+ if (previousLocalValue != null) {
+ previousLocalValue.addDebugUser(this);
+ }
+ }
+ }
+
+ public static void clearUserInfo(Instruction instruction) {
+ if (instruction.outValue != null) {
+ instruction.outValue.clearUsersInfo();
+ }
+ instruction.inValues.forEach(Value::clearUsersInfo);
+ }
+
+ public final MoveType outType() {
+ return outValue.outType();
+ }
+
+ public abstract void buildDex(DexBuilder builder);
+
+ public void replacePhi(Phi phi, Value value) {
+ for (int i = 0; i < inValues.size(); i++) {
+ if (phi == inValues.get(i)) {
+ inValues.set(i, value);
+ value.addUser(this);
+ }
+ }
+ }
+
+ /**
+ * Returns the basic block containing this instruction.
+ */
+ public BasicBlock getBlock() {
+ assert block != null;
+ return block;
+ }
+
+ /**
+ * Set the basic block of this instruction. See IRBuilder.
+ */
+ public void setBlock(BasicBlock block) {
+ assert block != null;
+ this.block = block;
+ }
+
+ /**
+ * Clear the basic block of this instruction. Use when removing an instruction from a block.
+ */
+ public void clearBlock() {
+ assert block != null;
+ block = null;
+ }
+
+ public String getInstructionName() {
+ return getClass().getSimpleName();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(getInstructionName());
+ for (int i = builder.length(); i < 20; i++) {
+ builder.append(" ");
+ }
+ builder.append(" ");
+ if (outValue != null) {
+ builder.append(outValue);
+ builder.append(" <- ");
+ }
+ if (!inValues.isEmpty()) {
+ StringUtils.append(builder, inValues, ", ", BraceType.NONE);
+ }
+ return builder.toString();
+ }
+
+ public void print(CfgPrinter printer) {
+ int uses = 0;
+ String value;
+ if (outValue == null) {
+ value = printer.makeUnusedValue();
+ } else {
+ if (outValue.hasUsersInfo()) {
+ uses = outValue.uniqueUsers().size() + outValue.uniquePhiUsers().size();
+ }
+ value = "v" + outValue.getNumber();
+ }
+ printer
+ .print(0) // bci
+ .sp().append(uses) // use
+ .sp().append(value) // tid
+ .sp().append(getClass().getSimpleName());
+ for (Value in : inValues) {
+ printer.append(" v").append(in.getNumber());
+ }
+ }
+
+ public void printLIR(CfgPrinter printer) {
+ // TODO(ager): Improve the instruction printing. Use different name for values so that the
+ // HIR and LIR values are not confused in the c1 visualizer.
+ printer.print(number).sp().append(toString());
+ }
+
+ public int getNumber() {
+ return number;
+ }
+
+ public void setNumber(int number) {
+ assert number != -1;
+ this.number = number;
+ }
+
+ /**
+ * Compare equality of two class-equivalent instructions modulo their values.
+ *
+ * <p>It is a precondition to this method that this.getClass() == other.getClass().
+ */
+ public abstract boolean identicalNonValueParts(Instruction other);
+
+ public abstract int compareNonValueParts(Instruction other);
+
+ private boolean identicalAfterRegisterAllocation(
+ Value a, int aInstr, Value b, int bInstr, RegisterAllocator allocator) {
+ if (a.needsRegister() != b.needsRegister()) {
+ return false;
+ }
+ if (a.needsRegister()) {
+ if (allocator.getRegisterForValue(a, aInstr) != allocator.getRegisterForValue(b, bInstr)) {
+ return false;
+ }
+ } else {
+ ConstNumber aNum = a.getConstInstruction().asConstNumber();
+ ConstNumber bNum = b.getConstInstruction().asConstNumber();
+ if (!aNum.identicalNonValueParts(bNum)) {
+ return false;
+ }
+ }
+ if (a.outType() != b.outType()) {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean identicalAfterRegisterAllocation(Instruction other, RegisterAllocator allocator) {
+ if (other.getClass() != getClass()) {
+ return false;
+ }
+ if (!identicalNonValueParts(other)) {
+ return false;
+ }
+ if (isInvokeDirect() && !asInvokeDirect().sameConstructorReceiverValue(other.asInvoke())) {
+ return false;
+ }
+ if (outValue != null) {
+ if (other.outValue == null) {
+ return false;
+ }
+ if (!identicalAfterRegisterAllocation(
+ outValue, getNumber(), other.outValue, other.getNumber(), allocator)) {
+ return false;
+ }
+ } else if (other.outValue != null) {
+ return false;
+ }
+ // Check that all input values have the same type and allocated registers.
+ if (inValues.size() != other.inValues.size()) {
+ return false;
+ }
+ for (int j = 0; j < inValues.size(); j++) {
+ Value in0 = inValues.get(j);
+ Value in1 = other.inValues.get(j);
+ if (!identicalAfterRegisterAllocation(in0, getNumber(), in1, other.getNumber(), allocator)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if this instruction may throw an exception.
+ */
+ public boolean instructionTypeCanThrow() {
+ return false;
+ }
+
+ public boolean instructionInstanceCanThrow() {
+ return instructionTypeCanThrow();
+ }
+
+ /** Returns true is this instruction can be treated as dead code if its outputs are not used. */
+ public boolean canBeDeadCode(InternalOptions options) {
+ return !instructionInstanceCanThrow();
+ }
+
+ /**
+ * Returns true if this instruction need this value in a register.
+ */
+ public boolean needsValueInRegister(Value value) {
+ return true;
+ }
+
+ /**
+ * Returns true if the out value of this instruction is a constant.
+ *
+ * @return whether the out value of this instruction is a constant.
+ */
+ public boolean isOutConstant() {
+ return false;
+ }
+
+ /**
+ * Returns the ConstInstruction defining the constant out value if the out value is constant.
+ *
+ * @return ConstInstruction or null.
+ */
+ public ConstInstruction getOutConstantConstInstruction() {
+ return null;
+ }
+
+ public abstract int maxInValueRegister();
+
+ public abstract int maxOutValueRegister();
+
+ public DebugInfo getDebugInfo() {
+ return outValue == null ? null : outValue.getDebugInfo();
+ }
+
+ public DebugLocalInfo getLocalInfo() {
+ return outValue == null ? null : outValue.getLocalInfo();
+ }
+
+ public Value getPreviousLocalValue() {
+ return outValue == null ? null : outValue.getPreviousLocalValue();
+ }
+
+ public void replacePreviousLocalValue(Value value) {
+ outValue.replacePreviousLocalValue(value);
+ }
+
+ public boolean isArrayGet() {
+ return false;
+ }
+
+ public ArrayGet asArrayGet() {
+ return null;
+ }
+
+ public boolean isArrayLength() {
+ return false;
+ }
+
+ public ArrayLength asArrayLength() {
+ return null;
+ }
+
+ public boolean isArrayPut() {
+ return false;
+ }
+
+ public ArrayPut asArrayPut() {
+ return null;
+ }
+
+ public boolean isArgument() {
+ return false;
+ }
+
+ public Argument asArgument() {
+ return null;
+ }
+
+ public boolean isArithmeticBinop() {
+ return false;
+ }
+
+ public ArithmeticBinop asArithmeticBinop() {
+ return null;
+ }
+
+ public boolean isBinop() {
+ return false;
+ }
+
+ public Binop asBinop() {
+ return null;
+ }
+
+ public boolean isUnop() {
+ return false;
+ }
+
+ public Unop asUnop() {
+ return null;
+ }
+
+ public boolean isCheckCast() {
+ return false;
+ }
+
+ public CheckCast asCheckCast() {
+ return null;
+ }
+
+ public boolean isConstNumber() {
+ return false;
+ }
+
+ public ConstNumber asConstNumber() {
+ return null;
+ }
+
+ public boolean isConstInstruction() {
+ return false;
+ }
+
+ public ConstInstruction asConstInstruction() {
+ return null;
+ }
+
+ public boolean isConstClass() {
+ return false;
+ }
+
+ public ConstClass asConstClass() {
+ return null;
+ }
+
+ public boolean isConstString() {
+ return false;
+ }
+
+ public ConstString asConstString() {
+ return null;
+ }
+
+ public boolean isCmp() {
+ return false;
+ }
+
+ public Cmp asCmp() {
+ return null;
+ }
+
+ public boolean isJumpInstruction() {
+ return false;
+ }
+
+ public JumpInstruction asJumpInstruction() {
+ return null;
+ }
+
+ public boolean isGoto() {
+ return false;
+ }
+
+ public Goto asGoto() {
+ return null;
+ }
+
+ public boolean isIf() {
+ return false;
+ }
+
+ public If asIf() {
+ return null;
+ }
+
+ public boolean isSwitch() {
+ return false;
+ }
+
+ public Switch asSwitch() {
+ return null;
+ }
+
+ public boolean isInstanceGet() {
+ return false;
+ }
+
+ public InstanceGet asInstanceGet() {
+ return null;
+ }
+
+ public boolean isInstanceOf() {
+ return false;
+ }
+
+ public InstanceOf asInstanceOf() {
+ return null;
+ }
+
+ public boolean isInstancePut() {
+ return false;
+ }
+
+ public InstancePut asInstancePut() {
+ return null;
+ }
+
+ public boolean isInvoke() {
+ return false;
+ }
+
+ public Invoke asInvoke() {
+ return null;
+ }
+
+ public boolean isMonitor() {
+ return false;
+ }
+
+ public Monitor asMonitor() {
+ return null;
+ }
+
+ public boolean isMove() {
+ return false;
+ }
+
+ public Move asMove() {
+ return null;
+ }
+
+ public boolean isNewArrayEmpty() {
+ return false;
+ }
+
+ public NewArrayEmpty asNewArrayEmpty() {
+ return null;
+ }
+
+ public boolean isNewArrayFilledData() {
+ return false;
+ }
+
+ public NewArrayFilledData asNewArrayFilledData() {
+ return null;
+ }
+
+ public boolean isNeg() {
+ return false;
+ }
+
+ public Neg asNeg() {
+ return null;
+ }
+
+ public boolean isNewInstance() {
+ return false;
+ }
+
+ public NewInstance asNewInstance() {
+ return null;
+ }
+
+ public boolean isNot() {
+ return false;
+ }
+
+ public Not asNot() {
+ return null;
+ }
+
+ public boolean isNumberConversion() {
+ return false;
+ }
+
+ public NumberConversion asNumberConversion() {
+ return null;
+ }
+
+ public boolean isReturn() {
+ return false;
+ }
+
+ public Return asReturn() {
+ return null;
+ }
+
+ public boolean isThrow() {
+ return false;
+ }
+
+ public Throw asThrow() {
+ return null;
+ }
+
+ public boolean isStaticGet() {
+ return false;
+ }
+
+ public StaticGet asStaticGet() {
+ return null;
+ }
+
+ public boolean isStaticPut() {
+ return false;
+ }
+
+ public StaticPut asStaticPut() {
+ return null;
+ }
+
+ public boolean isAdd() {
+ return false;
+ }
+
+ public Add asAdd() {
+ return null;
+ }
+
+ public boolean isSub() {
+ return false;
+ }
+
+ public Sub asSub() {
+ return null;
+ }
+
+ public boolean isMul() {
+ return false;
+ }
+
+ public Mul asMul() {
+ return null;
+ }
+
+ public boolean isDiv() {
+ return false;
+ }
+
+ public Div asDiv() {
+ return null;
+ }
+
+ public boolean isRem() {
+ return false;
+ }
+
+ public Rem asRem() {
+ return null;
+ }
+
+ public boolean isLogicalBinop() {
+ return false;
+ }
+
+ public LogicalBinop asLogicalBinop() {
+ return null;
+ }
+
+ public boolean isShl() {
+ return false;
+ }
+
+ public Shl asShl() {
+ return null;
+ }
+
+ public boolean isShr() {
+ return false;
+ }
+
+ public Shr asShr() {
+ return null;
+ }
+
+ public boolean isUshr() {
+ return false;
+ }
+
+ public Ushr asUshr() {
+ return null;
+ }
+
+ public boolean isAnd() {
+ return false;
+ }
+
+ public And asAnd() {
+ return null;
+ }
+
+ public boolean isOr() {
+ return false;
+ }
+
+ public Or asOr() {
+ return null;
+ }
+
+ public boolean isXor() {
+ return false;
+ }
+
+ public Xor asXor() {
+ return null;
+ }
+
+ public boolean isMoveException() {
+ return false;
+ }
+
+ public MoveException asMoveException() {
+ return null;
+ }
+
+ public boolean isDebugInstruction() {
+ return isDebugPosition()
+ || isDebugLocalWrite()
+ || isDebugLocalRead()
+ || isDebugLocalUninitialized();
+ }
+
+ public boolean isDebugPosition() {
+ return false;
+ }
+
+ public DebugPosition asDebugPosition() {
+ return null;
+ }
+
+ public boolean isDebugLocalUninitialized() {
+ return false;
+ }
+
+ public DebugLocalUninitialized asDebugLocalUninitialized() {
+ return null;
+ }
+
+ public boolean isDebugLocalWrite() {
+ return false;
+ }
+
+ public DebugLocalWrite asDebugLocalWrite() {
+ return null;
+ }
+
+ public boolean isDebugLocalRead() {
+ return false;
+ }
+
+ public DebugLocalRead asDebugLocalRead() {
+ return null;
+ }
+
+ public boolean isInvokeMethod() {
+ return false;
+ }
+
+ public InvokeMethod asInvokeMethod() {
+ return null;
+ }
+
+ public boolean isInvokeNewArray() {
+ return false;
+ }
+
+ public InvokeNewArray asInvokeNewArray() {
+ return null;
+ }
+
+ public boolean isInvokeCustom() {
+ return false;
+ }
+
+ public InvokeCustom asInvokeCustom() {
+ return null;
+ }
+
+ public boolean isInvokeDirect() {
+ return false;
+ }
+
+ public InvokeDirect asInvokeDirect() {
+ return null;
+ }
+
+ public boolean isInvokeInterface() {
+ return false;
+ }
+
+ public InvokeInterface asInvokeInterface() {
+ return null;
+ }
+
+ public boolean isInvokeStatic() {
+ return false;
+ }
+
+ public InvokeStatic asInvokeStatic() {
+ return null;
+ }
+
+ public boolean isInvokeSuper() {
+ return false;
+ }
+
+ public InvokeSuper asInvokeSuper() {
+ return null;
+ }
+
+ public boolean isInvokeVirtual() {
+ return false;
+ }
+
+ public InvokeVirtual asInvokeVirtual() {
+ return null;
+ }
+
+ public boolean isInvokePolymorphic() {
+ return false;
+ }
+
+ public InvokePolymorphic asInvokePolymorphic() {
+ return null;
+ }
+
+ public boolean canBeFolded() {
+ return false;
+ }
+
+ public ConstInstruction fold(ValueNumberGenerator valueNumberGenerator) {
+ throw new Unreachable("Unsupported folding for " + this);
+ }
+
+ // Returns the inlining constraint for this instruction.
+ public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+ return InliningConstraint.NEVER;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java
new file mode 100644
index 0000000..997b092
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java
@@ -0,0 +1,36 @@
+// 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.ir.code;
+
+import java.util.Iterator;
+
+public interface InstructionIterator extends Iterator<Instruction> {
+ /**
+ * Replace the current instruction (aka the {@link Instruction} returned by the previous call to
+ * {@link #next} with the passed in <code>newInstruction</code>.
+ * <p>
+ * The current instruction will be completely detached from the instruction stream with uses
+ * of its in-values removed.
+ * <p>
+ * If the current instruction produces an out-value the new instruction must also produce
+ * an out-value, and all uses of the current instructions out-value will be replaced by the
+ * new instructions out-value.
+ * <p>
+ * The debug information of the current instruction will be attached to the new instruction.
+ *
+ * @param newInstruction the instruction to insert instead of the current.
+ */
+ void replaceCurrentInstruction(Instruction newInstruction);
+
+ /**
+ * Adds an instruction. The instruction will be added just before the current
+ * cursor position.
+ *
+ * The instruction will be assigned to the block it is added to.
+ *
+ * @param instruction The instruction to add.
+ */
+ void add(Instruction instruction);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
new file mode 100644
index 0000000..b9815a1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -0,0 +1,177 @@
+// 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.ir.code;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.function.Predicate;
+
+public interface InstructionListIterator extends ListIterator<Instruction> {
+
+ /**
+ * Peek the previous instruction.
+ *
+ * @return what will be returned by calling {@link #previous}. If there is no previous instruction
+ * <code>null</code> is returned.
+ */
+ default Instruction peekPrevious() {
+ Instruction previous = null;
+ if (hasPrevious()) {
+ previous = previous();
+ next();
+ }
+ return previous;
+ }
+
+ /**
+ * Peek the next instruction.
+ *
+ * @return what will be returned by calling {@link #next}. If there is no next instruction
+ * <code>null</code> is returned.
+ */
+ default Instruction peekNext() {
+ Instruction next = null;
+ if (hasNext()) {
+ next = next();
+ previous();
+ }
+ return next;
+ }
+
+ /**
+ * Continue to call {@link #next} while {@code predicate} tests {@code false}.
+ *
+ * @returns the instruction that matched the predicate or {@code null} if all instructions fails
+ * the predicate test
+ */
+ default Instruction nextUntil(Predicate<Instruction> predicate) {
+ while (hasNext()) {
+ Instruction instruction = next();
+ if (predicate.test(instruction)) {
+ return instruction;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Remove the current instruction (aka the {@link Instruction} returned by the previous call to
+ * {@link #next}) without updating its def/use chains.
+ * <p>
+ * This is useful for instance when moving an instruction to another block that still dominates
+ * all its uses. In order to do that you would detach the instruction from the original
+ * block and add it to the new block.
+ */
+ void detach();
+
+ /**
+ * Replace the current instruction (aka the {@link Instruction} returned by the previous call to
+ * {@link #next} with the passed in <code>newInstruction</code>.
+ *
+ * The current instruction will be completely detached from the instruction stream with uses
+ * of its in-values removed.
+ *
+ * If the current instruction produces an out-value the new instruction must also produce
+ * an out-value, and all uses of the current instructions out-value will be replaced by the
+ * new instructions out-value.
+ *
+ * @param newInstruction the instruction to insert instead of the current.
+ */
+ void replaceCurrentInstruction(Instruction newInstruction);
+
+ /**
+ * Split the block into two blocks at the point of the {@link ListIterator} cursor. The existing
+ * block will have all the instructions before the cursor, and the new block all the
+ * instructions after the cursor.
+ *
+ * If the current block has catch handlers these catch handlers will be attached to the block
+ * containing the throwing instruction after the split.
+ *
+ * @param code the IR code for the block this iterator originates from.
+ * @param blockIterator basic block iterator used to iterate the blocks. This must be positioned
+ * just after the block for which this is the instruction iterator. After this method returns it
+ * will be positioned just after the basic block returned.
+ * @return Returns the new block with the instructions after the cursor.
+ */
+ BasicBlock split(IRCode code, ListIterator<BasicBlock> blockIterator);
+
+
+ default BasicBlock split(IRCode code) {
+ return split(code, null);
+ }
+
+ /**
+ * Split the block into three blocks. The first split is at the point of the {@link ListIterator}
+ * cursor and the second split is <code>instructions</code> after the cursor. The existing
+ * block will have all the instructions before the cursor, and the two new blocks all the
+ * instructions after the cursor.
+ *
+ * If the current block have catch handlers these catch handlers will be attached to the block
+ * containing the throwing instruction after the split.
+ *
+ * @param instructions the number of instructions to include in the second block.
+ * @param code the IR code for the block this iterator originates from.
+ * @param blockIterator basic block iterator used to iterate the blocks. This must be positioned
+ * just after the block for this is the instruction iterator. After this method returns it will be
+ * positioned just after the second block inserted. That is after the successor of the block
+ * returned.
+ * @return Returns the new block with the instructions right after the cursor.
+ */
+ // TODO(sgjesse): Refactor to avoid the need for passing code and blockIterator.
+ BasicBlock split(int instructions, IRCode code, ListIterator<BasicBlock> blockIterator);
+
+ /**
+ * See {@link #split(int, IRCode, ListIterator)}.
+ */
+ default BasicBlock split(int instructions, IRCode code) {
+ return split(instructions, code, null);
+ }
+
+ /**
+ * Inline the code in {@code inlinee} into {@code code}, replacing the invoke instruction at the
+ * position after the cursor.
+ *
+ * The instruction at the position after cursor must be an invoke that matches the signature for
+ * the code in {@code inlinee}.
+ *
+ * With one exception (see below) both the calling code and the inlinee can have catch handlers.
+ *
+ * <strong>EXCEPTION:</strong> If the invoke instruction is covered by catch handlers, and the
+ * code for {@code inlinee} always throws (does not have a normal return) inlining is currently
+ * <strong>NOT</strong> supported.
+ *
+ * @param code the IR code for the block this iterator originates from.
+ * @param inlinee the IR code for the block this iterator originates from.
+ * @param blockIterator basic block iterator used to iterate the blocks. This must be positioned
+ * just after the block for which this is the instruction iterator. After this method returns it
+ * will be positioned just after the basic block returned.
+ * @param blocksToRemove list passed where blocks that where detached from the graph, but not
+ * removed are added. When inlining an inlinee that always throws blocks in the <code>code</code>
+ * can be detached, and not simply removed unsing the passed <code>blockIterator</code>. When
+ * iterating using <code>blockIterator</code> after then method returns the blocks in this list
+ * must be skipped when iterating with the active <code>blockIterator</code> and ultimately
+ * removed.
+ * @return the basic block with the instructions right after the inlining. This can be a block
+ * which can also be in the <code>blocksToRemove</code> list.
+ */
+ // TODO(sgjesse): Refactor to avoid the need for passing code.
+ // TODO(sgjesse): Refactor to avoid the need for passing blocksToRemove.
+ // TODO(sgjesse): Maybe don't return a BasicBlock, as it can be in blocksToRemove.
+ // TODO(sgjesse): Maybe find a better place for this method.
+ // TODO(sgjesse): Support inlinee with throwing instructions for invokes with existing handlers.
+ BasicBlock inlineInvoke(IRCode code, IRCode inlinee, ListIterator<BasicBlock> blockIterator,
+ List<BasicBlock> blocksToRemove);
+
+ /**
+ * See {@link #inlineInvoke(IRCode, IRCode, ListIterator, List<BasicBlock>)}.
+ */
+ default BasicBlock inlineInvoke(IRCode code, IRCode inlinee) {
+ List<BasicBlock> blocksToRemove = new ArrayList<>();
+ BasicBlock result = inlineInvoke(code, inlinee, null, blocksToRemove);
+ code.removeBlocks(blocksToRemove);
+ return result;
+ }
+}
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
new file mode 100644
index 0000000..5b516b7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -0,0 +1,193 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.MoveResult;
+import com.android.tools.r8.code.MoveResultObject;
+import com.android.tools.r8.code.MoveResultWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import java.util.List;
+
+public abstract class Invoke extends Instruction {
+
+ public enum Type {
+ DIRECT,
+ INTERFACE,
+ STATIC,
+ SUPER,
+ VIRTUAL,
+ NEW_ARRAY,
+ CUSTOM,
+ POLYMORPHIC
+ }
+
+ public Invoke(Value result, List<Value> arguments) {
+ super(result, arguments);
+ }
+
+ public static Invoke create(
+ Type type, DexItem target, DexProto proto, Value result, List<Value> arguments) {
+ switch (type) {
+ case DIRECT:
+ return new InvokeDirect((DexMethod) target, result, arguments);
+ case INTERFACE:
+ return new InvokeInterface((DexMethod) target, result, arguments);
+ case STATIC:
+ return new InvokeStatic((DexMethod) target, result, arguments);
+ case SUPER:
+ return new InvokeSuper((DexMethod) target, result, arguments);
+ case VIRTUAL:
+ return new InvokeVirtual((DexMethod) target, result, arguments);
+ case NEW_ARRAY:
+ return new InvokeNewArray((DexType) target, result, arguments);
+ case CUSTOM:
+ throw new Unreachable("Use InvokeCustom constructor instead");
+ case POLYMORPHIC:
+ return new InvokePolymorphic((DexMethod) target, proto, result, arguments);
+ }
+ throw new Unreachable("Unknown invoke type: " + type);
+ }
+
+ public static Instruction createFromTemplate(
+ Invoke template, Value outValue, List<Value> inValues) {
+ if (template.isInvokeMethod()) {
+ return create(template.getType(),
+ template.asInvokeMethod().getInvokedMethod(),
+ template.isInvokePolymorphic() ? template.asInvokePolymorphic().getProto() : null,
+ outValue,
+ inValues);
+ }
+
+ if (template.isInvokeNewArray()) {
+ return new InvokeNewArray(template.asInvokeNewArray().getArrayType(), outValue, inValues);
+ }
+
+ assert template.isInvokeCustom();
+ InvokeCustom custom = template.asInvokeCustom();
+ return new InvokeCustom(custom.getCallSite(), outValue, inValues);
+ }
+
+ abstract public Type getType();
+
+ public List<Value> arguments() {
+ return inValues;
+ }
+
+ public int requiredArgumentRegisters() {
+ int registers = 0;
+ for (Value inValue : inValues) {
+ registers += inValue.requiredRegisters();
+ }
+ return registers;
+ }
+
+ protected int argumentRegisterValue(int i, DexBuilder builder) {
+ if (i < arguments().size()) {
+ return builder.allocatedRegister(arguments().get(i), getNumber());
+ }
+ return 0;
+ }
+
+ protected int fillArgumentRegisters(DexBuilder builder, int[] registers) {
+ int i = 0;
+ for (Value value : arguments()) {
+ int register = builder.allocatedRegister(value, getNumber());
+ for (int j = 0; j < value.requiredRegisters(); j++) {
+ assert i < 5;
+ registers[i++] = register++;
+ }
+ }
+ return i;
+ }
+
+ protected boolean hasHighArgumentRegister(DexBuilder builder) {
+ for (Value value : arguments()) {
+ if (builder.argumentValueUsesHighRegister(value, getNumber())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected boolean argumentsConsecutive(DexBuilder builder) {
+ Value value = arguments().get(0);
+ int next = builder.allocatedRegister(value, getNumber()) + value.requiredRegisters();
+ for (int i = 1; i < arguments().size(); i++) {
+ value = arguments().get(i);
+ assert next == builder.allocatedRegister(value, getNumber());
+ next += value.requiredRegisters();
+ }
+ return true;
+ }
+
+ protected void addInvokeAndMoveResult(com.android.tools.r8.code.Instruction instruction, DexBuilder builder) {
+ if (outValue != null && outValue.needsRegister()) {
+ int register = builder.allocatedRegister(outValue, getNumber());
+ com.android.tools.r8.code.Instruction moveResult;
+ switch (outType()) {
+ case SINGLE:
+ moveResult = new MoveResult(register);
+ break;
+ case WIDE:
+ moveResult = new MoveResultWide(register);
+ break;
+ case OBJECT:
+ moveResult = new MoveResultObject(register);
+ break;
+ default:
+ throw new Unreachable("Unexpected result type " + outType());
+ }
+ builder.add(this, new com.android.tools.r8.code.Instruction[]{instruction, moveResult});
+ } else {
+ builder.add(this, instruction);
+ }
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return true;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ if (requiredArgumentRegisters() > 5) {
+ return Constants.U16BIT_MAX;
+ }
+ return Constants.U4BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ abstract protected String getTypeString();
+
+ @Override
+ public String getInstructionName() {
+ return "Invoke-" + getTypeString();
+ }
+
+ // This method is used for inlining.
+ // It returns the target method iff this invoke has only one target.
+ abstract public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo);
+
+ @Override
+ public boolean isInvoke() {
+ return true;
+ }
+
+ @Override
+ public Invoke asInvoke() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
new file mode 100644
index 0000000..2c7ea4b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -0,0 +1,91 @@
+// 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.ir.code;
+
+import com.android.tools.r8.code.InvokeCustomRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import java.util.List;
+
+public final class InvokeCustom extends Invoke {
+
+ private final DexCallSite callSite;
+
+ public InvokeCustom(DexCallSite callSite, Value result, List<Value> arguments) {
+ super(result, arguments);
+ assert callSite != null;
+ this.callSite = callSite;
+ }
+
+ public DexCallSite getCallSite() {
+ return callSite;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.CUSTOM;
+ }
+
+ @Override
+ protected String getTypeString() {
+ return "Custom";
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "; call site: " + callSite.toSourceString();
+ }
+
+ @Override
+ public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+ // Target method can not be known at compile time.
+ return null;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int argumentRegisters = requiredArgumentRegisters();
+ builder.requestOutgoingRegisters(argumentRegisters);
+ if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+ assert argumentsConsecutive(builder);
+ int firstRegister = argumentRegisterValue(0, builder);
+ instruction = new InvokeCustomRange(firstRegister, argumentRegisters, getCallSite());
+ } else {
+ int[] individualArgumentRegisters = new int[5];
+ int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+ instruction = new com.android.tools.r8.code.InvokeCustom(
+ argumentRegistersCount,
+ getCallSite(),
+ individualArgumentRegisters[0], // C
+ individualArgumentRegisters[1], // D
+ individualArgumentRegisters[2], // E
+ individualArgumentRegisters[3], // F
+ individualArgumentRegisters[4]); // G
+ }
+ addInvokeAndMoveResult(instruction, builder);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.isInvokeCustom() && callSite == other.asInvokeCustom().callSite;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ assert other.isInvokeCustom();
+ assert false : "Not supported";
+ return 0;
+ }
+
+ public boolean isInvokeCustom() {
+ return true;
+ }
+
+ public InvokeCustom asInvokeCustom() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
new file mode 100644
index 0000000..26bc116
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -0,0 +1,95 @@
+// 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.ir.code;
+
+import com.android.tools.r8.code.InvokeDirectRange;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public class InvokeDirect extends InvokeMethod {
+
+ public InvokeDirect(DexMethod target, Value result, List<Value> arguments) {
+ super(target, result, arguments);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.DIRECT;
+ }
+
+ @Override
+ protected String getTypeString() {
+ return "Direct";
+ }
+
+ @Override
+ public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+ return appInfo.lookupDirectTarget(getInvokedMethod());
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int argumentRegisters = requiredArgumentRegisters();
+ builder.requestOutgoingRegisters(argumentRegisters);
+ if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+ assert argumentsConsecutive(builder);
+ int firstRegister = argumentRegisterValue(0, builder);
+ instruction = new InvokeDirectRange(firstRegister, argumentRegisters, getInvokedMethod());
+ } else {
+ int[] individualArgumentRegisters = new int[5];
+ int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+ instruction = new com.android.tools.r8.code.InvokeDirect(
+ argumentRegistersCount,
+ getInvokedMethod(),
+ individualArgumentRegisters[0], // C
+ individualArgumentRegisters[1], // D
+ individualArgumentRegisters[2], // E
+ individualArgumentRegisters[3], // F
+ individualArgumentRegisters[4]); // G
+ }
+ addInvokeAndMoveResult(instruction, builder);
+ }
+
+ /**
+ * Two invokes of a constructor are only allowed to be considered equal if the object
+ * they are initializing is the same. Art rejects code that has objects created by
+ * different new-instance instructions flow to one constructor invoke.
+ */
+ public boolean sameConstructorReceiverValue(Invoke other) {
+ if (!getInvokedMethod().name.toString().equals(Constants.INSTANCE_INITIALIZER_NAME)) {
+ return true;
+ }
+ return inValues.get(0) == other.inValues.get(0);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ if (!other.isInvokeDirect()) {
+ return false;
+ }
+ return super.identicalNonValueParts(other);
+ }
+
+ @Override
+ public boolean isInvokeDirect() {
+ return true;
+ }
+
+ @Override
+ public InvokeDirect asInvokeDirect() {
+ return this;
+ }
+
+ @Override
+ public InlineAction computeInlining(InliningOracle decider) {
+ return decider.computeForInvokeDirect(this);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
new file mode 100644
index 0000000..4e3a4b8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -0,0 +1,83 @@
+// 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.ir.code;
+
+import com.android.tools.r8.code.InvokeInterfaceRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public class InvokeInterface extends InvokeMethod {
+
+ public InvokeInterface(DexMethod target, Value result, List<Value> arguments) {
+ super(target, result, arguments);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.INTERFACE;
+ }
+
+ @Override
+ protected String getTypeString() {
+ return "Interface";
+ }
+
+ @Override
+ public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+ return appInfo.lookupSingleInterfaceTarget(getInvokedMethod());
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int argumentRegisters = requiredArgumentRegisters();
+ builder.requestOutgoingRegisters(argumentRegisters);
+ if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+ assert argumentsConsecutive(builder);
+ int firstRegister = argumentRegisterValue(0, builder);
+ instruction = new InvokeInterfaceRange(firstRegister, argumentRegisters, getInvokedMethod());
+ } else {
+ int[] individualArgumentRegisters = new int[5];
+ int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+ instruction = new com.android.tools.r8.code.InvokeInterface(
+ argumentRegistersCount,
+ getInvokedMethod(),
+ individualArgumentRegisters[0], // C
+ individualArgumentRegisters[1], // D
+ individualArgumentRegisters[2], // E
+ individualArgumentRegisters[3], // F
+ individualArgumentRegisters[4]); // G
+ }
+ addInvokeAndMoveResult(instruction, builder);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ if (!other.isInvokeInterface()) {
+ return false;
+ }
+ return super.identicalNonValueParts(other);
+ }
+
+ @Override
+ public boolean isInvokeInterface() {
+ return true;
+ }
+
+ @Override
+ public InvokeInterface asInvokeInterface() {
+ return this;
+ }
+
+ @Override
+ public InlineAction computeInlining(InliningOracle decider) {
+ return decider.computeForInvokeInterface(this);
+ }
+
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
new file mode 100644
index 0000000..6435790
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -0,0 +1,50 @@
+// 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.ir.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public abstract class InvokeMethod extends Invoke {
+
+ private DexMethod method;
+
+ public InvokeMethod(DexMethod target, Value result, List<Value> arguments) {
+ super(result, arguments);
+ this.method = target;
+ }
+
+ public DexMethod getInvokedMethod() {
+ return method;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return method == other.asInvokeMethod().getInvokedMethod();
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return getInvokedMethod().slowCompareTo(other.asInvokeMethod().getInvokedMethod());
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "; method: " + method.toSourceString();
+ }
+
+ @Override
+ public boolean isInvokeMethod() {
+ return true;
+ }
+
+ @Override
+ public InvokeMethod asInvokeMethod() {
+ return this;
+ }
+
+ abstract public InlineAction computeInlining(InliningOracle decider);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
new file mode 100644
index 0000000..0725fed
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -0,0 +1,96 @@
+// 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.ir.code;
+
+import com.android.tools.r8.code.FilledNewArray;
+import com.android.tools.r8.code.FilledNewArrayRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import java.util.List;
+
+public class InvokeNewArray extends Invoke {
+
+ private DexType type;
+
+ public InvokeNewArray(DexType type, Value result, List<Value> arguments) {
+ super(result, arguments);
+ this.type = type;
+ }
+
+ public DexType getArrayType() {
+ return type;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.NEW_ARRAY;
+ }
+
+ @Override
+ protected String getTypeString() {
+ return "NewArray";
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "; type: " + type.toSourceString();
+ }
+
+ @Override
+ public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+ return null;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int argumentRegisters = requiredArgumentRegisters();
+ builder.requestOutgoingRegisters(argumentRegisters);
+ if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+ assert argumentsConsecutive(builder);
+ int firstRegister = argumentRegisterValue(0, builder);
+ instruction = new FilledNewArrayRange(firstRegister, argumentRegisters, type);
+ } else {
+ int[] individualArgumentRegisters = new int[5];
+ int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+ instruction = new FilledNewArray(
+ argumentRegistersCount,
+ type,
+ individualArgumentRegisters[0], // C
+ individualArgumentRegisters[1], // D
+ individualArgumentRegisters[2], // E
+ individualArgumentRegisters[3], // F
+ individualArgumentRegisters[4]); // G
+ }
+ addInvokeAndMoveResult(instruction, builder);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ if (!other.isInvokeNewArray()) {
+ return false;
+ }
+ return type == other.asInvokeNewArray().type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ if (!other.isInvokeNewArray()) {
+ return -1;
+ }
+ return type.slowCompareTo(other.asInvokeNewArray().type);
+ }
+
+ @Override
+ public boolean isInvokeNewArray() {
+ return true;
+ }
+
+ @Override
+ public InvokeNewArray asInvokeNewArray() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
new file mode 100644
index 0000000..9c87144
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -0,0 +1,93 @@
+// 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.ir.code;
+
+import com.android.tools.r8.code.InvokePolymorphicRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public class InvokePolymorphic extends InvokeMethod {
+
+ private final DexProto proto;
+
+ public InvokePolymorphic(DexMethod target, DexProto proto, Value result, List<Value> arguments) {
+ super(target, result, arguments);
+ this.proto = proto;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.POLYMORPHIC;
+ }
+
+ @Override
+ protected String getTypeString() {
+ return "Polymorphic";
+ }
+
+ public DexProto getProto() {
+ return proto;
+ }
+
+ @Override
+ public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+ // Target method can not be known at compile time.
+ return null;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int argumentRegisters = requiredArgumentRegisters();
+ builder.requestOutgoingRegisters(argumentRegisters);
+ if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+ assert argumentsConsecutive(builder);
+ int firstRegister = argumentRegisterValue(0, builder);
+ instruction = new InvokePolymorphicRange(
+ firstRegister, argumentRegisters, getInvokedMethod(), getProto());
+ } else {
+ int[] individualArgumentRegisters = new int[5];
+ int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+ instruction = new com.android.tools.r8.code.InvokePolymorphic(
+ argumentRegistersCount,
+ getInvokedMethod(),
+ getProto(),
+ individualArgumentRegisters[0], // C
+ individualArgumentRegisters[1], // D
+ individualArgumentRegisters[2], // E
+ individualArgumentRegisters[3], // F
+ individualArgumentRegisters[4]); // G
+ }
+ addInvokeAndMoveResult(instruction, builder);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ if (!other.isInvokePolymorphic()) {
+ return false;
+ }
+ return proto.equals(((InvokePolymorphic) other).proto) && super.identicalNonValueParts(other);
+ }
+
+ @Override
+ public boolean isInvokePolymorphic() {
+ return true;
+ }
+
+ @Override
+ public InvokePolymorphic asInvokePolymorphic() {
+ return this;
+ }
+
+ @Override
+ public InlineAction computeInlining(InliningOracle decider) {
+ return decider.computeForInvokePolymorpic(this);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
new file mode 100644
index 0000000..0c4fe14
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -0,0 +1,82 @@
+// 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.ir.code;
+
+import com.android.tools.r8.code.InvokeStaticRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public class InvokeStatic extends InvokeMethod {
+
+ public InvokeStatic(DexMethod target, Value result, List<Value> arguments) {
+ super(target, result, arguments);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.STATIC;
+ }
+
+ @Override
+ protected String getTypeString() {
+ return "Static";
+ }
+
+ @Override
+ public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+ return appInfo.lookupStaticTarget(getInvokedMethod());
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int argumentRegisters = requiredArgumentRegisters();
+ builder.requestOutgoingRegisters(argumentRegisters);
+ if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+ assert argumentsConsecutive(builder);
+ int firstRegister = argumentRegisterValue(0, builder);
+ instruction = new InvokeStaticRange(firstRegister, argumentRegisters, getInvokedMethod());
+ } else {
+ int[] individualArgumentRegisters = new int[5];
+ int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+ instruction = new com.android.tools.r8.code.InvokeStatic(
+ argumentRegistersCount,
+ getInvokedMethod(),
+ individualArgumentRegisters[0], // C
+ individualArgumentRegisters[1], // D
+ individualArgumentRegisters[2], // E
+ individualArgumentRegisters[3], // F
+ individualArgumentRegisters[4]); // G
+ }
+ addInvokeAndMoveResult(instruction, builder);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ if (!other.isInvokeStatic()) {
+ return false;
+ }
+ return super.identicalNonValueParts(other);
+ }
+
+ @Override
+ public boolean isInvokeStatic() {
+ return true;
+ }
+
+ @Override
+ public InvokeStatic asInvokeStatic() {
+ return this;
+ }
+
+ @Override
+ public InlineAction computeInlining(InliningOracle decider) {
+ return decider.computeForInvokeStatic(this);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
new file mode 100644
index 0000000..2e1e3e7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -0,0 +1,81 @@
+// 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.ir.code;
+
+import com.android.tools.r8.code.InvokeSuperRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public class InvokeSuper extends InvokeMethod {
+
+ public InvokeSuper(DexMethod target, Value result, List<Value> arguments) {
+ super(target, result, arguments);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.SUPER;
+ }
+
+ @Override
+ protected String getTypeString() {
+ return "Super";
+ }
+
+ @Override
+ public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+ return appInfo.lookupDirectTarget(getInvokedMethod());
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int argumentRegisters = requiredArgumentRegisters();
+ builder.requestOutgoingRegisters(argumentRegisters);
+ if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+ assert argumentsConsecutive(builder);
+ int firstRegister = argumentRegisterValue(0, builder);
+ instruction = new InvokeSuperRange(firstRegister, argumentRegisters, getInvokedMethod());
+ } else {
+ int[] individualArgumentRegisters = new int[5];
+ int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+ instruction = new com.android.tools.r8.code.InvokeSuper(
+ argumentRegistersCount,
+ getInvokedMethod(),
+ individualArgumentRegisters[0], // C
+ individualArgumentRegisters[1], // D
+ individualArgumentRegisters[2], // E
+ individualArgumentRegisters[3], // F
+ individualArgumentRegisters[4]); // G
+ }
+ addInvokeAndMoveResult(instruction, builder);
+ }
+
+ public boolean identicalNonValueParts(Instruction other) {
+ if (!other.isInvokeSuper()) {
+ return false;
+ }
+ return super.identicalNonValueParts(other);
+ }
+
+ @Override
+ public boolean isInvokeSuper() {
+ return true;
+ }
+
+ @Override
+ public InvokeSuper asInvokeSuper() {
+ return this;
+ }
+
+ @Override
+ public InlineAction computeInlining(InliningOracle decider) {
+ return decider.computeForInvokeSuper(this);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
new file mode 100644
index 0000000..15ec76e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -0,0 +1,82 @@
+// 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.ir.code;
+
+import com.android.tools.r8.code.InvokeVirtualRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public class InvokeVirtual extends InvokeMethod {
+
+ public InvokeVirtual(DexMethod target, Value result, List<Value> arguments) {
+ super(target, result, arguments);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.VIRTUAL;
+ }
+
+ @Override
+ protected String getTypeString() {
+ return "Virtual";
+ }
+
+ @Override
+ public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+ return appInfo.lookupSingleVirtualTarget(getInvokedMethod());
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int argumentRegisters = requiredArgumentRegisters();
+ builder.requestOutgoingRegisters(argumentRegisters);
+ if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+ assert argumentsConsecutive(builder);
+ int firstRegister = argumentRegisterValue(0, builder);
+ instruction = new InvokeVirtualRange(firstRegister, argumentRegisters, getInvokedMethod());
+ } else {
+ int[] individualArgumentRegisters = new int[5];
+ int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+ instruction = new com.android.tools.r8.code.InvokeVirtual(
+ argumentRegistersCount,
+ getInvokedMethod(),
+ individualArgumentRegisters[0], // C
+ individualArgumentRegisters[1], // D
+ individualArgumentRegisters[2], // E
+ individualArgumentRegisters[3], // F
+ individualArgumentRegisters[4]); // G
+ }
+ addInvokeAndMoveResult(instruction, builder);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ if (!other.isInvokeVirtual()) {
+ return false;
+ }
+ return super.identicalNonValueParts(other);
+ }
+
+ @Override
+ public boolean isInvokeVirtual() {
+ return true;
+ }
+
+ @Override
+ public InvokeVirtual asInvokeVirtual() {
+ return this;
+ }
+
+ @Override
+ public InlineAction computeInlining(InliningOracle decider) {
+ return decider.computeForInvokeVirtual(this);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java b/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java
new file mode 100644
index 0000000..2a02218
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
+
+public abstract class JumpInstruction extends Instruction {
+
+ public JumpInstruction(Value out) {
+ super(null);
+ }
+
+ public JumpInstruction(Value out, Value in) {
+ super(out, in);
+ }
+
+ public JumpInstruction(Value out, List<? extends Value> ins) {
+ super(out, ins);
+ }
+
+ public BasicBlock fallthroughBlock() {
+ return null;
+ }
+
+ public void setFallthroughBlock(BasicBlock block) {
+ assert false : "We should not change the fallthrough of a JumpInstruction with no fallthrough.";
+ }
+
+ @Override
+ public boolean canBeDeadCode(InternalOptions options) {
+ return false;
+ }
+
+ @Override
+ public boolean isJumpInstruction() {
+ return true;
+ }
+
+ @Override
+ public JumpInstruction asJumpInstruction() {
+ return this;
+ }
+
+ @Override
+ public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+ return InliningConstraint.ALWAYS;
+ }
+}
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
new file mode 100644
index 0000000..dc16d43
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public abstract class LogicalBinop extends Binop {
+
+ public LogicalBinop(NumericType type, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ }
+
+ public abstract com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right);
+
+ public abstract Instruction CreateLong(int dest, int left, int right);
+
+ public abstract Instruction CreateInt2Addr(int left, int right);
+
+ public abstract Instruction CreateLong2Addr(int left, int right);
+
+ public abstract Instruction CreateIntLit8(int dest, int left, int constant);
+
+ public abstract Instruction CreateIntLit16(int dest, int left, int constant);
+
+ @Override
+ public boolean canBeFolded() {
+ return leftValue().isConstant() && rightValue().isConstant();
+ }
+
+ public ConstInstruction fold(ValueNumberGenerator valueNumberGenerator) {
+ assert canBeFolded();
+ if (type == NumericType.INT) {
+ int left = leftValue().getConstInstruction().asConstNumber().getIntValue();
+ int right = rightValue().getConstInstruction().asConstNumber().getIntValue();
+ int result = foldIntegers(left, right);
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+ return new ConstNumber(ConstType.INT, value, result);
+ } else {
+ assert type == NumericType.LONG;
+ long left = leftValue().getConstInstruction().asConstNumber().getLongValue();
+ long right;
+ if (isShl() || isShr() || isUshr()) {
+ // Right argument for shl, shr and ushr is always of type single.
+ right = rightValue().getConstInstruction().asConstNumber().getIntValue();
+ } else {
+ right = rightValue().getConstInstruction().asConstNumber().getLongValue();
+ }
+ long result = foldLongs(left, right);
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.WIDE, getDebugInfo());
+ return new ConstNumber(ConstType.LONG, value, result);
+ }
+ }
+
+ public boolean needsValueInRegister(Value value) {
+ // Always require the left value in a register. If left and right are the same value, then
+ // both will use its register.
+ if (value == leftValue()) {
+ return true;
+ }
+ assert value == rightValue();
+ return !fitsInDexInstruction(value);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ // TODO(ager, sgjesse): For now the left operand is always a value. With constant propagation
+ // that will change.
+ int left = builder.allocatedRegister(leftValue(), getNumber());
+ int dest = builder.allocatedRegister(outValue, getNumber());
+ Instruction instruction;
+ if (isTwoAddr(builder)) {
+ int right = builder.allocatedRegister(rightValue(), getNumber());
+ if (left != dest) {
+ assert isCommutative();
+ assert right == dest;
+ right = left;
+ }
+ switch (type) {
+ case INT:
+ instruction = CreateInt2Addr(dest, right);
+ break;
+ case LONG:
+ instruction = CreateLong2Addr(dest, right);
+ break;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ } else if (!rightValue().needsRegister()) {
+ assert fitsInDexInstruction(rightValue());
+ ConstNumber right = rightValue().getConstInstruction().asConstNumber();
+ if (right.is8Bit()) {
+ instruction = CreateIntLit8(dest, left, right.getIntValue());
+ } else {
+ assert right.is16Bit();
+ instruction = CreateIntLit16(dest, left, right.getIntValue());
+ }
+ } else {
+ int right = builder.allocatedRegister(rightValue(), getNumber());
+ switch (type) {
+ case INT:
+ instruction = CreateInt(dest, left, right);
+ break;
+ case LONG:
+ instruction = CreateLong(dest, left, right);
+ break;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ }
+ builder.add(this, instruction);
+ }
+
+ @Override
+ public boolean isLogicalBinop() {
+ return true;
+ }
+
+ @Override
+ public LogicalBinop asLogicalBinop() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/MemberType.java b/src/main/java/com/android/tools/r8/ir/code/MemberType.java
new file mode 100644
index 0000000..5a992ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/MemberType.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexType;
+
+public enum MemberType {
+ SINGLE,
+ WIDE,
+ OBJECT,
+ BOOLEAN,
+ BYTE,
+ CHAR,
+ SHORT;
+
+ public static MoveType moveTypeFor(MemberType type) {
+ switch (type) {
+ case BOOLEAN:
+ case BYTE:
+ case CHAR:
+ case SHORT:
+ case SINGLE:
+ return MoveType.SINGLE;
+ case WIDE:
+ return MoveType.WIDE;
+ case OBJECT:
+ return MoveType.OBJECT;
+ }
+ return null;
+ }
+
+ public static MemberType fromTypeDescriptorChar(char descriptor) {
+ switch (descriptor) {
+ case 'L': // object
+ case '[': // array
+ return MemberType.OBJECT;
+ case 'Z': // boolean
+ return MemberType.BOOLEAN;
+ case 'B': // byte
+ return MemberType.BYTE;
+ case 'S': // short
+ return MemberType.SHORT;
+ case 'C': // char
+ return MemberType.CHAR;
+ case 'I': // int
+ case 'F': // float
+ return MemberType.SINGLE;
+ case 'J': // long
+ case 'D': // double
+ return MemberType.WIDE;
+ case 'V':
+ throw new InternalCompilerError("No member type for void type.");
+ default:
+ throw new Unreachable("Invalid descriptor char '" + descriptor + "'");
+ }
+ }
+
+ public static MemberType fromDexType(DexType type) {
+ return fromTypeDescriptorChar((char) type.descriptor.content[0]);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Monitor.java b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
new file mode 100644
index 0000000..869d1d0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import static com.android.tools.r8.dex.Constants.U8BIT_MAX;
+
+import com.android.tools.r8.code.MonitorEnter;
+import com.android.tools.r8.code.MonitorExit;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class Monitor extends Instruction {
+
+ public enum Type {
+ ENTER, EXIT
+ }
+
+ private final Type type;
+
+ public Monitor(Type type, Value object) {
+ super(null, object);
+ this.type = type;
+ }
+
+ private Value object() {
+ return inValues.get(0);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int object = builder.allocatedRegister(object(), getNumber());
+ if (type == Type.ENTER) {
+ builder.add(this, new MonitorEnter(object));
+ } else {
+ builder.add(this, new MonitorExit(object));
+ }
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asMonitor().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asMonitor().type.ordinal();
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return U8BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ assert false : "Monitor defines no values.";
+ return 0;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return true;
+ }
+
+ @Override
+ public boolean isMonitor() {
+ return true;
+ }
+
+ @Override
+ public Monitor asMonitor() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Move.java b/src/main/java/com/android/tools/r8/ir/code/Move.java
new file mode 100644
index 0000000..6f70c57
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Move.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+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.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+
+public class Move extends Instruction {
+
+ public Move(Value dest, Value src) {
+ super(dest, src);
+ if (src.isNeverNull()) {
+ dest.markNeverNull();
+ }
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ public Value src() {
+ return inValues.get(0);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ builder.addMove(this);
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U16BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U16BIT_MAX;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ assert other.isMove();
+ return true;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ assert other.isMove();
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " (" + outType() + ")";
+ }
+
+ @Override
+ public boolean isOutConstant() {
+ return src().isConstant();
+ }
+
+ @Override
+ public ConstInstruction getOutConstantConstInstruction() {
+ assert isOutConstant();
+ return src().definition.getOutConstantConstInstruction();
+ }
+
+ @Override
+ public boolean isMove() {
+ return true;
+ }
+
+ @Override
+ public Move asMove() {
+ return this;
+ }
+
+ @Override
+ public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+ return InliningConstraint.ALWAYS;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
new file mode 100644
index 0000000..3e63e50
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class MoveException extends Instruction {
+
+ public MoveException(Value dest) {
+ super(dest);
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int dest = builder.allocatedRegister(dest(), getNumber());
+ builder.add(this, new com.android.tools.r8.code.MoveException(dest));
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ assert false : "MoveException has no register arguments.";
+ return 0;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ assert other.isMoveException();
+ return true;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ assert other.isMoveException();
+ return 0;
+ }
+
+ @Override
+ public boolean isMoveException() {
+ return true;
+ }
+
+ @Override
+ public MoveException asMoveException() {
+ return this;
+ }
+
+ @Override
+ public boolean canBeDeadCode(InternalOptions options) {
+ return !options.debug;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveType.java b/src/main/java/com/android/tools/r8/ir/code/MoveType.java
new file mode 100644
index 0000000..18f5632
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveType.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexType;
+
+public enum MoveType {
+ SINGLE,
+ WIDE,
+ OBJECT;
+
+ public int requiredRegisters() {
+ return this == MoveType.WIDE ? 2 : 1;
+ }
+
+ public static MoveType fromMemberType(MemberType type) {
+ switch (type) {
+ case BOOLEAN:
+ case BYTE:
+ case CHAR:
+ case SHORT:
+ case SINGLE:
+ return MoveType.SINGLE;
+ case WIDE:
+ return MoveType.WIDE;
+ case OBJECT:
+ return MoveType.OBJECT;
+ }
+ assert false;
+ return null;
+ }
+
+ public static MoveType fromTypeDescriptorChar(char descriptor) {
+ switch (descriptor) {
+ case 'L': // object
+ case '[': // array
+ return MoveType.OBJECT;
+ case 'Z': // boolean
+ case 'B': // byte
+ case 'S': // short
+ case 'C': // char
+ case 'I': // int
+ case 'F': // float
+ return MoveType.SINGLE;
+ case 'J': // long
+ case 'D': // double
+ return MoveType.WIDE;
+ case 'V':
+ throw new InternalCompilerError("No move type for void type.");
+ default:
+ throw new Unreachable("Invalid descriptor char '" + descriptor + "'");
+ }
+ }
+
+ public static MoveType fromDexType(DexType type) {
+ return fromTypeDescriptorChar((char) type.descriptor.content[0]);
+ }
+
+ public static MoveType fromConstType(ConstType type) {
+ switch (type) {
+ case INT:
+ case FLOAT:
+ case INT_OR_FLOAT:
+ return MoveType.SINGLE;
+ case OBJECT:
+ return MoveType.OBJECT;
+ case LONG:
+ case DOUBLE:
+ case LONG_OR_DOUBLE:
+ return MoveType.WIDE;
+ default:
+ throw new Unreachable("Invalid const type '" + type + "'");
+ }
+ }
+
+ public static MoveType fromNumericType(NumericType type) {
+ switch (type) {
+ case BYTE:
+ case CHAR:
+ case SHORT:
+ case INT:
+ case FLOAT:
+ return MoveType.SINGLE;
+ case LONG:
+ case DOUBLE:
+ return MoveType.WIDE;
+ default:
+ throw new Unreachable("Invalid numeric type '" + 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
new file mode 100644
index 0000000..19024f5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Mul.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.MulDouble;
+import com.android.tools.r8.code.MulDouble2Addr;
+import com.android.tools.r8.code.MulFloat;
+import com.android.tools.r8.code.MulFloat2Addr;
+import com.android.tools.r8.code.MulInt;
+import com.android.tools.r8.code.MulInt2Addr;
+import com.android.tools.r8.code.MulIntLit16;
+import com.android.tools.r8.code.MulIntLit8;
+import com.android.tools.r8.code.MulLong;
+import com.android.tools.r8.code.MulLong2Addr;
+
+public class Mul extends ArithmeticBinop {
+
+ public Mul(NumericType type, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ }
+
+ @Override
+ public boolean isCommutative() {
+ return true;
+ }
+
+ public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+ return new MulInt(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+ return new MulLong(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateFloat(int dest, int left, int right) {
+ return new MulFloat(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateDouble(int dest, int left, int right) {
+ return new MulDouble(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+ return new MulInt2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+ return new MulLong2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateFloat2Addr(int left, int right) {
+ return new MulFloat2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateDouble2Addr(int left, int right) {
+ return new MulDouble2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+ return new MulIntLit8(dest, left, constant);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+ return new MulIntLit16(dest, left, constant);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asMul().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asMul().type.ordinal();
+ }
+
+ @Override
+ int foldIntegers(int left, int right) {
+ return left * right;
+ }
+
+ @Override
+ long foldLongs(long left, long right) {
+ return left * right;
+ }
+
+ @Override
+ float foldFloat(float left, float right) {
+ return left * right;
+ }
+
+ @Override
+ double foldDouble(double left, double right) {
+ return left * right;
+ }
+
+ @Override
+ public boolean isMul() {
+ return true;
+ }
+
+ @Override
+ public Mul asMul() {
+ return this;
+ }
+}
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
new file mode 100644
index 0000000..6066439
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Neg.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.NegDouble;
+import com.android.tools.r8.code.NegFloat;
+import com.android.tools.r8.code.NegInt;
+import com.android.tools.r8.code.NegLong;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class Neg extends Unop {
+
+ public final NumericType type;
+
+ public Neg(NumericType type, Value dest, Value source) {
+ super(dest, source);
+ this.type = type;
+ }
+
+ @Override
+ public boolean canBeFolded() {
+ return (type == NumericType.INT || type == NumericType.LONG || type == NumericType.FLOAT
+ || type == NumericType.DOUBLE)
+ && source().isConstant();
+ }
+
+ @Override
+ public ConstInstruction fold(ValueNumberGenerator valueNumberGenerator) {
+ assert canBeFolded();
+ if (type == NumericType.INT) {
+ int result = -source().getConstInstruction().asConstNumber().getIntValue();
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+ return new ConstNumber(ConstType.INT, value, result);
+ } else if (type == NumericType.LONG) {
+ long result = -source().getConstInstruction().asConstNumber().getLongValue();
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.WIDE, getDebugInfo());
+ return new ConstNumber(ConstType.LONG, value, result);
+ } else if (type == NumericType.FLOAT) {
+ float result = -source().getConstInstruction().asConstNumber().getFloatValue();
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+ return new ConstNumber(ConstType.FLOAT, value, Float.floatToIntBits(result));
+ } else {
+ assert type == NumericType.DOUBLE;
+ double result = -source().getConstInstruction().asConstNumber().getDoubleValue();
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.WIDE, getDebugInfo());
+ return new ConstNumber(ConstType.DOUBLE, value, Double.doubleToLongBits(result));
+ }
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asNeg().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asNeg().type.ordinal();
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int dest = builder.allocatedRegister(dest(), getNumber());
+ int src = builder.allocatedRegister(source(), getNumber());
+ switch (type) {
+ case INT:
+ instruction = new NegInt(dest, src);
+ break;
+ case LONG:
+ instruction = new NegLong(dest, src);
+ break;
+ case FLOAT:
+ instruction = new NegFloat(dest, src);
+ break;
+ case DOUBLE:
+ instruction = new NegDouble(dest, src);
+ break;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ builder.add(this, instruction);
+ }
+
+ @Override
+ public boolean isNeg() {
+ return true;
+ }
+
+ @Override
+ public Neg asNeg() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
new file mode 100644
index 0000000..f7b0367
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.NewArray;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class NewArrayEmpty extends Instruction {
+
+ public final DexType type;
+
+ public NewArrayEmpty(Value dest, Value size, DexType type) {
+ super(dest, size);
+ dest.markNeverNull();
+ this.type = type;
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ public Value size() {
+ return inValues.get(0);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int size = builder.allocatedRegister(size(), getNumber());
+ int dest = builder.allocatedRegister(dest(), getNumber());
+ builder.add(this, new NewArray(dest, size, type));
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U4BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U4BIT_MAX;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ // new-array throws if its size is negative, but can also potentially throw on out-of-memory.
+ return true;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asNewArrayEmpty().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.slowCompareTo(other.asNewArrayEmpty().type);
+ }
+
+ @Override
+ public boolean isNewArrayEmpty() {
+ return true;
+ }
+
+ @Override
+ public NewArrayEmpty asNewArrayEmpty() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
new file mode 100644
index 0000000..b7d05e8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.FillArrayData;
+import com.android.tools.r8.code.FillArrayDataPayload;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Arrays;
+
+public class NewArrayFilledData extends Instruction {
+
+ public final int element_width;
+ public final long size;
+ public final short[] data;
+
+ // Primitive array with fill-array-data. The type is not known from the original Dex instruction.
+ public NewArrayFilledData(Value src, int element_width, long size, short[] data) {
+ super(null, src);
+ this.element_width = element_width;
+ this.size = size;
+ this.data = data;
+ }
+
+ public Value src() {
+ return inValues.get(0);
+ }
+
+ public FillArrayDataPayload createPayload() {
+ return new FillArrayDataPayload(element_width, size, data);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int src = builder.allocatedRegister(src(), getNumber());
+ builder.addFillArrayData(this, new FillArrayData(src));
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ NewArrayFilledData o = other.asNewArrayFilledData();
+ return o.element_width == element_width
+ && o.size == size
+ && Arrays.equals(o.data, data);
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ NewArrayFilledData o = other.asNewArrayFilledData();
+ int result;
+ result = element_width - o.element_width;
+ if (result != 0) {
+ return result;
+ }
+ result = Long.signum(size - o.size);
+ if (result != 0) {
+ return result;
+ }
+ assert data.length == o.data.length;
+ for (int i = 0; i < data.length; i++) {
+ result = data[i] - o.data[i];
+ if (result != 0) {
+ return result;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ assert false : "NewArrayFilledData defines no values.";
+ return 0;
+ }
+
+ @Override
+ public boolean canBeDeadCode(InternalOptions options) {
+ // Side-effects its input values.
+ return false;
+ }
+
+ @Override
+ public boolean isNewArrayFilledData() {
+ return true;
+ }
+
+ @Override
+ public NewArrayFilledData asNewArrayFilledData() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
new file mode 100644
index 0000000..1aa2cbb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class NewInstance extends Instruction {
+
+ public final DexType clazz;
+
+ public NewInstance(DexType clazz, Value dest) {
+ super(dest);
+ dest.markNeverNull();
+ assert clazz != null;
+ this.clazz = clazz;
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int dest = builder.allocatedRegister(dest(), getNumber());
+ builder.add(this, new com.android.tools.r8.code.NewInstance(dest, clazz));
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " " + clazz;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asNewInstance().clazz == clazz;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return clazz.slowCompareTo(other.asNewInstance().clazz);
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ assert false : "NewInstance has no register arguments";
+ return 0;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ // Creating a new instance can throw if the type is not found, or on out-of-memory.
+ return true;
+ }
+
+ @Override
+ public boolean isNewInstance() {
+ return true;
+ }
+
+ @Override
+ public NewInstance asNewInstance() {
+ return this;
+ }
+}
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
new file mode 100644
index 0000000..9e41f3e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Not.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.NotInt;
+import com.android.tools.r8.code.NotLong;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class Not extends Unop {
+
+ public final NumericType type;
+
+ public Not(NumericType type, Value dest, Value source) {
+ super(dest, source);
+ this.type = type;
+ }
+
+ @Override
+ public boolean canBeFolded() {
+ return source().isConstant();
+ }
+
+ @Override
+ public ConstInstruction fold(ValueNumberGenerator valueNumberGenerator) {
+ assert canBeFolded();
+ if (type == NumericType.INT) {
+ int result = ~(source().getConstInstruction().asConstNumber().getIntValue());
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+ return new ConstNumber(ConstType.INT, value, result);
+ } else {
+ assert type == NumericType.LONG;
+ long result = ~source().getConstInstruction().asConstNumber().getLongValue();
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.WIDE, getDebugInfo());
+ return new ConstNumber(ConstType.LONG, value, result);
+ }
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int dest = builder.allocatedRegister(dest(), getNumber());
+ int src = builder.allocatedRegister(source(), getNumber());
+ switch (type) {
+ case INT:
+ instruction = new NotInt(dest, src);
+ break;
+ case LONG:
+ instruction = new NotLong(dest, src);
+ break;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ builder.add(this, instruction);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asNot().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asNot().type.ordinal();
+ }
+
+ @Override
+ public boolean isNot() {
+ return true;
+ }
+
+ @Override
+ public Not asNot() {
+ return this;
+ }
+}
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
new file mode 100644
index 0000000..23cbb69
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
@@ -0,0 +1,142 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.DoubleToFloat;
+import com.android.tools.r8.code.DoubleToInt;
+import com.android.tools.r8.code.DoubleToLong;
+import com.android.tools.r8.code.FloatToDouble;
+import com.android.tools.r8.code.FloatToInt;
+import com.android.tools.r8.code.FloatToLong;
+import com.android.tools.r8.code.IntToByte;
+import com.android.tools.r8.code.IntToChar;
+import com.android.tools.r8.code.IntToDouble;
+import com.android.tools.r8.code.IntToFloat;
+import com.android.tools.r8.code.IntToLong;
+import com.android.tools.r8.code.IntToShort;
+import com.android.tools.r8.code.LongToDouble;
+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.DexBuilder;
+
+public class NumberConversion extends Unop {
+
+ public final NumericType from;
+ public final NumericType to;
+
+ public NumberConversion(NumericType from, NumericType to, Value dest, Value source) {
+ super(dest, source);
+ this.from = from;
+ this.to = to;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int dest = builder.allocatedRegister(dest(), getNumber());
+ int src = builder.allocatedRegister(source(), getNumber());
+ switch (from) {
+ case INT:
+ switch (to) {
+ case BYTE:
+ instruction = new IntToByte(dest, src);
+ break;
+ case CHAR:
+ instruction = new IntToChar(dest, src);
+ break;
+ case SHORT:
+ instruction = new IntToShort(dest, src);
+ break;
+ case LONG:
+ instruction = new IntToLong(dest, src);
+ break;
+ case FLOAT:
+ instruction = new IntToFloat(dest, src);
+ break;
+ case DOUBLE:
+ instruction = new IntToDouble(dest, src);
+ break;
+ default:
+ throw new Unreachable("Unexpected types " + from + ", " + to);
+ }
+ break;
+ case LONG:
+ switch (to) {
+ case INT:
+ instruction = new LongToInt(dest, src);
+ break;
+ case FLOAT:
+ instruction = new LongToFloat(dest, src);
+ break;
+ case DOUBLE:
+ instruction = new LongToDouble(dest, src);
+ break;
+ default:
+ throw new Unreachable("Unexpected types " + from + ", " + to);
+ }
+ break;
+ case FLOAT:
+ switch (to) {
+ case INT:
+ instruction = new FloatToInt(dest, src);
+ break;
+ case LONG:
+ instruction = new FloatToLong(dest, src);
+ break;
+ case DOUBLE:
+ instruction = new FloatToDouble(dest, src);
+ break;
+ default:
+ throw new Unreachable("Unexpected types " + from + ", " + to);
+ }
+ break;
+ case DOUBLE:
+ switch (to) {
+ case INT:
+ instruction = new DoubleToInt(dest, src);
+ break;
+ case LONG:
+ instruction = new DoubleToLong(dest, src);
+ break;
+ case FLOAT:
+ instruction = new DoubleToFloat(dest, src);
+ break;
+ default:
+ throw new Unreachable("Unexpected types " + from + ", " + to);
+ }
+ break;
+ default:
+ throw new Unreachable("Unexpected types " + from + ", " + to);
+ }
+ builder.add(this, instruction);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ NumberConversion o = other.asNumberConversion();
+ return o.from == from && o.to == to;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ NumberConversion o = other.asNumberConversion();
+ int result;
+ result = from.ordinal() - o.from.ordinal();
+ if (result != 0) {
+ return result;
+ }
+ return to.ordinal() - o.to.ordinal();
+ }
+
+ @Override
+ public boolean isNumberConversion() {
+ return true;
+ }
+
+ @Override
+ public NumberConversion asNumberConversion() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumericType.java b/src/main/java/com/android/tools/r8/ir/code/NumericType.java
new file mode 100644
index 0000000..765bfe1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/NumericType.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+
+public enum NumericType {
+ BYTE,
+ CHAR,
+ SHORT,
+ INT,
+ LONG,
+ FLOAT,
+ DOUBLE;
+
+ public MoveType moveTypeFor() {
+ if (this == NumericType.DOUBLE || this == NumericType.LONG) {
+ return MoveType.WIDE;
+ }
+ return MoveType.SINGLE;
+ }
+
+ public DexType dexTypeFor(DexItemFactory factory) {
+ switch (this) {
+ case BYTE:
+ return factory.byteType;
+ case CHAR:
+ return factory.charType;
+ case SHORT:
+ return factory.shortType;
+ case INT:
+ return factory.intType;
+ case LONG:
+ return factory.longType;
+ case FLOAT:
+ return factory.floatType;
+ case DOUBLE:
+ return factory.doubleType;
+ default:
+ throw new Unreachable("Invalid numeric type '" + this + "'");
+ }
+ }
+
+ public static NumericType fromDexType(DexType type) {
+ switch (type.descriptor.content[0]) {
+ case 'B': // byte
+ return NumericType.BYTE;
+ case 'S': // short
+ return NumericType.SHORT;
+ case 'C': // char
+ return NumericType.CHAR;
+ case 'I': // int
+ return NumericType.INT;
+ case 'F': // float
+ return NumericType.FLOAT;
+ case 'J': // long
+ return NumericType.LONG;
+ case 'D': // double
+ return NumericType.DOUBLE;
+ default:
+ return null;
+ }
+ }
+}
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
new file mode 100644
index 0000000..bfa69f4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Or.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+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;
+
+public class Or extends LogicalBinop {
+
+ public Or(NumericType type, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ }
+
+ @Override
+ public boolean isOr() {
+ return true;
+ }
+
+ @Override
+ public Or asOr() {
+ return this;
+ }
+
+ @Override
+ public boolean isCommutative() {
+ return true;
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+ return new OrInt(dest, left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+ // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+ // the first part of the result long before reading the second part of the input longs.
+ // Therefore, there can be no overlap of the second part of an input long and the first
+ // part of the output long.
+ assert dest != left + 1;
+ assert dest != right + 1;
+ return new OrLong(dest, left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+ return new OrInt2Addr(left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+ return new OrLong2Addr(left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+ return new OrIntLit8(dest, left, constant);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+ return new OrIntLit16(dest, left, constant);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asOr().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asOr().type.ordinal();
+ }
+
+ int foldIntegers(int left, int right) {
+ return left | right;
+ }
+
+ long foldLongs(long left, long right) {
+ return left | right;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
new file mode 100644
index 0000000..0d74321
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -0,0 +1,313 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.ir.code.BasicBlock.EdgeType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class Phi extends Value {
+
+ private final BasicBlock block;
+ private final List<Value> operands = new ArrayList<>();
+
+ // Trivial phis are eliminated during IR construction. When a trivial phi is eliminated
+ // we need to update all references to it. A phi can be referenced from phis, instructions
+ // and current definition mappings. This list contains the current definitions mappings that
+ // contain this phi.
+ private List<Map<Integer, Value>> definitionUsers = new ArrayList<>();
+
+ // The computed out type is not always the same as 'this.type' because of the type
+ // confusion around null and constant zero. The null object can be used in a single
+ // context (if tests) and the single 0 can be used as null. A phi can therefore
+ // have either of the creation types 'single' and 'object' depending on the use that
+ // triggered the creation of the phi. We therefore have to delay the output type
+ // computation of the phi until all operands are known.
+ private MoveType outType = null;
+
+ public Phi(int number, int register, BasicBlock block, MoveType type, DebugLocalInfo local) {
+ super(number, register, type, local == null ? null : new DebugInfo(local, null));
+ this.block = block;
+ block.addPhi(this);
+ }
+
+ @Override
+ public boolean isPhi() {
+ return true;
+ }
+
+ @Override
+ public Phi asPhi() {
+ return this;
+ }
+
+ public BasicBlock getBlock() {
+ return block;
+ }
+
+ public void addOperands(IRBuilder builder) {
+ // Phi operands are only filled in once to complete the phi. Some phis are incomplete for a
+ // period of time to break cycles. When the cycle has been resolved they are completed
+ // exactly once by adding the operands.
+ assert operands.isEmpty();
+ boolean canBeNull = false;
+ for (BasicBlock pred : block.getPredecessors()) {
+ EdgeType edgeType = pred.getEdgeType(block);
+ // Since this read has been delayed we must provide the local info for the value.
+ Value operand = builder.readRegister(
+ getOriginalRegister(), pred, edgeType, type, getLocalInfo());
+ canBeNull |= operand.canBeNull();
+ appendOperand(operand);
+ }
+ if (!canBeNull) {
+ markNeverNull();
+ }
+ removeTrivialPhi();
+ }
+
+ public void addOperands(List<Value> operands) {
+ // Phi operands are only filled in once to complete the phi. Some phis are incomplete for a
+ // period of time to break cycles. When the cycle has been resolved they are completed
+ // exactly once by adding the operands.
+ assert this.operands.isEmpty();
+ boolean canBeNull = false;
+ for (Value operand : operands) {
+ canBeNull |= operand.canBeNull();
+ appendOperand(operand);
+ }
+ if (!canBeNull) {
+ markNeverNull();
+ }
+ removeTrivialPhi();
+ }
+
+ private void appendOperand(Value operand) {
+ operands.add(operand);
+ operand.addPhiUser(this);
+ }
+
+ public Value getOperand(int predIndex) {
+ return operands.get(predIndex);
+ }
+
+ public List<Value> getOperands() {
+ return operands;
+ }
+
+ public void removeOperand(int index) {
+ operands.get(index).removePhiUser(this);
+ operands.remove(index);
+ }
+
+ public void removeOperandsByIndex(List<Integer> operandsToRemove) {
+ if (operandsToRemove.isEmpty()) {
+ return;
+ }
+ List<Value> copy = new ArrayList<>(operands);
+ operands.clear();
+ int current = 0;
+ for (int i : operandsToRemove) {
+ operands.addAll(copy.subList(current, i));
+ copy.get(i).removePhiUser(this);
+ current = i + 1;
+ }
+ operands.addAll(copy.subList(current, copy.size()));
+ }
+
+ public void replace(int predIndex, Value newValue) {
+ Value current = operands.get(predIndex);
+ operands.set(predIndex, newValue);
+ newValue.addPhiUser(this);
+ current.removePhiUser(this);
+ }
+
+ // Removing the phi user from the current value leads to concurrent modification errors
+ // during trivial phi elimination. It is safe to not remove the phi user from current
+ // since current will be unreachable after trivial phi elimination.
+ // TODO(ager): can we unify the these replace methods and avoid the concurrent modification
+ // issue?
+ private void replaceTrivialPhi(Value current, Value newValue) {
+ for (int i = 0; i < operands.size(); i++) {
+ if (operands.get(i) == current) {
+ operands.set(i, newValue);
+ newValue.addPhiUser(this);
+ }
+ }
+ }
+
+ public boolean isTrivialPhi() {
+ Value same = null;
+ for (Value op : operands) {
+ if (op == same || op == this) {
+ // Have only seen one value other than this.
+ continue;
+ }
+ if (same != null) {
+ // Merged at least two values and is therefore not trivial.
+ return false;
+ }
+ same = op;
+ }
+ return true;
+ }
+
+ public void removeTrivialPhi() {
+ Value same = null;
+ for (Value op : operands) {
+ if (op == same || op == this) {
+ // Have only seen one value other than this.
+ continue;
+ }
+ if (same != null) {
+ // Merged at least two values and is therefore not trivial.
+ assert !isTrivialPhi();
+ return;
+ }
+ same = op;
+ }
+ assert isTrivialPhi();
+ if (same == null) {
+ same = Value.UNDEFINED;
+ }
+ // Removing this phi, so get rid of it as a phi user from all of the operands to avoid
+ // recursively getting back here with the same phi. If the phi has itself as an operand
+ // that also removes the self-reference.
+ for (Value op : operands) {
+ op.removePhiUser(this);
+ }
+ // Replace this phi with the unique value in all users.
+ for (Instruction user : uniqueUsers()) {
+ user.replacePhi(this, same);
+ }
+ for (Phi user : uniquePhiUsers()) {
+ user.replaceTrivialPhi(this, same);
+ }
+ if (debugUsers() != null) {
+ for (Instruction user : debugUsers()) {
+ if (this == user.getPreviousLocalValue()) {
+ if (same.getDebugInfo() == null) {
+ user.replacePreviousLocalValue(null);
+ } else {
+ user.replacePreviousLocalValue(same);
+ same.addDebugUser(user);
+ }
+ }
+ }
+ }
+ // If IR construction is taking place, update the definition users.
+ if (definitionUsers != null) {
+ for (Map<Integer, Value> user : definitionUsers) {
+ for (Map.Entry<Integer, Value> entry : user.entrySet()) {
+ if (entry.getValue() == this) {
+ entry.setValue(same);
+ if (same.isPhi()) {
+ same.asPhi().addDefinitionsUser(user);
+ }
+ }
+ }
+ }
+ }
+ // Try to simplify phi users that might now have become trivial.
+ for (Phi user : uniquePhiUsers()) {
+ user.removeTrivialPhi();
+ }
+ // Get rid of the phi itself.
+ block.removePhi(this);
+ }
+
+ public String printPhi() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("v");
+ builder.append(number);
+ builder.append(" <- phi");
+ StringUtils.append(builder, ListUtils.map(operands, (Value operand) -> "v" + operand.number));
+ return builder.toString();
+ }
+
+ public void print(CfgPrinter printer) {
+ int uses = numberOfPhiUsers() + numberOfUsers();
+ printer
+ .print("0 ") // bci
+ .append(uses) // use
+ .append(" v").append(number) // tid
+ .append(" Phi");
+ for (Value operand : operands) {
+ printer.append(" v").append(operand.number);
+ }
+ }
+
+ public void addDefinitionsUser(Map<Integer, Value> currentDefinitions) {
+ definitionUsers.add(currentDefinitions);
+ }
+
+ public void removeDefinitionsUser(Map<Integer, Value> currentDefinitions) {
+ definitionUsers.remove(currentDefinitions);
+ }
+
+ public void clearDefinitionsUsers() {
+ definitionUsers = null;
+ }
+
+ private boolean isSingleConstZero(Value value) {
+ return value.definition != null && value.definition.isConstNumber() &&
+ value.definition.asConstNumber().isZero() &&
+ value.outType() == MoveType.SINGLE;
+ }
+
+ private MoveType computeOutType(Set<Phi> active) {
+ if (outType != null) {
+ return outType;
+ }
+ active.add(this);
+ // Go through non-phi operands first to determine if we have an operand that dictates the type.
+ for (Value operand : operands) {
+ // Since a constant zero can be either an integer or an Object (null) we skip them
+ // when computing types and rely on other operands to specify the actual type.
+ if (!operand.isPhi() && !isSingleConstZero(operand)) {
+ return operand.outType();
+ }
+ }
+ // We did not find a non-phi operand that dictates the type. Recurse on phi arguments.
+ for (Value operand : operands) {
+ if (operand.isPhi() && !active.contains(operand)) {
+ MoveType phiType = operand.asPhi().computeOutType(active);
+ // TODO(zerny): If we had a CONST_ZERO type element, we could often avoid going through
+ // all phis. We would only have to recurse until we got a non CONST_ZERO out type.
+ if (phiType != MoveType.SINGLE) {
+ return phiType;
+ }
+ }
+ }
+ // All operands were the constant zero or phis with out type SINGLE and the out type is either
+ // object or single depending on the use. Since all inputs have out type SINGLE it is safe to
+ // return MoveType.SINGLE here.
+ assert type == MoveType.SINGLE || type == MoveType.OBJECT;
+ return MoveType.SINGLE;
+ }
+
+ @Override
+ public MoveType outType() {
+ if (outType != null) {
+ return outType;
+ }
+ return computeOutType(new HashSet<>());
+ }
+
+ @Override
+ public boolean isConstant() {
+ return false;
+ }
+
+ public boolean needsRegister() {
+ return true;
+ }
+}
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
new file mode 100644
index 0000000..6521ae7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Rem.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.RemDouble;
+import com.android.tools.r8.code.RemDouble2Addr;
+import com.android.tools.r8.code.RemFloat;
+import com.android.tools.r8.code.RemFloat2Addr;
+import com.android.tools.r8.code.RemInt;
+import com.android.tools.r8.code.RemInt2Addr;
+import com.android.tools.r8.code.RemIntLit16;
+import com.android.tools.r8.code.RemIntLit8;
+import com.android.tools.r8.code.RemLong;
+import com.android.tools.r8.code.RemLong2Addr;
+
+public class Rem extends ArithmeticBinop {
+
+ public Rem(NumericType type, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ }
+
+ @Override
+ public boolean isRem() {
+ return true;
+ }
+
+ @Override
+ public Rem asRem() {
+ return this;
+ }
+
+ @Override
+ public boolean isCommutative() {
+ return false;
+ }
+
+ public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+ return new RemInt(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+ return new RemLong(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateFloat(int dest, int left, int right) {
+ return new RemFloat(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateDouble(int dest, int left, int right) {
+ return new RemDouble(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+ return new RemInt2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+ return new RemLong2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateFloat2Addr(int left, int right) {
+ return new RemFloat2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateDouble2Addr(int left, int right) {
+ return new RemDouble2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+ return new RemIntLit8(dest, left, constant);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+ return new RemIntLit16(dest, left, constant);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asRem().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asRem().type.ordinal();
+ }
+
+ @Override
+ public boolean canBeFolded() {
+ return super.canBeFolded() && !rightValue().getConstInstruction().asConstNumber().isZero();
+ }
+
+ @Override
+ int foldIntegers(int left, int right) {
+ return left % right;
+ }
+
+ @Override
+ long foldLongs(long left, long right) {
+ return left % right;
+ }
+
+ @Override
+ float foldFloat(float left, float right) {
+ return left % right;
+ }
+
+ @Override
+ double foldDouble(double left, double right) {
+ return left % right;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return type != NumericType.DOUBLE && type != NumericType.FLOAT;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
new file mode 100644
index 0000000..2819c58
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.ReturnObject;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.code.ReturnWide;
+import com.android.tools.r8.dex.Constants;
+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.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+
+public class Return extends JumpInstruction {
+
+ // Need to keep track of the original return type, as a null value will have MoveType.SINGLE.
+ final private MoveType returnType;
+
+ public Return() {
+ super(null);
+ returnType = null;
+ }
+
+ public Return(Value value, MoveType returnType) {
+ super(null, value);
+ this.returnType = returnType;
+ }
+
+ public boolean isReturnVoid() {
+ return inValues.size() == 0;
+ }
+
+ public MoveType getReturnType() {
+ return returnType;
+ }
+
+ public Value returnValue() {
+ assert !isReturnVoid();
+ return inValues.get(0);
+ }
+
+ public com.android.tools.r8.code.Instruction createDexInstruction(DexBuilder builder) {
+ if (isReturnVoid()) {
+ return new ReturnVoid();
+ } else {
+ switch (returnValue().type) {
+ case OBJECT:
+ assert returnType == MoveType.OBJECT;
+ return new ReturnObject(builder.allocatedRegister(returnValue(), getNumber()));
+ case SINGLE:
+ if (returnType == MoveType.OBJECT) {
+ return new ReturnObject(builder.allocatedRegister(returnValue(), getNumber()));
+ } else {
+ assert returnType == MoveType.SINGLE;
+ return new com.android.tools.r8.code.Return(builder.allocatedRegister(returnValue(), getNumber()));
+ }
+ case WIDE:
+ assert returnType == MoveType.WIDE;
+ return new ReturnWide(builder.allocatedRegister(returnValue(), getNumber()));
+ default:
+ throw new Unreachable();
+ }
+ }
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ builder.add(this, createDexInstruction(builder));
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ if (isReturnVoid()) {
+ return other.asReturn().isReturnVoid();
+ } else {
+ return other.asReturn().returnValue().type == returnValue().type;
+ }
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ if (isReturnVoid()) {
+ return other.asReturn().isReturnVoid() ? 0 : -1;
+ } else {
+ return returnValue().type.ordinal() - other.asReturn().returnValue().type.ordinal();
+ }
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ assert false : "Return defines no values.";
+ return 0;
+ }
+
+ @Override
+ public boolean isReturn() {
+ return true;
+ }
+
+ @Override
+ public Return asReturn() {
+ return this;
+ }
+
+ @Override
+ public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+ return InliningConstraint.ALWAYS;
+ }
+}
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
new file mode 100644
index 0000000..8236891
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Shl.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+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;
+
+public class Shl extends LogicalBinop {
+
+ public Shl(NumericType type, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ }
+
+ @Override
+ boolean fitsInDexInstruction(Value value) {
+ // The shl instruction only has the /lit8 variant.
+ return fitsInLit8Instruction(value);
+ }
+
+ @Override
+ public boolean isCommutative() {
+ return false;
+ }
+
+ @Override
+ public boolean isShl() {
+ return true;
+ }
+
+ @Override
+ public Shl asShl() {
+ return this;
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+ return new ShlInt(dest, left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+ return new ShlLong(dest, left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+ return new ShlInt2Addr(left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+ return new ShlLong2Addr(left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+ return new ShlIntLit8(dest, left, constant);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+ throw new Unreachable("Unsupported instruction ShlIntLit16");
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asShl().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asShl().type.ordinal();
+ }
+
+ @Override
+ int foldIntegers(int left, int right) {
+ return left << right;
+ }
+
+ @Override
+ long foldLongs(long left, long right) {
+ return left << right;
+ }
+}
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
new file mode 100644
index 0000000..e4bd97c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Shr.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+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;
+
+public class Shr extends LogicalBinop {
+
+ public Shr(NumericType type, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ }
+
+ @Override
+ boolean fitsInDexInstruction(Value value) {
+ // The shr instruction only has the /lit8 variant.
+ return fitsInLit8Instruction(value);
+ }
+
+ @Override
+ public boolean isShr() {
+ return true;
+ }
+
+ @Override
+ public Shr asShr() {
+ return this;
+ }
+
+ @Override
+ public boolean isCommutative() {
+ return false;
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+ return new ShrInt(dest, left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+ return new ShrLong(dest, left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+ return new ShrInt2Addr(left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+ return new ShrLong2Addr(left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+ return new ShrIntLit8(dest, left, constant);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+ throw new Unreachable("Unsupported instruction ShrIntLit16");
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asShr().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asShr().type.ordinal();
+ }
+
+ @Override
+ int foldIntegers(int left, int right) {
+ return left >> right;
+ }
+
+ @Override
+ long foldLongs(long left, long right) {
+ return left >> right;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/SingleConstant.java b/src/main/java/com/android/tools/r8/ir/code/SingleConstant.java
new file mode 100644
index 0000000..2fc8e0b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/SingleConstant.java
@@ -0,0 +1,10 @@
+// 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.ir.code;
+
+public interface SingleConstant {
+
+ int decodedValue();
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
new file mode 100644
index 0000000..ad340c2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Sget;
+import com.android.tools.r8.code.SgetBoolean;
+import com.android.tools.r8.code.SgetByte;
+import com.android.tools.r8.code.SgetChar;
+import com.android.tools.r8.code.SgetObject;
+import com.android.tools.r8.code.SgetShort;
+import com.android.tools.r8.code.SgetWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class StaticGet extends FieldInstruction {
+
+ public StaticGet(MemberType type, Value dest, DexField field) {
+ super(type, field, dest, (Value) null);
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int dest = builder.allocatedRegister(dest(), getNumber());
+ switch (type) {
+ case SINGLE:
+ instruction = new Sget(dest, field);
+ break;
+ case WIDE:
+ instruction = new SgetWide(dest, field);
+ break;
+ case OBJECT:
+ instruction = new SgetObject(dest, field);
+ break;
+ case BOOLEAN:
+ instruction = new SgetBoolean(dest, field);
+ break;
+ case BYTE:
+ instruction = new SgetByte(dest, field);
+ break;
+ case CHAR:
+ instruction = new SgetChar(dest, field);
+ break;
+ case SHORT:
+ instruction = new SgetShort(dest, field);
+ break;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ builder.add(this, instruction);
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ // This can cause <clinit> to run.
+ return true;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ StaticGet o = other.asStaticGet();
+ return o.field == field && o.type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ StaticGet o = other.asStaticGet();
+ int result;
+ result = field.slowCompareTo(o.field);
+ if (result != 0) {
+ return result;
+ }
+ return type.ordinal() - o.type.ordinal();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "; field: " + field.toSourceString();
+ }
+
+ @Override
+ public boolean isStaticGet() {
+ return true;
+ }
+
+ @Override
+ public StaticGet asStaticGet() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
new file mode 100644
index 0000000..e8a1fed
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Sput;
+import com.android.tools.r8.code.SputBoolean;
+import com.android.tools.r8.code.SputByte;
+import com.android.tools.r8.code.SputChar;
+import com.android.tools.r8.code.SputObject;
+import com.android.tools.r8.code.SputShort;
+import com.android.tools.r8.code.SputWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class StaticPut extends FieldInstruction {
+
+ public StaticPut(MemberType type, Value source, DexField field) {
+ super(type, field, null, source);
+ }
+
+ public Value inValue() {
+ assert inValues.size() == 1;
+ return inValues.get(0);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ com.android.tools.r8.code.Instruction instruction;
+ int src = builder.allocatedRegister(inValue(), getNumber());
+ switch (type) {
+ case SINGLE:
+ instruction = new Sput(src, field);
+ break;
+ case WIDE:
+ instruction = new SputWide(src, field);
+ break;
+ case OBJECT:
+ instruction = new SputObject(src, field);
+ break;
+ case BOOLEAN:
+ instruction = new SputBoolean(src, field);
+ break;
+ case BYTE:
+ instruction = new SputByte(src, field);
+ break;
+ case CHAR:
+ instruction = new SputChar(src, field);
+ break;
+ case SHORT:
+ instruction = new SputShort(src, field);
+ break;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ builder.add(this, instruction);
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ // This can cause <clinit> to run.
+ return true;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ assert false : "StaticPut instructions define no values.";
+ return 0;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ StaticPut o = other.asStaticPut();
+ return o.field == field && o.type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ StaticPut o = other.asStaticPut();
+ int result;
+ result = field.slowCompareTo(o.field);
+ if (result != 0) {
+ return result;
+ }
+ return type.ordinal() - o.type.ordinal();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "; field: " + field.toSourceString();
+ }
+
+ @Override
+ public boolean isStaticPut() {
+ return true;
+ }
+
+ @Override
+ public StaticPut asStaticPut() {
+ return this;
+ }
+}
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
new file mode 100644
index 0000000..8470070
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Sub.java
@@ -0,0 +1,204 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.AddIntLit16;
+import com.android.tools.r8.code.AddIntLit8;
+import com.android.tools.r8.code.RsubInt;
+import com.android.tools.r8.code.RsubIntLit8;
+import com.android.tools.r8.code.SubDouble;
+import com.android.tools.r8.code.SubDouble2Addr;
+import com.android.tools.r8.code.SubFloat;
+import com.android.tools.r8.code.SubFloat2Addr;
+import com.android.tools.r8.code.SubInt;
+import com.android.tools.r8.code.SubInt2Addr;
+import com.android.tools.r8.code.SubLong;
+import com.android.tools.r8.code.SubLong2Addr;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class Sub extends ArithmeticBinop {
+
+ public Sub(NumericType type, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ }
+
+ @Override
+ public boolean isCommutative() {
+ return false;
+ }
+
+ public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+ return new SubInt(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+ // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+ // the first part of the result long before reading the second part of the input longs.
+ // Therefore, there can be no overlap of the second part of an input long and the first
+ // part of the output long.
+ assert dest != left + 1;
+ assert dest != right + 1;
+ return new SubLong(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateFloat(int dest, int left, int right) {
+ return new SubFloat(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateDouble(int dest, int left, int right) {
+ return new SubDouble(dest, left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+ return new SubInt2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+ return new SubLong2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateFloat2Addr(int left, int right) {
+ return new SubFloat2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateDouble2Addr(int left, int right) {
+ return new SubDouble2Addr(left, right);
+ }
+
+ public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+ // The sub instructions with constants are rsub, and is handled below.
+ throw new Unreachable("Unsupported instruction SubIntLit8");
+ }
+
+ public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+ // The sub instructions with constants are rsub, and is handled below.
+ throw new Unreachable("Unsupported instruction SubIntLit16");
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asSub().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asSub().type.ordinal();
+ }
+
+ @Override
+ int foldIntegers(int left, int right) {
+ return left - right;
+ }
+
+ @Override
+ long foldLongs(long left, long right) {
+ return left - right;
+ }
+
+ @Override
+ float foldFloat(float left, float right) {
+ return left - right;
+ }
+
+ @Override
+ double foldDouble(double left, double right) {
+ return left - right;
+ }
+
+ boolean negativeFitsInDexInstruction(Value value) {
+ return type == NumericType.INT &&
+ value.isConstant() &&
+ value.getConstInstruction().asConstNumber().negativeIs16Bit();
+ }
+
+ // This is overridden to give the correct value when adding the negative constant.
+ @Override
+ int maxInOutValueRegisterSize() {
+ if (!leftValue().needsRegister()) {
+ assert fitsInDexInstruction(leftValue());
+ ConstNumber left = leftValue().getConstInstruction().asConstNumber();
+ return left.is8Bit() ? Constants.U8BIT_MAX : Constants.U4BIT_MAX;
+ } else if (!rightValue().needsRegister()) {
+ assert negativeFitsInDexInstruction(rightValue());
+ ConstNumber right = rightValue().getConstInstruction().asConstNumber();
+ return right.negativeIs8Bit() ? Constants.U8BIT_MAX : Constants.U4BIT_MAX;
+ }
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public boolean needsValueInRegister(Value value) {
+ if (leftValue() == rightValue()) {
+ // We cannot distinguish the the two values, so both must end up in registers no matter what.
+ return true;
+ }
+ if (value == leftValue()) {
+ // If the left value fits in the dex instruction no register is needed for that (rsub
+ // instruction).
+ return !fitsInDexInstruction(value);
+ } else {
+ assert value == rightValue();
+ // If the negative right value fits in the dex instruction no register is needed for that (add
+ // instruction with the negative value), unless the left is taking that place.
+ return !negativeFitsInDexInstruction(value) || fitsInDexInstruction(leftValue());
+ }
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ // Handle two address and non-int case through the generic arithmetic binop.
+ if (isTwoAddr(builder) || type != NumericType.INT) {
+ super.buildDex(builder);
+ return;
+ }
+
+ com.android.tools.r8.code.Instruction instruction = null;
+ if (!leftValue().needsRegister()) {
+ // Sub instructions with small left constant is emitted as rsub.
+ assert fitsInDexInstruction(leftValue());
+ ConstNumber left = leftValue().getConstInstruction().asConstNumber();
+ int right = builder.allocatedRegister(rightValue(), getNumber());
+ int dest = builder.allocatedRegister(outValue, getNumber());
+ if (left.is8Bit()) {
+ instruction = new RsubIntLit8(dest, right, left.getIntValue());
+ } else {
+ assert left.is16Bit();
+ instruction = new RsubInt(dest, right, left.getIntValue());
+ }
+ } else if (!rightValue().needsRegister()) {
+ // Sub instructions with small right constant are emitted as add of the negative constant.
+ assert negativeFitsInDexInstruction(rightValue());
+ int dest = builder.allocatedRegister(outValue, getNumber());
+ assert leftValue().needsRegister();
+ int left = builder.allocatedRegister(leftValue(), getNumber());
+ ConstNumber right = rightValue().getConstInstruction().asConstNumber();
+ if (right.negativeIs8Bit()) {
+ instruction = new AddIntLit8(dest, left, -right.getIntValue());
+ } else {
+ assert right.negativeIs16Bit();
+ instruction = new AddIntLit16(dest, left, -right.getIntValue());
+ }
+ } else {
+ assert type == NumericType.INT;
+ int left = builder.allocatedRegister(leftValue(), getNumber());
+ int right = builder.allocatedRegister(rightValue(), getNumber());
+ int dest = builder.allocatedRegister(outValue, getNumber());
+ instruction = CreateInt(dest, left, right);
+ }
+ builder.add(this, instruction);
+ }
+
+ @Override
+ public boolean isSub() {
+ return true;
+ }
+
+ @Override
+ public Sub asSub() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Switch.java b/src/main/java/com/android/tools/r8/ir/code/Switch.java
new file mode 100644
index 0000000..96e00f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Switch.java
@@ -0,0 +1,152 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Nop;
+import com.android.tools.r8.code.PackedSwitch;
+import com.android.tools.r8.code.PackedSwitchPayload;
+import com.android.tools.r8.code.SparseSwitch;
+import com.android.tools.r8.code.SparseSwitchPayload;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.CfgPrinter;
+
+public class Switch extends JumpInstruction {
+
+ public enum Type {
+ PACKED, SPARSE
+ }
+
+ private final Type type;
+ private final int[] keys;
+ private final int[] targetBlockIndices;
+ private int fallthroughBlockIndex;
+
+ public Switch(
+ Type type,
+ Value value,
+ int[] keys,
+ int[] targetBlockIndices,
+ int fallthroughBlockIndex) {
+ super(null, value);
+ this.type = type;
+ this.keys = keys;
+ this.targetBlockIndices = targetBlockIndices;
+ this.fallthroughBlockIndex = fallthroughBlockIndex;
+ }
+
+ private Value value() {
+ return inValues.get(0);
+ }
+
+ @Override
+ public boolean isSwitch() {
+ return true;
+ }
+
+ @Override
+ public Switch asSwitch() {
+ return this;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ assert other.isSwitch();
+ return false;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ assert other.isSwitch();
+ return 0;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int value = builder.allocatedRegister(value(), getNumber());
+ if (type == Type.PACKED) {
+ builder.addSwitch(this, new PackedSwitch(value));
+ } else {
+ builder.addSwitch(this, new SparseSwitch(value));
+ }
+ }
+
+ private int numberOfKeys() {
+ return targetBlockIndices.length;
+ }
+
+ public int[] targetBlockIndices() {
+ return targetBlockIndices;
+ }
+
+ @Override
+ public BasicBlock fallthroughBlock() {
+ return getBlock().getSuccessors().get(fallthroughBlockIndex);
+ }
+
+ public int getFallthroughBlockIndex() {
+ return fallthroughBlockIndex;
+ }
+
+ public void setFallthroughBlockIndex(int i) {
+ fallthroughBlockIndex = i;
+ }
+
+ public BasicBlock targetBlock(int index) {
+ return getBlock().getSuccessors().get(targetBlockIndices()[index]);
+ }
+
+ @Override
+ public void setFallthroughBlock(BasicBlock block) {
+ getBlock().getSuccessors().set(fallthroughBlockIndex, block);
+ }
+
+ public Nop buildPayload(int[] targets) {
+ if (type == Type.PACKED) {
+ return new PackedSwitchPayload(numberOfKeys(), keys[0], targets);
+ } else {
+ return new SparseSwitchPayload(numberOfKeys(), keys, targets);
+ }
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(
+ super.toString() + " (" + (type == Type.PACKED ? "PACKED" : "SPARSE") + ")\n");
+ for (int i = 0; i < numberOfKeys(); i++) {
+ builder.append(" ");
+ if (type == Type.PACKED) {
+ builder.append(keys[0] + i);
+ } else {
+ builder.append(keys[i]);
+ }
+ builder.append(" -> ");
+ builder.append(targetBlock(i).getNumber());
+ builder.append("\n");
+ }
+ builder.append(" F -> ");
+ builder.append(fallthroughBlock().getNumber());
+ return builder.toString();
+ }
+
+ @Override
+ public void print(CfgPrinter printer) {
+ super.print(printer);
+ for (int index : targetBlockIndices) {
+ BasicBlock target = getBlock().getSuccessors().get(index);
+ printer.append(" B").append(target.getNumber());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Throw.java b/src/main/java/com/android/tools/r8/ir/code/Throw.java
new file mode 100644
index 0000000..e0ba866
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Throw.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+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.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+
+public class Throw extends JumpInstruction {
+
+ public Throw(Value exception) {
+ super(null, exception);
+ }
+
+ public Value exception() {
+ return inValues.get(0);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ builder.add(this, new com.android.tools.r8.code.Throw(builder.allocatedRegister(exception(), getNumber())));
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ assert false : "Throw defines no values.";
+ return 0;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return true;
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ assert other.isThrow();
+ return true;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ assert other.isThrow();
+ return 0;
+ }
+
+ @Override
+ public boolean isThrow() {
+ return true;
+ }
+
+ @Override
+ public Throw asThrow() {
+ return this;
+ }
+
+ @Override
+ public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+ return InliningConstraint.ALWAYS;
+ }
+}
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
new file mode 100644
index 0000000..ff1744a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Unop.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+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.optimize.Inliner.InliningConstraint;
+
+abstract public class Unop extends Instruction {
+
+ public Unop(Value dest, Value source) {
+ super(dest, source);
+ }
+
+ public Value dest() {
+ return outValue;
+ }
+
+ public Value source() {
+ return inValues.get(0);
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U4BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U4BIT_MAX;
+ }
+
+ @Override
+ public boolean isUnop() {
+ return true;
+ }
+
+ @Override
+ public Unop asUnop() {
+ return this;
+ }
+
+ @Override
+ public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+ return InliningConstraint.ALWAYS;
+ }
+}
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
new file mode 100644
index 0000000..d90ce4a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Ushr.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+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;
+
+public class Ushr extends LogicalBinop {
+
+ public Ushr(NumericType type, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ }
+
+ @Override
+ boolean fitsInDexInstruction(Value value) {
+ // The ushr instruction only has the /lit8 variant.
+ return fitsInLit8Instruction(value);
+ }
+
+ @Override
+ public boolean isUshr() {
+ return true;
+ }
+
+ @Override
+ public Ushr asUshr() {
+ return this;
+ }
+
+ @Override
+ public boolean isCommutative() {
+ return false;
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+ return new UshrInt(dest, left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+ return new UshrLong(dest, left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+ return new UshrInt2Addr(left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+ return new UshrLong2Addr(left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+ return new UshrIntLit8(dest, left, constant);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+ throw new Unreachable("Unsupported instruction ShrIntLit16");
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asUshr().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asUshr().type.ordinal();
+ }
+
+ @Override
+ int foldIntegers(int left, int right) {
+ return left >>> right;
+ }
+
+ @Override
+ long foldLongs(long left, long right) {
+ return left >>> right;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
new file mode 100644
index 0000000..2b7c647
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -0,0 +1,530 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.ir.regalloc.LiveIntervals;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.LongInterval;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+public class Value {
+
+ /**
+ * Immutable view of the debug info associated with an SSA value.
+ *
+ * Used during IR building and to construct replacement values.
+ */
+ public static class DebugInfo {
+ private final DebugLocalInfo local;
+ private final Value previousLocalValue;
+
+ public DebugInfo(DebugLocalInfo local, Value previousLocalValue) {
+ assert local != null;
+ this.local = local;
+ this.previousLocalValue = previousLocalValue;
+ }
+ }
+
+ // Actual internal data for the debug information of locals.
+ // This is wrapped in a class to avoid multiple pointers in the value structure.
+ private static class DebugData {
+ final DebugLocalInfo local;
+ Value previousLocalValue;
+ Set<Instruction> debugUsers = new HashSet<>();
+ List<DebugLocalRead> localStarts = new ArrayList<>();
+ List<DebugLocalRead> localEnds = new ArrayList<>();
+
+ DebugData(DebugInfo info) {
+ this(info.local, info.previousLocalValue);
+ }
+
+ DebugData(DebugLocalInfo local, Value previousLocalValue) {
+ assert previousLocalValue == null || !previousLocalValue.isUninitializedLocal();
+ this.local = local;
+ this.previousLocalValue = previousLocalValue;
+ }
+ }
+
+ public static final Value UNDEFINED = new Value(-1, -1, MoveType.OBJECT, null);
+
+ protected final int number;
+ protected MoveType type;
+ private int originalRegister;
+ public Instruction definition = null;
+ private LinkedList<Instruction> users = new LinkedList<>();
+ private Set<Instruction> uniqueUsers = null;
+ private LinkedList<Phi> phiUsers = new LinkedList<>();
+ private Set<Phi> uniquePhiUsers = null;
+ private Value nextConsecutive = null;
+ private Value previousConsecutive = null;
+ private LiveIntervals liveIntervals;
+ private int needsRegister = -1;
+ private boolean neverNull = false;
+ private boolean isThis = false;
+ private boolean isArgument = false;
+ private LongInterval valueRange;
+ private DebugData debugData;
+
+ public Value(int number, int originalRegister, MoveType type, DebugInfo debugInfo) {
+ this.number = number;
+ this.type = type;
+ this.originalRegister = originalRegister;
+ this.debugData = debugInfo == null ? null : new DebugData(debugInfo);
+ }
+
+ public boolean isFixedRegisterValue() {
+ return false;
+ }
+
+ public FixedRegisterValue asFixedRegisterValue() {
+ return null;
+ }
+
+ public int getNumber() {
+ return number;
+ }
+
+ public int getOriginalRegister() {
+ return originalRegister;
+ }
+
+ public int requiredRegisters() {
+ return type.requiredRegisters();
+ }
+
+ public DebugInfo getDebugInfo() {
+ return debugData == null ? null : new DebugInfo(debugData.local, debugData.previousLocalValue);
+ }
+
+ public DebugLocalInfo getLocalInfo() {
+ return debugData == null ? null : debugData.local;
+ }
+
+ public Value getPreviousLocalValue() {
+ return debugData == null ? null : debugData.previousLocalValue;
+ }
+
+ public void replacePreviousLocalValue(Value value) {
+ if (value == null || value.isUninitializedLocal()) {
+ debugData.previousLocalValue = null;
+ } else {
+ debugData.previousLocalValue = value;
+ }
+ }
+
+ public List<DebugLocalRead> getDebugLocalStarts() {
+ return debugData.localStarts;
+ }
+
+ public List<DebugLocalRead> getDebugLocalEnds() {
+ return debugData.localEnds;
+ }
+
+ public void addDebugLocalStart(DebugLocalRead start) {
+ assert start != null;
+ debugData.localStarts.add(start);
+ }
+
+ public void addDebugLocalEnd(DebugLocalRead end) {
+ assert end != null;
+ debugData.localEnds.add(end);
+ }
+
+ public void linkTo(Value other) {
+ assert nextConsecutive == null || nextConsecutive == other;
+ assert other.previousConsecutive == null || other.previousConsecutive == this;
+ other.previousConsecutive = this;
+ nextConsecutive = other;
+ }
+
+ public void replaceLink(Value newArgument) {
+ assert isLinked();
+ if (previousConsecutive != null) {
+ previousConsecutive.nextConsecutive = newArgument;
+ newArgument.previousConsecutive = previousConsecutive;
+ previousConsecutive = null;
+ }
+ if (nextConsecutive != null) {
+ nextConsecutive.previousConsecutive = newArgument;
+ newArgument.nextConsecutive = nextConsecutive;
+ nextConsecutive = null;
+ }
+ }
+
+ public boolean isLinked() {
+ return nextConsecutive != null || previousConsecutive != null;
+ }
+
+ public Value getStartOfConsecutive() {
+ Value current = this;
+ while (current.getPreviousConsecutive() != null) {
+ current = current.getPreviousConsecutive();
+ }
+ return current;
+ }
+
+ public Value getNextConsecutive() {
+ return nextConsecutive;
+ }
+
+ public Value getPreviousConsecutive() {
+ return previousConsecutive;
+ }
+
+ public Set<Instruction> uniqueUsers() {
+ if (uniqueUsers != null) {
+ return uniqueUsers;
+ }
+ return uniqueUsers = ImmutableSet.copyOf(users);
+ }
+
+ public Set<Phi> uniquePhiUsers() {
+ if (uniquePhiUsers != null) {
+ return uniquePhiUsers;
+ }
+ return uniquePhiUsers = ImmutableSet.copyOf(phiUsers);
+ }
+
+ public Set<Instruction> debugUsers() {
+ if (debugData == null) {
+ return null;
+ }
+ return Collections.unmodifiableSet(debugData.debugUsers);
+ }
+
+ public int numberOfUsers() {
+ int size = users.size();
+ if (size <= 1) {
+ return size;
+ }
+ return uniqueUsers().size();
+ }
+
+ public int numberOfPhiUsers() {
+ int size = phiUsers.size();
+ if (size <= 1) {
+ return size;
+ }
+ return uniquePhiUsers().size();
+ }
+
+ public int numberOfDebugUsers() {
+ return debugData == null ? 0 : debugData.debugUsers.size();
+ }
+
+ public int numberOfAllUsers() {
+ return numberOfUsers() + numberOfPhiUsers() + numberOfDebugUsers();
+ }
+
+ public void addUser(Instruction user) {
+ users.add(user);
+ uniqueUsers = null;
+ }
+
+ public void removeUser(Instruction user) {
+ users.remove(user);
+ uniqueUsers = null;
+ }
+
+ public void clearUsers() {
+ users.clear();
+ uniqueUsers = null;
+ phiUsers.clear();
+ uniquePhiUsers = null;
+ if (debugData != null) {
+ debugData.debugUsers.clear();
+ }
+ }
+
+ public void addPhiUser(Phi user) {
+ phiUsers.add(user);
+ uniquePhiUsers = null;
+ }
+
+ public void removePhiUser(Phi user) {
+ phiUsers.remove(user);
+ uniquePhiUsers = null;
+ }
+
+ public void addDebugUser(Instruction user) {
+ if (isUninitializedLocal()) {
+ return;
+ }
+ assert !debugData.debugUsers.contains(user);
+ debugData.debugUsers.add(user);
+ }
+
+ public boolean isUninitializedLocal() {
+ return definition != null && definition.isDebugLocalUninitialized();
+ }
+
+ public boolean isInitializedLocal() {
+ return !isUninitializedLocal();
+ }
+
+ public void removeDebugUser(Instruction user) {
+ debugData.debugUsers.remove(user);
+ }
+
+ public boolean hasUsersInfo() {
+ return users != null;
+ }
+
+ public void clearUsersInfo() {
+ users = null;
+ uniqueUsers = null;
+ phiUsers = null;
+ uniquePhiUsers = null;
+ if (debugData != null) {
+ debugData.debugUsers = null;
+ }
+ }
+
+ public void replaceUsers(Value newValue) {
+ if (this == newValue) {
+ return;
+ }
+ for (Instruction user : uniqueUsers()) {
+ user.inValues.replaceAll(v -> {
+ if (v == this) {
+ newValue.addUser(user);
+ return newValue;
+ }
+ return v;
+ });
+ }
+ for (Phi user : uniquePhiUsers()) {
+ user.getOperands().replaceAll(v -> {
+ if (v == this) {
+ newValue.addPhiUser(user);
+ return newValue;
+ }
+ return v;
+ });
+ }
+ if (debugData != null) {
+ for (Instruction user : debugUsers()) {
+ if (user.getPreviousLocalValue() == this) {
+ newValue.addDebugUser(user);
+ user.replacePreviousLocalValue(newValue);
+ }
+ }
+ }
+ clearUsers();
+ }
+
+ public void setLiveIntervals(LiveIntervals intervals) {
+ assert liveIntervals == null;
+ liveIntervals = intervals;
+ }
+
+ public LiveIntervals getLiveIntervals() {
+ return liveIntervals;
+ }
+
+ public boolean needsRegister() {
+ assert needsRegister >= 0;
+ assert !hasUsersInfo() || (needsRegister > 0) == internalComputeNeedsRegister();
+ return needsRegister > 0;
+ }
+
+ public void setNeedsRegister(boolean value) {
+ assert needsRegister == -1 || (needsRegister > 0) == value;
+ needsRegister = value ? 1 : 0;
+ }
+
+ public void computeNeedsRegister() {
+ assert needsRegister < 0;
+ setNeedsRegister(internalComputeNeedsRegister());
+ }
+
+ public boolean internalComputeNeedsRegister() {
+ if (getLocalInfo() != null) {
+ return true;
+ }
+ if (!isConstant()) {
+ return true;
+ }
+ if (numberOfPhiUsers() > 0) {
+ return true;
+ }
+ for (Instruction user : uniqueUsers()) {
+ if (user.needsValueInRegister(this)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return number;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("v");
+ builder.append(number);
+ builder.append("(");
+ if (definition != null && isConstant()) {
+ ConstNumber constNumber = getConstInstruction().asConstNumber();
+ if (constNumber.outType() == MoveType.SINGLE) {
+ builder.append((int) constNumber.getRawValue());
+ } else {
+ builder.append(constNumber.getRawValue());
+ }
+ } else {
+ if (originalRegister >= 0) {
+ builder.append("r");
+ builder.append(originalRegister);
+ } else {
+ builder.append("_");
+ }
+ }
+ if (getLocalInfo() != null) {
+ builder.append(", ").append(getLocalInfo());
+ }
+ builder.append(")");
+ if (valueRange != null) {
+ builder.append(valueRange);
+ }
+ return builder.toString();
+ }
+
+ public MoveType outType() {
+ return type;
+ }
+
+ public ConstInstruction getConstInstruction() {
+ assert definition.isOutConstant();
+ return definition.getOutConstantConstInstruction();
+ }
+
+ public boolean isConstant() {
+ return definition.isOutConstant();
+ }
+
+ public boolean isPhi() {
+ return false;
+ }
+
+ public Phi asPhi() {
+ return null;
+ }
+
+ public void markNeverNull() {
+ assert !neverNull;
+ neverNull = true;
+ }
+
+ /**
+ * Returns whether this value is know to never be <code>null</code>.
+ */
+ public boolean isNeverNull() {
+ return neverNull;
+ }
+
+ public boolean canBeNull() {
+ return !neverNull;
+ }
+
+ public void markAsArgument() {
+ assert !isArgument;
+ assert !isThis;
+ isArgument = true;
+ }
+
+ public boolean isArgument() {
+ return isArgument;
+ }
+
+ public void markAsThis() {
+ assert isArgument;
+ assert !isThis;
+ isThis = true;
+ markNeverNull();
+ }
+
+ /**
+ * Returns whether this value is known to be the receiver (this argument) in a method body.
+ * <p>
+ * For a receiver value {@link #isNeverNull()} is guarenteed to be <code>true</code> as well.
+ */
+ public boolean isThis() {
+ return isThis;
+ }
+
+ public void setValueRange(LongInterval range) {
+ valueRange = range;
+ }
+
+ public boolean hasValueRange() {
+ return valueRange != null || isConstant();
+ }
+
+ public boolean isValueInRange(int value) {
+ if (isConstant()) {
+ return value == getConstInstruction().asConstNumber().getIntValue();
+ } else {
+ return valueRange != null && valueRange.containsValue(value);
+ }
+ }
+
+ public LongInterval getValueRange() {
+ if (isConstant()) {
+ if (type == MoveType.SINGLE) {
+ int value = getConstInstruction().asConstNumber().getIntValue();
+ return new LongInterval(value, value);
+ } else {
+ assert type == MoveType.WIDE;
+ long value = getConstInstruction().asConstNumber().getLongValue();
+ return new LongInterval(value, value);
+ }
+ } else {
+ return valueRange;
+ }
+ }
+
+ public boolean isDead(InternalOptions options) {
+ // Totally unused values are trivially dead.
+ return numberOfAllUsers() == 0 || isDead(new HashSet<>(), options);
+ }
+
+ protected boolean isDead(Set<Value> active, InternalOptions options) {
+ // If the value has debug users we cannot eliminate it since it represents a value in a local
+ // variable that should be visible in the debugger.
+ if (numberOfDebugUsers() != 0) {
+ return false;
+ }
+ // This is a candidate for a dead value. Guard against looping by adding it to the set of
+ // currently active values.
+ active.add(this);
+ for (Instruction instruction : uniqueUsers()) {
+ if (!instruction.canBeDeadCode(options)) {
+ return false;
+ }
+ Value outValue = instruction.outValue();
+ // Instructions with no out value cannot be dead code by the current definition
+ // (unused out value). They typically side-effect input values or deals with control-flow.
+ assert outValue != null;
+ if (!active.contains(outValue) && !outValue.isDead(active, options)) {
+ return false;
+ }
+ }
+ for (Phi phi : uniquePhiUsers()) {
+ if (!active.contains(phi) && !phi.isDead(active, options)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ValueNumberGenerator.java b/src/main/java/com/android/tools/r8/ir/code/ValueNumberGenerator.java
new file mode 100644
index 0000000..ac07f11
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ValueNumberGenerator.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+public class ValueNumberGenerator {
+ private int nextValueNumber = 0;
+
+ public int next() {
+ return nextValueNumber++;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/WideConstant.java b/src/main/java/com/android/tools/r8/ir/code/WideConstant.java
new file mode 100644
index 0000000..e77d038
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/WideConstant.java
@@ -0,0 +1,10 @@
+// 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.ir.code;
+
+public interface WideConstant {
+
+ long decodedValue();
+}
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
new file mode 100644
index 0000000..dfe47bc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Xor.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+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;
+
+public class Xor extends LogicalBinop {
+
+ public Xor(NumericType type, Value dest, Value left, Value right) {
+ super(type, dest, left, right);
+ }
+
+ @Override
+ public boolean isXor() {
+ return true;
+ }
+
+ @Override
+ public Xor asXor() {
+ return this;
+ }
+
+ @Override
+ public boolean isCommutative() {
+ return true;
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+ return new XorInt(dest, left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+ // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+ // the first part of the result long before reading the second part of the input longs.
+ // Therefore, there can be no overlap of the second part of an input long and the first
+ // part of the output long.
+ assert dest != left + 1;
+ assert dest != right + 1;
+ return new XorLong(dest, left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+ return new XorInt2Addr(left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+ return new XorLong2Addr(left, right);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+ return new XorIntLit8(dest, left, constant);
+ }
+
+ @Override
+ public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+ return new XorIntLit16(dest, left, constant);
+ }
+
+ @Override
+ public boolean identicalNonValueParts(Instruction other) {
+ return other.asXor().type == type;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return type.ordinal() - other.asXor().type.ordinal();
+ }
+
+ @Override
+ int foldIntegers(int left, int right) {
+ return left ^ right;
+ }
+
+ @Override
+ long foldLongs(long left, long right) {
+ return left ^ right;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ArrayFilledDataPayloadResolver.java b/src/main/java/com/android/tools/r8/ir/conversion/ArrayFilledDataPayloadResolver.java
new file mode 100644
index 0000000..4f91695
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ArrayFilledDataPayloadResolver.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.code.FillArrayData;
+import com.android.tools.r8.code.FillArrayDataPayload;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class for resolving payload information during IR construction.
+ */
+public class ArrayFilledDataPayloadResolver {
+
+ private static class PayloadData {
+ public int element_width;
+ public long size;
+ public short[] data;
+ }
+
+ private final Map<Integer, FillArrayDataPayload> unresolvedPayload = new HashMap<>();
+ private final Map<Integer, PayloadData> payloadToData = new HashMap<>();
+
+ public void addPayloadUser(FillArrayData dex) {
+ int offset = dex.getOffset();
+ int payloadOffset = offset + dex.getPayloadOffset();
+ assert !payloadToData.containsKey(payloadOffset);
+ payloadToData.put(payloadOffset, new PayloadData());
+ if (unresolvedPayload.containsKey(payloadOffset)) {
+ FillArrayDataPayload payload = unresolvedPayload.remove(payloadOffset);
+ resolve(payload);
+ }
+ }
+
+ public void resolve(FillArrayDataPayload payload) {
+ int payloadOffset = payload.getOffset();
+ PayloadData data = payloadToData.get(payloadOffset);
+ if (data == null) {
+ unresolvedPayload.put(payloadOffset, payload);
+ return;
+ }
+
+ data.element_width = payload.element_width;
+ data.size = payload.size;
+ data.data = payload.data;
+ }
+
+ public int getElementWidth(int payloadOffset) {
+ return payloadToData.get(payloadOffset).element_width;
+ }
+
+ public long getSize(int payloadOffset) {
+ return payloadToData.get(payloadOffset).size;
+ }
+
+ public short[] getData(int payloadOffset) {
+ return payloadToData.get(payloadOffset).data;
+ }
+
+ public void clear() {
+ payloadToData.clear();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
new file mode 100644
index 0000000..bd48134
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -0,0 +1,494 @@
+// 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.ir.conversion;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Call graph representation.
+ * <p>
+ * Each node in the graph contain the methods called and the calling methods. For virtual and
+ * interface calls all potential calls from subtypes are recorded.
+ * <p>
+ * Only methods in the program - not library methods - are represented.
+ * <p>
+ * The directional edges are represented as sets of nodes in each node (called methods and callees).
+ * <p>
+ * A call from method <code>a</code> to method <code>b</code> is only present once no matter how
+ * many calls of <code>a</code> there are in <code>a</code>.
+ * <p>
+ * Recursive calls are not present.
+ */
+public class CallGraph {
+
+ private class Node {
+
+ public final DexEncodedMethod method;
+ private int invokeCount = 0;
+ private boolean isRecursive = false;
+
+ // Outgoing calls from this method.
+ public final Set<Node> calls = new LinkedHashSet<>();
+
+ // Incoming calls to this method.
+ public final Set<Node> callees = new LinkedHashSet<>();
+
+ private Node(DexEncodedMethod method) {
+ this.method = method;
+ }
+
+ public boolean isBridge() {
+ return method.accessFlags.isBridge();
+ }
+
+ private void addCalls(Node method) {
+ calls.add(method);
+ }
+
+ private void addCaller(Node method) {
+ callees.add(method);
+ }
+
+ boolean isRecursive() {
+ return isRecursive;
+ }
+
+ boolean isLeaf() {
+ return calls.isEmpty();
+ }
+
+ int callDegree() {
+ return calls.size();
+ }
+
+ @Override
+ public int hashCode() {
+ return method.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("MethodNode for: ");
+ builder.append(method.qualifiedName());
+ builder.append(" (");
+ builder.append(calls.size());
+ builder.append(" calls, ");
+ builder.append(callees.size());
+ builder.append(" callees");
+ if (isBridge()) {
+ builder.append(", bridge");
+ }
+ if (isRecursive()) {
+ builder.append(", recursive");
+ }
+ builder.append(", invoke count " + invokeCount);
+ builder.append(").\n");
+ if (calls.size() > 0) {
+ builder.append("Calls:\n");
+ for (Node call : calls) {
+ builder.append(" ");
+ builder.append(call.method.qualifiedName());
+ builder.append("\n");
+ }
+ }
+ if (callees.size() > 0) {
+ builder.append("Callees:\n");
+ for (Node callee : callees) {
+ builder.append(" ");
+ builder.append(callee.method.qualifiedName());
+ builder.append("\n");
+ }
+ }
+ return builder.toString();
+ }
+ }
+
+ public class Leaves {
+
+ private final List<DexEncodedMethod> leaves;
+ private final boolean brokeCycles;
+
+ private Leaves(List<DexEncodedMethod> leaves, boolean brokeCycles) {
+ this.leaves = leaves;
+ this.brokeCycles = brokeCycles;
+ }
+
+ public int size() {
+ return leaves.size();
+ }
+
+ public List<DexEncodedMethod> getLeaves() {
+ return leaves;
+ }
+
+ public boolean brokeCycles() {
+ return brokeCycles;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Leaves: ");
+ builder.append(leaves.size());
+ builder.append("\n");
+ builder.append(brokeCycles ? "Cycles broken" : "No cycles broken");
+ return builder.toString();
+ }
+ }
+
+ private final GraphLense graphLense;
+ private final Map<DexEncodedMethod, Node> nodes = new LinkedHashMap<>();
+ private List<Node> leaves = null;
+ private Set<DexEncodedMethod> singleCallSite = Sets.newIdentityHashSet();
+ private Set<DexEncodedMethod> doubleCallSite = Sets.newIdentityHashSet();
+
+ private CallGraph(GraphLense graphLense) {
+ this.graphLense = graphLense;
+ }
+
+ public static CallGraph build(DexApplication application, AppInfoWithSubtyping appInfo,
+ GraphLense graphLense) {
+
+ CallGraph graph = new CallGraph(graphLense);
+
+ for (DexClass clazz : application.classes()) {
+ for (DexEncodedMethod method : clazz.directMethods()) {
+ Node node = graph.ensureMethodNode(method);
+ InvokeExtractor extractor = new InvokeExtractor(appInfo, graphLense, node, graph);
+ method.registerReachableDefinitions(extractor);
+ }
+ for (DexEncodedMethod method : clazz.virtualMethods()) {
+ Node node = graph.ensureMethodNode(method);
+ InvokeExtractor extractor = new InvokeExtractor(appInfo, graphLense, node, graph);
+ method.registerReachableDefinitions(extractor);
+ }
+ }
+
+ assert allMethodsExists(application, graph);
+
+ graph.fillCallSiteSets(appInfo);
+ graph.fillInitialLeaves();
+ return graph;
+ }
+
+ /**
+ * Check if the <code>method</code> is guaranteed to only have a single call site.
+ * <p>
+ * For pinned methods (methods kept through Proguard keep rules) this will always answer
+ * <code>false</code>.
+ */
+ public boolean hasSingleCallSite(DexEncodedMethod method) {
+ return singleCallSite.contains(method);
+ }
+
+ public boolean hasDoubleCallSite(DexEncodedMethod method) {
+ return doubleCallSite.contains(method);
+ }
+
+ private void fillCallSiteSets(AppInfoWithSubtyping appInfo) {
+ assert singleCallSite.isEmpty();
+ AppInfoWithLiveness liveAppInfo = appInfo.withLiveness();
+ if (liveAppInfo == null) {
+ return;
+ }
+ for (Node value : nodes.values()) {
+ // For non-pinned methods we know the exact number of call sites.
+ if (!appInfo.withLiveness().pinnedItems.contains(value.method)) {
+ if (value.invokeCount == 1) {
+ singleCallSite.add(value.method);
+ } else if (value.invokeCount == 2) {
+ doubleCallSite.add(value.method);
+ }
+ }
+ }
+ }
+
+ private void fillInitialLeaves() {
+ assert leaves == null;
+ leaves = new ArrayList<>();
+ for (Node node : nodes.values()) {
+ if (node.isLeaf()) {
+ leaves.add(node);
+ }
+ }
+ }
+
+ private static boolean allMethodsExists(DexApplication application, CallGraph graph) {
+ for (DexProgramClass clazz : application.classes()) {
+ for (DexEncodedMethod method : clazz.directMethods()) {
+ assert graph.nodes.get(method) != null;
+ }
+ for (DexEncodedMethod method : clazz.virtualMethods()) {
+ assert graph.nodes.get(method) != null;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Remove all leaves (nodes with an call (outgoing) degree of 0).
+ * <p>
+ *
+ * @return List of {@link DexEncodedMethod} of the leaves removed.
+ */
+ private List<DexEncodedMethod> removeLeaves() {
+ List<DexEncodedMethod> result = new ArrayList<>();
+ List<Node> newLeaves = new ArrayList<>();
+ for (Node leaf : leaves) {
+ assert nodes.containsKey(leaf.method) && nodes.get(leaf.method).calls.isEmpty();
+ remove(leaf, newLeaves);
+ result.add(leaf.method);
+ }
+ leaves = newLeaves;
+ return result;
+ }
+
+ /**
+ * Pick the next set of leaves (nodes with an call (outgoing) degree of 0) if any.
+ * <p>
+ * If the graph has no leaves then some cycles in the graph will be broken to create a set of
+ * leaves. See {@link #breakCycles} on how cycles are broken. This ensures that at least one
+ * leave is returned if the graph is not empty.
+ * <p>
+ *
+ * @return object with the leaves as a List of {@link DexEncodedMethod} and <code>boolean</code>
+ * indication of whether cycels where broken to produce leaves. <code>null</code> if the graph is
+ * empty.
+ */
+ public Leaves pickLeaves() {
+ boolean cyclesBroken = false;
+ if (isEmpty()) {
+ return null;
+ }
+ List<DexEncodedMethod> leaves = removeLeaves();
+ if (leaves.size() == 0) {
+ // The graph had no more leaves, so break cycles to construct leaves.
+ breakCycles();
+ cyclesBroken = true;
+ leaves = removeLeaves();
+ }
+ assert leaves.size() > 0;
+ for (DexEncodedMethod leaf : leaves) {
+ assert !leaf.isProcessed();
+ }
+ return new Leaves(leaves, cyclesBroken);
+ }
+
+ /**
+ * Break some cycles in the graph by removing edges.
+ * <p>
+ * This will find the lowest call (outgoing) degree in the graph. Then go through all nodes with
+ * that call (outgoing) degree, and remove all call (outgoing) edges from nodes with that call
+ * (outgoing) degree.
+ * <p>
+ * It will avoid removing edges from bridge-methods if possible.
+ */
+ private void breakCycles() {
+ // Break non bridges with degree 1.
+ int minDegree = nodes.size();
+ for (Node node : nodes.values()) {
+ // Break cycles and add all leaves created in the process.
+ if (!node.isBridge() && node.callDegree() <= 1) {
+ assert node.callDegree() == 1;
+ removeAllCalls(node);
+ leaves.add(node);
+ } else {
+ minDegree = Integer.min(minDegree, node.callDegree());
+ }
+ }
+
+ // Return if new leaves where created.
+ if (leaves.size() > 0) {
+ return;
+ }
+
+ // Break methods with the found minimum degree and add all leaves created in the process.
+ for (Node node : nodes.values()) {
+ if (node.callDegree() <= minDegree) {
+ assert node.callDegree() == minDegree;
+ removeAllCalls(node);
+ leaves.add(node);
+ }
+ }
+ assert leaves.size() > 0;
+ }
+
+ synchronized private Node ensureMethodNode(DexEncodedMethod method) {
+ return nodes.computeIfAbsent(method, k -> new Node(method));
+ }
+
+ synchronized private void addCall(Node caller, Node callee) {
+ assert caller != null;
+ assert callee != null;
+ if (caller != callee) {
+ caller.addCalls(callee);
+ callee.addCaller(caller);
+ } else {
+ caller.isRecursive = true;
+ }
+ callee.invokeCount++;
+ }
+
+ private void removeAllCalls(Node node) {
+ for (Node call : node.calls) {
+ call.callees.remove(node);
+ }
+ node.calls.clear();
+ }
+
+ private void remove(Node node, List<Node> leaves) {
+ assert node != null;
+ for (Node callee : node.callees) {
+ boolean removed = callee.calls.remove(node);
+ if (callee.isLeaf()) {
+ leaves.add(callee);
+ }
+ assert removed;
+ }
+ nodes.remove(node.method);
+ }
+
+ public boolean isEmpty() {
+ return nodes.size() == 0;
+ }
+
+ public void dump() {
+ nodes.forEach((m, n) -> System.out.println(n + "\n"));
+ }
+
+ private static class InvokeExtractor extends UseRegistry {
+
+ AppInfoWithSubtyping appInfo;
+ GraphLense graphLense;
+ Node caller;
+ CallGraph graph;
+
+ InvokeExtractor(AppInfoWithSubtyping appInfo, GraphLense graphLense, Node caller,
+ CallGraph graph) {
+ this.appInfo = appInfo;
+ this.graphLense = graphLense;
+ this.caller = caller;
+ this.graph = graph;
+ }
+
+ private void processInvoke(DexEncodedMethod source, Invoke.Type type, DexMethod method) {
+ method = graphLense.lookupMethod(method, source);
+ DexEncodedMethod definition = appInfo.lookup(type, method);
+ if (definition != null) {
+ assert !source.accessFlags.isBridge() || definition != caller.method;
+ DexType definitionHolder = definition.method.getHolder();
+ assert definitionHolder.isClassType();
+ if (!appInfo.definitionFor(definitionHolder).isLibraryClass()) {
+ Node callee = graph.ensureMethodNode(definition);
+ graph.addCall(caller, callee);
+ // For virtual and interface calls add all potential targets that could be called.
+ if (type == Type.VIRTUAL || type == Type.INTERFACE) {
+ Set<DexEncodedMethod> possibleTargets;
+ if (definitionHolder.isInterface()) {
+ possibleTargets = appInfo.lookupInterfaceTargets(definition.method);
+ } else {
+ possibleTargets = appInfo.lookupVirtualTargets(definition.method);
+ }
+ for (DexEncodedMethod possibleTarget : possibleTargets) {
+ if (possibleTarget != definition) {
+ DexClass possibleTargetClass =
+ appInfo.definitionFor(possibleTarget.method.getHolder());
+ if (possibleTargetClass != null && !possibleTargetClass.isLibraryClass()) {
+ callee = graph.ensureMethodNode(possibleTarget);
+ graph.addCall(caller, callee);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean registerInvokeVirtual(DexMethod method) {
+ processInvoke(caller.method, Type.VIRTUAL, method);
+ return false;
+ }
+
+ @Override
+ public boolean registerInvokeDirect(DexMethod method) {
+ processInvoke(caller.method, Type.DIRECT, method);
+ return false;
+ }
+
+ @Override
+ public boolean registerInvokeStatic(DexMethod method) {
+ processInvoke(caller.method, Type.STATIC, method);
+ return false;
+ }
+
+ @Override
+ public boolean registerInvokeInterface(DexMethod method) {
+ processInvoke(caller.method, Type.INTERFACE, method);
+ return false;
+ }
+
+ @Override
+ public boolean registerInvokeSuper(DexMethod method) {
+ processInvoke(caller.method, Type.SUPER, method);
+ return false;
+ }
+
+ @Override
+ public boolean registerInstanceFieldWrite(DexField field) {
+ return false;
+ }
+
+ @Override
+ public boolean registerInstanceFieldRead(DexField field) {
+ return false;
+ }
+
+ @Override
+ public boolean registerNewInstance(DexType type) {
+ return false;
+ }
+
+ @Override
+ public boolean registerStaticFieldRead(DexField field) {
+ return false;
+ }
+
+ @Override
+ public boolean registerStaticFieldWrite(DexField field) {
+ return false;
+ }
+
+ @Override
+ public boolean registerTypeReference(DexType type) {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
new file mode 100644
index 0000000..511f36b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -0,0 +1,1127 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.code.FillArrayData;
+import com.android.tools.r8.code.FillArrayDataPayload;
+import com.android.tools.r8.code.Format31t;
+import com.android.tools.r8.code.Goto;
+import com.android.tools.r8.code.Goto16;
+import com.android.tools.r8.code.Goto32;
+import com.android.tools.r8.code.IfEq;
+import com.android.tools.r8.code.IfEqz;
+import com.android.tools.r8.code.IfGe;
+import com.android.tools.r8.code.IfGez;
+import com.android.tools.r8.code.IfGt;
+import com.android.tools.r8.code.IfGtz;
+import com.android.tools.r8.code.IfLe;
+import com.android.tools.r8.code.IfLez;
+import com.android.tools.r8.code.IfLt;
+import com.android.tools.r8.code.IfLtz;
+import com.android.tools.r8.code.IfNe;
+import com.android.tools.r8.code.IfNez;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.Move16;
+import com.android.tools.r8.code.MoveFrom16;
+import com.android.tools.r8.code.MoveObject;
+import com.android.tools.r8.code.MoveObject16;
+import com.android.tools.r8.code.MoveObjectFrom16;
+import com.android.tools.r8.code.MoveWide;
+import com.android.tools.r8.code.MoveWide16;
+import com.android.tools.r8.code.MoveWideFrom16;
+import com.android.tools.r8.code.Nop;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
+import com.android.tools.r8.graph.DexDebugEventBuilder;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.Move;
+import com.android.tools.r8.ir.code.NewArrayFilledData;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Builder object for constructing dex bytecode from the high-level IR.
+ */
+public class DexBuilder {
+
+ // The IR representation of the code to build.
+ private final IRCode ir;
+
+ // The register allocator providing register assignments for the code to build.
+ private final RegisterAllocator registerAllocator;
+
+ // List of information about switch payloads that have to be created at the end of the
+ // dex code.
+ private final List<SwitchPayloadInfo> switchPayloadInfos = new ArrayList<>();
+
+ // List of generated FillArrayData dex instructions.
+ private final List<FillArrayDataInfo> fillArrayDataInfos = new ArrayList<>();
+
+ // First jumbo string if known.
+ private final DexString firstJumboString;
+
+ // Set of if instructions that have offsets that are so large that they cannot be encoded in
+ // the if instruction format.
+ private Set<BasicBlock> ifsNeedingRewrite = Sets.newIdentityHashSet();
+
+ // Running bounds on offsets.
+ private int maxOffset = 0;
+ private int minOffset = 0;
+
+ // Mapping from IR instructions to info for computing the dex translation. Use the
+ // getInfo/setInfo methods to access the mapping.
+ private Info[] instructionToInfo;
+
+ // The number of ingoing and outgoing argument registers for the code.
+ private int inRegisterCount = 0;
+ private int outRegisterCount = 0;
+
+ // The string reference in the code with the highest index.
+ private DexString highestSortingReferencedString = null;
+
+ BasicBlock nextBlock;
+
+ public DexBuilder(IRCode ir, RegisterAllocator registerAllocator) {
+ assert ir != null;
+ assert registerAllocator != null;
+ this.ir = ir;
+ this.registerAllocator = registerAllocator;
+ this.firstJumboString = null;
+ }
+
+ public DexBuilder(IRCode ir, RegisterAllocator registerAllocator, DexString firstJumboString) {
+ assert ir != null;
+ assert registerAllocator != null;
+ this.ir = ir;
+ this.registerAllocator = registerAllocator;
+ this.firstJumboString = firstJumboString;
+ }
+
+ private void reset() {
+ switchPayloadInfos.clear();
+ fillArrayDataInfos.clear();
+ ifsNeedingRewrite.clear();
+ maxOffset = 0;
+ minOffset = 0;
+ instructionToInfo = new Info[instructionNumberToIndex(ir.numberRemainingInstructions())];
+ inRegisterCount = 0;
+ outRegisterCount = 0;
+ highestSortingReferencedString = null;
+ nextBlock = null;
+ }
+
+ public boolean isJumboString(DexString string) {
+ if (firstJumboString == null) {
+ return false;
+ }
+ // We have to use compareTo here, as slowCompareTo will return the wrong order when minification
+ // is used.
+ return firstJumboString.compareTo(string) <= 0;
+ }
+
+ /**
+ * Build the dex instructions added to this builder.
+ *
+ * This is a two pass construction that will first compute concrete offsets and then construct
+ * the concrete instructions.
+ */
+ public DexCode build(int numberOfArguments) {
+ int numberOfInstructions;
+ int offset;
+
+ do {
+ // Rewrite ifs that are know from the previous iteration to have offsets that are too
+ // large for the if encoding.
+ rewriteIfs();
+
+ // Reset the state of the builder to start from scratch.
+ reset();
+
+ // Populate the builder info objects.
+ numberOfInstructions = 0;
+ ListIterator<BasicBlock> iterator = ir.listIterator();
+ assert iterator.hasNext();
+ BasicBlock block = iterator.next();
+ do {
+ nextBlock = iterator.hasNext() ? iterator.next() : null;
+ block.buildDex(this);
+ block = nextBlock;
+ } while (block != null);
+
+ // Compute offsets.
+ offset = 0;
+ InstructionIterator it = ir.instructionIterator();
+ while (it.hasNext()) {
+ Info info = getInfo(it.next());
+ info.setOffset(offset);
+ offset += info.computeSize(this);
+ ++numberOfInstructions;
+ }
+ } while (!ifsNeedingRewrite.isEmpty());
+
+ // Build instructions.
+ DexDebugEventBuilder debugEventBuilder = new DexDebugEventBuilder(ir.method.method);
+ List<Instruction> dexInstructions = new ArrayList<>(numberOfInstructions);
+ int instructionOffset = 0;
+ InstructionIterator instructionIterator = ir.instructionIterator();
+ int lastMoveExceptionOffset = -1;
+ while (instructionIterator.hasNext()) {
+ com.android.tools.r8.ir.code.Instruction ir = instructionIterator.next();
+ Info info = getInfo(ir);
+ int previousInstructionCount = dexInstructions.size();
+ info.addInstructions(this, dexInstructions);
+
+ if (ir.isArgument()) {
+ int register = registerAllocator.getRegisterForValue(ir.outValue(), ir.getNumber());
+ debugEventBuilder.startArgument(register, ir.getLocalInfo(), ir.outValue().isThis());
+ } else if (ir.isDebugPosition()) {
+ int pc = lastMoveExceptionOffset >= 0 ? lastMoveExceptionOffset : instructionOffset;
+ debugEventBuilder.setPosition(pc, ir.asDebugPosition());
+ }
+ lastMoveExceptionOffset = ir.isMoveException() ? instructionOffset : -1;
+
+ if (previousInstructionCount < dexInstructions.size()) {
+ while (previousInstructionCount < dexInstructions.size()) {
+ Instruction instruction = dexInstructions.get(previousInstructionCount++);
+ instruction.setOffset(instructionOffset);
+ instructionOffset += instruction.getSize();
+ }
+ }
+ }
+
+ // Compute switch payloads.
+ for (SwitchPayloadInfo switchPayloadInfo : switchPayloadInfos) {
+ // Align payloads at even addresses.
+ if (offset % 2 != 0) {
+ Nop nop = new Nop();
+ nop.setOffset(offset++);
+ dexInstructions.add(nop);
+ }
+ // Create payload and add it to the instruction stream.
+ Nop payload = createSwitchPayload(switchPayloadInfo, offset);
+ payload.setOffset(offset);
+ offset += payload.getSize();
+ dexInstructions.add(payload);
+ }
+
+ // Compute fill array data payloads.
+ for (FillArrayDataInfo info : fillArrayDataInfos) {
+ // Align payloads at even addresses.
+ if (offset % 2 != 0) {
+ Nop nop = new Nop();
+ nop.setOffset(offset++);
+ dexInstructions.add(nop);
+ }
+ // Create payload and add it to the instruction stream.
+ FillArrayDataPayload payload = info.ir.createPayload();
+ payload.setOffset(offset);
+ info.dex.setPayloadOffset(offset - info.dex.getOffset());
+ offset += payload.getSize();
+ dexInstructions.add(payload);
+ }
+
+ // Construct try-catch info.
+ TryInfo tryInfo = computeTryInfo();
+
+ // Return the dex code.
+ DexCode code = new DexCode(
+ registerAllocator.registersUsed(),
+ inRegisterCount,
+ outRegisterCount,
+ dexInstructions.toArray(new Instruction[dexInstructions.size()]), tryInfo.tries,
+ tryInfo.handlers,
+ debugEventBuilder.build(),
+ highestSortingReferencedString);
+
+ return code;
+ }
+
+ // Rewrite ifs with offsets that are too large for the if encoding. The rewriting transforms:
+ //
+ //
+ // BB0: if condition goto BB_FAR_AWAY
+ // BB1: ...
+ //
+ // to:
+ //
+ // BB0: if !condition goto BB1
+ // BB2: goto BB_FAR_AWAY
+ // BB1: ...
+ private void rewriteIfs() {
+ if (ifsNeedingRewrite.isEmpty()) {
+ return;
+ }
+ ListIterator<BasicBlock> it = ir.blocks.listIterator();
+ while (it.hasNext()) {
+ BasicBlock block = it.next();
+ if (ifsNeedingRewrite.contains(block)) {
+ If theIf = block.exit().asIf();
+ BasicBlock trueTarget = theIf.getTrueTarget();
+ BasicBlock newBlock = BasicBlock.createGotoBlock(trueTarget, ir.blocks.size());
+ theIf.setTrueTarget(newBlock);
+ theIf.invert();
+ it.add(newBlock);
+ }
+ }
+ }
+
+ private void needsIfRewriting(BasicBlock block) {
+ ifsNeedingRewrite.add(block);
+ }
+
+ public void registerStringReference(DexString string) {
+ if (highestSortingReferencedString == null
+ || string.slowCompareTo(highestSortingReferencedString) > 0) {
+ highestSortingReferencedString = string;
+ }
+ }
+
+ public void requestOutgoingRegisters(int requiredRegisterCount) {
+ if (requiredRegisterCount > outRegisterCount) {
+ outRegisterCount = requiredRegisterCount;
+ }
+ }
+
+ public int allocatedRegister(Value value, int instructionNumber) {
+ return registerAllocator.getRegisterForValue(value, instructionNumber);
+ }
+
+ public boolean argumentValueUsesHighRegister(Value value, int instructionNumber) {
+ return registerAllocator.argumentValueUsesHighRegister(value, instructionNumber);
+ }
+
+ public void addGoto(com.android.tools.r8.ir.code.Goto jump) {
+ if (jump.getTarget() != nextBlock) {
+ add(jump, new GotoInfo(jump));
+ } else {
+ addNop(jump);
+ }
+ }
+
+ public void addIf(If branch) {
+ assert nextBlock == branch.fallthroughBlock();
+ add(branch, new IfInfo(branch));
+ }
+
+ public void addMove(Move move) {
+ add(move, new MoveInfo(move));
+ }
+
+ public void addNop(com.android.tools.r8.ir.code.Instruction instruction) {
+ add(instruction, new FallThroughInfo(instruction));
+ }
+
+ public void add(com.android.tools.r8.ir.code.Instruction ir, Instruction dex) {
+ assert !ir.isGoto();
+ add(ir, new FixedSizeInfo(ir, dex));
+ }
+
+ public void add(com.android.tools.r8.ir.code.Instruction ir, Instruction[] dex) {
+ assert !ir.isGoto();
+ add(ir, new MultiFixedSizeInfo(ir, dex));
+ }
+
+ public void addSwitch(Switch s, Format31t dex) {
+ assert nextBlock == s.fallthroughBlock();
+ switchPayloadInfos.add(new SwitchPayloadInfo(s, dex));
+ add(s, dex);
+ }
+
+ public void addFillArrayData(NewArrayFilledData nafd, FillArrayData dex) {
+ fillArrayDataInfos.add(new FillArrayDataInfo(nafd, dex));
+ add(nafd, dex);
+ }
+
+ public void addArgument(Argument argument) {
+ inRegisterCount += argument.outValue().requiredRegisters();
+ add(argument, new FallThroughInfo(argument));
+ }
+
+ private void add(com.android.tools.r8.ir.code.Instruction ir, Info info) {
+ assert ir != null;
+ assert info != null;
+ assert getInfo(ir) == null;
+ info.setMinOffset(minOffset);
+ info.setMaxOffset(maxOffset);
+ minOffset += info.minSize();
+ maxOffset += info.maxSize();
+ setInfo(ir, info);
+ }
+
+ private static int instructionNumberToIndex(int instructionNumber) {
+ return instructionNumber / LinearScanRegisterAllocator.INSTRUCTION_NUMBER_DELTA;
+ }
+
+ // Helper used by the info objects.
+ private Info getInfo(com.android.tools.r8.ir.code.Instruction instruction) {
+ return instructionToInfo[instructionNumberToIndex(instruction.getNumber())];
+ }
+
+ private void setInfo(com.android.tools.r8.ir.code.Instruction instruction, Info info) {
+ instructionToInfo[instructionNumberToIndex(instruction.getNumber())] = info;
+ }
+
+ private Info getTargetInfo(BasicBlock block) {
+ InstructionIterator iterator = block.iterator();
+ com.android.tools.r8.ir.code.Instruction instruction = null;
+ while (iterator.hasNext()) {
+ instruction = iterator.next();
+ Info info = getInfo(instruction);
+ if (!(info instanceof FallThroughInfo)) {
+ return info;
+ }
+ }
+ assert instruction != null && instruction.isGoto();
+ return getTargetInfo(instruction.asGoto().getTarget());
+ }
+
+ // Helper for computing switch payloads.
+ private Nop createSwitchPayload(SwitchPayloadInfo info, int offset) {
+ Switch ir = info.ir;
+ // Patch the payload offset in the generated switch instruction now
+ // that the location is known.
+ info.dex.setPayloadOffset(offset - getInfo(ir).getOffset());
+ // Compute target offset for each of the keys based on the offset of the
+ // first instruction in the block that the switch goes to for that key.
+ int[] targetBlockIndices = ir.targetBlockIndices();
+ int[] targets = new int[targetBlockIndices.length];
+ for (int i = 0; i < targetBlockIndices.length; i++) {
+ BasicBlock targetBlock = ir.targetBlock(i);
+ com.android.tools.r8.ir.code.Instruction targetInstruction = targetBlock.entry();
+ targets[i] = getInfo(targetInstruction).getOffset() - getInfo(ir).getOffset();
+ }
+ return ir.buildPayload(targets);
+ }
+
+ // Helpers for computing the try items and handlers.
+
+ private TryInfo computeTryInfo() {
+ // Canonical map of handlers.
+ BiMap<CatchHandlers<BasicBlock>, Integer> canonicalHandlers = HashBiMap.create();
+ // Compute the list of try items and their handlers.
+ List<TryItem> tryItems = computeTryItems(canonicalHandlers);
+ // Compute handler sets before dex items which depend on the handler index.
+ Try[] tries = getDexTryItems(tryItems, canonicalHandlers);
+ TryHandler[] handlers = getDexTryHandlers(canonicalHandlers.inverse());
+ return new TryInfo(tries, handlers);
+ }
+
+ private List<TryItem> computeTryItems(
+ BiMap<CatchHandlers<BasicBlock>, Integer> handlerToIndex) {
+ BiMap<Integer, CatchHandlers<BasicBlock>> indexToHandler = handlerToIndex.inverse();
+ List<TryItem> tryItems = new ArrayList<>();
+ List<BasicBlock> blocksWithHandlers = new ArrayList<>();
+ TryItem currentTryItem = null;
+ // Create try items with maximal ranges to get as much coalescing as possible. After coalescing
+ // the try ranges are trimmed.
+ for (BasicBlock block : ir.blocks) {
+ CatchHandlers<BasicBlock> handlers = block.getCatchHandlers();
+ // If this assert is hit, then the block contains no instruction that can throw. This is most
+ // likely due to dead-code elimination or other optimizations that might now work on a refined
+ // notion of what can throw. If so, the trivial blocks should either be removed or their catch
+ // handlers deleted to reflect the simpler graph prior to building the dex code.
+ assert handlers.isEmpty() || block.canThrow();
+ if (!handlers.isEmpty()) {
+ if (handlerToIndex.containsKey(handlers)) {
+ handlers = indexToHandler.get(handlerToIndex.get(handlers));
+ } else {
+ handlerToIndex.put(handlers, handlerToIndex.size());
+ }
+ Info startInfo = getInfo(block.entry());
+ Info endInfo = getInfo(block.exit());
+ int start = startInfo.getOffset();
+ int end = endInfo.getOffset() + endInfo.getSize();
+ currentTryItem = new TryItem(handlers, start, end);
+ tryItems.add(currentTryItem);
+ blocksWithHandlers.add(block);
+ } else if (currentTryItem != null && !block.canThrow()) {
+ Info endInfo = getInfo(block.exit());
+ // If the block only contains a goto there might not be an info for the exit instruction.
+ if (endInfo != null) {
+ currentTryItem.end = endInfo.getOffset() + endInfo.getSize();
+ }
+ } else {
+ currentTryItem = null;
+ }
+ }
+
+ // If there are no try items it is trivially coalesced.
+ if (tryItems.isEmpty()) {
+ return tryItems;
+ }
+
+ // Coalesce try blocks.
+ tryItems.sort(TryItem::compareTo);
+ List<TryItem> coalescedTryItems = new ArrayList<>(tryItems.size());
+ for (int i = 0; i < tryItems.size(); ) {
+ TryItem item = tryItems.get(i);
+ coalescedTryItems.add(item);
+ // Trim the range start for non-throwing instructions when starting a new range.
+ List<com.android.tools.r8.ir.code.Instruction> instructions = blocksWithHandlers.get(i).getInstructions();
+ for (com.android.tools.r8.ir.code.Instruction insn : instructions) {
+ if (insn.instructionTypeCanThrow()) {
+ item.start = getInfo(insn).getOffset();
+ break;
+ }
+ }
+ // Append all consecutive ranges that define the same handlers.
+ ++i;
+ while (i < tryItems.size()) {
+ TryItem next = tryItems.get(i);
+ if (item.end != next.start || !item.handlers.equals(next.handlers)) {
+ item.end = trimEnd(blocksWithHandlers.get(i - 1));
+ break;
+ }
+ item.end = next.end;
+ ++i;
+ }
+ }
+ // Trim the last try range.
+ int lastIndex = tryItems.size() - 1;
+ TryItem lastItem = tryItems.get(lastIndex);
+ lastItem.end = trimEnd(blocksWithHandlers.get(lastIndex));
+ return coalescedTryItems;
+ }
+
+ private int trimEnd(BasicBlock block) {
+ // Trim the range end for non-throwing instructions when end has been computed.
+ List<com.android.tools.r8.ir.code.Instruction> instructions = block.getInstructions();
+ for (com.android.tools.r8.ir.code.Instruction insn : Lists.reverse(instructions)) {
+ if (insn.instructionTypeCanThrow()) {
+ Info info = getInfo(insn);
+ return info.getOffset() + info.getSize();
+ }
+ }
+ throw new Unreachable("Expected to find a possibly throwing instruction");
+ }
+
+ private static Try[] getDexTryItems(List<TryItem> tryItems,
+ Map<CatchHandlers<BasicBlock>, Integer> catchHandlers) {
+ Try[] tries = new Try[tryItems.size()];
+ for (int i = 0; i < tries.length; ++i) {
+ TryItem item = tryItems.get(i);
+ Try dexTry = new Try(item.start, item.end - item.start, -1);
+ dexTry.handlerIndex = catchHandlers.get(item.handlers);
+ tries[i] = dexTry;
+ }
+ return tries;
+ }
+
+ private TryHandler[] getDexTryHandlers(Map<Integer, CatchHandlers<BasicBlock>> catchHandlers) {
+ TryHandler[] handlers = new TryHandler[catchHandlers.size()];
+ for (int j = 0; j < catchHandlers.size(); j++) {
+ CatchHandlers<BasicBlock> handlerGroup = catchHandlers.get(j);
+ int catchAllOffset = TryHandler.NO_HANDLER;
+ List<TypeAddrPair> pairs = new ArrayList<>();
+ for (int i = 0; i < handlerGroup.getGuards().size(); i++) {
+ DexType type = handlerGroup.getGuards().get(i);
+ BasicBlock target = handlerGroup.getAllTargets().get(i);
+ int targetOffset = getInfo(target.entry()).getOffset();
+ if (type == DexItemFactory.catchAllType) {
+ assert i == handlerGroup.getGuards().size() - 1;
+ catchAllOffset = targetOffset;
+ } else {
+ pairs.add(new TypeAddrPair(type, targetOffset, -1));
+ }
+ }
+ TypeAddrPair[] pairsArray = pairs.toArray(new TypeAddrPair[pairs.size()]);
+ handlers[j] = new TryHandler(pairsArray, catchAllOffset);
+ }
+ return handlers;
+ }
+
+ // Dex instruction wrapper with information to compute instruction sizes and offsets for jumps.
+ private static abstract class Info {
+
+ private final com.android.tools.r8.ir.code.Instruction ir;
+ // Concrete final offset of the instruction.
+ private int offset = -1;
+ // Lower and upper bound of the final offset.
+ private int minOffset = -1;
+ private int maxOffset = -1;
+
+ public Info(com.android.tools.r8.ir.code.Instruction ir) {
+ assert ir != null;
+ this.ir = ir;
+ }
+
+ // Computes the final size of the instruction.
+ // All instruction offsets up-to and including this instruction will be defined at this point.
+ public abstract int computeSize(DexBuilder builder);
+
+ // Materialize the actual construction.
+ // All instruction offsets are known at this point.
+ public abstract void addInstructions(DexBuilder builder, List<Instruction> instructions);
+
+ // Lower bound on the size of the instruction.
+ public abstract int minSize();
+
+ // Upper bound on the size of the instruction.
+ public abstract int maxSize();
+
+ public abstract int getSize();
+
+ public int getOffset() {
+ assert offset >= 0 : this;
+ return offset;
+ }
+
+ public void setOffset(int offset) {
+ assert offset >= 0;
+ this.offset = offset;
+ }
+
+ public int getMinOffset() {
+ assert minOffset >= 0;
+ return minOffset;
+ }
+
+ public void setMinOffset(int minOffset) {
+ assert minOffset >= 0;
+ this.minOffset = minOffset;
+ }
+
+ public int getMaxOffset() {
+ assert maxOffset >= 0;
+ return maxOffset;
+ }
+
+ public void setMaxOffset(int maxOffset) {
+ assert maxOffset >= 0;
+ this.maxOffset = maxOffset;
+ }
+
+ public com.android.tools.r8.ir.code.Instruction getIR() {
+ return ir;
+ }
+ }
+
+ private static class FixedSizeInfo extends Info {
+
+ private Instruction instruction;
+
+ public FixedSizeInfo(com.android.tools.r8.ir.code.Instruction ir, Instruction instruction) {
+ super(ir);
+ this.instruction = instruction;
+ }
+
+ @Override
+ public int getSize() {
+ return instruction.getSize();
+ }
+
+ @Override
+ public int minSize() {
+ return instruction.getSize();
+ }
+
+ @Override
+ public int maxSize() {
+ return instruction.getSize();
+ }
+
+ @Override
+ public int computeSize(DexBuilder builder) {
+ instruction.setOffset(getOffset()); // for better printing of the dex code.
+ return instruction.getSize();
+ }
+
+ @Override
+ public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
+ instructions.add(instruction);
+ }
+ }
+
+ private static class MultiFixedSizeInfo extends Info {
+
+ private Instruction[] instructions;
+ private final int size;
+
+ public MultiFixedSizeInfo(com.android.tools.r8.ir.code.Instruction ir, Instruction[] instructions) {
+ super(ir);
+ this.instructions = instructions;
+ int size = 0;
+ for (Instruction instruction : instructions) {
+ size += instruction.getSize();
+ }
+ this.size = size;
+ }
+
+ @Override
+ public int computeSize(DexBuilder builder) {
+ return size;
+ }
+
+ @Override
+ public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
+ int offset = getOffset();
+ for (Instruction instruction : this.instructions) {
+ instructions.add(instruction);
+ instruction.setOffset(offset);
+ offset += instruction.getSize();
+ }
+ }
+
+ @Override
+ public int minSize() {
+ return size;
+ }
+
+ @Override
+ public int maxSize() {
+ return size;
+ }
+
+ @Override
+ public int getSize() {
+ return size;
+ }
+ }
+
+ private static class FallThroughInfo extends Info {
+
+ public FallThroughInfo(com.android.tools.r8.ir.code.Instruction ir) {
+ super(ir);
+ }
+
+ @Override
+ public int getSize() {
+ return 0;
+ }
+
+ @Override
+ public int computeSize(DexBuilder builder) {
+ return 0;
+ }
+
+ @Override
+ public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
+ }
+
+ @Override
+ public int minSize() {
+ return 0;
+ }
+
+ @Override
+ public int maxSize() {
+ return 0;
+ }
+ }
+
+ private static class GotoInfo extends Info {
+
+ private int size = -1;
+
+ public GotoInfo(com.android.tools.r8.ir.code.Goto jump) {
+ super(jump);
+ }
+
+ private com.android.tools.r8.ir.code.Goto getJump() {
+ return (com.android.tools.r8.ir.code.Goto) getIR();
+ }
+
+ @Override
+ public int getSize() {
+ assert size > 0;
+ return size;
+ }
+
+ @Override
+ public int minSize() {
+ assert new Goto(42).getSize() == 1;
+ return 1;
+ }
+
+ @Override
+ public int maxSize() {
+ assert new Goto32(0).getSize() == 3;
+ return 3;
+ }
+
+ @Override
+ public int computeSize(DexBuilder builder) {
+ assert size < 0;
+ com.android.tools.r8.ir.code.Goto jump = getJump();
+ Info targetInfo = builder.getTargetInfo(jump.getTarget());
+ // Trivial loop will be emitted as: nop & goto -1
+ if (jump == targetInfo.getIR()) {
+ size = 2;
+ return size;
+ }
+ int maxOffset = getMaxOffset();
+ int maxTargetOffset = targetInfo.getMaxOffset();
+ int delta;
+ if (maxTargetOffset < maxOffset) {
+ // Backward branch: compute exact size (the target offset is set).
+ delta = getOffset() - targetInfo.getOffset();
+ } else {
+ // Forward branch: over estimate the distance, but take into account the sizes
+ // of instructions generated so far. That way the over estimation is only for the
+ // instructions between this one and the target.
+ int maxOverEstimation = maxOffset - getOffset();
+ delta = (maxTargetOffset - maxOverEstimation) - getOffset();
+ }
+ if (delta <= Byte.MAX_VALUE) {
+ size = 1;
+ } else if (delta <= Short.MAX_VALUE) {
+ size = 2;
+ } else {
+ size = 3;
+ }
+ if (targetInfo.getIR().isReturn()) {
+ // Set the size to the min of the size of the return and the size of the goto. When
+ // adding instructions, we use the return if the computed size matches the size of the
+ // return.
+ size = Math.min(targetInfo.getSize(), size);
+ }
+ return size;
+ }
+
+ @Override
+ public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
+ com.android.tools.r8.ir.code.Goto jump = getJump();
+ int source = builder.getInfo(jump).getOffset();
+ Info targetInfo = builder.getTargetInfo(jump.getTarget());
+ int relativeOffset = targetInfo.getOffset() - source;
+ // We should never generate a goto to the following instruction or two consecutive returns.
+ // TODO(b/34726595): We might have a goto to the following instruction if we fail to DCE.
+ // assert relativeOffset != size;
+ Instruction dex;
+ // Emit a return if the target is a return and the size of the return is the computed
+ // size of this instruction.
+ if (targetInfo.getIR().isReturn() && size == targetInfo.getSize()) {
+ dex = targetInfo.getIR().asReturn().createDexInstruction(builder);
+ } else {
+ switch (size) {
+ case 1:
+ assert relativeOffset != 0;
+ dex = new Goto(relativeOffset);
+ break;
+ case 2:
+ if (relativeOffset == 0) {
+ Nop nop = new Nop();
+ instructions.add(nop);
+ dex = new Goto(-nop.getSize());
+ } else {
+ dex = new Goto16(relativeOffset);
+ }
+ break;
+ case 3:
+ dex = new Goto32(relativeOffset);
+ break;
+ default:
+ throw new Unreachable("Unexpected size for goto instruction: " + size);
+ }
+ }
+ dex.setOffset(getOffset()); // for better printing of the dex code.
+ instructions.add(dex);
+ }
+ }
+
+ public static class IfInfo extends Info {
+
+ private int size = -1;
+
+ public IfInfo(If branch) {
+ super(branch);
+ }
+
+ private If getBranch() {
+ return (If) getIR();
+ }
+
+ private boolean branchesToSelf(DexBuilder builder) {
+ If branch = getBranch();
+ Info trueTargetInfo = builder.getTargetInfo(branch.getTrueTarget());
+ return branch == trueTargetInfo.getIR();
+ }
+
+ private boolean offsetOutOfRange(DexBuilder builder) {
+ Info targetInfo = builder.getTargetInfo(getBranch().getTrueTarget());
+ int maxOffset = getMaxOffset();
+ int maxTargetOffset = targetInfo.getMaxOffset();
+ if (maxTargetOffset < maxOffset) {
+ return getOffset() - targetInfo.getOffset() < Short.MIN_VALUE;
+ }
+ // Forward branch: over estimate the distance, but take into account the sizes
+ // of instructions generated so far. That way the over estimation is only for the
+ // instructions between this one and the target.
+ int maxOverEstimation = maxOffset - getOffset();
+ return (maxTargetOffset - maxOverEstimation) - getOffset() > Short.MAX_VALUE;
+ }
+
+ @Override
+ public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
+ If branch = getBranch();
+ int source = builder.getInfo(branch).getOffset();
+ int target = builder.getInfo(branch.getTrueTarget().entry()).getOffset();
+ int relativeOffset = target - source;
+ int register1 = builder.allocatedRegister(branch.inValues().get(0), branch.getNumber());
+ if (size == 3) {
+ assert branchesToSelf(builder);
+ Nop nop = new Nop();
+ relativeOffset -= nop.getSize();
+ instructions.add(nop);
+ }
+ assert relativeOffset != 0;
+ Instruction instruction = null;
+ if (branch.isZeroTest()) {
+ switch (getBranch().getType()) {
+ case EQ:
+ instruction = new IfEqz(register1, relativeOffset);
+ break;
+ case GE:
+ instruction = new IfGez(register1, relativeOffset);
+ break;
+ case GT:
+ instruction = new IfGtz(register1, relativeOffset);
+ break;
+ case LE:
+ instruction = new IfLez(register1, relativeOffset);
+ break;
+ case LT:
+ instruction = new IfLtz(register1, relativeOffset);
+ break;
+ case NE:
+ instruction = new IfNez(register1, relativeOffset);
+ break;
+ }
+ } else {
+ int register2 = builder.allocatedRegister(branch.inValues().get(1), branch.getNumber());
+ switch (getBranch().getType()) {
+ case EQ:
+ instruction = new IfEq(register1, register2, relativeOffset);
+ break;
+ case GE:
+ instruction = new IfGe(register1, register2, relativeOffset);
+ break;
+ case GT:
+ instruction = new IfGt(register1, register2, relativeOffset);
+ break;
+ case LE:
+ instruction = new IfLe(register1, register2, relativeOffset);
+ break;
+ case LT:
+ instruction = new IfLt(register1, register2, relativeOffset);
+ break;
+ case NE:
+ instruction = new IfNe(register1, register2, relativeOffset);
+ break;
+ }
+ }
+ instruction.setOffset(getOffset());
+ instructions.add(instruction);
+ }
+
+ @Override
+ public int computeSize(DexBuilder builder) {
+ if (offsetOutOfRange(builder)) {
+ builder.needsIfRewriting(getBranch().getBlock());
+ }
+ size = branchesToSelf(builder) ? 3 : 2;
+ return size;
+ }
+
+ @Override
+ public int minSize() {
+ return 2;
+ }
+
+ @Override
+ public int maxSize() {
+ return 3;
+ }
+
+ @Override
+ public int getSize() {
+ return size;
+ }
+ }
+
+ public static class MoveInfo extends Info {
+
+ private int size = -1;
+
+ public MoveInfo(Move move) {
+ super(move);
+ }
+
+ private Move getMove() {
+ return (Move) getIR();
+ }
+
+ @Override
+ public int computeSize(DexBuilder builder) {
+ Move move = getMove();
+ int srcRegister = builder.allocatedRegister(move.src(), move.getNumber());
+ int destRegister = builder.allocatedRegister(move.dest(), move.getNumber());
+ if (srcRegister == destRegister) {
+ size = 1;
+ } else if (srcRegister <= Constants.U4BIT_MAX && destRegister <= Constants.U4BIT_MAX) {
+ size = 1;
+ } else if (destRegister <= Constants.U8BIT_MAX) {
+ size = 2;
+ } else {
+ size = 3;
+ }
+ return size;
+ }
+
+ @Override
+ public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
+ Move move = getMove();
+ int dest = builder.allocatedRegister(move.dest(), move.getNumber());
+ int src = builder.allocatedRegister(move.src(), move.getNumber());
+ Instruction instruction = null;
+ switch (size) {
+ case 1:
+ if (src == dest) {
+ instruction = new Nop();
+ break;
+ }
+ switch (move.outType()) {
+ case SINGLE:
+ instruction = new com.android.tools.r8.code.Move(dest, src);
+ break;
+ case WIDE:
+ instruction = new MoveWide(dest, src);
+ break;
+ case OBJECT:
+ instruction = new MoveObject(dest, src);
+ break;
+ default:
+ throw new Unreachable("Unexpected type: " + move.outType());
+ }
+ break;
+ case 2:
+ switch (move.outType()) {
+ case SINGLE:
+ instruction = new MoveFrom16(dest, src);
+ break;
+ case WIDE:
+ instruction = new MoveWideFrom16(dest, src);
+ break;
+ case OBJECT:
+ instruction = new MoveObjectFrom16(dest, src);
+ break;
+ default:
+ throw new Unreachable("Unexpected type: " + move.outType());
+ }
+ break;
+ case 3:
+ switch (move.outType()) {
+ case SINGLE:
+ instruction = new Move16(dest, src);
+ break;
+ case WIDE:
+ instruction = new MoveWide16(dest, src);
+ break;
+ case OBJECT:
+ instruction = new MoveObject16(dest, src);
+ break;
+ default:
+ throw new Unreachable("Unexpected type: " + move.outType());
+ }
+ break;
+ }
+ instruction.setOffset(getOffset());
+ instructions.add(instruction);
+ }
+
+ @Override
+ public int minSize() {
+ assert new Nop().getSize() == 1 && new com.android.tools.r8.code.Move(0, 0).getSize() == 1;
+ return 1;
+ }
+
+ @Override
+ public int maxSize() {
+ assert new Move16(0, 0).getSize() == 3;
+ return 3;
+ }
+
+ @Override
+ public int getSize() {
+ assert size > 0;
+ return size;
+ }
+ }
+
+ // Return-type wrapper for try-related data.
+ private static class TryInfo {
+
+ public final Try[] tries;
+ public final TryHandler[] handlers;
+
+ public TryInfo(Try[] tries, TryHandler[] handlers) {
+ this.tries = tries;
+ this.handlers = handlers;
+ }
+ }
+
+ // Helper class for coalescing ranges for try blocks.
+ private static class TryItem implements Comparable<TryItem> {
+
+ public final CatchHandlers<BasicBlock> handlers;
+ public int start;
+ public int end;
+
+ public TryItem(CatchHandlers<BasicBlock> handlers, int start, int end) {
+ this.handlers = handlers;
+ this.start = start;
+ this.end = end;
+ }
+
+ @Override
+ public int compareTo(TryItem other) {
+ return Integer.compare(start, other.start);
+ }
+ }
+
+ private static class SwitchPayloadInfo {
+
+ public final Switch ir;
+ public final Format31t dex;
+
+ public SwitchPayloadInfo(Switch ir, Format31t dex) {
+ this.ir = ir;
+ this.dex = dex;
+ }
+ }
+
+ private static class FillArrayDataInfo {
+
+ public final NewArrayFilledData ir;
+ public final FillArrayData dex;
+
+ public FillArrayDataInfo(NewArrayFilledData ir, FillArrayData dex) {
+ this.ir = ir;
+ this.dex = dex;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
new file mode 100644
index 0000000..7e47ed2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -0,0 +1,376 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.code.FillArrayData;
+import com.android.tools.r8.code.FillArrayDataPayload;
+import com.android.tools.r8.code.FilledNewArray;
+import com.android.tools.r8.code.FilledNewArrayRange;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.InvokeDirectRange;
+import com.android.tools.r8.code.InvokeInterface;
+import com.android.tools.r8.code.InvokeInterfaceRange;
+import com.android.tools.r8.code.InvokePolymorphic;
+import com.android.tools.r8.code.InvokePolymorphicRange;
+import com.android.tools.r8.code.InvokeStatic;
+import com.android.tools.r8.code.InvokeStaticRange;
+import com.android.tools.r8.code.InvokeSuper;
+import com.android.tools.r8.code.InvokeSuperRange;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.InvokeVirtualRange;
+import com.android.tools.r8.code.MoveResult;
+import com.android.tools.r8.code.MoveResultObject;
+import com.android.tools.r8.code.MoveResultWide;
+import com.android.tools.r8.code.SwitchPayload;
+import com.android.tools.r8.code.Throw;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
+import com.android.tools.r8.graph.DexDebugEntry;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Switch.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class DexSourceCode implements SourceCode {
+
+ private final DexCode code;
+ private final DexAccessFlags accessFlags;
+ private final DexProto proto;
+
+ // Mapping from instruction offset to instruction index in the DexCode instruction array.
+ private final Map<Integer, Integer> offsetToInstructionIndex = new HashMap<>();
+
+ private final SwitchPayloadResolver switchPayloadResolver = new SwitchPayloadResolver();
+ private final ArrayFilledDataPayloadResolver arrayFilledDataPayloadResolver =
+ new ArrayFilledDataPayloadResolver();
+
+ private Try currentTryRange = null;
+ private CatchHandlers<Integer> currentCatchHandlers = null;
+ private Instruction currentDexInstruction = null;
+
+ private final List<MoveType> argumentTypes;
+
+ private List<DexDebugEntry> debugEntries = null;
+
+ public DexSourceCode(DexCode code, DexEncodedMethod method) {
+ this.code = code;
+ this.proto = method.method.proto;
+ this.accessFlags = method.accessFlags;
+ argumentTypes = computeArgumentTypes();
+ DexDebugInfo info = code.getDebugInfo();
+ if (info != null) {
+ debugEntries = info.computeEntries();
+ }
+ }
+
+ @Override
+ public boolean verifyRegister(int register) {
+ return register < code.registerSize;
+ }
+
+ @Override
+ public boolean needsPrelude() {
+ return !accessFlags.isStatic() || !argumentTypes.isEmpty();
+ }
+
+ @Override
+ public int instructionCount() {
+ return code.instructions.length;
+ }
+
+ @Override
+ public DebugLocalInfo getCurrentLocal(int register) {
+ // TODO(zerny): Support locals in the dex front-end. b/36378142
+ return null;
+ }
+
+ @Override
+ public void setUp() {
+ // Collect all payloads in the instruction stream.
+ for (int index = 0; index < code.instructions.length; index++) {
+ Instruction insn = code.instructions[index];
+ offsetToInstructionIndex.put(insn.getOffset(), index);
+ if (insn.isPayload()) {
+ if (insn.isSwitchPayload()) {
+ switchPayloadResolver.resolve((SwitchPayload) insn);
+ } else {
+ arrayFilledDataPayloadResolver.resolve((FillArrayDataPayload) insn);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void buildPrelude(IRBuilder builder) {
+ if (code.incomingRegisterSize == 0) {
+ return;
+ }
+ // Fill in the Argument instructions (incomingRegisterSize last registers) in the argument
+ // block.
+ int register = code.registerSize - code.incomingRegisterSize;
+ if (!accessFlags.isStatic()) {
+ builder.addThisArgument(register);
+ ++register;
+ }
+ for (MoveType type : argumentTypes) {
+ builder.addNonThisArgument(register, type);
+ register += type.requiredRegisters();
+ }
+ }
+
+ @Override
+ public void buildPostlude(IRBuilder builder) {
+ // Intentionally left empty. (Needed in the Java bytecode frontend for synchronization support.)
+ }
+
+ @Override
+ public void closedCurrentBlockWithFallthrough(int fallthroughInstructionIndex) {
+ }
+
+ @Override
+ public void closedCurrentBlock() {
+ }
+
+ @Override
+ public void buildInstruction(IRBuilder builder, int instructionIndex) {
+ updateCurrentCatchHandlers(instructionIndex);
+ emitDebugPosition(instructionIndex, builder);
+ currentDexInstruction = code.instructions[instructionIndex];
+ currentDexInstruction.buildIR(builder);
+ }
+
+ @Override
+ public CatchHandlers<Integer> getCurrentCatchHandlers() {
+ return currentCatchHandlers;
+ }
+
+ @Override
+ public boolean verifyCurrentInstructionCanThrow() {
+ return currentDexInstruction.canThrow();
+ }
+
+ @Override
+ public boolean verifyLocalInScope(DebugLocalInfo local) {
+ return true;
+ }
+
+ private void updateCurrentCatchHandlers(int instructionIndex) {
+ Try tryRange = getTryForOffset(instructionOffset(instructionIndex));
+ if (tryRange == currentTryRange) {
+ return;
+ }
+ currentTryRange = tryRange;
+ if (tryRange == null) {
+ currentCatchHandlers = null;
+ } else {
+ currentCatchHandlers = new CatchHandlers<>(
+ getTryHandlerGuards(tryRange),
+ getTryHandlerOffsets(tryRange));
+ }
+ }
+
+ private void emitDebugPosition(int instructionIndex, IRBuilder builder) {
+ if (debugEntries == null || debugEntries.isEmpty()) {
+ return;
+ }
+ int offset = instructionOffset(instructionIndex);
+ for (DexDebugEntry entry : debugEntries) {
+ if (entry.address == offset) {
+ builder.updateCurrentDebugPosition(entry.line, entry.sourceFile);
+ return;
+ }
+ if (entry.address > offset) {
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void clear() {
+ switchPayloadResolver.clear();
+ arrayFilledDataPayloadResolver.clear();
+ }
+
+ @Override
+ public int instructionIndex(int instructionOffset) {
+ return offsetToInstructionIndex.get(instructionOffset);
+ }
+
+ @Override
+ public int instructionOffset(int instructionIndex) {
+ return code.instructions[instructionIndex].getOffset();
+ }
+
+ @Override
+ public void resolveAndBuildSwitch(Type type, int value, int fallthroughOffset, int payloadOffset,
+ IRBuilder builder) {
+ builder.addSwitch(type, value, switchPayloadResolver.getKeys(payloadOffset), fallthroughOffset,
+ switchPayloadResolver.absoluteTargets(payloadOffset));
+ }
+
+ @Override
+ public void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset,
+ IRBuilder builder) {
+ builder.addNewArrayFilledData(arrayRef,
+ arrayFilledDataPayloadResolver.getElementWidth(payloadOffset),
+ arrayFilledDataPayloadResolver.getSize(payloadOffset),
+ arrayFilledDataPayloadResolver.getData(payloadOffset));
+ }
+
+ private List<MoveType> computeArgumentTypes() {
+ List<MoveType> types = new ArrayList<>(proto.parameters.values.length);
+ String shorty = proto.shorty.toString();
+ for (int i = 1; i < proto.shorty.size; i++) {
+ MoveType moveType = MoveType.fromTypeDescriptorChar(shorty.charAt(i));
+ types.add(moveType);
+ }
+ return types;
+ }
+
+ private boolean isInvoke(Instruction dex) {
+ return dex instanceof InvokeDirect
+ || dex instanceof InvokeDirectRange
+ || dex instanceof InvokeVirtual
+ || dex instanceof InvokeVirtualRange
+ || dex instanceof InvokeInterface
+ || dex instanceof InvokeInterfaceRange
+ || dex instanceof InvokeStatic
+ || dex instanceof InvokeStaticRange
+ || dex instanceof InvokeSuper
+ || dex instanceof InvokeSuperRange
+ || dex instanceof InvokePolymorphic
+ || dex instanceof InvokePolymorphicRange
+ || dex instanceof FilledNewArray
+ || dex instanceof FilledNewArrayRange;
+ }
+
+ private boolean isMoveResult(Instruction dex) {
+ return dex instanceof MoveResult
+ || dex instanceof MoveResultObject
+ || dex instanceof MoveResultWide;
+ }
+
+ @Override
+ public boolean traceInstruction(int index, IRBuilder builder) {
+ Instruction dex = code.instructions[index];
+ int offset = dex.getOffset();
+ assert !dex.isPayload();
+ int[] targets = dex.getTargets();
+ if (targets != Instruction.NO_TARGETS) {
+ // Check that we don't ever have instructions that can throw and have targets.
+ assert !dex.canThrow();
+ for (int relativeOffset : targets) {
+ builder.ensureSuccessorBlock(offset + relativeOffset);
+ }
+ return true;
+ }
+ if (dex.canThrow()) {
+ // If the instruction can throw and is in a try block, add edges to its catch successors.
+ Try tryRange = getTryForOffset(offset);
+ if (tryRange != null) {
+ // Ensure the block starts at the start of the try-range (don't enqueue, not a target).
+ int tryRangeStartAddress = tryRange.startAddress;
+ if (isMoveResult(code.instructions[offsetToInstructionIndex.get(tryRangeStartAddress)])) {
+ // If a handler range starts at a move result instruction it is safe to start it at
+ // the following instruction since the move-result cannot throw an exception. Doing so
+ // makes sure that we do not split an invoke and its move result instruction across
+ // two blocks.
+ ++tryRangeStartAddress;
+ }
+ builder.ensureBlockWithoutEnqueuing(tryRangeStartAddress);
+ // Edge to exceptional successors.
+ for (Integer handlerOffset : getUniqueTryHandlerOffsets(tryRange)) {
+ builder.ensureSuccessorBlock(handlerOffset);
+ }
+ // If the following instruction is a move-result include it in this (the invokes) block.
+ if (index + 1 < code.instructions.length && isMoveResult(code.instructions[index + 1])) {
+ assert isInvoke(dex);
+ ++index;
+ dex = code.instructions[index];
+ }
+ // Edge to normal successor if any (fallthrough).
+ if (!(dex instanceof Throw)) {
+ builder.ensureSuccessorBlock(dex.getOffset() + dex.getSize());
+ }
+ return true;
+ }
+ // Close the block if the instruction is a throw, otherwise the block remains open.
+ return dex instanceof Throw;
+ }
+ if (dex.isSwitch()) {
+ // TODO(zerny): Remove this from block computation.
+ switchPayloadResolver.addPayloadUser(dex);
+
+ for (int target : switchPayloadResolver.absoluteTargets(dex)) {
+ builder.ensureSuccessorBlock(target);
+ }
+ builder.ensureSuccessorBlock(offset + dex.getSize());
+ return true;
+ }
+ // TODO(zerny): Remove this from block computation.
+ if (dex.hasPayload()) {
+ arrayFilledDataPayloadResolver.addPayloadUser((FillArrayData) dex);
+ }
+ // This instruction does not close the block.
+ return false;
+ }
+
+ private boolean inTryRange(Try tryItem, int offset) {
+ return tryItem.startAddress <= offset
+ && offset < tryItem.startAddress + tryItem.instructionCount;
+ }
+
+ private Try getTryForOffset(int offset) {
+ for (Try tryRange : code.tries) {
+ if (inTryRange(tryRange, offset)) {
+ return tryRange;
+ }
+ }
+ return null;
+ }
+
+ private Set<Integer> getUniqueTryHandlerOffsets(Try tryRange) {
+ return new HashSet<>(getTryHandlerOffsets(tryRange));
+ }
+
+ private List<Integer> getTryHandlerOffsets(Try tryRange) {
+ List<Integer> handlerOffsets = new ArrayList<>();
+ TryHandler handler = code.handlers[tryRange.handlerIndex];
+ for (TypeAddrPair pair : handler.pairs) {
+ handlerOffsets.add(pair.addr);
+ }
+ if (handler.catchAllAddr != TryHandler.NO_HANDLER) {
+ handlerOffsets.add(handler.catchAllAddr);
+ }
+ return handlerOffsets;
+ }
+
+ private List<DexType> getTryHandlerGuards(Try tryRange) {
+ List<DexType> handlerGuards = new ArrayList<>();
+ TryHandler handler = code.handlers[tryRange.handlerIndex];
+ for (TypeAddrPair pair : handler.pairs) {
+ handlerGuards.add(pair.type);
+ }
+ if (handler.catchAllAddr != TryHandler.NO_HANDLER) {
+ handlerGuards.add(DexItemFactory.catchAllType);
+ }
+ return handlerGuards;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
new file mode 100644
index 0000000..1576190
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -0,0 +1,1864 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.code.PackedSwitchPayload;
+import com.android.tools.r8.code.SparseSwitchPayload;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.And;
+import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.ArrayGet;
+import com.android.tools.r8.ir.code.ArrayLength;
+import com.android.tools.r8.ir.code.ArrayPut;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlock.EdgeType;
+import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.CheckCast;
+import com.android.tools.r8.ir.code.Cmp;
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.ConstClass;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstString;
+import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.DebugLocalRead;
+import com.android.tools.r8.ir.code.DebugLocalUninitialized;
+import com.android.tools.r8.ir.code.DebugLocalWrite;
+import com.android.tools.r8.ir.code.DebugPosition;
+import com.android.tools.r8.ir.code.Div;
+import com.android.tools.r8.ir.code.Goto;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.InstanceOf;
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.InvokeCustom;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.Monitor;
+import com.android.tools.r8.ir.code.MoveException;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Mul;
+import com.android.tools.r8.ir.code.Neg;
+import com.android.tools.r8.ir.code.NewArrayEmpty;
+import com.android.tools.r8.ir.code.NewArrayFilledData;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Not;
+import com.android.tools.r8.ir.code.NumberConversion;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Or;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Rem;
+import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.code.Shl;
+import com.android.tools.r8.ir.code.Shr;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Sub;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.ir.code.Throw;
+import com.android.tools.r8.ir.code.Ushr;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.Value.DebugInfo;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.code.Xor;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+/**
+ * Builder object for constructing high-level IR from dex bytecode.
+ *
+ * <p>The generated IR is in SSA form. The SSA construction is based on the paper
+ * "Simple and Efficient Construction of Static Single Assignment Form" available at
+ * http://compilers.cs.uni-saarland.de/papers/bbhlmz13cc.pdf
+ */
+public class IRBuilder {
+
+ // SSA construction uses a worklist of basic blocks reachable from the entry and their
+ // instruction offsets.
+ private static class WorklistItem {
+
+ private final BasicBlock block;
+ private final int firstInstructionIndex;
+
+ private WorklistItem(BasicBlock block, int firstInstructionIndex) {
+ assert block != null;
+ this.block = block;
+ this.firstInstructionIndex = firstInstructionIndex;
+ }
+ }
+
+ /**
+ * Representation of lists of values that can be used as keys in maps. A list of
+ * values is equal to another list of values if it contains exactly the same values
+ * in the same order.
+ */
+ private static class ValueList {
+
+ private List<Value> values = new ArrayList<>();
+
+ /**
+ * Creates a ValueList of all the operands at the given index in the list of phis.
+ */
+ public static ValueList fromPhis(List<Phi> phis, int index) {
+ ValueList result = new ValueList();
+ for (Phi phi : phis) {
+ result.values.add(phi.getOperand(index));
+ }
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ return values.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof ValueList)) {
+ return false;
+ }
+ ValueList o = (ValueList) other;
+ if (o.values.size() != values.size()) {
+ return false;
+ }
+ for (int i = 0; i < values.size(); i++) {
+ if (values.get(i) != o.values.get(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ // Mapping from instruction offsets to basic-block targets.
+ private final Map<Integer, BasicBlock> targets = new HashMap<>();
+
+ // Worklist of reachable blocks.
+ private final Queue<Integer> traceBlocksWorklist = new LinkedList<>();
+
+ // Bitmap to ensure we don't process an instruction more than once.
+ private boolean[] processedInstructions = null;
+
+ // Bitmap of processed subroutine instructions. Lazily allocated off the fast-path.
+ private Set<Integer> processedSubroutineInstructions = null;
+
+ // Worklist for SSA construction.
+ private final Queue<WorklistItem> ssaWorklist = new LinkedList<>();
+
+ // Basic blocks. Added after processing from the worklist.
+ private LinkedList<BasicBlock> blocks = new LinkedList<>();
+
+ private BasicBlock currentBlock = null;
+
+ // Mappings for canonicalizing constants of a given type at IR construction time.
+ private Map<Long, ConstNumber> intConstants = new HashMap<>();
+ private Map<Long, ConstNumber> longConstants = new HashMap<>();
+ private Map<Long, ConstNumber> floatConstants = new HashMap<>();
+ private Map<Long, ConstNumber> doubleConstants = new HashMap<>();
+ private Map<Long, ConstNumber> nullConstants = new HashMap<>();
+
+ private List<BasicBlock> exitBlocks = new ArrayList<>();
+ private BasicBlock normalExitBlock;
+
+ private List<BasicBlock.Pair> needGotoToCatchBlocks = new ArrayList<>();
+
+ final private ValueNumberGenerator valueNumberGenerator;
+
+ private DebugPosition currentDebugPosition = null;
+
+ private DexEncodedMethod method;
+
+ // Source code to build IR from. Null if already built.
+ private SourceCode source;
+
+ boolean throwingInstructionInCurrentBlock = false;
+
+ private final InternalOptions options;
+
+ public IRBuilder(DexEncodedMethod method, SourceCode source, InternalOptions options) {
+ this(method, source, new ValueNumberGenerator(), options);
+ }
+
+ public IRBuilder(
+ DexEncodedMethod method,
+ SourceCode source,
+ ValueNumberGenerator valueNumberGenerator,
+ InternalOptions options) {
+ assert source != null;
+ this.method = method;
+ this.source = source;
+ this.valueNumberGenerator = valueNumberGenerator;
+ this.options = options;
+ }
+
+
+ private void addToWorklist(BasicBlock block, int firstInstructionIndex) {
+ // TODO(ager): Filter out the ones that are already in the worklist, mark bit in block?
+ if (!block.isFilled()) {
+ ssaWorklist.add(new WorklistItem(block, firstInstructionIndex));
+ }
+ }
+
+ private void setCurrentBlock(BasicBlock block) {
+ currentBlock = block;
+ }
+
+ /**
+ * Build the high-level IR in SSA form.
+ *
+ * @return The list of basic blocks. First block is the main entry.
+ */
+ public IRCode build() {
+ assert source != null;
+ source.setUp();
+
+ // Create entry block.
+ setCurrentBlock(new BasicBlock());
+
+ // If the method needs a prelude, the entry block must never be the target of other blocks.
+ if (!source.needsPrelude()) {
+ targets.put(0, currentBlock);
+ }
+
+ // Process reachable code paths starting from instruction 0.
+ processedInstructions = new boolean[source.instructionCount()];
+ traceBlocksWorklist.add(0);
+ while (!traceBlocksWorklist.isEmpty()) {
+ int startOfBlockOffset = traceBlocksWorklist.remove();
+ int startOfBlockIndex = source.instructionIndex(startOfBlockOffset);
+ // Check that the block has not been processed after being added.
+ if (isIndexProcessed(startOfBlockIndex)) {
+ continue;
+ }
+ // Process each instruction until the block is closed.
+ for (int index = startOfBlockIndex; index < source.instructionCount(); ++index) {
+ markIndexProcessed(index);
+ boolean closed = source.traceInstruction(index, this);
+ if (closed) {
+ break;
+ }
+ // If the next instruction starts a block, fall through to it.
+ if (index + 1 < source.instructionCount()) {
+ int nextOffset = source.instructionOffset(index + 1);
+ if (getTarget(nextOffset) != null) {
+ ensureSuccessorBlock(nextOffset);
+ break;
+ }
+ }
+ }
+ }
+ processedInstructions = null;
+
+ source.buildPrelude(this);
+
+ // Process normal blocks reachable from the entry block using a worklist of reachable
+ // blocks.
+ int blockNumber = 0;
+ addToWorklist(currentBlock, 0);
+ blockNumber = processWorklist(blockNumber);
+
+ // Check that the last block is closed and does not fall off the end.
+ assert currentBlock == null;
+
+ // Handle where a catch handler hits the same block as the fallthrough.
+ blockNumber = handleFallthroughToCatchBlock(blockNumber);
+
+ // Verify that we have properly filled all blocks
+ // Must be after handle-catch (which has delayed edges),
+ // but before handle-exit (which does not maintain predecessor counts).
+ assert verifyFilledPredecessors();
+
+ // If there are multiple returns create an exit block.
+ blockNumber = handleExitBlock(blockNumber);
+
+ // Clear all reaching definitions to free up memory (and avoid invalid use).
+ for (BasicBlock block : blocks) {
+ block.clearCurrentDefinitions();
+ }
+
+ // Join predecessors for which all phis have the same inputs. This avoids generating the
+ // same phi moves in multiple blocks.
+ joinPredecessorsWithIdenticalPhis();
+
+ // Split critical edges to make sure that we have a place to insert phi moves if
+ // necessary.
+ splitCriticalEdges();
+
+ // Consistency check.
+ assert phiOperandsAreConsistent();
+
+ // Package up the IR code.
+ IRCode ir = new IRCode(method, blocks, normalExitBlock, valueNumberGenerator);
+
+ // Create block order and make sure that all blocks are immediately followed by their
+ // fallthrough block if any.
+ traceBlocks(ir);
+
+ // Clear the code so we don't build multiple times.
+ source.clear();
+ clearCanonicalizationMaps();
+ source = null;
+
+ assert ir.isConsistentSSA();
+ return ir;
+ }
+
+ private void clearCanonicalizationMaps() {
+ intConstants = null;
+ longConstants = null;
+ floatConstants = null;
+ doubleConstants = null;
+ nullConstants = null;
+ }
+
+ private boolean verifyFilledPredecessors() {
+ for (BasicBlock block : blocks) {
+ assert block.verifyFilledPredecessors();
+ }
+ return true;
+ }
+
+ private int processWorklist(int blockNumber) {
+ for (WorklistItem item = ssaWorklist.poll(); item != null; item = ssaWorklist.poll()) {
+ if (item.block.isFilled()) {
+ continue;
+ }
+ setCurrentBlock(item.block);
+ blocks.add(currentBlock);
+ currentBlock.setNumber(blockNumber++);
+ // Build IR for each dex instruction in the block.
+ for (int i = item.firstInstructionIndex; i < source.instructionCount(); ++i) {
+ if (currentBlock == null) {
+ source.closedCurrentBlock();
+ break;
+ }
+ BasicBlock block = getTarget(source.instructionOffset(i));
+ if (block != null && block != currentBlock) {
+ closeCurrentBlockWithFallThrough(block);
+ source.closedCurrentBlockWithFallthrough(i);
+ addToWorklist(block, i);
+ break;
+ }
+ source.buildInstruction(this, i);
+ }
+ }
+ return blockNumber;
+ }
+
+ // Helper to resolve switch payloads and build switch instructions (dex code only).
+ public void resolveAndBuildSwitch(Switch.Type type, int value, int fallthroughOffset,
+ int payloadOffset) {
+ source.resolveAndBuildSwitch(type, value, fallthroughOffset, payloadOffset, this);
+ }
+
+ // Helper to resolve fill-array data and build new-array instructions (dex code only).
+ public void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset) {
+ source.resolveAndBuildNewArrayFilledData(arrayRef, payloadOffset, this);
+ }
+
+ /**
+ * Add an (non-jump) instruction to the builder.
+ *
+ * @param ir IR instruction to add as the next instruction.
+ */
+ public void add(Instruction ir) {
+ assert !ir.isJumpInstruction();
+ addInstruction(ir);
+ }
+
+ public void addThisArgument(int register) {
+ DebugLocalInfo local = getCurrentLocal(register);
+ DebugInfo info = local == null ? null : new DebugInfo(local, null);
+ Value value = writeRegister(register, MoveType.OBJECT, ThrowingInfo.NO_THROW, info);
+ addInstruction(new Argument(value));
+ value.markAsThis();
+ }
+
+ public void addNonThisArgument(int register, MoveType moveType) {
+ DebugLocalInfo local = getCurrentLocal(register);
+ DebugInfo info = local == null ? null : new DebugInfo(local, null);
+ Value value = writeRegister(register, moveType, ThrowingInfo.NO_THROW, info);
+ addInstruction(new Argument(value));
+ }
+
+ public void addDebugUninitialized(int register, ConstType type) {
+ if (!options.debug) {
+ return;
+ }
+ Value value = writeRegister(register, MoveType.fromConstType(type), ThrowingInfo.NO_THROW, null);
+ assert value.getLocalInfo() == null;
+ addInstruction(new DebugLocalUninitialized(type, value));
+ }
+
+ public void addDebugLocalStart(int register, DebugLocalInfo local) {
+ if (!options.debug) {
+ return;
+ }
+ assert local != null;
+ assert local == getCurrentLocal(register);
+ MoveType moveType = MoveType.fromDexType(local.type);
+ Value in = readRegisterIgnoreLocal(register, moveType);
+ if (in.isPhi() || in.getLocalInfo() != local) {
+ // We cannot shortcut if the local is defined by a phi as it could end up being trivial.
+ addDebugLocalWrite(moveType, register, in);
+ } else {
+ DebugLocalRead read = addDebugLocalRead(register, local);
+ if (read != null) {
+ read.addDebugLocalStart();
+ }
+ }
+ }
+
+ public void addDebugLocalEnd(int register, DebugLocalInfo local) {
+ if (!options.debug) {
+ return;
+ }
+ DebugLocalRead read = addDebugLocalRead(register, local);
+ if (read != null) {
+ read.addDebugLocalEnd();
+ }
+ }
+
+ private void addDebugLocalWrite(MoveType type, int dest, Value in) {
+ Value out = writeRegister(dest, type, ThrowingInfo.NO_THROW);
+ DebugLocalWrite write = new DebugLocalWrite(out, in);
+ assert !write.instructionTypeCanThrow();
+ addInstruction(write);
+ }
+
+ public DebugLocalRead addDebugLocalRead(int register, DebugLocalInfo local) {
+ if (!options.debug) {
+ return null;
+ }
+ assert local != null;
+ assert local == getCurrentLocal(register);
+ MoveType moveType = MoveType.fromDexType(local.type);
+ // Invalid debug-info may cause attempt to read a local that is not actually alive.
+ // See b/37722432 and regression test {@code jasmin.InvalidDebugInfoTests::testInvalidInfoThrow}
+ Value in = readRegisterIgnoreLocal(register, moveType);
+ if (in.isUninitializedLocal() || in.getLocalInfo() != local) {
+ return null;
+ }
+ assert in.getLocalInfo() == local;
+ DebugLocalRead readLocal = new DebugLocalRead(in);
+ assert !readLocal.instructionTypeCanThrow();
+ addInstruction(readLocal);
+ return readLocal;
+ }
+
+ public void addAdd(NumericType type, int dest, int left, int right) {
+ Value in1 = readNumericRegister(left, type);
+ Value in2 = readNumericRegister(right, type);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Add instruction = new Add(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addAddLiteral(NumericType type, int dest, int value, int constant) {
+ assert isNonLongIntegerType(type);
+ Value in1 = readNumericRegister(value, type);
+ Value in2 = readLiteral(type, constant);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Add instruction = new Add(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addAnd(NumericType type, int dest, int left, int right) {
+ assert isIntegerType(type);
+ Value in1 = readNumericRegister(left, type);
+ Value in2 = readNumericRegister(right, type);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ And instruction = new And(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addAndLiteral(NumericType type, int dest, int value, int constant) {
+ assert isNonLongIntegerType(type);
+ Value in1 = readNumericRegister(value, type);
+ Value in2 = readLiteral(type, constant);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ And instruction = new And(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addArrayGet(MemberType type, int dest, int array, int index) {
+ Value in1 = readRegister(array, MoveType.OBJECT);
+ Value in2 = readRegister(index, MoveType.SINGLE);
+ Value out = writeRegister(dest, MoveType.fromMemberType(type), ThrowingInfo.CAN_THROW);
+ ArrayGet instruction = new ArrayGet(type, out, in1, in2);
+ assert instruction.instructionTypeCanThrow();
+ add(instruction);
+ }
+
+ public void addArrayLength(int dest, int array) {
+ Value in = readRegister(array, MoveType.OBJECT);
+ Value out = writeRegister(dest, MoveType.SINGLE, ThrowingInfo.CAN_THROW);
+ ArrayLength instruction = new ArrayLength(out, in);
+ assert instruction.instructionTypeCanThrow();
+ add(instruction);
+ }
+
+ public void addArrayPut(MemberType type, int value, int array, int index) {
+ List<Value> ins = new ArrayList<>(3);
+ ins.add(readRegister(value, MoveType.fromMemberType(type)));
+ ins.add(readRegister(array, MoveType.OBJECT));
+ ins.add(readRegister(index, MoveType.SINGLE));
+ ArrayPut instruction = new ArrayPut(type, ins);
+ add(instruction);
+ }
+
+ public void addCheckCast(int value, DexType type) {
+ Value in = readRegister(value, MoveType.OBJECT);
+ Value out = writeRegister(value, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
+ CheckCast instruction = new CheckCast(out, in, type);
+ assert instruction.instructionTypeCanThrow();
+ add(instruction);
+ }
+
+ public void addCmp(NumericType type, Bias bias, int dest, int left, int right) {
+ Value in1 = readNumericRegister(left, type);
+ Value in2 = readNumericRegister(right, type);
+ Value out = writeRegister(dest, MoveType.SINGLE, ThrowingInfo.NO_THROW);
+ Cmp instruction = new Cmp(type, bias, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ add(instruction);
+ }
+
+ public void addConst(MoveType type, int dest, long value) {
+ ConstNumber instruction;
+ if (type == MoveType.SINGLE) {
+ Value out = writeRegister(dest, type, ThrowingInfo.NO_THROW);
+ instruction = new ConstNumber(ConstType.INT_OR_FLOAT, out, value);
+ } else {
+ assert type == MoveType.WIDE;
+ Value out = writeRegister(dest, type, ThrowingInfo.NO_THROW);
+ instruction = new ConstNumber(ConstType.LONG_OR_DOUBLE, out, value);
+ }
+ assert !instruction.instructionTypeCanThrow();
+ add(instruction);
+ }
+
+ // TODO(ager): Does art support changing the value of locals during debugging? If so, we need
+ // to disable constant canonicalization in debug builds to make sure we have separate values
+ // for separate locals.
+ private void canonicalizeAndAddConst(
+ ConstType type, int dest, long value, Map<Long, ConstNumber> table) {
+ ConstNumber existing = table.get(value);
+ if (existing != null) {
+ currentBlock.writeCurrentDefinition(dest, existing.outValue(), ThrowingInfo.NO_THROW);
+ } else {
+ Value out = writeRegister(dest, MoveType.fromConstType(type), ThrowingInfo.NO_THROW);
+ ConstNumber instruction = new ConstNumber(type, out, value);
+ BasicBlock entryBlock = blocks.get(0);
+ if (currentBlock != entryBlock) {
+ // Insert the constant instruction at the start of the block right after the argument
+ // instructions. It is important that the const instruction is put before any instruction
+ // that can throw exceptions (since the value could be used on the exceptional edge).
+ InstructionListIterator it = entryBlock.listIterator();
+ while (it.hasNext()) {
+ if (!it.next().isArgument()) {
+ it.previous();
+ break;
+ }
+ }
+ it.add(instruction);
+ } else {
+ add(instruction);
+ }
+ table.put(value, instruction);
+ }
+ }
+
+ public void addLongConst(int dest, long value) {
+ canonicalizeAndAddConst(ConstType.LONG, dest, value, longConstants);
+ }
+
+ public void addDoubleConst(int dest, long value) {
+ canonicalizeAndAddConst(ConstType.DOUBLE, dest, value, doubleConstants);
+ }
+
+ public void addIntConst(int dest, long value) {
+ canonicalizeAndAddConst(ConstType.INT, dest, value, intConstants);
+ }
+
+ public void addFloatConst(int dest, long value) {
+ canonicalizeAndAddConst(ConstType.FLOAT, dest, value, floatConstants);
+ }
+
+ public void addNullConst(int dest, long value) {
+ canonicalizeAndAddConst(ConstType.INT, dest, value, nullConstants);
+ }
+
+ public void addConstClass(int dest, DexType type) {
+ Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
+ ConstClass instruction = new ConstClass(out, type);
+ assert instruction.instructionTypeCanThrow();
+ add(instruction);
+ }
+
+ public void addConstString(int dest, DexString string) {
+ Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
+ ConstString instruction = new ConstString(out, string);
+ add(instruction);
+ }
+
+ public void addDiv(NumericType type, int dest, int left, int right) {
+ boolean canThrow = type != NumericType.DOUBLE && type != NumericType.FLOAT;
+ Value in1 = readNumericRegister(left, type);
+ Value in2 = readNumericRegister(right, type);
+ Value out = writeNumericRegister(dest, type, canThrow ? ThrowingInfo.CAN_THROW : ThrowingInfo.NO_THROW);
+ Div instruction = new Div(type, out, in1, in2);
+ assert instruction.instructionTypeCanThrow() == canThrow;
+ add(instruction);
+ }
+
+ public void addDivLiteral(NumericType type, int dest, int value, int constant) {
+ assert isNonLongIntegerType(type);
+ boolean canThrow = type != NumericType.DOUBLE && type != NumericType.FLOAT;
+ Value in1 = readNumericRegister(value, type);
+ Value in2 = readLiteral(type, constant);
+ Value out = writeNumericRegister(dest, type, canThrow ? ThrowingInfo.CAN_THROW : ThrowingInfo.NO_THROW);
+ Div instruction = new Div(type, out, in1, in2);
+ assert instruction.instructionTypeCanThrow() == canThrow;
+ add(instruction);
+ }
+
+ public Monitor addMonitor(Monitor.Type type, int monitor) {
+ Value in = readRegister(monitor, MoveType.OBJECT);
+ Monitor monitorEnter = new Monitor(type, in);
+ add(monitorEnter);
+ return monitorEnter;
+ }
+
+ public void addMove(MoveType type, int dest, int src) {
+ Value in = readRegister(src, type);
+ if (options.debug) {
+ // If the move is writing to a different local we must construct a new value.
+ DebugLocalInfo destLocal = getCurrentLocal(dest);
+ if (destLocal != null && destLocal != in.getLocalInfo()) {
+ addDebugLocalWrite(type, dest, in);
+ return;
+ }
+ }
+ currentBlock.writeCurrentDefinition(dest, in, ThrowingInfo.NO_THROW);
+ }
+
+ public void addMul(NumericType type, int dest, int left, int right) {
+ Value in1 = readNumericRegister(left, type);
+ Value in2 = readNumericRegister(right, type);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Mul instruction = new Mul(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addMulLiteral(NumericType type, int dest, int value, int constant) {
+ assert isNonLongIntegerType(type);
+ Value in1 = readNumericRegister(value, type);
+ Value in2 = readLiteral(type, constant);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Mul instruction = new Mul(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addRem(NumericType type, int dest, int left, int right) {
+ boolean canThrow = type != NumericType.DOUBLE && type != NumericType.FLOAT;
+ Value in1 = readNumericRegister(left, type);
+ Value in2 = readNumericRegister(right, type);
+ Value out = writeNumericRegister(dest, type, canThrow ? ThrowingInfo.CAN_THROW : ThrowingInfo.NO_THROW);
+ Rem instruction = new Rem(type, out, in1, in2);
+ assert instruction.instructionTypeCanThrow() == canThrow;
+ addInstruction(instruction);
+ }
+
+ public void addRemLiteral(NumericType type, int dest, int value, int constant) {
+ assert isNonLongIntegerType(type);
+ boolean canThrow = type != NumericType.DOUBLE && type != NumericType.FLOAT;
+ Value in1 = readNumericRegister(value, type);
+ Value in2 = readLiteral(type, constant);
+ Value out = writeNumericRegister(dest, type, canThrow ? ThrowingInfo.CAN_THROW : ThrowingInfo.NO_THROW);
+ Rem instruction = new Rem(type, out, in1, in2);
+ assert instruction.instructionTypeCanThrow() == canThrow;
+ addInstruction(instruction);
+ }
+
+ public void addGoto(int targetOffset) {
+ addInstruction(new Goto());
+ BasicBlock targetBlock = getTarget(targetOffset);
+ if (currentBlock.isCatchSuccessor(targetBlock)) {
+ needGotoToCatchBlocks.add(new BasicBlock.Pair(currentBlock, targetBlock));
+ } else {
+ currentBlock.link(targetBlock);
+ }
+ addToWorklist(targetBlock, source.instructionIndex(targetOffset));
+ closeCurrentBlock();
+ }
+
+ private void addTrivialIf(int trueTargetOffset, int falseTargetOffset) {
+ assert trueTargetOffset == falseTargetOffset;
+ // Conditional instructions with the same true and false targets are noops. They will
+ // always go to the next instruction. We end this basic block with a goto instead of
+ // a conditional.
+ BasicBlock target = getTarget(trueTargetOffset);
+ // We expected an if here and therefore we incremented the expected predecessor count
+ // twice for the following block.
+ target.decrementUnfilledPredecessorCount();
+ addInstruction(new Goto());
+ currentBlock.link(target);
+ addToWorklist(target, source.instructionIndex(trueTargetOffset));
+ closeCurrentBlock();
+ }
+
+ private void addNonTrivialIf(If instruction, int trueTargetOffset, int falseTargetOffset) {
+ addInstruction(instruction);
+ BasicBlock trueTarget = getTarget(trueTargetOffset);
+ BasicBlock falseTarget = getTarget(falseTargetOffset);
+ currentBlock.link(trueTarget);
+ currentBlock.link(falseTarget);
+ // Generate fall-through before the block that is branched to.
+ addToWorklist(falseTarget, source.instructionIndex(falseTargetOffset));
+ addToWorklist(trueTarget, source.instructionIndex(trueTargetOffset));
+ closeCurrentBlock();
+ }
+
+ public void addIf(If.Type type, int value1, int value2,
+ int trueTargetOffset, int falseTargetOffset) {
+ if (trueTargetOffset == falseTargetOffset) {
+ addTrivialIf(trueTargetOffset, falseTargetOffset);
+ } else {
+ List<Value> values = new ArrayList<>(2);
+ values.add(readRegister(value1, MoveType.SINGLE));
+ values.add(readRegister(value2, MoveType.SINGLE));
+ If instruction = new If(type, values);
+ addNonTrivialIf(instruction, trueTargetOffset, falseTargetOffset);
+ }
+ }
+
+ public void addIfZero(If.Type type, int value, int trueTargetOffset, int falseTargetOffset) {
+ if (trueTargetOffset == falseTargetOffset) {
+ addTrivialIf(trueTargetOffset, falseTargetOffset);
+ } else {
+ If instruction = new If(type, readRegister(value, MoveType.SINGLE));
+ addNonTrivialIf(instruction, trueTargetOffset, falseTargetOffset);
+ }
+ }
+
+ public void addInstanceGet(
+ MemberType type,
+ int dest,
+ int object,
+ DexField field) {
+ Value in = readRegister(object, MoveType.OBJECT);
+ Value out = writeRegister(dest, MoveType.fromMemberType(type), ThrowingInfo.CAN_THROW);
+ InstanceGet instruction = new InstanceGet(type, out, in, field);
+ assert instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addInstanceOf(int dest, int value, DexType type) {
+ Value in = readRegister(value, MoveType.OBJECT);
+ Value out = writeRegister(dest, MoveType.SINGLE, ThrowingInfo.CAN_THROW);
+ InstanceOf instruction = new InstanceOf(out, in, type);
+ assert instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addInstancePut(
+ MemberType type,
+ int value,
+ int object,
+ DexField field) {
+ List<Value> values = new ArrayList<>(2);
+ values.add(readRegister(value, MoveType.fromMemberType(type)));
+ values.add(readRegister(object, MoveType.OBJECT));
+ InstancePut instruction = new InstancePut(type, values, field);
+ add(instruction);
+ }
+
+ public void addInvoke(
+ Type type, DexItem item, DexProto callSiteProto, List<Value> arguments) {
+ if (type == Invoke.Type.POLYMORPHIC && !options.canUseInvokePolymorphic()) {
+ throw new CompilationError(
+ "MethodHandle.invoke and MethodHandle.invokeExact is unsupported before "
+ + "Android O (--min-sdk-version " + Constants.ANDROID_O_API + ")");
+ }
+ add(Invoke.create(type, item, callSiteProto, null, arguments));
+ }
+
+ public void addInvoke(
+ Invoke.Type type,
+ DexItem item,
+ DexProto callSiteProto,
+ List<MoveType> types,
+ List<Integer> registers) {
+ assert types.size() == registers.size();
+ List<Value> arguments = new ArrayList<>(types.size());
+ for (int i = 0; i < types.size(); i++) {
+ arguments.add(readRegister(registers.get(i), types.get(i)));
+ }
+ addInvoke(type, item, callSiteProto, arguments);
+ }
+
+ public void addInvokeCustomRegisters(
+ DexCallSite callSite, int argumentRegisterCount, int[] argumentRegisters) {
+ int registerIndex = 0;
+ DexMethodHandle bootstrapMethod = callSite.bootstrapMethod;
+ List<Value> arguments = new ArrayList<>(argumentRegisterCount);
+
+ if (!bootstrapMethod.isStaticHandle()) {
+ arguments.add(readRegister(argumentRegisters[registerIndex], MoveType.OBJECT));
+ registerIndex += MoveType.OBJECT.requiredRegisters();
+ }
+
+ String shorty = callSite.methodProto.shorty.toString();
+
+ for (int i = 1; i < shorty.length(); i++) {
+ MoveType moveType = MoveType.fromTypeDescriptorChar(shorty.charAt(i));
+ arguments.add(readRegister(argumentRegisters[registerIndex], moveType));
+ registerIndex += moveType.requiredRegisters();
+ }
+
+ add(new InvokeCustom(callSite, null, arguments));
+ }
+
+ public void addInvokeCustomRange(
+ DexCallSite callSite, int argumentCount, int firstArgumentRegister) {
+ DexMethodHandle bootstrapMethod = callSite.bootstrapMethod;
+ List<Value> arguments = new ArrayList<>(argumentCount);
+
+ int register = firstArgumentRegister;
+ if (!bootstrapMethod.isStaticHandle()) {
+ arguments.add(readRegister(register, MoveType.OBJECT));
+ register += MoveType.OBJECT.requiredRegisters();
+ }
+
+ String shorty = callSite.methodProto.shorty.toString();
+
+ for (int i = 1; i < shorty.length(); i++) {
+ MoveType moveType = MoveType.fromTypeDescriptorChar(shorty.charAt(i));
+ arguments.add(readRegister(register, moveType));
+ register += moveType.requiredRegisters();
+ }
+ checkInvokeArgumentRegisters(register, firstArgumentRegister + argumentCount);
+ add(new InvokeCustom(callSite, null, arguments));
+ }
+
+ public void addInvokeCustom(
+ DexCallSite callSite, List<MoveType> types, List<Integer> registers) {
+ assert types.size() == registers.size();
+ List<Value> arguments = new ArrayList<>(types.size());
+ for (int i = 0; i < types.size(); i++) {
+ arguments.add(readRegister(registers.get(i), types.get(i)));
+ }
+ add(new InvokeCustom(callSite, null, arguments));
+ }
+
+ public void addInvokeRegisters(
+ Invoke.Type type,
+ DexMethod method,
+ DexProto callSiteProto,
+ int argumentRegisterCount,
+ int[] argumentRegisters) {
+ // The value of argumentRegisterCount is the number of registers - not the number of values,
+ // but it is an upper bound on the number of arguments.
+ List<Value> arguments = new ArrayList<>(argumentRegisterCount);
+ int registerIndex = 0;
+ if (type != Invoke.Type.STATIC) {
+ arguments.add(readRegister(argumentRegisters[registerIndex], MoveType.OBJECT));
+ registerIndex += MoveType.OBJECT.requiredRegisters();
+ }
+ DexString methodShorty;
+ if (type == Invoke.Type.POLYMORPHIC) {
+ // The call site signature for invoke polymorphic must be take from call site and not from
+ // the called method.
+ methodShorty = callSiteProto.shorty;
+ } else {
+ methodShorty = method.proto.shorty;
+ }
+ String shorty = methodShorty.toString();
+ for (int i = 1; i < methodShorty.size; i++) {
+ MoveType moveType = MoveType.fromTypeDescriptorChar(shorty.charAt(i));
+ arguments.add(readRegister(argumentRegisters[registerIndex], moveType));
+ registerIndex += moveType.requiredRegisters();
+ }
+ checkInvokeArgumentRegisters(registerIndex, argumentRegisterCount);
+ addInvoke(type, method, callSiteProto, arguments);
+ }
+
+ public void addInvokeNewArray(DexType type, int argumentCount, int[] argumentRegisters) {
+ String descriptor = type.descriptor.toString();
+ assert descriptor.charAt(0) == '[';
+ assert descriptor.length() >= 2;
+ MoveType moveType = MoveType.fromTypeDescriptorChar(descriptor.charAt(1));
+ List<Value> arguments = new ArrayList<>(argumentCount / moveType.requiredRegisters());
+ int registerIndex = 0;
+ while (registerIndex < argumentCount) {
+ arguments.add(readRegister(argumentRegisters[registerIndex], moveType));
+ if (moveType == MoveType.WIDE) {
+ assert registerIndex < argumentCount - 1;
+ assert argumentRegisters[registerIndex] == argumentRegisters[registerIndex + 1] + 1;
+ }
+ registerIndex += moveType.requiredRegisters();
+ }
+ checkInvokeArgumentRegisters(registerIndex, argumentCount);
+ addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments);
+ }
+
+ public void addInvokeRange(
+ Invoke.Type type,
+ DexMethod method,
+ DexProto callSiteProto,
+ int argumentCount,
+ int firstArgumentRegister) {
+ // The value of argumentCount is the number of registers - not the number of values, but it
+ // is an upper bound on the number of arguments.
+ List<Value> arguments = new ArrayList<>(argumentCount);
+ int register = firstArgumentRegister;
+ if (type != Invoke.Type.STATIC) {
+ arguments.add(readRegister(register, MoveType.OBJECT));
+ register += MoveType.OBJECT.requiredRegisters();
+ }
+ DexString methodShorty;
+ if (type == Invoke.Type.POLYMORPHIC) {
+ // The call site signature for invoke polymorphic must be take from call site and not from
+ // the called method.
+ methodShorty = callSiteProto.shorty;
+ } else {
+ methodShorty = method.proto.shorty;
+ }
+ String shorty = methodShorty.toString();
+ for (int i = 1; i < methodShorty.size; i++) {
+ MoveType moveType = MoveType.fromTypeDescriptorChar(shorty.charAt(i));
+ arguments.add(readRegister(register, moveType));
+ register += moveType.requiredRegisters();
+ }
+ checkInvokeArgumentRegisters(register, firstArgumentRegister + argumentCount);
+ addInvoke(type, method, callSiteProto, arguments);
+ }
+
+ public void addInvokeRangeNewArray(DexType type, int argumentCount, int firstArgumentRegister) {
+ String descriptor = type.descriptor.toString();
+ assert descriptor.charAt(0) == '[';
+ assert descriptor.length() >= 2;
+ MoveType moveType = MoveType.fromTypeDescriptorChar(descriptor.charAt(1));
+ List<Value> arguments = new ArrayList<>(argumentCount / moveType.requiredRegisters());
+ int register = firstArgumentRegister;
+ while (register < firstArgumentRegister + argumentCount) {
+ arguments.add(readRegister(register, moveType));
+ register += moveType.requiredRegisters();
+ }
+ checkInvokeArgumentRegisters(register, firstArgumentRegister + argumentCount);
+ addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments);
+ }
+
+ private void checkInvokeArgumentRegisters(int expected, int actual) {
+ if (expected != actual) {
+ throw new CompilationError("Invalid invoke instruction. "
+ + "Expected use of " + expected + " argument registers, "
+ + "found actual use of " + actual);
+ }
+ }
+
+ public void addMoveException(int dest) {
+ Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.NO_THROW);
+ MoveException instruction = new MoveException(out);
+ assert !instruction.instructionTypeCanThrow();
+ assert currentBlock.getInstructions().isEmpty();
+ addInstruction(instruction);
+ }
+
+ public void addMoveResult(MoveType type, int dest) {
+ List<Instruction> instructions = currentBlock.getInstructions();
+ Invoke invoke = instructions.get(instructions.size() - 1).asInvoke();
+ assert invoke.outValue() == null;
+ assert invoke.instructionTypeCanThrow();
+ invoke.setOutValue(writeRegister(dest, type, ThrowingInfo.CAN_THROW));
+ }
+
+ public void addNeg(NumericType type, int dest, int value) {
+ Value in = readNumericRegister(value, type);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Neg instruction = new Neg(type, out, in);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addNot(NumericType type, int dest, int value) {
+ Value in = readNumericRegister(value, type);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Not instruction = new Not(type, out, in);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addNewArrayEmpty(int dest, int size, DexType type) {
+ assert type.isArrayType();
+ Value in = readRegister(size, MoveType.SINGLE);
+ Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
+ NewArrayEmpty instruction = new NewArrayEmpty(out, in, type);
+ assert instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addNewArrayFilledData(int arrayRef, int elementWidth, long size, short[] data) {
+ add(new NewArrayFilledData(readRegister(arrayRef, MoveType.OBJECT), elementWidth, size, data));
+ }
+
+ public void addNewInstance(int dest, DexType type) {
+ Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
+ NewInstance instruction = new NewInstance(type, out);
+ assert instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addReturn(MoveType type, int value) {
+ Value in = readRegister(value, type);
+ addInstruction(new Return(in, type));
+ exitBlocks.add(currentBlock);
+ closeCurrentBlock();
+ }
+
+ public void addReturn() {
+ addInstruction(new Return());
+ exitBlocks.add(currentBlock);
+ closeCurrentBlock();
+ }
+
+ public void addStaticGet(MemberType type, int dest, DexField field) {
+ Value out = writeRegister(dest, MoveType.fromMemberType(type), ThrowingInfo.CAN_THROW);
+ StaticGet instruction = new StaticGet(type, out, field);
+ assert instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addStaticPut(MemberType type, int value, DexField field) {
+ Value in = readRegister(value, MoveType.fromMemberType(type));
+ add(new StaticPut(type, in, field));
+ }
+
+ public void addSub(NumericType type, int dest, int left, int right) {
+ Value in1 = readNumericRegister(left, type);
+ Value in2 = readNumericRegister(right, type);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Sub instruction = new Sub(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addRsubLiteral(NumericType type, int dest, int value, int constant) {
+ assert type != NumericType.DOUBLE;
+ Value in1 = readNumericRegister(value, type);
+ Value in2 = readLiteral(type, constant);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ // Add this as a sub instruction - sub instructions with literals need to have the constant
+ // on the left side (rsub).
+ Sub instruction = new Sub(type, out, in2, in1);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ private void addSwitchIf(int key, int value, int caseOffset, int fallthroughOffset) {
+ if (key == 0) {
+ addIfZero(If.Type.EQ, value, caseOffset, fallthroughOffset);
+ } else {
+ if (caseOffset == fallthroughOffset) {
+ addTrivialIf(caseOffset, fallthroughOffset);
+ } else {
+ List<Value> values = new ArrayList<>(2);
+ values.add(readRegister(value, MoveType.SINGLE));
+ values.add(readLiteral(NumericType.INT, key));
+ If instruction = new If(If.Type.EQ, values);
+ addNonTrivialIf(instruction, caseOffset, fallthroughOffset);
+ }
+ }
+ }
+
+ public void addSwitch(Switch.Type type, int value, int[] keys, int fallthroughOffset,
+ int[] labelOffsets) {
+ int numberOfTargets = labelOffsets.length;
+ assert (type == Switch.Type.PACKED && keys.length == 1) || (keys.length == numberOfTargets);
+
+ // If the switch has no targets simply add a goto to the fallthrough.
+ if (numberOfTargets == 0) {
+ addGoto(fallthroughOffset);
+ return;
+ }
+
+ Value switchValue = readRegister(value, MoveType.SINGLE);
+
+ // Change a switch with just one case to an if.
+ if (numberOfTargets == 1) {
+ addSwitchIf(keys[0], value, labelOffsets[0], fallthroughOffset);
+ return;
+ }
+
+ // javac generates sparse switch (lookupwitch bytecode) for all two-case switch. Change
+ // to packed switch for consecutive keys.
+ if (numberOfTargets == 2 && type == Switch.Type.SPARSE && keys[0] + 1 == keys[1]) {
+ addInstruction(createSwitch(
+ Switch.Type.PACKED, switchValue, new int[]{keys[0]}, fallthroughOffset, labelOffsets));
+ closeCurrentBlock();
+ return;
+ }
+
+ // javac generates packed switches pretty aggressively. We convert to sparse switches
+ // if the dex sparse switch payload takes up less space than the packed payload.
+ if (type == Switch.Type.PACKED) {
+ int numberOfFallthroughs = 0;
+ for (int i = 0; i < numberOfTargets; i++) {
+ if (labelOffsets[i] == fallthroughOffset) {
+ ++numberOfFallthroughs;
+ }
+ }
+ // If this was a packed switch with only fallthrough cases we can make it a goto.
+ // Oddly, this does happen.
+ if (numberOfFallthroughs == numberOfTargets) {
+ targets.get(fallthroughOffset).decrementUnfilledPredecessorCount(numberOfFallthroughs);
+ addGoto(fallthroughOffset);
+ return;
+ }
+ int numberOfSparseTargets = numberOfTargets - numberOfFallthroughs;
+ int sparseSwitchPayloadSize = SparseSwitchPayload.getSizeForTargets(numberOfSparseTargets);
+ int packedSwitchPayloadSize = PackedSwitchPayload.getSizeForTargets(numberOfTargets);
+ int bytesSaved = packedSwitchPayloadSize - sparseSwitchPayloadSize;
+ // Perform the rewrite if we can reduce the payload size by more than 20%.
+ if (bytesSaved > (packedSwitchPayloadSize / 5)) {
+ targets.get(fallthroughOffset).decrementUnfilledPredecessorCount(numberOfFallthroughs);
+ int nextCaseIndex = 0;
+ int currentKey = keys[0];
+ keys = new int[numberOfSparseTargets];
+ int[] newLabelOffsets = new int[numberOfSparseTargets];
+ for (int i = 0; i < numberOfTargets; i++) {
+ if (labelOffsets[i] != fallthroughOffset) {
+ keys[nextCaseIndex] = currentKey;
+ newLabelOffsets[nextCaseIndex] = labelOffsets[i];
+ ++nextCaseIndex;
+ }
+ ++currentKey;
+ }
+ addInstruction(createSwitch(
+ Switch.Type.SPARSE, switchValue, keys, fallthroughOffset, newLabelOffsets));
+ closeCurrentBlock();
+ return;
+ }
+ }
+
+ addInstruction(createSwitch(type, switchValue, keys, fallthroughOffset, labelOffsets));
+ closeCurrentBlock();
+ }
+
+ private Switch createSwitch(Switch.Type type, Value value, int[] keys, int fallthroughOffset,
+ int[] targetOffsets) {
+ assert keys.length > 0;
+ // Compute target blocks for all keys. Only add a successor block once even
+ // if it is hit by more of the keys.
+ int[] targetBlockIndices = new int[targetOffsets.length];
+ Map<Integer, Integer> offsetToBlockIndex = new HashMap<>();
+ // Start with fall-through block.
+ BasicBlock fallthroughBlock = getTarget(fallthroughOffset);
+ currentBlock.link(fallthroughBlock);
+ addToWorklist(fallthroughBlock, source.instructionIndex(fallthroughOffset));
+ int fallthroughBlockIndex = currentBlock.getSuccessors().size() - 1;
+ offsetToBlockIndex.put(fallthroughOffset, fallthroughBlockIndex);
+ // Then all the switch target blocks.
+ for (int i = 0; i < targetOffsets.length; i++) {
+ int targetOffset = targetOffsets[i];
+ BasicBlock targetBlock = getTarget(targetOffset);
+ Integer targetBlockIndex = offsetToBlockIndex.get(targetOffset);
+ if (targetBlockIndex == null) {
+ // Target block not added as successor. Add it now.
+ currentBlock.link(targetBlock);
+ addToWorklist(targetBlock, source.instructionIndex(targetOffset));
+ int successorIndex = currentBlock.getSuccessors().size() - 1;
+ offsetToBlockIndex.put(targetOffset, successorIndex);
+ targetBlockIndices[i] = successorIndex;
+ } else {
+ // Target block already added as successor. The target block therefore
+ // has one less predecessor than precomputed.
+ targetBlock.decrementUnfilledPredecessorCount();
+ targetBlockIndices[i] = targetBlockIndex;
+ }
+ }
+ return new Switch(type, value, keys, targetBlockIndices, fallthroughBlockIndex);
+ }
+
+ public void addThrow(int value) {
+ Value in = readRegister(value, MoveType.OBJECT);
+ addInstruction(new Throw(in));
+ closeCurrentBlock();
+ }
+
+ public void addOr(NumericType type, int dest, int left, int right) {
+ assert isIntegerType(type);
+ Value in1 = readNumericRegister(left, type);
+ Value in2 = readNumericRegister(right, type);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Or instruction = new Or(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addOrLiteral(NumericType type, int dest, int value, int constant) {
+ assert isNonLongIntegerType(type);
+ Value in1 = readNumericRegister(value, type);
+ Value in2 = readLiteral(type, constant);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Or instruction = new Or(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addShl(NumericType type, int dest, int left, int right) {
+ assert isIntegerType(type);
+ Value in1 = readNumericRegister(left, type);
+ Value in2 = readRegister(right, MoveType.SINGLE);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Shl instruction = new Shl(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addShlLiteral(NumericType type, int dest, int value, int constant) {
+ assert isNonLongIntegerType(type);
+ Value in1 = readNumericRegister(value, type);
+ Value in2 = readLiteral(type, constant);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Shl instruction = new Shl(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addShr(NumericType type, int dest, int left, int right) {
+ assert isIntegerType(type);
+ Value in1 = readNumericRegister(left, type);
+ Value in2 = readRegister(right, MoveType.SINGLE);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Shr instruction = new Shr(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addShrLiteral(NumericType type, int dest, int value, int constant) {
+ assert isNonLongIntegerType(type);
+ Value in1 = readNumericRegister(value, type);
+ Value in2 = readLiteral(type, constant);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Shr instruction = new Shr(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addUshr(NumericType type, int dest, int left, int right) {
+ assert isIntegerType(type);
+ Value in1 = readNumericRegister(left, type);
+ Value in2 = readRegister(right, MoveType.SINGLE);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Ushr instruction = new Ushr(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addUshrLiteral(NumericType type, int dest, int value, int constant) {
+ assert isNonLongIntegerType(type);
+ Value in1 = readNumericRegister(value, type);
+ Value in2 = readLiteral(type, constant);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Ushr instruction = new Ushr(type, out, in1, in2);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addXor(NumericType type, int dest, int left, int right) {
+ assert isIntegerType(type);
+ Value in1 = readNumericRegister(left, type);
+ Value in2 = readNumericRegister(right, type);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ Instruction instruction;
+ if (in2.isConstant() && in2.getConstInstruction().asConstNumber().isIntegerNegativeOne(type)) {
+ instruction = new Not(type, out, in1);
+ } else {
+ instruction = new Xor(type, out, in1, in2);
+ }
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addXorLiteral(NumericType type, int dest, int value, int constant) {
+ assert isNonLongIntegerType(type);
+ Value in1 = readNumericRegister(value, type);
+ Instruction instruction;
+ if (constant == -1) {
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ instruction = new Not(type, out, in1);
+ } else {
+ Value in2 = readLiteral(type, constant);
+ Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+ instruction = new Xor(type, out, in1, in2);
+ }
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ public void addConversion(NumericType to, NumericType from, int dest, int source) {
+ Value in = readNumericRegister(source, from);
+ Value out = writeNumericRegister(dest, to, ThrowingInfo.NO_THROW);
+ NumberConversion instruction = new NumberConversion(from, to, out, in);
+ assert !instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
+ // Value abstraction methods.
+
+ public Value readRegister(int register, MoveType type) {
+ DebugLocalInfo local = getCurrentLocal(register);
+ Value value = readRegister(register, currentBlock, EdgeType.NON_EDGE, type, local);
+ // Check that any information about a current-local is consistent with the read.
+ assert local == null || value.getLocalInfo() == local || value.isUninitializedLocal();
+ // Check that any local information on the value is actually visible.
+ // If this assert triggers, the probable cause is that we end up reading an SSA value
+ // after it should have been ended on a fallthrough from a conditional jump or a trivial-phi
+ // removal resurrected the local.
+ assert value.getLocalInfo() == null
+ || value.getDebugLocalEnds() != null
+ || source.verifyLocalInScope(value.getLocalInfo());
+ return value;
+ }
+
+ public Value readRegisterIgnoreLocal(int register, MoveType type) {
+ DebugLocalInfo local = getCurrentLocal(register);
+ return readRegister(register, currentBlock, EdgeType.NON_EDGE, type, local);
+ }
+
+ public Value readRegister(int register, BasicBlock block, EdgeType readingEdge, MoveType type,
+ DebugLocalInfo local) {
+ checkRegister(register);
+ Value value = block.readCurrentDefinition(register, readingEdge);
+ return value != null ? value : readRegisterRecursive(register, block, readingEdge, type, local);
+ }
+
+ private Value readRegisterRecursive(
+ int register, BasicBlock block, EdgeType readingEdge, MoveType type, DebugLocalInfo local) {
+ Value value;
+ if (!block.isSealed()) {
+ assert !blocks.isEmpty() : "No write to " + register;
+ Phi phi = new Phi(valueNumberGenerator.next(), register, block, type, local);
+ block.addIncompletePhi(register, phi, readingEdge);
+ value = phi;
+ } else if (block.getPredecessors().size() == 1) {
+ assert block.verifyFilledPredecessors();
+ BasicBlock pred = block.getPredecessors().get(0);
+ EdgeType edgeType = pred.getEdgeType(block);
+ value = readRegister(register, pred, edgeType, type, local);
+ } else {
+ Phi phi = new Phi(valueNumberGenerator.next(), register, block, type, local);
+ // We need to write the phi before adding operands to break cycles. If the phi is trivial
+ // and is removed by addOperands, the definition is overwritten and looked up again below.
+ block.updateCurrentDefinition(register, phi, readingEdge);
+ phi.addOperands(this);
+ // Lookup the value for the register again at this point. Recursive trivial
+ // phi removal could have simplified what we wanted to return here.
+ value = block.readCurrentDefinition(register, readingEdge);
+ }
+ block.updateCurrentDefinition(register, value, readingEdge);
+ return value;
+ }
+
+ public Value readNumericRegister(int register, NumericType type) {
+ return readRegister(register, type.moveTypeFor());
+ }
+
+ public Value readLiteral(NumericType type, long constant) {
+ Value value = new Value(valueNumberGenerator.next(), -1, MoveType.fromNumericType(type), null);
+ add(new ConstNumber(ConstType.fromNumericType(type), value, constant));
+ return value;
+ }
+
+ // This special write register is needed when changing the scoping of a local variable.
+ // See addDebugLocalStart and addDebugLocalEnd.
+ private Value writeRegister(int register, MoveType type, ThrowingInfo throwing, DebugInfo info) {
+ checkRegister(register);
+ Value value = new Value(valueNumberGenerator.next(), register, type, info);
+ currentBlock.writeCurrentDefinition(register, value, throwing);
+ return value;
+ }
+
+ public Value writeRegister(int register, MoveType type, ThrowingInfo throwing) {
+ DebugLocalInfo local = getCurrentLocal(register);
+ DebugInfo info = null;
+ if (local != null) {
+ Value previousLocal = readRegisterIgnoreLocal(register, type);
+ info = new DebugInfo(local, previousLocal.getLocalInfo() != local ? null : previousLocal);
+ }
+ return writeRegister(register, type, throwing, info);
+ }
+
+ public Value writeNumericRegister(int register, NumericType type, ThrowingInfo throwing) {
+ return writeRegister(register, type.moveTypeFor(), throwing);
+ }
+
+ private DebugLocalInfo getCurrentLocal(int register) {
+ return options.debug ? source.getCurrentLocal(register) : null;
+ }
+
+ private void checkRegister(int register) {
+ if (register < 0) {
+ throw new InternalCompilerError("Invalid register");
+ }
+ if (!source.verifyRegister(register)) {
+ throw new CompilationError("Invalid use of register " + register);
+ }
+ }
+
+ /**
+ * Ensure that the current block can hold a throwing instruction. This will create a new current
+ * block if the current block has handlers and already has one throwing instruction.
+ */
+ void ensureBlockForThrowingInstruction() {
+ if (!throwingInstructionInCurrentBlock) {
+ return;
+ }
+ BasicBlock block = new BasicBlock();
+ blocks.add(block);
+ block.incrementUnfilledPredecessorCount();
+ for (Integer offset : source.getCurrentCatchHandlers().getUniqueTargets()) {
+ BasicBlock target = getTarget(offset);
+ assert !target.isSealed();
+ target.incrementUnfilledPredecessorCount();
+ }
+ addInstruction(new Goto());
+ currentBlock.link(block);
+ closeCurrentBlock();
+ setCurrentBlock(block);
+ }
+
+ // Private instruction helpers.
+ private void addInstruction(Instruction ir) {
+ if (currentDebugPosition != null && !ir.isMoveException()) {
+ flushCurrentDebugPosition();
+ }
+ currentBlock.add(ir);
+ if (ir.instructionTypeCanThrow()) {
+ assert source.verifyCurrentInstructionCanThrow();
+ CatchHandlers<Integer> catchHandlers = source.getCurrentCatchHandlers();
+ if (catchHandlers != null) {
+ assert !throwingInstructionInCurrentBlock;
+ throwingInstructionInCurrentBlock = true;
+ List<BasicBlock> targets = new ArrayList<>(catchHandlers.getAllTargets().size());
+ for (int targetOffset : catchHandlers.getAllTargets()) {
+ BasicBlock target = getTarget(targetOffset);
+ addToWorklist(target, source.instructionIndex(targetOffset));
+ targets.add(target);
+ }
+ currentBlock.linkCatchSuccessors(catchHandlers.getGuards(), targets);
+ }
+ }
+ if (currentDebugPosition != null) {
+ assert ir.isMoveException();
+ flushCurrentDebugPosition();
+ }
+ }
+
+ // Package (ie, SourceCode accessed) helpers.
+
+ // Ensure there is a block starting at offset.
+ BasicBlock ensureBlockWithoutEnqueuing(int offset) {
+ BasicBlock block = targets.get(offset);
+ if (block == null) {
+ block = new BasicBlock();
+ targets.put(offset, block);
+ // If this is a processed instruction, the block split and it has a fall-through predecessor.
+ if (offset >= 0 && isOffsetProcessed(offset)) {
+ block.incrementUnfilledPredecessorCount();
+ }
+ }
+ return block;
+ }
+
+ // Ensure there is a block starting at offset and add it to the work-list if it needs processing.
+ private BasicBlock ensureBlock(int offset) {
+ // We don't enqueue negative targets (these are special blocks, eg, an argument prelude).
+ if (offset >= 0 && !isOffsetProcessed(offset)) {
+ traceBlocksWorklist.add(offset);
+ }
+ return ensureBlockWithoutEnqueuing(offset);
+ }
+
+ private boolean isOffsetProcessed(int offset) {
+ return isIndexProcessed(source.instructionIndex(offset));
+ }
+
+ private boolean isIndexProcessed(int index) {
+ if (index < processedInstructions.length) {
+ return processedInstructions[index];
+ }
+ ensureSubroutineProcessedInstructions();
+ return processedSubroutineInstructions.contains(index);
+ }
+
+ private void markIndexProcessed(int index) {
+ assert !isIndexProcessed(index);
+ if (index < processedInstructions.length) {
+ processedInstructions[index] = true;
+ return;
+ }
+ ensureSubroutineProcessedInstructions();
+ processedSubroutineInstructions.add(index);
+ }
+
+ private void ensureSubroutineProcessedInstructions() {
+ if (processedSubroutineInstructions == null) {
+ processedSubroutineInstructions = new HashSet<>();
+ }
+ }
+
+ // Ensure there is a block at offset and add a predecessor to it.
+ BasicBlock ensureSuccessorBlock(int offset) {
+ BasicBlock block = ensureBlock(offset);
+ block.incrementUnfilledPredecessorCount();
+ return block;
+ }
+
+ // Private block helpers.
+
+ private BasicBlock getTarget(int offset) {
+ return targets.get(offset);
+ }
+
+ private void closeCurrentBlock() {
+ // TODO(zerny): To ensure liveness of locals throughout the entire block, we might want to
+ // insert reads before closing the block. It is unclear if we can rely on a local-end to ensure
+ // liveness in all blocks where the local should be live.
+ assert currentBlock != null;
+ assert currentDebugPosition == null;
+ currentBlock.close(this);
+ setCurrentBlock(null);
+ throwingInstructionInCurrentBlock = false;
+ }
+
+ private void closeCurrentBlockWithFallThrough(BasicBlock nextBlock) {
+ assert currentBlock != null;
+ flushCurrentDebugPosition();
+ currentBlock.add(new Goto());
+ if (currentBlock.isCatchSuccessor(nextBlock)) {
+ needGotoToCatchBlocks.add(new BasicBlock.Pair(currentBlock, nextBlock));
+ } else {
+ currentBlock.link(nextBlock);
+ }
+ closeCurrentBlock();
+ }
+
+ int handleExitBlock(int blockNumber) {
+ if (exitBlocks.size() > 0) {
+ // Create and populate the exit block if needed (eg, synchronized support for jar).
+ setCurrentBlock(new BasicBlock());
+ source.buildPostlude(this);
+ // If the new exit block is empty and we only have one exit, abort building a new exit block.
+ if (currentBlock.getInstructions().isEmpty() && exitBlocks.size() == 1) {
+ normalExitBlock = exitBlocks.get(0);
+ setCurrentBlock(null);
+ return blockNumber;
+ }
+ // Commit to creating the new exit block.
+ normalExitBlock = currentBlock;
+ normalExitBlock.setNumber(blockNumber++);
+ blocks.add(normalExitBlock);
+ // Add the return instruction possibly creating a phi of return values.
+ Return origReturn = exitBlocks.get(0).exit().asReturn();
+ Phi phi = null;
+ if (origReturn.isReturnVoid()) {
+ normalExitBlock.add(new Return());
+ } else {
+ Value returnValue = origReturn.returnValue();
+ MoveType returnType = origReturn.getReturnType();
+ assert origReturn.getLocalInfo() == null;
+ phi = new Phi(
+ valueNumberGenerator.next(), -1, normalExitBlock, returnValue.outType(), null);
+ normalExitBlock.add(new Return(phi, returnType));
+ assert returnType == MoveType.fromDexType(method.method.proto.returnType);
+ }
+ closeCurrentBlock();
+ // Replace each return instruction with a goto to the new exit block.
+ List<Value> operands = new ArrayList<>();
+ for (BasicBlock block : exitBlocks) {
+ List<Instruction> instructions = block.getInstructions();
+ Return ret = block.exit().asReturn();
+ if (!ret.isReturnVoid()) {
+ operands.add(ret.returnValue());
+ ret.returnValue().removeUser(ret);
+ }
+ Goto gotoExit = new Goto();
+ gotoExit.setBlock(block);
+ instructions.set(instructions.size() - 1, gotoExit);
+ block.link(normalExitBlock);
+ gotoExit.setTarget(normalExitBlock);
+ }
+ if (phi != null) {
+ phi.addOperands(operands);
+ }
+ }
+ return blockNumber;
+ }
+
+ private int handleFallthroughToCatchBlock(int blockNumber) {
+ // When a catch handler for a block goes to the same block as the fallthrough for that
+ // block the graph only has one edge there. In these cases we add an additional block so the
+ // catch edge goes through that and then make the fallthrough go through a new direct edge.
+ for (BasicBlock.Pair pair : needGotoToCatchBlocks) {
+ BasicBlock source = pair.first;
+ BasicBlock target = pair.second;
+
+ // New block with one unfilled predecessor.
+ BasicBlock newBlock = BasicBlock.createGotoBlock(target, blockNumber++);
+ blocks.add(newBlock);
+ newBlock.incrementUnfilledPredecessorCount();
+
+ // Link blocks.
+ source.replaceSuccessor(target, newBlock);
+ newBlock.getPredecessors().add(source);
+ source.getSuccessors().add(target);
+ target.getPredecessors().add(newBlock);
+
+ // Check that the successor indexes are correct.
+ assert source.isCatchSuccessor(newBlock);
+ assert !source.isCatchSuccessor(target);
+
+ // Mark the filled predecessors to the blocks.
+ if (source.isFilled()) {
+ newBlock.filledPredecessor(this);
+ }
+ target.filledPredecessor(this);
+ }
+ return blockNumber;
+ }
+
+ private boolean phiOperandsAreConsistent() {
+ for (BasicBlock block : blocks) {
+ if (block.hasIncompletePhis()) {
+ StringBuilder builder = new StringBuilder("Incomplete phis in ");
+ builder.append(method);
+ builder.append(". The following registers appear to be uninitialized: ");
+ StringUtils.append(builder, block.getIncompletePhiRegisters(), ", ", BraceType.NONE);
+ throw new CompilationError(builder.toString());
+ }
+ for (Phi phi : block.getPhis()) {
+ assert phi.getOperands().size() == block.getPredecessors().size();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Change to control-flow graph to avoid repeated phi operands when all the same values
+ * flow in from multiple predecessors.
+ *
+ * <p> As an example:
+ *
+ * <pre>
+ *
+ * b1 b2 b3
+ * | |
+ * ----------\ | /----------
+ *
+ * b4
+ * v3 = phi(v1, v1, v2)
+ * </pre>
+ *
+ * <p> Is rewritten to:
+ *
+ * <pre>
+ * b1 b2 b3
+ * \ / /
+ * b5 -------
+ * \ /
+ * b4
+ * v3 = phi(v1, v2)
+ *
+ * </pre>
+ */
+ public void joinPredecessorsWithIdenticalPhis() {
+ List<BasicBlock> blocksToAdd = new ArrayList<>();
+ for (BasicBlock block : blocks) {
+ if (block.entry() instanceof MoveException) {
+ // TODO: Should we support joining in the presence of move-exception instructions?
+ continue;
+ }
+ List<Integer> operandsToRemove = new ArrayList<>();
+ Map<ValueList, Integer> values = new HashMap<>();
+ Map<Integer, BasicBlock> joinBlocks = new HashMap<>();
+ if (block.getPhis().size() > 0) {
+ Phi phi = block.getPhis().get(0);
+ for (int operandIndex = 0; operandIndex < phi.getOperands().size(); operandIndex++) {
+ ValueList v = ValueList.fromPhis(block.getPhis(), operandIndex);
+ BasicBlock predecessor = block.getPredecessors().get(operandIndex);
+ if (values.containsKey(v)) {
+ // Seen before, create a join block (or reuse an existing join block) to join through.
+ int otherPredecessorIndex = values.get(v);
+ BasicBlock joinBlock = joinBlocks.get(otherPredecessorIndex);
+ if (joinBlock == null) {
+ joinBlock = BasicBlock.createGotoBlock(block, blocks.size() + blocksToAdd.size());
+ joinBlocks.put(otherPredecessorIndex, joinBlock);
+ blocksToAdd.add(joinBlock);
+ BasicBlock otherPredecessor = block.getPredecessors().get(otherPredecessorIndex);
+ joinBlock.getPredecessors().add(otherPredecessor);
+ otherPredecessor.replaceSuccessor(block, joinBlock);
+ block.getPredecessors().set(otherPredecessorIndex, joinBlock);
+ }
+ joinBlock.getPredecessors().add(predecessor);
+ predecessor.replaceSuccessor(block, joinBlock);
+ operandsToRemove.add(operandIndex);
+ } else {
+ // Record the value and its predecessor index.
+ values.put(v, operandIndex);
+ }
+ }
+ }
+ block.removePredecessorsByIndex(operandsToRemove);
+ block.removePhisByIndex(operandsToRemove);
+ }
+ blocks.addAll(blocksToAdd);
+ }
+
+ private void splitCriticalEdges() {
+ List<BasicBlock> newBlocks = new ArrayList<>();
+ for (BasicBlock block : blocks) {
+ // We are using a spilling register allocator that might need to insert moves at
+ // all critical edges, so we always split them all.
+ List<BasicBlock> predecessors = block.getPredecessors();
+ if (predecessors.size() <= 1) {
+ continue;
+ }
+ // If any of the edges to the block are critical, we need to insert new blocks on each
+ // containing the move-exception instruction which must remain the first instruction.
+ if (block.entry() instanceof MoveException) {
+ block.splitCriticalExceptioEdges(valueNumberGenerator,
+ newBlock -> {
+ newBlock.setNumber(blocks.size() + newBlocks.size());
+ newBlocks.add(newBlock);
+ });
+ continue;
+ }
+ for (int predIndex = 0; predIndex < predecessors.size(); predIndex++) {
+ BasicBlock pred = predecessors.get(predIndex);
+ if (!pred.hasOneNormalExit()) {
+ // Critical edge: split it and inject a new block into which the
+ // phi moves can be inserted. The new block is created with the
+ // correct predecessor and successor structure. It is inserted
+ // at the end of the list of blocks disregarding branching
+ // structure.
+ int blockNumber = blocks.size() + newBlocks.size();
+ BasicBlock newBlock = BasicBlock.createGotoBlock(block, blockNumber);
+ newBlocks.add(newBlock);
+ pred.replaceSuccessor(block, newBlock);
+ newBlock.getPredecessors().add(pred);
+ predecessors.set(predIndex, newBlock);
+ }
+ }
+ }
+ blocks.addAll(newBlocks);
+ }
+
+ /**
+ * Trace blocks and attempt to put fallthrough blocks immediately after the block that
+ * falls through. When we fail to do that we create a new fallthrough block with an explicit
+ * goto to the actual fallthrough block.
+ */
+ private void traceBlocks(IRCode code) {
+ BasicBlock[] sorted = code.topologicallySortedBlocks();
+ code.clearMarks();
+ int nextBlockNumber = blocks.size();
+ LinkedList<BasicBlock> tracedBlocks = new LinkedList<>();
+ for (BasicBlock block : sorted) {
+ if (!block.isMarked()) {
+ block.mark();
+ tracedBlocks.add(block);
+ BasicBlock current = block;
+ BasicBlock fallthrough = block.exit().fallthroughBlock();
+ while (fallthrough != null && !fallthrough.isMarked()) {
+ fallthrough.mark();
+ tracedBlocks.add(fallthrough);
+ current = fallthrough;
+ fallthrough = fallthrough.exit().fallthroughBlock();
+ }
+ if (fallthrough != null) {
+ BasicBlock newFallthrough = BasicBlock.createGotoBlock(fallthrough, nextBlockNumber++);
+ current.exit().setFallthroughBlock(newFallthrough);
+ newFallthrough.getPredecessors().add(current);
+ fallthrough.replacePredecessor(current, newFallthrough);
+ newFallthrough.mark();
+ tracedBlocks.add(newFallthrough);
+ }
+ }
+ }
+ code.blocks = tracedBlocks;
+ }
+
+ // Debug info helpers.
+
+ public void updateCurrentDebugPosition(int line, DexString file) {
+ // Stack-trace support requires position information in both debug and release mode.
+ flushCurrentDebugPosition();
+ currentDebugPosition = new DebugPosition(line, file);
+ }
+
+ private void flushCurrentDebugPosition() {
+ if (currentDebugPosition != null) {
+ DebugPosition position = currentDebugPosition;
+ currentDebugPosition = null;
+ addInstruction(position);
+ }
+ }
+
+ // Other stuff.
+
+ boolean isIntegerType(NumericType type) {
+ return type != NumericType.FLOAT && type != NumericType.DOUBLE;
+ }
+
+ boolean isNonLongIntegerType(NumericType type) {
+ return type != NumericType.FLOAT && type != NumericType.DOUBLE && type != NumericType.LONG;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(("blocks:\n"));
+ for (BasicBlock block : blocks) {
+ builder.append(block.toDetailedString());
+ builder.append("\n");
+ }
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
new file mode 100644
index 0000000..b01fad8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -0,0 +1,562 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.conversion;
+
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
+import com.android.tools.r8.ir.desugar.LambdaRewriter;
+import com.android.tools.r8.ir.optimize.CodeRewriter;
+import com.android.tools.r8.ir.optimize.DeadCodeRemover;
+import com.android.tools.r8.ir.optimize.Inliner;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import com.android.tools.r8.ir.optimize.MemberValuePropagation;
+import com.android.tools.r8.ir.optimize.Outliner;
+import com.android.tools.r8.ir.optimize.PeepholeOptimizer;
+import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.function.BiConsumer;
+
+public class IRConverter {
+
+ public static final int PEEPHOLE_OPTIMIZATION_PASSES = 2;
+
+ private final Timing timing;
+ public final DexApplication application;
+ public final AppInfo appInfo;
+ private final Outliner outliner;
+ private final LambdaRewriter lambdaRewriter;
+ private final InterfaceMethodRewriter interfaceMethodRewriter;
+ private final InternalOptions options;
+ private final CfgPrinter printer;
+ private final GraphLense graphLense;
+ private final CodeRewriter codeRewriter;
+ private final MemberValuePropagation memberValuePropagation;
+ private final LensCodeRewriter lensCodeRewriter;
+ private final Inliner inliner;
+ private CallGraph callGraph;
+
+ private DexString highestSortingString;
+
+ public IRConverter(
+ DexApplication application,
+ AppInfo appInfo,
+ InternalOptions options,
+ CfgPrinter printer,
+ boolean enableDesugaring) {
+ this.timing = new Timing("Testing");
+ this.application = application;
+ this.appInfo = appInfo;
+ this.options = options;
+ this.printer = printer;
+ this.graphLense = GraphLense.getIdentityLense();
+ this.inliner = null;
+ this.outliner = null;
+ this.codeRewriter = new CodeRewriter(appInfo);
+ this.memberValuePropagation = null;
+ this.lambdaRewriter = enableDesugaring ? new LambdaRewriter(this) : null;
+ this.interfaceMethodRewriter =
+ (enableDesugaring && enableInterfaceMethodDesugaring())
+ ? new InterfaceMethodRewriter(this) : null;
+ lensCodeRewriter = null;
+ markLibraryMethodsReturningReceiver();
+ }
+
+ public IRConverter(
+ DexApplication application, AppInfo appInfo, InternalOptions options, CfgPrinter printer) {
+ this(application, appInfo, options, printer, true);
+ }
+
+ public IRConverter(
+ Timing timing,
+ DexApplication application,
+ AppInfoWithSubtyping appInfo,
+ InternalOptions options,
+ CfgPrinter printer,
+ GraphLense graphLense) {
+ this.timing = timing;
+ this.application = application;
+ this.appInfo = appInfo;
+ this.options = options;
+ this.printer = printer;
+ this.graphLense = graphLense;
+ this.codeRewriter = new CodeRewriter(appInfo);
+ this.outliner = new Outliner(appInfo, options);
+ this.memberValuePropagation = new MemberValuePropagation(appInfo);
+ this.inliner = new Inliner(appInfo, options);
+ this.lambdaRewriter = new LambdaRewriter(this);
+ this.interfaceMethodRewriter = enableInterfaceMethodDesugaring()
+ ? new InterfaceMethodRewriter(this) : null;
+ lensCodeRewriter = new LensCodeRewriter(graphLense, appInfo);
+ markLibraryMethodsReturningReceiver();
+ }
+
+ private boolean enableInterfaceMethodDesugaring() {
+ switch (options.interfaceMethodDesugaring) {
+ case Off:
+ return false;
+ case Auto:
+ return !options.canUseDefaultAndStaticInterfaceMethods();
+ }
+ throw new Unreachable();
+ }
+
+ private void markLibraryMethodsReturningReceiver() {
+ DexItemFactory dexItemFactory = appInfo.dexItemFactory;
+ dexItemFactory.stringBuilderMethods.forEeachAppendMethod(this::markReturnsReceiver);
+ dexItemFactory.stringBufferMethods.forEeachAppendMethod(this::markReturnsReceiver);
+ }
+
+ private void markReturnsReceiver(DexMethod method) {
+ DexEncodedMethod definition = appInfo.definitionFor(method);
+ if (definition != null) {
+ definition.markReturnsArgument(0);
+ }
+ }
+
+ public DexApplication convertToDex() {
+ convertClassesToDex(application.classes());
+
+ // Build a new application with jumbo string info,
+ Builder builder = new Builder(application);
+ builder.setHighestSortingString(highestSortingString);
+
+ // Lambda rewriter
+ if (lambdaRewriter != null) {
+ lambdaRewriter.adjustAccessibility(builder);
+ lambdaRewriter.synthesizeLambdaClasses(builder);
+ }
+
+ if (interfaceMethodRewriter != null) {
+ interfaceMethodRewriter.desugarInterfaceMethods(builder, ExcludeDexResources);
+ }
+
+ return builder.build();
+ }
+
+ private void convertClassesToDex(Iterable<DexProgramClass> classes) {
+ for (DexProgramClass clazz : classes) {
+ convertMethodsToDex(clazz.directMethods());
+ convertMethodsToDex(clazz.virtualMethods());
+ }
+ }
+
+ private void convertMethodsToDex(DexEncodedMethod[] methods) {
+ for (int i = 0; i < methods.length; i++) {
+ DexEncodedMethod method = methods[i];
+ if (method.getCode() != null) {
+ boolean matchesMethodFilter = options.methodMatchesFilter(method);
+ if (matchesMethodFilter) {
+ if (method.getCode().isJarCode()) {
+ rewriteCode(method, Outliner::noProcessing);
+ }
+ updateHighestSortingStrings(method);
+ }
+ }
+ }
+ }
+
+ public DexApplication optimize() throws ExecutionException {
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ try {
+ return optimize(executor);
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ public DexApplication optimize(ExecutorService executorService) throws ExecutionException {
+ timing.begin("Build call graph");
+ callGraph = CallGraph.build(application, appInfo.withSubtyping(), graphLense);
+ timing.end();
+
+ // The process is in two phases.
+ // 1) Subject all DexEncodedMethods to optimization (except outlining).
+ // - a side effect is candidates for outlining are identified.
+ // 2) Perform outlining for the collected candidates.
+ // Ideally, we should outline eagerly when threshold for a template has been reached.
+
+ // Process the application identifying outlining candidates.
+ timing.begin("IR conversion phase 1");
+ while (!callGraph.isEmpty()) {
+ CallGraph.Leaves leaves = callGraph.pickLeaves();
+ List<DexEncodedMethod> leafMethods = leaves.getLeaves();
+ assert leafMethods.size() > 0;
+ // If cycles where broken to produce leaves, don't do parallel processing to keep
+ // deterministic output.
+ // TODO(37133161): Most likely the failing:
+ // java.com.android.tools.r8.internal.R8GMSCoreDeterministicTest
+ // is caused by processing in multiple threads.
+ if (true || leaves.brokeCycles()) {
+ for (DexEncodedMethod method : leafMethods) {
+ processMethod(method,
+ outliner == null ? Outliner::noProcessing : outliner::identifyCandidates);
+ }
+ } else {
+ List<Future<?>> futures = new ArrayList<>();
+ // For testing we have the option to randomize the processing order for the
+ // deterministic tests.
+ if (options.testing.randomizeCallGraphLeaves) {
+ Collections.shuffle(leafMethods, new Random(System.nanoTime()));
+ }
+ for (DexEncodedMethod method : leafMethods) {
+ futures.add(executorService.submit(() -> {
+ processMethod(method,
+ outliner == null ? Outliner::noProcessing : outliner::identifyCandidates);
+ }));
+ }
+ ThreadUtils.awaitFutures(futures);
+ }
+ }
+ timing.end();
+
+ // Build a new application with jumbo string info.
+ Builder builder = new Builder(application);
+ builder.setHighestSortingString(highestSortingString);
+
+ // Second inlining pass.
+ if ((inliner != null) && (inliner.doubleInlineCallers.size() > 0)) {
+ inliner.applyDoubleInlining = true;
+ for (DexEncodedMethod method : inliner.doubleInlineCallers) {
+ method.markNotProcessed();
+ processMethod(method, Outliner::noProcessing);
+ assert method.isProcessed();
+ }
+ }
+
+ // Lambda rewriter.
+ if (lambdaRewriter != null) {
+ lambdaRewriter.adjustAccessibility(builder);
+ lambdaRewriter.synthesizeLambdaClasses(builder);
+ }
+
+ if (interfaceMethodRewriter != null) {
+ interfaceMethodRewriter.desugarInterfaceMethods(builder, IncludeAllResources);
+ }
+
+ if (outliner != null) {
+ timing.begin("IR conversion phase 2");
+ // Compile all classes flagged for outlining and
+ // add the outline support class IF needed.
+ DexProgramClass outlineClass = prepareOutlining();
+ if (outlineClass != null) {
+ // Process the selected methods for outlining.
+ for (DexEncodedMethod method : outliner.getMethodsSelectedForOutlining()) {
+ // This is the second time we compile this method first mark it not processed.
+ assert !method.getCode().isOutlineCode();
+ method.markNotProcessed();
+ processMethod(method, outliner::applyOutliningCandidate);
+ assert method.isProcessed();
+ }
+ builder.addSynthesizedClass(outlineClass, true);
+ clearDexMethodCompilationState(outlineClass);
+ }
+ timing.end();
+ }
+ clearDexMethodCompilationState();
+ return builder.build();
+ }
+
+ public void processJumboStrings(DexEncodedMethod method, DexString firstJumboString) {
+ convertMethodJumboStringsOnly(method, firstJumboString);
+ }
+
+ private void clearDexMethodCompilationState() {
+ application.classes().forEach(this::clearDexMethodCompilationState);
+ }
+
+ private void clearDexMethodCompilationState(DexProgramClass clazz) {
+ Arrays.stream(clazz.directMethods()).forEach(DexEncodedMethod::markNotProcessed);
+ Arrays.stream(clazz.virtualMethods()).forEach(DexEncodedMethod::markNotProcessed);
+ }
+
+ /**
+ * This will replace the Dex code in the method with the Dex code generated from the provided IR.
+ *
+ * This method is *only* intended for testing, where tests manipulate the IR and need runnable Dex
+ * code.
+ *
+ * @param method the method to replace code for
+ * @param code the IR code for the method
+ */
+ public void replaceCodeForTesting(DexEncodedMethod method, IRCode code) {
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s", method.toSourceString(), code);
+ }
+ assert code.isConsistentSSA();
+ RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
+ method.setCode(code, registerAllocator);
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Resulting dex code for %s:\n%s",
+ method.toSourceString(), logCode(options, method));
+ }
+ }
+
+ // Find an unused name for the outlining class. When multiple runs produces additional
+ // outlining the default outlining class might already be present.
+ private DexType computeOutlineClassType() {
+ DexType result;
+ int count = 0;
+ do {
+ String name = options.outline.className + (count == 0 ? "" : Integer.toString(count));
+ count++;
+ result = application.dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(name));
+ } while (application.definitionFor(result) != null);
+ return result;
+ }
+
+ private DexProgramClass prepareOutlining() {
+ if (!outliner.selectMethodsForOutlining()) {
+ return null;
+ }
+ DexProgramClass outlineClass = outliner.buildOutlinerClass(computeOutlineClassType());
+ optimizeSynthesizedClass(outlineClass);
+ return outlineClass;
+ }
+
+ public void optimizeSynthesizedClass(DexProgramClass clazz) {
+ // Process the generated class, but don't apply any outlining.
+ for (DexEncodedMethod method : clazz.directMethods()) {
+ optimizeSynthesizedMethod(method);
+ }
+ for (DexEncodedMethod method : clazz.virtualMethods()) {
+ optimizeSynthesizedMethod(method);
+ }
+ }
+
+ public void optimizeSynthesizedMethod(DexEncodedMethod method) {
+ // Process the generated method, but don't apply any outlining.
+ processMethod(method, Outliner::noProcessing);
+ }
+
+ private String logCode(InternalOptions options, DexEncodedMethod method) {
+ return options.useSmaliSyntax ? method.toSmaliString(null) : method.codeToString();
+ }
+
+ private void processMethod(
+ DexEncodedMethod method, BiConsumer<IRCode, DexEncodedMethod> outlineHandler) {
+ Code code = method.getCode();
+ boolean matchesMethodFilter = options.methodMatchesFilter(method);
+ if (code != null && matchesMethodFilter) {
+ assert !method.isProcessed();
+ InliningConstraint state = rewriteCode(method, outlineHandler);
+ method.markProcessed(state);
+ } else {
+ // Mark abstract methods as processed as well.
+ method.markProcessed(InliningConstraint.NEVER);
+ }
+ }
+
+ private InliningConstraint rewriteCode(DexEncodedMethod method,
+ BiConsumer<IRCode, DexEncodedMethod> outlineHandler) {
+ if (options.verbose) {
+ System.out.println("Processing: " + method.toSourceString());
+ }
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Original code for %s:\n%s",
+ method.toSourceString(), logCode(options, method));
+ }
+ IRCode code = method.buildIR(options);
+ if (code == null) {
+ return InliningConstraint.NEVER;
+ }
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s", method.toSourceString(), code);
+ }
+ // Compilation header if printing CFGs for this method.
+ printC1VisualizerHeader(method);
+ printMethod(code, "Initial IR (SSA)");
+
+ if (lensCodeRewriter != null) {
+ lensCodeRewriter.rewrite(code, method);
+ } else {
+ assert graphLense.isIdentityLense();
+ }
+ if (memberValuePropagation != null) {
+ memberValuePropagation.rewriteWithConstantValues(code);
+ }
+ if (options.inlineAccessors && inliner != null) {
+ inliner.performInlining(method, code, callGraph);
+ }
+ codeRewriter.rewriteLongCompareAndRequireNonNull(code, options.canUseObjectsNonNull());
+ codeRewriter.commonSubexpressionElimination(code);
+ codeRewriter.simplifyArrayConstruction(code);
+ codeRewriter.rewriteMoveResult(code);
+ codeRewriter.splitConstants(code);
+ codeRewriter.foldConstants(code);
+ codeRewriter.simplifyIf(code);
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Intermediate (SSA) flow graph for %s:\n%s",
+ method.toSourceString(), code);
+ }
+ codeRewriter.removeUnneededCatchHandlers(code);
+ // Dead code removal. Performed after simplifications to remove code that becomes dead
+ // as a result of those simplifications. The following optimizations could reveal more
+ // dead code which is removed right before register allocation in performRegisterAllocation.
+ DeadCodeRemover.removeDeadCode(code, codeRewriter, options);
+ assert code.isConsistentSSA();
+
+ if (lambdaRewriter != null) {
+ lambdaRewriter.desugarLambdas(method, code);
+ assert code.isConsistentSSA();
+ }
+
+ if (interfaceMethodRewriter != null) {
+ interfaceMethodRewriter.rewriteMethodReferences(method, code);
+ assert code.isConsistentSSA();
+ }
+
+ if (options.outline.enabled) {
+ outlineHandler.accept(code, method);
+ assert code.isConsistentSSA();
+ }
+
+ codeRewriter.shortenLiveRanges(code);
+ codeRewriter.identifyReturnsArgument(method, code);
+
+ printMethod(code, "Optimized IR (SSA)");
+ // Perform register allocation.
+ RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
+ method.setCode(code, registerAllocator);
+ updateHighestSortingStrings(method);
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Resulting dex code for %s:\n%s",
+ method.toSourceString(), logCode(options, method));
+ }
+ printMethod(code, "Final IR (non-SSA)");
+
+ // After all the optimizations have take place, we compute whether method should be inlined.
+ if (!options.inlineAccessors || inliner == null) {
+ return InliningConstraint.NEVER;
+ }
+ return inliner.identifySimpleMethods(code, method);
+ }
+
+ private void updateHighestSortingStrings(DexEncodedMethod method) {
+ DexString highestSortingReferencedString = method.getCode().asDexCode().highestSortingString;
+ if (highestSortingReferencedString != null) {
+ if (highestSortingString == null
+ || highestSortingReferencedString.slowCompareTo(highestSortingString) > 0) {
+ highestSortingString = highestSortingReferencedString;
+ }
+ }
+ }
+
+ // Convert a method ensuring that strings sorting equal or higher than the argument
+ // firstJumboString are encoded as jumbo strings.
+ // TODO(sgjesse): Consider replacing this with a direct dex2dex converter instead of going
+ // through IR.
+ private void convertMethodJumboStringsOnly(
+ DexEncodedMethod method, DexString firstJumboString) {
+ // This is only used for methods already converted to Dex, but missing jumbo strings.
+ assert method.getCode() != null && method.getCode().isDexCode();
+ if (options.verbose) {
+ System.out.println("Processing jumbo strings: " + method.toSourceString());
+ }
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Original code for %s:\n%s",
+ method.toSourceString(), logCode(options, method));
+ }
+ IRCode code = method.buildIR(options);
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s",
+ method.toSourceString(), code);
+ }
+ // Compilation header if printing CFGs for this method.
+ printC1VisualizerHeader(method);
+ printMethod(code, "Initial IR (SSA)");
+
+ // Methods passed through here should have been through IR processing already and
+ // therefore, we skip most of the IR processing.
+
+ // Perform register allocation.
+ RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
+ method.setCode(code, registerAllocator, firstJumboString);
+
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Resulting dex code for %s:\n%s",
+ method.toSourceString(), logCode(options, method));
+ }
+ printMethod(code, "Final IR (non-SSA)");
+ }
+
+ private RegisterAllocator performRegisterAllocation(IRCode code, DexEncodedMethod method) {
+ // Always perform dead code elimination before register allocation. The register allocator
+ // does not allow dead code (to make sure that we do not waste registers for unneeded values).
+ DeadCodeRemover.removeDeadCode(code, codeRewriter, options);
+ LinearScanRegisterAllocator registerAllocator = new LinearScanRegisterAllocator(code);
+ registerAllocator.allocateRegisters(options.debug);
+ printMethod(code, "After register allocation (non-SSA)");
+ printLiveRanges(registerAllocator, "Final live ranges.");
+ if (!options.debug) {
+ CodeRewriter.removedUnneededDebugPositions(code);
+ }
+ for (int i = 0; i < PEEPHOLE_OPTIMIZATION_PASSES; i++) {
+ CodeRewriter.collapsTrivialGotos(method, code);
+ PeepholeOptimizer.optimize(code, registerAllocator);
+ }
+ CodeRewriter.collapsTrivialGotos(method, code);
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Final (non-SSA) flow graph for %s:\n%s",
+ method.toSourceString(), code);
+ }
+ return registerAllocator;
+ }
+
+ private void printC1VisualizerHeader(DexEncodedMethod method) {
+ if (printer != null) {
+ printer.begin("compilation");
+ printer.print("name \"").append(method.toSourceString()).append("\"").ln();
+ printer.print("method \"").append(method.toSourceString()).append("\"").ln();
+ printer.print("date 0").ln();
+ printer.end("compilation");
+ }
+ }
+
+ private void printMethod(IRCode code, String title) {
+ if (printer != null) {
+ printer.resetUnusedValue();
+ printer.begin("cfg");
+ printer.print("name \"").append(title).append("\"\n");
+ code.print(printer);
+ printer.end("cfg");
+ }
+ }
+
+ private void printLiveRanges(LinearScanRegisterAllocator allocator, String title) {
+ if (printer != null) {
+ allocator.print(printer, title);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
new file mode 100644
index 0000000..5647878
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -0,0 +1,2097 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.Descriptor;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.JarApplicationReader;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.Monitor;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.ir.conversion.JarState.Local;
+import com.android.tools.r8.ir.conversion.JarState.Slot;
+import com.android.tools.r8.logging.Log;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.FieldInsnNode;
+import org.objectweb.asm.tree.IincInsnNode;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.InvokeDynamicInsnNode;
+import org.objectweb.asm.tree.JumpInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.LdcInsnNode;
+import org.objectweb.asm.tree.LineNumberNode;
+import org.objectweb.asm.tree.LocalVariableNode;
+import org.objectweb.asm.tree.LookupSwitchInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.MultiANewArrayInsnNode;
+import org.objectweb.asm.tree.TableSwitchInsnNode;
+import org.objectweb.asm.tree.TryCatchBlockNode;
+import org.objectweb.asm.tree.TypeInsnNode;
+import org.objectweb.asm.tree.VarInsnNode;
+import org.objectweb.asm.util.Textifier;
+import org.objectweb.asm.util.TraceMethodVisitor;
+
+
+public class JarSourceCode implements SourceCode {
+
+ // Try-catch block wrapper containing resolved offsets.
+ private static class TryCatchBlock {
+
+ private final int handler;
+ private final int start;
+ private final int end;
+
+ private final String type;
+
+ public TryCatchBlock(TryCatchBlockNode node, JarSourceCode code) {
+ this(code.getOffset(node.handler),
+ code.getOffset(node.start),
+ code.getOffset(node.end),
+ node.type);
+ }
+
+ private TryCatchBlock(int handler, int start, int end, String type) {
+ assert start < end;
+ this.handler = handler;
+ this.start = start;
+ this.end = end;
+ this.type = type;
+ }
+
+ int getStart() {
+ return start;
+ }
+
+ int getEnd() {
+ return end;
+ }
+
+ int getHandler() {
+ return handler;
+ }
+
+ String getType() {
+ return type;
+ }
+ }
+
+ // Various descriptors.
+ private static final String INT_ARRAY_DESC = "[I";
+ private static final String REFLECT_ARRAY_DESC = "Ljava/lang/reflect/Array;";
+ private static final String REFLECT_ARRAY_NEW_INSTANCE_NAME = "newInstance";
+ private static final String REFLECT_ARRAY_NEW_INSTANCE_DESC =
+ "(Ljava/lang/Class;[I)Ljava/lang/Object;";
+ private static final String METHODHANDLE_INVOKE_OR_INVOKEEXACT_DESC =
+ "([Ljava/lang/Object;)Ljava/lang/Object;";
+
+ // Language types.
+ private static final Type CLASS_TYPE = Type.getObjectType("java/lang/Class");
+ private static final Type STRING_TYPE = Type.getObjectType("java/lang/String");
+ private static final Type INT_ARRAY_TYPE = Type.getObjectType(INT_ARRAY_DESC);
+ private static final Type THROWABLE_TYPE = Type.getObjectType("java/lang/Throwable");
+
+ private static final int[] NO_TARGETS = {};
+
+ private final JarApplicationReader application;
+ private final MethodNode node;
+ private final DexType clazz;
+ private final List<Type> parameterTypes;
+ private final LabelNode initialLabel;
+
+ private TraceMethodVisitor printVisitor = null;
+
+ private final JarState state;
+ private AbstractInsnNode currentInstruction = null;
+
+ // Special try-catch block for synchronized methods.
+ // This block is given a negative instruction index as it is not part of the instruction stream.
+ // The start range of 0 ensures that a new block will start at the first real instruction and
+ // thus that the monitor-entry prelude (part of the argument block which must not have a try-catch
+ // successor) is not joined with the first instruction block (which likely will have a try-catch
+ // successor).
+ private static final int EXCEPTIONAL_SYNC_EXIT_OFFSET = -1;
+ private static final TryCatchBlock EXCEPTIONAL_SYNC_EXIT =
+ new TryCatchBlock(EXCEPTIONAL_SYNC_EXIT_OFFSET, 0, Integer.MAX_VALUE, null);
+
+ // Instruction that enters the monitor. Null if the method is not synchronized.
+ private Monitor monitorEnter = null;
+
+ // State to signal that the code currently being emitted is part of synchronization prelude/exits.
+ private boolean generatingMethodSynchronization = false;
+
+ public JarSourceCode(DexType clazz, MethodNode node, JarApplicationReader application) {
+ assert node != null;
+ assert node.desc != null;
+ this.node = node;
+ this.application = application;
+ this.clazz = clazz;
+ parameterTypes = Arrays.asList(Type.getArgumentTypes(node.desc));
+ state = new JarState(node.maxLocals, computeLocals(node.localVariables, application));
+ AbstractInsnNode first = node.instructions.getFirst();
+ initialLabel = first instanceof LabelNode ? (LabelNode) first : null;
+ }
+
+ private static Map<LocalVariableNode, DebugLocalInfo> computeLocals(
+ List localNodes, JarApplicationReader application) {
+ Map<DebugLocalInfo, DebugLocalInfo> canonical = new HashMap<>(localNodes.size());
+ Map<LocalVariableNode, DebugLocalInfo> localVariables = new HashMap<>(localNodes.size());
+ for (Object o : localNodes) {
+ LocalVariableNode node = (LocalVariableNode) o;
+ localVariables.computeIfAbsent(node, n -> canonicalizeLocal(n, canonical, application));
+ }
+ return localVariables;
+ }
+
+ private static DebugLocalInfo canonicalizeLocal(
+ LocalVariableNode node,
+ Map<DebugLocalInfo, DebugLocalInfo> canonicalLocalVariables,
+ JarApplicationReader application) {
+ DebugLocalInfo info = new DebugLocalInfo(
+ application.getString(node.name),
+ application.getType(Type.getType(node.desc)),
+ node.signature == null ? null : application.getString(node.signature));
+ DebugLocalInfo canonical = canonicalLocalVariables.putIfAbsent(info, info);
+ return canonical != null ? canonical : info;
+ }
+
+ private boolean isStatic() {
+ return (node.access & Opcodes.ACC_STATIC) > 0;
+ }
+
+ private boolean isSynchronized() {
+ return (node.access & Opcodes.ACC_SYNCHRONIZED) > 0;
+ }
+
+ private int formalParameterCount() {
+ return parameterTypes.size();
+ }
+
+ private int actualArgumentCount() {
+ return isStatic() ? formalParameterCount() : formalParameterCount() + 1;
+ }
+
+ @Override
+ public int instructionCount() {
+ return node.instructions.size();
+ }
+
+ @Override
+ public int instructionIndex(int instructionOffset) {
+ return instructionOffset;
+ }
+
+ @Override
+ public int instructionOffset(int instructionIndex) {
+ return instructionIndex;
+ }
+
+ @Override
+ public boolean verifyRegister(int register) {
+ // The register set is dynamically managed by the state so we assume all values valid here.
+ return true;
+ }
+
+ @Override
+ public void setUp() {
+ if (Log.ENABLED) {
+ Log.debug(JarSourceCode.class, "Computing blocks for:\n" + toString());
+ }
+ }
+
+ @Override
+ public void clear() {
+
+ }
+
+ @Override
+ public boolean needsPrelude() {
+ return isSynchronized() || actualArgumentCount() > 0 || !node.localVariables.isEmpty();
+ }
+
+ @Override
+ public void buildPrelude(IRBuilder builder) {
+ Map<Integer, MoveType> initializedLocals = new HashMap<>(node.localVariables.size());
+ if (initialLabel != null) {
+ state.openLocals(initialLabel);
+ }
+ int argumentRegister = 0;
+ if (!isStatic()) {
+ Type thisType = Type.getType(clazz.descriptor.toString());
+ int register = state.writeLocal(argumentRegister++, thisType);
+ builder.addThisArgument(register);
+ initializedLocals.put(register, moveType(thisType));
+ }
+ for (Type type : parameterTypes) {
+ MoveType moveType = moveType(type);
+ int register = state.writeLocal(argumentRegister, type);
+ builder.addNonThisArgument(register, moveType);
+ argumentRegister += moveType.requiredRegisters();
+ initializedLocals.put(register, moveType);
+ }
+ if (isSynchronized()) {
+ generatingMethodSynchronization = true;
+ Type clazzType = Type.getType(clazz.toDescriptorString());
+ int monitorRegister;
+ if (isStatic()) {
+ // Load the class using a temporary on the stack.
+ monitorRegister = state.push(clazzType);
+ state.pop();
+ builder.addConstClass(monitorRegister, clazz);
+ } else {
+ assert actualArgumentCount() > 0;
+ // The object is stored in the first local.
+ monitorRegister = state.readLocal(0, clazzType).register;
+ }
+ // Build the monitor enter and save it for when generating exits later.
+ monitorEnter = builder.addMonitor(Monitor.Type.ENTER, monitorRegister);
+ generatingMethodSynchronization = false;
+ }
+ // Initialize all non-argument locals to ensure safe insertion of debug-local instructions.
+ for (Object o : node.localVariables) {
+ LocalVariableNode local = (LocalVariableNode) o;
+ Type localType = Type.getType(local.desc);
+ int localRegister = state.getLocalRegister(local.index, localType);
+ MoveType exitingLocalType = initializedLocals.get(localRegister);
+ assert exitingLocalType == null || exitingLocalType == moveType(localType);
+ if (exitingLocalType == null) {
+ int localRegister2 = state.writeLocal(local.index, localType);
+ assert localRegister == localRegister2;
+ initializedLocals.put(localRegister, moveType(localType));
+ builder.addDebugUninitialized(localRegister, constType(localType));
+ }
+ }
+ }
+
+ @Override
+ public void buildPostlude(IRBuilder builder) {
+ if (isSynchronized()) {
+ generatingMethodSynchronization = true;
+ buildMonitorExit(builder);
+ generatingMethodSynchronization = false;
+ }
+ }
+
+ private void buildExceptionalPostlude(IRBuilder builder) {
+ assert isSynchronized();
+ assert state.isInvalid();
+ generatingMethodSynchronization = true;
+ int exceptionRegister = 0; // We are exiting the method so we just overwrite register 0.
+ builder.addMoveException(exceptionRegister);
+ buildMonitorExit(builder);
+ builder.addThrow(exceptionRegister);
+ generatingMethodSynchronization = false;
+ }
+
+ private void buildMonitorExit(IRBuilder builder) {
+ assert generatingMethodSynchronization;
+ builder.add(new Monitor(Monitor.Type.EXIT, monitorEnter.inValues().get(0)));
+ }
+
+ @Override
+ public void closedCurrentBlockWithFallthrough(int fallthroughInstructionIndex) {
+ assert !state.isInvalid();
+ state.recordStateForTarget(fallthroughInstructionIndex, this);
+ state.invalidateState();
+ }
+
+ @Override
+ public void closedCurrentBlock() {
+ assert state.isInvalid();
+ }
+
+ @Override
+ public void buildInstruction(IRBuilder builder, int instructionIndex) {
+ if (instructionIndex == EXCEPTIONAL_SYNC_EXIT_OFFSET) {
+ buildExceptionalPostlude(builder);
+ return;
+ }
+ AbstractInsnNode insn = getInstruction(instructionIndex);
+ currentInstruction = insn;
+ assert verifyExceptionEdgesAreRecorded(insn);
+
+ // Restore the state if invalid.
+ if (state.isInvalid()) {
+ state.restoreState(instructionIndex);
+ // If the block being restored is a try-catch handler push the exception on the stack.
+ for (int i = 0; i < node.tryCatchBlocks.size(); i++) {
+ TryCatchBlockNode tryCatchBlockNode = (TryCatchBlockNode) node.tryCatchBlocks.get(i);
+ if (tryCatchBlockNode.handler == insn) {
+ builder.addMoveException(state.push(THROWABLE_TYPE));
+ break;
+ }
+ }
+ }
+
+ String preInstructionState;
+ if (Log.ENABLED) {
+ preInstructionState = state.toString();
+ }
+
+ build(insn, builder);
+
+ // Process local-variable end scopes if this instruction is not a control-flow instruction.
+ // For control-flow instructions, processing takes place before closing their respective blocks.
+ if (!isControlFlowInstruction(insn)) {
+ processLocalVariableEnd(insn, builder);
+ }
+
+ if (Log.ENABLED && !(insn instanceof LineNumberNode)) {
+ int offset = getOffset(insn);
+ if (insn instanceof LabelNode) {
+ Log.debug(getClass(), "\n%4d: %s",
+ offset, instructionToString(insn).replace('\n', ' '));
+ } else {
+ Log.debug(getClass(), "\n%4d: %s pre: %s\n post: %s",
+ offset, instructionToString(insn), preInstructionState, state);
+ }
+ }
+ }
+
+ private boolean verifyExceptionEdgesAreRecorded(AbstractInsnNode insn) {
+ if (canThrow(insn)) {
+ for (TryCatchBlock tryCatchBlock : getTryHandlers(insn)) {
+ assert tryCatchBlock.getHandler() == EXCEPTIONAL_SYNC_EXIT_OFFSET
+ || state.hasState(tryCatchBlock.getHandler());
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void resolveAndBuildSwitch(Switch.Type type, int value, int fallthroughOffset,
+ int payloadOffset, IRBuilder builder) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset,
+ IRBuilder builder) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public DebugLocalInfo getCurrentLocal(int register) {
+ return state.getLocalInfoForRegister(register);
+ }
+
+ @Override
+ public CatchHandlers<Integer> getCurrentCatchHandlers() {
+ if (generatingMethodSynchronization) {
+ return null;
+ }
+ List<TryCatchBlock> tryCatchBlocks = getTryHandlers(currentInstruction);
+ if (tryCatchBlocks.isEmpty()) {
+ return null;
+ }
+ // TODO(zerny): Compute this more efficiently.
+ return new CatchHandlers<>(
+ getTryHandlerGuards(tryCatchBlocks),
+ getTryHandlerOffsets(tryCatchBlocks));
+ }
+
+ @Override
+ public boolean verifyCurrentInstructionCanThrow() {
+ return generatingMethodSynchronization || canThrow(currentInstruction);
+ }
+
+ @Override
+ public boolean verifyLocalInScope(DebugLocalInfo local) {
+ for (Local open : state.getLocals()) {
+ if (open.info != null && open.info.name == local.name) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private AbstractInsnNode getInstruction(int index) {
+ return node.instructions.get(index);
+ }
+
+ private static boolean isReturn(AbstractInsnNode insn) {
+ return Opcodes.IRETURN <= insn.getOpcode() && insn.getOpcode() <= Opcodes.RETURN;
+ }
+
+ private static boolean isSwitch(AbstractInsnNode insn) {
+ return Opcodes.TABLESWITCH == insn.getOpcode() || insn.getOpcode() == Opcodes.LOOKUPSWITCH;
+ }
+
+ private static boolean isThrow(AbstractInsnNode insn) {
+ return Opcodes.ATHROW == insn.getOpcode();
+ }
+
+ private static boolean isControlFlowInstruction(AbstractInsnNode insn) {
+ return isReturn(insn) || isThrow(insn) || isSwitch(insn) || (insn instanceof JumpInsnNode)
+ || insn.getOpcode() == Opcodes.RET;
+ }
+
+ private boolean canThrow(AbstractInsnNode insn) {
+ switch (insn.getOpcode()) {
+ case Opcodes.AALOAD:
+ case Opcodes.AASTORE:
+ case Opcodes.ANEWARRAY:
+ // ARETURN does not throw in its dex image.
+ case Opcodes.ARRAYLENGTH:
+ case Opcodes.ATHROW:
+ case Opcodes.BALOAD:
+ case Opcodes.BASTORE:
+ case Opcodes.CALOAD:
+ case Opcodes.CASTORE:
+ case Opcodes.CHECKCAST:
+ case Opcodes.DALOAD:
+ case Opcodes.DASTORE:
+ // DRETURN does not throw in its dex image.
+ case Opcodes.FALOAD:
+ case Opcodes.FASTORE:
+ // FRETURN does not throw in its dex image.
+ case Opcodes.GETFIELD:
+ case Opcodes.GETSTATIC:
+ case Opcodes.IALOAD:
+ case Opcodes.IASTORE:
+ case Opcodes.IDIV:
+ case Opcodes.INSTANCEOF:
+ case Opcodes.INVOKEDYNAMIC:
+ case Opcodes.INVOKEINTERFACE:
+ case Opcodes.INVOKESPECIAL:
+ case Opcodes.INVOKESTATIC:
+ case Opcodes.INVOKEVIRTUAL:
+ case Opcodes.IREM:
+ // IRETURN does not throw in its dex image.
+ case Opcodes.LALOAD:
+ case Opcodes.LASTORE:
+ case Opcodes.LDIV:
+ case Opcodes.LREM:
+ // LRETURN does not throw in its dex image.
+ case Opcodes.MONITORENTER:
+ case Opcodes.MONITOREXIT:
+ case Opcodes.MULTIANEWARRAY:
+ case Opcodes.NEW:
+ case Opcodes.NEWARRAY:
+ case Opcodes.PUTFIELD:
+ case Opcodes.PUTSTATIC:
+ // RETURN does not throw in its dex image.
+ case Opcodes.SALOAD:
+ case Opcodes.SASTORE:
+ return true;
+ case Opcodes.LDC: {
+ // const-class and const-string* may throw in dex.
+ LdcInsnNode ldc = (LdcInsnNode) insn;
+ return ldc.cst instanceof String || ldc.cst instanceof Type;
+ }
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public boolean traceInstruction(int index, IRBuilder builder) {
+ AbstractInsnNode insn = getInstruction(index);
+ // Exit early on no-op instructions.
+ if (insn instanceof LabelNode || insn instanceof LineNumberNode) {
+ return false;
+ }
+ // If this instruction exits, close this block.
+ if (isReturn(insn)) {
+ return true;
+ }
+ // For each target ensure a basic block and close this block.
+ int[] targets = getTargets(insn);
+ if (targets != NO_TARGETS) {
+ assert !canThrow(insn);
+ for (int target : targets) {
+ builder.ensureSuccessorBlock(target);
+ }
+ return true;
+ }
+ if (canThrow(insn)) {
+ List<TryCatchBlock> tryCatchBlocks = getTryHandlers(insn);
+ if (!tryCatchBlocks.isEmpty()) {
+ Set<Integer> seenHandlerOffsets = new HashSet<>();
+ for (TryCatchBlock tryCatchBlock : tryCatchBlocks) {
+ // Ensure the block starts at the start of the try-range (don't enqueue, not a target).
+ builder.ensureBlockWithoutEnqueuing(tryCatchBlock.getStart());
+ // Add edge to exceptional successor (only one edge for each unique successor).
+ int handler = tryCatchBlock.getHandler();
+ if (!seenHandlerOffsets.contains(handler)) {
+ seenHandlerOffsets.add(handler);
+ builder.ensureSuccessorBlock(handler);
+ }
+ }
+ // Edge to normal successor if any (fallthrough).
+ if (!isThrow(insn)) {
+ builder.ensureSuccessorBlock(getOffset(insn.getNext()));
+ }
+ return true;
+ }
+ // If the throwable instruction is "throw" it closes the block.
+ return isThrow(insn);
+ }
+ // This instruction does not close the block.
+ return false;
+ }
+
+ private List<TryCatchBlock> getPotentialTryHandlers(AbstractInsnNode insn) {
+ int offset = getOffset(insn);
+ return getPotentialTryHandlers(offset);
+ }
+
+ private boolean tryBlockRelevant(TryCatchBlockNode tryHandler, int offset) {
+ int start = getOffset(tryHandler.start);
+ int end = getOffset(tryHandler.end);
+ return start <= offset && offset < end;
+ }
+
+ private List<TryCatchBlock> getPotentialTryHandlers(int offset) {
+ List<TryCatchBlock> handlers = new ArrayList<>();
+ for (int i = 0; i < node.tryCatchBlocks.size(); i++) {
+ TryCatchBlockNode tryBlock = (TryCatchBlockNode) node.tryCatchBlocks.get(i);
+ if (tryBlockRelevant(tryBlock, offset)) {
+ handlers.add(new TryCatchBlock(tryBlock, this));
+ }
+ }
+ return handlers;
+ }
+
+ private List<TryCatchBlock> getTryHandlers(AbstractInsnNode insn) {
+ List<TryCatchBlock> handlers = new ArrayList<>();
+ Set<String> seen = new HashSet<>();
+ // The try-catch blocks are ordered by precedence.
+ for (TryCatchBlock tryCatchBlock : getPotentialTryHandlers(insn)) {
+ if (tryCatchBlock.getType() == null) {
+ handlers.add(tryCatchBlock);
+ return handlers;
+ }
+ if (!seen.contains(tryCatchBlock.getType())) {
+ seen.add(tryCatchBlock.getType());
+ handlers.add(tryCatchBlock);
+ }
+ }
+ if (isSynchronized()) {
+ // Add synchronized exceptional exit for synchronized-method instructions without a default.
+ assert handlers.isEmpty() || handlers.get(handlers.size() - 1).getType() != null;
+ handlers.add(EXCEPTIONAL_SYNC_EXIT);
+ }
+ return handlers;
+ }
+
+ private List<Integer> getTryHandlerOffsets(List<TryCatchBlock> tryCatchBlocks) {
+ List<Integer> offsets = new ArrayList<>();
+ for (TryCatchBlock tryCatchBlock : tryCatchBlocks) {
+ offsets.add(tryCatchBlock.getHandler());
+ }
+ return offsets;
+ }
+
+ private List<DexType> getTryHandlerGuards(List<TryCatchBlock> tryCatchBlocks) {
+ List<DexType> guards = new ArrayList<>();
+ for (TryCatchBlock tryCatchBlock : tryCatchBlocks) {
+ guards.add(tryCatchBlock.getType() == null
+ ? DexItemFactory.catchAllType
+ : application.getTypeFromName(tryCatchBlock.getType()));
+
+ }
+ return guards;
+ }
+
+ int getOffset(AbstractInsnNode insn) {
+ return node.instructions.indexOf(insn);
+ }
+
+ private int[] getTargets(AbstractInsnNode insn) {
+ switch (insn.getType()) {
+ case AbstractInsnNode.TABLESWITCH_INSN: {
+ TableSwitchInsnNode switchInsn = (TableSwitchInsnNode) insn;
+ return getSwitchTargets(switchInsn.dflt, switchInsn.labels);
+ }
+ case AbstractInsnNode.LOOKUPSWITCH_INSN: {
+ LookupSwitchInsnNode switchInsn = (LookupSwitchInsnNode) insn;
+ return getSwitchTargets(switchInsn.dflt, switchInsn.labels);
+ }
+ case AbstractInsnNode.JUMP_INSN: {
+ return getJumpTargets((JumpInsnNode) insn);
+ }
+ case AbstractInsnNode.VAR_INSN: {
+ return getVarTargets((VarInsnNode) insn);
+ }
+ default:
+ return NO_TARGETS;
+ }
+ }
+
+ private int[] getSwitchTargets(LabelNode dflt, List labels) {
+ int[] targets = new int[1 + labels.size()];
+ targets[0] = getOffset(dflt);
+ for (int i = 1; i < targets.length; i++) {
+ targets[i] = getOffset((LabelNode) labels.get(i - 1));
+ }
+ return targets;
+ }
+
+ private int[] getJumpTargets(JumpInsnNode jump) {
+ switch (jump.getOpcode()) {
+ case Opcodes.IFEQ:
+ case Opcodes.IFNE:
+ case Opcodes.IFLT:
+ case Opcodes.IFGE:
+ case Opcodes.IFGT:
+ case Opcodes.IFLE:
+ case Opcodes.IF_ICMPEQ:
+ case Opcodes.IF_ICMPNE:
+ case Opcodes.IF_ICMPLT:
+ case Opcodes.IF_ICMPGE:
+ case Opcodes.IF_ICMPGT:
+ case Opcodes.IF_ICMPLE:
+ case Opcodes.IF_ACMPEQ:
+ case Opcodes.IF_ACMPNE:
+ case Opcodes.IFNULL:
+ case Opcodes.IFNONNULL:
+ return new int[]{getOffset(jump.label), getOffset(jump.getNext())};
+ case Opcodes.GOTO:
+ return new int[]{getOffset(jump.label)};
+ case Opcodes.JSR: {
+ throw new Unreachable("JSR should be handled by the ASM jsr inliner");
+ }
+ default:
+ throw new Unreachable("Unexpected opcode in jump instruction: " + jump);
+ }
+ }
+
+ private int[] getVarTargets(VarInsnNode insn) {
+ if (insn.getOpcode() == Opcodes.RET) {
+ throw new Unreachable("RET should be handled by the ASM jsr inliner");
+ }
+ return NO_TARGETS;
+ }
+
+ // Type conversion helpers.
+
+ private static MoveType moveType(Type type) {
+ switch (type.getSort()) {
+ case Type.ARRAY:
+ case Type.OBJECT:
+ return MoveType.OBJECT;
+ case Type.BOOLEAN:
+ case Type.BYTE:
+ case Type.SHORT:
+ case Type.CHAR:
+ case Type.INT:
+ case Type.FLOAT:
+ return MoveType.SINGLE;
+ case Type.LONG:
+ case Type.DOUBLE:
+ return MoveType.WIDE;
+ case Type.VOID:
+ // Illegal. Throws in fallthrough.
+ default:
+ throw new Unreachable("Invalid type in moveType: " + type);
+ }
+ }
+
+ private static ConstType constType(Type type) {
+ switch (type.getSort()) {
+ case Type.ARRAY:
+ case Type.OBJECT:
+ return ConstType.OBJECT;
+ case Type.BOOLEAN:
+ case Type.BYTE:
+ case Type.SHORT:
+ case Type.CHAR:
+ case Type.INT:
+ return ConstType.INT;
+ case Type.FLOAT:
+ return ConstType.FLOAT;
+ case Type.LONG:
+ return ConstType.LONG;
+ case Type.DOUBLE:
+ return ConstType.DOUBLE;
+ case Type.VOID:
+ // Illegal. Throws in fallthrough.
+ default:
+ throw new Unreachable("Invalid type in constType: " + type);
+ }
+ }
+
+ private static MemberType memberType(Type type) {
+ switch (type.getSort()) {
+ case Type.ARRAY:
+ case Type.OBJECT:
+ return MemberType.OBJECT;
+ case Type.BOOLEAN:
+ return MemberType.BOOLEAN;
+ case Type.BYTE:
+ return MemberType.BYTE;
+ case Type.SHORT:
+ return MemberType.SHORT;
+ case Type.CHAR:
+ return MemberType.CHAR;
+ case Type.INT:
+ case Type.FLOAT:
+ return MemberType.SINGLE;
+ case Type.LONG:
+ case Type.DOUBLE:
+ return MemberType.WIDE;
+ case Type.VOID:
+ // Illegal. Throws in fallthrough.
+ default:
+ throw new Unreachable("Invalid type in memberType: " + type);
+ }
+ }
+
+ private static MemberType memberType(String fieldDesc) {
+ return memberType(Type.getType(fieldDesc));
+ }
+
+ private static NumericType numericType(Type type) {
+ switch (type.getSort()) {
+ case Type.BYTE:
+ return NumericType.BYTE;
+ case Type.CHAR:
+ return NumericType.CHAR;
+ case Type.SHORT:
+ return NumericType.SHORT;
+ case Type.INT:
+ return NumericType.INT;
+ case Type.LONG:
+ return NumericType.LONG;
+ case Type.FLOAT:
+ return NumericType.FLOAT;
+ case Type.DOUBLE:
+ return NumericType.DOUBLE;
+ default:
+ throw new Unreachable("Invalid type in numericType: " + type);
+ }
+ }
+
+ private Invoke.Type invokeType(MethodInsnNode method) {
+ switch (method.getOpcode()) {
+ case Opcodes.INVOKEVIRTUAL:
+ if (isCallToPolymorphicSignatureMethod(method)) {
+ return Invoke.Type.POLYMORPHIC;
+ }
+ return Invoke.Type.VIRTUAL;
+ case Opcodes.INVOKESTATIC:
+ return Invoke.Type.STATIC;
+ case Opcodes.INVOKEINTERFACE:
+ return Invoke.Type.INTERFACE;
+ case Opcodes.INVOKESPECIAL: {
+ DexType owner = application.getTypeFromName(method.owner);
+ if (owner == clazz || method.name.equals(Constants.INSTANCE_INITIALIZER_NAME)) {
+ return Invoke.Type.DIRECT;
+ } else {
+ return Invoke.Type.SUPER;
+ }
+ }
+ default:
+ throw new Unreachable("Unexpected MethodInsnNode opcode: " + method.getOpcode());
+ }
+ }
+
+ static Type getArrayElementType(Type array) {
+ if (array == JarState.NULL_TYPE) {
+ return null;
+ }
+ String desc = array.getDescriptor();
+ assert desc.charAt(0) == '[';
+ return Type.getType(desc.substring(1));
+ }
+
+ private static Type makeArrayType(Type elementType) {
+ return Type.getObjectType("[" + elementType.getDescriptor());
+ }
+
+ private static String arrayTypeDesc(int arrayTypeCode) {
+ switch (arrayTypeCode) {
+ case Opcodes.T_BOOLEAN:
+ return "[Z";
+ case Opcodes.T_CHAR:
+ return "[C";
+ case Opcodes.T_FLOAT:
+ return "[F";
+ case Opcodes.T_DOUBLE:
+ return "[D";
+ case Opcodes.T_BYTE:
+ return "[B";
+ case Opcodes.T_SHORT:
+ return "[S";
+ case Opcodes.T_INT:
+ return "[I";
+ case Opcodes.T_LONG:
+ return "[J";
+ default:
+ throw new Unreachable("Unexpected array-type code " + arrayTypeCode);
+ }
+ }
+
+ private static Type getArrayElementTypeForOpcode(int opcode) {
+ switch (opcode) {
+ case Opcodes.IALOAD:
+ case Opcodes.IASTORE:
+ return Type.INT_TYPE;
+ case Opcodes.FALOAD:
+ case Opcodes.FASTORE:
+ return Type.FLOAT_TYPE;
+ case Opcodes.LALOAD:
+ case Opcodes.LASTORE:
+ return Type.LONG_TYPE;
+ case Opcodes.DALOAD:
+ case Opcodes.DASTORE:
+ return Type.DOUBLE_TYPE;
+ case Opcodes.AALOAD:
+ case Opcodes.AASTORE:
+ return JarState.NULL_TYPE; // We might not know the type.
+ case Opcodes.BALOAD:
+ case Opcodes.BASTORE:
+ return Type.BYTE_TYPE; // We don't distinguish byte and boolean.
+ case Opcodes.CALOAD:
+ case Opcodes.CASTORE:
+ return Type.CHAR_TYPE;
+ case Opcodes.SALOAD:
+ case Opcodes.SASTORE:
+ return Type.SHORT_TYPE;
+ default:
+ throw new Unreachable("Unexpected array opcode " + opcode);
+ }
+ }
+
+ private static boolean isCompatibleArrayElementType(int opcode, Type type) {
+ switch (opcode) {
+ case Opcodes.IALOAD:
+ case Opcodes.IASTORE:
+ return Slot.isCompatible(type, Type.INT_TYPE);
+ case Opcodes.FALOAD:
+ case Opcodes.FASTORE:
+ return Slot.isCompatible(type, Type.FLOAT_TYPE);
+ case Opcodes.LALOAD:
+ case Opcodes.LASTORE:
+ return Slot.isCompatible(type, Type.LONG_TYPE);
+ case Opcodes.DALOAD:
+ case Opcodes.DASTORE:
+ return Slot.isCompatible(type, Type.DOUBLE_TYPE);
+ case Opcodes.AALOAD:
+ case Opcodes.AASTORE:
+ return Slot.isCompatible(type, JarState.REFERENCE_TYPE);
+ case Opcodes.BALOAD:
+ case Opcodes.BASTORE:
+ return Slot.isCompatible(type, Type.BYTE_TYPE)
+ || Slot.isCompatible(type, Type.BOOLEAN_TYPE);
+ case Opcodes.CALOAD:
+ case Opcodes.CASTORE:
+ return Slot.isCompatible(type, Type.CHAR_TYPE);
+ case Opcodes.SALOAD:
+ case Opcodes.SASTORE:
+ return Slot.isCompatible(type, Type.SHORT_TYPE);
+ default:
+ throw new Unreachable("Unexpected array 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);
+ }
+ }
+
+ private static Type opType(int opcode) {
+ 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 Type.INT_TYPE;
+ 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 Type.LONG_TYPE;
+ case Opcodes.FADD:
+ case Opcodes.FSUB:
+ case Opcodes.FMUL:
+ case Opcodes.FDIV:
+ case Opcodes.FREM:
+ case Opcodes.FNEG:
+ return Type.FLOAT_TYPE;
+ case Opcodes.DADD:
+ case Opcodes.DSUB:
+ case Opcodes.DMUL:
+ case Opcodes.DDIV:
+ case Opcodes.DREM:
+ case Opcodes.DNEG:
+ return Type.DOUBLE_TYPE;
+ default:
+ throw new Unreachable("Unexpected opcode " + opcode);
+ }
+ }
+
+ // IR instruction building procedures.
+
+ private void build(AbstractInsnNode insn, IRBuilder builder) {
+ switch (insn.getType()) {
+ case AbstractInsnNode.INSN:
+ build((InsnNode) insn, builder);
+ break;
+ case AbstractInsnNode.INT_INSN:
+ build((IntInsnNode) insn, builder);
+ break;
+ case AbstractInsnNode.VAR_INSN:
+ build((VarInsnNode) insn, builder);
+ break;
+ case AbstractInsnNode.TYPE_INSN:
+ build((TypeInsnNode) insn, builder);
+ break;
+ case AbstractInsnNode.FIELD_INSN:
+ build((FieldInsnNode) insn, builder);
+ break;
+ case AbstractInsnNode.METHOD_INSN:
+ build((MethodInsnNode) insn, builder);
+ break;
+ case AbstractInsnNode.INVOKE_DYNAMIC_INSN:
+ build((InvokeDynamicInsnNode) insn, builder);
+ break;
+ case AbstractInsnNode.JUMP_INSN:
+ build((JumpInsnNode) insn, builder);
+ break;
+ case AbstractInsnNode.LABEL:
+ build((LabelNode) insn, builder);
+ break;
+ case AbstractInsnNode.LDC_INSN:
+ build((LdcInsnNode) insn, builder);
+ break;
+ case AbstractInsnNode.IINC_INSN:
+ build((IincInsnNode) insn, builder);
+ break;
+ case AbstractInsnNode.TABLESWITCH_INSN:
+ build((TableSwitchInsnNode) insn, builder);
+ break;
+ case AbstractInsnNode.LOOKUPSWITCH_INSN:
+ build((LookupSwitchInsnNode) insn, builder);
+ break;
+ case AbstractInsnNode.MULTIANEWARRAY_INSN:
+ build((MultiANewArrayInsnNode) insn, builder);
+ break;
+ case AbstractInsnNode.LINE:
+ build((LineNumberNode) insn, builder);
+ break;
+ default:
+ throw new Unreachable("Unexpected instruction " + insn);
+ }
+ }
+
+ private void processLocalVariableEnd(AbstractInsnNode insn, IRBuilder builder) {
+ assert !isControlFlowInstruction(insn);
+ if (!(insn.getNext() instanceof LabelNode)) {
+ return;
+ }
+ // If the label is the end of any local-variable scopes end the locals.
+ LabelNode label = (LabelNode) insn.getNext();
+ List<Local> locals = state.getLocalsToClose(label);
+ for (Local local : locals) {
+ builder.addDebugLocalEnd(local.slot.register, local.info);
+ }
+ state.closeLocals(locals);
+ }
+
+ private void processLocalVariablesAtControlEdge(AbstractInsnNode insn, IRBuilder builder) {
+ assert isControlFlowInstruction(insn) && !isReturn(insn);
+ if (!(insn.getNext() instanceof LabelNode)) {
+ return;
+ }
+ // If the label is the end of any local-variable scopes read the locals to ensure liveness.
+ LabelNode label = (LabelNode) insn.getNext();
+ for (Local local : state.getLocalsToClose(label)) {
+ builder.addDebugLocalRead(local.slot.register, local.info);
+ }
+ }
+
+ private void processLocalVariablesAtExit(AbstractInsnNode insn, IRBuilder builder) {
+ assert isReturn(insn) || isThrow(insn);
+ // Read all locals live at exit to ensure liveness.
+ for (Local local : state.getLocals()) {
+ if (local.info != null) {
+ builder.addDebugLocalRead(local.slot.register, local.info);
+ }
+ }
+ }
+
+ private void build(InsnNode insn, IRBuilder builder) {
+ int opcode = insn.getOpcode();
+ switch (opcode) {
+ case Opcodes.NOP:
+ // Intentionally left empty.
+ break;
+ case Opcodes.ACONST_NULL:
+ builder.addNullConst(state.push(JarState.NULL_TYPE), 0);
+ 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:
+ builder.addIntConst(state.push(Type.INT_TYPE), opcode - Opcodes.ICONST_0);
+ break;
+ case Opcodes.LCONST_0:
+ case Opcodes.LCONST_1:
+ builder.addLongConst(state.push(Type.LONG_TYPE), opcode - Opcodes.LCONST_0);
+ break;
+ case Opcodes.FCONST_0:
+ case Opcodes.FCONST_1:
+ case Opcodes.FCONST_2:
+ builder.addFloatConst(state.push(Type.FLOAT_TYPE),
+ Float.floatToRawIntBits(opcode - Opcodes.FCONST_0));
+ break;
+ case Opcodes.DCONST_0:
+ case Opcodes.DCONST_1:
+ builder.addDoubleConst(state.push(Type.DOUBLE_TYPE),
+ Double.doubleToRawLongBits(opcode - Opcodes.DCONST_0));
+ 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: {
+ Slot index = state.pop(Type.INT_TYPE);
+ Slot array = state.pop(JarState.ARRAY_TYPE);
+ Type elementType = getArrayElementType(array.type);
+ if (elementType == null) {
+ elementType = getArrayElementTypeForOpcode(opcode);
+ }
+ int dest = state.push(elementType);
+ assert isCompatibleArrayElementType(opcode, elementType);
+ builder.addArrayGet(memberType(elementType), dest, array.register, index.register);
+ 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: {
+ Slot value = state.pop();
+ Slot index = state.pop(Type.INT_TYPE);
+ Slot array = state.pop(JarState.ARRAY_TYPE);
+ Type elementType = getArrayElementType(array.type);
+ if (elementType == null) {
+ elementType = getArrayElementTypeForOpcode(opcode);
+ }
+ assert isCompatibleArrayElementType(opcode, elementType);
+ assert isCompatibleArrayElementType(opcode, value.type);
+ builder.addArrayPut(
+ memberType(elementType), value.register, array.register, index.register);
+ break;
+ }
+ case Opcodes.POP: {
+ Slot value = state.pop();
+ assert value.isCategory1();
+ break;
+ }
+ case Opcodes.POP2: {
+ Slot value = state.pop();
+ if (value.isCategory1()) {
+ Slot value2 = state.pop();
+ assert value2.isCategory1();
+ }
+ break;
+ }
+ case Opcodes.DUP: {
+ Slot value = state.peek();
+ assert value.isCategory1();
+ int copy = state.push(value.type);
+ builder.addMove(moveType(value.type), copy, value.register);
+ break;
+ }
+ case Opcodes.DUP_X1: {
+ // Stack transformation: ..., v2, v1 -> ..., v1, v2, v1
+ Slot value1 = state.pop();
+ Slot value2 = state.pop();
+ assert value1.isCategory1() && value2.isCategory1();
+ int stack2 = state.push(value1.type);
+ int stack1 = state.push(value2.type);
+ int stack0 = state.push(value1.type);
+ assert value2.register == stack2;
+ assert value1.register == stack1;
+ // stack0 is new top-of-stack.
+ builder.addMove(moveType(value1.type), stack0, stack1);
+ builder.addMove(moveType(value2.type), stack1, stack2);
+ builder.addMove(moveType(value1.type), stack2, stack0);
+ break;
+ }
+ case Opcodes.DUP_X2: {
+ Slot value1 = state.pop();
+ Slot value2 = state.pop();
+ assert value1.isCategory1();
+ if (value2.isCategory1()) {
+ Slot value3 = state.pop();
+ assert value3.isCategory1();
+ // Stack transformation: ..., v3, v2, v1 -> ..., v1, v3, v2, v1
+ dupOneBelowTwo(value3, value2, value1, builder);
+ } else {
+ // Stack transformation: ..., w2, v1 -> ..., v1, w2, v1
+ dupOneBelowOne(value2, value1, builder);
+ }
+ break;
+ }
+ case Opcodes.DUP2: {
+ Slot value1 = state.pop();
+ if (value1.isCategory1()) {
+ Slot value2 = state.pop();
+ // Stack transformation: ..., v2, v1 -> ..., v2, v1, v2, v1
+ assert value2.isCategory1();
+ state.push(value2.type);
+ state.push(value1.type);
+ int copy2 = state.push(value2.type);
+ int copy1 = state.push(value1.type);
+ builder.addMove(moveType(value1.type), copy1, value1.register);
+ builder.addMove(moveType(value2.type), copy2, value2.register);
+ } else {
+ // Stack transformation: ..., w1 -> ..., w1, w1
+ state.push(value1.type);
+ int copy1 = state.push(value1.type);
+ builder.addMove(moveType(value1.type), copy1, value1.register);
+ }
+ break;
+ }
+ case Opcodes.DUP2_X1: {
+ Slot value1 = state.pop();
+ Slot value2 = state.pop();
+ assert value2.isCategory1();
+ if (value1.isCategory1()) {
+ // Stack transformation: ..., v3, v2, v1 -> v2, v1, v3, v2, v1
+ Slot value3 = state.pop();
+ assert value3.isCategory1();
+ dupTwoBelowOne(value3, value2, value1, builder);
+ } else {
+ // Stack transformation: ..., v2, w1 -> ..., w1, v2, w1
+ dupOneBelowOne(value2, value1, builder);
+ }
+ break;
+ }
+ case Opcodes.DUP2_X2: {
+ Slot value1 = state.pop();
+ Slot value2 = state.pop();
+ if (!value1.isCategory1() && !value2.isCategory1()) {
+ // State transformation: ..., w2, w1 -> w1, w2, w1
+ dupOneBelowOne(value2, value1, builder);
+ } else {
+ Slot value3 = state.pop();
+ if (!value1.isCategory1()) {
+ assert value2.isCategory1();
+ assert value3.isCategory1();
+ // State transformation: ..., v3, v2, w1 -> w1, v3, v2, w1
+ dupOneBelowTwo(value3, value2, value1, builder);
+ } else if (!value3.isCategory1()) {
+ assert value1.isCategory1();
+ assert value2.isCategory1();
+ // State transformation: ..., w3, v2, v1 -> v2, v1, w3, v2, v1
+ dupTwoBelowOne(value3, value2, value1, builder);
+ } else {
+ Slot value4 = state.pop();
+ assert value1.isCategory1();
+ assert value2.isCategory1();
+ assert value3.isCategory1();
+ assert value4.isCategory1();
+ // State transformation: ..., v4, v3, v2, v1 -> v2, v1, v4, v3, v2, v1
+ dupTwoBelowTwo(value4, value3, value2, value1, builder);
+ }
+ }
+ break;
+ }
+ case Opcodes.SWAP: {
+ Slot value1 = state.pop();
+ Slot value2 = state.pop();
+ assert value1.isCategory1() && value2.isCategory1();
+ state.push(value1.type);
+ state.push(value2.type);
+ int tmp = state.push(value1.type);
+ builder.addMove(moveType(value1.type), tmp, value1.register);
+ builder.addMove(moveType(value2.type), value1.register, value2.register);
+ builder.addMove(moveType(value1.type), value2.register, tmp);
+ state.pop(); // Remove temp.
+ 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: {
+ Type type = opType(opcode);
+ NumericType numericType = numericType(type);
+ int right = state.pop(type).register;
+ int left = state.pop(type).register;
+ int dest = state.push(type);
+ if (opcode <= Opcodes.DADD) {
+ builder.addAdd(numericType, dest, left, right);
+ } else if (opcode <= Opcodes.DSUB) {
+ builder.addSub(numericType, dest, left, right);
+ } else if (opcode <= Opcodes.DMUL) {
+ builder.addMul(numericType, dest, left, right);
+ } else if (opcode <= Opcodes.DDIV) {
+ builder.addDiv(numericType, dest, left, right);
+ } else {
+ assert Opcodes.IREM <= opcode && opcode <= Opcodes.DREM;
+ builder.addRem(numericType, dest, left, right);
+ }
+ break;
+ }
+ case Opcodes.INEG:
+ case Opcodes.LNEG:
+ case Opcodes.FNEG:
+ case Opcodes.DNEG: {
+ Type type = opType(opcode);
+ NumericType numericType = numericType(type);
+ int value = state.pop(type).register;
+ int dest = state.push(type);
+ builder.addNeg(numericType, dest, value);
+ break;
+ }
+ case Opcodes.ISHL:
+ case Opcodes.LSHL:
+ case Opcodes.ISHR:
+ case Opcodes.LSHR:
+ case Opcodes.IUSHR:
+ case Opcodes.LUSHR: {
+ Type type = opType(opcode);
+ NumericType numericType = numericType(type);
+ int right = state.pop(Type.INT_TYPE).register;
+ int left = state.pop(type).register;
+ int dest = state.push(type);
+ if (opcode <= Opcodes.LSHL) {
+ builder.addShl(numericType, dest, left, right);
+ } else if (opcode <= Opcodes.LSHR) {
+ builder.addShr(numericType, dest, left, right);
+ } else {
+ assert opcode == Opcodes.IUSHR || opcode == Opcodes.LUSHR;
+ builder.addUshr(numericType, dest, left, right);
+ }
+ break;
+ }
+ case Opcodes.IAND:
+ case Opcodes.LAND: {
+ Type type = opcode == Opcodes.IAND ? Type.INT_TYPE : Type.LONG_TYPE;
+ int right = state.pop(type).register;
+ int left = state.pop(type).register;
+ int dest = state.push(type);
+ builder.addAnd(numericType(type), dest, left, right);
+ break;
+ }
+ case Opcodes.IOR:
+ case Opcodes.LOR: {
+ Type type = opcode == Opcodes.IOR ? Type.INT_TYPE : Type.LONG_TYPE;
+ int right = state.pop(type).register;
+ int left = state.pop(type).register;
+ int dest = state.push(type);
+ builder.addOr(numericType(type), dest, left, right);
+ break;
+ }
+ case Opcodes.IXOR:
+ case Opcodes.LXOR: {
+ Type type = opcode == Opcodes.IXOR ? Type.INT_TYPE : Type.LONG_TYPE;
+ int right = state.pop(type).register;
+ int left = state.pop(type).register;
+ int dest = state.push(type);
+ builder.addXor(numericType(type), dest, left, right);
+ break;
+ }
+ case Opcodes.I2L:
+ buildConversion(Type.INT_TYPE, Type.LONG_TYPE, builder);
+ break;
+ case Opcodes.I2F:
+ buildConversion(Type.INT_TYPE, Type.FLOAT_TYPE, builder);
+ break;
+ case Opcodes.I2D:
+ buildConversion(Type.INT_TYPE, Type.DOUBLE_TYPE, builder);
+ break;
+ case Opcodes.L2I:
+ buildConversion(Type.LONG_TYPE, Type.INT_TYPE, builder);
+ break;
+ case Opcodes.L2F:
+ buildConversion(Type.LONG_TYPE, Type.FLOAT_TYPE, builder);
+ break;
+ case Opcodes.L2D:
+ buildConversion(Type.LONG_TYPE, Type.DOUBLE_TYPE, builder);
+ break;
+ case Opcodes.F2I:
+ buildConversion(Type.FLOAT_TYPE, Type.INT_TYPE, builder);
+ break;
+ case Opcodes.F2L:
+ buildConversion(Type.FLOAT_TYPE, Type.LONG_TYPE, builder);
+ break;
+ case Opcodes.F2D:
+ buildConversion(Type.FLOAT_TYPE, Type.DOUBLE_TYPE, builder);
+ break;
+ case Opcodes.D2I:
+ buildConversion(Type.DOUBLE_TYPE, Type.INT_TYPE, builder);
+ break;
+ case Opcodes.D2L:
+ buildConversion(Type.DOUBLE_TYPE, Type.LONG_TYPE, builder);
+ break;
+ case Opcodes.D2F:
+ buildConversion(Type.DOUBLE_TYPE, Type.FLOAT_TYPE, builder);
+ break;
+ case Opcodes.I2B:
+ buildConversion(Type.INT_TYPE, Type.BYTE_TYPE, builder);
+ break;
+ case Opcodes.I2C:
+ buildConversion(Type.INT_TYPE, Type.CHAR_TYPE, builder);
+ break;
+ case Opcodes.I2S:
+ buildConversion(Type.INT_TYPE, Type.SHORT_TYPE, builder);
+ break;
+ case Opcodes.LCMP: {
+ Slot right = state.pop(Type.LONG_TYPE);
+ Slot left = state.pop(Type.LONG_TYPE);
+ int dest = state.push(Type.INT_TYPE);
+ builder.addCmp(NumericType.LONG, Bias.NONE, dest, left.register, right.register);
+ break;
+ }
+ case Opcodes.FCMPL:
+ case Opcodes.FCMPG: {
+ Slot right = state.pop(Type.FLOAT_TYPE);
+ Slot left = state.pop(Type.FLOAT_TYPE);
+ int dest = state.push(Type.INT_TYPE);
+ Bias bias = opcode == Opcodes.FCMPL ? Bias.LT : Bias.GT;
+ builder.addCmp(NumericType.FLOAT, bias, dest, left.register, right.register);
+ break;
+ }
+ case Opcodes.DCMPL:
+ case Opcodes.DCMPG: {
+ Slot right = state.pop(Type.DOUBLE_TYPE);
+ Slot left = state.pop(Type.DOUBLE_TYPE);
+ int dest = state.push(Type.INT_TYPE);
+ Bias bias = opcode == Opcodes.DCMPL ? Bias.LT : Bias.GT;
+ builder.addCmp(NumericType.DOUBLE, bias, dest, left.register, right.register);
+ break;
+ }
+ case Opcodes.IRETURN: {
+ Slot value = state.pop(Type.INT_TYPE);
+ addReturn(insn, MoveType.SINGLE, value.register, builder);
+ break;
+ }
+ case Opcodes.LRETURN: {
+ Slot value = state.pop(Type.LONG_TYPE);
+ addReturn(insn, MoveType.WIDE, value.register, builder);
+ break;
+ }
+ case Opcodes.FRETURN: {
+ Slot value = state.pop(Type.FLOAT_TYPE);
+ addReturn(insn, MoveType.SINGLE, value.register, builder);
+ break;
+ }
+ case Opcodes.DRETURN: {
+ Slot value = state.pop(Type.DOUBLE_TYPE);
+ addReturn(insn, MoveType.WIDE, value.register, builder);
+ break;
+ }
+ case Opcodes.ARETURN: {
+ Slot obj = state.pop(JarState.REFERENCE_TYPE);
+ addReturn(insn, MoveType.OBJECT, obj.register, builder);
+ break;
+ }
+ case Opcodes.RETURN: {
+ addReturn(insn, null, -1, builder);
+ break;
+ }
+ case Opcodes.ARRAYLENGTH: {
+ Slot array = state.pop(JarState.ARRAY_TYPE);
+ int dest = state.push(Type.INT_TYPE);
+ builder.addArrayLength(dest, array.register);
+ break;
+ }
+ case Opcodes.ATHROW: {
+ Slot object = state.pop(JarState.OBJECT_TYPE);
+ addThrow(insn, object.register, builder);
+ break;
+ }
+ case Opcodes.MONITORENTER: {
+ Slot object = state.pop(JarState.REFERENCE_TYPE);
+ builder.addMonitor(Monitor.Type.ENTER, object.register);
+ break;
+ }
+ case Opcodes.MONITOREXIT: {
+ Slot object = state.pop(JarState.REFERENCE_TYPE);
+ builder.addMonitor(Monitor.Type.EXIT, object.register);
+ break;
+ }
+ default:
+ throw new Unreachable("Unexpected Insn opcode: " + insn.getOpcode());
+ }
+ }
+
+ private void addThrow(InsnNode insn, int register, IRBuilder builder) {
+ if (getTryHandlers(insn).isEmpty()) {
+ processLocalVariablesAtExit(insn, builder);
+ } else {
+ processLocalVariablesAtControlEdge(insn, builder);
+ }
+ builder.addThrow(register);
+ state.invalidateState();
+ }
+
+ private void addReturn(InsnNode insn, MoveType type, int register, IRBuilder builder) {
+ processLocalVariablesAtExit(insn, builder);
+ if (type == null) {
+ assert register == -1;
+ builder.addReturn();
+ } else {
+ builder.addReturn(type, register);
+ }
+ state.invalidateState();
+ }
+
+ private void dupOneBelowTwo(Slot value3, Slot value2, Slot value1, IRBuilder builder) {
+ int stack3 = state.push(value1.type);
+ int stack2 = state.push(value3.type);
+ int stack1 = state.push(value2.type);
+ int stack0 = state.push(value1.type);
+ assert value3.register == stack3;
+ assert value2.register == stack2;
+ assert value1.register == stack1;
+ builder.addMove(moveType(value1.type), stack0, stack1);
+ builder.addMove(moveType(value2.type), stack1, stack2);
+ builder.addMove(moveType(value3.type), stack2, stack3);
+ builder.addMove(moveType(value1.type), stack3, stack0);
+ }
+
+ private void dupOneBelowOne(Slot value2, Slot value1, IRBuilder builder) {
+ int stack2 = state.push(value1.type);
+ int stack1 = state.push(value2.type);
+ int stack0 = state.push(value1.type);
+ assert value2.register == stack2;
+ assert value1.register == stack1;
+ builder.addMove(moveType(value1.type), stack0, stack1);
+ builder.addMove(moveType(value2.type), stack1, stack2);
+ builder.addMove(moveType(value1.type), stack2, stack0);
+ }
+
+ private void dupTwoBelowOne(Slot value3, Slot value2, Slot value1, IRBuilder builder) {
+ int stack4 = state.push(value2.type);
+ int stack3 = state.push(value1.type);
+ int stack2 = state.push(value3.type);
+ int stack1 = state.push(value2.type);
+ int stack0 = state.push(value1.type);
+ assert value3.register == stack4;
+ assert value2.register == stack3;
+ assert value1.register == stack2;
+ builder.addMove(moveType(value1.type), stack0, stack2);
+ builder.addMove(moveType(value2.type), stack1, stack3);
+ builder.addMove(moveType(value3.type), stack2, stack4);
+ builder.addMove(moveType(value1.type), stack3, stack0);
+ builder.addMove(moveType(value2.type), stack4, stack1);
+ }
+
+ private void dupTwoBelowTwo(Slot value4, Slot value3, Slot value2, Slot value1,
+ IRBuilder builder) {
+ int stack5 = state.push(value2.type);
+ int stack4 = state.push(value1.type);
+ int stack3 = state.push(value4.type);
+ int stack2 = state.push(value3.type);
+ int stack1 = state.push(value2.type);
+ int stack0 = state.push(value1.type);
+ assert value4.register == stack5;
+ assert value3.register == stack4;
+ assert value2.register == stack3;
+ assert value1.register == stack2;
+ builder.addMove(moveType(value1.type), stack0, stack2);
+ builder.addMove(moveType(value2.type), stack1, stack3);
+ builder.addMove(moveType(value3.type), stack2, stack4);
+ builder.addMove(moveType(value3.type), stack3, stack5);
+ builder.addMove(moveType(value1.type), stack4, stack0);
+ builder.addMove(moveType(value2.type), stack5, stack1);
+ }
+
+ private void buildConversion(Type from, Type to, IRBuilder builder) {
+ int source = state.pop(from).register;
+ int dest = state.push(to);
+ builder.addConversion(numericType(to), numericType(from), dest, source);
+ }
+
+ private void build(IntInsnNode insn, IRBuilder builder) {
+ switch (insn.getOpcode()) {
+ case Opcodes.BIPUSH:
+ case Opcodes.SIPUSH: {
+ int dest = state.push(Type.INT_TYPE);
+ builder.addIntConst(dest, insn.operand);
+ break;
+ }
+ case Opcodes.NEWARRAY: {
+ String desc = arrayTypeDesc(insn.operand);
+ Type type = Type.getType(desc);
+ DexType dexType = application.getTypeFromDescriptor(desc);
+ int count = state.pop(Type.INT_TYPE).register;
+ int array = state.push(type);
+ builder.addNewArrayEmpty(array, count, dexType);
+ break;
+ }
+ default:
+ throw new Unreachable("Unexpected IntInsn opcode: " + insn.getOpcode());
+ }
+ }
+
+ private void build(VarInsnNode insn, IRBuilder builder) {
+ int opcode = insn.getOpcode();
+ Type expectedType;
+ switch (opcode) {
+ case Opcodes.ILOAD:
+ case Opcodes.ISTORE:
+ expectedType = Type.INT_TYPE;
+ break;
+ case Opcodes.FLOAD:
+ case Opcodes.FSTORE:
+ expectedType = Type.FLOAT_TYPE;
+ break;
+ case Opcodes.LLOAD:
+ case Opcodes.LSTORE:
+ expectedType = Type.LONG_TYPE;
+ break;
+ case Opcodes.DLOAD:
+ case Opcodes.DSTORE:
+ expectedType = Type.DOUBLE_TYPE;
+ break;
+ case Opcodes.ALOAD:
+ case Opcodes.ASTORE:
+ expectedType = JarState.REFERENCE_TYPE;
+ break;
+ case Opcodes.RET: {
+ throw new Unreachable("RET should be handled by the ASM jsr inliner");
+ }
+ default:
+ throw new Unreachable("Unexpected VarInsn opcode: " + insn.getOpcode());
+ }
+ if (Opcodes.ILOAD <= opcode && opcode <= Opcodes.ALOAD) {
+ Slot src = state.readLocal(insn.var, expectedType);
+ int dest = state.push(src.type);
+ builder.addMove(moveType(src.type), dest, src.register);
+ } else {
+ assert Opcodes.ISTORE <= opcode && opcode <= Opcodes.ASTORE;
+ Slot src = state.pop(expectedType);
+ int dest = state.writeLocal(insn.var, src.type);
+ builder.addMove(moveType(src.type), dest, src.register);
+ }
+ }
+
+ private void build(TypeInsnNode insn, IRBuilder builder) {
+ Type type = Type.getObjectType(insn.desc);
+ switch (insn.getOpcode()) {
+ case Opcodes.NEW: {
+ DexType dexType = application.getTypeFromName(insn.desc);
+ int dest = state.push(type);
+ builder.addNewInstance(dest, dexType);
+ break;
+ }
+ case Opcodes.ANEWARRAY: {
+ Type arrayType = makeArrayType(type);
+ DexType dexArrayType = application.getTypeFromDescriptor(arrayType.getDescriptor());
+ int count = state.pop(Type.INT_TYPE).register;
+ int dest = state.push(arrayType);
+ builder.addNewArrayEmpty(dest, count, dexArrayType);
+ break;
+ }
+ case Opcodes.CHECKCAST: {
+ DexType dexType = application.getTypeFromDescriptor(type.getDescriptor());
+ // Pop the top value and push it back on with the checked type.
+ state.pop(type);
+ int object = state.push(type);
+ builder.addCheckCast(object, dexType);
+ break;
+ }
+ case Opcodes.INSTANCEOF: {
+ int obj = state.pop(JarState.REFERENCE_TYPE).register;
+ int dest = state.push(Type.INT_TYPE);
+ DexType dexType = application.getTypeFromDescriptor(type.getDescriptor());
+ builder.addInstanceOf(dest, obj, dexType);
+ break;
+ }
+ default:
+ throw new Unreachable("Unexpected TypeInsn opcode: " + insn.getOpcode());
+ }
+ }
+
+ private void build(FieldInsnNode insn, IRBuilder builder) {
+ DexField field = application.getField(insn.owner, insn.name, insn.desc);
+ Type type = Type.getType(insn.desc);
+ MemberType fieldType = memberType(insn.desc);
+ switch (insn.getOpcode()) {
+ case Opcodes.GETSTATIC:
+ builder.addStaticGet(fieldType, state.push(type), field);
+ break;
+ case Opcodes.PUTSTATIC:
+ builder.addStaticPut(fieldType, state.pop(type).register, field);
+ break;
+ case Opcodes.GETFIELD: {
+ Slot object = state.pop(JarState.OBJECT_TYPE);
+ int dest = state.push(type);
+ builder.addInstanceGet(fieldType, dest, object.register, field);
+ break;
+ }
+ case Opcodes.PUTFIELD: {
+ Slot value = state.pop(type);
+ Slot object = state.pop(JarState.OBJECT_TYPE);
+ builder.addInstancePut(fieldType, value.register, object.register, field);
+ break;
+ }
+ default:
+ throw new Unreachable("Unexpected FieldInsn opcode: " + insn.getOpcode());
+ }
+ }
+
+ private void build(MethodInsnNode insn, IRBuilder builder) {
+ // Resolve the target method of the invoke.
+ DexMethod method = application.getMethod(insn.owner, insn.name, insn.desc);
+
+ buildInvoke(
+ insn.desc,
+ Type.getObjectType(insn.owner),
+ insn.getOpcode() != Opcodes.INVOKESTATIC,
+ builder,
+ (types, registers) -> {
+ Invoke.Type invokeType = invokeType(insn);
+ DexProto callSiteProto = null;
+ DexMethod targetMethod = method;
+ if (invokeType == Invoke.Type.POLYMORPHIC) {
+ targetMethod = application.getMethod(
+ insn.owner, insn.name, METHODHANDLE_INVOKE_OR_INVOKEEXACT_DESC);
+ callSiteProto = application.getProto(insn.desc);
+ }
+ builder.addInvoke(invokeType, targetMethod, callSiteProto, types, registers);
+ });
+ }
+
+ private void buildInvoke(
+ String methodDesc, Type methodOwner, boolean addImplicitReceiver,
+ IRBuilder builder, BiConsumer<List<MoveType>, List<Integer>> creator) {
+
+ // Build the argument list of the form [owner, param1, ..., paramN].
+ // The arguments are in reverse order on the stack, so we pop off the parameters here.
+ Type[] parameterTypes = Type.getArgumentTypes(methodDesc);
+ Slot[] parameterRegisters = state.popReverse(parameterTypes.length);
+
+ List<MoveType> types = new ArrayList<>(parameterTypes.length + 1);
+ List<Integer> registers = new ArrayList<>(parameterTypes.length + 1);
+
+ // Add receiver argument for non-static calls.
+ if (addImplicitReceiver) {
+ addArgument(types, registers, methodOwner, state.pop());
+ }
+
+ // The remaining arguments are the parameters of the method.
+ for (int i = 0; i < parameterTypes.length; i++) {
+ addArgument(types, registers, parameterTypes[i], parameterRegisters[i]);
+ }
+
+ // Create the invoke.
+ creator.accept(types, registers);
+
+ // Move the result to the "top of stack".
+ Type returnType = Type.getReturnType(methodDesc);
+ if (returnType != Type.VOID_TYPE) {
+ builder.addMoveResult(moveType(returnType), state.push(returnType));
+ }
+ }
+
+ private static void addArgument(List<MoveType> types, List<Integer> registers, Type type,
+ Slot slot) {
+ assert slot.isCompatibleWith(type);
+ types.add(moveType(type));
+ registers.add(slot.register);
+ }
+
+ private void build(InvokeDynamicInsnNode insn, IRBuilder builder) {
+ // Bootstrap method
+ Handle bsmHandle = insn.bsm;
+ if (bsmHandle.getTag() != Opcodes.H_INVOKESTATIC &&
+ bsmHandle.getTag() != Opcodes.H_NEWINVOKESPECIAL) {
+ throw new Unreachable(
+ "Bootstrap handle is not yet supported: tag == " + bsmHandle.getTag());
+ }
+ // Resolve the bootstrap method.
+ DexMethodHandle bootstrapMethod = getMethodHandle(application, bsmHandle);
+
+ // Decode static bootstrap arguments
+ List<DexValue> bootstrapArgs = new ArrayList<>();
+ for (Object arg : insn.bsmArgs) {
+ bootstrapArgs.add(decodeBootstrapArgument(arg));
+ }
+
+ // Construct call site
+ DexCallSite callSite = application
+ .getCallSite(insn.name, insn.desc, bootstrapMethod, bootstrapArgs);
+
+ buildInvoke(insn.desc, null /* Not needed */,
+ false /* Receiver is passed explicitly */, builder,
+ (types, registers) -> builder.addInvokeCustom(callSite, types, registers));
+ }
+
+ private DexValue decodeBootstrapArgument(Object value) {
+ if (value instanceof Integer) {
+ return DexValue.DexValueInt.create((Integer) value);
+ } else if (value instanceof Long) {
+ return DexValue.DexValueLong.create((Long) value);
+ } else if (value instanceof Float) {
+ return DexValue.DexValueFloat.create((Float) value);
+ } else if (value instanceof Double) {
+ return DexValue.DexValueDouble.create((Double) value);
+ } else if (value instanceof String) {
+ return new DexValue.DexValueString(application.getString((String) value));
+
+ } else if (value instanceof Type) {
+ Type type = (Type) value;
+ switch (type.getSort()) {
+ case Type.OBJECT:
+ return new DexValue.DexValueType(
+ application.getTypeFromDescriptor(((Type) value).getDescriptor()));
+ case Type.METHOD:
+ return new DexValue.DexValueMethodType(
+ application.getProto(((Type) value).getDescriptor()));
+ }
+ throw new Unreachable("Type sort is not supported: " + type.getSort());
+
+ } else if (value instanceof Handle) {
+ return new DexValue.DexValueMethodHandle(getMethodHandle(application, (Handle) value));
+ } else {
+ throw new Unreachable(
+ "Unsupported bootstrap static argument of type " + value.getClass().getSimpleName());
+ }
+ }
+
+ private DexMethodHandle getMethodHandle(JarApplicationReader application, Handle handle) {
+ MethodHandleType methodHandleType = getMethodHandleType(handle);
+ Descriptor<? extends DexItem, ? extends Descriptor> descriptor =
+ methodHandleType.isFieldType()
+ ? application.getField(handle.getOwner(), handle.getName(), handle.getDesc())
+ : application.getMethod(handle.getOwner(), handle.getName(), handle.getDesc());
+ return application.getMethodHandle(methodHandleType, descriptor);
+ }
+
+ private MethodHandleType getMethodHandleType(Handle handle) {
+ switch (handle.getTag()) {
+ case Opcodes.H_GETFIELD:
+ return MethodHandleType.INSTANCE_GET;
+ case Opcodes.H_GETSTATIC:
+ return MethodHandleType.STATIC_GET;
+ case Opcodes.H_PUTFIELD:
+ return MethodHandleType.INSTANCE_PUT;
+ case Opcodes.H_PUTSTATIC:
+ return MethodHandleType.STATIC_PUT;
+ case Opcodes.H_INVOKESPECIAL:
+ DexType owner = application.getTypeFromName(handle.getOwner());
+ if (owner == clazz || handle.getName().equals(Constants.INSTANCE_INITIALIZER_NAME)) {
+ return MethodHandleType.INVOKE_INSTANCE;
+ } else {
+ return MethodHandleType.INVOKE_SUPER;
+ }
+ case Opcodes.H_INVOKEVIRTUAL:
+ return MethodHandleType.INVOKE_INSTANCE;
+ case Opcodes.H_INVOKEINTERFACE:
+ return MethodHandleType.INVOKE_INTERFACE;
+ case Opcodes.H_INVOKESTATIC:
+ return MethodHandleType.INVOKE_STATIC;
+ case Opcodes.H_NEWINVOKESPECIAL:
+ return MethodHandleType.INVOKE_CONSTRUCTOR;
+ default:
+ throw new Unreachable("MethodHandle tag is not supported: " + handle.getTag());
+ }
+ }
+
+ private void build(JumpInsnNode insn, IRBuilder builder) {
+ processLocalVariablesAtControlEdge(insn, builder);
+ int[] targets = getTargets(insn);
+ int opcode = insn.getOpcode();
+ if (Opcodes.IFEQ <= opcode && opcode <= Opcodes.IF_ACMPNE) {
+ assert targets.length == 2;
+ if (opcode <= Opcodes.IFLE) {
+ Slot value = state.pop(Type.INT_TYPE);
+ builder.addIfZero(ifType(opcode), value.register, targets[0], targets[1]);
+ } else {
+ Type expectedType = opcode < Opcodes.IF_ACMPEQ ? Type.INT_TYPE : JarState.REFERENCE_TYPE;
+ Slot value2 = state.pop(expectedType);
+ Slot value1 = state.pop(expectedType);
+ builder.addIf(ifType(opcode), value1.register, value2.register, targets[0], targets[1]);
+ }
+ } else {
+ switch (opcode) {
+ case Opcodes.GOTO: {
+ assert targets.length == 1;
+ builder.addGoto(targets[0]);
+ break;
+ }
+ case Opcodes.IFNULL:
+ case Opcodes.IFNONNULL: {
+ Slot value = state.pop(JarState.REFERENCE_TYPE);
+ If.Type type = opcode == Opcodes.IFNULL ? If.Type.EQ : If.Type.NE;
+ builder.addIfZero(type, value.register, targets[0], targets[1]);
+ break;
+ }
+ case Opcodes.JSR: {
+ throw new Unreachable("JSR should be handled by the ASM jsr inliner");
+ }
+ default:
+ throw new Unreachable("Unexpected JumpInsn opcode: " + insn.getOpcode());
+ }
+ }
+ for (int target : targets) {
+ state.recordStateForTarget(target, this);
+ }
+ state.invalidateState();
+ }
+
+ private void build(LabelNode insn, IRBuilder builder) {
+ // Open the scope of locals starting at this point.
+ if (insn != initialLabel) {
+ for (Local local : state.openLocals(insn)) {
+ builder.addDebugLocalStart(local.slot.register, local.info);
+ }
+ }
+ // Processing local-variable ends is done before closing potential control-flow edges.
+ // Record the state for all the try-catch handlers that are active at this label.
+ for (TryCatchBlock tryCatchBlock : getTryHandlers(insn)) {
+ state.recordStateForExceptionalTarget(tryCatchBlock.getHandler(), this);
+ }
+ }
+
+ private void build(LdcInsnNode insn, IRBuilder builder) {
+ if (insn.cst instanceof Type) {
+ Type type = (Type) insn.cst;
+ int dest = state.push(type);
+ builder.addConstClass(dest, application.getTypeFromDescriptor(type.getDescriptor()));
+ } else if (insn.cst instanceof String) {
+ int dest = state.push(STRING_TYPE);
+ builder.addConstString(dest, application.getString((String) insn.cst));
+ } else if (insn.cst instanceof Long) {
+ int dest = state.push(Type.LONG_TYPE);
+ builder.addLongConst(dest, (Long) insn.cst);
+ } else if (insn.cst instanceof Double) {
+ int dest = state.push(Type.DOUBLE_TYPE);
+ builder.addDoubleConst(dest, Double.doubleToRawLongBits((Double) insn.cst));
+ } else if (insn.cst instanceof Integer) {
+ int dest = state.push(Type.INT_TYPE);
+ builder.addIntConst(dest, (Integer) insn.cst);
+ } else {
+ assert insn.cst instanceof Float;
+ int dest = state.push(Type.FLOAT_TYPE);
+ builder.addFloatConst(dest, Float.floatToRawIntBits((Float) insn.cst));
+ }
+ }
+
+ private void build(IincInsnNode insn, IRBuilder builder) {
+ int local = state.readLocal(insn.var, Type.INT_TYPE).register;
+ builder.addAddLiteral(NumericType.INT, local, local, insn.incr);
+ }
+
+ private void build(TableSwitchInsnNode insn, IRBuilder builder) {
+ processLocalVariablesAtControlEdge(insn, builder);
+ buildSwitch(Switch.Type.PACKED, insn.dflt, insn.labels, new int[]{insn.min}, builder);
+ }
+
+ private void build(LookupSwitchInsnNode insn, IRBuilder builder) {
+ int[] keys = new int[insn.keys.size()];
+ for (int i = 0; i < insn.keys.size(); i++) {
+ keys[i] = (int) insn.keys.get(i);
+ }
+ buildSwitch(Switch.Type.SPARSE, insn.dflt, insn.labels, keys, builder);
+ }
+
+ private void buildSwitch(Switch.Type type, LabelNode dflt, List labels, int[] keys,
+ IRBuilder builder) {
+ int index = state.pop(Type.INT_TYPE).register;
+ int fallthroughOffset = getOffset(dflt);
+ state.recordStateForTarget(fallthroughOffset, this);
+ int[] labelOffsets = new int[labels.size()];
+ for (int i = 0; i < labels.size(); i++) {
+ int offset = getOffset((LabelNode) labels.get(i));
+ labelOffsets[i] = offset;
+ state.recordStateForTarget(offset, this);
+ }
+ builder.addSwitch(type, index, keys, fallthroughOffset, labelOffsets);
+ state.invalidateState();
+ }
+
+ private void build(MultiANewArrayInsnNode insn, IRBuilder builder) {
+ // Type of the full array.
+ Type arrayType = Type.getObjectType(insn.desc);
+ DexType dexArrayType = application.getType(arrayType);
+ // Type of the members. Can itself be of array type, eg, 'int[]' for 'new int[x][y][]'
+ DexType memberType = application.getTypeFromDescriptor(insn.desc.substring(insn.dims));
+ // Push an array containing the dimensions of the desired multi-dimensional array.
+ DexType dimArrayType = application.getTypeFromDescriptor(INT_ARRAY_DESC);
+ Slot[] slots = state.popReverse(insn.dims, Type.INT_TYPE);
+ int[] dimensions = new int[insn.dims];
+ for (int i = 0; i < insn.dims; i++) {
+ dimensions[i] = slots[i].register;
+ }
+ builder.addInvokeNewArray(dimArrayType, insn.dims, dimensions);
+ int dimensionsDestTemp = state.push(INT_ARRAY_TYPE);
+ builder.addMoveResult(MoveType.OBJECT, dimensionsDestTemp);
+ // Push the class object for the member type of the array.
+ int classDestTemp = state.push(CLASS_TYPE);
+ builder.ensureBlockForThrowingInstruction();
+ builder.addConstClass(classDestTemp, memberType);
+ // Create the actual multi-dimensional array using java.lang.reflect.Array::newInstance
+ DexType reflectArrayClass = application.getTypeFromDescriptor(REFLECT_ARRAY_DESC);
+ DexMethod newInstance = application.getMethod(reflectArrayClass,
+ REFLECT_ARRAY_NEW_INSTANCE_NAME, REFLECT_ARRAY_NEW_INSTANCE_DESC);
+ List<MoveType> argumentTypes = Arrays.asList(moveType(CLASS_TYPE), moveType(INT_ARRAY_TYPE));
+ List<Integer> argumentRegisters = Arrays.asList(classDestTemp, dimensionsDestTemp);
+ builder.ensureBlockForThrowingInstruction();
+ builder.addInvoke(Invoke.Type.STATIC, newInstance, null, argumentTypes, argumentRegisters);
+ // Pop the temporaries and push the final result.
+ state.pop(); // classDestTemp.
+ state.pop(); // dimensionsDestTemp.
+ int result = state.push(arrayType);
+ builder.addMoveResult(moveType(arrayType), result);
+ // Insert cast check to satisfy verification.
+ builder.ensureBlockForThrowingInstruction();
+ builder.addCheckCast(result, dexArrayType);
+ }
+
+ private void build(LineNumberNode insn, IRBuilder builder) {
+ builder.updateCurrentDebugPosition(insn.line, null);
+ }
+
+ // Printing helpers.
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("node.name = [").append(node.name).append("]");
+ builder.append("\n");
+ builder.append("node.desc = ").append(node.desc);
+ builder.append("\n");
+ builder.append("node.maxStack = ").append(node.maxStack);
+ builder.append("\n");
+ builder.append("node.maxLocals = ").append(node.maxLocals);
+ builder.append("\n");
+ builder.append("node.locals.size = ").append(node.localVariables.size());
+ builder.append("\n");
+ builder.append("node.insns.size = ").append(node.instructions.size());
+ builder.append("\n");
+ for (int i = 0; i < parameterTypes.size(); i++) {
+ builder.append("arg ").append(i).append(", type: ").append(parameterTypes.get(i))
+ .append("\n");
+ }
+ for (int i = 0; i < node.localVariables.size(); i++) {
+ LocalVariableNode local = (LocalVariableNode) node.localVariables.get(i);
+ builder.append("local ").append(i)
+ .append(", name: ").append(local.name)
+ .append(", desc: ").append(local.desc)
+ .append(", index: ").append(local.index)
+ .append(", [").append(getOffset(local.start))
+ .append("..").append(getOffset(local.end))
+ .append("[\n");
+ }
+ for (int i = 0; i < node.tryCatchBlocks.size(); i++) {
+ TryCatchBlockNode tryCatchBlockNode = (TryCatchBlockNode) node.tryCatchBlocks.get(i);
+ builder.append("[").append(getOffset(tryCatchBlockNode.start))
+ .append("..").append(getOffset(tryCatchBlockNode.end)).append("[ ")
+ .append(tryCatchBlockNode.type).append(" -> ")
+ .append(getOffset(tryCatchBlockNode.handler))
+ .append("\n");
+ }
+ for (int i = 0; i < node.instructions.size(); i++) {
+ AbstractInsnNode insn = node.instructions.get(i);
+ builder.append(String.format("%4d: ", i)).append(instructionToString(insn));
+ }
+ return builder.toString();
+ }
+
+ private String instructionToString(AbstractInsnNode insn) {
+ if (printVisitor == null) {
+ printVisitor = new TraceMethodVisitor(new Textifier());
+ }
+ insn.accept(printVisitor);
+ StringWriter writer = new StringWriter();
+ printVisitor.p.print(new PrintWriter(writer));
+ printVisitor.p.getText().clear();
+ return writer.toString();
+ }
+
+ private boolean isCallToPolymorphicSignatureMethod(MethodInsnNode method) {
+ return method.owner.equals("java/lang/invoke/MethodHandle")
+ && (method.name.equals("invoke") || method.name.equals("invokeExact"));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
new file mode 100644
index 0000000..e45fcb2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
@@ -0,0 +1,488 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.conversion;
+
+import static com.android.tools.r8.ir.conversion.JarSourceCode.getArrayElementType;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.LocalVariableNode;
+
+/**
+ * Abstraction of the java bytecode state at a given control-flow point.
+ *
+ * The abstract state is defined by the abstract contents of locals and contents of the stack.
+ */
+public class JarState {
+
+ // Type representatives of "any object/array" using invalid names (only valid for asserting).
+ public static final Type REFERENCE_TYPE = Type.getObjectType("<any reference>");
+ public static final Type OBJECT_TYPE = Type.getObjectType("<any object>");
+ public static final Type ARRAY_TYPE = Type.getObjectType("[<any array>");
+
+ // Type representative for the null value (non-existent but works for tracking the types here).
+ public static final Type NULL_TYPE = Type.getObjectType("<null>");
+
+ // Type representative for an address type (used by JSR/RET).
+ public static final Type ADDR_TYPE = Type.getObjectType("<address>");
+
+ private static final Slot INVALID_SLOT = new Slot(-1, null);
+ private static final Local INVALID_LOCAL = new Local(INVALID_SLOT, null);
+
+ public boolean isInvalid() {
+ return stack.peek() == INVALID_SLOT;
+ }
+
+ // Typed mapping from a local slot or stack slot to a virtual register.
+ public static class Slot {
+ public final int register;
+ public final Type type;
+
+ @Override
+ public String toString() {
+ return this == INVALID_SLOT ? "<invalid slot>" : "r" + register + ":" + type;
+ }
+
+ public Slot(int register, Type type) {
+ assert type != REFERENCE_TYPE;
+ assert type != OBJECT_TYPE;
+ assert type != ARRAY_TYPE;
+ this.register = register;
+ this.type = type;
+ }
+
+ public boolean isCompatibleWith(Type other) {
+ return isCompatible(type, other);
+ }
+
+ public boolean isCategory1() {
+ return isCategory1(type);
+ }
+
+ public static boolean isCategory1(Type type) {
+ return type != Type.LONG_TYPE && type != Type.DOUBLE_TYPE;
+ }
+
+ public static boolean isCompatible(Type type, Type other) {
+ assert type != REFERENCE_TYPE;
+ assert type != OBJECT_TYPE;
+ assert type != ARRAY_TYPE;
+ int sort = type.getSort();
+ int otherSort = other.getSort();
+ if (isReferenceCompatible(type, other)) {
+ return true;
+ }
+ // Integers are assumed compatible with any other 32-bit integral.
+ if (isIntCompatible(sort)) {
+ return isIntCompatible(otherSort);
+ }
+ if (isIntCompatible(otherSort)) {
+ return isIntCompatible(sort);
+ }
+ // In all other cases we require the two types to represent the same concrete type.
+ return type.equals(other);
+ }
+
+ private static boolean isIntCompatible(int sort) {
+ return Type.BOOLEAN <= sort && sort <= Type.INT;
+ }
+
+ private static boolean isReferenceCompatible(Type type, Type other) {
+ int sort = type.getSort();
+ int otherSort = other.getSort();
+
+ // Catch all matching.
+ if (other == REFERENCE_TYPE) {
+ return sort == Type.OBJECT || sort == Type.ARRAY;
+ }
+ if (other == OBJECT_TYPE) {
+ return sort == Type.OBJECT;
+ }
+ if (other == ARRAY_TYPE) {
+ return type == NULL_TYPE || sort == Type.ARRAY;
+ }
+
+ return (sort == Type.OBJECT && otherSort == Type.ARRAY)
+ || (sort == Type.ARRAY && otherSort == Type.OBJECT)
+ || (sort == Type.OBJECT && otherSort == Type.OBJECT)
+ || (sort == Type.ARRAY && otherSort == Type.ARRAY
+ && isReferenceCompatible(getArrayElementType(type), getArrayElementType(other)));
+ }
+ }
+
+ public static class Local {
+ final Slot slot;
+ final DebugLocalInfo info;
+
+ public Local(Slot slot, DebugLocalInfo info) {
+ this.slot = slot;
+ this.info = info;
+ }
+ }
+
+ // Immutable recording of the state (locals and stack should not be mutated).
+ private static class Snapshot {
+ public final Local[] locals;
+ public final ImmutableList<Slot> stack;
+
+ public Snapshot(Local[] locals, ImmutableList<Slot> stack) {
+ this.locals = locals;
+ this.stack = stack;
+ }
+
+ @Override
+ public String toString() {
+ return "locals: " + localsToString(Arrays.asList(locals))
+ + ", stack: " + stackToString(stack);
+ }
+ }
+
+ private final int startOfStack;
+ private int topOfStack;
+
+ // Locals are split into three parts based on types:
+ // 1) reference-type locals have registers in range: [0; localsSize[
+ // 2) single-width locals have registers in range: [localsSize; 2*localsSize[
+ // 3) wide-width locals have registers in range: [2*localsSize; 3*localsSize[
+ // This ensures that we can insert debugging-ranges into the SSA graph (via DebugLocal{Start,End})
+ // without conflating locals that are shared among different types. This issue arises because a
+ // debugging range can be larger than the definite-assignment scope of a local (eg, a local
+ // introduced in an unscoped switch case). To ensure that the SSA graph is valid we must introduce
+ // the local before inserting any DebugLocalReads (we do so in the method prelude, but that can
+ // potentially lead to phi functions merging locals of different move-types. Thus we allocate
+ // registers from the three distinct spaces.
+ private final int localsSize;
+ private final Local[] locals;
+
+ // Mapping from local-variable nodes to their canonical local info.
+ private final Map<LocalVariableNode, DebugLocalInfo> localVariables;
+
+ // Scope-points of all local variables for inserting debug scoping instructions.
+ private final Multimap<LabelNode, LocalVariableNode> localVariableStartPoints;
+ private final Multimap<LabelNode, LocalVariableNode> localVariableEndPoints;
+
+
+ private final Deque<Slot> stack = new ArrayDeque<>();
+
+ private final Map<Integer, Snapshot> targetStates = new HashMap<>();
+
+
+ public JarState(int maxLocals, Map<LocalVariableNode, DebugLocalInfo> localVariables) {
+ int localsRegistersSize = maxLocals * 3;
+ localsSize = maxLocals;
+ locals = new Local[localsRegistersSize];
+ startOfStack = localsRegistersSize;
+ topOfStack = startOfStack;
+ this.localVariables = localVariables;
+ localVariableStartPoints = HashMultimap.create();
+ localVariableEndPoints = HashMultimap.create();
+ populateLocalTables();
+ }
+
+ private void populateLocalTables() {
+ for (LocalVariableNode node : localVariables.keySet()) {
+ if (node.start != node.end) {
+ localVariableStartPoints.put(node.start, node);
+ localVariableEndPoints.put(node.end, node);
+ }
+ }
+ }
+
+ // Local variable procedures.
+
+ public List<Local> openLocals(LabelNode label) {
+ Collection<LocalVariableNode> nodes = localVariableStartPoints.get(label);
+ ArrayList<Local> locals = new ArrayList<>(nodes.size());
+ for (LocalVariableNode node : nodes) {
+ locals.add(setLocal(node.index, Type.getType(node.desc), localVariables.get(node)));
+ }
+ // Sort to ensure deterministic instruction ordering (correctness is unaffected).
+ locals.sort(Comparator.comparingInt(local -> local.slot.register));
+ return locals;
+ }
+
+ public List<Local> getLocalsToClose(LabelNode label) {
+ Collection<LocalVariableNode> nodes = localVariableEndPoints.get(label);
+ ArrayList<Local> locals = new ArrayList<>(nodes.size());
+ for (LocalVariableNode node : nodes) {
+ Type type = Type.getType(node.desc);
+ int register = getLocalRegister(node.index, type);
+ Local local = getLocalForRegister(register);
+ assert local != null;
+ locals.add(local);
+ }
+ // Sort to ensure deterministic instruction ordering (correctness is unaffected).
+ locals.sort(Comparator.comparingInt(local -> local.slot.register));
+ return locals;
+ }
+
+ public void closeLocals(List<Local> localsToClose) {
+ for (Local local : localsToClose) {
+ assert local != null;
+ assert local == getLocalForRegister(local.slot.register);
+ setLocalForRegister(local.slot.register, local.slot.type, null);
+ }
+ }
+
+ public ImmutableList<Local> getLocals() {
+ ImmutableList.Builder<Local> nonNullLocals = ImmutableList.builder();
+ for (Local local : locals) {
+ if (local != null) {
+ nonNullLocals.add(local);
+ }
+ }
+ return nonNullLocals.build();
+ }
+
+ int getLocalRegister(int index, Type type) {
+ assert index < localsSize;
+ if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
+ return index;
+ }
+ return Slot.isCategory1(type) ? index + localsSize : index + 2 * localsSize;
+ }
+
+ public DebugLocalInfo getLocalInfoForRegister(int register) {
+ if (register >= locals.length) {
+ return null;
+ }
+ Local local = getLocalForRegister(register);
+ return local == null ? null : local.info;
+ }
+
+ private Local getLocalForRegister(int register) {
+ return locals[register];
+ }
+
+ private Local getLocal(int index, Type type) {
+ return getLocalForRegister(getLocalRegister(index, type));
+ }
+
+ private Local setLocal(int index, Type type, DebugLocalInfo info) {
+ return setLocalForRegister(getLocalRegister(index, type), type, info);
+ }
+
+ private Local setLocalForRegister(int register, Type type, DebugLocalInfo info) {
+ Slot slot = new Slot(register, type);
+ Local local = new Local(slot, info);
+ locals[register] = local;
+ return local;
+ }
+
+ public int writeLocal(int index, Type type) {
+ assert type != null;
+ Local local = getLocal(index, type);
+ assert local == null || local.info == null || local.slot.isCompatibleWith(type);
+ // We cannot assume consistency for writes because we do not have complete information about the
+ // scopes of locals. We assume the program to be verified and overwrite if the types mismatch.
+ if (local == null || (local.info == null && !local.slot.type.equals(type))) {
+ local = setLocal(index, type, null);
+ }
+ return local.slot.register;
+ }
+
+ public Slot readLocal(int index, Type type) {
+ Local local = getLocal(index, type);
+ assert local != null;
+ assert local.slot.isCompatibleWith(type);
+ return local.slot;
+ }
+
+ // Stack procedures.
+
+ public int push(Type type) {
+ assert type != null;
+ int top = topOfStack;
+ // For simplicity, every stack slot (and local variable) is wide (uses two registers).
+ topOfStack += 2;
+ Slot slot = new Slot(top, type);
+ stack.push(slot);
+ return top;
+ }
+
+ public Slot peek() {
+ return stack.peek();
+ }
+
+ public Slot peek(Type type) {
+ Slot slot = stack.peek();
+ assert slot.isCompatibleWith(type);
+ return slot;
+ }
+
+ public Slot pop() {
+ assert topOfStack > startOfStack;
+ // For simplicity, every stack slot (and local variable) is wide (uses two registers).
+ topOfStack -= 2;
+ Slot slot = stack.pop();
+ assert slot.type != null;
+ assert slot.register == topOfStack;
+ return slot;
+ }
+
+ public Slot pop(Type type) {
+ Slot slot = pop();
+ assert slot.isCompatibleWith(type);
+ return slot;
+ }
+
+ public Slot[] popReverse(int count) {
+ Slot[] slots = new Slot[count];
+ for (int i = count - 1; i >= 0; i--) {
+ slots[i] = pop();
+ }
+ return slots;
+ }
+
+ public Slot[] popReverse(int count, Type type) {
+ Slot[] slots = popReverse(count);
+ assert verifySlots(slots, type);
+ return slots;
+ }
+
+ // State procedures.
+
+ public void invalidateState() {
+ for (int i = 0; i < locals.length; i++) {
+ locals[i] = INVALID_LOCAL;
+ }
+ stack.clear();
+ stack.push(INVALID_SLOT);
+ topOfStack = -1;
+ }
+
+ public boolean hasState(int offset) {
+ return targetStates.get(offset) != null;
+ }
+
+ public void restoreState(int offset) {
+ Snapshot snapshot = targetStates.get(offset);
+ assert snapshot != null;
+ assert locals.length == snapshot.locals.length;
+ for (int i = 0; i < locals.length; i++) {
+ assert locals[i] == INVALID_LOCAL;
+ locals[i] = snapshot.locals[i];
+ }
+ assert stack.peek() == INVALID_SLOT;
+ assert topOfStack == -1;
+ stack.clear();
+ stack.addAll(snapshot.stack);
+ topOfStack = startOfStack + 2 * stack.size();
+ }
+
+ public void recordStateForTarget(int target, JarSourceCode source) {
+ recordStateForTarget(target, locals.clone(), ImmutableList.copyOf(stack), source);
+ }
+
+ public void recordStateForExceptionalTarget(int target, JarSourceCode source) {
+ recordStateForTarget(target, locals.clone(), ImmutableList.of(), source);
+ }
+
+ private void recordStateForTarget(int target, Local[] locals, ImmutableList<Slot> stack,
+ JarSourceCode source) {
+ Snapshot snapshot = targetStates.get(target);
+ if (snapshot == null) {
+ if (!localVariables.isEmpty()) {
+ for (int i = 0; i < locals.length; i++) {
+ if (locals[i] != null) {
+ locals[i] = new Local(locals[i].slot, null);
+ }
+ }
+ // TODO(zerny): Precompute and sort the local ranges.
+ for (LocalVariableNode node : localVariables.keySet()) {
+ int startOffset = source.getOffset(node.start);
+ int endOffset = source.getOffset(node.end);
+ if (startOffset <= target && target < endOffset) {
+ int register = getLocalRegister(node.index, Type.getType(node.desc));
+ Local local = locals[register];
+ locals[register] = new Local(local.slot, localVariables.get(node));
+ }
+ }
+ }
+ targetStates.put(target, new Snapshot(locals, stack));
+ return;
+ }
+ assert verifyStack(stack, snapshot.stack);
+ }
+
+ private static boolean verifyStack(List<Slot> stack, List<Slot> other) {
+ assert stack.size() == other.size();
+ int i = 0;
+ for (Slot slot : stack) {
+ assert slot.isCompatibleWith(other.get(i++).type);
+ }
+ return true;
+ }
+
+ // Other helpers.
+
+ private static boolean verifySlots(Slot[] slots, Type type) {
+ for (Slot slot : slots) {
+ assert slot.isCompatibleWith(type);
+ }
+ return true;
+ }
+
+ // Printing helpers.
+
+ public String toString() {
+ return "locals: " + localsToString(Arrays.asList(locals)) + ", stack: " + stackToString(stack);
+ }
+
+ public static String stackToString(Collection<Slot> stack) {
+ List<String> strings = new ArrayList<>(stack.size());
+ for (Slot slot : stack) {
+ if (slot == INVALID_SLOT) {
+ return "<stack invalidated>";
+ }
+ strings.add(slot.type.toString());
+ }
+ StringBuilder builder = new StringBuilder("{ ");
+ for (int i = strings.size() - 1; i >= 0; i--) {
+ builder.append(strings.get(i));
+ if (i > 0) {
+ builder.append(", ");
+ }
+ }
+ builder.append(" }");
+ return builder.toString();
+ }
+
+ public static String localsToString(Collection<Local> locals) {
+ StringBuilder builder = new StringBuilder("{ ");
+ boolean first = true;
+ for (Local local : locals) {
+ if (local == INVALID_LOCAL) {
+ return "<locals invalidated>";
+ }
+ if (!first) {
+ builder.append(", ");
+ } else {
+ first = false;
+ }
+ if (local == null) {
+ builder.append("_");
+ } else if (local.info != null) {
+ builder.append(local.info);
+ } else {
+ builder.append(local.slot.type.toString());
+ }
+ }
+ builder.append(" }");
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
new file mode 100644
index 0000000..13507ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -0,0 +1,194 @@
+// 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.ir.conversion;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.CheckCast;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.InvokeCustom;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Value;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.stream.Collectors;
+
+public class LensCodeRewriter {
+
+ private final GraphLense graphLense;
+ private final AppInfoWithSubtyping appInfo;
+
+ public LensCodeRewriter(GraphLense graphLense, AppInfoWithSubtyping appInfo) {
+ this.graphLense = graphLense;
+ this.appInfo = appInfo;
+ }
+
+ /**
+ * Replace invoke targets and field accesses with actual definitions.
+ */
+ public void rewrite(IRCode code, DexEncodedMethod method) {
+ ListIterator<BasicBlock> blocks = code.blocks.listIterator();
+ while (blocks.hasNext()) {
+ BasicBlock block = blocks.next();
+ InstructionListIterator iterator = block.listIterator();
+ while (iterator.hasNext()) {
+ Instruction current = iterator.next();
+ if (current.isInvokeCustom()) {
+ InvokeCustom invokeCustom = current.asInvokeCustom();
+ DexCallSite callSite = invokeCustom.getCallSite();
+ DexMethodHandle newBootstrapMethod = rewriteDexMethodHandle(method,
+ callSite.bootstrapMethod);
+ List<DexValue> newArgs = callSite.bootstrapArgs.stream().map(
+ (arg) -> {
+ if (arg instanceof DexValueMethodHandle) {
+ return new DexValueMethodHandle(
+ rewriteDexMethodHandle(method, ((DexValueMethodHandle) arg).value));
+ }
+ return arg;
+ })
+ .collect(Collectors.toList());
+
+ if (newBootstrapMethod != callSite.bootstrapMethod
+ || !newArgs.equals(callSite.bootstrapArgs)) {
+ DexCallSite newCallSite = appInfo.dexItemFactory.createCallSite(
+ callSite.methodName, callSite.methodProto, newBootstrapMethod, newArgs);
+ InvokeCustom newInvokeCustom = new InvokeCustom(newCallSite, invokeCustom.outValue(),
+ invokeCustom.inValues());
+ iterator.replaceCurrentInstruction(newInvokeCustom);
+ }
+ } else if (current.isInvokeMethod()) {
+ InvokeMethod invoke = current.asInvokeMethod();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ DexType invokedHolder = invokedMethod.getHolder();
+ if (!invokedHolder.isClassType()) {
+ continue;
+ }
+ DexMethod actualTarget = graphLense.lookupMethod(invokedMethod, method);
+ if (actualTarget != invokedMethod) {
+ Invoke newInvoke = Invoke
+ .create(getInvokeType(invoke, actualTarget), actualTarget, null,
+ invoke.outValue(), invoke.inValues());
+ iterator.replaceCurrentInstruction(newInvoke);
+ // Fix up the return type if needed.
+ if (actualTarget.proto.returnType != invokedMethod.proto.returnType
+ && newInvoke.outValue() != null) {
+ Value newValue = new Value(
+ code.valueNumberGenerator.next(), -1, newInvoke.outType(), invoke.getDebugInfo());
+ newInvoke.outValue().replaceUsers(newValue);
+ CheckCast cast = new CheckCast(
+ newValue,
+ newInvoke.outValue(),
+ invokedMethod.proto.returnType);
+ iterator.add(cast);
+ // If the current block has catch handlers split the check cast into its own block.
+ if (newInvoke.getBlock().hasCatchHandlers()) {
+ iterator.previous();
+ iterator.split(1, code, blocks);
+ }
+ }
+ }
+ } else if (current.isInstanceGet()) {
+ InstanceGet instanceGet = current.asInstanceGet();
+ DexField field = instanceGet.getField();
+ DexField actualField = graphLense.lookupField(field, method);
+ if (actualField != field) {
+ InstanceGet newInstanceGet =
+ new InstanceGet(
+ instanceGet.getType(), instanceGet.dest(), instanceGet.object(), actualField);
+ iterator.replaceCurrentInstruction(newInstanceGet);
+ }
+ } else if (current.isInstancePut()) {
+ InstancePut instancePut = current.asInstancePut();
+ DexField field = instancePut.getField();
+ DexField actualField = graphLense.lookupField(field, method);
+ if (actualField != field) {
+ InstancePut newInstancePut =
+ new InstancePut(instancePut.getType(), instancePut.inValues(), actualField);
+ iterator.replaceCurrentInstruction(newInstancePut);
+ }
+ } else if (current.isStaticGet()) {
+ StaticGet staticGet = current.asStaticGet();
+ DexField field = staticGet.getField();
+ DexField actualField = graphLense.lookupField(field, method);
+ if (actualField != field) {
+ StaticGet newStaticGet =
+ new StaticGet(staticGet.getType(), staticGet.dest(), actualField);
+ iterator.replaceCurrentInstruction(newStaticGet);
+ }
+ } else if (current.isStaticPut()) {
+ StaticPut staticPut = current.asStaticPut();
+ DexField field = staticPut.getField();
+ DexField actualField = graphLense.lookupField(field, method);
+ if (actualField != field) {
+ StaticPut newStaticPut =
+ new StaticPut(staticPut.getType(), staticPut.inValue(), actualField);
+ iterator.replaceCurrentInstruction(newStaticPut);
+ }
+ }
+ }
+ }
+ assert code.isConsistentSSA();
+ }
+
+ private DexMethodHandle rewriteDexMethodHandle(
+ DexEncodedMethod method, DexMethodHandle methodHandle) {
+ if (methodHandle.isMethodHandle()) {
+ DexMethod invokedMethod = methodHandle.asMethod();
+ DexMethod actualTarget = graphLense.lookupMethod(invokedMethod, method);
+ if (actualTarget != invokedMethod) {
+ DexClass clazz = appInfo.definitionFor(actualTarget.holder);
+ MethodHandleType newType = methodHandle.type;
+ if (clazz != null
+ && (newType.isInvokeInterface() || newType.isInvokeInstance())) {
+ newType = clazz.accessFlags.isInterface()
+ ? MethodHandleType.INVOKE_INTERFACE
+ : MethodHandleType.INVOKE_INSTANCE;
+ }
+ return new DexMethodHandle(newType, actualTarget);
+ }
+ } else {
+ DexField field = methodHandle.asField();
+ DexField actualField = graphLense.lookupField(field, method);
+ if (actualField != field) {
+ return new DexMethodHandle(methodHandle.type, actualField);
+ }
+ }
+ return methodHandle;
+ }
+
+ private Type getInvokeType(InvokeMethod invoke, DexMethod actualTarget) {
+ if (invoke.isInvokeVirtual() || invoke.isInvokeInterface()) {
+ // Get the invoke type of the actual definition.
+ DexClass clazz = appInfo.definitionFor(actualTarget.holder);
+ if (clazz == null) {
+ return invoke.getType();
+ } else {
+ return clazz.accessFlags.isInterface()
+ ? Type.INTERFACE
+ : Type.VIRTUAL;
+ }
+ } else {
+ return invoke.getType();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
new file mode 100644
index 0000000..ba03f82
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.Switch.Type;
+
+/**
+ * Abstraction of the input/source code for the IRBuilder.
+ *
+ * Implementations of the abstraction need to compute/provide the block-structure of the source and
+ * delegate building of the actual instruction stream.
+ */
+public interface SourceCode {
+
+ // Accessors.
+ int instructionCount();
+ int instructionIndex(int instructionOffset);
+ int instructionOffset(int instructionIndex);
+
+ // True if the method needs a special entry block (prelude) that is not a targetable block.
+ // This is the case for methods with arguments or that need additional synchronization support.
+ boolean needsPrelude();
+
+ DebugLocalInfo getCurrentLocal(int register);
+
+ /**
+ * Trace block structure of the source-program.
+ *
+ * <p>The instruction at {@code index} is traced and its target blocks are marked by using
+ * {@code IRBuilder.ensureSuccessorBlock} (and {@code ensureBlockWithoutEnqueuing}).
+ *
+ * @return True if the instruction closes the current block, false otherwise.
+ */
+ boolean traceInstruction(int instructionIndex, IRBuilder builder);
+
+ void closedCurrentBlockWithFallthrough(int fallthroughInstructionIndex);
+ void closedCurrentBlock();
+
+ // Setup and release resources used temporarily during trace/build.
+ void setUp();
+ void clear();
+
+ // Delegates for IR building.
+ void buildPrelude(IRBuilder builder);
+ void buildInstruction(IRBuilder builder, int instructionIndex);
+ void buildPostlude(IRBuilder builder);
+
+ // Helper to resolve switch payloads and build switch instructions (dex code only).
+ void resolveAndBuildSwitch(Type type, int value, int fallthroughOffset, int payloadOffset,
+ IRBuilder builder);
+
+ // Helper to resolve fill-array data and build new-array instructions (dex code only).
+ void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset, IRBuilder builder);
+
+ CatchHandlers<Integer> getCurrentCatchHandlers();
+
+ // For debugging/verification purpose.
+ boolean verifyRegister(int register);
+ boolean verifyCurrentInstructionCanThrow();
+ boolean verifyLocalInScope(DebugLocalInfo local);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/SwitchPayloadResolver.java b/src/main/java/com/android/tools/r8/ir/conversion/SwitchPayloadResolver.java
new file mode 100644
index 0000000..5367a3a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/SwitchPayloadResolver.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.SwitchPayload;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class for resolving payload information during IR construction.
+ */
+public class SwitchPayloadResolver {
+
+ public static class PayloadData {
+
+ public final static int NO_SIZE = -1;
+ public int userOffset;
+ public int fallthroughOffset;
+ public int[] absoluteTargets = null;
+ public int[] keys = null;
+ public int size = NO_SIZE;
+
+ public PayloadData(int userOffset, int fallthroughOffset) {
+ this.userOffset = userOffset;
+ this.fallthroughOffset = fallthroughOffset;
+ }
+ }
+
+ private final Map<Integer, SwitchPayload> unresolvedPayload = new HashMap<>();
+ private final Map<Integer, PayloadData> payloadToData = new HashMap<>();
+
+ public void addPayloadUser(Instruction dex) {
+ int offset = dex.getOffset();
+ int payloadOffset = offset + dex.getPayloadOffset();
+ payloadToData.put(payloadOffset, new PayloadData(offset, offset + dex.getSize()));
+ if (unresolvedPayload.containsKey(payloadOffset)) {
+ SwitchPayload payload = unresolvedPayload.remove(payloadOffset);
+ resolve(payload);
+ }
+ }
+
+ public void resolve(SwitchPayload payload) {
+ int payloadOffset = payload.getOffset();
+ PayloadData data = payloadToData.get(payloadOffset);
+ if (data == null) {
+ unresolvedPayload.put(payloadOffset, payload);
+ return;
+ }
+
+ int[] targets = payload.switchTargetOffsets();
+ int[] absoluteTargets = new int[targets.length];
+ for (int i = 0; i < targets.length; i++) {
+ absoluteTargets[i] = data.userOffset + targets[i];
+ }
+ data.absoluteTargets = absoluteTargets;
+ data.keys = payload.keys();
+ data.size = payload.numberOfKeys();
+ }
+
+ public int[] absoluteTargets(Instruction dex) {
+ assert dex.isSwitch();
+ return absoluteTargets(dex.getOffset() + dex.getPayloadOffset());
+ }
+
+ public int[] absoluteTargets(int payloadOffset) {
+ return payloadToData.get(payloadOffset).absoluteTargets;
+ }
+
+ public int[] getKeys(int payloadOffset) {
+ return payloadToData.get(payloadOffset).keys;
+ }
+
+ public int getSize(int payloadOffset) {
+ return payloadToData.get(payloadOffset).size;
+ }
+
+ public Collection<PayloadData> payloadDataSet() {
+ return payloadToData.values();
+ }
+
+ public void clear() {
+ payloadToData.clear();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
new file mode 100644
index 0000000..bee210c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -0,0 +1,124 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import java.util.ArrayList;
+import java.util.List;
+
+// Source code representing synthesized accessor method.
+final class AccessorMethodSourceCode extends SynthesizedLambdaSourceCode {
+ AccessorMethodSourceCode(LambdaClass lambda) {
+ super(/* no receiver for static method */ null, lambda, lambda.target.callTarget);
+ // We should never need an accessor for interface methods since
+ // they are supposed to be public.
+ assert !descriptor().implHandle.type.isInvokeInterface();
+ assert checkSignatures();
+ }
+
+ private boolean checkSignatures() {
+ DexMethodHandle implHandle = descriptor().implHandle;
+ assert implHandle != null;
+
+ DexType[] accessorParams = proto.parameters.values;
+ DexMethod implMethod = implHandle.asMethod();
+ DexProto implProto = implMethod.proto;
+ DexType[] implParams = implProto.parameters.values;
+
+ int index = 0;
+ if (implHandle.type.isInvokeInstance()) {
+ assert accessorParams[index] == descriptor().getImplReceiverType();
+ index++;
+ }
+
+ for (DexType implParam : implParams) {
+ assert accessorParams[index] == implParam;
+ index++;
+ }
+ assert index == accessorParams.length;
+
+ assert delegatingToConstructor()
+ ? this.proto.returnType == implMethod.holder
+ : this.proto.returnType == implProto.returnType;
+ return true;
+ }
+
+ private boolean isPrivateMethod() {
+ // We should be able to find targets for all private impl-methods, so
+ // we can rely on knowing accessibility flags for them.
+ DexAccessFlags flags = descriptor().getAccessibility();
+ return flags != null && flags.isPrivate();
+ }
+
+ // Are we delegating to a constructor?
+ private boolean delegatingToConstructor() {
+ return descriptor().implHandle.type.isInvokeConstructor();
+ }
+
+ private Invoke.Type inferInvokeType() {
+ switch (descriptor().implHandle.type) {
+ case INVOKE_INSTANCE:
+ return isPrivateMethod() ? Invoke.Type.DIRECT : Invoke.Type.VIRTUAL;
+ case INVOKE_STATIC:
+ return Invoke.Type.STATIC;
+ case INVOKE_CONSTRUCTOR:
+ return Invoke.Type.DIRECT;
+ case INVOKE_INTERFACE:
+ throw new Unreachable("Accessor for an interface method?");
+ default:
+ throw new Unreachable();
+ }
+ }
+
+ @Override
+ void prepareInstructions() {
+ DexMethod implMethod = descriptor().implHandle.asMethod();
+ DexType[] accessorParams = proto.parameters.values;
+
+ // Prepare call arguments.
+ List<MoveType> argMoveTypes = new ArrayList<>();
+ List<Integer> argRegisters = new ArrayList<>();
+
+ // If we are delegating to a constructor, we need to create the instance
+ // first. This instance will be the first argument to the call.
+ if (delegatingToConstructor()) {
+ int instance = nextRegister(MoveType.OBJECT);
+ add(builder -> builder.addNewInstance(instance, implMethod.holder));
+ argMoveTypes.add(MoveType.OBJECT);
+ argRegisters.add(instance);
+ }
+
+ for (int i = 0; i < accessorParams.length; i++) {
+ DexType param = accessorParams[i];
+ argMoveTypes.add(MoveType.fromDexType(param));
+ argRegisters.add(getParamRegister(i));
+ }
+
+ // Method call to the original impl-method.
+ add(builder -> builder.addInvoke(inferInvokeType(),
+ implMethod, implMethod.proto, argMoveTypes, argRegisters));
+
+ // Does the method have return value?
+ if (proto.returnType == factory().voidType) {
+ add(IRBuilder::addReturn);
+ } else if (delegatingToConstructor()) {
+ // Return newly created instance
+ add(builder -> builder.addReturn(MoveType.OBJECT, argRegisters.get(0)));
+ } else {
+ MoveType moveType = MoveType.fromDexType(proto.returnType);
+ int tempValue = nextRegister(moveType);
+ add(builder -> builder.addMoveResult(moveType, tempValue));
+ add(builder -> builder.addReturn(moveType, tempValue));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
new file mode 100644
index 0000000..ba15c9d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -0,0 +1,231 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Invoke;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+// Default and static method interface desugaring processor for classes.
+// Adds default interface methods into the class when needed.
+final class ClassProcessor {
+ private final InterfaceMethodRewriter rewriter;
+ // Set of already processed classes.
+ private final Set<DexClass> processedClasses = Sets.newIdentityHashSet();
+ // Maps already created methods into default methods they were generated based on.
+ private final Map<DexEncodedMethod, DexEncodedMethod> createdMethods = new IdentityHashMap<>();
+ // Caches default interface method info for already processed interfaces.
+ private final Map<DexType, DefaultMethodsHelper.Collection> cache = new IdentityHashMap<>();
+
+ ClassProcessor(InterfaceMethodRewriter rewriter) {
+ this.rewriter = rewriter;
+ }
+
+ final Set<DexEncodedMethod> getForwardMethods() {
+ return createdMethods.keySet();
+ }
+
+ final void process(DexClass clazz) {
+ if (clazz.isInterface()) {
+ throw new CompilationError("Interface in superclass chain.");
+ }
+ if (!clazz.isProgramClass()) {
+ // We assume that library classes don't need to be processed, since they
+ // are provided by a runtime not supporting default interface methods.
+ // We also skip classpath classes, which results in sub-optimal behavior
+ // in case classpath superclass when processed adds a default method which
+ // could have been reused in this class otherwise.
+ return;
+ }
+ if (!processedClasses.add(clazz)) {
+ return; // Has already been processed.
+ }
+
+ // Ensure superclasses are processed first. We need it since we use information
+ // about methods added to superclasses when we decide if we want to add a default
+ // method to class `clazz`.
+ DexType superType = clazz.superType;
+ if (superType != null && superType != rewriter.factory.objectType) {
+ process(rewriter.findRequiredClass(superType));
+ }
+
+ if (clazz.interfaces.isEmpty()) {
+ // Since superclass has already been processed and it has all missing methods
+ // added, these methods will be inherited by `clazz`, and only need to be revised
+ // in case this class has *additional* interfaces implemented, which may change
+ // the entire picture of the default method selection in runtime.
+ return;
+ }
+
+ // Collect the default interface methods to be added to this class.
+ List<DexEncodedMethod> methodsToImplement = collectMethodsToImplement(clazz);
+ if (methodsToImplement.isEmpty()) {
+ return;
+ }
+
+ // Add the methods.
+ DexEncodedMethod[] existing = clazz.virtualMethods;
+ clazz.virtualMethods = new DexEncodedMethod[existing.length + methodsToImplement.size()];
+ System.arraycopy(existing, 0, clazz.virtualMethods, 0, existing.length);
+
+ for (int i = 0; i < methodsToImplement.size(); i++) {
+ DexEncodedMethod method = methodsToImplement.get(i);
+ assert method.accessFlags.isPublic() && !method.accessFlags.isAbstract();
+ DexEncodedMethod newMethod = addForwardingMethod(method, clazz);
+ clazz.virtualMethods[existing.length + i] = newMethod;
+ createdMethods.put(newMethod, method);
+ }
+ }
+
+ private DexEncodedMethod addForwardingMethod(DexEncodedMethod defaultMethod, DexClass clazz) {
+ DexMethod method = defaultMethod.method;
+ assert !rewriter.findRequiredClass(method.holder).isLibraryClass();
+ // New method will have the same name, proto, and also all the flags of the
+ // default method, including bridge flag.
+ DexMethod newMethod = rewriter.factory.createMethod(clazz.type, method.proto, method.name);
+ DexAccessFlags newFlags = new DexAccessFlags(defaultMethod.accessFlags.get());
+ return new DexEncodedMethod(newMethod, newFlags,
+ defaultMethod.annotations, defaultMethod.parameterAnnotations,
+ new SynthesizedCode(new ForwardMethodSourceCode(
+ clazz.type, method.proto, /* static method */ null,
+ rewriter.defaultAsMethodOfCompanionClass(method),
+ Invoke.Type.STATIC)));
+ }
+
+ // For a given class `clazz` inspects all interfaces it implements directly or
+ // indirectly and collect a set of all default methods to be implemented
+ // in this class.
+ private List<DexEncodedMethod> collectMethodsToImplement(DexClass clazz) {
+ DefaultMethodsHelper helper = new DefaultMethodsHelper();
+
+ // Collect candidate default methods by inspecting interfaces implemented
+ // by this class as well as its superclasses.
+ DexClass current = clazz;
+ while (true) {
+ for (DexType type : clazz.interfaces.values) {
+ helper.merge(getOrCreateInterfaceInfo(type));
+ }
+
+ DexType superType = current.superType;
+ if (superType == null || superType == rewriter.factory.objectType) {
+ // We assume here that interfaces implemented by java.lang.Object don't
+ // have default methods and don't hide any default interface methods since
+ // they must be library interfaces.
+ break;
+ }
+ current = rewriter.findRequiredClass(superType);
+ }
+
+ List<DexEncodedMethod> candidates = helper.createCandidatesList();
+ if (candidates.isEmpty()) {
+ return candidates;
+ }
+
+ // Remove from candidates methods defined in class or any of its superclasses.
+ List<DexEncodedMethod> toBeImplemented = new ArrayList<>(candidates.size());
+ current = clazz;
+ while (true) {
+ // Hide candidates by virtual method of the class.
+ hideCandidates(clazz.virtualMethods, candidates, toBeImplemented);
+ if (candidates.isEmpty()) {
+ return toBeImplemented;
+ }
+
+ DexType superType = current.superType;
+ if (superType == null || superType == rewriter.factory.objectType) {
+ // Note that default interface methods must never have same
+ // name/signature as any method in java.lang.Object (JLS §9.4.1.2).
+
+ // Everything still in candidate list is not hidden.
+ toBeImplemented.addAll(candidates);
+ return toBeImplemented;
+ }
+ current = rewriter.findRequiredClass(superType);
+ }
+ }
+
+ private void hideCandidates(DexEncodedMethod[] virtualMethods,
+ List<DexEncodedMethod> candidates, List<DexEncodedMethod> toBeImplemented) {
+ Iterator<DexEncodedMethod> it = candidates.iterator();
+ while (it.hasNext()) {
+ DexEncodedMethod candidate = it.next();
+ for (DexEncodedMethod encoded : virtualMethods) {
+ if (candidate.method.match(encoded)) {
+ // Found a methods hiding the candidate.
+ DexEncodedMethod basedOnCandidate = createdMethods.get(encoded);
+ if (basedOnCandidate != null) {
+ // The method we found is a method we have generated for a default interface
+ // method in a superclass. If the method is based on the same candidate we don't
+ // need to re-generate this method again since it is going to be inherited.
+ if (basedOnCandidate != candidate) {
+ // Need to re-generate since the inherited version is
+ // based on a different candidate.
+ toBeImplemented.add(candidate);
+ }
+ }
+
+ // Done with this candidate.
+ it.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ private DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(DexType iface) {
+ DefaultMethodsHelper.Collection collection = cache.get(iface);
+ if (collection != null) {
+ return collection;
+ }
+ collection = createInterfaceInfo(iface);
+ cache.put(iface, collection);
+ return collection;
+ }
+
+ private DefaultMethodsHelper.Collection createInterfaceInfo(DexType iface) {
+ DefaultMethodsHelper helper = new DefaultMethodsHelper();
+ DexClass clazz = rewriter.findRequiredClass(iface);
+ if (!clazz.isInterface()) {
+ throw new CompilationError(
+ "Type " + iface.toSourceString() + " is expected to be an interface.");
+ }
+ if (clazz.isLibraryClass()) {
+ // For library interfaces we always assume there are no default
+ // methods, since the interface is part of framework provided by
+ // runtime which does not support default interface methods.
+ return DefaultMethodsHelper.Collection.EMPTY;
+ }
+
+ // Merge information from all superinterfaces.
+ for (DexType superinterface : clazz.interfaces.values) {
+ helper.merge(getOrCreateInterfaceInfo(superinterface));
+ }
+
+ // Hide by virtual methods of this interface.
+ for (DexEncodedMethod virtual : clazz.virtualMethods) {
+ helper.hideMatches(virtual.method);
+ }
+
+ // Add all default methods of this interface.
+ for (DexEncodedMethod encoded : clazz.virtualMethods) {
+ if (rewriter.isDefaultMethod(encoded)) {
+ helper.addDefaultMethod(encoded);
+ }
+ }
+
+ return helper.wrapInCollection();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
new file mode 100644
index 0000000..2f25b19
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
@@ -0,0 +1,111 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+// Helper class implementing bunch of default interface method handling operations.
+final class DefaultMethodsHelper {
+ // Current set of default interface methods, may overlap with `hidden`.
+ private final Set<DexEncodedMethod> candidates = Sets.newIdentityHashSet();
+ // Current set of known hidden default interface methods.
+ private final Set<DexEncodedMethod> hidden = Sets.newIdentityHashSet();
+
+ // Represents information about default interface methods of an
+ // interface and its superinterfaces. Namely: a list of live (not hidden)
+ // and hidden default interface methods in this interface's hierarchy.
+ //
+ // Note that it is assumes that these lists should never be big.
+ final static class Collection {
+ static final Collection EMPTY =
+ new Collection(Collections.emptyList(), Collections.emptyList());
+
+ // All live default interface methods in this interface's hierarchy.
+ private final List<DexEncodedMethod> live;
+ // All hidden default interface methods in this interface's hierarchy.
+ private final List<DexEncodedMethod> hidden;
+
+ private Collection(List<DexEncodedMethod> live, List<DexEncodedMethod> hidden) {
+ this.live = live;
+ this.hidden = hidden;
+ }
+ }
+
+ final void merge(Collection collection) {
+ candidates.addAll(collection.live);
+ hidden.addAll(collection.hidden);
+ }
+
+ final void hideMatches(DexMethod method) {
+ Iterator<DexEncodedMethod> it = candidates.iterator();
+ while (it.hasNext()) {
+ DexEncodedMethod candidate = it.next();
+ if (method.match(candidate)) {
+ hidden.add(candidate);
+ it.remove();
+ }
+ }
+ }
+
+ final void addDefaultMethod(DexEncodedMethod encoded) {
+ candidates.add(encoded);
+ }
+
+ // Creates a list of default method candidates to be implemented in the class.
+ final List<DexEncodedMethod> createCandidatesList() {
+ this.candidates.removeAll(hidden);
+ if (this.candidates.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ // The list of non-hidden default methods. The list is not expected to be big,
+ // since it only consists of default methods which are maximally specific
+ // interface method of a particular class.
+ List<DexEncodedMethod> candidates = new LinkedList<>();
+
+ // Note that it is possible for a class to have more than one maximally specific
+ // interface method. But runtime requires that when a method is called, there must be
+ // found *only one* maximally specific interface method and this method should be
+ // non-abstract, otherwise a runtime error is generated.
+ //
+ // This code assumes that if such erroneous case exist for particular name/signature,
+ // a method with this name/signature must be defined in class or one of its superclasses,
+ // or otherwise it should never be called. This means that if we see two default method
+ // candidates with same name/signature, it is safe to assume that we don't need to add
+ // these method to the class, because if it was missing in class and its superclasses
+ // but still called in the original code, this call would have resulted in runtime error.
+ // So we are just leaving it unimplemented with the same effect (with a different runtime
+ // exception though).
+ for (DexEncodedMethod candidate : this.candidates) {
+ Iterator<DexEncodedMethod> it = candidates.iterator();
+ boolean conflict = false;
+ while (it.hasNext()) {
+ if (candidate.method.match(it.next())) {
+ conflict = true;
+ it.remove();
+ }
+ }
+ if (!conflict) {
+ candidates.add(candidate);
+ }
+ }
+ return candidates;
+ }
+
+ // Create default interface collection based on collected information.
+ final Collection wrapInCollection() {
+ candidates.removeAll(hidden);
+ return (candidates.isEmpty() && hidden.isEmpty()) ? Collection.EMPTY
+ : new Collection(Lists.newArrayList(candidates), Lists.newArrayList(hidden));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ForwardMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/ForwardMethodSourceCode.java
new file mode 100644
index 0000000..808456b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ForwardMethodSourceCode.java
@@ -0,0 +1,98 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.List;
+
+// Source code representing simple forwarding method.
+final class ForwardMethodSourceCode extends SingleBlockSourceCode {
+ private final DexType targetReceiver;
+ private final DexMethod target;
+ private final Invoke.Type invokeType;
+
+ ForwardMethodSourceCode(DexType receiver, DexProto proto,
+ DexType targetReceiver, DexMethod target, Invoke.Type invokeType) {
+ super(receiver, proto);
+ assert (targetReceiver == null) == (invokeType == Invoke.Type.STATIC);
+
+ this.target = target;
+ this.targetReceiver = targetReceiver;
+ this.invokeType = invokeType;
+ assert checkSignatures();
+
+ if (invokeType != Invoke.Type.STATIC) {
+ throw new Unimplemented("Invoke type " + invokeType + " is not yet supported.");
+ }
+ }
+
+ private boolean checkSignatures() {
+ List<DexType> sourceParams = new ArrayList<>();
+ if (receiver != null) {
+ sourceParams.add(receiver);
+ }
+ sourceParams.addAll(Lists.newArrayList(proto.parameters.values));
+
+ List<DexType> targetParams = new ArrayList<>();
+ if (targetReceiver != null) {
+ targetParams.add(targetReceiver);
+ }
+ targetParams.addAll(Lists.newArrayList(target.proto.parameters.values));
+
+ assert sourceParams.size() == targetParams.size();
+ for (int i = 0; i < sourceParams.size(); i++) {
+ DexType source = sourceParams.get(i);
+ DexType target = targetParams.get(i);
+
+ // We assume source is compatible with target if they both are classes.
+ // This check takes care of receiver widening conversion but does not
+ // many others, like conversion from an array to Object.
+ assert (source.isClassType() && target.isClassType()) || source == target;
+ }
+
+ assert this.proto.returnType == target.proto.returnType;
+ return true;
+ }
+
+ @Override
+ void prepareInstructions() {
+ // Prepare call arguments.
+ List<MoveType> argMoveTypes = new ArrayList<>();
+ List<Integer> argRegisters = new ArrayList<>();
+
+ if (receiver != null) {
+ argMoveTypes.add(MoveType.OBJECT);
+ argRegisters.add(getReceiverRegister());
+ }
+
+ DexType[] accessorParams = proto.parameters.values;
+ for (int i = 0; i < accessorParams.length; i++) {
+ argMoveTypes.add(MoveType.fromDexType(accessorParams[i]));
+ argRegisters.add(getParamRegister(i));
+ }
+
+ // Method call to the target method.
+ add(builder -> builder.addInvoke(this.invokeType,
+ this.target, this.target.proto, argMoveTypes, argRegisters));
+
+ // Does the method return value?
+ if (proto.returnType.isVoidType()) {
+ add(IRBuilder::addReturn);
+ } else {
+ MoveType moveType = MoveType.fromDexType(proto.returnType);
+ int tempValue = nextRegister(moveType);
+ add(builder -> builder.addMoveResult(moveType, tempValue));
+ add(builder -> builder.addReturn(moveType, tempValue));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
new file mode 100644
index 0000000..81576ec
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -0,0 +1,265 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassPromise;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.InvokeSuper;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+//
+// Default and static interface method desugaring rewriter (note that lambda
+// desugaring should have already processed the code before this rewriter).
+//
+// In short, during default and static interface method desugaring
+// the following actions are performed:
+//
+// (1) All static interface methods are moved into companion classes. All calls
+// to these methods are redirected appropriately. All references to these
+// methods from method handles are reported as errors.
+//
+// Companion class is a synthesized class (<interface-name>-CC) created to host
+// static and former default interface methods (see below) from the interface.
+//
+// (2) All default interface methods are made static and moved into companion
+// class.
+//
+// (3) All calls to default interface methods made via 'super' are changed
+// to directly call appropriate static methods in companion classes.
+//
+// (4) All other calls or references to default interface methods are not changed.
+//
+// (5) For all program classes explicitly implementing interfaces we analyze the
+// set of default interface methods missing and add them, the created methods
+// forward the call to an appropriate method in interface companion class.
+//
+public final class InterfaceMethodRewriter {
+ private static final String COMPANION_CLASS_NAME_SUFFIX = "-CC";
+ private static final String DEFAULT_METHOD_PREFIX = "$default$";
+
+ private final IRConverter converter;
+ final DexItemFactory factory;
+
+ // All forwarding methods generated during desugaring. We don't synchronize access
+ // to this collection since it is only filled in ClassProcessor running synchronously.
+ private final Set<DexEncodedMethod> forwardingMethods = Sets.newIdentityHashSet();
+
+ /** Defines a minor variation in desugaring. */
+ public enum Flavor {
+ /** Process all application resources. */
+ IncludeAllResources,
+ /** Process all but DEX application resources. */
+ ExcludeDexResources
+ }
+
+ public InterfaceMethodRewriter(IRConverter converter) {
+ assert converter != null;
+ this.converter = converter;
+ this.factory = converter.application.dexItemFactory;
+ }
+
+ // Rewrites the references to static and default interface methods.
+ // NOTE: can be called for different methods concurrently.
+ public void rewriteMethodReferences(DexEncodedMethod encodedMethod, IRCode code) {
+ if (forwardingMethods.contains(encodedMethod)) {
+ return;
+ }
+
+ ListIterator<BasicBlock> blocks = code.listIterator();
+ while (blocks.hasNext()) {
+ BasicBlock block = blocks.next();
+ InstructionListIterator instructions = block.listIterator();
+ while (instructions.hasNext()) {
+ Instruction instruction = instructions.next();
+
+ if (instruction.isInvokeCustom()) {
+ // Check that static interface methods are not referenced
+ // from invoke-custom instructions via method handles.
+ DexCallSite callSite = instruction.asInvokeCustom().getCallSite();
+ reportStaticInterfaceMethodHandle(callSite.bootstrapMethod);
+ for (DexValue arg : callSite.bootstrapArgs) {
+ if (arg instanceof DexValue.DexValueMethodHandle) {
+ reportStaticInterfaceMethodHandle(((DexValue.DexValueMethodHandle) arg).value);
+ }
+ }
+ continue;
+ }
+
+ if (instruction.isInvokeStatic()) {
+ InvokeStatic invokeStatic = instruction.asInvokeStatic();
+ DexMethod method = invokeStatic.getInvokedMethod();
+ if (isInterfaceClass(method.holder)) {
+ // Retarget call to an appropriate method of companion class.
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(staticAsMethodOfCompanionClass(method),
+ invokeStatic.outValue(), invokeStatic.arguments()));
+ }
+ continue;
+ }
+
+ if (instruction.isInvokeSuper()) {
+ InvokeSuper invokeSuper = instruction.asInvokeSuper();
+ DexMethod method = invokeSuper.getInvokedMethod();
+ if (isInterfaceClass(method.holder)) {
+ // Retarget call to an appropriate method of companion class.
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(defaultAsMethodOfCompanionClass(method),
+ invokeSuper.outValue(), invokeSuper.arguments()));
+ }
+ }
+ }
+ }
+ }
+
+ private void reportStaticInterfaceMethodHandle(DexMethodHandle handle) {
+ if (handle.type.isInvokeStatic() && isInterfaceClass(handle.asMethod().holder)) {
+ throw new Unimplemented(
+ "Desugaring of static interface method handle in is not yet supported.");
+ }
+ }
+
+ private boolean isInterfaceClass(DexType type) {
+ return findRequiredClass(type).isInterface();
+ }
+
+ // Returns the class for the type specified, report errors for missing classes.
+ final DexClass findRequiredClass(DexType type) {
+ DexClass clazz = converter.appInfo.definitionFor(type);
+ if (clazz != null) {
+ return clazz;
+ }
+ throw new CompilationError("Type '" + type.toSourceString() +
+ "' required for default and static interface methods desugaring not found.");
+ }
+
+ // Gets the companion class for the interface `type`.
+ final DexType getCompanionClassType(DexType type) {
+ assert type.isClassType();
+ String descriptor = type.descriptor.toString();
+ String ccTypeDescriptor = descriptor.substring(0, descriptor.length() - 1)
+ + COMPANION_CLASS_NAME_SUFFIX + ";";
+ return factory.createType(ccTypeDescriptor);
+ }
+
+ private boolean isInMainDexList(DexType iface) {
+ ImmutableSet<DexType> list = converter.application.mainDexList;
+ return list != null && list.contains(iface);
+ }
+
+ // Represent a static interface method as a method of companion class.
+ final DexMethod staticAsMethodOfCompanionClass(DexMethod method) {
+ // No changes for static methods.
+ return factory.createMethod(getCompanionClassType(method.holder), method.proto, method.name);
+ }
+
+ // Represent a default interface method as a method of companion class.
+ final DexMethod defaultAsMethodOfCompanionClass(DexMethod method) {
+ // Add an implicit argument to represent the receiver.
+ DexType[] params = method.proto.parameters.values;
+ DexType[] newParams = new DexType[params.length + 1];
+ newParams[0] = method.holder;
+ System.arraycopy(params, 0, newParams, 1, params.length);
+
+ // Add prefix to avoid name conflicts.
+ return factory.createMethod(getCompanionClassType(method.holder),
+ factory.createProto(method.proto.returnType, newParams),
+ factory.createString(DEFAULT_METHOD_PREFIX + method.name.toString()));
+ }
+
+ /**
+ * Move static and default interface methods to companion classes,
+ * add missing methods to forward to moved default methods implementation.
+ */
+ public void desugarInterfaceMethods(Builder builder, Flavor flavour) {
+ // Process all classes first. Add missing forwarding methods to
+ // replace desugared default interface methods.
+ forwardingMethods.addAll(processClasses(builder, flavour));
+
+ // Process interfaces, create companion class if needed, move static methods
+ // to companion class, copy default interface methods to companion classes,
+ // make original default methods abstract, remove bridge methods.
+ Map<DexProgramClass, DexProgramClass> companionClasses =
+ processInterfaces(builder, flavour);
+
+ for (Map.Entry<DexProgramClass, DexProgramClass> entry : companionClasses.entrySet()) {
+ // Don't need to optimize synthesized class since all of its methods
+ // are just moved from interfaces and don't need to be re-processed.
+ builder.addSynthesizedClass(entry.getValue(), isInMainDexList(entry.getKey().type));
+ }
+
+ for (DexEncodedMethod method : forwardingMethods) {
+ converter.optimizeSynthesizedMethod(method);
+ }
+ }
+
+ private static boolean shouldProcess(
+ DexClassPromise promise, Flavor flavour, boolean mustBeInterface) {
+ return promise.isProgramClass()
+ && (promise.getOrigin() != DexClass.Origin.Dex || flavour == Flavor.IncludeAllResources)
+ && promise.get().isInterface() == mustBeInterface;
+ }
+
+ private Map<DexProgramClass, DexProgramClass> processInterfaces(
+ Builder builder, Flavor flavour) {
+ InterfaceProcessor processor = new InterfaceProcessor(this);
+ for (DexClassPromise promise : builder.classMap.values()) {
+ if (shouldProcess(promise, flavour, true)) {
+ processor.process(promise.get().asProgramClass());
+ }
+ }
+ return processor.companionClasses;
+ }
+
+ private Set<DexEncodedMethod> processClasses(Builder builder, Flavor flavour) {
+ ClassProcessor processor = new ClassProcessor(this);
+ for (DexClassPromise promise : builder.classMap.values()) {
+ if (shouldProcess(promise, flavour, false)) {
+ processor.process(promise.get());
+ }
+ }
+ return processor.getForwardMethods();
+ }
+
+ final boolean isDefaultMethod(DexEncodedMethod method) {
+ assert !method.accessFlags.isConstructor();
+ assert !method.accessFlags.isStatic();
+
+ if (method.accessFlags.isAbstract()) {
+ return false;
+ }
+ if (method.accessFlags.isNative()) {
+ throw new Unimplemented("Native default interface methods are not yet supported.");
+ }
+ if (!method.accessFlags.isPublic()) {
+ // NOTE: even though the class is allowed to have non-public interface methods
+ // with code, for example private methods, all such methods we are aware of are
+ // created by the compiler for stateful lambdas and they must be converted into
+ // static methods by lambda desugaring by this time.
+ throw new Unimplemented("Non public default interface methods are not yet supported.");
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
new file mode 100644
index 0000000..772d74c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -0,0 +1,149 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+// Default and static method interface desugaring processor for interfaces.
+//
+// Makes default interface methods abstract, moves their implementation to
+// a companion class. Removes bridge default methods.
+//
+// Also moves static interface methods into a companion class.
+final class InterfaceProcessor {
+ private final InterfaceMethodRewriter rewriter;
+ // All created companion classes indexed by interface classes.
+ final Map<DexProgramClass, DexProgramClass> companionClasses = new IdentityHashMap<>();
+
+ InterfaceProcessor(InterfaceMethodRewriter rewriter) {
+ this.rewriter = rewriter;
+ }
+
+ void process(DexProgramClass iface) {
+ assert iface.isInterface();
+
+ // The list of methods to be created in companion class.
+ List<DexEncodedMethod> companionMethods = new ArrayList<>();
+
+ // Process virtual interface methods first.
+ List<DexEncodedMethod> remainingMethods = new ArrayList<>();
+ for (DexEncodedMethod virtual : iface.virtualMethods) {
+ if (rewriter.isDefaultMethod(virtual)) {
+ // Create a new method in a companion class to represent default method implementation.
+ DexMethod companionMethod = rewriter.defaultAsMethodOfCompanionClass(virtual.method);
+
+ Code code = virtual.getCode();
+ if (code == null) {
+ throw new CompilationError("Code is missing for default "
+ + "interface method: " + virtual.method.toSourceString());
+ }
+
+ DexAccessFlags newFlags = new DexAccessFlags(virtual.accessFlags.get());
+ newFlags.unsetBridge();
+ newFlags.setStatic();
+ DexCode dexCode = code.asDexCode();
+ // TODO(ager): Should we give the new first parameter an actual name? Maybe 'this'?
+ dexCode.setDebugInfo(dexCode.debugInfoWithAdditionalFirstParameter(null));
+ assert (dexCode.getDebugInfo() == null)
+ || (companionMethod.proto.parameters.values.length
+ == dexCode.getDebugInfo().parameters.length);
+
+ companionMethods.add(new DexEncodedMethod(companionMethod,
+ newFlags, virtual.annotations, virtual.parameterAnnotations, code));
+
+ // Make the method abstract.
+ virtual.accessFlags.setAbstract();
+ virtual.removeCode();
+ }
+
+ // Remove bridge methods.
+ if (!virtual.accessFlags.isBridge()) {
+ remainingMethods.add(virtual);
+ }
+ }
+
+ // If at least one bridge methods was removed update the table.
+ if (remainingMethods.size() < iface.virtualMethods.length) {
+ iface.virtualMethods = remainingMethods.toArray(
+ new DexEncodedMethod[remainingMethods.size()]);
+ }
+ remainingMethods.clear();
+
+ // Process static methods, move them into companion class as well.
+ for (DexEncodedMethod direct : iface.directMethods) {
+ if (direct.accessFlags.isPrivate()) {
+ // We only expect to see private methods which are lambda$ methods,
+ // and they are supposed to be relaxed to package private static methods
+ // by this time by lambda rewriter.
+ throw new Unimplemented("Private method are not yet supported.");
+ }
+
+ if (isStaticMethod(direct)) {
+ companionMethods.add(new DexEncodedMethod(
+ rewriter.staticAsMethodOfCompanionClass(direct.method), direct.accessFlags,
+ direct.annotations, direct.parameterAnnotations, direct.getCode()));
+ } else {
+ // Since there are no interface constructors at this point,
+ // this should only be class constructor.
+ assert rewriter.factory.isClassConstructor(direct.method);
+ remainingMethods.add(direct);
+ }
+ }
+ if (remainingMethods.size() < iface.directMethods.length) {
+ iface.directMethods = remainingMethods.toArray(
+ new DexEncodedMethod[remainingMethods.size()]);
+ }
+
+ if (companionMethods.isEmpty()) {
+ return; // No methods to create, companion class not needed.
+ }
+
+ DexAccessFlags companionClassFlags = new DexAccessFlags(iface.accessFlags.get());
+ companionClassFlags.unsetAbstract();
+ companionClassFlags.unsetInterface();
+ companionClassFlags.setFinal();
+ companionClassFlags.setSynthetic();
+
+ // Create companion class.
+ DexType companionClassType = rewriter.getCompanionClassType(iface.type);
+ DexProgramClass companionClass = new DexProgramClass(
+ companionClassType,
+ DexClass.Origin.Synthetic,
+ companionClassFlags,
+ rewriter.factory.objectType,
+ DexTypeList.empty(),
+ iface.sourceFile,
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedField.EMPTY_ARRAY,
+ companionMethods.toArray(new DexEncodedMethod[companionMethods.size()]),
+ DexEncodedMethod.EMPTY_ARRAY
+ );
+ companionClasses.put(iface, companionClass);
+ }
+
+ private boolean isStaticMethod(DexEncodedMethod method) {
+ if (method.accessFlags.isNative()) {
+ throw new Unimplemented("Native interface methods are not yet supported.");
+ }
+ return method.accessFlags.isStatic() && !rewriter.factory.isClassConstructor(method.method);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
new file mode 100644
index 0000000..d35566a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
@@ -0,0 +1,59 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import java.util.ArrayList;
+import java.util.List;
+
+// Source code representing synthesized lambda bridge method.
+final class LambdaBridgeMethodSourceCode extends SynthesizedLambdaSourceCode {
+ private final DexMethod mainMethod;
+
+ LambdaBridgeMethodSourceCode(LambdaClass lambda, DexMethod mainMethod, DexMethod bridgeMethod) {
+ super(lambda, bridgeMethod);
+ this.mainMethod = mainMethod;
+ }
+
+ @Override
+ void prepareInstructions() {
+ DexType[] currentParams = proto.parameters.values;
+ DexType[] enforcedParams = descriptor().enforcedProto.parameters.values;
+
+ // Prepare call arguments.
+ List<MoveType> argMoveTypes = new ArrayList<>();
+ List<Integer> argRegisters = new ArrayList<>();
+
+ // Always add a receiver representing 'this' of the lambda class.
+ argMoveTypes.add(MoveType.OBJECT);
+ argRegisters.add(getReceiverRegister());
+
+ // Prepare arguments.
+ for (int i = 0; i < currentParams.length; i++) {
+ DexType expectedParamType = enforcedParams[i];
+ argMoveTypes.add(MoveType.fromDexType(expectedParamType));
+ argRegisters.add(enforceParameterType(
+ getParamRegister(i), currentParams[i], expectedParamType));
+ }
+
+ // Method call to the main functional interface method.
+ add(builder -> builder.addInvoke(Invoke.Type.VIRTUAL,
+ this.mainMethod, this.mainMethod.proto, argMoveTypes, argRegisters));
+
+ // Does the method have return value?
+ if (proto.returnType == factory().voidType) {
+ add(IRBuilder::addReturn);
+ } else {
+ MoveType moveType = MoveType.fromDexType(proto.returnType);
+ int tempValue = nextRegister(moveType);
+ add(builder -> builder.addMoveResult(moveType, tempValue));
+ add(builder -> builder.addReturn(moveType, tempValue));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
new file mode 100644
index 0000000..e92f78f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -0,0 +1,498 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexClassPromise;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.ir.code.Invoke;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Represents lambda class generated for a lambda descriptor in context
+ * of lambda instantiation point.
+ *
+ * Even though call sites, and thus lambda descriptors, are canonicalized
+ * across the application, the context may require several lambda classes
+ * to be generated for the same lambda descriptor.
+ *
+ * One reason is that we always generate a lambda class in the same package
+ * lambda instantiation point is located in, so if same call site is used in
+ * two classes from different packages (which can happen if same public method
+ * is being references via method reference expression) we generate separate
+ * lambda classes in those packages.
+ *
+ * Another reason is that if we generate an accessor, we generate it in the
+ * class referencing the call site, and thus two such classes will require two
+ * separate lambda classes.
+ */
+final class LambdaClass {
+ final LambdaRewriter rewriter;
+ final DexType type;
+ final LambdaDescriptor descriptor;
+ final DexMethod constructor;
+ final DexMethod classConstructor;
+ final DexField instanceField;
+ final Target target;
+ final AtomicBoolean addToMainDexList = new AtomicBoolean(false);
+
+ LambdaClass(LambdaRewriter rewriter, DexType accessedFrom,
+ DexType lambdaClassType, LambdaDescriptor descriptor) {
+ assert rewriter != null;
+ assert lambdaClassType != null;
+ assert descriptor != null;
+
+ this.rewriter = rewriter;
+ this.type = lambdaClassType;
+ this.descriptor = descriptor;
+
+ DexItemFactory factory = rewriter.factory;
+ DexProto constructorProto = factory.createProto(
+ factory.voidType, descriptor.captures.values);
+ this.constructor = factory.createMethod(
+ lambdaClassType, constructorProto, rewriter.constructorName);
+
+ this.target = createTarget(accessedFrom);
+
+ boolean stateless = isStateless();
+ this.classConstructor = !stateless ? null
+ : factory.createMethod(lambdaClassType, constructorProto, rewriter.classConstructorName);
+ this.instanceField = !stateless ? null
+ : factory.createField(lambdaClassType, lambdaClassType, rewriter.instanceFieldName);
+ }
+
+ // Generate unique lambda class type for lambda descriptor and instantiation point context.
+ static DexType createLambdaClassType(
+ LambdaRewriter rewriter, DexType accessedFrom, LambdaDescriptor match) {
+ StringBuilder lambdaClassDescriptor = new StringBuilder("L");
+
+ // We always create lambda class in the same package where it is referenced.
+ String packageDescriptor = accessedFrom.getPackageDescriptor();
+ if (!packageDescriptor.isEmpty()) {
+ lambdaClassDescriptor.append(packageDescriptor).append('/');
+ }
+
+ // Lambda class name prefix
+ lambdaClassDescriptor.append(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX);
+
+ // If the lambda class should match 1:1 the class it is accessed from, we
+ // just add the name of this type to make lambda class name unique.
+ // It also helps link the class lambda originated from in some cases.
+ if (match.delegatesToLambdaImplMethod() || match.needsAccessor(accessedFrom)) {
+ lambdaClassDescriptor.append(accessedFrom.getName()).append('$');
+ }
+
+ // Add unique lambda descriptor id
+ lambdaClassDescriptor.append(match.uniqueId).append(';');
+ return rewriter.factory.createType(lambdaClassDescriptor.toString());
+ }
+
+ final DexProgramClass synthesizeLambdaClass() {
+ return new DexProgramClass(
+ type,
+ DexClass.Origin.Synthetic,
+ new DexAccessFlags(Constants.ACC_FINAL | Constants.ACC_SYNTHETIC),
+ rewriter.factory.objectType,
+ buildInterfaces(),
+ rewriter.factory.createString("lambda"),
+ DexAnnotationSet.empty(),
+ synthesizeStaticFields(),
+ synthesizeInstanceFields(),
+ synthesizeDirectMethods(),
+ synthesizeVirtualMethods()
+ );
+ }
+
+ final DexField getCaptureField(int index) {
+ return rewriter.factory.createField(this.type,
+ descriptor.captures.values[index], rewriter.factory.createString("f$" + index));
+ }
+
+ final boolean isStateless() {
+ return descriptor.isStateless();
+ }
+
+ // Synthesize virtual methods.
+ private DexEncodedMethod[] synthesizeVirtualMethods() {
+ DexEncodedMethod[] methods = new DexEncodedMethod[1 + descriptor.bridges.size()];
+ int index = 0;
+
+ // Synthesize main method.
+ DexMethod mainMethod = rewriter.factory
+ .createMethod(type, descriptor.erasedProto, descriptor.name);
+ methods[index++] = new DexEncodedMethod(
+ mainMethod,
+ new DexAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_FINAL),
+ DexAnnotationSet.empty(),
+ DexAnnotationSetRefList.empty(),
+ new SynthesizedCode(new LambdaMainMethodSourceCode(this, mainMethod)));
+
+ // Synthesize bridge methods.
+ for (DexProto bridgeProto : descriptor.bridges) {
+ DexMethod bridgeMethod = rewriter.factory.createMethod(type, bridgeProto, descriptor.name);
+ methods[index++] = new DexEncodedMethod(
+ bridgeMethod,
+ new DexAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_FINAL
+ | Constants.ACC_SYNTHETIC | Constants.ACC_BRIDGE),
+ DexAnnotationSet.empty(),
+ DexAnnotationSetRefList.empty(),
+ new SynthesizedCode(
+ new LambdaBridgeMethodSourceCode(this, mainMethod, bridgeMethod)));
+ }
+ return methods;
+ }
+
+ // Synthesize direct methods.
+ private DexEncodedMethod[] synthesizeDirectMethods() {
+ boolean stateless = isStateless();
+ DexEncodedMethod[] methods = new DexEncodedMethod[stateless ? 2 : 1];
+
+ // Constructor.
+ methods[0] = new DexEncodedMethod(
+ constructor,
+ new DexAccessFlags((stateless ? Constants.ACC_PRIVATE : Constants.ACC_PUBLIC) |
+ Constants.ACC_SYNTHETIC | Constants.ACC_CONSTRUCTOR),
+ DexAnnotationSet.empty(),
+ DexAnnotationSetRefList.empty(),
+ new SynthesizedCode(new LambdaConstructorSourceCode(this)));
+
+ // Class constructor for stateless lambda classes.
+ if (stateless) {
+ methods[1] = new DexEncodedMethod(
+ classConstructor,
+ new DexAccessFlags(
+ Constants.ACC_SYNTHETIC | Constants.ACC_CONSTRUCTOR | Constants.ACC_STATIC),
+ DexAnnotationSet.empty(),
+ DexAnnotationSetRefList.empty(),
+ new SynthesizedCode(new LambdaClassConstructorSourceCode(this)));
+ }
+ return methods;
+ }
+
+ // Synthesize instance fields to represent captured values.
+ private DexEncodedField[] synthesizeInstanceFields() {
+ DexType[] fieldTypes = descriptor.captures.values;
+ int fieldCount = fieldTypes.length;
+ DexEncodedField[] fields = new DexEncodedField[fieldCount];
+ for (int i = 0; i < fieldCount; i++) {
+ DexAccessFlags accessFlags = new DexAccessFlags(
+ Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PRIVATE);
+ fields[i] = new DexEncodedField(
+ getCaptureField(i), accessFlags, DexAnnotationSet.empty(), null);
+ }
+ return fields;
+ }
+
+ // Synthesize static fields to represent singleton instance.
+ private DexEncodedField[] synthesizeStaticFields() {
+ if (!isStateless()) {
+ return DexEncodedField.EMPTY_ARRAY;
+ }
+
+ // Create instance field for stateless lambda.
+ assert this.instanceField != null;
+ DexEncodedField[] fields = new DexEncodedField[1];
+ fields[0] = new DexEncodedField(
+ this.instanceField,
+ new DexAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_FINAL
+ | Constants.ACC_SYNTHETIC | Constants.ACC_STATIC),
+ DexAnnotationSet.empty(),
+ DexValue.NULL);
+ return fields;
+ }
+
+ // Build a list of implemented interfaces.
+ private DexTypeList buildInterfaces() {
+ List<DexType> interfaces = descriptor.interfaces;
+ return interfaces.isEmpty() ? DexTypeList.empty()
+ : new DexTypeList(interfaces.toArray(new DexType[interfaces.size()]));
+ }
+
+ // Creates a delegation target for this particular lambda class. Note that we
+ // should always be able to create targets for the lambdas we support.
+ private Target createTarget(DexType accessedFrom) {
+ if (descriptor.delegatesToLambdaImplMethod()) {
+ return createLambdaImplMethodTarget(accessedFrom);
+ }
+
+ // Method referenced directly, without lambda$ method.
+ switch (descriptor.implHandle.type) {
+ case INVOKE_SUPER:
+ throw new Unimplemented("Method references to super methods are not yet supported");
+ case INVOKE_INTERFACE:
+ return createInterfaceMethodTarget(accessedFrom);
+ case INVOKE_CONSTRUCTOR:
+ return createConstructorTarget(accessedFrom);
+ case INVOKE_STATIC:
+ return createStaticMethodTarget(accessedFrom);
+ case INVOKE_INSTANCE:
+ return createInstanceMethodTarget(accessedFrom);
+ default:
+ throw new Unreachable("Unexpected method handle type in " + descriptor.implHandle);
+ }
+ }
+
+ private Target createLambdaImplMethodTarget(DexType accessedFrom) {
+ DexMethodHandle implHandle = descriptor.implHandle;
+ assert implHandle != null;
+ DexMethod implMethod = implHandle.asMethod();
+
+ // Lambda$ method. We must always find it.
+ assert implMethod.holder == accessedFrom;
+ assert descriptor.targetFoundInClass(accessedFrom);
+ assert descriptor.getAccessibility() != null;
+ assert descriptor.getAccessibility().isPrivate();
+ assert descriptor.getAccessibility().isSynthetic();
+
+ if (implHandle.type.isInvokeStatic()) {
+ return new StaticLambdaImplTarget();
+ }
+
+ assert implHandle.type.isInvokeInstance();
+
+ // If lambda$ method is an instance method we convert it into a static methods and
+ // relax its accessibility.
+ DexProto implProto = implMethod.proto;
+ DexType[] implParams = implProto.parameters.values;
+ DexType[] newParams = new DexType[implParams.length + 1];
+ newParams[0] = implMethod.holder;
+ System.arraycopy(implParams, 0, newParams, 1, implParams.length);
+
+ DexProto newProto = rewriter.factory.createProto(implProto.returnType, newParams);
+ return new InstanceLambdaImplTarget(
+ rewriter.factory.createMethod(implMethod.holder, newProto, implMethod.name));
+ }
+
+ // Create targets for instance method referenced directly without
+ // lambda$ methods. It may require creation of accessors in some cases.
+ private Target createInstanceMethodTarget(DexType accessedFrom) {
+ assert descriptor.implHandle.type.isInvokeInstance();
+
+ if (!descriptor.needsAccessor(accessedFrom)) {
+ return new NoAccessorMethodTarget(Invoke.Type.VIRTUAL);
+ }
+ // We need to generate an accessor method in `accessedFrom` class/interface
+ // for accessing the original instance impl-method. Note that impl-method's
+ // holder does not have to be the same as `accessedFrom`.
+ DexMethod implMethod = descriptor.implHandle.asMethod();
+ DexProto implProto = implMethod.proto;
+ DexType[] implParams = implProto.parameters.values;
+
+ // The accessor method will be static, package private, and take the
+ // receiver as the first argument. The receiver must be captured and
+ // be the first captured value in case there are more than one.
+ DexType[] accessorParams = new DexType[1 + implParams.length];
+ accessorParams[0] = descriptor.getImplReceiverType();
+ System.arraycopy(implParams, 0, accessorParams, 1, implParams.length);
+ DexProto accessorProto = rewriter.factory.createProto(implProto.returnType, accessorParams);
+ DexMethod accessorMethod = rewriter.factory.createMethod(
+ accessedFrom, accessorProto, generateUniqueLambdaMethodName());
+
+ return new ClassMethodWithAccessorTarget(accessorMethod);
+ }
+
+ // Create targets for static method referenced directly without
+ // lambda$ methods. It may require creation of accessors in some cases.
+ private Target createStaticMethodTarget(DexType accessedFrom) {
+ assert descriptor.implHandle.type.isInvokeStatic();
+
+ if (!descriptor.needsAccessor(accessedFrom)) {
+ return new NoAccessorMethodTarget(Invoke.Type.STATIC);
+ }
+
+ // We need to generate an accessor method in `accessedFrom` class/interface
+ // for accessing the original static impl-method. The accessor method will be
+ // static, package private with exactly same signature and the original method.
+ DexMethod accessorMethod = rewriter.factory.createMethod(accessedFrom,
+ descriptor.implHandle.asMethod().proto, generateUniqueLambdaMethodName());
+ return new ClassMethodWithAccessorTarget(accessorMethod);
+ }
+
+ // Create targets for constructor referenced directly without lambda$ methods.
+ // It may require creation of accessors in some cases.
+ private Target createConstructorTarget(DexType accessedFrom) {
+ DexMethodHandle implHandle = descriptor.implHandle;
+ assert implHandle != null;
+ assert implHandle.type.isInvokeConstructor();
+
+ if (!descriptor.needsAccessor(accessedFrom)) {
+ return new NoAccessorMethodTarget(Invoke.Type.DIRECT);
+ }
+
+ // We need to generate an accessor method in `accessedFrom` class/interface for
+ // instantiating the class and calling constructor on it. The accessor method will
+ // be static, package private with exactly same parameters as the constructor,
+ // and return the newly created instance.
+ DexMethod implMethod = implHandle.asMethod();
+ DexType returnType = implMethod.holder;
+ DexProto accessorProto = rewriter.factory.createProto(
+ returnType, implMethod.proto.parameters.values);
+ DexMethod accessorMethod = rewriter.factory.createMethod(accessedFrom,
+ accessorProto, generateUniqueLambdaMethodName());
+ return new ClassMethodWithAccessorTarget(accessorMethod);
+ }
+
+ // Create targets for interface methods.
+ private Target createInterfaceMethodTarget(DexType accessedFrom) {
+ assert descriptor.implHandle.type.isInvokeInterface();
+ assert !descriptor.needsAccessor(accessedFrom);
+ return new NoAccessorMethodTarget(Invoke.Type.INTERFACE);
+ }
+
+ private DexString generateUniqueLambdaMethodName() {
+ return rewriter.factory.createString(
+ LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX + descriptor.uniqueId);
+ }
+
+ // Represents information about the method lambda class need to delegate the call to. It may
+ // be the same method as specified in lambda descriptor or a newly synthesized accessor.
+ // Also provides action for ensuring accessibility of the referenced symbols.
+ abstract class Target {
+ final DexMethod callTarget;
+ final Invoke.Type invokeType;
+
+ Target(DexMethod callTarget, Invoke.Type invokeType) {
+ assert callTarget != null;
+ assert invokeType != null;
+ this.callTarget = callTarget;
+ this.invokeType = invokeType;
+ }
+
+ // Ensure access of the referenced symbol(s).
+ abstract boolean ensureAccessibility(Builder builder);
+ }
+
+ // Used for targeting methods referenced directly without creating accessors.
+ private final class NoAccessorMethodTarget extends Target {
+ NoAccessorMethodTarget(Invoke.Type invokeType) {
+ super(descriptor.implHandle.asMethod(), invokeType);
+ }
+
+ @Override
+ boolean ensureAccessibility(Builder builder) {
+ return true;
+ }
+ }
+
+ // Used for static private lambda$ methods. Only needs access relaxation.
+ private final class StaticLambdaImplTarget extends Target {
+ StaticLambdaImplTarget() {
+ super(descriptor.implHandle.asMethod(), Invoke.Type.STATIC);
+ }
+
+ @Override
+ boolean ensureAccessibility(Builder builder) {
+ // We already found the static method to be called, just relax its accessibility.
+ assert descriptor.getAccessibility() != null;
+ descriptor.getAccessibility().unsetPrivate();
+ DexClassPromise promise = builder.classMap.get(descriptor.implHandle.asMethod().holder);
+ assert promise != null;
+ DexClass implMethodHolder = promise.get();
+ if (implMethodHolder.isInterface()) {
+ descriptor.getAccessibility().setPublic();
+ }
+ return true;
+ }
+ }
+
+ // Used for instance private lambda$ methods. Needs to be converted to
+ // a package-private static method.
+ private class InstanceLambdaImplTarget extends Target {
+ InstanceLambdaImplTarget(DexMethod staticMethod) {
+ super(staticMethod, Invoke.Type.STATIC);
+ }
+
+ @Override
+ boolean ensureAccessibility(Builder builder) {
+ // For all instantiation points for which compiler creates lambda$
+ // methods, it creates these methods in the same class/interface.
+ DexMethod implMethod = descriptor.implHandle.asMethod();
+ DexClassPromise promise = builder.classMap.get(implMethod.holder);
+ assert promise != null;
+ DexClass implMethodHolder = promise.get();
+
+ DexEncodedMethod[] directMethods = implMethodHolder.directMethods;
+ for (int i = 0; i < directMethods.length; i++) {
+ DexEncodedMethod encodedMethod = directMethods[i];
+ if (implMethod.match(encodedMethod)) {
+ // We need to create a new static method with the same code to be able to safely
+ // relax its accessibility without making it virtual.
+ DexEncodedMethod newMethod = new DexEncodedMethod(
+ callTarget, encodedMethod.accessFlags, encodedMethod.annotations,
+ encodedMethod.parameterAnnotations, encodedMethod.getCode());
+ // TODO(ager): Should we give the new first parameter an actual name? Maybe 'this'?
+ encodedMethod.accessFlags.setStatic();
+ encodedMethod.accessFlags.unsetPrivate();
+ if (implMethodHolder.isInterface()) {
+ encodedMethod.accessFlags.setPublic();
+ }
+ DexCode dexCode = newMethod.getCode().asDexCode();
+ dexCode.setDebugInfo(dexCode.debugInfoWithAdditionalFirstParameter(null));
+ assert (dexCode.getDebugInfo() == null)
+ || (callTarget.proto.parameters.values.length
+ == dexCode.getDebugInfo().parameters.length);
+ directMethods[i] = newMethod;
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ // Used for instance/static methods or constructors accessed via
+ // synthesized accessor method. Needs accessor method to be created.
+ private class ClassMethodWithAccessorTarget extends Target {
+ ClassMethodWithAccessorTarget(DexMethod accessorMethod) {
+ super(accessorMethod, Invoke.Type.STATIC);
+ }
+
+ @Override
+ boolean ensureAccessibility(Builder builder) {
+ // Create a static accessor with proper accessibility.
+ DexClassPromise promise = builder.classMap.get(callTarget.holder);
+ assert promise != null && promise.isProgramClass();
+ DexClass accessorClass = promise.get();
+
+ DexAccessFlags accessorFlags = new DexAccessFlags(
+ Constants.ACC_SYNTHETIC | Constants.ACC_STATIC |
+ (accessorClass.isInterface() ? Constants.ACC_PUBLIC : 0));
+ DexEncodedMethod accessorEncodedMethod = new DexEncodedMethod(
+ callTarget, accessorFlags, DexAnnotationSet.empty(), DexAnnotationSetRefList.empty(),
+ new SynthesizedCode(new AccessorMethodSourceCode(LambdaClass.this)));
+ accessorClass.directMethods = appendMethod(
+ accessorClass.directMethods, accessorEncodedMethod);
+ rewriter.converter.optimizeSynthesizedMethod(accessorEncodedMethod);
+ return true;
+ }
+
+ private DexEncodedMethod[] appendMethod(DexEncodedMethod[] methods, DexEncodedMethod method) {
+ int size = methods.length;
+ DexEncodedMethod[] newMethods = new DexEncodedMethod[size + 1];
+ System.arraycopy(methods, 0, newMethods, 0, size);
+ newMethods[size] = method;
+ return newMethods;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
new file mode 100644
index 0000000..db8e1f5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
@@ -0,0 +1,36 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import java.util.Collections;
+
+// Source code representing synthesized lambda class constructor.
+// Used for stateless lambdas to instantiate singleton instance.
+final class LambdaClassConstructorSourceCode extends SynthesizedLambdaSourceCode {
+ LambdaClassConstructorSourceCode(LambdaClass lambda) {
+ super(null /* Class initializer is static */, lambda, lambda.classConstructor);
+ assert lambda.instanceField != null;
+ }
+
+ @Override
+ void prepareInstructions() {
+ // Create and initialize an instance.
+ int instance = nextRegister(MoveType.OBJECT);
+ add(builder -> builder.addNewInstance(instance, lambda.type));
+ add(builder -> builder.addInvoke(
+ Invoke.Type.DIRECT, lambda.constructor, lambda.constructor.proto,
+ Collections.singletonList(MoveType.OBJECT), Collections.singletonList(instance)));
+
+ // Assign to a field.
+ add(builder -> builder.addStaticPut(MemberType.OBJECT, instance, lambda.instanceField));
+
+ // Final return.
+ add(IRBuilder::addReturn);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
new file mode 100644
index 0000000..b4aef78
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
@@ -0,0 +1,65 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import java.util.Collections;
+
+// Source code representing synthesized lambda constructor.
+final class LambdaConstructorSourceCode extends SynthesizedLambdaSourceCode {
+ LambdaConstructorSourceCode(LambdaClass lambda) {
+ super(lambda, lambda.constructor);
+ }
+
+ @Override
+ void prepareInstructions() {
+ // Super constructor call (always java.lang.Object.<init>()).
+ DexMethod objectInitMethod = lambda.rewriter.objectInitMethod;
+ add(builder -> builder.addInvoke(Invoke.Type.DIRECT, objectInitMethod,
+ objectInitMethod.proto, Collections.singletonList(getReceiverValue())));
+
+ // Assign capture fields.
+ DexType[] capturedTypes = captures();
+ int capturedValues = capturedTypes.length;
+ if (capturedValues > 0) {
+ for (int i = 0; i < capturedValues; i++) {
+ MemberType memberType = MemberType.fromDexType(capturedTypes[i]);
+ DexField field = lambda.getCaptureField(i);
+ int idx = i;
+ add(builder -> builder.addInstancePut(memberType,
+ getParamRegister(idx), getReceiverRegister(), field));
+ }
+ }
+
+ // Final return.
+ add(IRBuilder::addReturn);
+ }
+
+ @Override
+ public int hashCode() {
+ // We want all zero-parameter constructor source code instances to
+ // be treated as equal, since it only has one call to super constructor,
+ // which is always java.lang.Object.<init>().
+ return captures().length == 0
+ ? System.identityHashCode(lambda.rewriter.objectInitMethod) : super.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof LambdaConstructorSourceCode)) {
+ return false;
+ }
+ if (captures().length == 0) {
+ // See comment in hashCode().
+ return ((LambdaConstructorSourceCode) obj).captures().length == 0;
+ }
+ return super.equals(obj);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
new file mode 100644
index 0000000..4a51135
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -0,0 +1,371 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DexValue;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+// Represents the lambda descriptor inferred from calls site.
+final class LambdaDescriptor {
+ private static final int LAMBDA_ALT_SERIALIZABLE = 1;
+ private static final int LAMBDA_ALT_HAS_EXTRA_INTERFACES = 2;
+ private static final int LAMBDA_ALT_HAS_BRIDGES = 4;
+ private static final int LAMBDA_ALT_MASK = LAMBDA_ALT_SERIALIZABLE
+ | LAMBDA_ALT_HAS_EXTRA_INTERFACES | LAMBDA_ALT_HAS_BRIDGES;
+
+ static final LambdaDescriptor MATCH_FAILED = new LambdaDescriptor();
+
+ final String uniqueId;
+ final DexString name;
+ final DexProto erasedProto;
+ final DexProto enforcedProto;
+ final DexMethodHandle implHandle;
+
+ final List<DexType> interfaces = new ArrayList<>();
+ final Set<DexProto> bridges = Sets.newIdentityHashSet();
+ final DexTypeList captures;
+
+ // Used for accessibility analysis and few assertions only.
+ private final DexEncodedMethod targetMethod;
+
+ private LambdaDescriptor() {
+ uniqueId = null;
+ name = null;
+ erasedProto = null;
+ enforcedProto = null;
+ implHandle = null;
+ captures = null;
+ targetMethod = null;
+ }
+
+ private LambdaDescriptor(LambdaRewriter rewriter, DexCallSite callSite,
+ DexString name, DexProto erasedProto, DexProto enforcedProto,
+ DexMethodHandle implHandle, DexType mainInterface, DexTypeList captures) {
+ assert rewriter != null;
+ assert callSite != null;
+ assert name != null;
+ assert erasedProto != null;
+ assert enforcedProto != null;
+ assert implHandle != null;
+ assert mainInterface != null;
+ assert captures != null;
+
+ this.uniqueId = callSite.getHash();
+ this.name = name;
+ this.erasedProto = erasedProto;
+ this.enforcedProto = enforcedProto;
+ this.implHandle = implHandle;
+ this.captures = captures;
+
+ this.interfaces.add(mainInterface);
+ this.targetMethod = lookupTargetMethod(rewriter);
+ }
+
+ final DexType getImplReceiverType() {
+ // The receiver of instance impl-method is captured as the first captured
+ // value or should be the first argument of the enforced method signature.
+ DexType[] params = enforcedProto.parameters.values;
+ DexType[] captures = this.captures.values;
+ assert captures.length > 0 || params.length > 0;
+ return captures.length > 0 ? captures[0] : params[0];
+ }
+
+ private DexEncodedMethod lookupTargetMethod(LambdaRewriter rewriter) {
+ // Find the lambda's impl-method target.
+ DexMethod method = implHandle.asMethod();
+ switch (implHandle.type) {
+ case INVOKE_INSTANCE: {
+ AppInfo appInfo = rewriter.converter.appInfo;
+ DexEncodedMethod target = appInfo.lookupVirtualTarget(getImplReceiverType(), method);
+ if (target == null) {
+ target = appInfo.lookupDirectTarget(method);
+ }
+ assert target == null ||
+ (!target.accessFlags.isConstructor() && !target.accessFlags.isStatic());
+ return target;
+ }
+
+ case INVOKE_STATIC: {
+ AppInfo appInfo = rewriter.converter.appInfo;
+ DexEncodedMethod target = appInfo.lookupStaticTarget(method);
+ assert target == null || target.accessFlags.isStatic();
+ return target;
+ }
+
+ case INVOKE_CONSTRUCTOR: {
+ AppInfo appInfo = rewriter.converter.appInfo;
+ DexEncodedMethod target = appInfo.lookupDirectTarget(method);
+ assert target == null || target.accessFlags.isConstructor();
+ return target;
+ }
+
+ case INVOKE_INTERFACE: {
+ AppInfo appInfo = rewriter.converter.appInfo;
+ DexEncodedMethod target = appInfo.lookupVirtualTarget(getImplReceiverType(), method);
+ assert target == null ||
+ (!target.accessFlags.isConstructor() && !target.accessFlags.isStatic());
+ return target;
+ }
+
+ default:
+ throw new Unreachable("Unexpected method handle kind in " + implHandle);
+ }
+ }
+
+ final DexAccessFlags getAccessibility() {
+ return targetMethod == null ? null : targetMethod.accessFlags;
+ }
+
+ final boolean targetFoundInClass(DexType type) {
+ return targetMethod != null && targetMethod.method.holder == type;
+ }
+
+ /** If the lambda delegates to lambda$ method. */
+ boolean delegatesToLambdaImplMethod() {
+ DexString methodName = implHandle.asMethod().name;
+ return methodName.toString().startsWith(LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX);
+ }
+
+ /** Is a stateless lambda, i.e. lambda does not capture any values */
+ final boolean isStateless() {
+ return captures.isEmpty();
+ }
+
+ /** Checks if call site needs a accessor when referenced from `accessedFrom`. */
+ boolean needsAccessor(DexType accessedFrom) {
+ if (delegatesToLambdaImplMethod()) {
+ return false;
+ }
+
+ if (implHandle.type.isInvokeInterface()) {
+ // Interface methods must be public.
+ return false;
+ }
+
+ boolean staticTarget = implHandle.type.isInvokeStatic();
+ boolean instanceTarget = implHandle.type.isInvokeInstance();
+ boolean initTarget = implHandle.type.isInvokeConstructor();
+ assert instanceTarget || staticTarget | initTarget;
+
+ if (targetMethod == null) {
+ // The target cannot be a private method, since otherwise it
+ // should have been found.
+
+ if (staticTarget || initTarget) {
+ // Create accessor only in case it is accessed from other
+ // package, since otherwise it can be called directly.
+ // NOTE: This case is different from regular instance method case
+ // because the method being called must be present in method holder,
+ // and not in one from its supertypes.
+ boolean accessedFromSamePackage =
+ accessedFrom.getPackageDescriptor().equals(
+ implHandle.asMethod().holder.getPackageDescriptor());
+ return !accessedFromSamePackage;
+ }
+
+ // Since instance method was not found, always generate an accessor
+ // since it may be a protected method located in another package.
+ return true;
+ }
+
+ DexAccessFlags flags = targetMethod.accessFlags;
+
+ // Private methods always need accessors.
+ if (flags.isPrivate()) {
+ return true;
+ }
+ if (flags.isPublic()) {
+ return false;
+ }
+
+ boolean accessedFromSamePackage =
+ accessedFrom.getPackageDescriptor().equals(
+ targetMethod.method.holder.getPackageDescriptor());
+ assert flags.isProtected() || accessedFromSamePackage;
+ return flags.isProtected() && !accessedFromSamePackage;
+ }
+
+ /**
+ * Matches call site for lambda metafactory invocation pattern and
+ * returns extracted match information, or null if match failed.
+ */
+ static LambdaDescriptor infer(LambdaRewriter rewriter, DexCallSite callSite) {
+ // We expect bootstrap method to be either `metafactory` or `altMetafactory` method
+ // of `java.lang.invoke.LambdaMetafactory` class. Both methods are static.
+ if (!callSite.bootstrapMethod.type.isInvokeStatic()) {
+ return LambdaDescriptor.MATCH_FAILED;
+ }
+
+ DexMethod bootstrapMethod = callSite.bootstrapMethod.asMethod();
+ boolean isMetafactoryMethod = bootstrapMethod == rewriter.metafactoryMethod;
+ boolean isAltMetafactoryMethod = bootstrapMethod == rewriter.metafactoryAltMethod;
+ if (!isMetafactoryMethod && !isAltMetafactoryMethod) {
+ // It is not a lambda, thus no need to manage this call site.
+ return LambdaDescriptor.MATCH_FAILED;
+ }
+
+ // 'Method name' operand of the invoke-custom instruction represents
+ // the name of the functional interface main method.
+ DexString funcMethodName = callSite.methodName;
+
+ // Signature of main functional interface method.
+ DexValue.DexValueMethodType funcErasedSignature =
+ getBootstrapArgument(callSite, 0, DexValue.DexValueMethodType.class);
+
+ // Method handle of the implementation method.
+ DexMethodHandle lambdaImplMethodHandle =
+ getBootstrapArgument(callSite, 1, DexValue.DexValueMethodHandle.class).value;
+ // Even though there are some limitations on which method handle kinds are
+ // allowed for lambda impl-methods, there is no way to detect unsupported
+ // handle kinds after they are transformed into DEX method handle.
+
+ // Signature to be enforced on main method.
+ DexValue.DexValueMethodType funcEnforcedSignature =
+ getBootstrapArgument(callSite, 2, DexValue.DexValueMethodType.class);
+ if (!isEnforcedSignatureValid(
+ rewriter, funcEnforcedSignature.value, funcErasedSignature.value)) {
+ throw new Unreachable(
+ "Enforced and erased signatures are inconsistent in " + callSite.toString());
+ }
+
+ // 'Method type' of the invoke-custom instruction represents the signature
+ // of the lambda method factory.
+ DexProto lambdaFactoryProto = callSite.methodProto;
+ // Main functional interface is the return type of the lambda factory method.
+ DexType mainFuncInterface = lambdaFactoryProto.returnType;
+ // Lambda captures are represented as parameters of the lambda factory method.
+ DexTypeList captures = lambdaFactoryProto.parameters;
+
+ // Create a match.
+ LambdaDescriptor match = new LambdaDescriptor(rewriter, callSite,
+ funcMethodName, funcErasedSignature.value, funcEnforcedSignature.value,
+ lambdaImplMethodHandle, mainFuncInterface, captures);
+
+ if (isMetafactoryMethod) {
+ if (callSite.bootstrapArgs.size() != 3) {
+ throw new Unreachable(
+ "Unexpected number of metafactory method arguments in " + callSite.toString());
+ }
+ } else {
+ extractExtraLambdaInfo(rewriter, callSite, match);
+ }
+
+ return match;
+ }
+
+ private static void extractExtraLambdaInfo(
+ LambdaRewriter rewriter, DexCallSite callSite, LambdaDescriptor match) {
+ int argIndex = 3;
+ int flagsArg = getBootstrapArgument(
+ callSite, argIndex++, DexValue.DexValueInt.class).value;
+ assert (flagsArg & ~LAMBDA_ALT_MASK) == 0;
+
+ // Load extra interfaces if any.
+ if ((flagsArg & LAMBDA_ALT_HAS_EXTRA_INTERFACES) != 0) {
+ int count = getBootstrapArgument(
+ callSite, argIndex++, DexValue.DexValueInt.class).value;
+ for (int i = 0; i < count; i++) {
+ DexType type = getBootstrapArgument(
+ callSite, argIndex++, DexValue.DexValueType.class).value;
+ if (!match.interfaces.contains(type)) {
+ match.interfaces.add(type);
+ }
+ }
+ }
+
+ // If the lambda is serializable, add it.
+ if ((flagsArg & LAMBDA_ALT_SERIALIZABLE) != 0) {
+ if (!match.interfaces.contains(rewriter.serializableType)) {
+ match.interfaces.add(rewriter.serializableType);
+ }
+ }
+
+ // Load bridges if any.
+ if ((flagsArg & LAMBDA_ALT_HAS_BRIDGES) != 0) {
+ int count = getBootstrapArgument(
+ callSite, argIndex++, DexValue.DexValueInt.class).value;
+ for (int i = 0; i < count; i++) {
+ DexProto bridgeProto = getBootstrapArgument(
+ callSite, argIndex++, DexValue.DexValueMethodType.class).value;
+ match.bridges.add(bridgeProto);
+ }
+ }
+
+ if (callSite.bootstrapArgs.size() != argIndex) {
+ throw new Unreachable(
+ "Unexpected number of metafactory method arguments in " + callSite.toString());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> T getBootstrapArgument(DexCallSite callSite, int i, Class<T> clazz) {
+ List<DexValue> bootstrapArgs = callSite.bootstrapArgs;
+ if (bootstrapArgs.size() < i) {
+ throw new Unreachable("Expected to find at least "
+ + i + " bootstrap arguments in " + callSite.toString());
+ }
+ DexValue value = bootstrapArgs.get(i);
+ if (!clazz.isAssignableFrom(value.getClass())) {
+ throw new Unreachable("Unexpected type of "
+ + "bootstrap arguments #" + i + " in " + callSite.toString());
+ }
+ return (T) value;
+ }
+
+ private static boolean isEnforcedSignatureValid(
+ LambdaRewriter rewriter, DexProto enforced, DexProto erased) {
+ if (!isSameOrDerived(rewriter.factory, enforced.returnType, erased.returnType)) {
+ return false;
+ }
+ DexType[] enforcedValues = enforced.parameters.values;
+ DexType[] erasedValues = erased.parameters.values;
+ int count = enforcedValues.length;
+ if (count != erasedValues.length) {
+ return false;
+ }
+ for (int i = 0; i < count; i++) {
+ if (!isSameOrDerived(rewriter.factory, enforcedValues[i], erasedValues[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Checks if the types are the same OR both types are reference types and
+ // `subType` is derived from `b`. Note that in the latter case we only check if
+ // both types are class types, for the reasons mentioned in isSameOrAdaptableTo(...).
+ static boolean isSameOrDerived(
+ DexItemFactory factory, DexType subType, DexType superType) {
+ if (subType == superType || (subType.isClassType() && superType.isClassType())) {
+ return true;
+ }
+
+ if (subType.isArrayType()) {
+ if (superType.isArrayType()) {
+ // X[] -> Y[].
+ return isSameOrDerived(factory,
+ subType.toArrayElementType(factory), superType.toArrayElementType(factory));
+ }
+ return superType == factory.objectType; // T[] -> Object.
+ }
+
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
new file mode 100644
index 0000000..9e4c570
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -0,0 +1,478 @@
+// 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.ir.desugar;
+
+import static com.android.tools.r8.ir.desugar.LambdaRewriter.EMPTY_TYPE_ARRAY;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+// Source code representing synthesized lambda main method
+final class LambdaMainMethodSourceCode extends SynthesizedLambdaSourceCode {
+ LambdaMainMethodSourceCode(LambdaClass lambda, DexMethod mainMethod) {
+ super(lambda, mainMethod);
+ }
+
+ private boolean checkSignatures(
+ DexType[] captures, DexType[] enforcedParams, DexType enforcedReturnType,
+ List<DexType> implReceiverAndArgs, DexType implReturnType) {
+ List<DexType> capturesAndParams = new ArrayList<>();
+ capturesAndParams.addAll(Lists.newArrayList(captures));
+ capturesAndParams.addAll(Lists.newArrayList(enforcedParams));
+
+ int size = capturesAndParams.size();
+ if (size != implReceiverAndArgs.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < size; i++) {
+ if (!isSameOrAdaptableTo(capturesAndParams.get(i), implReceiverAndArgs.get(i))) {
+ return false;
+ }
+ }
+
+ if (!enforcedReturnType.isVoidType()
+ && !isSameOrAdaptableTo(implReturnType, enforcedReturnType)) {
+ return false;
+ }
+ return true;
+ }
+
+ private DexType getPrimitiveFromBoxed(DexType boxedPrimitive) {
+ DexString descriptor = boxedPrimitive.descriptor;
+ DexItemFactory factory = factory();
+ if (descriptor == factory.boxedBooleanDescriptor) {
+ return factory.booleanType;
+ }
+ if (descriptor == factory.boxedByteDescriptor) {
+ return factory.byteType;
+ }
+ if (descriptor == factory.boxedCharDescriptor) {
+ return factory.charType;
+ }
+ if (descriptor == factory.boxedShortDescriptor) {
+ return factory.shortType;
+ }
+ if (descriptor == factory.boxedIntDescriptor) {
+ return factory.intType;
+ }
+ if (descriptor == factory.boxedLongDescriptor) {
+ return factory.longType;
+ }
+ if (descriptor == factory.boxedFloatDescriptor) {
+ return factory.floatType;
+ }
+ if (descriptor == factory.boxedDoubleDescriptor) {
+ return factory.doubleType;
+ }
+ return null;
+ }
+
+ private DexType getBoxedForPrimitiveType(DexType primitive) {
+ switch (primitive.descriptor.content[0]) {
+ case 'Z': // byte
+ return factory().boxedBooleanType;
+ case 'B': // byte
+ return factory().boxedByteType;
+ case 'S': // short
+ return factory().boxedShortType;
+ case 'C': // char
+ return factory().boxedCharType;
+ case 'I': // int
+ return factory().boxedIntType;
+ case 'J': // long
+ return factory().boxedLongType;
+ case 'F': // float
+ return factory().boxedFloatType;
+ case 'D': // double
+ return factory().boxedDoubleType;
+ default:
+ throw new Unreachable("Invalid primitive type descriptor: " + primitive);
+ }
+ }
+
+ // Checks if the types are the same OR type `a` is adaptable to type `b`.
+ private boolean isSameOrAdaptableTo(DexType a, DexType b) {
+ if (a == b) {
+ return true;
+ }
+
+ DexItemFactory factory = factory();
+ if (a.isArrayType()) {
+ // Arrays are only adaptable to java.lang.Object.
+ return b == factory.objectType;
+ }
+
+ if (a.isPrimitiveType()) {
+ if (b.isPrimitiveType()) {
+ return isSameOrAdaptableTo(a.descriptor.content[0], b.descriptor.content[0]);
+ }
+
+ // `a` is primitive and `b` is a supertype of the boxed type `a`.
+ DexType boxedPrimitiveType = getBoxedForPrimitiveType(a);
+ if (b == boxedPrimitiveType || b == factory.objectType) {
+ return true;
+ }
+ return boxedPrimitiveType != factory.boxedCharType
+ && boxedPrimitiveType != factory.boxedBooleanType
+ && b.descriptor == factory.boxedNumberDescriptor;
+ }
+
+ if (b.isPrimitiveType()) {
+ // `a` is a boxed type for `a*` which can be
+ // widened to primitive type `b`.
+ DexType unboxedA = getPrimitiveFromBoxed(a);
+ return unboxedA != null &&
+ isSameOrAdaptableTo(unboxedA.descriptor.content[0], b.descriptor.content[0]);
+ }
+
+ // Otherwise `a` should be a reference type derived from `b`.
+ // NOTE: we don't check `b` for being actually a supertype, since we
+ // might not have full classpath with inheritance information to do that.
+ // We assume this requirement stands and will be caught by cast
+ // instruction anyways in most cases.
+ return a.isClassType() && b.isClassType();
+ }
+
+ // For two primitive types `a` is adjustable to `b` iff `a` is the same as `b`
+ // or can be converted to `b` via a primitive widening conversion.
+ private boolean isSameOrAdaptableTo(byte from, byte to) {
+ if (from == to) {
+ return true;
+ }
+ switch (from) {
+ case 'B': // byte
+ return to == 'S' || to == 'I' || to == 'J' || to == 'F' || to == 'D';
+ case 'S': // short
+ case 'C': // char
+ return to == 'I' || to == 'J' || to == 'F' || to == 'D';
+ case 'I': // int
+ return to == 'J' || to == 'F' || to == 'D';
+ case 'J': // long
+ return to == 'F' || to == 'D';
+ case 'F': // float
+ return to == 'D';
+ case 'Z': // boolean
+ case 'D': // double
+ return false;
+ default:
+ throw new Unreachable("Invalid primitive type descriptor: " + from);
+ }
+ }
+
+ @Override
+ void prepareInstructions() {
+ DexType[] capturedTypes = captures();
+ DexType[] erasedParams = descriptor().erasedProto.parameters.values;
+ DexType erasedReturnType = descriptor().erasedProto.returnType;
+ DexType[] enforcedParams = descriptor().enforcedProto.parameters.values;
+ DexType enforcedReturnType = descriptor().enforcedProto.returnType;
+
+ LambdaClass.Target target = lambda.target;
+ DexMethod methodToCall = target.callTarget;
+
+ // Only constructor call should use direct invoke type since super
+ // and private methods require accessor methods.
+ boolean constructorTarget = target.invokeType == Invoke.Type.DIRECT;
+ assert !constructorTarget || methodToCall.name == factory().constructorMethodName;
+
+ List<DexType> implReceiverAndArgs = new ArrayList<>();
+ if (target.invokeType == Invoke.Type.VIRTUAL || target.invokeType == Invoke.Type.INTERFACE) {
+ implReceiverAndArgs.add(methodToCall.holder);
+ }
+ implReceiverAndArgs.addAll(Lists.newArrayList(methodToCall.proto.parameters.values));
+ DexType implReturnType = methodToCall.proto.returnType;
+
+ assert target.invokeType == Invoke.Type.STATIC
+ || target.invokeType == Invoke.Type.VIRTUAL
+ || target.invokeType == Invoke.Type.DIRECT
+ || target.invokeType == Invoke.Type.INTERFACE;
+ assert checkSignatures(capturedTypes, enforcedParams,
+ enforcedReturnType, implReceiverAndArgs,
+ constructorTarget ? target.callTarget.holder : implReturnType);
+
+ // Prepare call arguments.
+ List<MoveType> argMoveTypes = new ArrayList<>();
+ List<Integer> argRegisters = new ArrayList<>();
+
+ // If the target is a constructor, we need to create the instance first.
+ // This instance will be the first argument to the call.
+ if (constructorTarget) {
+ int instance = nextRegister(MoveType.OBJECT);
+ add(builder -> builder.addNewInstance(instance, methodToCall.holder));
+ argMoveTypes.add(MoveType.OBJECT);
+ argRegisters.add(instance);
+ }
+
+ // Load captures if needed.
+ int capturedValues = capturedTypes.length;
+ for (int i = 0; i < capturedValues; i++) {
+ MemberType memberType = MemberType.fromDexType(capturedTypes[i]);
+ MoveType moveType = MemberType.moveTypeFor(memberType);
+ int register = nextRegister(moveType);
+
+ argMoveTypes.add(moveType);
+ argRegisters.add(register);
+
+ // Read field into tmp local.
+ DexField field = lambda.getCaptureField(i);
+ add(builder -> builder.addInstanceGet(
+ memberType, register, getReceiverRegister(), field));
+ }
+
+ // Prepare arguments.
+ for (int i = 0; i < erasedParams.length; i++) {
+ DexType expectedParamType = implReceiverAndArgs.get(i + capturedValues);
+ argMoveTypes.add(MoveType.fromDexType(expectedParamType));
+ argRegisters.add(prepareParameterValue(
+ getParamRegister(i), erasedParams[i], enforcedParams[i], expectedParamType));
+ }
+
+ // Method call to the method implementing lambda or method-ref.
+ add(builder -> builder.addInvoke(target.invokeType,
+ methodToCall, methodToCall.proto, argMoveTypes, argRegisters));
+
+ // Does the method have return value?
+ if (enforcedReturnType.isVoidType()) {
+ add(IRBuilder::addReturn);
+ } else if (constructorTarget) {
+ // Return newly created instance
+ int instanceRegister = argRegisters.get(0);
+ int adjustedValue = prepareReturnValue(instanceRegister,
+ erasedReturnType, enforcedReturnType, methodToCall.holder);
+ add(builder -> builder.addReturn(
+ MoveType.fromDexType(erasedReturnType), adjustedValue));
+ } else {
+ MoveType implMoveType = MoveType.fromDexType(implReturnType);
+ int tempValue = nextRegister(implMoveType);
+ add(builder -> builder.addMoveResult(implMoveType, tempValue));
+ int adjustedValue = prepareReturnValue(tempValue,
+ erasedReturnType, enforcedReturnType, methodToCall.proto.returnType);
+ MoveType adjustedMoveType = MoveType.fromDexType(erasedReturnType);
+ add(builder -> builder.addReturn(adjustedMoveType, adjustedValue));
+ }
+ }
+
+ // Adds necessary casts and transformations to adjust the value
+ // returned by impl-method to expected return type of the method.
+ private int prepareReturnValue(int register,
+ DexType erasedType, DexType enforcedType, DexType actualType) {
+ // `actualType` must be adjusted to `enforcedType` first.
+ register = adjustType(register, actualType, enforcedType);
+
+ // `erasedType` and `enforcedType` may only differ when they both
+ // are class types and `erasedType` is a base type of `enforcedType`,
+ // so no transformation is actually needed.
+ assert LambdaDescriptor.isSameOrDerived(factory(), enforcedType, erasedType);
+ return register;
+ }
+
+ // Adds necessary casts and transformations to adjust parameter
+ // value to the expected type of method-impl argument.
+ //
+ // Note that the original parameter type (`erasedType`) may need to
+ // be converted to enforced parameter type (`enforcedType`), which,
+ // in its turn, may need to be adjusted to the parameter type of
+ // the impl-method (`expectedType`).
+ private int prepareParameterValue(int register,
+ DexType erasedType, DexType enforcedType, DexType expectedType) {
+ register = enforceParameterType(register, erasedType, enforcedType);
+ register = adjustType(register, enforcedType, expectedType);
+ return register;
+ }
+
+ private int adjustType(int register, DexType fromType, DexType toType) {
+ if (fromType == toType) {
+ return register;
+ }
+
+ boolean fromTypePrimitive = fromType.isPrimitiveType();
+ boolean toTypePrimitive = toType.isPrimitiveType();
+
+ // If both are primitive they must be convertible via primitive widening conversion.
+ if (fromTypePrimitive && toTypePrimitive) {
+ return addPrimitiveWideningConversion(register, fromType, toType);
+ }
+
+ // If the first one is a boxed primitive type and the second one is a primitive
+ // type, the value must be unboxed and converted to the resulting type via primitive
+ // widening conversion.
+ if (toTypePrimitive) {
+ DexType fromTypeAsPrimitive = getPrimitiveFromBoxed(fromType);
+ if (fromTypeAsPrimitive != null) {
+ int unboxedRegister = addPrimitiveUnboxing(register, fromTypeAsPrimitive, fromType);
+ return addPrimitiveWideningConversion(unboxedRegister, fromTypeAsPrimitive, toType);
+ }
+ }
+
+ // If the first one is a primitive type and the second one is a boxed
+ // type for this primitive type, just box the value.
+ if (fromTypePrimitive) {
+ DexType boxedFromType = getBoxedForPrimitiveType(fromType);
+ if (toType == boxedFromType ||
+ toType == factory().objectType ||
+ (boxedFromType != factory().booleanType &&
+ boxedFromType != factory().charType &&
+ toType == factory().boxedNumberType)) {
+ return addPrimitiveBoxing(register, fromType, boxedFromType);
+ }
+ }
+
+ if (fromType.isArrayType() && toType == factory().objectType) {
+ // If `fromType` is an array and `toType` is java.lang.Object, no cast is needed.
+ return register;
+ }
+
+ if (fromType.isClassType() && toType.isClassType()) {
+ // If `fromType` and `toType` are both reference types, `fromType` must
+ // be deriving from `toType`.
+ // NOTE: we don't check `toType` for being actually a supertype, since we
+ // might not have full classpath with inheritance information to do that.
+ return register;
+ }
+
+ throw new Unreachable("Unexpected type adjustment from "
+ + fromType.toSourceString() + " to " + toType);
+ }
+
+ private int addPrimitiveWideningConversion(int register, DexType fromType, DexType toType) {
+ assert fromType.isPrimitiveType() && toType.isPrimitiveType();
+ if (fromType == toType) {
+ return register;
+ }
+
+ NumericType from = NumericType.fromDexType(fromType);
+ NumericType to = NumericType.fromDexType(toType);
+
+ if (from != null && to != null) {
+ assert from != to;
+
+ switch (to) {
+ case SHORT: {
+ if (from != NumericType.BYTE) {
+ break; // Only BYTE can be converted to SHORT via widening conversion.
+ }
+ int result = nextRegister(MoveType.SINGLE);
+ add(builder -> builder.addConversion(to, NumericType.INT, result, register));
+ return result;
+ }
+
+ case INT:
+ if (from == NumericType.BYTE || from == NumericType.CHAR || from == NumericType.SHORT) {
+ return register; // No actual conversion is needed.
+ }
+ break;
+
+ case LONG: {
+ if (from == NumericType.FLOAT || from == NumericType.DOUBLE) {
+ break; // Not a widening conversion.
+ }
+ int result = nextRegister(MoveType.WIDE);
+ add(builder -> builder.addConversion(to, NumericType.INT, result, register));
+ return result;
+ }
+
+ case FLOAT: {
+ if (from == NumericType.DOUBLE) {
+ break; // Not a widening conversion.
+ }
+ int result = nextRegister(MoveType.SINGLE);
+ NumericType type = (from == NumericType.LONG) ? NumericType.LONG : NumericType.INT;
+ add(builder -> builder.addConversion(to, type, result, register));
+ return result;
+ }
+
+ case DOUBLE: {
+ int result = nextRegister(MoveType.WIDE);
+ NumericType type = (from == NumericType.FLOAT || from == NumericType.LONG)
+ ? from : NumericType.INT;
+ add(builder -> builder.addConversion(to, type, result, register));
+ return result;
+ }
+ }
+ }
+
+ throw new Unreachable("Type " + fromType.toSourceString() + " cannot be " +
+ "converted to " + toType.toSourceString() + " via primitive widening conversion.");
+ }
+
+ private DexMethod getUnboxMethod(byte primitive, DexType boxType) {
+ DexItemFactory factory = factory();
+ DexProto proto;
+ switch (primitive) {
+ case 'Z': // byte
+ proto = factory.createProto(factory.booleanType, EMPTY_TYPE_ARRAY);
+ return factory.createMethod(boxType, proto, factory.unboxBooleanMethodName);
+ case 'B': // byte
+ proto = factory.createProto(factory.byteType, EMPTY_TYPE_ARRAY);
+ return factory.createMethod(boxType, proto, factory.unboxByteMethodName);
+ case 'S': // short
+ proto = factory.createProto(factory.shortType, EMPTY_TYPE_ARRAY);
+ return factory.createMethod(boxType, proto, factory.unboxShortMethodName);
+ case 'C': // char
+ proto = factory.createProto(factory.charType, EMPTY_TYPE_ARRAY);
+ return factory.createMethod(boxType, proto, factory.unboxCharMethodName);
+ case 'I': // int
+ proto = factory.createProto(factory.intType, EMPTY_TYPE_ARRAY);
+ return factory.createMethod(boxType, proto, factory.unboxIntMethodName);
+ case 'J': // long
+ proto = factory.createProto(factory.longType, EMPTY_TYPE_ARRAY);
+ return factory.createMethod(boxType, proto, factory.unboxLongMethodName);
+ case 'F': // float
+ proto = factory.createProto(factory.floatType, EMPTY_TYPE_ARRAY);
+ return factory.createMethod(boxType, proto, factory.unboxFloatMethodName);
+ case 'D': // double
+ proto = factory.createProto(factory.doubleType, EMPTY_TYPE_ARRAY);
+ return factory.createMethod(boxType, proto, factory.unboxDoubleMethodName);
+ default:
+ throw new Unreachable("Invalid primitive type descriptor: " + primitive);
+ }
+ }
+
+ private int addPrimitiveUnboxing(int register, DexType primitiveType, DexType boxType) {
+ DexMethod method = getUnboxMethod(primitiveType.descriptor.content[0], boxType);
+
+ List<MoveType> argMoveTypes = Collections.singletonList(MoveType.OBJECT);
+ List<Integer> argRegisters = Collections.singletonList(register);
+ add(builder -> builder.addInvoke(Invoke.Type.VIRTUAL,
+ method, method.proto, argMoveTypes, argRegisters));
+
+ MoveType moveType = MoveType.fromDexType(primitiveType);
+ int result = nextRegister(moveType);
+ add(builder -> builder.addMoveResult(moveType, result));
+ return result;
+ }
+
+ private int addPrimitiveBoxing(int register, DexType primitiveType, DexType boxType) {
+ // Generate factory method fo boxing.
+ DexItemFactory factory = factory();
+ DexProto proto = factory.createProto(boxType, new DexType[] { primitiveType });
+ DexMethod method = factory.createMethod(boxType, proto, factory.valueOfMethodName);
+
+ MoveType moveType = MoveType.fromDexType(primitiveType);
+ List<MoveType> argMoveTypes = Collections.singletonList(moveType);
+ List<Integer> argRegisters = Collections.singletonList(register);
+ add(builder -> builder.addInvoke(Invoke.Type.STATIC,
+ method, method.proto, argMoveTypes, argRegisters));
+
+ int result = nextRegister(MoveType.OBJECT);
+ add(builder -> builder.addMoveResult(MoveType.OBJECT, result));
+ return result;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
new file mode 100644
index 0000000..11484fb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -0,0 +1,295 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeCustom;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+/**
+ * Lambda desugaring rewriter.
+ *
+ * Performs lambda instantiation point matching,
+ * lambda class generation, and instruction patching.
+ */
+public class LambdaRewriter {
+ private static final String METAFACTORY_TYPE_DESCR = "Ljava/lang/invoke/LambdaMetafactory;";
+ private static final String CALLSITE_TYPE_DESCR = "Ljava/lang/invoke/CallSite;";
+ private static final String LOOKUP_TYPE_DESCR = "Ljava/lang/invoke/MethodHandles$Lookup;";
+ private static final String METHODTYPE_TYPE_DESCR = "Ljava/lang/invoke/MethodType;";
+ private static final String METHODHANDLE_TYPE_DESCR = "Ljava/lang/invoke/MethodHandle;";
+ private static final String OBJECT_ARRAY_TYPE_DESCR = "[Ljava/lang/Object;";
+ private static final String SERIALIZABLE_TYPE_DESCR = "Ljava/io/Serializable;";
+
+ private static final String METAFACTORY_METHOD_NAME = "metafactory";
+ private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
+
+ static final String LAMBDA_CLASS_NAME_PREFIX = "-$Lambda$";
+ static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
+ static final DexType[] EMPTY_TYPE_ARRAY = new DexType[0];
+ static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
+
+ final IRConverter converter;
+ final DexItemFactory factory;
+
+ final DexMethod metafactoryMethod;
+ final DexMethod objectInitMethod;
+
+ final DexMethod metafactoryAltMethod;
+ final DexType serializableType;
+
+ final DexString constructorName;
+ final DexString classConstructorName;
+ final DexString instanceFieldName;
+
+ // Maps call sites seen so far to inferred lambda descriptor. It is intended
+ // to help avoid re-matching call sites we already seen. Note that same call
+ // site may match one or several lambda classes.
+ //
+ // NOTE: synchronize concurrent access on `knownCallSites`.
+ private final Map<DexCallSite, LambdaDescriptor> knownCallSites = new IdentityHashMap<>();
+
+ // Maps lambda class type into lambda class representation. Since lambda class
+ // type uniquely defines lambda class, effectively canonicalizes lambda classes.
+ // NOTE: synchronize concurrent access on `knownLambdaClasses`.
+ private final Map<DexType, LambdaClass> knownLambdaClasses = new IdentityHashMap<>();
+
+ // Checks if the type starts with lambda-class prefix.
+ public static boolean hasLambdaClassPrefix(DexType clazz) {
+ return clazz.getName().startsWith(LAMBDA_CLASS_NAME_PREFIX);
+ }
+
+ public LambdaRewriter(IRConverter converter) {
+ assert converter != null;
+ this.converter = converter;
+ this.factory = converter.application.dexItemFactory;
+
+ DexType metafactoryType = factory.createType(METAFACTORY_TYPE_DESCR);
+ DexType callSiteType = factory.createType(CALLSITE_TYPE_DESCR);
+ DexType lookupType = factory.createType(LOOKUP_TYPE_DESCR);
+ DexType methodTypeType = factory.createType(METHODTYPE_TYPE_DESCR);
+ DexType methodHandleType = factory.createType(METHODHANDLE_TYPE_DESCR);
+ DexType objectArrayType = factory.createType(OBJECT_ARRAY_TYPE_DESCR);
+
+ this.metafactoryMethod = factory.createMethod(metafactoryType,
+ factory.createProto(callSiteType, new DexType[] {
+ lookupType, factory.stringType, methodTypeType,
+ methodTypeType, methodHandleType, methodTypeType
+ }),
+ factory.createString(METAFACTORY_METHOD_NAME));
+
+ this.metafactoryAltMethod = factory.createMethod(metafactoryType,
+ factory.createProto(callSiteType, new DexType[] {
+ lookupType, factory.stringType, methodTypeType, objectArrayType
+ }),
+ factory.createString(METAFACTORY_ALT_METHOD_NAME));
+
+ this.constructorName = factory.createString(Constants.INSTANCE_INITIALIZER_NAME);
+ DexProto initProto = factory.createProto(factory.voidType, EMPTY_TYPE_ARRAY);
+ this.objectInitMethod = factory.createMethod(factory.objectType, initProto, constructorName);
+ this.classConstructorName = factory.createString(Constants.CLASS_INITIALIZER_NAME);
+ this.instanceFieldName = factory.createString(LAMBDA_INSTANCE_FIELD_NAME);
+ this.serializableType = factory.createType(SERIALIZABLE_TYPE_DESCR);
+ }
+
+ /**
+ * Detect and desugar lambdas and method references found in the code.
+ *
+ * NOTE: this method can be called concurrently for several different methods.
+ */
+ public void desugarLambdas(DexEncodedMethod encodedMethod, IRCode code) {
+ DexType currentType = encodedMethod.method.holder;
+ ListIterator<BasicBlock> blocks = code.listIterator();
+ while (blocks.hasNext()) {
+ BasicBlock block = blocks.next();
+ InstructionListIterator instructions = block.listIterator();
+ while (instructions.hasNext()) {
+ Instruction instruction = instructions.next();
+ if (instruction.isInvokeCustom()) {
+ LambdaDescriptor descriptor = inferLambdaDescriptor(
+ instruction.asInvokeCustom().getCallSite());
+ if (descriptor == LambdaDescriptor.MATCH_FAILED) {
+ continue;
+ }
+
+ // We have a descriptor, get or create lambda class.
+ LambdaClass lambdaClass = getOrCreateLambdaClass(descriptor, currentType);
+ assert lambdaClass != null;
+
+ // We rely on patch performing its work in a way which
+ // keeps both `instructions` and `blocks` iterators in
+ // valid state so that we can continue iteration.
+ patchInstruction(lambdaClass, code, blocks, instructions);
+ }
+ }
+ }
+ }
+
+ /**
+ * Adjust accessibility of referenced application symbols or
+ * creates necessary accessors.
+ */
+ public void adjustAccessibility(Builder builder) {
+ // For each lambda class perform necessary adjustment of the
+ // referenced symbols to make them accessible. This can result in
+ // method access relaxation or creation of accessor method.
+ for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
+ lambdaClass.target.ensureAccessibility(builder);
+ }
+ }
+
+ /** Generates lambda classes and adds them to the builder. */
+ public void synthesizeLambdaClasses(Builder builder) {
+ for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
+ DexProgramClass synthesizedClass = lambdaClass.synthesizeLambdaClass();
+ converter.optimizeSynthesizedClass(synthesizedClass);
+ builder.addSynthesizedClass(synthesizedClass, lambdaClass.addToMainDexList.get());
+ }
+ }
+
+ // Matches invoke-custom instruction operands to infer lambda descriptor
+ // corresponding to this lambda invocation point.
+ //
+ // Returns the lambda descriptor or `MATCH_FAILED`.
+ private LambdaDescriptor inferLambdaDescriptor(DexCallSite callSite) {
+ // We check the map before and after inferring lambda descriptor to minimize time
+ // spent in synchronized block. As a result we may throw away calculated descriptor
+ // in rare case when another thread has same call site processed concurrently,
+ // but this is a low price to pay comparing to making whole method synchronous.
+ LambdaDescriptor descriptor = getKnown(knownCallSites, callSite);
+ return descriptor != null ? descriptor
+ : putIfAbsent(knownCallSites, callSite, LambdaDescriptor.infer(this, callSite));
+ }
+
+ private boolean isInMainDexList(DexType type) {
+ return converter.application.mainDexList.contains(type);
+ }
+
+ // Returns a lambda class corresponding to the lambda descriptor and context,
+ // creates the class if it does not yet exist.
+ private LambdaClass getOrCreateLambdaClass(LambdaDescriptor descriptor, DexType accessedFrom) {
+ DexType lambdaClassType = LambdaClass.createLambdaClassType(this, accessedFrom, descriptor);
+ // We check the map twice to to minimize time spent in synchronized block.
+ LambdaClass lambdaClass = getKnown(knownLambdaClasses, lambdaClassType);
+ if (lambdaClass == null) {
+ lambdaClass = putIfAbsent(knownLambdaClasses, lambdaClassType,
+ new LambdaClass(this, accessedFrom, lambdaClassType, descriptor));
+ }
+ if (isInMainDexList(accessedFrom)) {
+ lambdaClass.addToMainDexList.set(true);
+ }
+ return lambdaClass;
+ }
+
+ private <K, V> V getKnown(Map<K, V> map, K key) {
+ synchronized (map) {
+ return map.get(key);
+ }
+ }
+
+ private <K, V> V putIfAbsent(Map<K, V> map, K key, V value) {
+ synchronized (map) {
+ V known = map.get(key);
+ if (known != null) {
+ return known;
+ }
+ map.put(key, value);
+ return value;
+ }
+ }
+
+ // Patches invoke-custom instruction to create or get an instance
+ // of the generated lambda class.
+ private void patchInstruction(LambdaClass lambdaClass, IRCode code,
+ ListIterator<BasicBlock> blocks, InstructionListIterator instructions) {
+ assert lambdaClass != null;
+ assert instructions != null;
+ assert instructions.peekPrevious().isInvokeCustom();
+
+ // Move to the previous instruction, must be InvokeCustom
+ InvokeCustom invoke = instructions.previous().asInvokeCustom();
+
+ // The value representing new lambda instance: we reuse the
+ // value from the original invoke-custom instruction, and thus
+ // all its usages.
+ Value lambdaInstanceValue = invoke.outValue();
+ if (lambdaInstanceValue == null) {
+ // The out value might be empty in case it was optimized out.
+ lambdaInstanceValue = new Value(
+ code.valueNumberGenerator.next(), -1, MoveType.OBJECT, null);
+ }
+
+ // For stateless lambdas we replace InvokeCustom instruction with StaticGet
+ // reading the value of INSTANCE field created for singleton lambda class.
+ if (lambdaClass.isStateless()) {
+ instructions.replaceCurrentInstruction(
+ new StaticGet(MemberType.OBJECT, lambdaInstanceValue, lambdaClass.instanceField));
+ // Note that since we replace one throwing operation with another we don't need
+ // to have any special handling for catch handlers.
+ return;
+ }
+
+ // For stateful lambdas we always create a new instance since we need to pass
+ // captured values to the constructor.
+ //
+ // We replace InvokeCustom instruction with a new NewInstance instruction
+ // instantiating lambda followed by InvokeDirect instruction calling a
+ // constructor on it.
+ //
+ // original:
+ // Invoke-Custom rResult <- { rArg0, rArg1, ... }; call site: ...
+ //
+ // result:
+ // NewInstance rResult <- LambdaClass
+ // Invoke-Direct { rResult, rArg0, rArg1, ... }; method: void LambdaClass.<init>(...)
+ NewInstance newInstance = new NewInstance(lambdaClass.type, lambdaInstanceValue);
+ instructions.replaceCurrentInstruction(newInstance);
+
+ List<Value> arguments = new ArrayList<>();
+ arguments.add(lambdaInstanceValue);
+ arguments.addAll(invoke.arguments()); // Optional captures.
+ InvokeDirect constructorCall = new InvokeDirect(
+ lambdaClass.constructor, null /* no return value */, arguments);
+ instructions.add(constructorCall);
+
+ // If we don't have catch handlers we are done.
+ if (!constructorCall.getBlock().hasCatchHandlers()) {
+ return;
+ }
+
+ // Move the iterator back to position it between the two instructions, split
+ // the block between the two instructions, and copy the catch handlers.
+ instructions.previous();
+ assert instructions.peekNext().isInvokeDirect();
+ BasicBlock currentBlock = newInstance.getBlock();
+ BasicBlock nextBlock = instructions.split(code, blocks);
+ assert !instructions.hasNext();
+ nextBlock.copyCatchHandlers(code, blocks, currentBlock);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SingleBlockSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/SingleBlockSourceCode.java
new file mode 100644
index 0000000..32631c2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/SingleBlockSourceCode.java
@@ -0,0 +1,212 @@
+// 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.ir.desugar;
+
+import static com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo.NO_THROW;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.SourceCode;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+abstract class SingleBlockSourceCode implements SourceCode {
+ final DexType receiver;
+ final DexProto proto;
+
+ // The next free register, note that we always
+ // assign each value a new (next available) register.
+ private int nextRegister = 0;
+
+ // Registers for receiver and parameters
+ private final int receiverRegister;
+ private int[] paramRegisters;
+ // Values representing receiver and parameters will be filled in
+ // buildPrelude() and should only be accessed via appropriate methods
+ private Value receiverValue;
+ private Value[] paramValues;
+
+ // Instruction constructors
+ private List<Consumer<IRBuilder>> constructors = new ArrayList<>();
+
+ SingleBlockSourceCode(DexType receiver, DexProto proto) {
+ assert proto != null;
+ this.receiver = receiver;
+ this.proto = proto;
+
+ // Initialize register values for receiver and arguments
+ this.receiverRegister = receiver != null ? nextRegister(MoveType.OBJECT) : -1;
+
+ DexType[] params = proto.parameters.values;
+ int paramCount = params.length;
+ this.paramRegisters = new int[paramCount];
+ this.paramValues = new Value[paramCount];
+ for (int i = 0; i < paramCount; i++) {
+ this.paramRegisters[i] = nextRegister(MoveType.fromDexType(params[i]));
+ }
+ }
+
+ final void add(Consumer<IRBuilder> constructor) {
+ constructors.add(constructor);
+ }
+
+ final int nextRegister(MoveType type) {
+ int value = nextRegister;
+ nextRegister += type == MoveType.WIDE ? 2 : 1;
+ return value;
+ }
+
+ final Value getReceiverValue() {
+ assert receiver != null;
+ assert receiverValue != null;
+ return receiverValue;
+ }
+
+ final int getReceiverRegister() {
+ assert receiver != null;
+ assert receiverRegister >= 0;
+ return receiverRegister;
+ }
+
+ final Value getParamValue(int paramIndex) {
+ assert paramIndex >= 0;
+ assert paramIndex < paramValues.length;
+ return paramValues[paramIndex];
+ }
+
+ final int getParamCount() {
+ return paramValues.length;
+ }
+
+ final int getParamRegister(int paramIndex) {
+ assert paramIndex >= 0;
+ assert paramIndex < paramRegisters.length;
+ return paramRegisters[paramIndex];
+ }
+
+ abstract void prepareInstructions();
+
+ @Override
+ public final boolean needsPrelude() {
+ return receiver != null || paramRegisters.length > 0;
+ }
+
+ @Override
+ public final int instructionCount() {
+ return constructors.size();
+ }
+
+ @Override
+ public final int instructionIndex(int instructionOffset) {
+ return instructionOffset;
+ }
+
+ @Override
+ public final int instructionOffset(int instructionIndex) {
+ return instructionIndex;
+ }
+
+ @Override
+ public DebugLocalInfo getCurrentLocal(int register) {
+ return null;
+ }
+
+ @Override
+ public final boolean traceInstruction(int instructionIndex, IRBuilder builder) {
+ return instructionIndex == constructors.size() - 1;
+ }
+
+ @Override
+ public final void closedCurrentBlockWithFallthrough(int fallthroughInstructionIndex) {
+ }
+
+ @Override
+ public final void closedCurrentBlock() {
+ }
+
+ @Override
+ public final void setUp() {
+ assert constructors.isEmpty();
+ prepareInstructions();
+ assert !constructors.isEmpty();
+ }
+
+ @Override
+ public final void clear() {
+ constructors = null;
+ paramRegisters = null;
+ paramValues = null;
+ receiverValue = null;
+ }
+
+ @Override
+ public final void buildPrelude(IRBuilder builder) {
+ if (receiver != null) {
+ receiverValue = builder.writeRegister(receiverRegister, MoveType.OBJECT, NO_THROW);
+ builder.add(new Argument(receiverValue));
+ }
+
+ // Fill in the Argument instructions in the argument block.
+ DexType[] parameters = proto.parameters.values;
+ for (int i = 0; i < parameters.length; i++) {
+ MoveType moveType = MoveType.fromDexType(parameters[i]);
+ Value paramValue = builder.writeRegister(paramRegisters[i], moveType, NO_THROW);
+ paramValues[i] = paramValue;
+ builder.add(new Argument(paramValue));
+ }
+ }
+
+ @Override
+ public final void buildPostlude(IRBuilder builder) {
+ // Intentionally left empty.
+ }
+
+ @Override
+ public final void buildInstruction(IRBuilder builder, int instructionIndex) {
+ constructors.get(instructionIndex).accept(builder);
+ }
+
+ @Override
+ public final void resolveAndBuildSwitch(
+ Switch.Type type, int value, int fallthroughOffset,
+ int payloadOffset, IRBuilder builder) {
+ throw new Unreachable("Unexpected call to resolveAndBuildSwitch");
+ }
+
+ @Override
+ public final void resolveAndBuildNewArrayFilledData(
+ int arrayRef, int payloadOffset, IRBuilder builder) {
+ throw new Unreachable("Unexpected call to resolveAndBuildNewArrayFilledData");
+ }
+
+ @Override
+ public final CatchHandlers<Integer> getCurrentCatchHandlers() {
+ return null;
+ }
+
+ @Override
+ public final boolean verifyCurrentInstructionCanThrow() {
+ return true;
+ }
+
+ @Override
+ public boolean verifyLocalInScope(DebugLocalInfo local) {
+ return true;
+ }
+
+ @Override
+ public final boolean verifyRegister(int register) {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedCode.java
new file mode 100644
index 0000000..3a07ef4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedCode.java
@@ -0,0 +1,54 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.SourceCode;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.InternalOptions;
+
+public final class SynthesizedCode extends Code {
+ private final SourceCode sourceCode;
+
+ public SynthesizedCode(SourceCode sourceCode) {
+ this.sourceCode = sourceCode;
+ }
+
+ @Override
+ public final IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
+ return new IRBuilder(encodedMethod, sourceCode, options).build();
+ }
+
+ @Override
+ public final String toString() {
+ return toString(null);
+ }
+
+ @Override
+ public final void registerReachableDefinitions(UseRegistry registry) {
+ throw new Unreachable();
+ }
+
+ @Override
+ protected final int computeHashCode() {
+ return sourceCode.hashCode();
+ }
+
+ @Override
+ protected final boolean computeEquals(Object other) {
+ return other instanceof SynthesizedCode &&
+ this.sourceCode.equals(((SynthesizedCode) other).sourceCode);
+ }
+
+ @Override
+ public final String toString(ClassNameMapper naming) {
+ return "SynthesizedCode: " + sourceCode.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
new file mode 100644
index 0000000..7a63c32
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
@@ -0,0 +1,57 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+
+// Represents source code of synthesized lambda class methods.
+abstract class SynthesizedLambdaSourceCode extends SingleBlockSourceCode {
+ final DexMethod currentMethod;
+ final LambdaClass lambda;
+
+ SynthesizedLambdaSourceCode(DexType receiver, LambdaClass lambda, DexMethod currentMethod) {
+ super(receiver, currentMethod.proto);
+ this.lambda = lambda;
+ this.currentMethod = currentMethod;
+ }
+
+ SynthesizedLambdaSourceCode(LambdaClass lambda, DexMethod currentMethod) {
+ this(lambda.type, lambda, currentMethod);
+ }
+
+ final LambdaDescriptor descriptor() {
+ return lambda.descriptor;
+ }
+
+ final DexType[] captures() {
+ DexTypeList captures = descriptor().captures;
+ assert captures != null;
+ return captures.values;
+ }
+
+ final DexItemFactory factory() {
+ return lambda.rewriter.factory;
+ }
+
+ final int enforceParameterType(int register, DexType paramType, DexType enforcedType) {
+ // `paramType` must be either same as `enforcedType` or both must be class
+ // types and `enforcedType` must be a subclass of `paramType` in which case
+ // a cast need to be inserted.
+ if (paramType != enforcedType) {
+ assert LambdaDescriptor.isSameOrDerived(factory(), enforcedType, paramType);
+ add(builder -> builder.addCheckCast(register, enforcedType));
+ }
+ return register;
+ }
+
+ @Override
+ public String toString() {
+ return currentMethod.toSourceString();
+ }
+}
+
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java b/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java
new file mode 100644
index 0000000..27cf89a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.google.common.base.Equivalence;
+import java.util.List;
+
+class BasicBlockInstructionsEquivalence extends Equivalence<BasicBlock> {
+
+ private static final int MAX_HASH_INSTRUCTIONS = 5;
+ private final RegisterAllocator allocator;
+
+ BasicBlockInstructionsEquivalence(RegisterAllocator allocator) {
+ this.allocator = allocator;
+ }
+
+ private boolean hasIdenticalInstructions(BasicBlock first, BasicBlock second) {
+ List<Instruction> instructions0 = first.getInstructions();
+ List<Instruction> instructions1 = second.getInstructions();
+ if (instructions0.size() != instructions1.size()) {
+ return false;
+ }
+ for (int i = 0; i < instructions0.size(); i++) {
+ Instruction i0 = instructions0.get(i);
+ Instruction i1 = instructions1.get(i);
+ if (!i0.identicalAfterRegisterAllocation(i1, allocator)) {
+ return false;
+ }
+ }
+ CatchHandlers<BasicBlock> handlers0 = first.getCatchHandlers();
+ CatchHandlers<BasicBlock> handlers1 = second.getCatchHandlers();
+ if (!handlers0.equals(handlers1)) {
+ return false;
+ }
+ // Normal successors are equal based on equality of the instruction stream. Verify that here.
+ assert verifyAllSuccessors(first.getSuccessors(), second.getSuccessors());
+ return true;
+ }
+
+ private boolean verifyAllSuccessors(List<BasicBlock> successors0, List<BasicBlock> successors1) {
+ if (successors0.size() != successors1.size()) {
+ return false;
+ }
+ for (int i = 0; i < successors0.size(); i++) {
+ if (successors0.get(i) != successors1.get(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean doEquivalent(BasicBlock a, BasicBlock b) {
+ return hasIdenticalInstructions(a, b);
+ }
+
+ @Override
+ protected int doHash(BasicBlock basicBlock) {
+ List<Instruction> instructions = basicBlock.getInstructions();
+ int hash = instructions.size();
+ for (int i = 0; i < instructions.size() && i < MAX_HASH_INSTRUCTIONS; i++) {
+ Instruction instruction = instructions.get(i);
+ int hashPart = 0;
+ if (instruction.outValue() != null && instruction.outValue().needsRegister()) {
+ hashPart += allocator.getRegisterForValue(instruction.outValue(), instruction.getNumber());
+ }
+ for (Value inValue : instruction.inValues()) {
+ hashPart = hashPart << 4;
+ if (inValue.needsRegister()) {
+ hashPart += allocator.getRegisterForValue(inValue, instruction.getNumber());
+ }
+ }
+ hash = hash * 3 + hashPart;
+ }
+ return hash;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
new file mode 100644
index 0000000..5e701ed
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -0,0 +1,941 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.ArrayPut;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.Binop;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.Cmp;
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.DominatorTree;
+import com.android.tools.r8.ir.code.Goto;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.JumpInstruction;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NewArrayEmpty;
+import com.android.tools.r8.ir.code.NewArrayFilledData;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.LongInterval;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ListMultimap;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+public class CodeRewriter {
+
+ private static final int UNKNOWN_CAN_THROW = 0;
+ private static final int CAN_THROW = 1;
+ private static final int CANNOT_THROW = 2;
+ private static final int MAX_FILL_ARRAY_SIZE = 4 * Constants.KILOBYTE;
+
+ private final AppInfo appInfo;
+ private final DexItemFactory dexItemFactory;
+
+ public CodeRewriter(AppInfo appInfo) {
+ this.appInfo = appInfo;
+ this.dexItemFactory = appInfo.dexItemFactory;
+ }
+
+ /**
+ * Removes all debug positions that are not needed to maintain proper stack trace information.
+ * If a debug position is followed by another debug position and no instructions between the two
+ * can throw then it is unneeded (in a release build).
+ * If a block with a position has (normal) outgoing edges, this property depends on the
+ * possibility of the successors throwing before the next debug position is hit.
+ */
+ public static boolean removedUnneededDebugPositions(IRCode code) {
+ computeThrowsColorForAllBlocks(code);
+ for (BasicBlock block : code.blocks) {
+ InstructionListIterator iterator = block.listIterator();
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ if (instruction.isDebugPosition()
+ && getThrowsColorForBlock(block, iterator.nextIndex()) == CANNOT_THROW) {
+ iterator.remove();
+ }
+ }
+ }
+ return true;
+ }
+
+ private static void computeThrowsColorForAllBlocks(IRCode code) {
+ // First pass colors blocks in reverse topological order, based on the instructions.
+ code.clearMarks();
+ List<BasicBlock> blocks = code.blocks;
+ ArrayList<BasicBlock> worklist = new ArrayList<>();
+ for (int i = blocks.size() - 1; i >= 0; i--) {
+ BasicBlock block = blocks.get(i);
+ // Mark the block as not-throwing if no successor implies otherwise.
+ // This ensures that a loop back to this block will be seen as non-throwing.
+ block.setColor(CANNOT_THROW);
+ int color = getThrowsColorForBlock(block, 0);
+ block.setColor(color);
+ if (color == UNKNOWN_CAN_THROW) {
+ worklist.add(block);
+ }
+ }
+ // A fixed point then ensures that we propagate the color backwards over normal edges.
+ ArrayList<BasicBlock> remaining = new ArrayList<>(worklist.size());
+ while (!worklist.isEmpty()) {
+ ImmutableList<BasicBlock> work = new ImmutableList.Builder<BasicBlock>()
+ .addAll(worklist)
+ .addAll(remaining)
+ .build();
+ worklist.clear();
+ remaining.clear();
+ for (BasicBlock block : work) {
+ if (!block.hasColor(UNKNOWN_CAN_THROW)) {
+ continue;
+ }
+ block.setColor(CANNOT_THROW);
+ int color = getThrowsColorForSuccessors(block);
+ block.setColor(color);
+ if (color == UNKNOWN_CAN_THROW) {
+ remaining.add(block);
+ } else {
+ for (BasicBlock predecessor : block.getNormalPredecessors()) {
+ if (predecessor.hasColor(UNKNOWN_CAN_THROW)) {
+ worklist.add(predecessor);
+ }
+ }
+ }
+ }
+ }
+ // Any remaining set of blocks represents a cycle of blocks containing no throwing instructions.
+ for (BasicBlock block : remaining) {
+ assert !block.canThrow();
+ block.setColor(CANNOT_THROW);
+ }
+ }
+
+ private static int getThrowsColorForBlock(BasicBlock block, int index) {
+ InstructionListIterator iterator = block.listIterator(index);
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ if (instruction.isDebugPosition()) {
+ return CANNOT_THROW;
+ }
+ if (instruction.instructionTypeCanThrow()) {
+ return CAN_THROW;
+ }
+ }
+ return getThrowsColorForSuccessors(block);
+ }
+
+ private static int getThrowsColorForSuccessors(BasicBlock block) {
+ int color = CANNOT_THROW;
+ for (BasicBlock successor : block.getNormalSucessors()) {
+ if (successor.hasColor(CAN_THROW)) {
+ return CAN_THROW;
+ }
+ if (successor.hasColor(UNKNOWN_CAN_THROW)) {
+ color = UNKNOWN_CAN_THROW;
+ }
+ }
+ return color;
+ }
+
+ private static boolean removedTrivialGotos(IRCode code) {
+ ListIterator<BasicBlock> iterator = code.listIterator();
+ assert iterator.hasNext();
+ BasicBlock block = iterator.next();
+ BasicBlock nextBlock;
+ do {
+ nextBlock = iterator.hasNext() ? iterator.next() : null;
+ // Trivial goto block are only kept if they are self-targeting or are targeted by
+ // fallthroughs.
+ BasicBlock blk = block; // Additional local for lambda below.
+ assert !block.isTrivialGoto()
+ || block.exit().asGoto().getTarget() == block
+ || block.getPredecessors().stream().anyMatch((b) -> b.exit().fallthroughBlock() == blk);
+ // Trivial goto blocks never target the next block (in that case there should just be a
+ // fallthrough).
+ assert !block.isTrivialGoto() || block.exit().asGoto().getTarget() != nextBlock;
+ block = nextBlock;
+ } while (block != null);
+ return true;
+ }
+
+ private static BasicBlock endOfGotoChain(BasicBlock block) {
+ block.mark();
+ BasicBlock target = block;
+ while (target.isTrivialGoto()) {
+ BasicBlock nextTarget = target.exit().asGoto().getTarget();
+ if (nextTarget.isMarked()) {
+ clearTrivialGotoMarks(block);
+ return nextTarget;
+ }
+ nextTarget.mark();
+ target = nextTarget;
+ }
+ clearTrivialGotoMarks(block);
+ return target;
+ }
+
+ private static void clearTrivialGotoMarks(BasicBlock block) {
+ while (block.isMarked()) {
+ block.clearMark();
+ if (block.isTrivialGoto()) {
+ block = block.exit().asGoto().getTarget();
+ }
+ }
+ }
+
+ private static void collapsTrivialGoto(
+ BasicBlock block, BasicBlock nextBlock, List<BasicBlock> blocksToRemove) {
+
+ // This is the base case for GOTO loops.
+ if (block.exit().asGoto().getTarget() == block) {
+ return;
+ }
+
+ BasicBlock target = endOfGotoChain(block);
+
+ boolean needed = false;
+ if (target != nextBlock) {
+ for (BasicBlock pred : block.getPredecessors()) {
+ if (pred.exit().fallthroughBlock() == block) {
+ needed = true;
+ break;
+ }
+ }
+ }
+
+ // This implies we are in a loop of GOTOs. In that case, we will iteratively remove each trival
+ // GOTO one-by-one until the above base case (one block targeting itself) is left.
+ if (target == block) {
+ target = block.exit().asGoto().getTarget();
+ }
+
+ if (!needed) {
+ blocksToRemove.add(block);
+ for (BasicBlock pred : block.getPredecessors()) {
+ pred.replaceSuccessor(block, target);
+ }
+ for (BasicBlock succ : block.getSuccessors()) {
+ succ.getPredecessors().remove(block);
+ }
+ for (BasicBlock pred : block.getPredecessors()) {
+ if (!target.getPredecessors().contains(pred)) {
+ target.getPredecessors().add(pred);
+ }
+ }
+ }
+ }
+
+ private static void collapsIfTrueTarget(BasicBlock block) {
+ If insn = block.exit().asIf();
+ BasicBlock target = insn.getTrueTarget();
+ BasicBlock newTarget = endOfGotoChain(target);
+ BasicBlock fallthrough = insn.fallthroughBlock();
+ BasicBlock newFallthrough = endOfGotoChain(fallthrough);
+ if (target != newTarget) {
+ insn.getBlock().replaceSuccessor(target, newTarget);
+ target.getPredecessors().remove(block);
+ if (!newTarget.getPredecessors().contains(block)) {
+ newTarget.getPredecessors().add(block);
+ }
+ }
+ if (block.exit().isIf()) {
+ insn = block.exit().asIf();
+ if (insn.getTrueTarget() == newFallthrough) {
+ // Replace if with the same true and fallthrough target with a goto to the fallthrough.
+ block.replaceSuccessor(insn.getTrueTarget(), fallthrough);
+ assert block.exit().isGoto();
+ assert block.exit().asGoto().getTarget() == fallthrough;
+ }
+ }
+ }
+
+ private static void collapsNonFallthroughSwitchTargets(BasicBlock block) {
+ Switch insn = block.exit().asSwitch();
+ BasicBlock fallthroughBlock = insn.fallthroughBlock();
+ Set<BasicBlock> replacedBlocks = new HashSet<>();
+ for (int j = 0; j < insn.targetBlockIndices().length; j++) {
+ BasicBlock target = insn.targetBlock(j);
+ if (target != fallthroughBlock) {
+ BasicBlock newTarget = endOfGotoChain(target);
+ if (target != newTarget && !replacedBlocks.contains(target)) {
+ insn.getBlock().replaceSuccessor(target, newTarget);
+ target.getPredecessors().remove(block);
+ if (!newTarget.getPredecessors().contains(block)) {
+ newTarget.getPredecessors().add(block);
+ }
+ replacedBlocks.add(target);
+ }
+ }
+ }
+ }
+
+ /**
+ * Rewrite all branch targets to the destination of trivial goto chains when possible.
+ * Does not rewrite fallthrough targets as that would require block reordering and the
+ * transformation only makes sense after SSA destruction where there are no phis.
+ */
+ public static void collapsTrivialGotos(DexEncodedMethod method, IRCode code) {
+ assert code.isConsistentGraph();
+ List<BasicBlock> blocksToRemove = new ArrayList<>();
+ // Rewrite all non-fallthrough targets to the end of trivial goto chains and remove
+ // first round of trivial goto blocks.
+ ListIterator<BasicBlock> iterator = code.listIterator();
+ assert iterator.hasNext();
+ BasicBlock block = iterator.next();
+ BasicBlock nextBlock;
+
+ // The marks will be used for cycle detection.
+ code.clearMarks();
+ do {
+ nextBlock = iterator.hasNext() ? iterator.next() : null;
+ if (block.isTrivialGoto()) {
+ collapsTrivialGoto(block, nextBlock, blocksToRemove);
+ }
+ if (block.exit().isIf()) {
+ collapsIfTrueTarget(block);
+ }
+ if (block.exit().isSwitch()) {
+ collapsNonFallthroughSwitchTargets(block);
+ }
+ block = nextBlock;
+ } while (nextBlock != null);
+ code.removeBlocks(blocksToRemove);
+ // Get rid of gotos to the next block.
+ while (!blocksToRemove.isEmpty()) {
+ blocksToRemove = new ArrayList<>();
+ iterator = code.listIterator();
+ block = iterator.next();
+ do {
+ nextBlock = iterator.hasNext() ? iterator.next() : null;
+ if (block.isTrivialGoto()) {
+ collapsTrivialGoto(block, nextBlock, blocksToRemove);
+ }
+ block = nextBlock;
+ } while (block != null);
+ code.removeBlocks(blocksToRemove);
+ }
+ assert removedTrivialGotos(code);
+ assert code.isConsistentGraph();
+ }
+
+ public void identifyReturnsArgument(
+ DexEncodedMethod method, IRCode code) {
+ if (code.getNormalExitBlock() != null) {
+ Return ret = code.getNormalExitBlock().exit().asReturn();
+ if (!ret.isReturnVoid()) {
+ Value returnValue = ret.returnValue();
+ if (returnValue.isArgument()) {
+ // Find the argument number.
+ int index = code.collectArguments().indexOf(returnValue);
+ assert index != -1;
+ method.markReturnsArgument(index);
+ }
+ if (returnValue.isConstant() && returnValue.definition.isConstNumber()) {
+ long value = returnValue.definition.asConstNumber().getRawValue();
+ method.markReturnsConstant(value);
+ }
+ if (returnValue.isNeverNull()) {
+ method.markNeverReturnsNull();
+ }
+ }
+ }
+ }
+
+ private boolean checkArgumentType(InvokeMethod invoke, DexMethod target, int argumentIndex) {
+ DexType returnType = invoke.getInvokedMethod().proto.returnType;
+ // TODO(sgjesse): Insert cast if required.
+ if (invoke.isInvokeStatic()) {
+ return invoke.getInvokedMethod().proto.parameters.values[argumentIndex] == returnType;
+ } else {
+ if (argumentIndex == 0) {
+ return invoke.getInvokedMethod().getHolder() == returnType;
+ } else {
+ return invoke.getInvokedMethod().proto.parameters.values[argumentIndex - 1] == returnType;
+ }
+ }
+ }
+
+ // Replace result uses for methods where something is known about what is returned.
+ public void rewriteMoveResult(IRCode code) {
+ if (!appInfo.hasSubtyping()) {
+ return;
+ }
+ InstructionIterator iterator = code.instructionIterator();
+ while (iterator.hasNext()) {
+ Instruction current = iterator.next();
+ if (current.isInvokeMethod()) {
+ InvokeMethod invoke = current.asInvokeMethod();
+ if (invoke.outValue() != null) {
+ DexEncodedMethod target = invoke.computeSingleTarget(appInfo.withSubtyping());
+ if (target != null) {
+ DexMethod invokedMethod = target.method;
+ // Check if the invoked method is known to return one of its arguments.
+ DexEncodedMethod definition = appInfo.definitionFor(invokedMethod);
+ if (definition != null && definition.getOptimizationInfo().returnsArgument()) {
+ int argumentIndex = definition.getOptimizationInfo().getReturnedArgument();
+ // Replace the out value of the invoke with the argument and ignore the out value.
+ if (argumentIndex != -1 && checkArgumentType(invoke, target.method, argumentIndex)) {
+ Value argument = invoke.arguments().get(argumentIndex);
+ assert (invoke.outType() == argument.outType()) ||
+ (invoke.outType() == MoveType.OBJECT
+ && argument.outType() == MoveType.SINGLE
+ && argument.getConstInstruction().asConstNumber().isZero());
+ invoke.outValue().replaceUsers(argument);
+ invoke.setOutValue(null);
+ }
+ }
+ }
+ }
+ }
+ }
+ assert code.isConsistentGraph();
+ }
+
+ private boolean canBeFolded(Instruction instruction) {
+ return (instruction.isBinop() && instruction.asBinop().canBeFolded()) ||
+ (instruction.isUnop() && instruction.asUnop().canBeFolded());
+ }
+
+ public void foldConstants(IRCode code) {
+ Queue<BasicBlock> worklist = new LinkedList<>();
+ worklist.addAll(code.blocks);
+ for (BasicBlock block = worklist.poll(); block != null; block = worklist.poll()) {
+ InstructionIterator iterator = block.iterator();
+ while (iterator.hasNext()) {
+ Instruction current = iterator.next();
+ Instruction folded;
+ if (canBeFolded(current)) {
+ folded = current.fold(code.valueNumberGenerator);
+ iterator.replaceCurrentInstruction(folded);
+ folded.outValue().uniqueUsers()
+ .forEach(instruction -> worklist.add(instruction.getBlock()));
+ }
+ }
+ }
+ assert code.isConsistentSSA();
+ }
+
+ // Constants are canonicalized in the entry block. We split some of them when it is likely
+ // that having them canonicalized in the entry block will lead to poor code quality.
+ public void splitConstants(IRCode code) {
+ for (BasicBlock block : code.blocks) {
+ // Split constants that flow into phis. It is likely that these constants will have moves
+ // generated for them anyway and we might as well insert a const instruction in the right
+ // predecessor block.
+ splitPhiConstants(code, block);
+ // Split constants that flow into ranged invokes. This gives the register allocator more
+ // freedom in assigning register to ranged invokes which can greatly reduce the number
+ // of register needed (and thereby code size as well).
+ splitRangedInvokeConstants(code, block);
+ }
+ }
+
+ private void splitRangedInvokeConstants(IRCode code, BasicBlock block) {
+ InstructionListIterator it = block.listIterator();
+ while (it.hasNext()) {
+ Instruction current = it.next();
+ if (current.isInvoke() && current.asInvoke().requiredArgumentRegisters() > 5) {
+ Invoke invoke = current.asInvoke();
+ it.previous();
+ Map<ConstNumber, ConstNumber> oldToNew = new HashMap<>();
+ for (int i = 0; i < invoke.inValues().size(); i++) {
+ Value value = invoke.inValues().get(i);
+ if (value.isConstant() && value.numberOfUsers() > 1) {
+ ConstNumber definition = value.getConstInstruction().asConstNumber();
+ Value originalValue = definition.outValue();
+ ConstNumber newNumber = oldToNew.get(definition);
+ if (newNumber == null) {
+ newNumber = ConstNumber.copyOf(code, definition);
+ it.add(newNumber);
+ oldToNew.put(definition, newNumber);
+ }
+ invoke.inValues().set(i, newNumber.outValue());
+ originalValue.removeUser(invoke);
+ newNumber.outValue().addUser(invoke);
+ }
+ }
+ it.next();
+ }
+ }
+ }
+
+ private void splitPhiConstants(IRCode code, BasicBlock block) {
+ for (int i = 0; i < block.getPredecessors().size(); i++) {
+ Map<ConstNumber, ConstNumber> oldToNew = new HashMap<>();
+ BasicBlock predecessor = block.getPredecessors().get(i);
+ for (Phi phi : block.getPhis()) {
+ Value operand = phi.getOperand(i);
+ if (!operand.isPhi() && operand.isConstant()) {
+ ConstNumber definition = operand.getConstInstruction().asConstNumber();
+ ConstNumber newNumber = oldToNew.get(definition);
+ Value originalValue = definition.outValue();
+ if (newNumber == null) {
+ newNumber = ConstNumber.copyOf(code, definition);
+ oldToNew.put(definition, newNumber);
+ insertConstantInBlock(newNumber, predecessor);
+ }
+ phi.getOperands().set(i, newNumber.outValue());
+ originalValue.removePhiUser(phi);
+ newNumber.outValue().addPhiUser(phi);
+ }
+ }
+ }
+ }
+
+ public void shortenLiveRanges(IRCode code) {
+ // Currently, we are only shortening the live range of constants in the entry block.
+ // TODO(ager): Generalize this to shorten live ranges for more instructions? Currently
+ // doing so seems to make things worse.
+ DominatorTree dominatorTree = new DominatorTree(code);
+ BasicBlock block = code.blocks.get(0);
+ InstructionListIterator it = block.listIterator();
+ List<Instruction> toInsertInThisBlock = new ArrayList<>();
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ if (instruction.isConstNumber()) {
+ // Collect the blocks for all users of the constant.
+ List<BasicBlock> userBlocks = new LinkedList<>();
+ for (Instruction user : instruction.outValue().uniqueUsers()) {
+ userBlocks.add(user.getBlock());
+ }
+ for (Phi phi : instruction.outValue().uniquePhiUsers()) {
+ userBlocks.add(phi.getBlock());
+ }
+ // Locate the closest dominator block for all user blocks.
+ BasicBlock dominator = dominatorTree.closestDominator(userBlocks);
+ // If the closest dominator block is a block that uses the constant for a phi the constant
+ // needs to go in the immediate dominator block so that it is available for phi moves.
+ for (Phi phi : instruction.outValue().uniquePhiUsers()) {
+ if (phi.getBlock() == dominator) {
+ dominator = dominatorTree.immediateDominator(dominator);
+ break;
+ }
+ }
+ // Move the const instruction as close to its uses as possible.
+ it.detach();
+ if (dominator != block) {
+ insertConstantInBlock(instruction, dominator);
+ } else {
+ toInsertInThisBlock.add(instruction);
+ }
+ }
+ }
+ for (Instruction toInsert : toInsertInThisBlock) {
+ insertConstantInBlock(toInsert, block);
+ }
+ }
+
+ private void insertConstantInBlock(Instruction instruction, BasicBlock block) {
+ boolean hasCatchHandlers = block.hasCatchHandlers();
+ InstructionListIterator insertAt = block.listIterator();
+ // Place the instruction as late in the block as we can. It needs to go before users
+ // and if we have catch handlers it needs to be placed before the throwing instruction.
+ insertAt.nextUntil(i -> {
+ return i.inValues().contains(instruction.outValue())
+ || i.isJumpInstruction()
+ || (hasCatchHandlers && i.instructionInstanceCanThrow());
+ });
+ insertAt.previous();
+ insertAt.add(instruction);
+ }
+
+ private short[] computeArrayFilledData(
+ NewArrayEmpty newArray, int size, BasicBlock block, int elementSize) {
+ ConstNumber[] values = computeConstantArrayValues(newArray, block, size);
+ if (values == null) {
+ return null;
+ }
+ if (elementSize == 1) {
+ short[] result = new short[(size + 1) / 2];
+ for (int i = 0; i < size; i += 2) {
+ assert values[i].getIntValue() <= Constants.S8BIT_MAX
+ && values[i].getIntValue() >= Constants.S8BIT_MIN;
+ short value = (short) (values[i].getIntValue() & 0xFF);
+ if (i + 1 < size) {
+ value |= (short) ((values[i + 1].getIntValue() & 0xFF) << 8);
+ }
+ result[i / 2] = value;
+ }
+ return result;
+ }
+ assert elementSize == 2 || elementSize == 4 || elementSize == 8;
+ int shortsPerConstant = elementSize / 2;
+ short[] result = new short[size * shortsPerConstant];
+ for (int i = 0; i < size; i++) {
+ long value = values[i].getRawValue();
+ for (int part = 0; part < shortsPerConstant; part++) {
+ result[i * shortsPerConstant + part] = (short) ((value >> (16 * part)) & 0xFFFFL);
+ }
+ }
+ return result;
+ }
+
+ private ConstNumber[] computeConstantArrayValues(
+ NewArrayEmpty newArray, BasicBlock block, int size) {
+ if (size > MAX_FILL_ARRAY_SIZE) {
+ return null;
+ }
+ ConstNumber[] values = new ConstNumber[size];
+ int remaining = size;
+ Set<Instruction> users = newArray.outValue().uniqueUsers();
+ // We allow the array instantiations to cross block boundaries as long as it hasn't encountered
+ // an instruction instance that can throw an exception.
+ InstructionListIterator it = block.listIterator();
+ it.nextUntil(i -> i == newArray);
+ do {
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ // If we encounter an instruction that can throw an exception we need to bail out of the
+ // optimization so that we do not transform half-initialized arrays into fully initialized
+ // arrays on exceptional edges.
+ if (instruction.instructionInstanceCanThrow()) {
+ return null;
+ }
+ if (!users.contains(instruction)) {
+ continue;
+ }
+ // If the initialization sequence is broken by another use we cannot use a
+ // fill-array-data instruction.
+ if (!instruction.isArrayPut()) {
+ return null;
+ }
+ ArrayPut arrayPut = instruction.asArrayPut();
+ if (!arrayPut.source().isConstant()) {
+ return null;
+ }
+ assert arrayPut.index().isConstant();
+ int index = arrayPut.index().getConstInstruction().asConstNumber().getIntValue();
+ assert index >= 0 && index < values.length;
+ if (values[index] != null) {
+ return null;
+ }
+ ConstNumber value = arrayPut.source().getConstInstruction().asConstNumber();
+ values[index] = value;
+ --remaining;
+ if (remaining == 0) {
+ return values;
+ }
+ }
+ block = block.exit().isGoto() ? block.exit().asGoto().getTarget() : null;
+ it = block != null ? block.listIterator() : null;
+ } while (it != null);
+ return null;
+ }
+
+ private boolean isPrimitiveNewArrayWithConstantPositiveSize(Instruction instruction) {
+ if (!(instruction instanceof NewArrayEmpty)) {
+ return false;
+ }
+ NewArrayEmpty newArray = instruction.asNewArrayEmpty();
+ if (!newArray.size().isConstant()) {
+ return false;
+ }
+ int size = newArray.size().getConstInstruction().asConstNumber().getIntValue();
+ if (size < 1) {
+ return false;
+ }
+ if (!newArray.type.isPrimitiveArrayType()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Replace NewArrayEmpty followed by stores of constants to all entries with NewArrayEmpty
+ * and FillArrayData.
+ */
+ public void simplifyArrayConstruction(IRCode code) {
+ for (BasicBlock block : code.blocks) {
+ // Map from the array value to the number of array put instruction to remove for that value.
+ Map<Value, Integer> storesToRemoveForArray = new HashMap<>();
+ // First pass: identify candidates and insert fill array data instruction.
+ InstructionListIterator it = block.listIterator();
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ if (!isPrimitiveNewArrayWithConstantPositiveSize(instruction)) {
+ continue;
+ }
+ NewArrayEmpty newArray = instruction.asNewArrayEmpty();
+ int size = newArray.size().getConstInstruction().asConstNumber().getIntValue();
+ // If there is only one element it is typically smaller to generate the array put
+ // instruction instead of fill array data.
+ if (size == 1) {
+ continue;
+ }
+ int elementSize = newArray.type.elementSizeForPrimitiveArrayType();
+ short[] contents = computeArrayFilledData(newArray, size, block, elementSize);
+ if (contents == null) {
+ continue;
+ }
+ storesToRemoveForArray.put(newArray.outValue(), size);
+ int arraySize = newArray.size().getConstInstruction().asConstNumber().getIntValue();
+ NewArrayFilledData fillArray = new NewArrayFilledData(
+ newArray.outValue(), elementSize, arraySize, contents);
+ it.add(fillArray);
+ }
+ // Second pass: remove all the array put instructions for the array for which we have
+ // inserted a fill array data instruction instead.
+ if (!storesToRemoveForArray.isEmpty()) {
+ do {
+ it = block.listIterator();
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ if (instruction.isArrayPut()) {
+ Value array = instruction.asArrayPut().array();
+ Integer toRemoveCount = storesToRemoveForArray.get(array);
+ if (toRemoveCount != null && toRemoveCount > 0) {
+ storesToRemoveForArray.put(array, toRemoveCount - 1);
+ it.remove();
+ }
+ }
+ }
+ block = block.exit().isGoto() ? block.exit().asGoto().getTarget() : null;
+ } while (block != null);
+ }
+ }
+ }
+
+ private class ExpressionEquivalence extends Equivalence<Instruction> {
+
+ @Override
+ protected boolean doEquivalent(Instruction a, Instruction b) {
+ if (a.getClass() != b.getClass() || !a.identicalNonValueParts(b)) {
+ return false;
+ }
+ // For commutative binary operations any order of in-values are equal.
+ if (a.isBinop() && a.asBinop().isCommutative()) {
+ Value a0 = a.inValues().get(0);
+ Value a1 = a.inValues().get(1);
+ Value b0 = b.inValues().get(0);
+ Value b1 = b.inValues().get(1);
+ return (a0.equals(b0) && a1.equals(b1)) || (a0.equals(b1) && a1.equals(b0));
+ } else {
+ // Compare all in-values.
+ assert a.inValues().size() == b.inValues().size();
+ for (int i = 0; i < a.inValues().size(); i++) {
+ if (!a.inValues().get(i).equals(b.inValues().get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ @Override
+ protected int doHash(Instruction instruction) {
+ final int prime = 29;
+ int hash = instruction.getClass().hashCode();
+ if (instruction.isBinop()) {
+ Binop binop = instruction.asBinop();
+ Value in0 = instruction.inValues().get(0);
+ Value in1 = instruction.inValues().get(1);
+ if (binop.isCommutative()) {
+ hash += hash * prime + in0.hashCode() * in1.hashCode();
+ } else {
+ hash += hash * prime + in0.hashCode();
+ hash += hash * prime + in1.hashCode();
+ }
+ return hash;
+ } else {
+ for (Value value : instruction.inValues()) {
+ hash += hash * prime + value.hashCode();
+ }
+ }
+ return hash;
+ }
+ }
+
+ private boolean shareCatchHandlers(Instruction i0, Instruction i1) {
+ if (!i0.instructionTypeCanThrow()) {
+ assert !i1.instructionTypeCanThrow();
+ return true;
+ }
+ assert i1.instructionTypeCanThrow();
+ // TODO(sgjesse): This could be even better by checking for the exceptions thrown, e.g. div
+ // and rem only ever throw ArithmeticException.
+ CatchHandlers<BasicBlock> ch0 = i0.getBlock().getCatchHandlers();
+ CatchHandlers<BasicBlock> ch1 = i1.getBlock().getCatchHandlers();
+ return ch0.equals(ch1);
+ }
+
+ public void commonSubexpressionElimination(IRCode code) {
+ final ListMultimap<Wrapper<Instruction>, Value> instructionToValue = ArrayListMultimap.create();
+ final DominatorTree dominatorTree = new DominatorTree(code);
+ final ExpressionEquivalence equivalence = new ExpressionEquivalence();
+
+ for (int i = 0; i < dominatorTree.getSortedBlocks().length; i++) {
+ BasicBlock block = dominatorTree.getSortedBlocks()[i];
+ Iterator<Instruction> iterator = block.iterator();
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ if (instruction.isBinop()
+ || instruction.isUnop()
+ || instruction.isInstanceOf()
+ || instruction.isCheckCast()) {
+ List<Value> candidates = instructionToValue.get(equivalence.wrap(instruction));
+ boolean eliminated = false;
+ if (candidates.size() > 0) {
+ for (Value candidate : candidates) {
+ if (dominatorTree.dominatedBy(block, candidate.definition.getBlock()) &&
+ shareCatchHandlers(instruction, candidate.definition)) {
+ instruction.outValue().replaceUsers(candidate);
+ eliminated = true;
+ iterator.remove();
+ break; // Don't try any more candidates.
+ }
+ }
+ }
+ if (!eliminated) {
+ instructionToValue.put(equivalence.wrap(instruction), instruction.outValue());
+ }
+ }
+ }
+ }
+ assert code.isConsistentSSA();
+ }
+
+ public void simplifyIf(IRCode code) {
+ DominatorTree dominator = new DominatorTree(code);
+ code.clearMarks();
+ for (BasicBlock block : code.blocks) {
+ if (block.isMarked()) {
+ continue;
+ }
+ JumpInstruction exit = block.exit();
+ if (exit.isIf()) {
+ If theIf = exit.asIf();
+ List<Value> inValues = theIf.inValues();
+ int cond;
+ if (inValues.get(0).isConstant()
+ && (theIf.isZeroTest() || inValues.get(1).isConstant())) {
+ // Zero test with a constant of comparison between between two constants.
+ if (theIf.isZeroTest()) {
+ cond = inValues.get(0).getConstInstruction().asConstNumber().getIntValue();
+ } else {
+ int left = inValues.get(0).getConstInstruction().asConstNumber().getIntValue();
+ int right = inValues.get(1).getConstInstruction().asConstNumber().getIntValue();
+ cond = left - right;
+ }
+ } else if (inValues.get(0).hasValueRange()
+ && (theIf.isZeroTest() || inValues.get(1).hasValueRange())) {
+ // Zero test with a value range, or comparison between between two values,
+ // each with a value ranges.
+ if (theIf.isZeroTest()) {
+ if (inValues.get(0).isValueInRange(0)) {
+ // Zero in in the range - can't determine the comparison.
+ continue;
+ }
+ cond = Long.signum(inValues.get(0).getValueRange().getMin());
+ } else {
+ LongInterval leftRange = inValues.get(0).getValueRange();
+ LongInterval rightRange = inValues.get(1).getValueRange();
+ if (leftRange.overlapsWith(rightRange)) {
+ // Ranges overlap - can't determine the comparison.
+ continue;
+ }
+ // There is no overlap.
+ cond = Long.signum(leftRange.getMin() - rightRange.getMin());
+ }
+ } else {
+ continue;
+ }
+ BasicBlock target = theIf.targetFromCondition(cond);
+ BasicBlock deadTarget =
+ target == theIf.getTrueTarget() ? theIf.fallthroughBlock() : theIf.getTrueTarget();
+ List<BasicBlock> removedBlocks = block.unlink(deadTarget, dominator);
+ for (BasicBlock removedBlock : removedBlocks) {
+ if (!removedBlock.isMarked()) {
+ removedBlock.mark();
+ }
+ }
+ assert theIf == block.exit();
+ InstructionListIterator iterator = block.listIterator(block.getInstructions().size());
+ iterator.previous();
+ iterator.replaceCurrentInstruction(new Goto());
+ assert block.exit().isGoto();
+ assert block.exit().asGoto().getTarget() == target;
+ }
+ }
+ code.removeMarkedBlocks();
+ assert code.isConsistentSSA();
+ }
+
+ public void removeUnneededCatchHandlers(IRCode code) {
+ DominatorTree dominator = new DominatorTree(code);
+ code.clearMarks();
+ for (BasicBlock block : code.blocks) {
+ if (block.hasCatchHandlers() && !block.canThrow()) {
+ CatchHandlers<BasicBlock> handlers = block.getCatchHandlers();
+ for (BasicBlock target : handlers.getUniqueTargets()) {
+ for (BasicBlock unlinked : block.unlink(target, dominator)) {
+ if (!unlinked.isMarked()) {
+ unlinked.mark();
+ }
+ }
+ }
+ }
+ }
+ code.removeMarkedBlocks();
+ assert code.isConsistentSSA();
+ }
+
+ public void rewriteLongCompareAndRequireNonNull(IRCode code, boolean canUseObjectsNonNull) {
+ InstructionIterator iterator = code.instructionIterator();
+
+ while (iterator.hasNext()) {
+ Instruction current = iterator.next();
+ if (current.isInvokeMethod()) {
+ DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
+ if (invokedMethod == dexItemFactory.longMethods.compare) {
+ List<Value> inValues = current.inValues();
+ assert inValues.size() == 2;
+ iterator.replaceCurrentInstruction(
+ new Cmp(NumericType.LONG, Bias.NONE, current.outValue(), inValues.get(0),
+ inValues.get(1)));
+ } else if (!canUseObjectsNonNull
+ && invokedMethod == dexItemFactory.objectsMethods.requireNonNull) {
+ // Rewrite calls to Objects.requireNonNull(Object) because Javac 9 start to use it for
+ // synthesized null checks.
+ InvokeVirtual callToGetClass = new InvokeVirtual(dexItemFactory.objectMethods.getClass,
+ null, current.inValues());
+ if (current.outValue() != null) {
+ current.outValue().replaceUsers(current.inValues().get(0));
+ current.setOutValue(null);
+ }
+ iterator.replaceCurrentInstruction(callToGetClass);
+ }
+ }
+ }
+ assert code.isConsistentSSA();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
new file mode 100644
index 0000000..d6023ed
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+public class DeadCodeRemover {
+
+ public static void removeDeadCode(
+ IRCode code, CodeRewriter codeRewriter, InternalOptions options) {
+ Queue<BasicBlock> worklist = new LinkedList<>();
+ worklist.addAll(code.blocks);
+ for (BasicBlock block = worklist.poll(); block != null; block = worklist.poll()) {
+ removeDeadInstructions(worklist, block, options);
+ removeDeadPhis(worklist, block, options);
+ }
+ codeRewriter.rewriteMoveResult(code);
+ }
+
+ private static void removeDeadPhis(
+ Queue<BasicBlock> worklist, BasicBlock block, InternalOptions options) {
+ List<Phi> toRemove = new ArrayList<>();
+ for (Phi phi : block.getPhis()) {
+ if (phi.isDead(options)) {
+ toRemove.add(phi);
+ for (Value operand : phi.getOperands()) {
+ operand.removePhiUser(phi);
+ if (operand.isPhi()) {
+ worklist.add(operand.asPhi().getBlock());
+ } else {
+ worklist.add(operand.definition.getBlock());
+ }
+ }
+ }
+ }
+ if (!toRemove.isEmpty()) {
+ List<Phi> newPhis = new ArrayList<>(block.getPhis().size() - toRemove.size());
+ int toRemoveIndex = 0;
+ List<Phi> phis = block.getPhis();
+ int i = 0;
+ for (; i < phis.size() && toRemoveIndex < toRemove.size(); i++) {
+ Phi phi = phis.get(i);
+ if (phi == toRemove.get(toRemoveIndex)) {
+ toRemoveIndex++;
+ } else {
+ newPhis.add(phi);
+ }
+ }
+ newPhis.addAll(phis.subList(i, phis.size()));
+ block.setPhis(newPhis);
+ }
+ }
+
+ private static void removeDeadInstructions(
+ Queue<BasicBlock> worklist, BasicBlock block, InternalOptions options) {
+ InstructionListIterator iterator = block.listIterator(block.getInstructions().size());
+ while (iterator.hasPrevious()) {
+ Instruction current = iterator.previous();
+ // Remove unused invoke results.
+ if (current.isInvoke()
+ && current.outValue() != null
+ && current.outValue().numberOfAllUsers() == 0) {
+ current.setOutValue(null);
+ }
+ // Never remove instructions that can have side effects.
+ if (!current.canBeDeadCode(options)) {
+ continue;
+ }
+ Value outValue = current.outValue();
+ // Instructions with no out value cannot be dead code by the current definition
+ // (unused out value). They typically side-effect input values or deals with control-flow.
+ assert outValue != null;
+ if (!outValue.isDead(options)) {
+ continue;
+ }
+ for (Value inValue : current.inValues()) {
+ if (inValue.isPhi()) {
+ worklist.add(inValue.asPhi().getBlock());
+ } else {
+ worklist.add(inValue.definition.getBlock());
+ }
+ }
+ Value previousLocalValue = current.getPreviousLocalValue();
+ if (previousLocalValue != null) {
+ if (previousLocalValue.isPhi()) {
+ worklist.add(previousLocalValue.asPhi().getBlock());
+ } else {
+ worklist.add(previousLocalValue.definition.getBlock());
+ }
+ }
+ // All users will be removed for this instruction. Eagerly clear them so further inspection
+ // of this instruction during dead code elimination will terminate here.
+ outValue.clearUsers();
+ iterator.remove();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
new file mode 100644
index 0000000..bcf1a81
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -0,0 +1,251 @@
+// 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.ir.optimize;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.conversion.CallGraph;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+public class Inliner {
+
+ private static final int INLINING_INSTRUCTION_LIMIT = 5;
+
+ protected final AppInfoWithSubtyping appInfo;
+ private final InternalOptions options;
+
+ // State for inlining methods which are known to be called twice.
+ public boolean applyDoubleInlining = false;
+ public final Set<DexEncodedMethod> doubleInlineCallers = Sets.newIdentityHashSet();
+ public final Set<DexEncodedMethod> doubleInlineSelectedTargets = Sets.newIdentityHashSet();
+ public final Map<DexEncodedMethod, DexEncodedMethod> doubleInlineeCandidates = new HashMap<>();
+
+ public Inliner(
+ AppInfoWithSubtyping appInfo, InternalOptions options) {
+ this.appInfo = appInfo;
+ this.options = options;
+ }
+
+ private InliningConstraint instructionAllowedForInlining(
+ DexEncodedMethod method, Instruction instruction) {
+ InliningConstraint result = instruction.inliningConstraint(appInfo, method.method.holder);
+ if ((result == InliningConstraint.NEVER) && instruction.isDebugInstruction()) {
+ return InliningConstraint.ALWAYS;
+ }
+ return result;
+ }
+
+ public InliningConstraint identifySimpleMethods(IRCode code, DexEncodedMethod method) {
+ DexCode dex = method.getCode().asDexCode();
+ // We have generated code for a method and we want to figure out whether the method is a
+ // candidate for inlining. The code is the final IR after optimizations.
+ if (dex.instructions.length > INLINING_INSTRUCTION_LIMIT) {
+ return InliningConstraint.NEVER;
+ }
+ InliningConstraint result = InliningConstraint.ALWAYS;
+ ListIterator<BasicBlock> iterator = code.listIterator();
+ assert iterator.hasNext();
+ BasicBlock block = iterator.next();
+ BasicBlock nextBlock;
+ do {
+ nextBlock = iterator.hasNext() ? iterator.next() : null;
+ InstructionListIterator it = block.listIterator();
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ InliningConstraint state = instructionAllowedForInlining(method, instruction);
+ if (state == InliningConstraint.NEVER) {
+ return InliningConstraint.NEVER;
+ }
+ if (state.ordinal() < result.ordinal()) {
+ result = state;
+ }
+ }
+ block = nextBlock;
+ } while (block != null);
+ return result;
+ }
+
+ protected boolean hasInliningAccess(DexEncodedMethod method, DexEncodedMethod target) {
+ if (target.accessFlags.isPublic()) {
+ return true;
+ }
+ DexType methodHolder = method.method.getHolder();
+ DexType targetHolder = target.method.getHolder();
+ if (target.accessFlags.isPrivate()) {
+ return methodHolder == targetHolder;
+ }
+ if (target.accessFlags.isProtected() &&
+ methodHolder.isSubtypeOf(targetHolder, appInfo)) {
+ return true;
+ }
+ return methodHolder.isSamePackage(targetHolder);
+ }
+
+ public enum InliningConstraint {
+ // The ordinal values are important so please do not reorder.
+ NEVER, // Never inline this.
+ PRIVATE, // Only inline this into methods with same holder.
+ PACKAGE, // Only inline this into methods with holders from same package.
+ ALWAYS, // No restrictions for inlining this.
+ }
+
+ static public class InlineAction {
+ public final DexEncodedMethod target;
+ public final Invoke invoke;
+ public final boolean forceInline;
+
+ public InlineAction(
+ DexEncodedMethod target, Invoke invoke, boolean forceInline) {
+ this.target = target;
+ this.invoke = invoke;
+ this.forceInline = forceInline;
+ }
+
+ public InlineAction(DexEncodedMethod target, Invoke invoke) {
+ this.target = target;
+ this.invoke = invoke;
+ this.forceInline = target.getOptimizationInfo().forceInline();
+ }
+
+ public IRCode buildIR(ValueNumberGenerator generator, InternalOptions options) {
+ assert target.isProcessed();
+ assert target.getCode().isDexCode();
+ return target.buildIR(generator, options);
+ }
+ }
+
+ private int numberOfInstructions(IRCode code) {
+ int numOfInstructions = 0;
+ for (BasicBlock block : code.blocks) {
+ numOfInstructions += block.getInstructions().size();
+ }
+ return numOfInstructions;
+ }
+
+ private boolean legalConstructorInline(DexEncodedMethod method, IRCode code) {
+ // In the Java VM Specification section "4.10.2.4. Instance Initialization Methods and
+ // Newly Created Objects" it says:
+ //
+ // Before that method invokes another instance initialization method of myClass or its direct
+ // superclass on this, the only operation the method can perform on this is assigning fields
+ // declared within myClass.
+ //
+
+ // Allow inlining a constructor into a constructor, as the constructor code is expected to
+ // adhere to the VM specification.
+ if (method.accessFlags.isConstructor()) {
+ return true;
+ }
+
+ // Don't allow inlining a constructor into a non-constructor if the first use of the
+ // un-initialized object is not an argument of an invoke of <init>.
+ InstructionIterator iterator = code.instructionIterator();
+ Instruction instruction = iterator.next();
+ // A constructor always has the un-initialized object as the first argument.
+ assert instruction.isArgument();
+ Value unInitializedObject = instruction.outValue();
+ while (iterator.hasNext()) {
+ instruction = iterator.next();
+ if (instruction.inValues().contains(unInitializedObject)) {
+ return instruction.isInvokeDirect();
+ }
+ }
+ assert false : "Execution should never reach this point";
+ return false;
+ }
+
+ /// Computer the receiver value for the holder method.
+ private Value receiverValue(DexEncodedMethod method, IRCode code) {
+ // Ignore static methods.
+ if (method.accessFlags.isStatic()) {
+ return null;
+ }
+ // Find the outValue of the first argument instruction in the first block.
+ return code.collectArguments().get(0);
+ }
+
+ public void performInlining(DexEncodedMethod method, IRCode code, CallGraph callGraph) {
+ int instruction_allowance = 1500;
+ instruction_allowance -= numberOfInstructions(code);
+ if (instruction_allowance < 0) {
+ return;
+ }
+ Value receiver = receiverValue(method, code);
+ InliningOracle oracle = new InliningOracle(this, method, receiver, callGraph);
+
+ List<BasicBlock> blocksToRemove = new ArrayList<>();
+ ListIterator<BasicBlock> blockIterator = code.listIterator();
+ while (blockIterator.hasNext() && (instruction_allowance >= 0)) {
+ BasicBlock block = blockIterator.next();
+ if (blocksToRemove.contains(block)) {
+ continue;
+ }
+ InstructionListIterator iterator = block.listIterator();
+ while (iterator.hasNext() && (instruction_allowance >= 0)) {
+ Instruction current = iterator.next();
+ if (current.isInvokeMethod()) {
+ InvokeMethod invoke = current.asInvokeMethod();
+ InlineAction result = invoke.computeInlining(oracle);
+ if (result != null) {
+ IRCode inlinee = result.buildIR(code.valueNumberGenerator, options);
+ if (inlinee != null) {
+ // TODO(sgjesse): Get rid of this additional check by improved inlining.
+ if (block.hasCatchHandlers() && inlinee.getNormalExitBlock() == null) {
+ continue;
+ }
+ // Make sure constructor inlining is legal.
+ DexEncodedMethod target = appInfo.lookup(invoke.getType(), invoke.getInvokedMethod());
+ if (target.accessFlags.isConstructor() && !legalConstructorInline(method, inlinee)) {
+ continue;
+ }
+ if (invoke.isInvokeInterface()
+ || invoke.isInvokeVirtual()
+ || invoke.isInvokeDirect()) {
+ // If the invoke has a receiver but the declared method holder is different
+ // from the computed target holder, inlining is cancelled due to verification
+ // issues.
+ if (result.target.method.getHolder() != target.method.getHolder()) {
+ continue;
+ }
+ }
+ // Inline the inlinee code in place of the invoke instruction
+ // Back up before the invoke instruction.
+ iterator.previous();
+ instruction_allowance -= numberOfInstructions(inlinee);
+ if (instruction_allowance >= 0 || result.forceInline) {
+ iterator.inlineInvoke(code, inlinee, blockIterator, blocksToRemove);
+ }
+ // If we inlined the invoke from a bridge method, it is no longer a bridge method.
+ if (method.accessFlags.isBridge()) {
+ method.accessFlags.unsetSynthetic();
+ method.accessFlags.unsetBridge();
+ }
+ }
+ }
+ }
+ }
+ }
+ oracle.finish();
+ code.removeBlocks(blocksToRemove);
+ assert code.isConsistentSSA();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningInfo.java
new file mode 100644
index 0000000..543aba4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningInfo.java
@@ -0,0 +1,93 @@
+// 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.ir.optimize;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import java.util.ArrayList;
+import java.util.List;
+
+
+// Class for collecting inlining information for one compiled DexEncodedMethod.
+public class InliningInfo {
+
+ static class Edge {
+ final Type type;
+ final DexMethod declared;
+ final Node inlinee;
+
+ public Edge(Type type, DexMethod declared, Node inlinee) {
+ this.type = type;
+ this.declared = declared;
+ this.inlinee = inlinee;
+ }
+
+ void appendOn(StringBuffer buffer) {
+ if (declared != null) {
+ buffer.append(declared.toSourceString());
+ buffer.append(' ');
+ }
+ inlinee.appendOn(buffer);
+ }
+ }
+
+ static abstract class Node {
+ abstract void appendOn(StringBuffer buffer);
+ }
+
+ static class Inlining extends Node {
+ final DexEncodedMethod target;
+
+ Inlining(DexEncodedMethod target) {
+ this.target = target;
+ }
+
+ void appendOn(StringBuffer buffer) {
+ buffer.append("<< INLINED");
+ }
+ }
+
+ static class NotInlining extends Node {
+
+ final String reason;
+
+ NotInlining(String reason) {
+ this.reason = reason;
+ }
+
+ public void appendOn(StringBuffer buffer) {
+ buffer.append("-- no inlining: ");
+ buffer.append(reason);
+ }
+ }
+
+ final DexEncodedMethod method;
+ final List<Edge> edges = new ArrayList<>();
+
+ public InliningInfo(DexEncodedMethod method) {
+ this.method = method;
+ }
+
+ public void include(Type type, DexEncodedMethod target) {
+ edges.add(new Edge(type, target.method, new Inlining(target)));
+ }
+
+ public void exclude(InvokeMethod invoke, String reason) {
+ edges.add(new Edge(invoke.getType(), invoke.getInvokedMethod(), new NotInlining(reason)));
+ }
+
+ public String toString() {
+ StringBuffer buffer = new StringBuffer(method.method.toSourceString());
+ buffer.append(" {\n");
+ for (Edge edge : edges) {
+ buffer.append(" ");
+ edge.appendOn(buffer);
+ buffer.append(".\n");
+ }
+ buffer.append("}\n");
+ return buffer.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
new file mode 100644
index 0000000..aa34226
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
@@ -0,0 +1,282 @@
+// 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.ir.optimize;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeInterface;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokePolymorphic;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.InvokeSuper;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.CallGraph;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.logging.Log;
+
+/**
+ * The InliningOracle contains information needed for when inlining
+ * other methods into @method.
+ */
+public class InliningOracle {
+ final Inliner inliner;
+ final DexEncodedMethod method;
+ final Value receiver;
+ final CallGraph callGraph;
+ final private InliningInfo info;
+
+ public InliningOracle(
+ Inliner inliner,
+ DexEncodedMethod method,
+ Value receiver,
+ CallGraph callGraph) {
+ this.inliner = inliner;
+ this.method = method;
+ this.receiver = receiver;
+ this.callGraph = callGraph;
+ info = Log.ENABLED ? new InliningInfo(method) : null;
+ }
+
+ public void finish() {
+ if (Log.ENABLED) {
+ System.out.println(info.toString());
+ }
+ }
+
+ DexEncodedMethod validateCandidate(InvokeMethod invoke) {
+ DexEncodedMethod candidate = invoke.computeSingleTarget(inliner.appInfo);
+ if ((candidate == null)
+ || (candidate.getCode() == null)
+ || inliner.appInfo.definitionFor(candidate.method.getHolder()).isLibraryClass()) {
+ if (info != null) {
+ info.exclude(invoke, "No inlinee");
+ }
+ return null;
+ }
+ if (method == candidate) {
+ // Cannot handle recursive inlining at this point.
+ // Bridge methods should never have recursive calls.
+ assert !candidate.getOptimizationInfo().forceInline();
+ return null;
+ }
+ if (candidate.accessFlags.isSynchronized()) {
+ // Don't inline if target is synchronized.
+ if (info != null) {
+ info.exclude(invoke, "Inlinee candidate is synchronized");
+ }
+ return null;
+ }
+ if (!inliner.hasInliningAccess(method, candidate)) {
+ if (info != null) {
+ info.exclude(invoke, "Inlinee candidate does not have right access flags");
+ }
+ return null;
+ }
+ return candidate;
+ }
+
+ private DexEncodedMethod doubleInlining(DexEncodedMethod candidate) {
+ if (!inliner.applyDoubleInlining) {
+ if (inliner.doubleInlineeCandidates.containsKey(candidate)) {
+ // Both calls can be inlined.
+ inliner.doubleInlineCallers.add(inliner.doubleInlineeCandidates.get(candidate));
+ inliner.doubleInlineCallers.add(method);
+ inliner.doubleInlineSelectedTargets.add(candidate);
+ } else {
+ // First call can be inlined.
+ inliner.doubleInlineeCandidates.put(candidate, method);
+ }
+ // Just preparing for double inlining.
+ return null;
+ } else {
+ // Don't perform the actual inlining if this was not selected.
+ if (!inliner.doubleInlineSelectedTargets.contains(candidate)) {
+ return null;
+ }
+ }
+ return candidate;
+ }
+
+ public InlineAction computeForInvokeWithReceiver(InvokeMethod invoke) {
+ boolean receiverIsNeverNull = invoke.arguments().get(0).isNeverNull();
+ if (!receiverIsNeverNull) {
+ if (info != null) {
+ info.exclude(invoke, "receiver for candidate can be null");
+ }
+ return null;
+ }
+ DexEncodedMethod target = invoke.computeSingleTarget(inliner.appInfo);
+ if (target == null) {
+ // Abort inlining attempt if we cannot find single target.
+ if (info != null) {
+ info.exclude(invoke, "could not find single target");
+ }
+ return null;
+ }
+
+ boolean fromBridgeMethod = target.getOptimizationInfo().forceInline();
+
+ if (target == method) {
+ // Bridge methods should never have recursive calls.
+ assert !fromBridgeMethod;
+ return null;
+ }
+
+ if (target.getCode() == null) {
+ return null;
+ }
+
+ DexClass holder = inliner.appInfo.definitionFor(target.method.getHolder());
+ if (holder.isInterface()) {
+ // Art978_virtual_interfaceTest correctly expects an IncompatibleClassChangeError exception at runtime.
+ if (info != null) {
+ info.exclude(invoke, "Do not inline target if method holder is an interface class");
+ }
+ return null;
+ }
+
+ if (holder.isLibraryClass()) {
+ // Library functions should not be inlined.
+ return null;
+ }
+
+ // Don't inline if target is synchronized.
+ if (target.accessFlags.isSynchronized()) {
+ if (info != null) {
+ info.exclude(invoke, "target is synchronized");
+ }
+ return null;
+ }
+
+ boolean doubleInlineTarget =
+ callGraph.hasDoubleCallSite(target)
+ && target.getCode().isDexCode()
+ && (target.getCode().asDexCode().instructions.length
+ <= 10); // 10 is found from measuring.
+ // Determine if this should be inlined no matter how big it is.
+ boolean forceInline =
+ fromBridgeMethod | callGraph.hasSingleCallSite(target) | doubleInlineTarget;
+ if (!target.isInliningCandidate(method, forceInline)) {
+ // Abort inlining attempt if the single target is not an inlining candidate.
+ if (info != null) {
+ info.exclude(invoke, "target is not identified for inlining");
+ }
+ return null;
+ }
+
+ // Abort inlining attempt if method -> target access is not right.
+ if (!inliner.hasInliningAccess(method, target)) {
+ if (info != null) {
+ info.exclude(invoke, "target does not have right access");
+ }
+ return null;
+ }
+
+ // Attempt to inline a candidate that is only called twice.
+ if (doubleInlineTarget && (doubleInlining(target) == null)) {
+ if (info != null) {
+ info.exclude(invoke, "target is not ready for double inlining");
+ }
+ return null;
+ }
+
+ if (info != null) {
+ info.include(invoke.getType(), target);
+ }
+ return new InlineAction(target, invoke, forceInline);
+ }
+
+ public InlineAction computeForInvokeVirtual(InvokeVirtual invoke) {
+ return computeForInvokeWithReceiver(invoke);
+ }
+
+ public InlineAction computeForInvokeInterface(InvokeInterface invoke) {
+ return computeForInvokeWithReceiver(invoke);
+ }
+
+ public InlineAction computeForInvokeDirect(InvokeDirect invoke) {
+ return computeForInvokeWithReceiver(invoke);
+ }
+
+ private boolean canInlineStaticInvoke(DexEncodedMethod method, DexEncodedMethod target) {
+ // Only proceed with inlining a static invoke if:
+ // - the holder for the target equals the holder for the method, or
+ // - there is no class initializer.
+ DexType targetHolder = target.method.getHolder();
+ if (method.method.getHolder() == targetHolder) {
+ return true;
+ }
+ DexClass clazz = inliner.appInfo.definitionFor(targetHolder);
+ return (clazz != null) && (clazz.getClassInitializer(inliner.appInfo.dexItemFactory) == null);
+ }
+
+ public InlineAction computeForInvokeStatic(InvokeStatic invoke) {
+ DexEncodedMethod candidate = validateCandidate(invoke);
+ if (candidate == null) {
+ return null;
+ }
+ boolean fromBridgeMethod = candidate.getOptimizationInfo().forceInline();
+ boolean doubleInlineTarget =
+ callGraph.hasDoubleCallSite(candidate)
+ && candidate.getCode().isDexCode()
+ && (candidate.getCode().asDexCode().instructions.length
+ <= 10); // 10 is found from measuring.
+ // Determine if this should be inlined no matter how big it is.
+ boolean forceInline =
+ fromBridgeMethod | callGraph.hasSingleCallSite(candidate) | doubleInlineTarget;
+ if (!candidate.isInliningCandidate(method, forceInline)) {
+ // Abort inlining attempt if the single target is not an inlining candidate.
+ if (info != null) {
+ info.exclude(invoke, "target is not identified for inlining");
+ }
+ return null;
+ }
+
+ // Abort inlining attempt if we can not guarantee class for static target has been initialized.
+ if (!canInlineStaticInvoke(method, candidate)) {
+ if (info != null) {
+ info.exclude(invoke, "target is static but we cannot guarantee class has been initialized");
+ }
+ return null;
+ }
+
+ // Attempt to inline a candidate that is only called twice.
+ if (doubleInlineTarget && (doubleInlining(candidate) == null)) {
+ if (info != null) {
+ info.exclude(invoke, "target is not ready for double inlining");
+ }
+ return null;
+ }
+
+ if (info != null) {
+ info.include(invoke.getType(), candidate);
+ }
+ return new InlineAction(candidate, invoke);
+ }
+
+ public InlineAction computeForInvokeSuper(InvokeSuper invoke) {
+ DexEncodedMethod candidate = validateCandidate(invoke);
+ if (candidate == null) {
+ if (info != null) {
+ info.exclude(invoke, "not a valid inlining target");
+ }
+ return null;
+ }
+ if (info != null) {
+ info.include(invoke.getType(), candidate);
+ }
+ return new InlineAction(candidate, invoke);
+ }
+
+ public InlineAction computeForInvokePolymorpic(InvokePolymorphic invoke) {
+ // TODO: No inlining of invoke polymorphic for now.
+ if (info != null) {
+ info.exclude(invoke, "inlining through invoke signature polymorpic is not supported");
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InstructionEquivalence.java b/src/main/java/com/android/tools/r8/ir/optimize/InstructionEquivalence.java
new file mode 100644
index 0000000..f9c0b30
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InstructionEquivalence.java
@@ -0,0 +1,37 @@
+// 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.ir.optimize;
+
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.google.common.base.Equivalence;
+
+public class InstructionEquivalence extends Equivalence<Instruction> {
+ private final RegisterAllocator allocator;
+
+ InstructionEquivalence(RegisterAllocator allocator) {
+ this.allocator = allocator;
+ }
+
+ @Override
+ protected boolean doEquivalent(Instruction a, Instruction b) {
+ return a.identicalAfterRegisterAllocation(b, allocator);
+ }
+
+ @Override
+ protected int doHash(Instruction instruction) {
+ int hash = 0;
+ if (instruction.outValue() != null && instruction.outValue().needsRegister()) {
+ hash += allocator.getRegisterForValue(instruction.outValue(), instruction.getNumber());
+ }
+ for (Value inValue : instruction.inValues()) {
+ hash = hash<< 4;
+ if (inValue.needsRegister()) {
+ hash += allocator.getRegisterForValue(inValue, instruction.getNumber());
+ }
+ }
+ return hash;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
new file mode 100644
index 0000000..5842609
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -0,0 +1,250 @@
+// 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.ir.optimize;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.ProguardMemberRule;
+
+public class MemberValuePropagation {
+
+ private final AppInfo appInfo;
+ private final AppInfoWithLiveness liveSet;
+
+ private enum RuleType {
+ NONE,
+ ASSUME_NO_SIDE_EFFECTS,
+ ASSUME_VALUES
+ }
+
+ private class ProguardMemberRuleLookup {
+
+ final RuleType type;
+ final ProguardMemberRule rule;
+
+ ProguardMemberRuleLookup(RuleType type, ProguardMemberRule rule) {
+ this.type = type;
+ this.rule = rule;
+ }
+ }
+
+ public MemberValuePropagation(AppInfo appInfo) {
+ this.appInfo = appInfo;
+ this.liveSet = appInfo.withLiveness();
+ }
+
+ private ProguardMemberRuleLookup lookupMemberRule(DexItem item) {
+ if (liveSet == null) {
+ return null;
+ }
+ ProguardMemberRule rule = liveSet.noSideEffects.get(item);
+ if (rule != null) {
+ return new ProguardMemberRuleLookup(RuleType.ASSUME_NO_SIDE_EFFECTS, rule);
+ }
+ rule = liveSet.assumedValues.get(item);
+ if (rule != null) {
+ return new ProguardMemberRuleLookup(RuleType.ASSUME_VALUES, rule);
+ }
+ return null;
+ }
+
+ private Instruction constantReplacementFromProguardRule(
+ ProguardMemberRule rule, IRCode code, Instruction instruction) {
+ // Check if this value can be assumed constant.
+ Instruction replacement = null;
+ MoveType moveType = instruction.outValue().outType();
+ if (rule != null && rule.hasReturnValue() && rule.getReturnValue().isSingleValue()) {
+ assert moveType != MoveType.OBJECT;
+ Value value = new Value(
+ code.valueNumberGenerator.next(), -1, moveType, instruction.getDebugInfo());
+ replacement = new ConstNumber(
+ ConstType.fromMoveType(moveType), value, rule.getReturnValue().getSingleValue());
+ }
+ if (replacement == null &&
+ rule != null && rule.hasReturnValue() && rule.getReturnValue().isField()) {
+ DexField field = rule.getReturnValue().getField();
+ DexEncodedField staticField = appInfo.lookupStaticTarget(field.clazz, field);
+ if (staticField != null) {
+ Value value = new Value(
+ code.valueNumberGenerator.next(), -1, moveType, instruction.getDebugInfo());
+ replacement = staticField.staticValue.asConstInstruction(false, value);
+ } else {
+ throw new CompilationError(field.clazz.toSourceString() + "." + field.name.toString() +
+ " used in assumevalues rule does not exist.");
+ }
+ }
+ return replacement;
+ }
+
+ private void setValueRangeFromProguardRule(ProguardMemberRule rule, Value value) {
+ if (rule.hasReturnValue() && rule.getReturnValue().isValueRange()) {
+ assert !rule.getReturnValue().isSingleValue();
+ value.setValueRange(rule.getReturnValue().getValueRange());
+ }
+ }
+
+ private void replaceInstructionFromProguardRule(RuleType ruleType, InstructionIterator iterator,
+ Instruction current, Instruction replacement) {
+ if (ruleType == RuleType.ASSUME_NO_SIDE_EFFECTS) {
+ iterator.replaceCurrentInstruction(replacement);
+ } else {
+ if (current.outValue() != null) {
+ assert replacement.outValue() != null;
+ current.outValue().replaceUsers(replacement.outValue());
+ }
+ iterator.add(replacement);
+ }
+ }
+
+ /**
+ * Replace invoke targets and field accesses with constant values where possible.
+ * <p>
+ * Also assigns value ranges to values where possible.
+ */
+ public void rewriteWithConstantValues(IRCode code) {
+ InstructionIterator iterator = code.instructionIterator();
+ while (iterator.hasNext()) {
+ Instruction current = iterator.next();
+ if (current.isInvokeMethod()) {
+ InvokeMethod invoke = current.asInvokeMethod();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ DexType invokedHolder = invokedMethod.getHolder();
+ if (!invokedHolder.isClassType()) {
+ continue;
+ }
+ DexEncodedMethod definition = appInfo.lookup(invoke.getType(), invokedMethod);
+
+ // Process invokes marked as having no side effects.
+ boolean invokeReplaced = false;
+ ProguardMemberRuleLookup lookup = lookupMemberRule(definition);
+ if (lookup != null) {
+ if (lookup.type == RuleType.ASSUME_NO_SIDE_EFFECTS
+ && (invoke.outValue() == null || invoke.outValue().numberOfAllUsers() == 0)) {
+ iterator.remove();
+ invokeReplaced = true;
+ } else {
+ // Check to see if a constant value can be assumed.
+ Instruction replacement =
+ constantReplacementFromProguardRule(lookup.rule, code, invoke);
+ if (replacement != null) {
+ replaceInstructionFromProguardRule(lookup.type, iterator, current, replacement);
+ invokeReplaced = true;
+ } else {
+ // Check to see if a value range can be assumed.
+ setValueRangeFromProguardRule(lookup.rule, current.outValue());
+ }
+ }
+ }
+
+ // If no Proguard rule could replace the instruction check for knowledge about the
+ // return value.
+ if (!invokeReplaced && liveSet != null && invoke.outValue() != null) {
+ DexEncodedMethod target = invoke.computeSingleTarget(liveSet);
+ if (target != null) {
+ if (target.getOptimizationInfo().neverReturnsNull()) {
+ invoke.outValue().markNeverNull();
+ }
+ if (target.getOptimizationInfo().returnsConstant()) {
+ long constant = target.getOptimizationInfo().getReturnedConstant();
+ MoveType moveType = invoke.outType();
+ if (moveType == MoveType.OBJECT) {
+ assert constant == 0;
+ moveType = MoveType.SINGLE;
+ }
+ Value value = new Value(code.valueNumberGenerator.next(), -1, moveType, null);
+ // TODO(ager): Attempt to get a more precise const type from the method analysis?
+ Instruction knownConstReturn =
+ new ConstNumber(ConstType.fromMoveType(moveType), value, constant);
+ invoke.outValue().replaceUsers(value);
+ iterator.add(knownConstReturn);
+ }
+ }
+ }
+ } else if (current.isInstancePut()) {
+ InstancePut instancePut = current.asInstancePut();
+ DexField field = instancePut.getField();
+ DexEncodedField target = appInfo.lookupInstanceTarget(field.getHolder(), field);
+ if (target != null) {
+ // Remove writes to dead (i.e. never read) fields.
+ if (!isFieldRead(target, false) && instancePut.object().isNeverNull()) {
+ iterator.remove();
+ }
+ }
+ } else if (current.isStaticGet()) {
+ StaticGet staticGet = current.asStaticGet();
+ DexField field = staticGet.getField();
+ Instruction replacement = null;
+ DexEncodedField target = appInfo.lookupStaticTarget(field.getHolder(), field);
+ ProguardMemberRuleLookup lookup = null;
+ if (target != null) {
+ // Check if a this value is known const.
+ replacement = target.valueAsConstInstruction(appInfo, staticGet.dest());
+ if (replacement == null) {
+ lookup = lookupMemberRule(target);
+ if (lookup != null) {
+ replacement = constantReplacementFromProguardRule(lookup.rule, code, staticGet);
+ }
+ }
+ if (replacement == null) {
+ // If no const replacement was found, at least store the range information.
+ if (lookup != null) {
+ setValueRangeFromProguardRule(lookup.rule, staticGet.dest());
+ }
+ }
+ if (replacement != null) {
+ // Ignore assumenosideeffects for fields.
+ if (lookup != null && lookup.type == RuleType.ASSUME_VALUES) {
+ replaceInstructionFromProguardRule(lookup.type, iterator, current, replacement);
+ } else {
+ iterator.replaceCurrentInstruction(replacement);
+ }
+ }
+ }
+ } else if (current.isStaticPut()) {
+ StaticPut staticPut = current.asStaticPut();
+ DexField field = staticPut.getField();
+ DexEncodedField target = appInfo.lookupStaticTarget(field.getHolder(), field);
+ if (target != null) {
+ // Remove writes to dead (i.e. never read) fields.
+ if (!isFieldRead(target, true)) {
+ iterator.remove();
+ }
+ }
+ }
+ }
+ assert code.isConsistentSSA();
+ }
+
+ private boolean isFieldRead(DexEncodedField field, boolean isStatic) {
+ // Without live set information we cannot tell and assume true.
+ if (liveSet == null
+ || isStatic && liveSet.staticFieldsRead.contains(field.field)
+ || !isStatic && liveSet.instanceFieldsRead.contains(field.field)
+ || liveSet.pinnedItems.contains(field)) {
+ return true;
+ }
+ // For library classes we don't know whether a field is read.
+ DexClass holder = appInfo.definitionFor(field.field.clazz);
+ return holder == null || holder.isLibraryClass();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java b/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java
new file mode 100644
index 0000000..0d93561
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Move;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import java.util.HashSet;
+
+class MoveEliminator {
+ private final HashSet<Move> activeMoves = new HashSet<>();
+ private final RegisterAllocator allocator;
+
+ MoveEliminator(RegisterAllocator allocator) {
+ this.allocator = allocator;
+ }
+
+ public boolean shouldBeEliminated(Instruction instruction) {
+ if (instruction.isMove()) {
+ Move move = instruction.asMove();
+ int moveSrcRegister = allocator.getRegisterForValue(move.src(), move.getNumber());
+ int moveDstRegister = allocator.getRegisterForValue(move.dest(), move.getNumber());
+ if (moveSrcRegister == moveDstRegister) {
+ return true;
+ }
+ for (Move activeMove : activeMoves) {
+ int activeMoveSrcRegister =
+ allocator.getRegisterForValue(activeMove.src(), activeMove.getNumber());
+ int activeMoveDstRegister =
+ allocator.getRegisterForValue(activeMove.dest(), activeMove.getNumber());
+ if (activeMoveSrcRegister == moveSrcRegister
+ && activeMoveDstRegister == moveDstRegister) {
+ return true;
+ }
+ }
+ }
+ if (instruction.outValue() != null && instruction.outValue().needsRegister()) {
+ Value defined = instruction.outValue();
+ int definedRegister = allocator.getRegisterForValue(defined, instruction.getNumber());
+ activeMoves.removeIf((m) -> {
+ int moveSrcRegister = allocator.getRegisterForValue(m.inValues().get(0), m.getNumber());
+ int moveDstRegister = allocator.getRegisterForValue(m.outValue(), m.getNumber());
+ for (int i = 0; i < defined.requiredRegisters(); i++) {
+ if (definedRegister + i == moveDstRegister || definedRegister + i == moveSrcRegister) {
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+ if (instruction.isMove()) {
+ activeMoves.add(instruction.asMove());
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
new file mode 100644
index 0000000..80a5975
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -0,0 +1,1046 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.Div;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Mul;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Rem;
+import com.android.tools.r8.ir.code.Sub;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.SourceCode;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class Outliner {
+
+ private final InternalOptions options;
+ private final Map<Outline, List<DexEncodedMethod>> candidates = new HashMap<>();
+ private final Map<Outline, DexMethod> generatedOutlines = new HashMap<>();
+ private final Set<DexEncodedMethod> methodsSelectedForOutlining = Sets.newIdentityHashSet();
+
+ static final int MAX_IN_SIZE = 5; // Avoid using ranged calls for outlined code.
+
+ final private AppInfo appInfo;
+ final private DexItemFactory dexItemFactory;
+
+ // Representation of an outline.
+ // This includes the instructions in the outline, and a map from the arguments of this outline
+ // to the values in the instructions.
+ //
+ // E.g. an outline of two StringBuilder.append(String) calls could look like this:
+ //
+ // InvokeVirtual { v5 v6 } Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ // InvokeVirtual { v5 v9 } Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ // ReturnVoid
+ //
+ // It takes three arguments
+ //
+ // v5, v6, v9
+ //
+ // and the argument map is a list mapping the arguments to the in-values of all instructions in
+ // the order they are encountered, in this case:
+ //
+ // [0, 1, 0, 2]
+ //
+ // The actual value numbers (in this example v5, v6, v9 are "arbitrary", as the instruction in
+ // the outline are taken from the block where they are collected as candidates. The comparison
+ // of two outlines rely on the instructions and the argument mapping *not* the concrete values.
+ public class Outline implements Comparable<Outline> {
+
+ final List<Value> arguments;
+ final List<DexType> argumentTypes;
+ final List<Integer> argumentMap;
+ final List<Instruction> templateInstructions = new ArrayList<>();
+ final public DexType returnType;
+
+ private DexProto proto;
+
+ // Build an outline over the instructions [start, end[.
+ // The arguments are the arguments to pass to an outline of these instructions.
+ Outline(BasicBlock block, List<Value> arguments, List<DexType> argumentTypes,
+ List<Integer> argumentMap, DexType returnType, int start, int end) {
+ this.arguments = arguments;
+ this.argumentTypes = argumentTypes;
+ this.argumentMap = argumentMap;
+ this.returnType = returnType;
+
+ List<Instruction> instructions = block.getInstructions();
+ for (int i = start; i < end; i++) {
+ Instruction current = instructions.get(i);
+ if (current.isInvoke() || current.isNewInstance() || current.isArithmeticBinop()) {
+ templateInstructions.add(current);
+ } else if (current.isConstInstruction()) {
+ // Don't include const instructions in the template.
+ } else {
+ assert false : "Unexpected type of instruction in outlining template.";
+ }
+ }
+ }
+
+ int argumentCount() {
+ return arguments.size();
+ }
+
+ DexProto buildProto() {
+ if (proto == null) {
+ DexType[] argumentTypesArray = argumentTypes.toArray(new DexType[argumentTypes.size()]);
+ proto = dexItemFactory.createProto(returnType, argumentTypesArray);
+ }
+ return proto;
+ }
+
+ // Build the DexMethod for this outline.
+ DexMethod buildMethod(DexType clazz, DexString name) {
+ return dexItemFactory.createMethod(clazz, buildProto(), name);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof Outline)) {
+ return false;
+ }
+ List<Instruction> instructions0 = this.templateInstructions;
+ List<Instruction> instructions1 = ((Outline) other).templateInstructions;
+ if (instructions0.size() != instructions1.size()) {
+ return false;
+ }
+ for (int i = 0; i < instructions0.size(); i++) {
+ Instruction i0 = instructions0.get(i);
+ Instruction i1 = instructions1.get(i);
+ if (i0.getClass() != i1.getClass() || !i0.identicalNonValueParts(i1)) {
+ return false;
+ }
+ if ((i0.outValue() != null) != (i1.outValue() != null)) {
+ return false;
+ }
+ }
+ return argumentMap.equals(((Outline) other).argumentMap)
+ && returnType == ((Outline) other).returnType;
+ }
+
+ @Override
+ public int hashCode() {
+ final int MAX_HASH_INSTRUCTIONS = 5;
+
+ int hash = templateInstructions.size();
+ int hashPart = 0;
+ for (int i = 0; i < templateInstructions.size() && i < MAX_HASH_INSTRUCTIONS; i++) {
+ Instruction instruction = templateInstructions.get(i);
+ if (instruction.isInvokeMethod()) {
+ hashPart = hashPart << 4;
+ hashPart += instruction.asInvokeMethod().getInvokedMethod().hashCode();
+ }
+ hash = hash * 3 + hashPart;
+ }
+ return hash;
+ }
+
+ @Override
+ public int compareTo(Outline other) {
+ if (this == other) {
+ return 0;
+ }
+ // First compare the proto.
+ int result;
+ result = buildProto().slowCompareTo(other.buildProto());
+ if (result != 0) {
+ assert !equals(other);
+ return result;
+ }
+ assert argumentCount() == other.argumentCount();
+ // Then compare the instructions (non value part).
+ List<Instruction> instructions0 = templateInstructions;
+ List<Instruction> instructions1 = other.templateInstructions;
+ result = instructions0.size() - instructions1.size();
+ if (result != 0) {
+ assert !equals(other);
+ return result;
+ }
+ for (int i = 0; i < instructions0.size(); i++) {
+ Instruction i0 = instructions0.get(i);
+ Instruction i1 = instructions1.get(i);
+ result = i0.getInstructionName().compareTo(i1.getInstructionName());
+ if (result != 0) {
+ assert !equals(other);
+ return result;
+ }
+ result = i0.inValues().size() - i1.inValues().size();
+ if (result != 0) {
+ assert !equals(other);
+ return result;
+ }
+ result = (i0.outValue() != null ? 1 : 0) - (i1.outValue() != null ? 1 : 0);
+ if (result != 0) {
+ assert !equals(other);
+ return result;
+ }
+ result = i0.compareNonValueParts(i1);
+ if (result != 0) {
+ assert !equals(other);
+ return result;
+ }
+ }
+ // Finally compare the value part.
+ result = argumentMap.size() - other.argumentMap.size();
+ if (result != 0) {
+ assert !equals(other);
+ return result;
+ }
+ for (int i = 0; i < argumentMap.size(); i++) {
+ result = argumentMap.get(i) - other.argumentMap.get(i);
+ if (result != 0) {
+ assert !equals(other);
+ return result;
+ }
+ }
+ assert equals(other);
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ // The printing of the code for an outline maps the value numbers to the arguments numbers.
+ int outRegisterNumber = arguments.size();
+ StringBuilder builder = new StringBuilder();
+ builder.append(returnType);
+ builder.append(" anOutline");
+ StringUtils.append(builder, argumentTypes, ", ", BraceType.PARENS);
+ builder.append("\n");
+ int argumentMapIndex = 0;
+ for (Instruction instruction : templateInstructions) {
+ String name = instruction.getInstructionName();
+ StringUtils.appendRightPadded(builder, name, 20);
+ if (instruction.outValue() != null) {
+ builder.append("v" + outRegisterNumber);
+ builder.append(" <- ");
+ }
+ for (int i = 0; i < instruction.inValues().size(); i++) {
+ builder.append(i > 0 ? ", " : "");
+ builder.append("v");
+ int index = argumentMap.get(argumentMapIndex++);
+ if (index >= 0) {
+ builder.append(index);
+ } else {
+ builder.append(outRegisterNumber);
+ }
+ }
+ if (instruction.isInvoke()) {
+ builder.append("; method: ");
+ builder.append(instruction.asInvokeMethod().getInvokedMethod().toSourceString());
+ }
+ if (instruction.isNewInstance()) {
+ builder.append(instruction.asNewInstance().clazz.toSourceString());
+ }
+ builder.append("\n");
+ }
+ if (returnType == dexItemFactory.voidType) {
+ builder.append("Return-Void");
+ } else {
+ StringUtils.appendRightPadded(builder, "Return", 20);
+ builder.append("v" + (outRegisterNumber));
+ }
+ builder.append("\n");
+ builder.append(argumentMap);
+ return builder.toString();
+ }
+ }
+
+ // Spot the outline opportunities in a basic block.
+ // This is the superclass for both collection candidates and actually replacing code.
+ // TODO(sgjesse): Collect more information in the candidate collection and reuse that for
+ // replacing.
+ abstract private class OutlineSpotter {
+
+ final DexEncodedMethod method;
+ final BasicBlock block;
+ final List<Instruction> instructions;
+
+ int start;
+ int index;
+ int actualInstructions;
+ List<Value> arguments;
+ List<DexType> argumentTypes;
+ List<Integer> argumentsMap;
+ int argumentRegisters;
+ DexType returnType;
+ Value returnValue;
+ int returnValueUsersLeft;
+ int pendingNewInstanceIndex = -1;
+
+ OutlineSpotter(DexEncodedMethod method, BasicBlock block) {
+ this.method = method;
+ this.block = block;
+ this.instructions = block.getInstructions();
+ reset(0);
+ }
+
+ protected void process() {
+ List<Instruction> instructions = block.getInstructions();
+ while (index < instructions.size()) {
+ Instruction current = instructions.get(index);
+ processInstruction(current);
+ }
+ }
+
+ // Get int in-values for an instruction. For commutative binary operations using the current
+ // return value (active out-value) make sure that that value is the left value.
+ protected List<Value> orderedInValues(Instruction instruction, Value returnValue) {
+ List<Value> inValues = instruction.inValues();
+ if (instruction.isBinop() && instruction.asBinop().isCommutative()) {
+ if (inValues.get(1) == returnValue) {
+ Value tmp = inValues.get(0);
+ inValues.set(0, inValues.get(1));
+ inValues.set(1, tmp);
+ }
+ }
+ return inValues;
+ }
+
+ // Process instruction. Returns true if an outline candidate was found.
+ private void processInstruction(Instruction instruction) {
+ // Figure out whether to include the instruction.
+ boolean include = false;
+ int instructionIncrement = 1;
+ if (instruction.isConstInstruction()) {
+ if (index == start) {
+ // Restart for first const instruction.
+ reset(index + 1);
+ return;
+ } else {
+ // Otherwise include const instructions.
+ include = true;
+ instructionIncrement = 0;
+ }
+ } else {
+ include = canIncludeInstruction(instruction);
+ }
+
+ if (include) {
+ actualInstructions += instructionIncrement;
+
+ // Add this instruction.
+ includeInstruction(instruction);
+ // Check if this instruction ends the outline.
+ if (actualInstructions >= options.outline.maxSize) {
+ candidate(start, index + 1);
+ } else {
+ index++;
+ }
+ } else if (index > start) {
+ // Do not add this instruction, candidate ends with previous instruction.
+ candidate(start, index);
+ } else {
+ // Restart search from next instruction.
+ reset(index + 1);
+ }
+ }
+
+ // Check if the current instruction can be included in the outline.
+ private boolean canIncludeInstruction(Instruction instruction) {
+ // Find the users of the active out-value (potential return value).
+ int returnValueUsersLeftIfIncluded = returnValueUsersLeft;
+ if (returnValue != null) {
+ for (Value value : instruction.inValues()) {
+ if (value == returnValue) {
+ returnValueUsersLeftIfIncluded--;
+ }
+ }
+ }
+
+ // If this instruction has an out-value, but the previous one is still active end the
+ // outline.
+ if (instruction.outValue() != null && returnValueUsersLeftIfIncluded > 0) {
+ return false;
+ }
+
+ // Allow all new-instance instructions in a outline.
+ if (instruction.isNewInstance()) {
+ if (instruction.outValue().numberOfAllUsers() > 0) {
+ // Track the new-instance value to make sure the <init> call is part of the outline.
+ pendingNewInstanceIndex = index;
+ }
+ return true;
+ }
+
+ if (instruction.isArithmeticBinop()) {
+ return true;
+ }
+
+ // Otherwise we only allow invoke.
+ if (!instruction.isInvokeMethod()) {
+ return false;
+ }
+ InvokeMethod invoke = instruction.asInvokeMethod();
+ boolean constructor = dexItemFactory.isConstructor(invoke.getInvokedMethod());
+
+ // Lookup the encoded method.
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ DexEncodedMethod target;
+ if (invoke.isInvokeStatic()) {
+ target = appInfo.lookupStaticTarget(invokedMethod);
+ } else if (invoke.isInvokeDirect()) {
+ target = appInfo.lookupDirectTarget(invokedMethod);
+ } else {
+ if (invokedMethod.getHolder().isArrayType()) {
+ return false;
+ }
+ target = appInfo.lookupVirtualTarget(invokedMethod.getHolder(), invokedMethod);
+ }
+ // If the encoded method is found check the access flags.
+ if (target != null) {
+ if (!target.accessFlags.isPublic()) {
+ return false;
+ }
+ DexClass holder = appInfo.definitionFor(invokedMethod.getHolder());
+ if (!holder.accessFlags.isPublic()) {
+ return false;
+ }
+ }
+
+ // Find the number of in-going arguments, if adding this instruction.
+ int newArgumentRegisters = argumentRegisters;
+ if (instruction.inValues().size() > 0) {
+ List<Value> inValues = orderedInValues(instruction, returnValue);
+ for (int i = 0; i < inValues.size(); i++) {
+ Value value = inValues.get(i);
+ if (value == returnValue) {
+ continue;
+ }
+ if (invoke.isInvokeStatic()) {
+ newArgumentRegisters += value.requiredRegisters();
+ } else {
+ // For virtual calls only re-use the receiver argument.
+ if (i > 0 || !arguments.contains(value)) {
+ newArgumentRegisters += value.requiredRegisters();
+ }
+ }
+ }
+ }
+ if (newArgumentRegisters > MAX_IN_SIZE) {
+ return false;
+ }
+
+ // Only include constructors if the previous instruction is the corresponding new-instance.
+ if (constructor) {
+ if (start == index) {
+ return false;
+ }
+ assert index > 0;
+ int offset = 0;
+ Instruction previous;
+ do {
+ offset++;
+ previous = block.getInstructions().get(index - offset);
+ } while (previous.isConstInstruction());
+ if (!previous.isNewInstance() || previous.outValue() != returnValue) {
+ return false;
+ }
+ // Clear pending new instance flag as the last thing, now the matching constructor is known
+ // to be included.
+ pendingNewInstanceIndex = -1;
+ }
+ return true;
+ }
+
+ // Add the current instruction to the outline.
+ private void includeInstruction(Instruction instruction) {
+ List<Value> inValues = orderedInValues(instruction, returnValue);
+
+ Value prevReturnValue = returnValue;
+ if (returnValue != null) {
+ for (Value value : inValues) {
+ if (value == returnValue) {
+ assert returnValueUsersLeft > 0;
+ returnValueUsersLeft--;
+ }
+ if (returnValueUsersLeft == 0) {
+ returnValue = null;
+ returnType = dexItemFactory.voidType;
+ }
+ }
+ }
+
+ if (instruction.isNewInstance()) {
+ assert returnValue == null;
+ updateReturnValueState(instruction.outValue(), instruction.asNewInstance().clazz);
+ return;
+ }
+
+ assert instruction.isInvoke()
+ || instruction.isConstInstruction()
+ || instruction.isArithmeticBinop();
+ if (inValues.size() > 0) {
+ for (int i = 0; i < inValues.size(); i++) {
+ Value value = inValues.get(i);
+ if (value == prevReturnValue) {
+ argumentsMap.add(-1);
+ continue;
+ }
+ if (instruction.isInvoke()
+ && instruction.asInvoke().getType() != Type.STATIC
+ && instruction.asInvoke().getType() != Type.CUSTOM) {
+ InvokeMethod invoke = instruction.asInvokeMethod();
+ int argumentIndex = arguments.indexOf(value);
+ // For virtual calls only re-use the receiver argument.
+ if (i == 0 && argumentIndex != -1) {
+ argumentsMap.add(argumentIndex);
+ } else {
+ arguments.add(value);
+ argumentRegisters += value.requiredRegisters();
+ if (i == 0) {
+ argumentTypes.add(invoke.getInvokedMethod().getHolder());
+ } else {
+ DexProto methodProto;
+ if (instruction.asInvoke().getType() == Type.POLYMORPHIC) {
+ // Type of argument of a polymorphic call must be take from the call site
+ methodProto = instruction.asInvokePolymorphic().getProto();
+ } else {
+ methodProto = invoke.getInvokedMethod().proto;
+ }
+ // -1 due to receiver
+ argumentTypes.add(methodProto.parameters.values[i - 1]);
+ }
+ argumentsMap.add(arguments.size() - 1);
+ }
+ } else {
+ arguments.add(value);
+ if (instruction.isInvokeMethod()) {
+ argumentTypes
+ .add(instruction.asInvokeMethod().getInvokedMethod().proto.parameters.values[i]);
+ } else {
+ argumentTypes.add(instruction.asBinop().getNumericType().dexTypeFor(dexItemFactory));
+ }
+ argumentsMap.add(arguments.size() - 1);
+ }
+ }
+ }
+ if (!instruction.isConstInstruction() && instruction.outValue() != null) {
+ assert returnValue == null;
+ if (instruction.isInvokeMethod()) {
+ updateReturnValueState(
+ instruction.outValue(),
+ instruction.asInvokeMethod().getInvokedMethod().proto.returnType);
+ } else {
+ updateReturnValueState(
+ instruction.outValue(),
+ instruction.asBinop().getNumericType().dexTypeFor(dexItemFactory));
+ }
+ }
+ }
+
+ private void updateReturnValueState(Value newReturnValue, DexType newReturnType) {
+ returnValueUsersLeft = newReturnValue.numberOfAllUsers();
+ // If the return value is not used don't track it.
+ if (returnValueUsersLeft == 0) {
+ returnValue = null;
+ returnType = dexItemFactory.voidType;
+ } else {
+ returnValue = newReturnValue;
+ returnType = newReturnType;
+ }
+ }
+
+
+ protected abstract void handle(int start, int end, Outline outline);
+
+ private void candidate(int start, int index) {
+ assert !instructions.get(start).isConstInstruction();
+
+ if (pendingNewInstanceIndex != -1) {
+ if (pendingNewInstanceIndex == start) {
+ reset(index);
+ } else {
+ reset(pendingNewInstanceIndex);
+ }
+ return;
+ }
+
+ // Back out of any const instructions ending this candidate.
+ int end = index;
+ while (instructions.get(end - 1).isConstInstruction()) {
+ end--;
+ }
+
+ // Check if the candidate qualifies.
+ int nonConstInstructions = 0;
+ for (int i = start; i < end; i++) {
+ if (!instructions.get(i).isConstInstruction()) {
+ nonConstInstructions++;
+ }
+ }
+ if (nonConstInstructions < options.outline.minSize) {
+ reset(start + 1);
+ return;
+ }
+
+ Outline outline = new Outline(
+ block, arguments, argumentTypes, argumentsMap, returnType, start, end);
+ handle(start, end, outline);
+
+ // Start a new candidate search from the next instruction after this outline.
+ reset(index);
+ }
+
+ // Restart the collection of outline candidate to the given instruction start index.
+ private void reset(int startIndex) {
+ start = startIndex;
+ index = startIndex;
+ actualInstructions = 0;
+ arguments = new ArrayList<>(MAX_IN_SIZE);
+ argumentTypes = new ArrayList<>(MAX_IN_SIZE);
+ argumentsMap = new ArrayList<>(MAX_IN_SIZE);
+ argumentRegisters = 0;
+ returnType = dexItemFactory.voidType;
+ returnValue = null;
+ returnValueUsersLeft = 0;
+ pendingNewInstanceIndex = -1;
+ }
+ }
+
+ // Collect outlining candidates with the methods that can use them.
+ // TODO(sgjesse): This does not take several usages in the same method into account.
+ private class OutlineIdentifier extends OutlineSpotter {
+
+ OutlineIdentifier(DexEncodedMethod method, BasicBlock block) {
+ super(method, block);
+ }
+
+ protected void handle(int start, int end, Outline outline) {
+ synchronized (candidates) {
+ candidates.computeIfAbsent(outline, k -> new ArrayList<>()).add(method);
+ }
+ }
+ }
+
+ // Replace instructions with a call to the outlined method.
+ private class OutlineRewriter extends OutlineSpotter {
+
+ private final IRCode code;
+ private final ListIterator<BasicBlock> blocksIterator;
+ private final List<Integer> toRemove;
+ int argumentsMapIndex;
+ Value returnValue;
+
+ OutlineRewriter(
+ DexEncodedMethod method, IRCode code,
+ ListIterator<BasicBlock> blocksIterator, BasicBlock block, List<Integer> toRemove) {
+ super(method, block);
+ this.code = code;
+ this.blocksIterator = blocksIterator;
+ this.toRemove = toRemove;
+ }
+
+ protected void handle(int start, int end, Outline outline) {
+ if (candidates.containsKey(outline)) {
+ DexMethod m = generatedOutlines.get(outline);
+ assert m != null;
+ List<Instruction> instructions = block.getInstructions();
+ List<Value> in = new ArrayList<>();
+ returnValue = null;
+ argumentsMapIndex = 0;
+ for (int i = start; i < end; i++) {
+ Instruction current = instructions.get(i);
+ if (current.isConstInstruction()) {
+ // Leave any const instructions.
+ continue;
+ }
+
+ // Prepare to remove the instruction.
+ List<Value> inValues = orderedInValues(current, returnValue);
+ for (int j = 0; j < inValues.size(); j++) {
+ Value value = inValues.get(j);
+ value.removeUser(current);
+ int argumentIndex = outline.argumentMap.get(argumentsMapIndex++);
+ if (argumentIndex >= in.size()) {
+ assert argumentIndex == in.size();
+ in.add(value);
+ }
+ }
+ if (current.outValue() != null) {
+ returnValue = current.outValue();
+ }
+ // The invoke of the outline method will be placed at the last instruction index,
+ // so don't mark that for removal.
+ if (i < end - 1) {
+ toRemove.add(i);
+ }
+ }
+ assert m.proto.shorty.toString().length() - 1 == in.size();
+ if (returnValue != null && returnValue.numberOfAllUsers() == 0) {
+ returnValue = null;
+ }
+ Invoke outlineInvoke = new InvokeStatic(m, returnValue, in);
+ outlineInvoke.setBlock(block);
+ instructions.get(end - 1).clearBlock();
+ instructions.set(end - 1, outlineInvoke);
+ if (block.hasCatchHandlers()) {
+ // If the inserted invoke is inserted in a block with handlers, split the block after
+ // the inserted invoke.
+ // TODO(sgjesse): Integrate the use of an instruction iterator into the code above.
+ block.listIterator(end).split(code, blocksIterator);
+ }
+ }
+ }
+ }
+
+ public Outliner(AppInfo appInfo, InternalOptions options) {
+ this.appInfo = appInfo;
+ this.dexItemFactory = appInfo.dexItemFactory;
+ this.options = options;
+ }
+
+ public void identifyCandidates(IRCode code, DexEncodedMethod method) {
+ assert !(method.getCode() instanceof OutlineCode);
+ for (BasicBlock block : code.blocks) {
+ new OutlineIdentifier(method, block).process();
+ }
+ }
+
+ public boolean selectMethodsForOutlining() {
+ assert methodsSelectedForOutlining.size() == 0;
+ List<Outline> toRemove = new ArrayList<>();
+ for (Entry<Outline, List<DexEncodedMethod>> entry : candidates.entrySet()) {
+ if (entry.getValue().size() < options.outline.threshold) {
+ toRemove.add(entry.getKey());
+ } else {
+ methodsSelectedForOutlining.addAll(entry.getValue());
+ }
+ }
+ for (Outline outline : toRemove) {
+ candidates.remove(outline);
+ }
+ return methodsSelectedForOutlining.size() > 0;
+ }
+
+ public Set<DexEncodedMethod> getMethodsSelectedForOutlining() {
+ return methodsSelectedForOutlining;
+ }
+
+ public void applyOutliningCandidate(IRCode code, DexEncodedMethod method) {
+ assert !(method.getCode() instanceof OutlineCode);
+ ListIterator<BasicBlock> blocksIterator = code.blocks.listIterator();
+ while (blocksIterator.hasNext()) {
+ BasicBlock block = blocksIterator.next();
+ List<Integer> toRemove = new ArrayList<>();
+ new OutlineRewriter(method, code, blocksIterator, block, toRemove).process();
+ block.removeInstructions(toRemove);
+ }
+ }
+
+ static public void noProcessing(IRCode code, DexEncodedMethod method) {
+ // No operation.
+ }
+
+ private class OutlineSourceCode implements SourceCode {
+
+ final private Outline outline;
+ int argumentMapIndex = 0;
+
+ OutlineSourceCode(Outline outline) {
+ this.outline = outline;
+ }
+
+ @Override
+ public boolean needsPrelude() {
+ return outline.argumentCount() > 0;
+ }
+
+ @Override
+ public int instructionCount() {
+ return outline.templateInstructions.size() + 1;
+ }
+
+ @Override
+ public int instructionIndex(int instructionOffset) {
+ return instructionOffset;
+ }
+
+ @Override
+ public int instructionOffset(int instructionIndex) {
+ return instructionIndex;
+ }
+
+ @Override
+ public DebugLocalInfo getCurrentLocal(int register) {
+ return null;
+ }
+
+ @Override
+ public boolean traceInstruction(int instructionIndex, IRBuilder builder) {
+ // There is just one block, and after the last instruction it is closed.
+ return instructionIndex == outline.templateInstructions.size();
+ }
+
+ @Override
+ public void closedCurrentBlockWithFallthrough(int fallthroughInstructionIndex) {
+ }
+
+ @Override
+ public void closedCurrentBlock() {
+ }
+
+ @Override
+ public void setUp() {
+ }
+
+ @Override
+ public void clear() {
+ }
+
+ @Override
+ public void buildPrelude(IRBuilder builder) {
+ // Fill in the Argument instructions in the argument block.
+ for (int i = 0; i < outline.arguments.size(); i++) {
+ MoveType moveType = outline.arguments.get(i).outType();
+ builder.addNonThisArgument(i, moveType);
+ }
+ }
+
+ @Override
+ public void buildPostlude(IRBuilder builder) {
+ // Intentionally left empty. (Needed for Java-bytecode-frontend synchronization support.)
+ }
+
+ @Override
+ public void buildInstruction(IRBuilder builder, int instructionIndex) {
+ if (instructionIndex == outline.templateInstructions.size()) {
+ if (outline.returnType == dexItemFactory.voidType) {
+ builder.addReturn();
+ } else {
+ builder.addReturn(MoveType.fromDexType(outline.returnType), outline.argumentCount());
+ }
+ return;
+ }
+ // Build IR from the template.
+ Instruction template = outline.templateInstructions.get(instructionIndex);
+ List<Value> inValues = new ArrayList<>(template.inValues().size());
+ List<Value> templateInValues = template.inValues();
+ // The template in-values are not re-ordered for commutative binary operations, as it does
+ // not matter.
+ for (int i = 0; i < templateInValues.size(); i++) {
+ Value value = templateInValues.get(i);
+ int register = outline.argumentMap.get(argumentMapIndex++);
+ if (register == -1) {
+ register = outline.argumentCount();
+ }
+ inValues.add(
+ builder.readRegister(register, value.outType()));
+ }
+ Value outValue = null;
+ if (template.outValue() != null) {
+ Value value = template.outValue();
+ outValue = builder
+ .writeRegister(outline.argumentCount(), value.outType(), ThrowingInfo.CAN_THROW);
+ }
+
+ Instruction newInstruction = null;
+ if (template.isInvoke()) {
+ Invoke templateInvoke = template.asInvoke();
+ newInstruction = Invoke.createFromTemplate(templateInvoke, outValue, inValues);
+ } else if (template.isAdd()) {
+ Add templateInvoke = template.asAdd();
+ newInstruction = new Add(
+ templateInvoke.getNumericType(), outValue, inValues.get(0), inValues.get(1));
+ } else if (template.isMul()) {
+ Mul templateInvoke = template.asMul();
+ newInstruction = new Mul(
+ templateInvoke.getNumericType(), outValue, inValues.get(0), inValues.get(1));
+ } else if (template.isSub()) {
+ Sub templateInvoke = template.asSub();
+ newInstruction = new Sub(
+ templateInvoke.getNumericType(), outValue, inValues.get(0), inValues.get(1));
+ } else if (template.isDiv()) {
+ Div templateInvoke = template.asDiv();
+ newInstruction = new Div(
+ templateInvoke.getNumericType(), outValue, inValues.get(0), inValues.get(1));
+ } else if (template.isRem()) {
+ Rem templateInvoke = template.asRem();
+ newInstruction = new Rem(
+ templateInvoke.getNumericType(), outValue, inValues.get(0), inValues.get(1));
+ } else {
+ assert template.isNewInstance();
+ NewInstance templateNewInstance = template.asNewInstance();
+ newInstruction = new NewInstance(templateNewInstance.clazz, outValue);
+ }
+ builder.add(newInstruction);
+ }
+
+ @Override
+ public void resolveAndBuildSwitch(Switch.Type type, int value, int fallthroughOffset,
+ int payloadOffset, IRBuilder builder) {
+ throw new Unreachable("Unexpected call to resolveAndBuildSwitch");
+ }
+
+ @Override
+ public void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset,
+ IRBuilder builder) {
+ throw new Unreachable("Unexpected call to resolveAndBuildNewArrayFilledData");
+ }
+
+ @Override
+ public CatchHandlers<Integer> getCurrentCatchHandlers() {
+ return null;
+ }
+
+ @Override
+ public boolean verifyCurrentInstructionCanThrow() {
+ // TODO(sgjesse): Check more here?
+ return true;
+ }
+
+ @Override
+ public boolean verifyLocalInScope(DebugLocalInfo local) {
+ return true;
+ }
+
+ @Override
+ public boolean verifyRegister(int register) {
+ return true;
+ }
+ }
+
+ public class OutlineCode extends Code {
+
+ private Outline outline;
+
+ OutlineCode(Outline outline) {
+ this.outline = outline;
+ }
+
+ @Override
+ public boolean isOutlineCode() {
+ return true;
+ }
+
+ @Override
+ public OutlineCode asOutlineCode() {
+ return this;
+ }
+
+ @Override
+ public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
+ OutlineSourceCode source = new OutlineSourceCode(outline);
+ IRBuilder builder = new IRBuilder(encodedMethod, source, options);
+ return builder.build();
+ }
+
+ @Override
+ public String toString() {
+ return outline.toString();
+ }
+
+ @Override
+ public void registerReachableDefinitions(UseRegistry registry) {
+ throw new Unreachable();
+ }
+
+ @Override
+ protected int computeHashCode() {
+ return outline.hashCode();
+ }
+
+ @Override
+ protected boolean computeEquals(Object other) {
+ return outline.equals(outline);
+ }
+
+ @Override
+ public String toString(ClassNameMapper naming) {
+ return null;
+ }
+ }
+
+
+ public DexProgramClass buildOutlinerClass(DexType type) {
+ if (candidates.size() == 0) {
+ return null;
+ }
+
+ // Build the outlined methods.
+ DexEncodedMethod[] direct = new DexEncodedMethod[candidates.size()];
+ int count = 0;
+
+ // By now the candidates are the actual selected outlines. Name the generated methods in a
+ // consistent order, to provide deterministic output.
+ List<Outline> outlines = new ArrayList<>(candidates.keySet());
+ outlines.sort(Comparator.naturalOrder());
+ for (Outline outline : outlines) {
+ DexAccessFlags methodAccess = new DexAccessFlags(Constants.ACC_PUBLIC, Constants.ACC_STATIC);
+ DexString methodName = dexItemFactory.createString(options.outline.methodPrefix + count);
+ DexMethod method = outline.buildMethod(type, methodName);
+ direct[count] = new DexEncodedMethod(method, methodAccess, DexAnnotationSet.empty(),
+ DexAnnotationSetRefList.empty(), new OutlineCode(outline));
+ generatedOutlines.put(outline, method);
+ count++;
+ }
+ // No need to sort the direct methods as they are generated in sorted order.
+
+ // Build the outliner class.
+ DexType superType = dexItemFactory.createType("Ljava/lang/Object;");
+ DexTypeList interfaces = DexTypeList.empty();
+ DexString sourceFile = dexItemFactory.createString("outline");
+ DexAccessFlags accessFlags = new DexAccessFlags(Constants.ACC_PUBLIC);
+ DexProgramClass clazz = new DexProgramClass(
+ type,
+ DexClass.Origin.Synthetic,
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ // TODO: Build dex annotations structure.
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY, // Static fields.
+ DexEncodedField.EMPTY_ARRAY, // Instance fields.
+ direct,
+ DexEncodedMethod.EMPTY_ARRAY // Virtual methods.
+ );
+
+ return clazz;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
new file mode 100644
index 0000000..839a51b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
@@ -0,0 +1,260 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.Goto;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
+import com.android.tools.r8.ir.regalloc.LiveIntervals;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.google.common.base.Equivalence.Wrapper;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+public class PeepholeOptimizer {
+
+ /**
+ * Perform optimizations of the code with register assignments provided by the register allocator.
+ */
+ public static void optimize(IRCode code, LinearScanRegisterAllocator allocator) {
+ removeIdenticalPredecessorBlocks(code, allocator);
+ removeRedundantInstructions(code, allocator);
+ shareIdenticalBlockSuffix(code, allocator);
+ assert code.isConsistentGraph();
+ }
+
+ /**
+ * Identify common suffixes in predecessor blocks and share them.
+ */
+ private static void shareIdenticalBlockSuffix(IRCode code, RegisterAllocator allocator) {
+ Collection<BasicBlock> blocks = code.blocks;
+ do {
+ Map<BasicBlock, BasicBlock> newBlocks = new IdentityHashMap<>();
+ for (BasicBlock block : blocks) {
+ InstructionEquivalence equivalence = new InstructionEquivalence(allocator);
+ // Group interesting predecessor blocks by their last instruction.
+ Map<Wrapper<Instruction>, List<BasicBlock>> lastInstructionToBlocks = new HashMap<>();
+ for (BasicBlock pred : block.getPredecessors()) {
+ // Only deal with predecessors with one successor. This way we can move throwing
+ // instructions as well since there are no handlers (or the handler is the same as the
+ // normal control-flow block). Alternatively, we could move only non-throwing instructions
+ // and allow both a goto edge and exception edges when the target does not start with a
+ // MoveException instruction. However, that would require us to require rewriting of
+ // catch handlers as well.
+ if (pred.exit().isGoto() &&
+ pred.getSuccessors().size() == 1 &&
+ pred.getInstructions().size() > 1) {
+ List<Instruction> instructions = pred.getInstructions();
+ Instruction lastInstruction = instructions.get(instructions.size() - 2);
+ List<BasicBlock> value = lastInstructionToBlocks.computeIfAbsent(
+ equivalence.wrap(lastInstruction), (k) -> new ArrayList<>());
+ value.add(pred);
+ }
+ }
+ // For each group of predecessors of size 2 or more, find the largest common suffix and
+ // move that to a separate block.
+ for (List<BasicBlock> predsWithSameLastInstruction : lastInstructionToBlocks.values()) {
+ if (predsWithSameLastInstruction.size() < 2) {
+ continue;
+ }
+ BasicBlock firstPred = predsWithSameLastInstruction.get(0);
+ int commonSuffixSize = firstPred.getInstructions().size();
+ for (int i = 1; i < predsWithSameLastInstruction.size(); i++) {
+ BasicBlock pred = predsWithSameLastInstruction.get(i);
+ assert pred.exit().isGoto();
+ commonSuffixSize = Math.min(
+ commonSuffixSize, sharedSuffixSizeExcludingExit(firstPred, pred, allocator));
+ }
+ assert commonSuffixSize >= 1;
+ int blockNumber = code.blocks.size() + newBlocks.size();
+ BasicBlock newBlock = createAndInsertBlockForSuffix(
+ blockNumber, commonSuffixSize, predsWithSameLastInstruction, block);
+ newBlocks.put(predsWithSameLastInstruction.get(0), newBlock);
+ }
+ }
+ ListIterator<BasicBlock> blockIterator = code.listIterator();
+ while (blockIterator.hasNext()) {
+ BasicBlock block = blockIterator.next();
+ if (newBlocks.containsKey(block)) {
+ blockIterator.add(newBlocks.get(block));
+ }
+ }
+ // Go through all the newly introduced blocks to find more common suffixes to share.
+ blocks = newBlocks.values();
+ } while (!blocks.isEmpty());
+ }
+
+ private static BasicBlock createAndInsertBlockForSuffix(
+ int blockNumber, int suffixSize, List<BasicBlock> preds, BasicBlock block) {
+ BasicBlock newBlock = BasicBlock.createGotoBlock(blockNumber);
+ BasicBlock first = preds.get(0);
+ InstructionListIterator from = first.listIterator(first.getInstructions().size() - 1);
+ boolean movedThrowingInstruction = false;
+ for (int i = 0; i < suffixSize; i++) {
+ Instruction instruction = from.previous();
+ movedThrowingInstruction = movedThrowingInstruction || instruction.instructionTypeCanThrow();
+ newBlock.getInstructions().addFirst(instruction);
+ }
+ if (movedThrowingInstruction && first.hasCatchHandlers()) {
+ newBlock.transferCatchHandlers(first);
+ }
+ for (BasicBlock pred : preds) {
+ LinkedList<Instruction> instructions = pred.getInstructions();
+ Instruction exit = instructions.removeLast();
+ for (int i = 0; i < suffixSize; i++) {
+ instructions.removeLast();
+ }
+ instructions.add(exit);
+ newBlock.getPredecessors().add(pred);
+ pred.replaceSuccessor(block, newBlock);
+ block.getPredecessors().remove(pred);
+ if (movedThrowingInstruction) {
+ pred.clearCatchHandlers();
+ }
+ }
+ newBlock.link(block);
+ return newBlock;
+ }
+
+ private static int sharedSuffixSizeExcludingExit(
+ BasicBlock block0, BasicBlock block1, RegisterAllocator allocator) {
+ InstructionListIterator it0 = block0.listIterator(block0.getInstructions().size() - 1);
+ InstructionListIterator it1 = block1.listIterator(block1.getInstructions().size() - 1);
+ int suffixSize = 0;
+ while (it0.hasPrevious() && it1.hasPrevious()) {
+ Instruction i0 = it0.previous();
+ Instruction i1 = it1.previous();
+ if (!i0.identicalAfterRegisterAllocation(i1, allocator)) {
+ return suffixSize;
+ }
+ suffixSize++;
+ }
+ return suffixSize;
+ }
+
+ /**
+ * If two predecessors have the same code and successors. Replace one of them with an
+ * empty block with a goto to the other.
+ */
+ private static void removeIdenticalPredecessorBlocks(IRCode code, RegisterAllocator allocator) {
+ BasicBlockInstructionsEquivalence equivalence =
+ new BasicBlockInstructionsEquivalence(allocator);
+ // Locate one block at a time that has identical predecessors. Rewrite those predecessors and
+ // then start over. Restarting when one blocks predecessors have been rewritten simplifies
+ // the rewriting and reduces the size of the data structures.
+ boolean changed;
+ do {
+ changed = false;
+ for (BasicBlock block : code.blocks) {
+ Map<Wrapper<BasicBlock>, Integer> blockToIndex = new HashMap<>();
+ for (int predIndex = 0; predIndex < block.getPredecessors().size(); predIndex++) {
+ BasicBlock pred = block.getPredecessors().get(predIndex);
+ if (pred.getInstructions().size() == 1) {
+ continue;
+ }
+ Wrapper<BasicBlock> wrapper = equivalence.wrap(pred);
+ if (blockToIndex.containsKey(wrapper)) {
+ changed = true;
+ int otherPredIndex = blockToIndex.get(wrapper);
+ BasicBlock otherPred = block.getPredecessors().get(otherPredIndex);
+ pred.clearCatchHandlers();
+ pred.getInstructions().clear();
+ for (BasicBlock succ : pred.getSuccessors()) {
+ succ.removePredecessor(pred);
+ }
+ pred.getSuccessors().clear();
+ pred.getSuccessors().add(otherPred);
+ assert !otherPred.getPredecessors().contains(pred);
+ otherPred.getPredecessors().add(pred);
+ Goto exit = new Goto();
+ exit.setBlock(pred);
+ pred.getInstructions().add(exit);
+ } else {
+ blockToIndex.put(wrapper, predIndex);
+ }
+ }
+ }
+ } while (changed);
+ }
+
+ /**
+ * Remove redundant instructions from the code.
+ *
+ * <p>Currently removes move instructions with the same src and target register and const
+ * instructions where the constant is known to be in the register already.
+ *
+ * @param code the code from which to remove redundant instruction
+ * @param allocator the register allocator providing registers for values
+ */
+ private static void removeRedundantInstructions(
+ IRCode code, LinearScanRegisterAllocator allocator) {
+ for (BasicBlock block : code.blocks) {
+ // Mapping from register number to const number instructions for this basic block.
+ // Used to remove redundant const instructions that reloads the same constant into
+ // the same register.
+ Map<Integer, ConstNumber> registerToNumber = new HashMap<>();
+ MoveEliminator moveEliminator = new MoveEliminator(allocator);
+ ListIterator<Instruction> iterator = block.getInstructions().listIterator();
+ while (iterator.hasNext()) {
+ Instruction current = iterator.next();
+ if (moveEliminator.shouldBeEliminated(current)) {
+ iterator.remove();
+ } else if (current.outValue() != null && current.outValue().needsRegister()) {
+ Value outValue = current.outValue();
+ int instructionNumber = current.getNumber();
+ if (current.isConstNumber()) {
+ if (constantSpilledAtDefinition(current.asConstNumber(), allocator)) {
+ // Remove constant instructions that are spilled at their definition and are
+ // therefore unused.
+ iterator.remove();
+ continue;
+ }
+ int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
+ ConstNumber numberInRegister = registerToNumber.get(outRegister);
+ if (numberInRegister != null && numberInRegister.identicalNonValueParts(current)) {
+ // This instruction is not needed, the same constant is already in this register.
+ iterator.remove();
+ } else {
+ // Insert the current constant in the mapping. Make sure to clobber the second
+ // register if wide.
+ registerToNumber.put(outRegister, current.asConstNumber());
+ if (current.outType() == MoveType.WIDE) {
+ registerToNumber.remove(outRegister + 1);
+ }
+ }
+ } else {
+ // This instruction writes registers with a non-constant value. Remove the registers
+ // from the mapping.
+ int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
+ for (int i = 0; i < outValue.requiredRegisters(); i++) {
+ registerToNumber.remove(outRegister + i);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static boolean constantSpilledAtDefinition(
+ ConstNumber constNumber, LinearScanRegisterAllocator allocator) {
+ if (constNumber.outValue().isFixedRegisterValue()) {
+ return false;
+ }
+ LiveIntervals definitionIntervals =
+ constNumber.outValue().getLiveIntervals().getSplitCovering(constNumber.getNumber());
+ return definitionIntervals.isSpilledAndRematerializable(allocator);
+ }
+}
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
new file mode 100644
index 0000000..e79abc1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -0,0 +1,1891 @@
+// 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.ir.regalloc;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.And;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.DebugPosition;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Move;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Or;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Sub;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.Value.DebugInfo;
+import com.android.tools.r8.ir.code.Xor;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multiset;
+import com.google.common.collect.Multiset.Entry;
+import com.google.common.collect.Multisets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.Queue;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Linear scan register allocator.
+ *
+ * <p>The implementation is inspired by:
+ *
+ * <ul>
+ * <li>"Linear Scan Register Allocation in the Context of SSA Form and Register Constraints"
+ * (ftp://ftp.ssw.uni-linz.ac.at/pub/Papers/Moe02.PDF)</li>
+ * <li>"Linear Scan Register Allocation on SSA Form"
+ * (http://www.christianwimmer.at/Publications/Wimmer10a/Wimmer10a.pdf)</li>
+ * <li>"Linear Scan Register Allocation for the Java HotSpot Client Compiler"
+ * (http://www.christianwimmer.at/Publications/Wimmer04a/Wimmer04a.pdf)</li>
+ * </ul>
+ */
+public class LinearScanRegisterAllocator implements RegisterAllocator {
+ public static final int NO_REGISTER = Integer.MIN_VALUE;
+
+ private enum ArgumentReuseMode {
+ ALLOW_ARGUMENT_REUSE,
+ DISALLOW_ARGUMENT_REUSE
+ }
+
+ private static class LocalRange implements Comparable<LocalRange> {
+ final DebugLocalInfo local;
+ final int register;
+ final int start;
+ final int end;
+
+ LocalRange(Value value, int register, int start, int end) {
+ assert value.getLocalInfo() != null;
+ this.local = value.getLocalInfo();
+ this.register = register;
+ this.start = start;
+ this.end = end;
+ }
+
+ @Override
+ public int compareTo(LocalRange o) {
+ return (start != o.start)
+ ? Integer.compare(start, o.start)
+ : Integer.compare(end, o.end);
+ }
+
+ @Override
+ public String toString() {
+ return local + " @ r" + register + ": " + new LiveRange(start, end);
+ }
+ }
+
+ // When numbering instructions we number instructions only with even numbers. This allows us to
+ // use odd instruction numbers for the insertion of moves during spilling.
+ public static final int INSTRUCTION_NUMBER_DELTA = 2;
+ // The max register number that will fit in any dex instruction encoding.
+ private static final int MAX_SMALL_REGISTER = Constants.U4BIT_MAX;
+ // We put one sentinel in front of the argument chain and one after the argument chain.
+ private static final int NUMBER_OF_SENTINEL_REGISTERS = 2;
+
+ // The code for which to allocate registers.
+ private final IRCode code;
+ // Number of registers used for arguments.
+ private final int numberOfArgumentRegisters;
+
+ // Mapping from basic blocks to the set of values live at entry to that basic block.
+ private Map<BasicBlock, Set<Value>> liveAtEntrySets = new IdentityHashMap<>();
+ // The sentinel value starting the chain of linked argument values.
+ private Value preArgumentSentinelValue = null;
+
+ // The set of registers that are free for allocation.
+ private TreeSet<Integer> freeRegisters = new TreeSet<>();
+ // The max register number used.
+ private int maxRegisterNumber = 0;
+ // The next available register number not yet included in the set of used registers.
+ private int nextUnusedRegisterNumber = 0;
+
+ // List of all top-level live intervals for all SSA values.
+ private List<LiveIntervals> liveIntervals = new ArrayList<>();
+ // List of active intervals.
+ private List<LiveIntervals> active = new LinkedList<>();
+ // List of intervals where the current instruction falls into one of their live range holes.
+ private List<LiveIntervals> inactive = new LinkedList<>();
+ // List of intervals that no register has been allocated to sorted by first live range.
+ private PriorityQueue<LiveIntervals> unhandled =
+ new PriorityQueue<>((o1, o2) -> Integer.compare(o1.getStart(), o2.getStart()));
+
+ // The first register used for parallel moves. After register allocation the parallel move
+ // temporary registers are [firstParallelMoveTemporary, maxRegisterNumber].
+ private int firstParallelMoveTemporary = NO_REGISTER;
+ // Mapping from register number to the number of unused register numbers below that register
+ // number. Used for compacting the register numbers if some spill registers are not used
+ // because their values can be rematerialized.
+ private int[] unusedRegisters = null;
+
+ // Whether or not the code has a move exception instruction. Used to pin the move exception
+ // register.
+ private boolean hasDedicatedMoveExceptionRegister = false;
+
+ public LinearScanRegisterAllocator(IRCode code) {
+ this.code = code;
+ int argumentRegisters = 0;
+ for (Instruction instruction : code.blocks.getFirst().getInstructions()) {
+ if (instruction.isArgument()) {
+ argumentRegisters += instruction.outValue().requiredRegisters();
+ }
+ }
+ numberOfArgumentRegisters = argumentRegisters;
+ }
+
+ /**
+ * Perform register allocation for the IRCode.
+ */
+ public void allocateRegisters(boolean debug) {
+ // There are no linked values prior to register allocation.
+ assert noLinkedValues();
+ assert code.isConsistentSSA();
+ computeNeedsRegister();
+ insertArgumentMoves();
+ BasicBlock[] blocks = computeLivenessInformation();
+ // First attempt to allocate register allowing argument reuse. This will fail if spilling
+ // is required or if we end up using more than 15 registers.
+ boolean noSpilling =
+ performAllocationWithoutMoveInsertion(ArgumentReuseMode.ALLOW_ARGUMENT_REUSE);
+ if (!noSpilling || registersUsed() > MAX_SMALL_REGISTER) {
+ // Redo allocation disallowing argument reuse. This always succeeds.
+ clearRegisterAssignments();
+ performAllocation(ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE);
+ } else {
+ // Insert spill and phi moves after allocating with argument reuse. If the moves causes
+ // the method to use more than 15 registers we redo allocation disallowing argument
+ // reuse. This very rarely happens in practice (12 methods on GMSCore v4 hits that case).
+ insertMoves();
+ if (registersUsed() > MAX_SMALL_REGISTER) {
+ // Redo allocation disallowing argument reuse. This always succeeds.
+ clearRegisterAssignments();
+ removeSpillAndPhiMoves();
+ performAllocation(ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE);
+ }
+ }
+
+ clearUserInfo();
+ assert code.isConsistentGraph();
+ if (Log.ENABLED) {
+ Log.debug(this.getClass(), toString());
+ }
+ computeUnusedRegisters();
+ if (debug) {
+ computeDebugInfo(blocks);
+ }
+ clearState();
+ }
+
+ private static Integer nextInRange(int start, int end, List<Integer> points) {
+ while (!points.isEmpty() && points.get(0) < start) {
+ points.remove(0);
+ }
+ if (points.isEmpty()) {
+ return null;
+ }
+ Integer next = points.get(0);
+ assert start <= next;
+ if (next < end) {
+ points.remove(0);
+ return next;
+ }
+ return null;
+ }
+
+ private void computeDebugInfo(BasicBlock[] blocks) {
+ // Collect live-ranges for all SSA values with local information.
+ List<LocalRange> ranges = new ArrayList<>();
+ for (LiveIntervals interval : liveIntervals) {
+ Value value = interval.getValue();
+ if (value.getLocalInfo() == null) {
+ continue;
+ }
+ List<Integer> starts = ListUtils.map(value.getDebugLocalStarts(), Instruction::getNumber);
+ List<Integer> ends = ListUtils.map(value.getDebugLocalEnds(), Instruction::getNumber);
+ List<LiveRange> liveRanges = new ArrayList<>();
+ liveRanges.addAll(interval.getRanges());
+ for (LiveIntervals child : interval.getSplitChildren()) {
+ assert child.getValue() == value;
+ assert child.getSplitChildren() == null || child.getSplitChildren().isEmpty();
+ liveRanges.addAll(child.getRanges());
+ }
+ liveRanges.sort((r1, r2) -> Integer.compare(r1.start, r2.start));
+ starts.sort(Integer::compare);
+ ends.sort(Integer::compare);
+
+ for (LiveRange liveRange : liveRanges) {
+ int start = liveRange.start;
+ int end = liveRange.end;
+ Integer nextEnd;
+ while ((nextEnd = nextInRange(start, end, ends)) != null) {
+ ranges.add(new LocalRange(value, getRegisterForValue(value, start), start, nextEnd));
+ Integer nextStart = nextInRange(nextEnd, end, starts);
+ if (nextStart == null) {
+ start = -1;
+ break;
+ }
+ start = nextStart;
+ }
+ if (start >= 0) {
+ ranges.add(new LocalRange(value, getRegisterForValue(value, start), start, end));
+ }
+ }
+ }
+ if (ranges.isEmpty()) {
+ return;
+ }
+ ranges.sort(LocalRange::compareTo);
+
+ // For each debug position compute the set of live locals.
+ // The previously known locals is used to share locals when unchanged.
+ boolean localsChanged = false;
+ ImmutableMap<Integer, DebugLocalInfo> previousLocals = ImmutableMap.of();
+ Map<Integer, DebugLocalInfo> currentLocals = new HashMap<>();
+
+ LinkedList<LocalRange> openRanges = new LinkedList<>();
+ Iterator<LocalRange> rangeIterator = ranges.iterator();
+ LocalRange nextStartingRange = rangeIterator.next();
+
+ // Open initial (argument) ranges.
+ while (nextStartingRange != null && nextStartingRange.start == 0) {
+ currentLocals.put(nextStartingRange.register, nextStartingRange.local);
+ openRanges.add(nextStartingRange);
+ nextStartingRange = rangeIterator.hasNext() ? rangeIterator.next() : null;
+ localsChanged = true;
+ }
+
+ for (BasicBlock block : blocks) {
+ Iterator<Instruction> instructionIterator = block.getInstructions().iterator();
+ while (instructionIterator.hasNext()) {
+ Instruction instruction = instructionIterator.next();
+ // Remove any local-read instructions now that live ranges are computed.
+ if (instruction.isDebugLocalRead()) {
+ instructionIterator.remove();
+ continue;
+ }
+ if (!instruction.isDebugPosition()) {
+ continue;
+ }
+ int index = instruction.getNumber();
+ ListIterator<LocalRange> it = openRanges.listIterator(0);
+ while (it.hasNext()) {
+ LocalRange openRange = it.next();
+ if (openRange.end < index) {
+ it.remove();
+ assert currentLocals.get(openRange.register) == openRange.local;
+ currentLocals.put(openRange.register, null);
+ localsChanged = true;
+ }
+ }
+ while (nextStartingRange != null && nextStartingRange.start <= index) {
+ // If the full range is between the two debug positions ignore it.
+ if (nextStartingRange.end >= index) {
+ openRanges.add(nextStartingRange);
+ assert currentLocals.get(nextStartingRange.register) == null;
+ currentLocals.put(nextStartingRange.register, nextStartingRange.local);
+ localsChanged = true;
+ }
+ nextStartingRange = rangeIterator.hasNext() ? rangeIterator.next() : null;
+ }
+ DebugPosition position = instruction.asDebugPosition();
+ if (localsChanged) {
+ ImmutableMap.Builder<Integer, DebugLocalInfo> locals = ImmutableMap.builder();
+ for (Map.Entry<Integer, DebugLocalInfo> entry : currentLocals.entrySet()) {
+ if (entry.getValue() != null) {
+ locals.put(entry);
+ }
+ }
+ localsChanged = false;
+ previousLocals = locals.build();
+ }
+ assert verifyLocalsEqual(previousLocals, currentLocals);
+ position.setLocals(previousLocals);
+ }
+ }
+ }
+
+ private boolean verifyLocalsEqual(
+ ImmutableMap<Integer, DebugLocalInfo> a, Map<Integer, DebugLocalInfo> b) {
+ int size = 0;
+ for (Map.Entry<Integer, DebugLocalInfo> entry : b.entrySet()) {
+ if (entry.getValue() != null) {
+ assert a.get(entry.getKey()) == entry.getValue();
+ ++size;
+ }
+ }
+ return a.size() == size;
+ }
+
+ private void clearState() {
+ liveAtEntrySets = null;
+ liveIntervals = null;
+ active = null;
+ inactive = null;
+ unhandled = null;
+ freeRegisters = null;
+ }
+
+ // Compute a table that for each register numbers contains the number of previous register
+ // numbers that were unused. This table is then used to slide down the actual registers
+ // used to fill the gaps.
+ private void computeUnusedRegisters() {
+ if (registersUsed() == 0) {
+ return;
+ }
+ // Compute the set of registers that is used based on all live intervals.
+ Set<Integer> usedRegisters = new HashSet<>();
+ for (LiveIntervals intervals : liveIntervals) {
+ addRegisterIfUsed(usedRegisters, intervals);
+ for (LiveIntervals childIntervals : intervals.getSplitChildren()) {
+ addRegisterIfUsed(usedRegisters, childIntervals);
+ }
+ }
+ // Additionally, we have used temporary registers for parallel move scheduling, those
+ // are used as well.
+ for (int i = firstParallelMoveTemporary; i < maxRegisterNumber + 1; i++) {
+ usedRegisters.add(realRegisterNumberFromAllocated(i));
+ }
+ // Compute the table based on the set of used registers.
+ int unused = 0;
+ int[] computed = new int[registersUsed()];
+ for (int i = 0; i < registersUsed(); i++) {
+ if (!usedRegisters.contains(i)) {
+ unused++;
+ }
+ computed[i] = unused;
+ }
+ unusedRegisters = computed;
+ }
+
+ private void addRegisterIfUsed(Set<Integer> used, LiveIntervals intervals) {
+ boolean unused = intervals.isSpilledAndRematerializable(this);
+ if (!unused) {
+ used.add(realRegisterNumberFromAllocated(intervals.getRegister()));
+ if (intervals.getType() == MoveType.WIDE) {
+ used.add(realRegisterNumberFromAllocated(intervals.getRegister() + 1));
+ }
+ }
+ }
+
+ @Override
+ public boolean argumentValueUsesHighRegister(Value value, int instructionNumber) {
+ return isHighRegister(
+ getRegisterForValue(value, instructionNumber) + value.requiredRegisters() - 1);
+ }
+
+ @Override
+ public int registersUsed() {
+ int numberOfRegister = maxRegisterNumber + 1 - NUMBER_OF_SENTINEL_REGISTERS;
+ if (unusedRegisters != null) {
+ return numberOfRegister - unusedRegisters[unusedRegisters.length - 1];
+ }
+ return numberOfRegister;
+ }
+
+ @Override
+ public int getRegisterForValue(Value value, int instructionNumber) {
+ if (value.isFixedRegisterValue()) {
+ return realRegisterNumberFromAllocated(value.asFixedRegisterValue().getRegister());
+ }
+ LiveIntervals intervals = value.getLiveIntervals();
+ if (intervals.hasSplits()) {
+ intervals = intervals.getSplitCovering(instructionNumber);
+ }
+ return getRegisterForIntervals(intervals);
+ }
+
+ private BasicBlock[] computeLivenessInformation() {
+ BasicBlock[] blocks = code.numberInstructions();
+ computeLiveAtEntrySets();
+ computeLiveRanges();
+ return blocks;
+ }
+
+ private boolean performAllocationWithoutMoveInsertion(ArgumentReuseMode mode) {
+ pinArgumentRegisters();
+ return performLinearScan(mode);
+ }
+
+ private boolean performAllocation(ArgumentReuseMode mode) {
+ boolean result = performAllocationWithoutMoveInsertion(mode);
+ insertMoves();
+ return result;
+ }
+
+ private void removeSpillAndPhiMoves() {
+ for (BasicBlock block : code.blocks) {
+ InstructionListIterator it = block.listIterator();
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ Value outValue = instruction.outValue();
+ if (outValue != null && outValue.isFixedRegisterValue()) {
+ // Only move and const number instructions are inserted for spill and phi moves. The
+ // const number instructions are for values that can be rematerialized instead of
+ // spilled.
+ assert instruction.isMove() || instruction.isConstNumber();
+ assert !instruction.isDebugInstruction();
+ it.remove();
+ }
+ }
+ }
+ }
+
+ private void clearRegisterAssignments() {
+ freeRegisters.clear();
+ maxRegisterNumber = 0;
+ nextUnusedRegisterNumber = 0;
+ active.clear();
+ inactive.clear();
+ unhandled.clear();
+ for (LiveIntervals intervals : liveIntervals) {
+ intervals.clearRegisterAssignment();
+ }
+ }
+
+ /**
+ * Get the register allocated to a given set of live intervals.
+ */
+ private int getRegisterForIntervals(LiveIntervals intervals) {
+ int intervalsRegister = intervals.getRegister();
+ return realRegisterNumberFromAllocated(intervalsRegister);
+ }
+
+ int realRegisterNumberFromAllocated(int allocated) {
+ assert allocated != NO_REGISTER;
+ assert allocated >= 0;
+ int register;
+ if (allocated < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS) {
+ // For the |numberOfArguments| first registers map to the correct argument register.
+ // Due to the sentinel register in front of the arguments, the register number returned is
+ // the argument number starting from one.
+ register = maxRegisterNumber - (numberOfArgumentRegisters - allocated)
+ - NUMBER_OF_SENTINEL_REGISTERS;
+ } else {
+ // For everything else use the lower numbers.
+ register = allocated - numberOfArgumentRegisters - NUMBER_OF_SENTINEL_REGISTERS;
+ }
+ // Adjust for spill registers that turn out to be unused because the value can be
+ // rematerialized instead of spilled.
+ if (unusedRegisters != null) {
+ return register - unusedRegisters[register];
+ }
+ return register;
+ }
+
+ private boolean isHighRegister(int register) {
+ return register > Constants.U4BIT_MAX;
+ }
+
+ private boolean performLinearScan(ArgumentReuseMode mode) {
+ unhandled.addAll(liveIntervals);
+ LiveIntervals argumentInterval = preArgumentSentinelValue.getLiveIntervals();
+ while (argumentInterval != null) {
+ assert argumentInterval.getRegister() != NO_REGISTER;
+ unhandled.remove(argumentInterval);
+ if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
+ // All the argument intervals are active in the beginning and have preallocated registers.
+ active.add(argumentInterval);
+ } else {
+ // Treat the argument interval as spilled which will require a load to a different
+ // register for all usages.
+ if (argumentInterval.getUses().size() > 1) {
+ LiveIntervalsUse use = argumentInterval.firstUseWithConstraint();
+ if (use != null) {
+ LiveIntervals split = argumentInterval.splitBefore(use.getPosition());
+ unhandled.add(split);
+ }
+ }
+ }
+ argumentInterval = argumentInterval.getNextConsecutive();
+ }
+
+ // We have to be careful when it comes to the register allocated for a move exception
+ // instruction. For move exception instructions there is no place to put spill or
+ // restore moves. The move exception instruction has to be the first instruction in a
+ // catch handler.
+ //
+ // When we allow argument reuse we do not allow any splitting, therefore we cannot get into
+ // trouble with move exception registers. When argument reuse is disallowed we block a fixed
+ // register to be used only by move exception instructions.
+ if (mode != ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
+ // Force all move exception ranges to start out with the exception in a fixed register. Split
+ // their live ranges which will force another register if used.
+ int moveExceptionRegister = NO_REGISTER;
+ List<LiveIntervals> moveExceptionIntervals = new ArrayList<>();
+ boolean overlappingMoveExceptionIntervals = false;
+ for (BasicBlock block : code.blocks) {
+ for (Instruction instruction : block.getInstructions()) {
+ if (instruction.isMoveException()) {
+ hasDedicatedMoveExceptionRegister = true;
+ Value exceptionValue = instruction.outValue();
+ LiveIntervals intervals = exceptionValue.getLiveIntervals();
+ unhandled.remove(intervals);
+ if (moveExceptionRegister == NO_REGISTER) {
+ moveExceptionRegister = getFreeConsecutiveRegisters(1);
+ }
+ intervals.setRegister(moveExceptionRegister);
+ if (!overlappingMoveExceptionIntervals) {
+ for (LiveIntervals other : moveExceptionIntervals) {
+ overlappingMoveExceptionIntervals |= other.overlaps(intervals);
+ }
+ }
+ moveExceptionIntervals.add(intervals);
+ }
+ }
+ }
+ if (overlappingMoveExceptionIntervals) {
+ for (LiveIntervals intervals : moveExceptionIntervals) {
+ if (intervals.getUses().size() > 1) {
+ LiveIntervalsUse firstUse = intervals.getUses().pollFirst();
+ LiveIntervalsUse secondUse = intervals.getUses().pollFirst();
+ intervals.getUses().add(firstUse);
+ intervals.getUses().add(secondUse);
+ LiveIntervals split = intervals.splitBefore(secondUse.getPosition());
+ unhandled.add(split);
+ }
+ }
+ }
+ }
+
+ // Go through each unhandled live interval and find a register for it.
+ while (!unhandled.isEmpty()) {
+ LiveIntervals unhandledInterval = unhandled.poll();
+ // If this interval value is the src of an argument move. Fix the registers for the
+ // consecutive arguments now and add hints to the move sources. This looks forward
+ // and propagate hints backwards to avoid many moves in connection with ranged invokes.
+ allocateArgumentIntervalsWithSrc(unhandledInterval);
+
+ int start = unhandledInterval.getStart();
+ // Check for active intervals that expired or became inactive.
+ Iterator<LiveIntervals> activeIterator = active.iterator();
+ while (activeIterator.hasNext()) {
+ LiveIntervals activeIntervals = activeIterator.next();
+ if (start >= activeIntervals.getEnd()) {
+ activeIterator.remove();
+ freeRegistersForIntervals(activeIntervals);
+ } else if (!activeIntervals.overlapsPosition(start)) {
+ activeIterator.remove();
+ assert activeIntervals.getRegister() != NO_REGISTER;
+ inactive.add(activeIntervals);
+ freeRegistersForIntervals(activeIntervals);
+ }
+ }
+
+ // Check for inactive intervals that expired or became reactivated.
+ Iterator<LiveIntervals> inactiveIterator = inactive.iterator();
+ while (inactiveIterator.hasNext()) {
+ LiveIntervals inactiveIntervals = inactiveIterator.next();
+ if (start >= inactiveIntervals.getEnd()) {
+ inactiveIterator.remove();
+ } else if (inactiveIntervals.overlapsPosition(start)) {
+ inactiveIterator.remove();
+ assert inactiveIntervals.getRegister() != NO_REGISTER;
+ active.add(inactiveIntervals);
+ takeRegistersForIntervals(inactiveIntervals);
+ }
+ }
+
+ // Perform the actual allocation.
+ if (unhandledInterval.isLinked() && !unhandledInterval.isArgumentInterval()) {
+ allocateLinkedIntervals(unhandledInterval);
+ } else {
+ if (!allocateSingleInterval(unhandledInterval, mode)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Perform look-ahead and allocate registers for linked argument chains that have the argument
+ * interval as an argument move source.
+ *
+ * <p>The end result of calling this method is that the argument intervals have registers
+ * allocated and have been moved from unhandled to inactive. The move sources have their hints
+ * updated. The rest of the register allocation state is unchanged.
+ */
+ private void allocateArgumentIntervalsWithSrc(LiveIntervals srcInterval) {
+ Value value = srcInterval.getValue();
+ for (Instruction instruction : value.uniqueUsers()) {
+ // If there is a move user that is an argument move, we allocate the consecutive
+ // registers for the argument intervals and propagate the selected registers back as
+ // hints to the sources.
+ if (instruction.isMove() && instruction.asMove().dest().isLinked()) {
+ Move move = instruction.asMove();
+ Value dest = move.dest();
+ LiveIntervals destIntervals = dest.getLiveIntervals();
+ if (destIntervals.getRegister() == NO_REGISTER) {
+ // Save the current register allocation state so we can restore it at the end.
+ TreeSet<Integer> savedFreeRegisters = new TreeSet<>(freeRegisters);
+ int savedUnusedRegisterNumber = nextUnusedRegisterNumber;
+ List<LiveIntervals> savedActive = new LinkedList<>(active);
+ List<LiveIntervals> savedInactive = new LinkedList<>(inactive);
+
+ // Add all the active intervals to the inactive set. When allocating linked intervals we
+ // check all inactive intervals and exclude the registers for overlapping inactive
+ // intervals.
+ for (LiveIntervals active : active) {
+ // TODO(ager): We could allow the use of all the currently active registers for the
+ // ranged invoke (by adding the registers for all the active intervals to freeRegisters
+ // here). That could lead to lower register pressure. However, it would also often mean
+ // that we cannot allocate the right argument register to the current unhandled
+ // interval. Size measurements on GMSCore indicate that blocking the current active
+ // registers works the best for code size.
+ if (active.isArgumentInterval()) {
+ // Allow the ranged invoke to use argument registers if free. This improves register
+ // allocation for brigde methods that forwards all of their arguments after check-cast
+ // checks on their types.
+ freeRegistersForIntervals(active);
+ }
+ inactive.add(active);
+ }
+
+ // Allocate the argument intervals.
+ unhandled.remove(destIntervals);
+ allocateLinkedIntervals(destIntervals);
+ // Restore the register allocation state.
+ freeRegisters = savedFreeRegisters;
+ for (int i = savedUnusedRegisterNumber; i < nextUnusedRegisterNumber; i++) {
+ freeRegisters.add(i);
+ }
+ active = savedActive;
+ inactive = savedInactive;
+ // Move all the argument intervals to the inactive set.
+ LiveIntervals current = destIntervals.getStartOfConsecutive();
+ while (current != null) {
+ assert !inactive.contains(current);
+ assert !active.contains(current);
+ assert !unhandled.contains(current);
+ inactive.add(current);
+ current = current.getNextConsecutive();
+ }
+ }
+ }
+ }
+ }
+
+ private void allocateLinkedIntervals(LiveIntervals unhandledInterval) {
+ // Exclude the registers that overlap the start of one of the live ranges we are
+ // going to assign registers to now.
+ LiveIntervals current = unhandledInterval.getStartOfConsecutive();
+ Set<Integer> excludedRegisters = new HashSet<>();
+ while (current != null) {
+ for (LiveIntervals inactiveIntervals : inactive) {
+ if (inactiveIntervals.overlaps(current)) {
+ excludeRegistersForInterval(inactiveIntervals, excludedRegisters);
+ }
+ }
+ current = current.getNextConsecutive();
+ }
+ // Select registers.
+ current = unhandledInterval.getStartOfConsecutive();
+ int numberOfRegister = current.numberOfConsecutiveRegisters();
+ int firstRegister = getFreeConsecutiveRegisters(numberOfRegister);
+ for (int i = 0; i < numberOfRegister; i++) {
+ assert current != null;
+ current.setRegister(firstRegister + i);
+ if (current.getType() == MoveType.WIDE) {
+ assert i < numberOfRegister;
+ i++;
+ }
+ // Propagate hints to the move sources.
+ Value value = current.getValue();
+ if (!value.isPhi() && value.definition.isMove()) {
+ Move move = value.definition.asMove();
+ LiveIntervals intervals = move.src().getLiveIntervals();
+ intervals.setHint(current);
+ }
+ if (current != unhandledInterval) {
+ // Only the start of unhandledInterval has been reached at this point. All other live
+ // intervals in the chain have been assigned registers but their start has not yet been
+ // reached. Therefore, they belong in the inactive set.
+ unhandled.remove(current);
+ assert current.getRegister() != NO_REGISTER;
+ inactive.add(current);
+ freeRegistersForIntervals(current);
+ }
+ current = current.getNextConsecutive();
+ }
+ assert current == null;
+ assert unhandledInterval.getRegister() != NO_REGISTER;
+ active.add(unhandledInterval);
+ // Include the registers for inactive ranges that we had to exclude for this allocation.
+ freeRegisters.addAll(excludedRegisters);
+ }
+
+ // Update the information about used registers when |register| has been selected for use.
+ private void updateRegisterState(int register, boolean needsRegisterPair) {
+ int maxRegister = register + (needsRegisterPair ? 1 : 0);
+ if (maxRegister >= nextUnusedRegisterNumber) {
+ nextUnusedRegisterNumber = maxRegister + 1;
+ }
+ maxRegisterNumber = Math.max(maxRegisterNumber, maxRegister);
+ }
+
+ // Select a spill register.
+ // TODO(ager): At this point this always takes the next unused register number. We need a
+ // more intelligent selection of spill registers.
+ private int getSpillRegister(LiveIntervals intervals) {
+ int registerNumber = nextUnusedRegisterNumber++;
+ maxRegisterNumber = registerNumber;
+ if (intervals.getType() == MoveType.WIDE) {
+ nextUnusedRegisterNumber++;
+ maxRegisterNumber++;
+ }
+ return registerNumber;
+ }
+
+ private int toInstructionPosition(int position) {
+ return position % 2 == 0 ? position : position + 1;
+ }
+
+ private int toGapPosition(int position) {
+ return position % 2 == 1 ? position : position - 1;
+ }
+
+ // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+ // the first part of the result long before reading the second part of the input longs.
+ //
+ // Therefore, on dalvik, we cannot generate code with overlapping long registers such as:
+ //
+ // add-long v3, v0, v2
+ //
+ // Dalvik would add v0 and v2 and write that to v3. It would then read v1 and v3 and produce
+ // the wrong result.
+ private boolean needsOverlappingLongRegisterWorkaround(LiveIntervals intervals) {
+ if (intervals.requiredRegisters() == 1) {
+ // Not the live range for a wide value.
+ return false;
+ }
+ if (intervals.getValue().isPhi()) {
+ // If this writes a new register pair it will be via a move and not a long operation.
+ return false;
+ }
+ if (intervals.getSplitParent() != intervals) {
+ // This is a split of the parent interval and therefore if this leads to a write of a
+ // register pair it will be via a move and not a long operation.
+ return false;
+ }
+ Instruction definition = intervals.getValue().definition;
+ if (definition.isArithmeticBinop() &&
+ definition.asArithmeticBinop().getNumericType() == NumericType.LONG) {
+ return definition instanceof Add || definition instanceof Sub;
+ }
+ if (definition.isLogicalBinop() &&
+ definition.asLogicalBinop().getNumericType() == NumericType.LONG) {
+ return definition instanceof Or || definition instanceof Xor || definition instanceof And;
+ }
+ return false;
+ }
+
+ private boolean hasOverlappingLongRegisters(LiveIntervals unhandledInterval, int register) {
+ assert needsOverlappingLongRegisterWorkaround(unhandledInterval);
+ Value left = unhandledInterval.getValue().definition.asBinop().leftValue();
+ Value right = unhandledInterval.getValue().definition.asBinop().rightValue();
+ int leftReg =
+ left.getLiveIntervals().getSplitCovering(unhandledInterval.getStart()).getRegister();
+ int rightReg =
+ right.getLiveIntervals().getSplitCovering(unhandledInterval.getStart()).getRegister();
+ assert leftReg != NO_REGISTER && rightReg != NO_REGISTER;
+ // The dalvik bug is actually only for overlap with the second operand, For now we
+ // make sure that there is no overlap with either operand.
+ if ((leftReg + 1) == register|| (rightReg + 1) == register) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean allocateSingleInterval(LiveIntervals unhandledInterval, ArgumentReuseMode mode) {
+ int registerConstraint = unhandledInterval.getRegisterLimit();
+ assert registerConstraint <= Constants.U16BIT_MAX;
+ if (registerConstraint < Constants.U16BIT_MAX) {
+ // We always have argument sentinels that will not actually occupy registers. Therefore, we
+ // allow the use of NUMBER_OF_SENTINEL_REGISTERS more than the limit.
+ registerConstraint += NUMBER_OF_SENTINEL_REGISTERS;
+ if (mode == ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE) {
+ // We know that none of the argument registers will be reused. Therefore, we allow the
+ // use of number of arguments more registers. (We will never use number of arguments +
+ // number of sentinel registers of them.)
+ registerConstraint += numberOfArgumentRegisters;
+ }
+ }
+
+ // Set all free positions for possible registers to max integer.
+ RegisterPositions freePositions = new RegisterPositions(registerConstraint + 1);
+
+ if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
+ // The sentinel registers cannot be used and we block them.
+ freePositions.set(0, 0);
+ int lastSentinelRegister = numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS - 1;
+ if (lastSentinelRegister <= registerConstraint) {
+ freePositions.set(lastSentinelRegister, 0);
+ }
+ } else {
+ // Argument reuse is not allowed and we block all the argument registers so that
+ // arguments are never free.
+ for (int i = 0; i < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS; i++) {
+ if (i <= registerConstraint) {
+ freePositions.set(i, 0);
+ }
+ }
+ }
+
+ // If there is a move exception instruction we block register 0 as the move exception
+ // register. If we cannot find a free valid register for the move exception value we have no
+ // place to put a spill move (because the move exception instruction has to be the
+ // first instruction in the handler block).
+ if (hasDedicatedMoveExceptionRegister) {
+ int moveExceptionRegister = numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS;
+ if (moveExceptionRegister <= registerConstraint) {
+ freePositions.set(moveExceptionRegister, 0);
+ }
+ }
+
+ // All the active intervals are not free at this point.
+ for (LiveIntervals intervals : active) {
+ int activeRegister = intervals.getRegister();
+ if (activeRegister <= registerConstraint) {
+ for (int i = 0; i < intervals.requiredRegisters(); i++) {
+ if (activeRegister + i <= registerConstraint) {
+ freePositions.set(activeRegister + i, 0);
+ }
+ }
+ }
+ }
+
+ // The register for inactive intervals that overlap with this interval are free until
+ // the next overlap.
+ for (LiveIntervals intervals : inactive) {
+ int inactiveRegister = intervals.getRegister();
+ if (inactiveRegister <= registerConstraint && unhandledInterval.overlaps(intervals)) {
+ int nextOverlap = unhandledInterval.nextOverlap(intervals);
+ for (int i = 0; i < intervals.requiredRegisters(); i++) {
+ if (inactiveRegister + i <= registerConstraint) {
+ int unhandledStart = toInstructionPosition(unhandledInterval.getStart());
+ if (nextOverlap == unhandledStart) {
+ // Don't use the register for an inactive interval that is only free until the next
+ // instruction. We can get into this situation when unhandledInterval starts at a
+ // gap position.
+ freePositions.set(inactiveRegister + i, 0);
+ } else {
+ freePositions.set(
+ inactiveRegister + i,
+ Math.min(freePositions.get(inactiveRegister + i), nextOverlap));
+ }
+ }
+ }
+ }
+ }
+
+ assert unhandledInterval.requiredRegisters() <= 2;
+ boolean needsRegisterPair = unhandledInterval.requiredRegisters() == 2;
+ // Attempt to use register hints.
+ if (useRegisterHint(unhandledInterval, registerConstraint, freePositions, needsRegisterPair)) {
+ return true;
+ }
+
+ // Get the register (pair) that is free the longest. That is the register with the largest
+ // free position.
+ int candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair);
+ if (needsOverlappingLongRegisterWorkaround(unhandledInterval)) {
+ while (hasOverlappingLongRegisters(unhandledInterval, candidate)) {
+ // Make the overlapping register unavailable for allocation and try again.
+ freePositions.set(candidate, 0);
+ candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair);
+ }
+ }
+ int largestFreePosition = freePositions.get(candidate);
+ if (needsRegisterPair) {
+ largestFreePosition = Math.min(largestFreePosition, freePositions.get(candidate + 1));
+ }
+
+ // Determine what to do based on how long the selected candidate is free.
+ if (largestFreePosition == 0) {
+ // Not free. We need to spill.
+ if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
+ // No spilling is allowed when we allow argument reuse. Bailout and start over with
+ // argument reuse disallowed.
+ return false;
+ }
+ allocateBlockedRegister(unhandledInterval);
+ } else if (largestFreePosition >= unhandledInterval.getEnd()) {
+ // Free for the entire interval. Allocate the register.
+ assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, candidate);
+ } else {
+ if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
+ // No splitting is allowed when we allow argument reuse. Bailout and start over with
+ // argument reuse disallowed.
+ return false;
+ }
+ // The candidate is free for the beginning of an interval. We split the interval
+ // and use the register for as long as we can.
+ int splitPosition = largestFreePosition;
+ LiveIntervals split = unhandledInterval.splitBefore(splitPosition);
+ assert split != unhandledInterval;
+ assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, candidate);
+ unhandled.add(split);
+ }
+ return true;
+ }
+
+ // Attempt to use the register hint for the unhandled interval in order to avoid generating
+ // moves.
+ private boolean useRegisterHint(LiveIntervals unhandledInterval, int registerConstraint,
+ RegisterPositions freePositions, boolean needsRegisterPair) {
+ // If the unhandled interval has a hint we give it that register if it is available without
+ // spilling. For phis we also use the hint before looking at the operand registers. The
+ // phi could have a hint from an argument moves which it seems more important to honor in
+ // practice.
+ LiveIntervals hint = unhandledInterval.getHint();
+ if (hint != null) {
+ int register = hint.getRegister();
+ if (tryHint(unhandledInterval, registerConstraint, freePositions, needsRegisterPair,
+ register)) {
+ return true;
+ }
+ }
+
+ // If there is no hint or it cannot be applied we search for a good register for phis using
+ // the registers assigned to the operand intervals. We determine all the registers used
+ // for operands and try them one by one based on frequency.
+ Value value = unhandledInterval.getValue();
+ if (value.isPhi()) {
+ Phi phi = value.asPhi();
+ Multiset<Integer> map = HashMultiset.create();
+ List<Value> operands = phi.getOperands();
+ for (int i = 0; i < operands.size(); i++) {
+ LiveIntervals intervals = operands.get(i).getLiveIntervals();
+ if (intervals.hasSplits()) {
+ BasicBlock pred = phi.getBlock().getPredecessors().get(i);
+ intervals = intervals.getSplitCovering(pred.exit().getNumber());
+ }
+ int operandRegister = intervals.getRegister();
+ if (operandRegister != NO_REGISTER) {
+ map.add(operandRegister);
+ }
+ }
+ for (Entry<Integer> entry : Multisets.copyHighestCountFirst(map).entrySet()) {
+ int register = entry.getElement();
+ if (tryHint(unhandledInterval, registerConstraint, freePositions, needsRegisterPair,
+ register)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Attempt to allocate the hint register to the unhandled intervals.
+ private boolean tryHint(LiveIntervals unhandledInterval, int registerConstraint,
+ RegisterPositions freePositions, boolean needsRegisterPair, int register) {
+ if (register + (needsRegisterPair ? 1 : 0) <= registerConstraint) {
+ int freePosition = freePositions.get(register);
+ if (needsRegisterPair) {
+ freePosition = Math.min(freePosition, freePositions.get(register + 1));
+ }
+ if (freePosition >= unhandledInterval.getEnd()) {
+ // Check for overlapping long registers issue.
+ if (needsOverlappingLongRegisterWorkaround(unhandledInterval) &&
+ hasOverlappingLongRegisters(unhandledInterval, register)) {
+ return false;
+ }
+ assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, register);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void assignRegister(LiveIntervals intervals, int register) {
+ intervals.setRegister(register);
+ updateRegisterHints(intervals);
+ }
+
+ private void updateRegisterHints(LiveIntervals intervals) {
+ Value value = intervals.getValue();
+ // If the value flows into a phi, set the hint for all the operand splits that flow into the
+ // phi and do not have hints yet.
+ for (Phi phi : value.uniquePhiUsers()) {
+ LiveIntervals phiIntervals = phi.getLiveIntervals();
+ if (phiIntervals.getHint() == null) {
+ for (int i = 0; i < phi.getOperands().size(); i++) {
+ Value operand = phi.getOperand(i);
+ LiveIntervals operandIntervals = operand.getLiveIntervals();
+ BasicBlock pred = phi.getBlock().getPredecessors().get(i);
+ operandIntervals = operandIntervals.getSplitCovering(pred.exit().getNumber());
+ if (operandIntervals.getHint() == null) {
+ operandIntervals.setHint(intervals);
+ }
+ }
+ }
+ }
+ // If the value is a phi and we are at the start of the interval, we set the register as
+ // the hint for all of the operand splits flowing into the phi. We set the hint no matter
+ // if there is already a hint. We know the register for the phi and want as many operands
+ // as possible to be allocated the same register to avoid phi moves.
+ if (value.isPhi() && intervals.getSplitParent() == intervals) {
+ Phi phi = value.asPhi();
+ BasicBlock block = phi.getBlock();
+ for (int i = 0; i < phi.getOperands().size(); i++) {
+ Value operand = phi.getOperand(i);
+ BasicBlock pred = block.getPredecessors().get(i);
+ LiveIntervals operandIntervals =
+ operand.getLiveIntervals().getSplitCovering(pred.exit().getNumber());
+ operandIntervals.setHint(intervals);
+ }
+ }
+ }
+
+ private void assignRegisterToUnhandledInterval(
+ LiveIntervals unhandledInterval, boolean needsRegisterPair, int register) {
+ assignRegister(unhandledInterval, register);
+ takeRegistersForIntervals(unhandledInterval);
+ updateRegisterState(register, needsRegisterPair);
+ active.add(unhandledInterval);
+ }
+
+ private int getLargestCandidate(
+ int registerConstraint, RegisterPositions freePositions, boolean needsRegisterPair) {
+ int candidate = 0;
+ int largest = freePositions.get(0);
+ if (needsRegisterPair) {
+ largest = Math.min(largest, freePositions.get(1));
+ }
+ for (int i = 1; i <= registerConstraint; i++) {
+ int freePosition = freePositions.get(i);
+ if (needsRegisterPair) {
+ if (i >= registerConstraint) {
+ break;
+ }
+ freePosition = Math.min(freePosition, freePositions.get(i + 1));
+ }
+ if (freePosition > largest) {
+ candidate = i;
+ largest = freePosition;
+ if (largest == Integer.MAX_VALUE) {
+ break;
+ }
+ }
+ }
+ return candidate;
+ }
+
+ private void allocateBlockedRegister(LiveIntervals unhandledInterval) {
+ int registerConstraint = unhandledInterval.getRegisterLimit();
+ if (registerConstraint < Constants.U16BIT_MAX) {
+ // We always have argument sentinels that will not actually occupy registers. Therefore, we
+ // allow the use of NUMBER_OF_SENTINEL_REGISTERS more than the limit. Additionally, we never
+ // reuse argument registers and therefore allow the use of numberOfArgumentRegisters as well.
+ registerConstraint += numberOfArgumentRegisters;
+ registerConstraint += NUMBER_OF_SENTINEL_REGISTERS;
+ }
+
+ // Initialize all candidate registers to Integer.MAX_VALUE.
+ RegisterPositions usePositions = new RegisterPositions(registerConstraint + 1);
+ RegisterPositions blockedPositions = new RegisterPositions(registerConstraint + 1);
+
+ // Compute next use location for all currently active registers.
+ for (LiveIntervals intervals : active) {
+ int activeRegister = intervals.getRegister();
+ if (activeRegister <= registerConstraint) {
+ for (int i = 0; i < intervals.requiredRegisters(); i++) {
+ if (activeRegister + i <= registerConstraint) {
+ int unhandledStart = unhandledInterval.getStart();
+ usePositions.set(activeRegister + i, intervals.firstUseAfter(unhandledStart));
+ }
+ }
+ }
+ }
+
+ // Compute next use location for all inactive registers that overlaps the unhandled interval.
+ for (LiveIntervals intervals : inactive) {
+ int inactiveRegister = intervals.getRegister();
+ if (inactiveRegister <= registerConstraint && intervals.overlaps(unhandledInterval)) {
+ for (int i = 0; i < intervals.requiredRegisters(); i++) {
+ if (inactiveRegister + i <= registerConstraint) {
+ int unhandledStart = unhandledInterval.getStart();
+ usePositions.set(inactiveRegister + i, Math.min(
+ usePositions.get(inactiveRegister + i), intervals.firstUseAfter(unhandledStart)));
+ }
+ }
+ }
+ }
+
+ // Disallow the reuse of argument registers by always treating them as being used
+ // at instruction number 0.
+ for (int i = 0; i < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS; i++) {
+ usePositions.set(i, 0);
+ }
+
+ // Disallow reuse of the move exception register if we have reserved one.
+ if (hasDedicatedMoveExceptionRegister) {
+ usePositions.set(numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS, 0);
+ }
+
+ // Treat active linked argument intervals as pinned. They cannot be given another register
+ // at their uses.
+ blockLinkedRegisters(
+ active, unhandledInterval, registerConstraint, usePositions, blockedPositions);
+
+ // Treat inactive linked argument intervals as pinned. They cannot be given another register
+ // at their uses.
+ blockLinkedRegisters(
+ inactive, unhandledInterval, registerConstraint, usePositions, blockedPositions);
+
+ // Get the register (pair) that has the highest use position.
+ assert unhandledInterval.requiredRegisters() <= 2;
+ boolean needsRegisterPair = unhandledInterval.requiredRegisters() == 2;
+ int candidate = getLargestCandidate(registerConstraint, usePositions, needsRegisterPair);
+ int largestUsePosition = usePositions.get(candidate);
+ int blockedPosition = blockedPositions.get(candidate);
+ if (needsRegisterPair) {
+ blockedPosition = Math.min(blockedPosition, blockedPositions.get(candidate + 1));
+ largestUsePosition = Math.min(largestUsePosition, usePositions.get(candidate + 1));
+ }
+
+ // TODO(ager): When selecting a spill candidate also take rematerialization into account.
+ // Prefer spilling a constant that can be rematerialized instead of spilling something that
+ // cannot be rematerialized.
+
+ if (largestUsePosition < unhandledInterval.getFirstUse()) {
+ // All active and inactive intervals are used before current. Therefore, it is best to spill
+ // current itself.
+ int splitPosition = unhandledInterval.getFirstUse();
+ LiveIntervals split = unhandledInterval.splitBefore(splitPosition);
+ assert split != unhandledInterval;
+ int registerNumber = getSpillRegister(unhandledInterval);
+ assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, registerNumber);
+ unhandledInterval.setSpilled(true);
+ unhandled.add(split);
+ } else if (blockedPosition > unhandledInterval.getEnd()) {
+ // Spilling can make a register available for the entire interval.
+ // It would have been nice to use assignRegisterToUnhandledInterval here, but unfortunately
+ // the order of the operations are extremely important here. updateRegisterState has to
+ // happen before spillOverlappingActiveIntervals and takeRegistersForIntervals has to happen
+ // after.
+ assignRegisterAndSpill(unhandledInterval, needsRegisterPair, candidate);
+ } else {
+ // Spilling only makes a register available for the first part of current.
+ LiveIntervals splitChild = unhandledInterval.splitBefore(blockedPosition);
+ unhandled.add(splitChild);
+ assignRegisterAndSpill(unhandledInterval, needsRegisterPair, candidate);
+ }
+ }
+
+ private void assignRegisterAndSpill(
+ LiveIntervals unhandledInterval, boolean needsRegisterPair, int candidate) {
+ assignRegister(unhandledInterval, candidate);
+ updateRegisterState(candidate, needsRegisterPair);
+ // Split and spill intersecting active intervals for this register.
+ spillOverlappingActiveIntervals(unhandledInterval, needsRegisterPair, candidate);
+ takeRegistersForIntervals(unhandledInterval);
+ active.add(unhandledInterval);
+ // Split all overlapping inactive intervals for this register. They need to have a new
+ // register assigned at the next use.
+ splitOverlappingInactiveIntervals(unhandledInterval, needsRegisterPair, candidate);
+ }
+
+ private void splitOverlappingInactiveIntervals(LiveIntervals unhandledInterval,
+ boolean needsRegisterPair, int candidate) {
+ Iterator<LiveIntervals> inactiveIterator = inactive.iterator();
+ while (inactiveIterator.hasNext()) {
+ LiveIntervals intervals = inactiveIterator.next();
+ if ((intervals.usesRegister(candidate) ||
+ (needsRegisterPair && intervals.usesRegister(candidate + 1))) &&
+ intervals.overlaps(unhandledInterval)) {
+ if (intervals.getStart() > unhandledInterval.getStart()) {
+ // The inactive live intervals hasn't started yet. Clear the temporary register
+ // assignment and move back to unhandled for register reassignment.
+ intervals.clearRegisterAssignment();
+ inactiveIterator.remove();
+ unhandled.add(intervals);
+ } else {
+ // The inactive live intervals is in a live range hole. Split the interval and
+ // put the ranges after the hole into the unhandled set for register reassignment.
+ LiveIntervals split = intervals.splitBefore(unhandledInterval.getStart());
+ unhandled.add(split);
+ }
+ }
+ }
+ }
+
+ private void spillOverlappingActiveIntervals(LiveIntervals unhandledInterval,
+ boolean needsRegisterPair, int candidate) {
+ List<LiveIntervals> newActive = new ArrayList<>();
+ Iterator<LiveIntervals> activeIterator = active.iterator();
+ while (activeIterator.hasNext()) {
+ LiveIntervals intervals = activeIterator.next();
+ if (intervals.usesRegister(candidate) ||
+ (needsRegisterPair && intervals.usesRegister(candidate + 1))) {
+ activeIterator.remove();
+ freeRegistersForIntervals(intervals);
+ LiveIntervals splitChild = intervals.splitBefore(unhandledInterval.getStart());
+ int registerNumber = getSpillRegister(intervals);
+ assignRegister(splitChild, registerNumber);
+ splitChild.setSpilled(true);
+ takeRegistersForIntervals(splitChild);
+ assert splitChild.getRegister() != NO_REGISTER;
+ assert intervals.getRegister() != NO_REGISTER;
+ newActive.add(splitChild);
+ // If the constant is split before its first actual use, mark the constant as being
+ // spilled. That will allows us to remove it afterwards if it is rematerializable.
+ if (intervals.getValue().isConstant()
+ && intervals.getStart() == intervals.getValue().definition.getNumber()
+ && intervals.getUses().size() == 1) {
+ intervals.setSpilled(true);
+ }
+ if (splitChild.getUses().size() > 0) {
+ if (splitChild.isLinked() && !splitChild.isArgumentInterval()) {
+ // Spilling a value with a pinned register. We need to move back at the next use.
+ LiveIntervals splitOfSplit = splitChild.splitBefore(splitChild.getFirstUse());
+ splitOfSplit.setRegister(intervals.getRegister());
+ inactive.add(splitOfSplit);
+ } else if (intervals.getValue().isConstant()) {
+ splitRangesForSpilledConstant(splitChild, registerNumber);
+ } else {
+ splitRangesForSpilledInterval(splitChild, registerNumber);
+
+ }
+ }
+ }
+ }
+ active.addAll(newActive);
+ }
+
+ private void splitRangesForSpilledInterval(LiveIntervals splitChild, int registerNumber) {
+ // Spilling a non-pinned, non-rematerializable value. We use the value in the spill
+ // register for as long as possible to avoid further moves.
+ assert splitChild.isSpilled();
+ assert !splitChild.getValue().isConstant();
+ assert !splitChild.isLinked() || splitChild.isArgumentInterval();
+ LiveIntervalsUse firstUseWithLowerLimit = null;
+ boolean hasUsesBeforeFirstUseWithLowerLimit = false;
+ for (LiveIntervalsUse use : splitChild.getUses()) {
+ if (registerNumber > use.getLimit()) {
+ firstUseWithLowerLimit = use;
+ break;
+ } else {
+ hasUsesBeforeFirstUseWithLowerLimit = true;
+ }
+ }
+ if (hasUsesBeforeFirstUseWithLowerLimit) {
+ splitChild.setSpilled(false);
+ }
+ if (firstUseWithLowerLimit != null) {
+ LiveIntervals splitOfSplit = splitChild.splitBefore(firstUseWithLowerLimit.getPosition());
+ unhandled.add(splitOfSplit);
+ }
+ }
+
+ private void splitRangesForSpilledConstant(LiveIntervals spilled, int spillRegister) {
+ // When spilling a constant we should not keep it alive in the spill register, instead
+ // we should use rematerialization. We aggressively spill the constant in all gaps
+ // between uses that span more than a certain number of instructions. If we needed to
+ // spill we are running low on registers and this constant should get out of the way
+ // as much as possible.
+ assert spilled.isSpilled();
+ assert spilled.getValue().isConstant();
+ assert !spilled.isLinked() || spilled.isArgumentInterval();
+ int maxGapSize = 50 * INSTRUCTION_NUMBER_DELTA;
+ if (!spilled.getUses().isEmpty()) {
+ // Split at first use after the spill position and add to unhandled to get a register
+ // assigned for rematerialization.
+ LiveIntervals split = spilled.splitBefore(spilled.getFirstUse());
+ unhandled.add(split);
+ // Now repeatedly split for each use that is more than maxGapSize away from the previous use.
+ boolean changed = true;
+ while (changed) {
+ changed = false;
+ int previousUse = split.getStart();
+ for (LiveIntervalsUse use : split.getUses()) {
+ if (use.getPosition() - previousUse > maxGapSize) {
+ // Found a use that is more than gap size away from the previous use. Split after
+ // the previous use.
+ split = split.splitBefore(previousUse + INSTRUCTION_NUMBER_DELTA);
+ // If the next use is not at the start of the new split, we split again at the next use
+ // and spill the gap.
+ if (toGapPosition(use.getPosition()) > split.getStart()) {
+ assignRegister(split, spillRegister);
+ split.setSpilled(true);
+ inactive.add(split);
+ split = split.splitBefore(use.getPosition());
+ }
+ // |split| now starts at the next use - add it to unhandled to get a register
+ // assigned for rematerialization.
+ unhandled.add(split);
+ // Break out of the loop to start iterating the new split uses.
+ changed = true;
+ break;
+ }
+ previousUse = use.getPosition();
+ }
+ }
+ }
+ }
+
+ private void blockLinkedRegisters(
+ List<LiveIntervals> intervalsList, LiveIntervals interval, int registerConstraint,
+ RegisterPositions usePositions, RegisterPositions blockedPositions) {
+ for (LiveIntervals other : intervalsList) {
+ if (other.isLinked()) {
+ int register = other.getRegister();
+ if (register <= registerConstraint && other.overlaps(interval)) {
+ for (int i = 0; i < other.requiredRegisters(); i++) {
+ if (register + i <= registerConstraint) {
+ for (LiveIntervalsUse use : other.getUses()) {
+ if (use.getPosition() > interval.getStart()) {
+ blockedPositions.set(
+ register + i,
+ Math.min(blockedPositions.get(register + i), use.getPosition()));
+ usePositions.set(
+ register + i,
+ Math.min(usePositions.get(register + i), use.getPosition()));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void insertMoves() {
+ SpillMoveSet spillMoves =
+ new SpillMoveSet(this, code, numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS);
+ for (LiveIntervals intervals : liveIntervals) {
+ if (intervals.hasSplits()) {
+ LiveIntervals current = intervals;
+ PriorityQueue<LiveIntervals> sortedChildren =
+ new PriorityQueue<>((o1, o2) -> Integer.compare(o1.getStart(), o2.getStart()));
+ sortedChildren.addAll(current.getSplitChildren());
+ for (LiveIntervals split = sortedChildren.poll();
+ split != null;
+ split = sortedChildren.poll()) {
+ int position = split.getStart();
+ spillMoves.addSpillOrRestoreMove(toGapPosition(position), split, current);
+ current = split;
+ }
+ }
+ }
+
+ resolveControlFlow(spillMoves);
+ firstParallelMoveTemporary = maxRegisterNumber + 1;
+ maxRegisterNumber += spillMoves.scheduleAndInsertMoves(maxRegisterNumber + 1);
+ }
+
+ // Resolve control flow by inserting phi moves and by inserting moves when the live intervals
+ // change for a value across block boundaries.
+ private void resolveControlFlow(SpillMoveSet spillMoves) {
+ // For a control-flow graph like the following where a value v is split at an instruction in
+ // block C a spill move is inserted in block C to transfer the value from register r0 to
+ // register r1. However, that move is not executed when taking the control-flow edge from
+ // B to D and therefore resolution will insert a move from r0 to r1 on that edge.
+ //
+ // r0 r1
+ // v: |----------------|--------|
+ //
+ // A ----> B ----> C ----> D
+ // | ^
+ // +---------------+
+ for (BasicBlock block : code.blocks) {
+ for (BasicBlock successor : block.getSuccessors()) {
+ // If we are processing an exception edge, we need to use the throwing instruction
+ // as the instruction we are coming from.
+ int fromInstruction = block.exit().getNumber();
+ boolean isCatch = block.isCatchSuccessor(successor);
+ if (isCatch) {
+ for (Instruction instruction : block.getInstructions()) {
+ if (instruction.instructionTypeCanThrow()) {
+ fromInstruction = instruction.getNumber();
+ break;
+ }
+ }
+ }
+ int toInstruction = successor.entry().getNumber();
+
+ // Insert spill/restore moves when a value changes across a block boundary.
+ Set<Value> liveAtEntry = liveAtEntrySets.get(successor);
+ for (Value value : liveAtEntry) {
+ LiveIntervals parentInterval = value.getLiveIntervals();
+ LiveIntervals fromIntervals = parentInterval.getSplitCovering(fromInstruction);
+ LiveIntervals toIntervals = parentInterval.getSplitCovering(toInstruction);
+ if (fromIntervals != toIntervals) {
+ if (block.exit().isGoto() && !isCatch) {
+ spillMoves.addOutResolutionMove(fromInstruction - 1, toIntervals, fromIntervals);
+ } else if (successor.entry().isMoveException()) {
+ spillMoves.addInResolutionMove(toInstruction + 1, toIntervals, fromIntervals);
+ } else {
+ spillMoves.addInResolutionMove(toInstruction - 1, toIntervals, fromIntervals);
+ }
+ }
+ }
+
+ // Insert phi moves.
+ int predIndex = successor.getPredecessors().indexOf(block);
+ for (Phi phi : successor.getPhis()) {
+ LiveIntervals toIntervals = phi.getLiveIntervals().getSplitCovering(toInstruction);
+ Value operand = phi.getOperand(predIndex);
+ LiveIntervals fromIntervals = operand.getLiveIntervals();
+ if (operand.isPhi() && operand != phi && successor.getPhis().contains(operand)) {
+ // If the input to this phi is another phi in this block we want the value after
+ // merging which is the value for that phi at the from instruction.
+ fromIntervals = fromIntervals.getSplitCovering(fromInstruction);
+ } else {
+ fromIntervals = fromIntervals.getSplitCovering(fromInstruction);
+ }
+ if (fromIntervals != toIntervals && !toIntervals.isArgumentInterval()) {
+ assert block.getSuccessors().size() == 1;
+ spillMoves.addPhiMove(fromInstruction - 1, toIntervals, fromIntervals);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Compute the set of live values at the entry to each block using a backwards data-flow
+ * analysis.
+ */
+ private void computeLiveAtEntrySets() {
+ Queue<BasicBlock> worklist = new LinkedList<>();
+ // Since this is a backwards data-flow analysis we process the blocks in reverse
+ // topological order to reduce the number of iterations.
+ BasicBlock[] sorted = code.topologicallySortedBlocks();
+ for (int i = sorted.length - 1; i >= 0; i--) {
+ worklist.add(sorted[i]);
+ }
+ while (!worklist.isEmpty()) {
+ BasicBlock block = worklist.poll();
+ Set<Value> live = new HashSet<>();
+ for (BasicBlock succ : block.getSuccessors()) {
+ Set<Value> succLiveAtEntry = liveAtEntrySets.get(succ);
+ if (succLiveAtEntry != null) {
+ live.addAll(succLiveAtEntry);
+ }
+ int predIndex = succ.getPredecessors().indexOf(block);
+ for (Phi phi : succ.getPhis()) {
+ live.add(phi.getOperand(predIndex));
+ }
+ }
+ ListIterator<Instruction> iterator =
+ block.getInstructions().listIterator(block.getInstructions().size());
+ while (iterator.hasPrevious()) {
+ Instruction instruction = iterator.previous();
+ if (instruction.outValue() != null) {
+ live.remove(instruction.outValue());
+ }
+ for (Value use : instruction.inValues()) {
+ if (use.needsRegister()) {
+ live.add(use);
+ }
+ }
+ Value use = instruction.getPreviousLocalValue();
+ if (use != null) {
+ assert use.needsRegister();
+ live.add(use);
+ }
+ }
+ for (Phi phi : block.getPhis()) {
+ live.remove(phi);
+ }
+ Set<Value> previousLiveAtEntry = liveAtEntrySets.put(block, live);
+ // If the live at entry set changed for this block at the predecessors to the worklist if
+ // they are not already there.
+ if (previousLiveAtEntry == null || !previousLiveAtEntry.equals(live)) {
+ for (BasicBlock pred : block.getPredecessors()) {
+ if (!worklist.contains(pred)) {
+ worklist.add(pred);
+ }
+ }
+ }
+ }
+ assert liveAtEntrySets.get(sorted[0]).size() == 0;
+ }
+
+ private void addLiveRange(Value v, BasicBlock b, int end) {
+ int firstInstructionInBlock = b.entry().getNumber();
+ int instructionsSize = b.getInstructions().size() * INSTRUCTION_NUMBER_DELTA;
+ int lastInstructionInBlock =
+ firstInstructionInBlock + instructionsSize - INSTRUCTION_NUMBER_DELTA;
+ int instructionNumber;
+ if (v.isPhi()) {
+ instructionNumber = firstInstructionInBlock;
+ } else {
+ Instruction instruction = v.definition;
+ instructionNumber = instruction.getNumber();
+ }
+ if (v.getLiveIntervals() == null) {
+ Value current = v.getStartOfConsecutive();
+ LiveIntervals intervals = new LiveIntervals(current);
+ while (true) {
+ liveIntervals.add(intervals);
+ Value next = current.getNextConsecutive();
+ if (next == null) {
+ break;
+ }
+ LiveIntervals nextIntervals = new LiveIntervals(next);
+ intervals.link(nextIntervals);
+ current = next;
+ intervals = nextIntervals;
+ }
+ }
+ LiveIntervals intervals = v.getLiveIntervals();
+ if (firstInstructionInBlock <= instructionNumber &&
+ instructionNumber <= lastInstructionInBlock) {
+ if (v.isPhi()) {
+ // Phis need to interfere with spill restore moves inserted before the instruction because
+ // the phi value is defined on the inflowing edge.
+ instructionNumber--;
+ }
+ intervals.addRange(new LiveRange(instructionNumber, end));
+ if (!v.isPhi()) {
+ int constraint = v.definition.maxOutValueRegister();
+ intervals.addUse(new LiveIntervalsUse(instructionNumber, constraint));
+ }
+ } else {
+ intervals.addRange(new LiveRange(firstInstructionInBlock - 1, end));
+ }
+ }
+
+ /**
+ * Compute live ranges based on liveAtEntry sets for all basic blocks.
+ */
+ private void computeLiveRanges() {
+ for (BasicBlock block : code.topologicallySortedBlocks()) {
+ Set<Value> live = new HashSet<>();
+ List<BasicBlock> successors = block.getSuccessors();
+ Set<Value> phiOperands = new HashSet<>();
+ for (BasicBlock successor : successors) {
+ live.addAll(liveAtEntrySets.get(successor));
+ for (Phi phi : successor.getPhis()) {
+ live.remove(phi);
+ phiOperands.add(phi.getOperand(successor.getPredecessors().indexOf(block)));
+ }
+ }
+ live.addAll(phiOperands);
+ List<Instruction> instructions = block.getInstructions();
+ for (Value value : live) {
+ int end = block.entry().getNumber() + instructions.size() * INSTRUCTION_NUMBER_DELTA;
+ // Make sure that phi operands do not overlap the phi live range. The phi operand is
+ // not live until the next instruction, but only until the gap before the next instruction
+ // where the phi value takes over.
+ if (phiOperands.contains(value)) {
+ end--;
+ }
+ addLiveRange(value, block, end);
+ }
+ ListIterator<Instruction> iterator =
+ block.getInstructions().listIterator(block.getInstructions().size());
+ while (iterator.hasPrevious()) {
+ Instruction instruction = iterator.previous();
+ Value definition = instruction.outValue();
+ if (definition != null) {
+ // For instructions that define values which have no use create a live range covering
+ // the instruction. This will typically be instructions that can have side effects even
+ // if their output is not used.
+ if (definition.numberOfAllUsers() == 0) {
+ addLiveRange(definition, block, instruction.getNumber() + INSTRUCTION_NUMBER_DELTA);
+ }
+ live.remove(definition);
+ }
+ for (Value use : instruction.inValues()) {
+ if (!live.contains(use) && use.needsRegister()) {
+ live.add(use);
+ addLiveRange(use, block, instruction.getNumber());
+ }
+ if (use.needsRegister()) {
+ LiveIntervals useIntervals = use.getLiveIntervals();
+ useIntervals.addUse(
+ new LiveIntervalsUse(instruction.getNumber(), instruction.maxInValueRegister()));
+ }
+ }
+ Value use = instruction.getPreviousLocalValue();
+ if (use != null) {
+ assert use.needsRegister();
+ if (!live.contains(use)) {
+ live.add(use);
+ addLiveRange(use, block, instruction.getNumber());
+ }
+ LiveIntervals useIntervals = use.getLiveIntervals();
+ useIntervals.addUse(new LiveIntervalsUse(instruction.getNumber(), Constants.U16BIT_MAX));
+ }
+ }
+ }
+ }
+
+ private void clearUserInfo() {
+ code.blocks.forEach(BasicBlock::clearUserInfo);
+ }
+
+ private Value createValue(MoveType type, DebugInfo debugInfo) {
+ Value value = new Value(code.valueNumberGenerator.next(), NO_REGISTER, type, debugInfo);
+ value.setNeedsRegister(true);
+ return value;
+ }
+
+ private void replaceArgument(Invoke invoke, int index, Value newArgument) {
+ Value argument = invoke.arguments().get(index);
+ invoke.arguments().set(index, newArgument);
+ argument.removeUser(invoke);
+ newArgument.addUser(invoke);
+ }
+
+ private void generateArgumentMoves(Invoke invoke, InstructionListIterator insertAt) {
+ // If the invoke instruction require more than 5 registers we link the inputs because they
+ // need to be in consecutive registers.
+ if (invoke.requiredArgumentRegisters() > 5) {
+ replaceFullArgumentList(invoke, insertAt);
+ }
+ }
+
+ private Value createSentinelRegisterValue() {
+ return createValue(MoveType.OBJECT, null);
+ }
+
+ private LiveIntervals createSentinelLiveInterval(Value sentinelValue) {
+ LiveIntervals sentinelInterval = new LiveIntervals(sentinelValue);
+ sentinelInterval.addRange(LiveRange.INFINITE);
+ liveIntervals.add(sentinelInterval);
+ return sentinelInterval;
+ }
+
+ private void linkArgumentValues() {
+ Value current = preArgumentSentinelValue = createSentinelRegisterValue();
+ for (Value argument : code.collectArguments()) {
+ current.linkTo(argument);
+ current = argument;
+ }
+ Value endSentinel = createSentinelRegisterValue();
+ current.linkTo(endSentinel);
+ }
+
+ private void setupArgumentLiveIntervals() {
+ Value current = preArgumentSentinelValue;
+ LiveIntervals currentIntervals = createSentinelLiveInterval(current);
+ current = current.getNextConsecutive();
+ int index = 0;
+ while (current.getNextConsecutive() != null) {
+ // Add a live range to this value from the beginning of the block up to the argument
+ // instruction to avoid dead arguments without a range. This may create an actually empty
+ // range like [0,0[ but that works, too.
+ LiveIntervals argumentInterval = new LiveIntervals(current);
+ argumentInterval.addRange(new LiveRange(0, index * INSTRUCTION_NUMBER_DELTA));
+ index++;
+ liveIntervals.add(argumentInterval);
+ currentIntervals.link(argumentInterval);
+ currentIntervals = argumentInterval;
+ current = current.getNextConsecutive();
+ }
+ currentIntervals.link(createSentinelLiveInterval(current));
+ }
+
+ private void insertArgumentMoves() {
+ // Record the constraint that incoming arguments are in consecutive registers.
+ // We also insert sentinel registers before and after the arguments at this
+ // point.
+ linkArgumentValues();
+ setupArgumentLiveIntervals();
+ for (BasicBlock block : code.blocks) {
+ InstructionListIterator it = block.listIterator();
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ if (instruction.isInvoke()) {
+ // Rewind so moves are inserted before the invoke.
+ it.previous();
+ // Generate the argument moves.
+ generateArgumentMoves(instruction.asInvoke(), it);
+ // Move past the move again.
+ it.next();
+ }
+ }
+ }
+ }
+
+ private void replaceFullArgumentList(Invoke invoke, InstructionListIterator insertAt) {
+ List<Value> arguments = invoke.arguments();
+ Value previous = null;
+ for (int i = 0; i < arguments.size(); i++) {
+ generateArgumentMove(invoke, i, insertAt);
+ if (previous != null) {
+ previous.linkTo(arguments.get(i));
+ }
+ previous = arguments.get(i);
+ }
+ }
+
+ private Move generateArgumentMove(Invoke invoke, int i) {
+ List<Value> arguments = invoke.arguments();
+ Value argument = arguments.get(i);
+ Value newArgument = createValue(argument.outType(), argument.getDebugInfo());
+ Move move = new Move(newArgument, argument);
+ move.setBlock(invoke.getBlock());
+ replaceArgument(invoke, i, newArgument);
+ return move;
+ }
+
+ private void generateArgumentMove(Invoke invoke, int i, InstructionListIterator insertAt) {
+ Move move = generateArgumentMove(invoke, i);
+ insertAt.add(move);
+ }
+
+ private void computeNeedsRegister() {
+ for (BasicBlock block : code.topologicallySortedBlocks()) {
+ for (Instruction instruction : block.getInstructions()) {
+ if (instruction.outValue() != null) {
+ instruction.outValue().computeNeedsRegister();
+ }
+ }
+ }
+ }
+
+ private void pinArgumentRegisters() {
+ // Special handling for arguments. Pin their register.
+ int register = 0;
+ Value current = preArgumentSentinelValue;
+ while (current != null) {
+ LiveIntervals argumentLiveInterval = current.getLiveIntervals();
+ // Pin the argument register. We use getFreeConsecutiveRegisters to make sure that we update
+ // the max register number.
+ register = getFreeConsecutiveRegisters(argumentLiveInterval.requiredRegisters());
+ argumentLiveInterval.setRegister(register);
+ current = current.getNextConsecutive();
+ }
+ assert register == numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS - 1;
+ }
+
+ private int getFreeConsecutiveRegisters(int numberOfRegister) {
+ List<Integer> unused = new ArrayList<>();
+ int first = getNextFreeRegister();
+ int current = first;
+ while ((current - first + 1) != numberOfRegister) {
+ for (int i = 0; i < numberOfRegister - 1; i++) {
+ int next = getNextFreeRegister();
+ if (next != current + 1) {
+ for (int j = first; j <= current; j++) {
+ unused.add(j);
+ }
+ first = next;
+ current = first;
+ break;
+ }
+ current++;
+ }
+ }
+ freeRegisters.addAll(unused);
+ maxRegisterNumber = Math.max(maxRegisterNumber, first + numberOfRegister - 1);
+ return first;
+ }
+
+ private int getNextFreeRegister() {
+ if (freeRegisters.size() > 0) {
+ return freeRegisters.pollFirst();
+ }
+ return nextUnusedRegisterNumber++;
+ }
+
+ private void excludeRegistersForInterval(LiveIntervals intervals, Set<Integer> excluded) {
+ int register = intervals.getRegister();
+ for (int i = 0; i < intervals.requiredRegisters(); i++) {
+ if (freeRegisters.remove(register + i)) {
+ excluded.add(register + i);
+ }
+ }
+ }
+
+ private void freeRegistersForIntervals(LiveIntervals intervals) {
+ int register = intervals.getRegister();
+ freeRegisters.add(register);
+ if (intervals.getType() == MoveType.WIDE) {
+ freeRegisters.add(register + 1);
+ }
+ }
+
+ private void takeRegistersForIntervals(LiveIntervals intervals) {
+ int register = intervals.getRegister();
+ freeRegisters.remove(register);
+ if (intervals.getType() == MoveType.WIDE) {
+ freeRegisters.remove(register + 1);
+ }
+ }
+
+ private boolean noLinkedValues() {
+ for (BasicBlock block : code.blocks) {
+ for (Phi phi : block.getPhis()) {
+ assert phi.getNextConsecutive() == null;
+ }
+ for (Instruction instruction : block.getInstructions()) {
+ for (Value value : instruction.inValues()) {
+ assert value.getNextConsecutive() == null;
+ }
+ assert instruction.outValue() == null ||
+ instruction.outValue().getNextConsecutive() == null;
+ }
+ }
+ return true;
+ }
+
+ public void print(CfgPrinter printer, String title) {
+ printer.begin("intervals");
+ printer.print("name \"").append(title).append("\"").ln();
+ PriorityQueue<LiveIntervals> sortedIntervals =
+ new PriorityQueue<>((o1, o2) -> Integer.compare(o1.getStart(), o2.getStart()));
+ sortedIntervals.addAll(liveIntervals);
+ for (LiveIntervals interval = sortedIntervals.poll();
+ interval != null;
+ interval = sortedIntervals.poll()) {
+ Value value = interval.getValue();
+ if (interval.getRanges().get(0).isInfinite()) {
+ // Skip argument sentinels.
+ continue;
+ }
+ interval.print(printer, value.getNumber(), value.getNumber());
+ }
+ printer.end("intervals");
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("Live ranges:\n");
+ for (LiveIntervals intervals : liveIntervals) {
+ Value value = intervals.getValue();
+ builder.append(value);
+ builder.append(" ");
+ builder.append(intervals);
+ }
+ builder.append("\nLive range ascii art: \n");
+ for (LiveIntervals intervals : liveIntervals) {
+ Value value = intervals.getValue();
+ if (intervals.getRegister() == NO_REGISTER) {
+ StringUtils.appendRightPadded(builder, value + " (no reg): ", 20);
+ } else {
+ StringUtils.appendRightPadded(builder, value + " r" + intervals.getRegister() + ": ", 20);
+ }
+ builder.append("|");
+ builder.append(intervals.toAscciArtString());
+ builder.append("\n");
+ }
+ return builder.toString();
+ }
+}
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
new file mode 100644
index 0000000..f1d3b9e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
@@ -0,0 +1,467 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.regalloc;
+
+import static com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator.NO_REGISTER;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.CfgPrinter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+
+public class LiveIntervals {
+
+ private final Value value;
+ private LiveIntervals nextConsecutive;
+ private LiveIntervals previousConsecutive;
+ private LiveIntervals splitParent;
+ private List<LiveIntervals> splitChildren = new ArrayList<>();
+ private List<LiveRange> ranges = new ArrayList<>();
+ private TreeSet<LiveIntervalsUse> uses = new TreeSet<>();
+ private int numberOfConsecutiveRegisters = -1;
+ private int register = NO_REGISTER;
+ private LiveIntervals hint;
+ private boolean spilled = false;
+
+ // Only registers up to and including the registerLimit are allowed for this interval.
+ private int registerLimit = Constants.U16BIT_MAX;
+
+ // Max register used for any of the non-spilled splits for these live intervals or for any of the
+ // live intervals that this live interval is connected to by phi moves. This is used to
+ // conservatively determine if it is safe to use rematerialization for this value.
+ private int maxNonSpilledRegister = NO_REGISTER;
+
+ LiveIntervals(Value value) {
+ this.value = value;
+ splitParent = this;
+ value.setLiveIntervals(this);
+ }
+
+ LiveIntervals(LiveIntervals splitParent) {
+ this.splitParent = splitParent;
+ value = splitParent.value;
+ }
+
+ private int toInstructionPosition(int position) {
+ return position % 2 == 0 ? position : position + 1;
+ }
+
+ private int toGapPosition(int position) {
+ return position % 2 == 1 ? position : position - 1;
+ }
+
+ public MoveType getType() {
+ return value.outType();
+ }
+
+ public Value getValue() {
+ return value;
+ }
+
+ public int requiredRegisters() {
+ return getType() == MoveType.WIDE ? 2 : 1;
+ }
+
+ public void setHint(LiveIntervals intervals) {
+ hint = intervals;
+ }
+
+ public LiveIntervals getHint() {
+ return hint;
+ }
+
+ public void setSpilled(boolean value) {
+ spilled = value;
+ }
+
+ public boolean isSpilled() {
+ return spilled;
+ }
+
+ public boolean isRematerializable(LinearScanRegisterAllocator registerAllocator) {
+ if (value.isPhi() || !value.definition.isConstNumber()) {
+ return false;
+ }
+ // If one of the non-spilled splits uses a register that is higher than U8BIT_MAX we cannot
+ // rematerialize it using a ConstNumber instruction and we use spill moves instead of
+ // rematerialization.
+ int max = registerAllocator.realRegisterNumberFromAllocated(getMaxNonSpilledRegister());
+ return max < Constants.U8BIT_MAX;
+ }
+
+ public boolean isSpilledAndRematerializable(LinearScanRegisterAllocator allocator) {
+ return isSpilled() && isRematerializable(allocator);
+ }
+
+ public void link(LiveIntervals next) {
+ assert numberOfConsecutiveRegisters == -1;
+ nextConsecutive = next;
+ next.previousConsecutive = this;
+ }
+
+ public boolean isLinked() {
+ return splitParent.previousConsecutive != null || splitParent.nextConsecutive != null;
+ }
+
+ public boolean isArgumentInterval() {
+ // TODO(ager): This is pretty indirect. We might want to have a more direct indication.
+ LiveIntervals current = splitParent;
+ while (current.previousConsecutive != null) {
+ current = current.previousConsecutive;
+ }
+ return current.ranges.get(0).isInfinite();
+ }
+
+
+ public LiveIntervals getStartOfConsecutive() {
+ LiveIntervals current = this;
+ while (current.previousConsecutive != null) {
+ current = current.previousConsecutive;
+ }
+ return current;
+ }
+
+ public LiveIntervals getNextConsecutive() {
+ return nextConsecutive;
+ }
+
+ public LiveIntervals getPreviousConsecutive() {
+ return previousConsecutive;
+ }
+
+ public int numberOfConsecutiveRegisters() {
+ LiveIntervals start = getStartOfConsecutive();
+ if (start.numberOfConsecutiveRegisters != -1) {
+ assert start.numberOfConsecutiveRegisters == computeNumberOfConsecutiveRegisters();
+ return start.numberOfConsecutiveRegisters;
+ }
+ return computeNumberOfConsecutiveRegisters();
+ }
+
+ private int computeNumberOfConsecutiveRegisters() {
+ LiveIntervals start = getStartOfConsecutive();
+ int result = 0;
+ for (LiveIntervals current = start;
+ current != null;
+ current = current.nextConsecutive) {
+ result += current.requiredRegisters();
+ }
+ start.numberOfConsecutiveRegisters = result;
+ return result;
+ }
+
+ public boolean hasSplits() {
+ return splitChildren.size() != 0;
+ }
+
+ public List<LiveIntervals> getSplitChildren() {
+ return splitChildren;
+ }
+
+ public LiveIntervals getSplitParent() {
+ return splitParent;
+ }
+
+ /**
+ * Add a live range to the intervals.
+ *
+ * @param range the range to add
+ */
+ public void addRange(LiveRange range) {
+ boolean added = tryAddRange(range);
+ assert added;
+ }
+
+ private boolean tryAddRange(LiveRange range) {
+ if (ranges.size() > 0) {
+ LiveRange lastRange = ranges.get(ranges.size() - 1);
+ if (lastRange.isInfinite()) {
+ return false;
+ }
+ int rangeStartInstructionPosition = toInstructionPosition(range.start);
+ int lastRangeEndInstructionPosition = toInstructionPosition(lastRange.end);
+ if (lastRangeEndInstructionPosition > rangeStartInstructionPosition) {
+ return false;
+ }
+ if (lastRangeEndInstructionPosition == rangeStartInstructionPosition) {
+ lastRange.end = range.end;
+ return true;
+ }
+ }
+ ranges.add(range);
+ return true;
+ }
+
+ /**
+ * Record a use for this interval.
+ */
+ public void addUse(LiveIntervalsUse use) {
+ uses.add(use);
+ updateRegisterConstraint(use.getLimit());
+ }
+
+ public void updateRegisterConstraint(int constraint) {
+ registerLimit = Math.min(registerLimit, constraint);
+ }
+
+ public TreeSet<LiveIntervalsUse> getUses() {
+ return uses;
+ }
+
+ public List<LiveRange> getRanges() {
+ return ranges;
+ }
+
+ public int getStart() {
+ assert !ranges.isEmpty();
+ return ranges.get(0).start;
+ }
+
+ public int getEnd() {
+ assert !ranges.isEmpty();
+ return ranges.get(ranges.size() - 1).end;
+ }
+
+ public int getRegister() {
+ return register;
+ }
+
+ public int getRegisterLimit() {
+ return registerLimit;
+ }
+
+ public void setRegister(int n) {
+ assert register == NO_REGISTER || register == n;
+ register = n;
+ }
+
+ private int computeMaxNonSpilledRegister() {
+ assert splitParent == this;
+ assert maxNonSpilledRegister == NO_REGISTER;
+ if (!isSpilled()) {
+ maxNonSpilledRegister = getRegister();
+ }
+ for (LiveIntervals child : splitChildren) {
+ if (!child.isSpilled()) {
+ maxNonSpilledRegister = Math.max(maxNonSpilledRegister, child.getRegister());
+ }
+ }
+ return maxNonSpilledRegister;
+ }
+
+ public void setMaxNonSpilledRegister(int i) {
+ assert i >= splitParent.maxNonSpilledRegister;
+ splitParent.maxNonSpilledRegister = i;
+ }
+
+ public int getMaxNonSpilledRegister() {
+ if (splitParent.maxNonSpilledRegister != NO_REGISTER) {
+ return splitParent.maxNonSpilledRegister;
+ }
+ return splitParent.computeMaxNonSpilledRegister();
+ }
+
+ public boolean usesRegister(int n) {
+ if (register == n || (getType() == MoveType.WIDE && register + 1 == n)) {
+ return true;
+ }
+ return false;
+ }
+
+ public void clearRegisterAssignment() {
+ register = NO_REGISTER;
+ hint = null;
+ }
+
+ public boolean overlapsPosition(int position) {
+ for (LiveRange range : ranges) {
+ if (range.start > position) {
+ // Ranges are sorted. When a range starts after position there is no overlap.
+ return false;
+ }
+ if (position < range.end) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean overlaps(LiveIntervals other) {
+ return nextOverlap(other) != -1;
+ }
+
+ public int nextOverlap(LiveIntervals other) {
+ Iterator<LiveRange> it = other.ranges.iterator();
+ LiveRange otherRange = it.next();
+ for (LiveRange range : ranges) {
+ while (otherRange.end <= range.start) {
+ if (!it.hasNext()) {
+ return -1;
+ }
+ otherRange = it.next();
+ }
+ if (otherRange.start < range.end) {
+ return otherRange.start;
+ }
+ }
+ return -1;
+ }
+
+ public int firstUseAfter(int unhandledStart) {
+ for (LiveIntervalsUse use : uses) {
+ if (use.getPosition() >= unhandledStart) {
+ return use.getPosition();
+ }
+ }
+ return Integer.MAX_VALUE;
+ }
+
+ public int getFirstUse() {
+ return uses.first().getPosition();
+ }
+
+ public LiveIntervalsUse firstUseWithConstraint() {
+ for (LiveIntervalsUse use : uses) {
+ if (use.getLimit() < Constants.U16BIT_MAX) {
+ return use;
+ }
+ }
+ return null;
+ }
+
+ public LiveIntervals splitBefore(int start) {
+ if (toInstructionPosition(start) == toInstructionPosition(getStart())) {
+ assert uses.size() == 0 || getFirstUse() != start;
+ register = NO_REGISTER;
+ return this;
+ }
+ start = toGapPosition(start);
+ LiveIntervals splitChild = new LiveIntervals(splitParent);
+ splitParent.splitChildren.add(splitChild);
+ List<LiveRange> beforeSplit = new ArrayList<>();
+ List<LiveRange> afterSplit = new ArrayList<>();
+ if (start == getEnd()) {
+ beforeSplit = ranges;
+ afterSplit.add(new LiveRange(start, start));
+ } else {
+ int rangeToSplitIndex = 0;
+ for (; rangeToSplitIndex < ranges.size(); rangeToSplitIndex++) {
+ LiveRange range = ranges.get(rangeToSplitIndex);
+ if (range.start <= start && range.end > start) {
+ break;
+ }
+ if (range.start > start) {
+ break;
+ }
+ }
+ LiveRange rangeToSplit = ranges.get(rangeToSplitIndex);
+ beforeSplit.addAll(ranges.subList(0, rangeToSplitIndex));
+ if (rangeToSplit.start < start) {
+ beforeSplit.add(new LiveRange(rangeToSplit.start, start));
+ afterSplit.add(new LiveRange(start, rangeToSplit.end));
+ } else {
+ afterSplit.add(rangeToSplit);
+ }
+ afterSplit.addAll(ranges.subList(rangeToSplitIndex + 1, ranges.size()));
+ }
+ splitChild.ranges = afterSplit;
+ ranges = beforeSplit;
+ while (!uses.isEmpty() && uses.last().getPosition() >= start) {
+ splitChild.addUse(uses.pollLast());
+ }
+ // Recompute limit after having removed uses from this interval.
+ recomputeLimit();
+ assert !ranges.isEmpty();
+ assert !splitChild.ranges.isEmpty();
+ return splitChild;
+ }
+
+ private void recomputeLimit() {
+ registerLimit = Constants.U16BIT_MAX;
+ for (LiveIntervalsUse use : uses) {
+ updateRegisterConstraint(use.getLimit());
+ }
+ }
+
+ public LiveIntervals getSplitCovering(int instructionNumber) {
+ // Check if this interval itself is covering the instruction.
+ if (getStart() <= instructionNumber && getEnd() > instructionNumber) {
+ return this;
+ }
+ // If the instruction number is not in this intervals range, we go through all split children.
+ // If we do not find a child that contains the instruction number we return the interval
+ // whose end is the instruction number. This is needed when transitioning values across
+ // control-flow boundaries.
+ LiveIntervals matchingEnd = getEnd() == instructionNumber ? this : null;
+ for (LiveIntervals splitChild : splitChildren) {
+ if (splitChild.getStart() <= instructionNumber && splitChild.getEnd() > instructionNumber) {
+ return splitChild;
+ }
+ if (splitChild.getEnd() == instructionNumber) {
+ matchingEnd = splitChild;
+ }
+ }
+ if (matchingEnd != null) {
+ return matchingEnd;
+ }
+ assert false : "Couldn't find split covering instruction position.";
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("(cons ");
+ // Use the field here to avoid toString to have side effects.
+ builder.append(numberOfConsecutiveRegisters);
+ builder.append("): ");
+ for (LiveRange range : getRanges()) {
+ builder.append(range);
+ builder.append(" ");
+ }
+ builder.append("\n");
+ return builder.toString();
+ }
+
+ public String toAscciArtString() {
+ StringBuilder builder = new StringBuilder();
+ int current = 0;
+ for (LiveRange range : getRanges()) {
+ if (range.isInfinite()) {
+ builder.append("--- infinite ---...");
+ break;
+ }
+ for (; current < range.start; current++) {
+ builder.append(" ");
+ }
+ for (; current < range.end; current++) {
+ builder.append("-");
+ }
+ }
+ return builder.toString();
+ }
+
+ public void print(CfgPrinter printer, int number, int parentNumber) {
+ printer.append(number * 10000 + register) // range number
+ .sp().append("object") // range type
+ .sp().append(parentNumber * 10000 + getSplitParent().getRegister()) // split parent
+ .sp().append(-1); // hint
+ for (LiveRange range : getRanges()) {
+ printer.sp().append(range.toString());
+ }
+ for (LiveIntervalsUse use : getUses()) {
+ printer.sp().append(use.getPosition()).sp().append("M");
+ }
+ printer.append(" \"\"").ln();
+ int delta = 0;
+ for (LiveIntervals splitChild : splitChildren) {
+ delta += 10000;
+ splitChild.print(printer, number + delta, number);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervalsUse.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervalsUse.java
new file mode 100644
index 0000000..fd37af8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervalsUse.java
@@ -0,0 +1,44 @@
+// 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.ir.regalloc;
+
+public class LiveIntervalsUse implements Comparable<LiveIntervalsUse> {
+ private int position;
+ private int limit;
+
+ public LiveIntervalsUse(int position, int limit) {
+ this.position = position;
+ this.limit = limit;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public int getLimit() {
+ return limit;
+ }
+
+ @Override
+ public int hashCode() {
+ return position + limit * 7;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof LiveIntervalsUse)) {
+ return false;
+ }
+ LiveIntervalsUse o = (LiveIntervalsUse) other;
+ return o.position == position && o.limit == limit;
+ }
+
+ @Override
+ public int compareTo(LiveIntervalsUse o) {
+ if (o.position != position) {
+ return position - o.position;
+ }
+ return limit - o.limit;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveRange.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveRange.java
new file mode 100644
index 0000000..0d7bb80
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveRange.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.regalloc;
+
+class LiveRange {
+
+ public final static LiveRange INFINITE = new LiveRange(0, Integer.MAX_VALUE);
+
+ public int start; // inclusive
+ public int end; // exclusive
+
+ public LiveRange(int start, int end) {
+ this.start = start;
+ this.end = end;
+ }
+
+ @Override
+ public String toString() {
+ return "[" + start + ", " + end + "[";
+ }
+
+ public boolean isInfinite() {
+ return this == INFINITE;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
new file mode 100644
index 0000000..9dba6d0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.regalloc;
+
+import com.android.tools.r8.ir.code.Value;
+
+public interface RegisterAllocator {
+ void allocateRegisters(boolean debug);
+ int registersUsed();
+ int getRegisterForValue(Value value, int instructionNumber);
+ boolean argumentValueUsesHighRegister(Value value, int instructionNumber);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java
new file mode 100644
index 0000000..9cb873e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java
@@ -0,0 +1,73 @@
+// 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.ir.regalloc;
+
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.MoveType;
+import java.util.Map;
+import java.util.Set;
+
+// Register moves used by the spilling register allocator. These are used both for spill and
+// for phi moves and they are moves between actual registers represented by their register number.
+public class RegisterMove {
+ MoveType type;
+ int dst;
+ int src;
+ Instruction definition;
+
+ public RegisterMove(int dst, int src, MoveType type) {
+ this.dst = dst;
+ this.src = src;
+ this.type = type;
+ }
+
+ public RegisterMove(int dst, MoveType type, Instruction definition) {
+ this.dst = dst;
+ this.src = LinearScanRegisterAllocator.NO_REGISTER;
+ this.type = type;
+ assert definition.isConstInstruction();
+ this.definition = definition;
+ }
+
+
+ private boolean writes(int register) {
+ if (type == MoveType.WIDE && (dst + 1) == register) {
+ return true;
+ }
+ return dst == register;
+ }
+
+ public boolean isBlocked(Set<RegisterMove> moveSet, Map<Integer, Integer> valueMap) {
+ for (RegisterMove move : moveSet) {
+ if (move.src == LinearScanRegisterAllocator.NO_REGISTER) {
+ continue;
+ }
+ if (move != this) {
+ if (writes(valueMap.get(move.src))) {
+ return true;
+ }
+ if (move.type == MoveType.WIDE) {
+ if (writes(valueMap.get(move.src) + 1)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return src + dst * 3 + type.hashCode() * 5 + (definition == null ? 0 : definition.hashCode());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof RegisterMove)) {
+ return false;
+ }
+ RegisterMove o = (RegisterMove) other;
+ return o.src == src && o.dst == dst && o.type == type && o.definition == definition;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
new file mode 100644
index 0000000..cc26729
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
@@ -0,0 +1,168 @@
+// 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.ir.regalloc;
+
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.FixedRegisterValue;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Move;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Value;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class RegisterMoveScheduler {
+ // The set of moves to schedule.
+ private Set<RegisterMove> moveSet = new LinkedHashSet<>();
+ // Mapping to keep track of which values currently corresponds to each other.
+ // This is initially an identity map but changes as we insert moves.
+ private Map<Integer, Integer> valueMap = new HashMap<>();
+ // Number of temp registers used to schedule the moves.
+ private int usedTempRegisters = 0;
+ // Location at which to insert the scheduled moves.
+ private final InstructionListIterator insertAt;
+ // The first available temporary register.
+ private final int tempRegister;
+
+ public RegisterMoveScheduler(InstructionListIterator insertAt, int tempRegister) {
+ this.insertAt = insertAt;
+ this.tempRegister = tempRegister;
+ }
+
+ public void addMove(RegisterMove move) {
+ moveSet.add(move);
+ if (move.src != LinearScanRegisterAllocator.NO_REGISTER) {
+ valueMap.put(move.src, move.src);
+ }
+ valueMap.put(move.dst, move.dst);
+ }
+
+ public void schedule() {
+ // Worklist of moves that are ready to be inserted.
+ Deque<RegisterMove> worklist = new LinkedList<>();
+
+ // Initialize worklist with the moves that do not interfere with other moves.
+ Iterator<RegisterMove> iterator = moveSet.iterator();
+ while (iterator.hasNext()) {
+ RegisterMove move = iterator.next();
+ if (!move.isBlocked(moveSet, valueMap)) {
+ worklist.addLast(move);
+ iterator.remove();
+ }
+ }
+
+ // Process the worklist generating moves. If the worklist becomes empty while the move set
+ // still contains elements we need to use a temporary to break cycles.
+ while (!worklist.isEmpty() || !moveSet.isEmpty()) {
+ while (!worklist.isEmpty()) {
+ RegisterMove move = worklist.removeFirst();
+ assert !move.isBlocked(moveSet, valueMap);
+ // Insert the move.
+ Integer generatedDest = createMove(move);
+ // Update the value map with the information that dest can be used instead of
+ // src starting now.
+ if (move.src != LinearScanRegisterAllocator.NO_REGISTER) {
+ valueMap.put(move.src, generatedDest);
+ }
+ // Iterate and find the moves that were blocked because they need to write to
+ // one of the move src. That is now valid because the move src is preserved in dest.
+ iterator = moveSet.iterator();
+ while (iterator.hasNext()) {
+ RegisterMove other = iterator.next();
+ if (!other.isBlocked(moveSet, valueMap)) {
+ worklist.addLast(other);
+ iterator.remove();
+ }
+ }
+ }
+ if (!moveSet.isEmpty()) {
+ // The remaining moves are conflicting. Chose a move and unblock it by generating moves to
+ // temporary registers for its destination value(s).
+ RegisterMove move = pickMoveToUnblock();
+ createMoveDestToTemp(move);
+ worklist.addLast(move);
+ }
+ }
+ }
+
+ public int getUsedTempRegisters() {
+ return usedTempRegisters;
+ }
+
+ private List<RegisterMove> findMovesWithSrc(int src, MoveType type) {
+ List<RegisterMove> result = new ArrayList<>();
+ assert src != LinearScanRegisterAllocator.NO_REGISTER;
+ for (RegisterMove move : moveSet) {
+ if (move.src == LinearScanRegisterAllocator.NO_REGISTER) {
+ continue;
+ }
+ int moveSrc = valueMap.get(move.src);
+ if (moveSrc == src) {
+ result.add(move);
+ } else if (move.type == MoveType.WIDE && (moveSrc + 1) == src) {
+ result.add(move);
+ } else if (type == MoveType.WIDE && (moveSrc - 1) == src) {
+ result.add(move);
+ }
+ }
+ return result;
+ }
+
+ private Integer createMove(RegisterMove move) {
+ Instruction instruction;
+ Value to = new FixedRegisterValue(move.type, move.dst);
+ if (move.definition != null) {
+ ConstNumber number = move.definition.asConstNumber();
+ instruction = new ConstNumber(number.type, to, number.getRawValue());
+ } else {
+ Value from = new FixedRegisterValue(move.type, valueMap.get(move.src));
+ instruction = new Move(to, from);
+ }
+ insertAt.add(instruction);
+ return move.dst;
+
+ }
+
+ private void createMoveDestToTemp(RegisterMove move) {
+ // In order to unblock this move we might have to move more than one value to temporary
+ // registers if we are unlucky with the overlap for values that use two registers.
+ List<RegisterMove> movesWithSrc = findMovesWithSrc(move.dst, move.type);
+ assert movesWithSrc.size() > 0;
+ for (RegisterMove moveWithSrc : movesWithSrc) {
+ // TODO(ager): For now we always use a new temporary register whenever we have to unblock
+ // a move. The move scheduler can have multiple unblocking temps live at the same time
+ // and therefore we cannot have just one tempRegister (pair). However, we could check here
+ // if the previously used tempRegisters is still needed by any of the moves in the move set
+ // (taking the value map into account). If not, we can reuse the temp register instead
+ // of generating a new one.
+ Value to = new FixedRegisterValue(moveWithSrc.type, tempRegister + usedTempRegisters);
+ Value from = new FixedRegisterValue(moveWithSrc.type, valueMap.get(moveWithSrc.src));
+ insertAt.add(new Move(to, from));
+ valueMap.put(moveWithSrc.src, tempRegister + usedTempRegisters);
+ usedTempRegisters += moveWithSrc.type == MoveType.WIDE ? 2 : 1;
+ }
+ }
+
+ private RegisterMove pickMoveToUnblock() {
+ Iterator<RegisterMove> iterator = moveSet.iterator();
+ RegisterMove move = null;
+ // Pick a non-wide move to unblock if possible.
+ while (iterator.hasNext()) {
+ move = iterator.next();
+ if (move.type != MoveType.WIDE) {
+ break;
+ }
+ }
+ iterator.remove();
+ return move;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterPositions.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterPositions.java
new file mode 100644
index 0000000..87eedca
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterPositions.java
@@ -0,0 +1,55 @@
+// 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.ir.regalloc;
+
+import java.util.Arrays;
+
+/**
+ * Simple mapping from a register to an int value.
+ * <p>
+ * The backing for the mapping grows as needed up to a given limit. If no mapping exists for
+ * a register number the value is assumed to be Integer.MAX_VALUE.
+ */
+
+public class RegisterPositions {
+ private static final int INITIAL_SIZE = 16;
+ private int limit;
+ private int[] backing;
+
+ public RegisterPositions(int limit) {
+ this.limit = limit;
+ backing = new int[INITIAL_SIZE];
+ for (int i = 0; i < INITIAL_SIZE; i++) {
+ backing[i] = Integer.MAX_VALUE;
+ }
+ }
+
+ public void set(int index, int value) {
+ if (index >= backing.length) {
+ grow(index + 1);
+ }
+ backing[index] = value;
+ }
+
+ public int get(int index) {
+ if (index < backing.length) {
+ return backing[index];
+ }
+ assert index < limit;
+ return Integer.MAX_VALUE;
+ }
+
+ public void grow(int minSize) {
+ int size = backing.length;
+ while (size < minSize) {
+ size *= 2;
+ }
+ size = Math.min(size, limit);
+ int oldSize = backing.length;
+ backing = Arrays.copyOf(backing, size);
+ for (int i = oldSize; i < size; i++) {
+ backing[i] = Integer.MAX_VALUE;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMove.java b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMove.java
new file mode 100644
index 0000000..c13d23e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMove.java
@@ -0,0 +1,62 @@
+// 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.ir.regalloc;
+
+import com.android.tools.r8.ir.code.MoveType;
+
+/**
+ * A SpillMove represents either a phi move that transfers an SSA value to the SSA phi value or
+ * a spill or restore move that transfers the same SSA value between different registers because
+ * of spilling.
+ */
+class SpillMove {
+ MoveType type;
+ LiveIntervals from;
+ LiveIntervals to;
+
+ public SpillMove(MoveType type, LiveIntervals to, LiveIntervals from) {
+ this.type = type;
+ this.to = to;
+ this.from = from;
+ assert to.getRegister() != LinearScanRegisterAllocator.NO_REGISTER;
+ assert from.getRegister() != LinearScanRegisterAllocator.NO_REGISTER;
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode() + 3 * from.getRegister() + 5 * to.getRegister();
+ }
+
+ public void updateMaxNonSpilled() {
+ int maxFrom = from.getMaxNonSpilledRegister();
+ int maxTo = to.getMaxNonSpilledRegister();
+ if (maxFrom > maxTo) {
+ to.setMaxNonSpilledRegister(maxFrom);
+ } else {
+ from.setMaxNonSpilledRegister(maxTo);
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (!(other instanceof SpillMove)) {
+ return false;
+ }
+ SpillMove o = (SpillMove) other;
+ return type == o.type
+ && from.getRegister() == o.from.getRegister()
+ && to.getRegister() == o.to.getRegister()
+ && from.getSplitParent() == o.from.getSplitParent()
+ && to.getSplitParent() == o.to.getSplitParent();
+ }
+
+ @Override
+ public String toString() {
+ return to.getRegister() + " <- " + from.getRegister() + " (" + type + ")";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
new file mode 100644
index 0000000..643a3c1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
@@ -0,0 +1,291 @@
+// 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.ir.regalloc;
+
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.MoveType;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A set of spill moves and functionality to schedule and insert them in the code.
+ */
+class SpillMoveSet {
+ // Spill and restore moves on entry.
+ private final Map<Integer, Set<SpillMove>> instructionToInMoves = new HashMap<>();
+ // Spill and restore moves on exit.
+ private final Map<Integer, Set<SpillMove>> instructionToOutMoves = new HashMap<>();
+ // Phi moves.
+ private final Map<Integer, Set<SpillMove>> instructionToPhiMoves = new HashMap<>();
+ // The code into which to insert the moves.
+ private final IRCode code;
+ // The register allocator generating moves.
+ private LinearScanRegisterAllocator allocator;
+ // All registers below this number are arguments.
+ // TODO(ager): Get rid of this field, we should deal with arguments and other values that can
+ // be rematerialized differently.
+ private final int argumentRegisterLimit;
+ // Mapping from instruction numbers to the block that start with that instruction if any.
+ private final Map<Integer, BasicBlock> blockStartMap = new HashMap<>();
+ // The number of temporary registers used for parallel moves when scheduling the moves.
+ private int usedTempRegisters = 0;
+
+ public SpillMoveSet(
+ LinearScanRegisterAllocator allocator, IRCode code, int argumentRegisterLimit) {
+ this.allocator = allocator;
+ this.code = code;
+ this.argumentRegisterLimit = argumentRegisterLimit;
+ for (BasicBlock block : code.blocks) {
+ blockStartMap.put(block.entry().getNumber(), block);
+ }
+ }
+
+ /**
+ * Add a spill or restore move.
+ *
+ * <p>This is used between all interval splits. The move is only inserted if it is restoring
+ * from a spill slot at a position that is not at the start of a block. All block start
+ * moves are handled by resolution.
+ *
+ * @param i instruction number (gap number) for which to insert the move
+ * @param to interval representing the destination for the move
+ * @param from interval representating the source for the move
+ */
+ public void addSpillOrRestoreMove(int i, LiveIntervals to, LiveIntervals from) {
+ assert i % 2 == 1;
+ assert to.getSplitParent() == from.getSplitParent();
+ BasicBlock atEntryToBlock = blockStartMap.get(i + 1);
+ if (atEntryToBlock == null) {
+ addInMove(i, to, from);
+ }
+ }
+
+ /**
+ * Add a resolution move. This deals with moves in order to transfer an SSA value to another
+ * register across basic block boundaries.
+ *
+ * @param i instruction number (gap number) for which to insert the move
+ * @param to interval representing the destination for the move
+ * @param from interval representing the source for the move
+ */
+ public void addInResolutionMove(int i, LiveIntervals to, LiveIntervals from) {
+ assert to.getSplitParent() == from.getSplitParent();
+ addInMove(i, to, from);
+ }
+
+ public void addOutResolutionMove(int i, LiveIntervals to, LiveIntervals from) {
+ assert to.getSplitParent() == from.getSplitParent();
+ addOutMove(i, to, from);
+ }
+
+ /**
+ * Add a phi move to transfer an incoming SSA value to the SSA value in the destination block.
+ *
+ * @param i instruction number (gap number) for which to insert the move
+ * @param to interval representing the destination for the move
+ * @param from interval representing the source for the move
+ */
+ public void addPhiMove(int i, LiveIntervals to, LiveIntervals from) {
+ assert i % 2 == 1;
+ SpillMove move = new SpillMove(moveTypeForIntervals(to, from), to, from);
+ move.updateMaxNonSpilled();
+ instructionToPhiMoves.computeIfAbsent(i, (k) -> new LinkedHashSet<>()).add(move);
+ }
+
+ private void addInMove(int i, LiveIntervals to, LiveIntervals from) {
+ assert i % 2 == 1;
+ instructionToInMoves.computeIfAbsent(i, (k) -> new LinkedHashSet<>()).add(
+ new SpillMove(moveTypeForIntervals(to, from), to, from));
+ }
+
+ private void addOutMove(int i, LiveIntervals to, LiveIntervals from) {
+ assert i % 2 == 1;
+ instructionToOutMoves.computeIfAbsent(i, (k) -> new LinkedHashSet<>()).add(
+ new SpillMove(moveTypeForIntervals(to, from), to, from));
+ }
+
+ /**
+ * Schedule the moves added to this SpillMoveSet and insert them into the code.
+ *
+ * <p>Scheduling requires parallel move semantics for some of the moves. That can require
+ * the use of temporary registers to break cycles.
+ *
+ * @param tempRegister the first temporary register to use
+ * @return the number of temporary registers used
+ */
+ public int scheduleAndInsertMoves(int tempRegister) {
+ for (BasicBlock block : code.blocks) {
+ InstructionListIterator it = block.listIterator();
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ int number = instruction.getNumber();
+ if (needsMovesBeforeInstruction(number)) {
+ // Move back so moves are inserted before the instruction.
+ it.previous();
+ scheduleMovesBeforeInstruction(tempRegister, number, it);
+ // Move past the instruction again.
+ it.next();
+ }
+ }
+ }
+ return usedTempRegisters;
+ }
+
+ private boolean isArgumentRegister(int register) {
+ return register < argumentRegisterLimit;
+ }
+
+ private MoveType moveTypeForIntervals(LiveIntervals to, LiveIntervals from) {
+ MoveType toType = to.getType();
+ MoveType fromType = from.getType();
+ if (toType == MoveType.OBJECT || fromType == MoveType.OBJECT) {
+ assert fromType == MoveType.OBJECT || fromType == MoveType.SINGLE;
+ assert toType == MoveType.OBJECT || toType == MoveType.SINGLE;
+ return MoveType.OBJECT;
+ }
+ assert toType == fromType;
+ return toType;
+ }
+
+ private boolean needsMovesBeforeInstruction(int i) {
+ return instructionToOutMoves.containsKey(i - 1)
+ || instructionToInMoves.containsKey(i - 1)
+ || instructionToPhiMoves.containsKey(i - 1);
+ }
+
+ private SpillMove getMoveWithSource(LiveIntervals src, Collection<SpillMove> moves) {
+ for (SpillMove move : moves) {
+ if (move.from == src) {
+ return move;
+ }
+ }
+ return null;
+ }
+
+ private SpillMove getMoveWritingSourceRegister(SpillMove inMove, Collection<SpillMove> moves) {
+ int srcRegister = inMove.from.getRegister();
+ int srcRegisters = inMove.type == MoveType.WIDE ? 2 : 1;
+ for (SpillMove move : moves) {
+ int dstRegister = move.to.getRegister();
+ int dstRegisters = move.type == MoveType.WIDE ? 2 : 1;
+ for (int s = 0; s < srcRegisters; s++) {
+ for (int d = 0; d < dstRegisters; d++) {
+ if ((dstRegister + d) == (srcRegister + s)) {
+ return move;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ // Shortcut move chains where we have a move in the in move set that moves to a
+ // location that is then moved in the out move set to its final destination.
+ //
+ // r1 <- r0 (in move set)
+ //
+ // r2 <- r1 (out move set)
+ //
+ // is replaced with
+ //
+ // r2 <- r0 (out move set)
+ //
+ // Care must be taken when there are other moves in the in move set that can interfere
+ // with the value. For example:
+ //
+ // r1 <- r0 (in move set)
+ // r0 <- r1 (in move set)
+ //
+ // r2 <- r1 (out move set)
+ //
+ // Additionally, if a phi move uses the destination of the in move it needs to stay.
+ //
+ // If such interference exists we don't rewrite the moves and parallel moves are generated
+ // to swap r1 and r0 on entry via a temporary register.
+ private void pruneParallelMoveSets(
+ Set<SpillMove> inMoves, Set<SpillMove> outMoves, Set<SpillMove> phiMoves) {
+ Iterator<SpillMove> it = inMoves.iterator();
+ while (it.hasNext()) {
+ SpillMove inMove = it.next();
+ SpillMove outMove = getMoveWithSource(inMove.to, outMoves);
+ SpillMove blockingInMove = getMoveWritingSourceRegister(inMove, inMoves);
+ SpillMove blockingPhiMove = getMoveWithSource(inMove.to, phiMoves);
+ if (outMove != null && blockingInMove == null && blockingPhiMove == null) {
+ it.remove();
+ outMove.from = inMove.from;
+ }
+ }
+ }
+
+ private void scheduleMovesBeforeInstruction(
+ int tempRegister, int instruction, InstructionListIterator insertAt) {
+ // Spill and restore moves for the incoming edge.
+ Set<SpillMove> inMoves =
+ instructionToInMoves.computeIfAbsent(instruction - 1, (k) -> new LinkedHashSet<>());
+ removeArgumentRestores(inMoves);
+
+ // Spill and restore moves for the outgoing edge.
+ Set<SpillMove> outMoves =
+ instructionToOutMoves.computeIfAbsent(instruction - 1, (k) -> new LinkedHashSet<>());
+ removeArgumentRestores(outMoves);
+
+ // Get the phi moves for this instruction and schedule them with the out going spill moves.
+ Set<SpillMove> phiMoves =
+ instructionToPhiMoves.computeIfAbsent(instruction - 1, (k) -> new LinkedHashSet<>());
+
+ // Remove/rewrite moves that we can guarantee will not be needed.
+ pruneParallelMoveSets(inMoves, outMoves, phiMoves);
+
+ // Schedule out and phi moves together.
+ outMoves.addAll(phiMoves);
+
+ // Perform parallel move scheduling independently for the in and out moves.
+ scheduleMoves(tempRegister, inMoves, insertAt);
+ scheduleMoves(tempRegister, outMoves, insertAt);
+ }
+
+ // Remove restore moves that restore arguments. Since argument register reuse is
+ // disallowed at this point we know that argument registers do not change value and
+ // therefore we don't have to perform spill moves. Performing spill moves will also
+ // make art reject the code because it loses type information for the argument.
+ private void removeArgumentRestores(Set<SpillMove> moves) {
+ Iterator<SpillMove> moveIterator = moves.iterator();
+ while (moveIterator.hasNext()) {
+ SpillMove move = moveIterator.next();
+ if (isArgumentRegister(move.to.getRegister())) {
+ moveIterator.remove();
+ }
+ }
+ }
+
+ private void scheduleMoves(
+ int tempRegister, Collection<SpillMove> moves, InstructionListIterator insertAt) {
+ RegisterMoveScheduler scheduler =
+ new RegisterMoveScheduler(insertAt, tempRegister);
+ for (SpillMove move : moves) {
+ // Do not generate moves to spill a value that can be rematerialized.
+ if (move.to.isSpilledAndRematerializable(allocator)) {
+ continue;
+ }
+ // Use rematerialization when possible and otherwise generate moves.
+ if (move.from.isSpilledAndRematerializable(allocator)) {
+ scheduler.addMove(
+ new RegisterMove(move.to.getRegister(), move.type, move.from.getValue().definition));
+ } else if (move.to.getRegister() != move.from.getRegister()) {
+ scheduler.addMove(
+ new RegisterMove(move.to.getRegister(), move.from.getRegister(), move.type));
+ }
+ }
+ scheduler.schedule();
+ usedTempRegisters = Math.max(usedTempRegisters, scheduler.getUsedTempRegisters());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
new file mode 100644
index 0000000..b8cbaa9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
@@ -0,0 +1,154 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jar;
+
+import static org.objectweb.asm.Opcodes.ASM5;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.JarApplicationReader;
+import com.android.tools.r8.graph.UseRegistry;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class JarRegisterEffectsVisitor extends MethodVisitor {
+ private final DexType clazz;
+ private final UseRegistry registry;
+ private final JarApplicationReader application;
+
+ public JarRegisterEffectsVisitor(DexType clazz, UseRegistry registry,
+ JarApplicationReader application) {
+ super(ASM5);
+ this.clazz = clazz;
+ this.registry = registry;
+ this.application = application;
+ }
+
+ @Override
+ public void visitTypeInsn(int opcode, String name) {
+ DexType type = application.getTypeFromName(name);
+ if (opcode == org.objectweb.asm.Opcodes.NEW) {
+ registry.registerNewInstance(type);
+ } else {
+ registry.registerTypeReference(type);
+ }
+ }
+
+ @Override
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ registry.registerTypeReference(application.getTypeFromDescriptor(desc));
+ }
+
+ @Override
+ public void visitLdcInsn(Object cst) {
+ if (cst instanceof Type) {
+ registry.registerTypeReference(application.getType((Type) cst));
+ }
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
+ DexType ownerType = application.getTypeFromName(owner);
+ DexMethod method = application.getMethod(ownerType, name, desc);
+ switch (opcode) {
+ case Opcodes.INVOKEVIRTUAL:
+ registry.registerInvokeVirtual(method);
+ break;
+ case Opcodes.INVOKESTATIC:
+ registry.registerInvokeStatic(method);
+ break;
+ case Opcodes.INVOKEINTERFACE:
+ registry.registerInvokeInterface(method);
+ break;
+ case Opcodes.INVOKESPECIAL:
+ if (name.equals(Constants.INSTANCE_INITIALIZER_NAME) || ownerType == clazz) {
+ registry.registerInvokeDirect(method);
+ } else {
+ registry.registerInvokeSuper(method);
+ }
+ break;
+ default:
+ throw new Unreachable("Unexpected opcode " + opcode);
+ }
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ DexField field = application.getField(owner, name, desc);
+ switch (opcode) {
+ case Opcodes.GETFIELD:
+ registry.registerInstanceFieldRead(field);
+ break;
+ case Opcodes.PUTFIELD:
+ registry.registerInstanceFieldWrite(field);
+ break;
+ case Opcodes.GETSTATIC:
+ registry.registerStaticFieldRead(field);
+ break;
+ case Opcodes.PUTSTATIC:
+ registry.registerStaticFieldWrite(field);
+ break;
+ default:
+ throw new Unreachable("Unexpected opcode " + opcode);
+ }
+ }
+
+ @Override
+ public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
+ registerMethodHandleType(bsm);
+
+ // Register bootstrap method arguments, only Type and MethodHandle need to be register.
+ for (Object arg : bsmArgs) {
+ if (arg instanceof Type && ((Type) arg).getSort() == Type.OBJECT) {
+ registry.registerTypeReference(application.getType((Type) arg));
+ } else if (arg instanceof Handle) {
+ registerMethodHandleType((Handle) arg);
+ }
+ }
+ }
+
+ private void registerMethodHandleType(Handle handle) {
+ switch (handle.getTag()) {
+ case Opcodes.H_GETFIELD:
+ visitFieldInsn(Opcodes.GETFIELD, handle.getOwner(), handle.getName(), handle.getDesc());
+ break;
+ case Opcodes.H_GETSTATIC:
+ visitFieldInsn(Opcodes.GETSTATIC, handle.getOwner(), handle.getName(), handle.getDesc());
+ break;
+ case Opcodes.H_PUTFIELD:
+ visitFieldInsn(Opcodes.PUTFIELD, handle.getOwner(), handle.getName(), handle.getDesc());
+ break;
+ case Opcodes.H_PUTSTATIC:
+ visitFieldInsn(Opcodes.PUTSTATIC, handle.getOwner(), handle.getName(), handle.getDesc());
+ break;
+ case Opcodes.H_INVOKEVIRTUAL:
+ visitMethodInsn(
+ Opcodes.INVOKEVIRTUAL, handle.getOwner(), handle.getName(), handle.getDesc(), false);
+ break;
+ case Opcodes.H_INVOKEINTERFACE:
+ visitMethodInsn(
+ Opcodes.INVOKEINTERFACE, handle.getOwner(), handle.getName(), handle.getDesc(), true);
+ break;
+ case Opcodes.H_INVOKESPECIAL:
+ visitMethodInsn(
+ Opcodes.INVOKESPECIAL, handle.getOwner(), handle.getName(), handle.getDesc(), false);
+ break;
+ case Opcodes.H_INVOKESTATIC:
+ visitMethodInsn(
+ Opcodes.INVOKESTATIC, handle.getOwner(), handle.getName(), handle.getDesc(), false);
+ break;
+ case Opcodes.H_NEWINVOKESPECIAL:
+ visitMethodInsn(
+ Opcodes.INVOKESPECIAL, handle.getOwner(), handle.getName(), handle.getDesc(), false);
+ break;
+ default:
+ throw new Unreachable("MethodHandle tag is not supported: " + handle.getTag());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/logging/Log.java b/src/main/java/com/android/tools/r8/logging/Log.java
new file mode 100644
index 0000000..8869bf0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/logging/Log.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.logging;
+
+public class Log {
+
+ static public final boolean ENABLED = false;
+
+ static private final boolean VERBOSE_ENABLED = false;
+ static private final boolean INFO_ENABLED = true;
+ static private final boolean DEBUG_ENABLED = true;
+ static private final boolean WARN_ENABLED = true;
+
+ public static void verbose(Class from, String message, Object... arguments) {
+ if (ENABLED && VERBOSE_ENABLED && isClassEnabled(from)) {
+ log("VERB", from, message, arguments);
+ }
+ }
+
+ public static void info(Class from, String message, Object... arguments) {
+ if (ENABLED && INFO_ENABLED && isClassEnabled(from)) {
+ log("INFO", from, message, arguments);
+ }
+ }
+
+ public static void debug(Class from, String message, Object... arguments) {
+ if (ENABLED && DEBUG_ENABLED && isClassEnabled(from)) {
+ log("DBG", from, message, arguments);
+ }
+ }
+
+ public static void warn(Class from, String message, Object... arguments) {
+ if (ENABLED && WARN_ENABLED && isClassEnabled(from)) {
+ log("WARN", from, message, arguments);
+ }
+ }
+
+ private static boolean isClassEnabled(Class clazz) {
+ return true;
+ }
+
+ synchronized private static void log(String kind, Class from, String message, Object... args) {
+ if (args.length > 0) {
+ message = String.format(message, args);
+ }
+ System.out.println("[" + kind + "] {" + from.getSimpleName() + "}: " + message);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
new file mode 100644
index 0000000..9e9a1ad
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -0,0 +1,164 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.function.Consumer;
+
+public class ClassNameMapper {
+
+ private final ImmutableMap<String, ClassNaming> classNameMappings;
+ private ImmutableBiMap<String, String> nameMapping;
+
+ private Hashtable<Signature, Signature> signatureMap = new Hashtable<>();
+
+ ClassNameMapper(Map<String, ClassNaming> classNameMappings) {
+ this.classNameMappings = ImmutableMap.copyOf(classNameMappings);
+ }
+
+ private Signature canonicalizeSignature(Signature signature) {
+ Signature result = signatureMap.get(signature);
+ if (result != null) {
+ return result;
+ }
+ signatureMap.put(signature, signature);
+ return signature;
+ }
+
+ public MethodSignature getRenamedMethodSignature(DexMethod method) {
+ DexType[] parameters = method.proto.parameters.values;
+ String[] parameterTypes = new String[parameters.length];
+ for (int i = 0; i < parameters.length; i++) {
+ parameterTypes[i] = deobfuscateType(parameters[i].toDescriptorString());
+ }
+ String returnType = deobfuscateType(method.proto.returnType.toDescriptorString());
+
+ MethodSignature signature = new MethodSignature(method.name.toString(), returnType,
+ parameterTypes);
+ return (MethodSignature) canonicalizeSignature(signature);
+ }
+
+ public Signature getRenamedFieldSignature(DexField field) {
+ String type = deobfuscateType(field.type.toDescriptorString());
+ return canonicalizeSignature(new FieldSignature(field.name.toString(), type));
+ }
+
+ public String deobfuscateClassName(String name) {
+ ClassNaming classNaming = classNameMappings.get(name);
+ if (classNaming == null) {
+ return null;
+ }
+ return classNaming.originalName;
+ }
+
+ private String deobfuscateType(String asString) {
+ return DescriptorUtils.descriptorToJavaType(asString, this);
+ }
+
+ public ClassNaming getClassNaming(String name) {
+ return classNameMappings.get(name);
+ }
+
+ public void write(Writer writer, boolean collapseRanges) throws IOException {
+ for (ClassNaming naming : classNameMappings.values()) {
+ naming.write(writer, collapseRanges);
+ }
+ }
+
+ public void forAllClassNamings(Consumer<ClassNaming> consumer) {
+ classNameMappings.values().forEach(consumer);
+ }
+
+ @Override
+ public String toString() {
+ try {
+ StringWriter writer = new StringWriter();
+ write(writer, false);
+ return writer.toString();
+ } catch (IOException e) {
+ return e.toString();
+ }
+ }
+
+ public BiMap<String, String> getObfuscatedToOriginalMapping() {
+ if (nameMapping == null) {
+ ImmutableBiMap.Builder<String, String> builder = ImmutableBiMap.builder();
+ for (String name : classNameMappings.keySet()) {
+ builder.put(name, classNameMappings.get(name).originalName);
+ }
+ nameMapping = builder.build();
+ }
+ return nameMapping;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ClassNameMapper
+ && classNameMappings.equals(((ClassNameMapper) o).classNameMappings);
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * classNameMappings.hashCode();
+ }
+
+ public String originalNameOf(IndexedDexItem item) {
+ if (item instanceof DexField) {
+ return lookupName(getRenamedFieldSignature((DexField) item), ((DexField) item).clazz);
+ } else if (item instanceof DexMethod) {
+ return lookupName(getRenamedMethodSignature((DexMethod) item), ((DexMethod) item).holder);
+ } else if (item instanceof DexType) {
+ return DescriptorUtils.descriptorToJavaType(((DexType) item).toDescriptorString(), this);
+ } else {
+ return item.toString();
+ }
+ }
+
+ private String lookupName(Signature signature, DexType clazz) {
+ String decoded = DescriptorUtils.descriptorToJavaType(clazz.descriptor.toString());
+ ClassNaming classNaming = getClassNaming(decoded);
+ if (classNaming == null) {
+ return decoded + " " + signature.toString();
+ }
+ MemberNaming memberNaming = classNaming.lookup(signature);
+ if (memberNaming == null) {
+ return classNaming.originalName + " " + signature.toString();
+ }
+ return classNaming.originalName + " " + memberNaming.signature.toString();
+ }
+
+ public Signature originalSignatureOf(DexMethod method) {
+ String decoded = DescriptorUtils
+ .descriptorToJavaType(method.holder.descriptor.toString());
+ MethodSignature memberSignature = getRenamedMethodSignature(method);
+ ClassNaming classNaming = getClassNaming(decoded);
+ if (classNaming == null) {
+ return memberSignature;
+ }
+ MemberNaming memberNaming = classNaming.lookup(memberSignature);
+ if (memberNaming == null) {
+ return memberSignature;
+ }
+ return memberNaming.signature;
+ }
+
+ public String originalNameOf(DexType clazz) {
+ return deobfuscateType(clazz.descriptor.toString());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
new file mode 100644
index 0000000..de80338
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -0,0 +1,171 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ClassNameMinifier {
+
+ /**
+ * We ban classes from these prefixes from minification. This is needed as some classes
+ * in the android sdk are given a @hide annotation, which will remove them from the
+ * sdk we tree-shake and minify against. Thus, the class will not be available and hence
+ * we won't find out that it is a library class.
+ * To save space, we by default minify classes we do not have an implementation for.
+ */
+ private static final Set<String> BANNED_CLASS_PREFIXES = ImmutableSet
+ .of("Ljava", "Landroid", "Ldalvik");
+
+ private final AppInfoWithLiveness appInfo;
+ private final RootSet rootSet;
+ private final String packagePrefix;
+ private final Set<DexString> usedTypeNames = Sets.newIdentityHashSet();
+
+ private final Map<DexType, DexString> renaming = Maps.newIdentityHashMap();
+ private final Map<String, NamingState> states = new HashMap<>();
+ final List<String> dictionary;
+
+ public ClassNameMinifier(AppInfoWithLiveness appInfo, RootSet rootSet, String packagePrefix,
+ List<String> dictionary) {
+ this.appInfo = appInfo;
+ this.rootSet = rootSet;
+ this.packagePrefix = packagePrefix;
+ this.dictionary = dictionary;
+ }
+
+ public Map<DexType, DexString> computeRenaming() {
+ // Collect names we have to keep.
+ for (DexClass clazz : appInfo.classes()) {
+ if (rootSet.noObfuscation.contains(clazz)) {
+ assert !renaming.containsKey(clazz.type);
+ renaming.put(clazz.type, clazz.type.descriptor);
+ usedTypeNames.add(clazz.type.descriptor);
+ }
+ }
+ for (DexClass clazz : appInfo.classes()) {
+ if (!renaming.containsKey(clazz.type)) {
+ String packageName = getPackageNameFor(clazz);
+ NamingState state = getStateFor(packageName);
+ renaming.put(clazz.type, state.nextTypeName());
+ }
+ }
+ renameTypesInProtosOf(appInfo.staticInvokes);
+ renameTypesInProtosOf(appInfo.superInvokes);
+ renameTypesInProtosOf(appInfo.directInvokes);
+ renameTypesInProtosOf(appInfo.virtualInvokes);
+ appInfo.dexItemFactory.forAllTypes(this::renameArrayTypeIfNeeded);
+
+ return Collections.unmodifiableMap(renaming);
+ }
+
+ private String getPackageNameFor(DexClass clazz) {
+ if (packagePrefix == null || rootSet.keepPackageName.contains(clazz)) {
+ return clazz.type.getPackageDescriptor();
+ } else {
+ return packagePrefix;
+ }
+ }
+
+ private NamingState getStateFor(String packageName) {
+ return states.computeIfAbsent(packageName, NamingState::new);
+ }
+
+ private void renameTypesInProtosOf(Iterable<DexMethod> methods) {
+ for (DexMethod method : methods) {
+ renameTypeWithoutClassDefinition(method.proto.returnType);
+ for (DexType type : method.proto.parameters.values) {
+ renameTypeWithoutClassDefinition(type);
+ }
+ }
+ }
+
+ private void renameTypeWithoutClassDefinition(DexType type) {
+ if (type.isArrayType()) {
+ type = type.toBaseType(appInfo.dexItemFactory);
+ }
+ if (type.isClassType() && !renaming.containsKey(type)) {
+ DexClass clazz = appInfo.definitionFor(type);
+ if (clazz == null || !clazz.isLibraryClass()) {
+ if (!classIsBannedFromRenaming(type)) {
+ String packageName = packagePrefix == null ? type.getPackageDescriptor() : packagePrefix;
+ NamingState state = getStateFor(packageName);
+ renaming.put(type, state.nextTypeName());
+ }
+ }
+ }
+ }
+
+ private boolean classIsBannedFromRenaming(DexType type) {
+ String desc = type.toDescriptorString();
+ int index = desc.indexOf('/');
+ String prefix = desc.substring(0, index);
+ return index != -1 && BANNED_CLASS_PREFIXES.contains(prefix);
+ }
+
+ private void renameArrayTypeIfNeeded(DexType type) {
+ if (type.isArrayType()) {
+ DexType base = type.toBaseType(appInfo.dexItemFactory);
+ DexString value = renaming.get(base);
+ if (value != null) {
+ int dimensions = type.descriptor.numberOfLeadingSquareBrackets();
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < dimensions; i++) {
+ builder.append('[');
+ }
+ builder.append(value.toString());
+ DexString descriptor = appInfo.dexItemFactory.createString(builder.toString());
+ renaming.put(type, descriptor);
+ }
+ }
+ }
+
+ private class NamingState {
+
+ private final char[] packagePrefix;
+ private int typeCounter = 1;
+ private Iterator<String> dictionaryIterator;
+
+ NamingState(String packageName) {
+ this.packagePrefix = ("L" + packageName + (packageName.isEmpty() ? "" : "/")).toCharArray();
+ this.dictionaryIterator = dictionary.iterator();
+ }
+
+ public char[] getPackagePrefix() {
+ return packagePrefix;
+ }
+
+ protected String nextSuggestedName() {
+ StringBuilder nextName = new StringBuilder();
+ if (dictionaryIterator.hasNext()) {
+ nextName.append(getPackagePrefix()).append(dictionaryIterator.next()).append(';');
+ return nextName.toString();
+ } else {
+ return StringUtils.numberToIdentifier(packagePrefix, typeCounter++, true);
+ }
+ }
+
+ private DexString nextTypeName() {
+ DexString candidate;
+ do {
+ candidate = appInfo.dexItemFactory.createString(nextSuggestedName());
+ } while (usedTypeNames.contains(candidate));
+ return candidate;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNaming.java b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
new file mode 100644
index 0000000..a00455f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * Stores name information for a class.
+ * <p>
+ * This includes how the class was renamed and information on the classes members.
+ */
+public class ClassNaming {
+
+ public final String originalName;
+ final String renamedName;
+
+ /**
+ * Mapping from the renamed signature to the naming information for a member.
+ * <p>
+ * A renamed signature is a signature where the member's name has been obfuscated but not the type
+ * information.
+ **/
+ final Map<Signature, MemberNaming> members = new LinkedHashMap<>();
+
+ ClassNaming(String renamedName, String originalName) {
+ this.renamedName = renamedName;
+ this.originalName = originalName;
+ }
+
+ void addMemberEntry(MemberNaming entry) {
+ Signature renamedSignature = entry.renamedSignature;
+ members.put(renamedSignature, entry);
+ }
+
+ public MemberNaming lookup(Signature renamedSignature) {
+ return members.get(renamedSignature);
+ }
+
+ public MemberNaming lookupByOriginalSignature(Signature original) {
+ for (MemberNaming naming : members.values()) {
+ if (naming.signature.equals(original)) {
+ return naming;
+ }
+ }
+ return null;
+ }
+
+ public void forAllMemberNaming(Consumer<MemberNaming> consumer) {
+ members.values().forEach(consumer);
+ }
+
+ void write(Writer writer, boolean collapseRanges) throws IOException {
+ writer.append(originalName);
+ writer.append(" -> ");
+ writer.append(renamedName);
+ writer.append(":\n");
+ for (MemberNaming member : members.values()) {
+ member.write(writer, collapseRanges, true);
+ }
+ }
+
+ @Override
+ public String toString() {
+ try {
+ StringWriter writer = new StringWriter();
+ write(writer, false);
+ return writer.toString();
+ } catch (IOException e) {
+ return e.toString();
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof ClassNaming)) {
+ return false;
+ }
+
+ ClassNaming that = (ClassNaming) o;
+
+ return originalName.equals(that.originalName)
+ && renamedName.equals(that.renamedName)
+ && members.equals(that.members);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = originalName.hashCode();
+ result = 31 * result + renamedName.hashCode();
+ result = 31 * result + members.hashCode();
+ return result;
+ }
+}
+
diff --git a/src/main/java/com/android/tools/r8/naming/ClassRenamingMapper.java b/src/main/java/com/android/tools/r8/naming/ClassRenamingMapper.java
new file mode 100644
index 0000000..6b283f0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ClassRenamingMapper.java
@@ -0,0 +1,98 @@
+package com.android.tools.r8.naming;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+
+/**
+ * Provides a translation between class names based on a source and target proguard map.
+ *
+ * A mapping consists of:
+ *
+ * <ul>
+ * <li> {@link #translation} a bidirectional mapping between obfuscated names on the source
+ * proguard map to the corresponding class in the target proguard map
+ * <li> {@link #newClasses} a set of the unobfuscated names of classes that are in the source but
+ * not the target map
+ * <li> {@link #unusedNames} a set of names in the target map that are not used by the source map
+ * </ul>
+ */
+public class ClassRenamingMapper {
+
+ public static ClassRenamingMapper from(ClassNameMapper originalMap, ClassNameMapper targetMap) {
+ ImmutableBiMap.Builder<String, String> translationBuilder = ImmutableBiMap.builder();
+ ImmutableSet.Builder<String> newClasses = ImmutableSet.builder();
+
+ BiMap<String, String> sourceObfuscatedToOriginal = originalMap.getObfuscatedToOriginalMapping();
+ BiMap<String, String> sourceOriginalToObfuscated = sourceObfuscatedToOriginal.inverse();
+ BiMap<String, String> targetObfuscatedToOriginal = targetMap.getObfuscatedToOriginalMapping();
+ BiMap<String, String> targetOriginalToObfuscated = targetObfuscatedToOriginal.inverse();
+
+ for (String originalName : sourceOriginalToObfuscated.keySet()) {
+ String sourceObfuscatedName = sourceOriginalToObfuscated.get(originalName);
+ String targetObfuscatedName = targetOriginalToObfuscated.get(originalName);
+ if (targetObfuscatedName == null) {
+ newClasses.add(originalName);
+ continue;
+ }
+ translationBuilder.put(sourceObfuscatedName, targetObfuscatedName);
+ }
+
+ ImmutableBiMap<String, String> translation = translationBuilder.build();
+ ImmutableSet<String> unusedNames = ImmutableSet
+ .copyOf(Sets.difference(targetObfuscatedToOriginal.keySet(), translation.values()));
+
+ return new ClassRenamingMapper(translation, newClasses.build(), unusedNames);
+ }
+
+ /**
+ * Mapping from obfuscated class names in the source map to their counterpart in the target name
+ * map.
+ */
+ public final ImmutableBiMap<String, String> translation;
+
+ /**
+ * Set of (unobfuscated) class names that are present in the source map but not in the target map.
+ */
+ public final ImmutableSet<String> newClasses;
+
+ /**
+ * Set of (obfuscated) class names that are present in the target map but not in the source map.
+ */
+ public final ImmutableSet<String> unusedNames;
+
+ private ClassRenamingMapper(ImmutableBiMap<String, String> translation,
+ ImmutableSet<String> newClasses, ImmutableSet<String> unusedNames) {
+ this.translation = translation;
+ this.newClasses = newClasses;
+ this.unusedNames = unusedNames;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Translation:\n\n");
+ for (String name : translation.keySet()) {
+ String newName = translation.get(name);
+ builder.append(name.equals(newName) ? " " : " --- ")
+ .append(name)
+ .append(" -> ")
+ .append(newName)
+ .append('\n');
+ }
+ builder.append("\nNew classes:\n\n");
+ for (String name : newClasses) {
+ builder.append(" ")
+ .append(name)
+ .append('\n');
+ }
+ builder.append("\nUnused names:\n\n");
+ for (String unused : unusedNames) {
+ builder.append(" ")
+ .append(unused)
+ .append('\n');
+ }
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/DictionaryReader.java b/src/main/java/com/android/tools/r8/naming/DictionaryReader.java
new file mode 100644
index 0000000..6c1e8e5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/DictionaryReader.java
@@ -0,0 +1,72 @@
+// 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.naming;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+public class DictionaryReader implements AutoCloseable {
+
+ private BufferedReader reader;
+
+ public DictionaryReader(Path path) throws IOException {
+ this.reader = Files.newBufferedReader(path);
+ }
+
+ public String readName() throws IOException {
+ assert reader != null;
+
+ StringBuilder name = new StringBuilder();
+ int readCharAsInt;
+
+ while ((readCharAsInt = reader.read()) != -1) {
+ char readChar = (char) readCharAsInt;
+
+ if ((name.length() != 0 && Character.isJavaIdentifierPart(readChar))
+ || (name.length() == 0 && Character.isJavaIdentifierStart(readChar))) {
+ name.append(readChar);
+ } else {
+ if (readChar == '#') {
+ reader.readLine();
+ }
+
+ if (name.length() != 0) {
+ return name.toString();
+ }
+ }
+ }
+
+ return name.toString();
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (reader != null) {
+ reader.close();
+ }
+ }
+
+ public static List<String> readAllNames(Path path) {
+ if (path != null) {
+ Builder<String> namesBuilder = new ImmutableList.Builder<String>();
+ try (DictionaryReader reader = new DictionaryReader(path);) {
+ String name = reader.readName();
+ while (!name.isEmpty()) {
+ namesBuilder.add(name);
+ name = reader.readName();
+ }
+ } catch (IOException e) {
+ System.err.println("Unable to create dictionary from file " + path.toString());
+ }
+ return namesBuilder.build();
+ } else {
+ return ImmutableList.of();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
new file mode 100644
index 0000000..3a72512
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -0,0 +1,88 @@
+// 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.naming;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class FieldNameMinifier {
+
+ private final AppInfoWithSubtyping appInfo;
+ private final RootSet rootSet;
+ private final Map<DexField, DexString> renaming = new IdentityHashMap<>();
+ private final List<String> dictionary;
+ private final Map<DexType, NamingState<DexType>> states = new IdentityHashMap<>();
+
+ public FieldNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, List<String> dictionary) {
+ this.appInfo = appInfo;
+ this.rootSet = rootSet;
+ this.dictionary = dictionary;
+ }
+
+ Map<DexField, DexString> computeRenaming() {
+ NamingState<DexType> rootState = NamingState.createRoot(appInfo.dexItemFactory, dictionary);
+ // Reserve names in all classes first. We do this in subtyping order so we do not
+ // shadow a reserved field in subclasses. While there is no concept of virtual field
+ // dispatch in Java, field resolution still traverses the super type chain and external
+ // code might use a subtype to reference the field.
+ reserveNamesInSubtypes(appInfo.dexItemFactory.objectType, rootState);
+ // Next, reserve field names in interfaces. These should only be static.
+ DexType.forAllInterfaces(appInfo.dexItemFactory,
+ iface -> reserveNamesInSubtypes(iface, rootState));
+ // Now rename the rest.
+ renameFieldsInSubtypes(appInfo.dexItemFactory.objectType);
+ DexType.forAllInterfaces(appInfo.dexItemFactory, this::renameFieldsInSubtypes);
+ return renaming;
+ }
+
+ private void reserveNamesInSubtypes(DexType type, NamingState<DexType> state) {
+ DexClass holder = appInfo.definitionFor(type);
+ if (holder == null) {
+ return;
+ }
+ NamingState<DexType> newState = states.computeIfAbsent(type, t -> state.createChild());
+ reserveFieldNames(newState, holder.instanceFields(), holder.isLibraryClass());
+ reserveFieldNames(newState, holder.staticFields(), holder.isLibraryClass());
+ type.forAllExtendsSubtypes(subtype -> reserveNamesInSubtypes(subtype, newState));
+ }
+
+ private void reserveFieldNames(NamingState<DexType> state, DexEncodedField[] fields,
+ boolean isLibrary) {
+ for (DexEncodedField encodedField : fields) {
+ if (isLibrary || rootSet.noObfuscation.contains(encodedField)) {
+ DexField field = encodedField.field;
+ state.reserveName(field.name, field.type);
+ }
+ }
+ }
+
+ private void renameFieldsInSubtypes(DexType type) {
+ DexClass clazz = appInfo.definitionFor(type);
+ if (clazz == null) {
+ return;
+ }
+ NamingState<DexType> state = states.get(clazz.type);
+ assert state != null;
+ renameFields(clazz.instanceFields(), state);
+ renameFields(clazz.staticFields(), state);
+ type.forAllExtendsSubtypes(this::renameFieldsInSubtypes);
+ }
+
+ private void renameFields(DexEncodedField[] fields, NamingState<DexType> state) {
+ for (DexEncodedField encodedField : fields) {
+ DexField field = encodedField.field;
+ if (!state.isReserved(field.name, field.type)) {
+ renaming.put(field, state.assignNewNameFor(field.name, field.type, false));
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
new file mode 100644
index 0000000..9c45617
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -0,0 +1,468 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Stores renaming information for a member.
+ * <p>
+ * This includes the signature, the original name and inlining range information.
+ */
+public class MemberNaming {
+
+ private static final int UNDEFINED_START_NUMBER = -1;
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof MemberNaming)) {
+ return false;
+ }
+
+ MemberNaming that = (MemberNaming) o;
+ return signature.equals(that.signature)
+ && renamedSignature.equals(that.renamedSignature)
+ && topLevelRange.equals(that.topLevelRange)
+ && inlineInformation.equals(that.inlineInformation);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = signature.hashCode();
+ result = 31 * result + renamedSignature.hashCode();
+ result = 31 * result + inlineInformation.hashCode();
+ result = 31 * result + topLevelRange.hashCode();
+ return result;
+ }
+
+ /**
+ * Original signature of the member
+ */
+ final Signature signature;
+ /**
+ * Renamed signature where the name (but not the types) have been renamed.
+ */
+ final Signature renamedSignature;
+ public final List<InlineInformation> inlineInformation = new LinkedList<>();
+ public final Range topLevelRange;
+
+ private int collapsedStartLineNumber = UNDEFINED_START_NUMBER;
+ private int originalStartLineNumber = UNDEFINED_START_NUMBER;
+
+ MemberNaming(Signature signature, String renamedName, Range inlinedLineRange) {
+ this.signature = signature;
+ this.renamedSignature = signature.asRenamed(renamedName);
+ topLevelRange = inlinedLineRange == null ? fakeZeroRange : inlinedLineRange;
+ }
+
+ public void addInliningRange(Range inlinedRange, Signature signature, Range originalRange) {
+ inlineInformation.add(new InlineInformation(inlinedRange, originalRange, signature));
+ }
+
+ public List<Range> getInlineRanges() {
+ List<Range> inlineRanges = new ArrayList<>();
+ for (InlineInformation information : inlineInformation) {
+ if (information.isActualInlining()) {
+ inlineRanges.add(information.inlinedRange);
+ }
+ }
+ return inlineRanges;
+ }
+
+ public Signature getOriginalSignature() {
+ return signature;
+ }
+
+ public String getRenamedName() {
+ return renamedSignature.name;
+ }
+
+ public void setCollapsedStartLineNumber(int value) {
+ assert collapsedStartLineNumber == UNDEFINED_START_NUMBER;
+ collapsedStartLineNumber = value;
+ }
+
+ public boolean isMethodNaming() {
+ return signature.kind() == SignatureKind.METHOD;
+ }
+
+ private int getCollapsedStartLineNumber() {
+ return collapsedStartLineNumber;
+ }
+
+ protected void write(Writer writer, boolean collapseRanges, boolean indent) throws IOException {
+ if (indent) {
+ writer.append(" ");
+ }
+ int rangeCounter =
+ collapseRanges ? getCollapsedStartLineNumber() : InlineInformation.DO_NOT_COLLAPSE;
+ // Avoid printing the range information if there was none in the original file.
+ if (topLevelRange != fakeZeroRange || rangeCounter != UNDEFINED_START_NUMBER) {
+ if (collapseRanges) {
+ // Skip ranges for methods that are used only once, as they do not have debug information.
+ if (rangeCounter != UNDEFINED_START_NUMBER) {
+ String rangeString = Integer.toString(rangeCounter);
+ writer.append(rangeString).append(":").append(rangeString).append(":");
+ } else {
+ rangeCounter = 0;
+ }
+ } else {
+ writer.append(topLevelRange.toString());
+ writer.append(':');
+ }
+ } else {
+ // We might end up in a case where we have no line information for the top entry but still
+ // have inline ranges. Just to be sure, set rangeCounter to a useful value.
+ if (collapseRanges) {
+ rangeCounter = 0;
+ }
+ }
+ signature.write(writer);
+ if (originalStartLineNumber != UNDEFINED_START_NUMBER) {
+ // If we have original line number information, print it here.
+ String originalSourceLineString = Integer.toString(originalStartLineNumber);
+ writer.append(':')
+ .append(originalSourceLineString)
+ .append(':')
+ .append(originalSourceLineString);
+ }
+ writer.append(" -> ");
+ writer.append(renamedSignature.name);
+ writer.append("\n");
+ for (InlineInformation information : inlineInformation) {
+ assert !collapseRanges || rangeCounter >= 0;
+ if (collapseRanges && information.isActualInlining()) {
+ rangeCounter++;
+ }
+ information.write(writer, rangeCounter, indent);
+ }
+ }
+
+ @Override
+ public String toString() {
+ try {
+ StringWriter writer = new StringWriter();
+ write(writer, false, false);
+ return writer.toString();
+ } catch (IOException e) {
+ return e.toString();
+ }
+ }
+
+ public void setOriginalStartLineNumber(int originalStartLineNumber) {
+ assert this.originalStartLineNumber == UNDEFINED_START_NUMBER;
+ this.originalStartLineNumber = originalStartLineNumber;
+ }
+
+ public abstract static class Signature {
+
+ public final String name;
+
+ protected Signature(String name) {
+ this.name = name;
+ }
+
+ abstract Signature asRenamed(String renamedName);
+
+ abstract public SignatureKind kind();
+
+ @Override
+ abstract public boolean equals(Object o);
+
+ @Override
+ abstract public int hashCode();
+
+ abstract void write(Writer builder) throws IOException;
+
+ @Override
+ public String toString() {
+ try {
+ StringWriter writer = new StringWriter();
+ write(writer);
+ return writer.toString();
+ } catch (IOException e) {
+ return e.toString();
+ }
+ }
+
+ enum SignatureKind {
+ METHOD,
+ FIELD
+ }
+ }
+
+ public static class FieldSignature extends Signature {
+
+ public final String type;
+
+ public FieldSignature(String name, String type) {
+ super(name);
+ this.type = type;
+ }
+
+ public static FieldSignature fromDexField(DexField field) {
+ return new FieldSignature(field.name.toSourceString(),
+ field.type.toSourceString());
+ }
+
+ @Override
+ Signature asRenamed(String renamedName) {
+ return new FieldSignature(renamedName, type);
+ }
+
+ @Override
+ public SignatureKind kind() {
+ return SignatureKind.FIELD;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof FieldSignature)) {
+ return false;
+ }
+ FieldSignature that = (FieldSignature) o;
+ return name.equals(that.name) && type.equals(that.type);
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode() * 31 + type.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return type + " " + name;
+ }
+
+ @Override
+ void write(Writer writer) throws IOException {
+ writer.append(type);
+ writer.append(' ');
+ writer.append(name);
+ }
+ }
+
+ public static class MethodSignature extends Signature {
+
+ public final String type;
+ public final String[] parameters;
+
+ public MethodSignature(String name, String type, String[] parameters) {
+ super(name);
+ this.type = type;
+ this.parameters = parameters;
+ }
+
+ public static MethodSignature fromDexMethod(DexMethod method) {
+ String[] paramNames = new String[method.proto.parameters.values.length];
+ DexType[] values = method.proto.parameters.values;
+ for (int i = 0; i < values.length; i++) {
+ paramNames[i] = values[i].toSourceString();
+ }
+ return new MethodSignature(method.name.toSourceString(),
+ method.proto.returnType.toSourceString(), paramNames);
+ }
+
+ @Override
+ Signature asRenamed(String renamedName) {
+ return new MethodSignature(renamedName, type, parameters);
+ }
+
+ @Override
+ public SignatureKind kind() {
+ return SignatureKind.METHOD;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof MethodSignature)) {
+ return false;
+ }
+
+ MethodSignature that = (MethodSignature) o;
+ return type.equals(that.type)
+ && name.equals(that.name)
+ && Arrays.equals(parameters, that.parameters);
+ }
+
+ @Override
+ public int hashCode() {
+ return (type.hashCode() * 17
+ + name.hashCode()) * 31
+ + Arrays.hashCode(parameters);
+ }
+
+ @Override
+ void write(Writer writer) throws IOException {
+ writer.append(type)
+ .append(' ')
+ .append(name)
+ .append('(');
+ for (int i = 0; i < parameters.length; i++) {
+ writer.append(parameters[i]);
+ if (i < parameters.length - 1) {
+ writer.append(',');
+ }
+ }
+ writer.append(')');
+ }
+ }
+
+ public class InlineInformation {
+ static final int DO_NOT_COLLAPSE = -1;
+
+ public final Range inlinedRange;
+ public final Range originalRange;
+ public final Signature signature;
+
+ public InlineInformation(Range inlinedRange, Range originalRange, Signature signature) {
+ this.inlinedRange = inlinedRange;
+ this.originalRange = originalRange;
+ this.signature = signature;
+ }
+
+ public boolean isActualInlining() {
+ return !(originalRange instanceof SingleLineRange);
+ }
+
+ public void write(Writer writer, int collapsedRange, boolean indent) throws IOException {
+ if (indent) {
+ writer.append(" ");
+ }
+ if (collapsedRange == DO_NOT_COLLAPSE) {
+ writer.append(inlinedRange.toString());
+ } else {
+ writer.append(Range.toCollapsedString(collapsedRange));
+ }
+ writer.append(":");
+ signature.write(writer);
+ if (originalRange != null) {
+ writer.append(':')
+ .append(originalRange.toString());
+ }
+ writer.append(" -> ")
+ .append(renamedSignature.name);
+ writer.append("\n");
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof InlineInformation)) {
+ return false;
+ }
+
+ InlineInformation that = (InlineInformation) o;
+
+ return inlinedRange.equals(that.inlinedRange)
+ && ((originalRange == null && that.originalRange == null)
+ || originalRange.equals(that.originalRange))
+ && signature.equals(that.signature);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = inlinedRange.hashCode();
+ result = 31 * result + originalRange.hashCode();
+ result = 31 * result + signature.hashCode();
+ return result;
+ }
+ }
+
+ /**
+ * Represents a linenumber range.
+ */
+ public static class Range {
+
+ public final int from;
+ public final int to;
+
+ Range(int from, int to) {
+ this.from = from;
+ this.to = to;
+ }
+
+ public boolean contains(int value) {
+ return value >= from && value <= to;
+ }
+
+ public boolean isSingle() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return from + ":" + to;
+ }
+
+ public static String toCollapsedString(int value) {
+ return value + ":" + value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Range)) {
+ return false;
+ }
+
+ Range range = (Range) o;
+ return from == range.from && to == range.to;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = from;
+ result = 31 * result + to;
+ return result;
+ }
+
+ }
+
+ /**
+ * Represents a single linenumber range (':' followed by a signle number), which
+ * is different semantically from a normal range that has the same from and to.
+ */
+ public static class SingleLineRange extends Range {
+ public SingleLineRange(int fromAndTo) {
+ super(fromAndTo, fromAndTo);
+ }
+
+ @Override
+ public boolean isSingle() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(from);
+ }
+ }
+
+ public final static Range fakeZeroRange = new Range(0, 0);
+}
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
new file mode 100644
index 0000000..d7b17c6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -0,0 +1,355 @@
+// 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.naming;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A pass to rename methods using common, short names.
+ * <p>
+ * To assign names, we model the scopes of methods names and overloading/shadowing based on the
+ * subtyping tree of classes. Such a naming scope is encoded by {@link NamingState}. It keeps
+ * track of its parent node, names that have been reserved (due to keep annotations or otherwise)
+ * and what names have been used for renaming so far.
+ * <p>
+ * As in the Dalvik VM method dispatch takes argument and return types of methods into account, we
+ * can further reuse names if the prototypes of two methods differ. For this, we store the above
+ * state separately for each proto using a map from protos to {@link NamingState.InternalState}
+ * objects. These internal state objects are also linked.
+ * <p>
+ * Name assignment happens in 4 stages. In the first stage, we record all names that are used by
+ * library classes or are flagged using a keep rule as reserved. This step also allocates the
+ * {@link NamingState} objects for library classes. We can fully allocate these objects as we
+ * never perform naming for library classes. For non-library classes, we only allocate a state
+ * for the highest non-library class, i.e., we allocate states for every direct subtype of a library
+ * class. The states at the boundary between library and program classes are referred to as the
+ * frontier states in the code.
+ * <p>
+ * When reserving names in program classes, we reserve them in the state of the corresponding
+ * frontier class. This is to ensure that the names are not used for renaming in any supertype.
+ * Thus, they will still be available in the subtype where they are reserved. Note that name
+ * reservation only blocks names from being used for minification. We assume that the input program
+ * is correctly named.
+ * <p>
+ * In stage 2, we reserve names that stem from interfaces. These are not propagated to
+ * subinterfaces or implementing classes. Instead, stage 3 makes sure to query related states when
+ * making naming decisions.
+ * <p>
+ * In stage 3, we compute minified names for all interface methods. We do this first to reduce
+ * assignment conflicts. Interfaces do not build a tree-like inheritance structure we can exploit.
+ * Thus, we have to infer the structure on the fly. For this, we compute a sets of reachable
+ * interfaces. i.e., interfaces that are related via subtyping. Based on these sets, we then
+ * find, for each method signature, the classes and interfaces this method signature is defined in.
+ * For classes, as we still use frontier states at this point, we do not have to consider subtype
+ * relations. For interfaces, we reserve the name in all reachable interfaces and thus ensure
+ * availability.
+ * <p>
+ * Name assignment in this phase is a search over all impacted naming states. Using the naming state
+ * of the interface this method first originated from, we propose names until we find a matching
+ * one. We use the naming state of the interface to not impact name availability in naming states of
+ * classes. Hence, skipping over names during interface naming does not impact their availability in
+ * the next phase.
+ * <p>
+ * In the final stage, we assign names to methods by traversing the subtype tree, now allocating
+ * separate naming states for each class starting from the frontier. In the first swoop, we allocate
+ * all non-private methods, updating naming states accordingly. In a second swoop, we then allocate
+ * private methods, as those may safely use names that are used by a public method further down in
+ * the subtyping tree.
+ * <p>
+ * Finally, the computed renamings are returned as a map from {@link DexMethod} to
+ * {@link DexString}. The MethodNameMinifier object should not be retained to ensure all
+ * intermediate state is freed.
+ * <p>
+ * TODO(herhut): Currently, we do not minify members of annotation interfaces, as this would require
+ * parsing and minification of the string arguments to annotations.
+ */
+public class MethodNameMinifier {
+
+ private final AppInfoWithSubtyping appInfo;
+ private final RootSet rootSet;
+ private final Map<DexType, NamingState<DexProto>> states = new IdentityHashMap<>();
+ private final NamingState<DexProto> globalState;
+ private MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
+ private final List<String> dictionary;
+
+ public MethodNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet,
+ List<String> dictionary) {
+ this.appInfo = appInfo;
+ this.rootSet = rootSet;
+ this.dictionary = dictionary;
+ this.globalState = NamingState.createRoot(appInfo.dexItemFactory, dictionary);
+ }
+
+ public Map<DexMethod, DexString> computeRenaming(Timing timing) {
+ // Phase 1: Reserve all the names that need to be kept and allocate linked state in the
+ // library part.
+ timing.begin("Phase 1");
+ Map<DexType, DexType> frontierMap = new IdentityHashMap<>();
+ reserveNamesInClasses(appInfo.dexItemFactory.objectType,
+ appInfo.dexItemFactory.objectType,
+ null, frontierMap);
+ timing.end();
+ // Phase 2: Reserve all the names that are required for interfaces.
+ timing.begin("Phase 2");
+ DexType.forAllInterfaces(appInfo.dexItemFactory, iface -> {
+ reserveNamesInInterfaces(iface, frontierMap);
+ });
+ timing.end();
+ // Phase 3: Assign names to interface methods. These are assigned by finding a name that is
+ // free in all naming states that may hold an implementation.
+ timing.begin("Phase 3");
+ Map<DexMethod, DexString> renaming = new IdentityHashMap<>();
+ assignNamesToInterfaceMethods(frontierMap, renaming, timing);
+ timing.end();
+ // Phase 4: Assign names top-down by traversing the subtype hierarchy.
+ timing.begin("Phase 4");
+ assignNamesToClassesMethods(appInfo.dexItemFactory.objectType, false, renaming);
+ timing.end();
+ // Phase 4: Do the same for private methods.
+ timing.begin("Phase 5");
+ assignNamesToClassesMethods(appInfo.dexItemFactory.objectType, true, renaming);
+ timing.end();
+
+ return renaming;
+ }
+
+ private void assignNamesToClassesMethods(DexType type, boolean doPrivates,
+ Map<DexMethod, DexString> renaming) {
+ DexClass holder = appInfo.definitionFor(type);
+ if (holder != null && !holder.isLibraryClass()) {
+ NamingState<DexProto> state = states
+ .computeIfAbsent(type, k -> states.get(holder.superType).createChild());
+ assignNamesToMethods(holder.directMethods(), state, doPrivates, renaming);
+ assignNamesToMethods(holder.virtualMethods(), state, doPrivates, renaming);
+ }
+ type.forAllExtendsSubtypes(
+ subtype -> assignNamesToClassesMethods(subtype, doPrivates, renaming));
+ }
+
+ private void assignNamesToMethods(DexEncodedMethod[] methods,
+ NamingState<DexProto> state, boolean doPrivates, Map<DexMethod, DexString> renaming) {
+ for (DexEncodedMethod encodedMethod : methods) {
+ if (encodedMethod.accessFlags.isPrivate() != doPrivates) {
+ continue;
+ }
+ DexMethod method = encodedMethod.method;
+ if (!state.isReserved(method.name, method.proto)
+ && !encodedMethod.accessFlags.isConstructor()) {
+ renaming.put(method, state.assignNewNameFor(method.name, method.proto, !doPrivates));
+ }
+ }
+ }
+
+ private Set<NamingState<DexProto>> getReachableStates(DexType type,
+ Map<DexType, DexType> frontierMap) {
+ Set<DexType> interfaces = Sets.newIdentityHashSet();
+ interfaces.add(type);
+ collectSuperInterfaces(type, interfaces);
+ collectSubInterfaces(type, interfaces);
+ Set<NamingState<DexProto>> reachableStates = new HashSet<>();
+ for (DexType iface : interfaces) {
+ // Add the interface itself
+ reachableStates.add(states.get(iface));
+ // And the frontiers that correspond to the classes that implement the interface.
+ iface.forAllImplementsSubtypes(t -> {
+ NamingState<DexProto> state = states.get(frontierMap.get(t));
+ assert state != null;
+ reachableStates.add(state);
+ });
+ }
+ return reachableStates;
+ }
+
+ private void assignNamesToInterfaceMethods(Map<DexType, DexType> frontierMap,
+ Map<DexMethod, DexString> renaming, Timing timing) {
+ // First compute a map from method signatures to a set of naming states for interfaces and
+ // frontier states of classes that implement them. We add the frontier states so that we can
+ // reserve the names for later method naming.
+ timing.begin("Compute map");
+ // A map from DexMethods to all the states linked to interfaces they appear in.
+ Map<Wrapper<DexMethod>, Set<NamingState<DexProto>>> globalStateMap = new HashMap<>();
+ // A map from DexMethods to all the definitions seen. Needed as the Wrapper equalizes them all.
+ Map<Wrapper<DexMethod>, Set<DexMethod>> sourceMethodsMap = new HashMap<>();
+ // A map from DexMethods to the first interface state it was seen in. Used to pick good names.
+ Map<Wrapper<DexMethod>, NamingState<DexProto>> originStates = new HashMap<>();
+ DexType.forAllInterfaces(appInfo.dexItemFactory, iface -> {
+ assert iface.isInterface();
+ DexClass clazz = appInfo.definitionFor(iface);
+ if (clazz != null) {
+ Set<NamingState<DexProto>> collectedStates = getReachableStates(iface, frontierMap);
+ addStatesToGlobalMapForMethods(clazz.directMethods(), collectedStates, globalStateMap,
+ sourceMethodsMap, originStates, iface);
+ addStatesToGlobalMapForMethods(clazz.virtualMethods(), collectedStates, globalStateMap,
+ sourceMethodsMap, originStates, iface);
+ }
+ });
+ timing.end();
+ // Go over every method and assign a name.
+ timing.begin("Allocate names");
+ // Sort the methods by the number of dependent states, so that we use short names for methods
+ // references in many places.
+ List<Wrapper<DexMethod>> methods = new ArrayList<>(globalStateMap.keySet());
+ methods.sort((a, b) -> globalStateMap.get(b).size() - globalStateMap.get(a).size());
+ for (Wrapper<DexMethod> key : methods) {
+ DexMethod method = key.get();
+ assignNameForInterfaceMethodInAllStates(method,
+ globalStateMap.get(key),
+ sourceMethodsMap.get(key),
+ renaming,
+ originStates.get(key));
+ }
+ timing.end();
+ }
+
+ private void collectSuperInterfaces(DexType iface, Set<DexType> interfaces) {
+ DexClass clazz = appInfo.definitionFor(iface);
+ // In cases where we lack the interface's definition, we can at least look at subtypes and
+ // tie those up to get proper naming.
+ if (clazz != null) {
+ for (DexType type : clazz.interfaces.values) {
+ if (interfaces.add(type)) {
+ collectSuperInterfaces(type, interfaces);
+ }
+ }
+ }
+ }
+
+
+ private void collectSubInterfaces(DexType iface, Set<DexType> interfaces) {
+ DexClass clazz = appInfo.definitionFor(iface);
+ iface.forAllExtendsSubtypes(subtype -> {
+ assert subtype.isInterface();
+ if (interfaces.add(subtype)) {
+ collectSubInterfaces(subtype, interfaces);
+ }
+ });
+ }
+
+ private void addStatesToGlobalMapForMethods(
+ DexEncodedMethod[] methods, Set<NamingState<DexProto>> collectedStates,
+ Map<Wrapper<DexMethod>, Set<NamingState<DexProto>>> globalStateMap,
+ Map<Wrapper<DexMethod>, Set<DexMethod>> sourceMethodsMap,
+ Map<Wrapper<DexMethod>, NamingState<DexProto>> originStates, DexType originInterface) {
+ for (DexEncodedMethod method : methods) {
+ Wrapper<DexMethod> key = equivalence.wrap(method.method);
+ Set<NamingState<DexProto>> stateSet = globalStateMap
+ .computeIfAbsent(key, k -> new HashSet<>());
+ stateSet.addAll(collectedStates);
+ sourceMethodsMap.computeIfAbsent(key, k -> new HashSet<>()).add(method.method);
+ originStates.putIfAbsent(key, states.get(originInterface));
+ }
+ }
+
+ private void assignNameForInterfaceMethodInAllStates(DexMethod method,
+ Set<NamingState<DexProto>> collectedStates,
+ Set<DexMethod> sourceMethods,
+ Map<DexMethod, DexString> renaming, NamingState<DexProto> originState) {
+ boolean isReserved = false;
+ if (globalState.isReserved(method.name, method.proto)) {
+ for (NamingState<DexProto> state : collectedStates) {
+ if (state.isReserved(method.name, method.proto)) {
+ isReserved = true;
+ break;
+ }
+ }
+ if (isReserved) {
+ // This method's name is reserved in at least on naming state, so reserve it everywhere.
+ for (NamingState<DexProto> state : collectedStates) {
+ state.reserveName(method.name, method.proto);
+ }
+ return;
+ }
+ }
+ // We use the origin state to allocate a name here so that we can reuse names between different
+ // unrelated interfaces. This saves some space. The alternative would be to use a global state
+ // for allocating names, which would save the work to search here.
+ DexString candidate = null;
+ do {
+ candidate = originState.assignNewNameFor(method.name, method.proto, false);
+ for (NamingState<DexProto> state : collectedStates) {
+ if (!state.isAvailable(method.name, method.proto, candidate)) {
+ candidate = null;
+ break;
+ }
+ }
+ } while (candidate == null);
+ for (NamingState<DexProto> state : collectedStates) {
+ state.addRenaming(method.name, method.proto, candidate);
+ }
+ // Rename all methods in interfaces that gave rise to this renaming.
+ for (DexMethod sourceMethod : sourceMethods) {
+ renaming.put(sourceMethod, candidate);
+ }
+ }
+
+ private void reserveNamesInClasses(DexType type, DexType libraryFrontier,
+ NamingState<DexProto> parent,
+ Map<DexType, DexType> frontierMap) {
+ assert !type.isInterface();
+ DexClass holder = appInfo.definitionFor(type);
+ NamingState<DexProto> state = allocateNamingStateAndReserve(holder, type, libraryFrontier,
+ parent, frontierMap);
+ // If this is a library class (or effectively a library class as it is missing) move the
+ // frontier forward.
+ type.forAllExtendsSubtypes(subtype -> {
+ assert !subtype.isInterface();
+ reserveNamesInClasses(subtype,
+ holder == null || holder.isLibraryClass() ? subtype : libraryFrontier,
+ state, frontierMap);
+ });
+ }
+
+ private void reserveNamesInInterfaces(DexType type, Map<DexType, DexType> frontierMap) {
+ assert type.isInterface();
+ frontierMap.put(type, type);
+ DexClass holder = appInfo.definitionFor(type);
+ allocateNamingStateAndReserve(holder, type, type, null, frontierMap);
+ }
+
+ private NamingState<DexProto> allocateNamingStateAndReserve(DexClass holder, DexType type,
+ DexType libraryFrontier,
+ NamingState<DexProto> parent,
+ Map<DexType, DexType> frontierMap) {
+ frontierMap.put(type, libraryFrontier);
+ NamingState<DexProto> state =
+ states.computeIfAbsent(libraryFrontier,
+ ignore -> parent == null
+ ? NamingState.createRoot(appInfo.dexItemFactory, dictionary)
+ : parent.createChild());
+ if (holder != null) {
+ boolean keepAll = holder.isLibraryClass() || holder.accessFlags.isAnnotation();
+ reserveNamesForMethods(holder.virtualMethods(), keepAll, state);
+ reserveNamesForMethods(holder.directMethods(), keepAll, state);
+ }
+ return state;
+ }
+
+ private void reserveNamesForMethods(DexEncodedMethod[] methods,
+ boolean keepAll, NamingState<DexProto> state) {
+ for (DexEncodedMethod method : methods) {
+ if (keepAll || rootSet.noObfuscation.contains(method)) {
+ state.reserveName(method.method.name, method.method.proto);
+ globalState.reserveName(method.method.name, method.method.proto);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java b/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
new file mode 100644
index 0000000..8c5da6f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Set;
+
+public class MinifiedNameMapPrinter {
+
+ private final DexApplication application;
+ private final NamingLens namingLens;
+ private final Set<DexType> seenTypes = Sets.newIdentityHashSet();
+
+ public MinifiedNameMapPrinter(DexApplication application, NamingLens namingLens) {
+ this.application = application;
+ this.namingLens = namingLens;
+ }
+
+ private void write(DexProgramClass clazz, PrintStream out) {
+ seenTypes.add(clazz.type);
+ DexString descriptor = namingLens.lookupDescriptor(clazz.type);
+ out.print(DescriptorUtils.descriptorToJavaType(clazz.type.descriptor.toSourceString()));
+ out.print(" -> ");
+ out.print(DescriptorUtils.descriptorToJavaType(descriptor.toSourceString()));
+ out.println(":");
+ write(clazz.instanceFields(), out);
+ write(clazz.staticFields(), out);
+ write(clazz.directMethods(), out);
+ write(clazz.virtualMethods(), out);
+ }
+
+ private void write(DexType type, PrintStream out) {
+ if (type.isClassType() && seenTypes.add(type)) {
+ DexString descriptor = namingLens.lookupDescriptor(type);
+ out.print(DescriptorUtils.descriptorToJavaType(type.descriptor.toSourceString()));
+ out.print(" -> ");
+ out.print(DescriptorUtils.descriptorToJavaType(descriptor.toSourceString()));
+ out.println(":");
+ }
+ }
+
+ private void write(DexEncodedField[] fields, PrintStream out) {
+ for (DexEncodedField encodedField : fields) {
+ DexField field = encodedField.field;
+ DexString renamed = namingLens.lookupName(field);
+ if (renamed != field.name) {
+ out.print(" ");
+ out.print(field.type.toSourceString());
+ out.print(" ");
+ out.print(field.name.toSourceString());
+ out.print(" -> ");
+ out.println(renamed.toSourceString());
+ }
+ }
+ }
+
+ private void write(DexEncodedMethod[] methods, PrintStream out) {
+ for (DexEncodedMethod encodedMethod : methods) {
+ DexMethod method = encodedMethod.method;
+ DexString renamed = namingLens.lookupName(method);
+ if (renamed != method.name) {
+ MethodSignature signature = MethodSignature.fromDexMethod(method);
+ out.print(" ");
+ out.print(signature);
+ out.print(" -> ");
+ out.println(renamed.toSourceString());
+ }
+ }
+ }
+
+ public void write(PrintStream out) {
+ // First write out all classes that have been renamed.
+ application.classes().forEach(clazz -> write(clazz, out));
+ // Now write out all types only mentioned in descriptors that have been renamed.
+ namingLens.forAllRenamedTypes(type -> write(type, out));
+ }
+
+ public void write(Path destination) throws IOException {
+ PrintStream out = new PrintStream(Files.newOutputStream(destination), true,
+ StandardCharsets.UTF_8.name());
+ write(out);
+ }
+
+}
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
new file mode 100644
index 0000000..18099fb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -0,0 +1,134 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Iterables;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+public class Minifier {
+
+ private final AppInfoWithLiveness appInfo;
+ private final RootSet rootSet;
+ private final InternalOptions options;
+
+ public Minifier(AppInfoWithLiveness appInfo, RootSet rootSet, InternalOptions options) {
+ this.appInfo = appInfo;
+ this.rootSet = rootSet;
+ this.options = options;
+ }
+
+ public NamingLens run(Timing timing) {
+ assert !options.skipMinification;
+ if (!options.allowAccessModification) {
+ throw new CompilationError("Minification requires allowaccessmodification.");
+ }
+ timing.begin("MinifyClasses");
+ Map<DexType, DexString> classRenaming =
+ new ClassNameMinifier(
+ appInfo, rootSet, options.packagePrefix, options.classObfuscationDictionary)
+ .computeRenaming();
+ timing.end();
+ timing.begin("MinifyMethods");
+ Map<DexMethod, DexString> methodRenaming =
+ new MethodNameMinifier(appInfo, rootSet, options.obfuscationDictionary)
+ .computeRenaming(timing);
+ timing.end();
+ timing.begin("MinifyFields");
+ Map<DexField, DexString> fieldRenaming =
+ new FieldNameMinifier(appInfo, rootSet, options.obfuscationDictionary).computeRenaming();
+ timing.end();
+ return new MinifiedRenaming(classRenaming, methodRenaming, fieldRenaming, appInfo);
+ }
+
+ private static class MinifiedRenaming extends NamingLens {
+
+ private final AppInfo appInfo;
+ private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
+
+ private MinifiedRenaming(Map<DexType, DexString> classRenaming,
+ Map<DexMethod, DexString> methodRenaming, Map<DexField, DexString> fieldRenaming,
+ AppInfo appInfo) {
+ this.appInfo = appInfo;
+ renaming.putAll(classRenaming);
+ renaming.putAll(methodRenaming);
+ renaming.putAll(fieldRenaming);
+ }
+
+ @Override
+ public DexString lookupDescriptor(DexType type) {
+ return renaming.getOrDefault(type, type.descriptor);
+ }
+
+ @Override
+ public DexString lookupName(DexMethod method) {
+ return renaming.getOrDefault(method, method.name);
+ }
+
+ @Override
+ public DexString lookupName(DexField field) {
+ return renaming.getOrDefault(field, field.name);
+ }
+
+ @Override
+ void forAllRenamedTypes(Consumer<DexType> consumer) {
+ Iterables.filter(renaming.keySet(), DexType.class).forEach(consumer);
+ }
+
+ /**
+ * Checks whether the target is precise enough to be translated,
+ * <p>
+ * We only track the renaming of actual definitions, Thus, if we encounter a method id that
+ * does not directly point at a definition, we won't find the actual renaming. To avoid
+ * dispatching on every lookup, we assume that the tree has been fully dispatched by
+ * {@link MemberRebindingAnalysis}.
+ * <p>
+ * Library methods are excluded from this check, as those are never renamed.
+ */
+ @Override
+ public boolean checkTargetCanBeTranslated(DexMethod item) {
+ if (item.holder.isArrayType()) {
+ // Array methods are never renamed, so do not bother to check.
+ return true;
+ }
+ DexClass holder = appInfo.definitionFor(item.holder);
+ if (holder == null || holder.isLibraryClass()) {
+ return true;
+ }
+ // We don't know which invoke type this method is used for, so checks that it has been
+ // rebound either way.
+ DexEncodedMethod staticTarget = appInfo.lookupStaticTarget(item);
+ DexEncodedMethod directTarget = appInfo.lookupDirectTarget(item);
+ DexEncodedMethod virtualTarget = appInfo.lookupVirtualTarget(item.holder, item);
+ DexClass staticTargetHolder =
+ staticTarget != null ? appInfo.definitionFor(staticTarget.method.getHolder()) : null;
+ DexClass directTargetHolder =
+ directTarget != null ? appInfo.definitionFor(directTarget.method.getHolder()) : null;
+ DexClass virtualTargetHolder =
+ virtualTarget != null ? appInfo.definitionFor(virtualTarget.method.getHolder()) : null;
+ return directTarget == null && staticTarget == null && virtualTarget == null
+ || virtualTarget != null && virtualTarget.method == item
+ || directTarget != null && directTarget.method == item
+ || staticTarget != null && staticTarget.method == item
+ || directTargetHolder != null && directTargetHolder.isLibraryClass()
+ || virtualTargetHolder != null && virtualTargetHolder.isLibraryClass()
+ || staticTargetHolder != null && staticTargetHolder.isLibraryClass();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
new file mode 100644
index 0000000..604190c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -0,0 +1,81 @@
+// 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.naming;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import java.util.function.Consumer;
+
+/**
+ * Implements a translation of the Dex graph from original names to new names produced by
+ * the {@link Minifier}.
+ * <p>
+ * The minifier does not actually rename classes and members but instead only produces a mapping
+ * from original ids to renamed ids. When writing the file, the graph has to be interpreted with
+ * that mapping in mind, i.e., it should be looked at only through this lens.
+ * <p>
+ * The translation relies on members being statically dispatched to actual definitions, as done
+ * by the {@link MemberRebindingAnalysis} optimization.
+ */
+public abstract class NamingLens {
+
+ public abstract DexString lookupDescriptor(DexType type);
+
+ public abstract DexString lookupName(DexMethod method);
+
+ public abstract DexString lookupName(DexField field);
+
+ public static NamingLens getIdentityLens() {
+ return new IdentityLens();
+ }
+
+ public final boolean isIdentityLens() {
+ return this instanceof IdentityLens;
+ }
+
+ abstract void forAllRenamedTypes(Consumer<DexType> consumer);
+
+ /**
+ * Checks whether the target will be translated properly by this lense.
+ * <p>
+ * Normally, this means that the target corresponds to an actual definition that has been
+ * renamed. For identity renamings, we are more relaxed, as no targets will be translated
+ * anyway.
+ */
+ public abstract boolean checkTargetCanBeTranslated(DexMethod item);
+
+ private static class IdentityLens extends NamingLens {
+
+ private IdentityLens() {
+ // Intentionally left empty.
+ }
+
+ @Override
+ public DexString lookupDescriptor(DexType type) {
+ return type.descriptor;
+ }
+
+ @Override
+ public DexString lookupName(DexMethod method) {
+ return method.name;
+ }
+
+ @Override
+ public DexString lookupName(DexField field) {
+ return field.name;
+ }
+
+ @Override
+ void forAllRenamedTypes(Consumer<DexType> consumer) {
+ // Intentionally left empty.
+ }
+
+ public boolean checkTargetCanBeTranslated(DexMethod item) {
+ return true;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/NamingState.java b/src/main/java/com/android/tools/r8/naming/NamingState.java
new file mode 100644
index 0000000..0474c1a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/NamingState.java
@@ -0,0 +1,192 @@
+// 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.naming;
+
+import com.android.tools.r8.graph.CanonicalizedDexItem;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Sets;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class NamingState<T extends CanonicalizedDexItem> {
+
+ private final NamingState<T> parent;
+ private final Map<T, InternalState> usedNames = new IdentityHashMap<>();
+ private final DexItemFactory itemFactory;
+ private final List<String> dictionary;
+
+ public static <T extends CanonicalizedDexItem> NamingState<T> createRoot(
+ DexItemFactory itemFactory, List<String> dictionary) {
+ return new NamingState<>(null, itemFactory, dictionary);
+ }
+
+ private NamingState(NamingState<T> parent, DexItemFactory itemFactory, List<String> dictionary) {
+ this.parent = parent;
+ this.itemFactory = itemFactory;
+ this.dictionary = dictionary;
+ }
+
+ public NamingState<T> createChild() {
+ return new NamingState<>(this, itemFactory, dictionary);
+ }
+
+ private InternalState findInternalStateFor(T proto) {
+ InternalState result = usedNames.get(proto);
+ if (result == null && parent != null) {
+ result = parent.findInternalStateFor(proto);
+ }
+ return result;
+ }
+
+ private InternalState getOrCreateInternalStateFor(T proto) {
+ // TODO(herhut): Maybe allocate these sparsely and search via state chain.
+ InternalState result = usedNames.get(proto);
+ if (result == null) {
+ if (parent != null) {
+ InternalState parentState = parent.getOrCreateInternalStateFor(proto);
+ result = parentState.createChild();
+ } else {
+ result = new InternalState(itemFactory, null, dictionary);
+ }
+ usedNames.put(proto, result);
+ }
+ return result;
+ }
+
+ public DexString getAssignedNameFor(DexString name, T proto) {
+ InternalState state = findInternalStateFor(proto);
+ if (state == null) {
+ return null;
+ }
+ return state.getAssignedNameFor(name);
+ }
+
+ public DexString assignNewNameFor(DexString original, T proto, boolean markAsUsed) {
+ DexString result = getAssignedNameFor(original, proto);
+ if (result == null) {
+ InternalState state = getOrCreateInternalStateFor(proto);
+ result = state.getNameFor(original, markAsUsed);
+ }
+ return result;
+ }
+
+ public void reserveName(DexString name, T proto) {
+ InternalState state = getOrCreateInternalStateFor(proto);
+ state.reserveName(name);
+ }
+
+ public boolean isReserved(DexString name, T proto) {
+ InternalState state = findInternalStateFor(proto);
+ if (state == null) {
+ return false;
+ }
+ return state.isReserved(name);
+ }
+
+ public boolean isAvailable(DexString original, T proto, DexString candidate) {
+ InternalState state = findInternalStateFor(proto);
+ if (state == null) {
+ return true;
+ }
+ assert state.getAssignedNameFor(original) != candidate;
+ return state.isAvailable(candidate);
+ }
+
+ public void addRenaming(DexString original, T proto, DexString newName) {
+ InternalState state = getOrCreateInternalStateFor(proto);
+ state.addRenaming(original, newName);
+ }
+
+ private static class InternalState {
+
+ private static int INITIAL_NAME_COUNT = 1;
+ private final static char[] EMPTY_CHAR_ARRARY = new char[0];
+
+ protected final DexItemFactory itemFactory;
+ private final InternalState parentInternalState;
+ private Set<DexString> reservedNames = null;
+ private Map<DexString, DexString> renamings = null;
+ private int nameCount;
+ private final Iterator<String> dictionaryIterator;
+
+ private InternalState(DexItemFactory itemFactory, InternalState parentInternalState,
+ Iterator<String> dictionaryIterator) {
+ this.itemFactory = itemFactory;
+ this.parentInternalState = parentInternalState;
+ this.nameCount =
+ parentInternalState == null ? INITIAL_NAME_COUNT : parentInternalState.nameCount;
+ this.dictionaryIterator = dictionaryIterator;
+ }
+
+ private InternalState(DexItemFactory itemFactory, InternalState parentInternalState,
+ List<String> dictionary) {
+ this(itemFactory, parentInternalState, dictionary.iterator());
+ }
+
+ private boolean isReserved(DexString name) {
+ return (reservedNames != null && reservedNames.contains(name))
+ || (parentInternalState != null && parentInternalState.isReserved(name));
+ }
+
+ private boolean isAvailable(DexString name) {
+ return !(renamings != null && renamings.containsValue(name))
+ && !(reservedNames != null && reservedNames.contains(name))
+ && (parentInternalState == null || parentInternalState.isAvailable(name));
+ }
+
+ public InternalState createChild() {
+ return new InternalState(itemFactory, this, dictionaryIterator);
+ }
+
+ public void reserveName(DexString name) {
+ if (reservedNames == null) {
+ reservedNames = Sets.newIdentityHashSet();
+ }
+ reservedNames.add(name);
+ }
+
+ public DexString getAssignedNameFor(DexString original) {
+ DexString result = renamings == null ? null : renamings.get(original);
+ if (result == null && parentInternalState != null) {
+ result = parentInternalState.getAssignedNameFor(original);
+ }
+ return result;
+ }
+
+ public DexString getNameFor(DexString original, boolean markAsUsed) {
+ DexString name = getAssignedNameFor(original);
+ if (name != null) {
+ return name;
+ }
+ do {
+ name = itemFactory.createString(nextSuggestedName());
+ } while (!isAvailable(name));
+ if (markAsUsed) {
+ addRenaming(original, name);
+ }
+ return name;
+ }
+
+ public void addRenaming(DexString original, DexString newName) {
+ if (renamings == null) {
+ renamings = HashBiMap.create();
+ }
+ renamings.put(original, newName);
+ }
+
+ protected String nextSuggestedName() {
+ if (dictionaryIterator.hasNext()) {
+ return dictionaryIterator.next();
+ } else {
+ return StringUtils.numberToIdentifier(EMPTY_CHAR_ARRARY, nameCount++, false);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
new file mode 100644
index 0000000..564e6de
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -0,0 +1,422 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Range;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.MemberNaming.SingleLineRange;
+import com.google.common.collect.ImmutableMap;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * Parses a Proguard mapping file and produces mappings from obfuscated class names to the original
+ * name and from obfuscated member signatures to the original members the obfuscated member
+ * was formed of.
+ * <p>
+ * The expected format is as follows
+ * <p>
+ * original-type-name ARROW obfuscated-type-name COLON starts a class mapping
+ * description and maps original to obfuscated.
+ * <p>
+ * followed by one or more of
+ * <p>
+ * signature ARROW name
+ * <p>
+ * which maps the member with the given signature to the new name. This mapping is not
+ * bidirectional as member names are overloaded by signature. To make it bidirectional, we extend
+ * the name with the signature of the original member.
+ * <p>
+ * Due to inlining, we might have the above prefixed with a range (two numbers separated by :).
+ * <p>
+ * range COLON signature ARROW name
+ * <p>
+ * This has the same meaning as the above but also encodes the line number range of the member. This
+ * may be followed by multiple inline mappings of the form
+ * <p>
+ * range COLON signature COLON range ARROW name
+ * <p>
+ * to identify that signature was inlined from the second range to the new line numbers in the first
+ * range. This is then followed by information on the call trace to where the member was inlined.
+ * These entries have the form
+ * <p>
+ * range COLON signature COLON number ARROW name
+ * <p>
+ * and are currently only stored to be able to reproduce them later.
+ */
+public class ProguardMapReader implements AutoCloseable {
+
+ private final BufferedReader reader;
+
+ public void close() throws IOException {
+ if (reader != null) {
+ reader.close();
+ }
+ }
+
+ private ProguardMapReader(BufferedReader reader) throws IOException {
+ this.reader = reader;
+ }
+
+ public static ClassNameMapper mapperFromInputStream(InputStream in) throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF8"));
+ try (ProguardMapReader proguardReader = new ProguardMapReader(reader)) {
+ return proguardReader.parse();
+ }
+ }
+
+ public static ClassNameMapper mapperFromFile(Path path) throws IOException {
+ return mapperFromInputStream(Files.newInputStream(path));
+ }
+
+ public static ClassNameMapper mapperFromString(String contents) throws IOException {
+ return mapperFromInputStream(
+ new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)));
+ }
+
+ // Internal parser state
+ private int lineNo = 0;
+ private int lineOffset = 0;
+ private String line;
+
+ private char peek() {
+ return peek(0);
+ }
+
+ private char peek(int distance) {
+ return lineOffset + distance < line.length()
+ ? line.charAt(lineOffset + distance)
+ : '\n';
+ }
+
+ private char next() {
+ try {
+ return line.charAt(lineOffset++);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new ParseException("Unexpected end of line");
+ }
+ }
+
+ private boolean nextLine() throws IOException {
+ if (line.length() != lineOffset) {
+ throw new ParseException("Expected end of line");
+ }
+ return skipLine();
+ }
+
+ private boolean skipLine() throws IOException {
+ lineNo++;
+ lineOffset = 0;
+ line = reader.readLine();
+ return hasLine();
+ }
+
+ private boolean hasLine() {
+ return line != null;
+ }
+
+ // Helpers for common pattern
+ private void skipWhitespace() {
+ while (Character.isWhitespace(peek())) {
+ next();
+ }
+ }
+
+ private char expect(char c) {
+ if (next() != c) {
+ throw new ParseException("Expected '" + c + "'");
+ }
+ return c;
+ }
+
+ public ClassNameMapper parse() throws IOException {
+ // Read the first line.
+ line = reader.readLine();
+ Map<String, ClassNaming> classNames = parseClassMappings();
+ return new ClassNameMapper(classNames);
+ }
+
+ // Parsing of entries
+
+ private Map<String, ClassNaming> parseClassMappings() throws IOException {
+ ImmutableMap.Builder<String, ClassNaming> builder = ImmutableMap.builder();
+ while (hasLine()) {
+ String before = parseType(false);
+ skipWhitespace();
+ // Workaround for proguard map files that contain entries for package-info.java files.
+ if (!acceptArrow()) {
+ // If this was a package-info line, we parsed the "package" string.
+ if (!before.endsWith("package") || !acceptString("-info")) {
+ throw new ParseException("Expected arrow after class name " + before);
+ }
+ skipLine();
+ continue;
+ }
+ skipWhitespace();
+ String after = parseType(false);
+ expect(':');
+ ClassNaming currentClass = new ClassNaming(after, before);
+ builder.put(after, currentClass);
+ if (nextLine()) {
+ parseMemberMappings(currentClass);
+ }
+ }
+ return builder.build();
+ }
+
+ private void parseMemberMappings(ClassNaming currentClass) throws IOException {
+ MemberNaming current = null;
+ Range previousInlineRange = null;
+ Signature previousSignature = null;
+ String previousRenamedName = null;
+ List<Consumer<MemberNaming>> collectedInfos = new ArrayList<>(10);
+
+ while (Character.isWhitespace(peek())) {
+ skipWhitespace();
+ Range inlinedLineRange = maybeParseRange();
+ if (inlinedLineRange != null) {
+ expect(':');
+ }
+ Signature signature = parseSignature();
+ Range originalLineRange;
+ if (peek() == ':') {
+ // This is an inlining definition
+ next();
+ originalLineRange = maybeParseRange();
+ if (originalLineRange == null) {
+ if (!skipLine()) {
+ break;
+ }
+ continue;
+ }
+ } else {
+ originalLineRange = null;
+ }
+ skipWhitespace();
+ skipArrow();
+ skipWhitespace();
+ String renamedName = parseMethodName();
+ // If there is no line number information at the front or if it changes, we have a new
+ // segment. Likewise, if the range information on the right hand side has two values, we have
+ // a new segment.
+ if (inlinedLineRange == null
+ || previousInlineRange == null
+ || originalLineRange == null
+ || !previousInlineRange.equals(inlinedLineRange)
+ || !originalLineRange.isSingle()) {
+ // We are at a range boundary. Either we parsed something new, or an inline frame is over.
+ // We detect this by checking whether the previous signature matches the one of current.
+ if (current == null || !previousSignature.equals(current.signature)) {
+ if (collectedInfos.size() == 1) {
+ current = new MemberNaming(previousSignature, previousRenamedName, previousInlineRange);
+ currentClass.addMemberEntry(current);
+ } else {
+ if (Log.ENABLED && !collectedInfos.isEmpty()) {
+ Log.warn(getClass(),
+ "More than one member entry that forms a new group at %s %s -> %s",
+ previousInlineRange, previousSignature, previousRenamedName);
+ }
+ }
+ } else {
+ MemberNaming finalCurrent = current;
+ collectedInfos.forEach(info -> info.accept(finalCurrent));
+ }
+ collectedInfos.clear();
+ }
+ // Defer the creation of the info until we have the correct member.
+ collectedInfos.add((m) -> m.addInliningRange(inlinedLineRange, signature, originalLineRange));
+ // We have parsed the whole line, move on.
+ previousInlineRange = inlinedLineRange;
+ previousSignature = signature;
+ previousRenamedName = renamedName;
+ if (!nextLine()) {
+ break;
+ }
+ }
+ // Process the last round if lines have been read.
+ if (current == null || !previousSignature.equals(current.signature)) {
+ if (collectedInfos.size() == 1) {
+ current = new MemberNaming(previousSignature, previousRenamedName, previousInlineRange);
+ currentClass.addMemberEntry(current);
+ }
+ } else {
+ MemberNaming finalCurrent = current;
+ collectedInfos.forEach(info -> info.accept(finalCurrent));
+ }
+ collectedInfos.clear();
+ }
+
+ // Parsing of components
+
+ private void skipIdentifier(boolean allowInit) {
+ boolean isInit = false;
+ if (allowInit && peek() == '<') {
+ // swallow the leading < character
+ next();
+ isInit = true;
+ }
+ if (!Character.isJavaIdentifierStart(peek())) {
+ throw new ParseException("Identifier expected");
+ }
+ next();
+ while (Character.isJavaIdentifierPart(peek())) {
+ next();
+ }
+ if (isInit) {
+ expect('>');
+ }
+ if (Character.isJavaIdentifierPart(peek())) {
+ throw new ParseException("End of identifier expected");
+ }
+ }
+
+ // Cache for canonicalizing strings.
+ // This saves 10% of heap space for large programs.
+ final HashMap<String, String> cache = new HashMap<>();
+
+ private String substring(int start) {
+ String result = line.substring(start, lineOffset);
+ if (cache.containsKey(result)) {
+ return cache.get(result);
+ }
+ cache.put(result, result);
+ return result;
+ }
+
+ private String parseMethodName() {
+ int startPosition = lineOffset;
+ skipIdentifier(true);
+ while (peek() == '.') {
+ next();
+ skipIdentifier(true);
+ }
+ return substring(startPosition);
+ }
+
+ private String parseType(boolean allowArray) {
+ int startPosition = lineOffset;
+ skipIdentifier(false);
+ while (peek() == '.') {
+ next();
+ skipIdentifier(false);
+ }
+ if (allowArray) {
+ while (peek() == '[') {
+ next();
+ expect(']');
+ }
+ }
+ return substring(startPosition);
+ }
+
+ private Signature parseSignature() {
+ String type = parseType(true);
+ expect(' ');
+ String name = parseMethodName();
+ Signature signature;
+ if (peek() == '(') {
+ next();
+ String[] arguments;
+ if (peek() == ')') {
+ arguments = new String[0];
+ } else {
+ List<String> items = new LinkedList<>();
+ items.add(parseType(true));
+ while (peek() != ')') {
+ expect(',');
+ items.add(parseType(true));
+ }
+ arguments = items.toArray(new String[items.size()]);
+ }
+ expect(')');
+ signature = new MethodSignature(name, type, arguments);
+ } else {
+ signature = new FieldSignature(name, type);
+ }
+ return signature;
+ }
+
+ private void skipArrow() {
+ expect('-');
+ expect('>');
+ }
+
+ private boolean acceptArrow() {
+ if (peek() == '-' && peek(1) == '>') {
+ next();
+ next();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean acceptString(String s) {
+ for (int i = 0; i < s.length(); i++) {
+ if (peek(i) != s.charAt(i)) {
+ return false;
+ }
+ }
+ for (int i = 0; i < s.length(); i++) {
+ next();
+ }
+ return true;
+ }
+
+ private Range maybeParseRange() {
+ if (!Character.isDigit(peek())) {
+ return null;
+ }
+ int from = parseNumber();
+ if (peek() != ':') {
+ return new SingleLineRange(from);
+ }
+ expect(':');
+ int to = parseNumber();
+ return new Range(from, to);
+ }
+
+ private int parseNumber() {
+ int result = 0;
+ if (!Character.isDigit(peek())) {
+ throw new ParseException("Number expected");
+ }
+ do {
+ result *= 10;
+ result += Character.getNumericValue(next());
+ } while (Character.isDigit(peek()));
+ return result;
+ }
+
+ private class ParseException extends RuntimeException {
+
+ private final int lineNo;
+ private final int lineOffset;
+ private final String msg;
+
+ ParseException(String msg) {
+ lineNo = ProguardMapReader.this.lineNo;
+ lineOffset = ProguardMapReader.this.lineOffset;
+ this.msg = msg;
+ }
+
+ public String toString() {
+ return "Parse error [" + lineNo + ":" + lineOffset + "] " + msg;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
new file mode 100644
index 0000000..f36e8c5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
@@ -0,0 +1,119 @@
+// 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.optimize;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public class BridgeMethodAnalysis {
+
+ private final GraphLense lense;
+ private final AppInfoWithSubtyping appInfo;
+ private final Map<DexMethod, DexMethod> bridgeTargetToBridgeMap = new IdentityHashMap<>();
+
+ public BridgeMethodAnalysis(GraphLense lense, AppInfoWithSubtyping appInfo) {
+ this.lense = lense;
+ this.appInfo = appInfo;
+ }
+
+ public GraphLense run() {
+ for (DexClass clazz : appInfo.classes()) {
+ identifyBridgeMethods(clazz.virtualMethods());
+ identifyBridgeMethods(clazz.directMethods());
+ }
+ return new BridgeLense(lense, bridgeTargetToBridgeMap);
+ }
+
+ private void identifyBridgeMethods(DexEncodedMethod[] dexEncodedMethods) {
+ for (DexEncodedMethod method : dexEncodedMethods) {
+ if (method.accessFlags.isBridge()) {
+ InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor();
+ method.getCode().registerReachableDefinitions(targetExtractor);
+ DexMethod target = targetExtractor.getTarget();
+ InvokeKind kind = targetExtractor.getKind();
+ if (target != null &&
+ target.proto.parameters.values.length == method.method.proto.parameters.values.length) {
+ assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor();
+ target = lense.lookupMethod(target, method);
+ if (kind == InvokeKind.STATIC) {
+ assert method.accessFlags.isStatic();
+ DexEncodedMethod targetMethod = appInfo.lookupStaticTarget(target);
+ if (targetMethod != null) {
+ addForwarding(method, targetMethod);
+ }
+ } else if (kind == InvokeKind.VIRTUAL) {
+ // TODO(herhut): Add support for bridges with multiple targets.
+ DexEncodedMethod targetMethod = appInfo.lookupSingleVirtualTarget(target);
+ if (targetMethod != null) {
+ addForwarding(method, targetMethod);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void addForwarding(DexEncodedMethod method, DexEncodedMethod target) {
+ // This is a single target bridge we can inline.
+ if (Log.ENABLED) {
+ Log.info(getClass(), "Adding bridge forwarding %s -> %s.", method.method,
+ target.method);
+ }
+ bridgeTargetToBridgeMap.put(target.method, method.method);
+ // Force the target to be inlined into the bridge.
+ target.markForceInline();
+ }
+
+
+
+ private static class BridgeLense extends GraphLense {
+
+ private final GraphLense previousLense;
+ private final Map<DexMethod, DexMethod> bridgeTargetToBridgeMap;
+
+ private BridgeLense(GraphLense previousLense,
+ Map<DexMethod, DexMethod> bridgeTargetToBridgeMap) {
+ this.previousLense = previousLense;
+ this.bridgeTargetToBridgeMap = bridgeTargetToBridgeMap;
+ }
+
+ @Override
+ public DexType lookupType(DexType type, DexEncodedMethod context) {
+ return previousLense.lookupType(type, context);
+ }
+
+ @Override
+ public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context) {
+ DexMethod previous = previousLense.lookupMethod(method, context);
+ DexMethod target = bridgeTargetToBridgeMap.get(previous);
+ // Do not forward calls from a bridge method to itself while the bridge method is still
+ // a bridge.
+ if (target == null ||
+ context.accessFlags.isBridge() && target == context.method) {
+ return previous;
+ } else {
+ return target;
+ }
+ }
+
+ @Override
+ public DexField lookupField(DexField field, DexEncodedMethod context) {
+ return previousLense.lookupField(field, context);
+ }
+
+ @Override
+ public boolean isContextFree() {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/DebugStripper.java b/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
new file mode 100644
index 0000000..d045f76
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
@@ -0,0 +1,192 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugEntry;
+import com.android.tools.r8.graph.DexDebugEventBuilder;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.ClassNaming;
+import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.naming.MemberNaming.Range;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.utils.HashMapInt;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableMap;
+import java.util.List;
+
+public class DebugStripper {
+
+ private static final int USED_MORE_THAN_ONCE = 0;
+ private static final int USED_ONCE = -1;
+
+ private final ClassNameMapper classNameMapper;
+ private final InternalOptions options;
+
+ public DebugStripper(ClassNameMapper classNameMapper, InternalOptions options) {
+ this.classNameMapper = classNameMapper;
+ this.options = options;
+ }
+
+ private String descriptorToName(String descriptor) {
+ // The format is L<name>; and '/' is used as package separator.
+ return descriptor.substring(1, descriptor.length() - 1).replace('/', '.');
+ }
+
+ private Range findRange(int value, List<Range> ranges, Range defaultRange) {
+ for (Range range : ranges) {
+ if (range.contains(value)) {
+ return range;
+ }
+ }
+ return defaultRange;
+ }
+
+ private static class NumberedDebugInfo {
+ final int numberOfEntries;
+ final DexDebugInfo info;
+
+ public NumberedDebugInfo(int numberOfEntries, DexDebugInfo info) {
+ this.numberOfEntries = numberOfEntries;
+ this.info = info;
+ }
+ }
+
+ private NumberedDebugInfo processDebugInfo(DexMethod method, DexDebugInfo info,
+ MemberNaming naming, int startLine) {
+ if (info == null || naming == null) {
+ return new NumberedDebugInfo(0, null);
+ }
+ List<Range> ranges = naming.getInlineRanges();
+ // Maintain line and address but only when entering or leaving a range of line numbers
+ // that pertains to a different method body.
+ Range currentRange = naming.topLevelRange;
+ DexDebugEventBuilder builder = new DexDebugEventBuilder(method);
+ // Always start with a no-op bytecode to make sure that the start-line is manifested by
+ // the Dalvik VM and the event based processing in R8. This also avoids empty bytecode
+ // sequences.
+ int entryCount = 1;
+ DexString file = null;
+ ImmutableMap<Integer, DebugLocalInfo> locals = null;
+ builder.setPosition(0, startLine, file, locals);
+ for (DexDebugEntry entry : info.computeEntries()) {
+ boolean addEntry = false;
+ // We are in a range, check whether we have left it.
+ if (currentRange != null && !currentRange.contains(entry.line)) {
+ currentRange = null;
+ addEntry = true;
+ }
+ // We have no range (because we left the old one or never were in a range).
+ if (currentRange == null) {
+ currentRange = findRange(entry.line, ranges, naming.topLevelRange);
+ // We entered a new Range, emit this entry.
+ if (currentRange != null) {
+ addEntry = true;
+ }
+ }
+ if (addEntry) {
+ int line = options.skipDebugLineNumberOpt
+ ? entry.line
+ : startLine + ranges.indexOf(currentRange) + 1;
+ builder.setPosition(entry.address, line, file, locals);
+ ++entryCount;
+ }
+ }
+ return new NumberedDebugInfo(entryCount, builder.build());
+ }
+
+ private void processCode(DexEncodedMethod encodedMethod, MemberNaming naming,
+ HashMapInt<DexString> nameCounts) {
+ if (encodedMethod.getCode() == null) {
+ return;
+ }
+ DexCode code = encodedMethod.getCode().asDexCode();
+ DexString name = encodedMethod.method.name;
+ DexDebugInfo originalInfo = code.getDebugInfo();
+ if (originalInfo == null) {
+ return;
+ }
+ int startLine;
+ boolean isUsedOnce = false;
+ if (options.skipDebugLineNumberOpt) {
+ startLine = originalInfo.startLine;
+ } else {
+ int nameCount = nameCounts.get(name);
+ if (nameCount == USED_ONCE) {
+ isUsedOnce = true;
+ startLine = 0;
+ } else {
+ startLine = nameCount;
+ }
+ }
+
+ NumberedDebugInfo numberedInfo = processDebugInfo(
+ encodedMethod.method, originalInfo, naming, startLine);
+ DexDebugInfo newInfo = numberedInfo.info;
+ if (!options.skipDebugLineNumberOpt) {
+ // Fix up the line information.
+ int previousCount = nameCounts.get(name);
+ nameCounts.put(name, previousCount + numberedInfo.numberOfEntries);
+ // If we don't actually need line information and there are no debug entries, throw it away.
+ if (newInfo != null && isUsedOnce && newInfo.events.length == 0) {
+ newInfo = null;
+ } else if (naming != null && newInfo != null) {
+ naming.setCollapsedStartLineNumber(startLine);
+ // Preserve the line number information we had.
+ naming.setOriginalStartLineNumber(originalInfo.startLine);
+ }
+ }
+ code.setDebugInfo(newInfo);
+ }
+
+ private void processMethod(DexEncodedMethod method, ClassNaming classNaming,
+ HashMapInt<DexString> nameCounts) {
+ MemberNaming naming = null;
+ if (classNaming != null) {
+ Signature renamedSignature = classNameMapper.getRenamedMethodSignature(method.method);
+ naming = classNaming.lookup(renamedSignature);
+ }
+ processCode(method, naming, nameCounts);
+ }
+
+ private void processMethods(DexEncodedMethod[] methods, ClassNaming naming,
+ HashMapInt<DexString> nameCounts) {
+ if (methods == null) {
+ return;
+ }
+ for (DexEncodedMethod method : methods) {
+ processMethod(method, naming, nameCounts);
+ }
+ }
+
+ public void processClass(DexProgramClass clazz) {
+ if (!clazz.hasMethodsOrFields()) {
+ return;
+ }
+ String name = descriptorToName(clazz.type.toDescriptorString());
+ ClassNaming naming = classNameMapper == null ? null : classNameMapper.getClassNaming(name);
+ HashMapInt<DexString> nameCounts = new HashMapInt<>();
+ setIntialNameCounts(nameCounts, clazz.directMethods());
+ setIntialNameCounts(nameCounts, clazz.virtualMethods());
+ processMethods(clazz.directMethods(), naming, nameCounts);
+ processMethods(clazz.virtualMethods(), naming, nameCounts);
+ }
+
+ private void setIntialNameCounts(HashMapInt<DexString> nameCounts,
+ DexEncodedMethod[] methods) {
+ for (DexEncodedMethod method : methods) {
+ if (nameCounts.containsKey(method.method.name)) {
+ nameCounts.put(method.method.name, USED_MORE_THAN_ONCE);
+ } else {
+ nameCounts.put(method.method.name, USED_ONCE);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java b/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
new file mode 100644
index 0000000..61b270d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
@@ -0,0 +1,105 @@
+// 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.optimize;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+
+class InvokeSingleTargetExtractor extends UseRegistry {
+ private InvokeKind kind = InvokeKind.NONE;
+ private DexMethod target;
+
+ InvokeSingleTargetExtractor() {
+ }
+
+ private boolean setTarget(DexMethod target, InvokeKind kind) {
+ if (this.kind != InvokeKind.NONE) {
+ this.kind = InvokeKind.ILLEGAL;
+ this.target = null;
+ } else {
+ assert this.target == null;
+ this.target = target;
+ this.kind = kind;
+ }
+ return false;
+ }
+
+ private boolean invalid() {
+ kind = InvokeKind.ILLEGAL;
+ return false;
+ }
+
+ public DexMethod getTarget() {
+ return target;
+ }
+
+ public InvokeKind getKind() {
+ return kind;
+ }
+
+ @Override
+ public boolean registerInvokeVirtual(DexMethod method) {
+ return setTarget(method, InvokeKind.VIRTUAL);
+ }
+
+ @Override
+ public boolean registerInvokeDirect(DexMethod method) {
+ return invalid();
+ }
+
+ @Override
+ public boolean registerInvokeStatic(DexMethod method) {
+ return setTarget(method, InvokeKind.STATIC);
+ }
+
+ @Override
+ public boolean registerInvokeInterface(DexMethod method) {
+ return invalid();
+ }
+
+ @Override
+ public boolean registerInvokeSuper(DexMethod method) {
+ return setTarget(method, InvokeKind.SUPER);
+ }
+
+ @Override
+ public boolean registerInstanceFieldWrite(DexField field) {
+ return invalid();
+ }
+
+ @Override
+ public boolean registerInstanceFieldRead(DexField field) {
+ return invalid();
+ }
+
+ @Override
+ public boolean registerNewInstance(DexType type) {
+ return invalid();
+ }
+
+ @Override
+ public boolean registerStaticFieldRead(DexField field) {
+ return invalid();
+ }
+
+ @Override
+ public boolean registerStaticFieldWrite(DexField field) {
+ return invalid();
+ }
+
+ @Override
+ public boolean registerTypeReference(DexType type) {
+ return invalid();
+ }
+
+ public enum InvokeKind {
+ VIRTUAL,
+ STATIC,
+ SUPER,
+ ILLEGAL,
+ NONE
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
new file mode 100644
index 0000000..1e4c51b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -0,0 +1,155 @@
+// 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.optimize;
+
+import com.android.tools.r8.graph.Descriptor;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+public class MemberRebindingAnalysis {
+ private final AppInfoWithLiveness appInfo;
+ private final GraphLense lense;
+ private final GraphLense.Builder builder = new GraphLense.Builder();
+
+ public MemberRebindingAnalysis(AppInfoWithLiveness appInfo, GraphLense lense) {
+ assert lense.isContextFree();
+ this.appInfo = appInfo;
+ this.lense = lense;
+ }
+
+ private DexMethod validTargetFor(DexMethod target, DexMethod original,
+ BiFunction<DexClass, DexMethod, DexEncodedMethod> lookup) {
+ DexClass clazz = appInfo.definitionFor(target.getHolder());
+ assert clazz != null;
+ if (!clazz.isLibraryClass()) {
+ return target;
+ }
+ DexType newHolder;
+ if (clazz.isInterface()) {
+ newHolder = firstLibraryClassForInterfaceTarget(target, original.getHolder(), lookup);
+ } else {
+ newHolder = firstLibraryClass(target.getHolder(), original.getHolder());
+ }
+ return appInfo.dexItemFactory.createMethod(newHolder, original.proto, original.name);
+ }
+
+ private DexField validTargetFor(DexField target, DexField original,
+ BiFunction<DexClass, DexField, DexEncodedField> lookup) {
+ DexClass clazz = appInfo.definitionFor(target.getHolder());
+ assert clazz != null;
+ if (!clazz.isLibraryClass()) {
+ return target;
+ }
+ DexType newHolder;
+ if (clazz.isInterface()) {
+ newHolder = firstLibraryClassForInterfaceTarget(target, original.getHolder(), lookup);
+ } else {
+ newHolder = firstLibraryClass(target.getHolder(), original.getHolder());
+ }
+ return appInfo.dexItemFactory.createField(newHolder, original.type, original.name);
+ }
+
+ private <T> DexType firstLibraryClassForInterfaceTarget(T target, DexType current,
+ BiFunction<DexClass, T, ?> lookup) {
+ DexClass clazz = appInfo.definitionFor(current);
+ Object potential = lookup.apply(clazz, target);
+ if (potential != null) {
+ // Found, return type.
+ return current;
+ }
+ if (clazz.superType != null) {
+ DexType matchingSuper = firstLibraryClassForInterfaceTarget(target, clazz.superType, lookup);
+ if (matchingSuper != null) {
+ // Found in supertype, return first libray class.
+ return clazz.isLibraryClass() ? current : matchingSuper;
+ }
+ }
+ for (DexType iface : clazz.interfaces.values) {
+ DexType matchingIface = firstLibraryClassForInterfaceTarget(target, iface, lookup);
+ if (matchingIface != null) {
+ // Found in interface, return first library class.
+ return clazz.isLibraryClass() ? current : matchingIface;
+ }
+ }
+ return null;
+ }
+
+ private DexType firstLibraryClass(DexType top, DexType bottom) {
+ assert appInfo.definitionFor(top).isLibraryClass();
+ DexClass searchClass = appInfo.definitionFor(bottom);
+ while (!searchClass.isLibraryClass()) {
+ searchClass = appInfo.definitionFor(searchClass.superType);
+ }
+ return searchClass.type;
+ }
+
+ private boolean isNotFromLibrary(Descriptor item) {
+ DexClass clazz = appInfo.definitionFor(item.getHolder());
+ return clazz != null && !clazz.isLibraryClass();
+ }
+
+ private DexEncodedMethod virtualLookup(DexMethod method) {
+ return appInfo.lookupVirtualDefinition(method.getHolder(), method);
+ }
+
+ private DexEncodedMethod superLookup(DexMethod method) {
+ return appInfo.lookupVirtualTarget(method.getHolder(), method);
+ }
+
+ private void computeMethodRebinding(Set<DexMethod> methods,
+ Function<DexMethod, DexEncodedMethod> lookupTarget,
+ BiFunction<DexClass, DexMethod, DexEncodedMethod> lookupTargetOnClass) {
+ for (DexMethod method : methods) {
+ method = lense.lookupMethod(method, null);
+ // We can safely ignore array types, as the corresponding methods are defined in a library.
+ if (!method.getHolder().isClassType()) {
+ continue;
+ }
+ DexEncodedMethod target = lookupTarget.apply(method);
+ // Rebind to the lowest library class or program class.
+ if (target != null && target.method != method) {
+ builder.map(method, validTargetFor(target.method, method, lookupTargetOnClass));
+ }
+ }
+ }
+
+ private void computeFieldRebinding(Set<DexField> fields,
+ BiFunction<DexType, DexField, DexEncodedField> lookup,
+ BiFunction<DexClass, DexField, DexEncodedField> lookupTargetOnClass) {
+ for (DexField field : fields) {
+ field = lense.lookupField(field, null);
+ DexEncodedField target = lookup.apply(field.getHolder(), field);
+ // Rebind to the lowest library class or program class.
+ if (target != null && target.field != field) {
+ builder.map(field, validTargetFor(target.field, field, lookupTargetOnClass));
+ }
+ }
+ }
+
+ public GraphLense run() {
+ computeMethodRebinding(appInfo.virtualInvokes, this::virtualLookup,
+ DexClass::findVirtualTarget);
+ computeMethodRebinding(appInfo.superInvokes, this::superLookup, DexClass::findVirtualTarget);
+ computeMethodRebinding(appInfo.directInvokes, appInfo::lookupDirectTarget,
+ DexClass::findDirectTarget);
+ computeMethodRebinding(appInfo.staticInvokes, appInfo::lookupStaticTarget,
+ DexClass::findDirectTarget);
+
+ computeFieldRebinding(Sets.union(appInfo.staticFieldsRead, appInfo.staticFieldsWritten),
+ appInfo::lookupStaticTarget, DexClass::findStaticTarget);
+ computeFieldRebinding(Sets.union(appInfo.instanceFieldsRead, appInfo.instanceFieldsWritten),
+ appInfo::lookupInstanceTarget, DexClass::findInstanceTarget);
+ return builder.build(lense);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
new file mode 100644
index 0000000..e4892f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -0,0 +1,88 @@
+// 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.optimize;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
+import com.google.common.collect.Sets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class VisibilityBridgeRemover {
+ private final AppInfoWithSubtyping appInfo;
+ private final DexApplication application;
+ private final Set<DexEncodedMethod> unneededVisibilityBridges = Sets.newIdentityHashSet();
+
+ public VisibilityBridgeRemover(AppInfoWithSubtyping appInfo, DexApplication application) {
+ this.appInfo = appInfo;
+ this.application = application;
+ }
+
+ private void identifyBridgeMethods(DexEncodedMethod[] dexEncodedMethods) {
+ for (DexEncodedMethod method : dexEncodedMethods) {
+ if (method.accessFlags.isBridge()) {
+ InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor();
+ method.getCode().registerReachableDefinitions(targetExtractor);
+ DexMethod target = targetExtractor.getTarget();
+ InvokeKind kind = targetExtractor.getKind();
+ if (target != null &&
+ target.proto.parameters.values.length == method.method.proto.parameters.values.length) {
+ assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor();
+ if (kind == InvokeKind.SUPER) {
+ // This is a visibility forward, so check for the direct target.
+ DexEncodedMethod targetMethod
+ = appInfo.lookupVirtualDefinition(target.getHolder(), target);
+ if (targetMethod != null && targetMethod.accessFlags.isPublic()) {
+ if (Log.ENABLED) {
+ Log.info(getClass(), "Removing visibility forwarding %s -> %s", method.method,
+ targetMethod.method);
+ }
+ unneededVisibilityBridges.add(method);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void removeUnneededVisibilityBridges() {
+ Set<DexType> classes = unneededVisibilityBridges.stream()
+ .map(method -> method.method.getHolder())
+ .collect(Collectors.toSet());
+ for (DexType type : classes) {
+ DexClass clazz = appInfo.definitionFor(type);
+ clazz.virtualMethods = removeMethods(clazz.virtualMethods, unneededVisibilityBridges);
+ }
+ }
+
+ private DexEncodedMethod[] removeMethods(DexEncodedMethod[] methods,
+ Set<DexEncodedMethod> removals) {
+ assert methods != null;
+ List<DexEncodedMethod> newMethods = Arrays.stream(methods)
+ .filter(method -> !removals.contains(method))
+ .collect(Collectors.toList());
+ assert newMethods.size() < methods.length;
+ return newMethods.toArray(new DexEncodedMethod[newMethods.size()]);
+ }
+
+ public DexApplication run() {
+ for (DexClass clazz : appInfo.classes()) {
+ identifyBridgeMethods(clazz.virtualMethods());
+ identifyBridgeMethods(clazz.directMethods());
+ }
+ if (!unneededVisibilityBridges.isEmpty()) {
+ removeUnneededVisibilityBridges();
+ }
+ return application;
+ }
+
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
new file mode 100644
index 0000000..2850790
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
@@ -0,0 +1,99 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class AbstractMethodRemover {
+
+ private final AppInfoWithSubtyping appInfo;
+ private ScopedDexItemSet scope;
+
+ public AbstractMethodRemover(AppInfoWithSubtyping appInfo) {
+ this.appInfo = appInfo;
+ }
+
+ public void run() {
+ assert scope == null;
+ processClass(appInfo.dexItemFactory.objectType);
+ }
+
+ private void processClass(DexType type) {
+ DexClass holder = appInfo.definitionFor(type);
+ scope = new ScopedDexItemSet(scope);
+ if (holder != null) {
+ holder.virtualMethods = processMethods(holder.virtualMethods);
+ }
+ type.forAllExtendsSubtypes(this::processClass);
+ scope = scope.getParent();
+ }
+
+ private DexEncodedMethod[] processMethods(DexEncodedMethod[] virtualMethods) {
+ if (virtualMethods == null) {
+ return null;
+ }
+ // Removal of abstract methods is rare, so avoid copying the array until we find one.
+ List<DexEncodedMethod> methods = null;
+ for (int i = 0; i < virtualMethods.length; i++) {
+ DexEncodedMethod method = virtualMethods[i];
+ if (scope.addMethod(method.method) || !method.accessFlags.isAbstract()) {
+ if (methods != null) {
+ methods.add(method);
+ }
+ } else {
+ if (methods == null) {
+ methods = new ArrayList<>(virtualMethods.length - 1);
+ for (int j = 0; j < i; j++) {
+ methods.add(virtualMethods[j]);
+ }
+ }
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Removing abstract method %s.", method.method);
+ }
+ }
+ }
+ return methods == null ? virtualMethods : methods.toArray(new DexEncodedMethod[methods.size()]);
+ }
+
+ private static class ScopedDexItemSet {
+ private static Equivalence<DexMethod> METHOD_EQUIVALENCE = MethodSignatureEquivalence.get();
+
+ private final ScopedDexItemSet parent;
+ private final Set<Wrapper<DexMethod>> items = new HashSet<>();
+
+ private ScopedDexItemSet() {
+ this(null);
+ }
+
+ private ScopedDexItemSet(ScopedDexItemSet parent) {
+ this.parent = parent;
+ }
+
+ private boolean contains(Wrapper<DexMethod> item) {
+ return items.contains(item)
+ || ((parent != null) && parent.contains(item));
+ }
+
+ boolean addMethod(DexMethod method) {
+ Wrapper<DexMethod> wrapped = METHOD_EQUIVALENCE.wrap(method);
+ return !contains(wrapped) && items.add(wrapped);
+ }
+
+ ScopedDexItemSet getParent() {
+ return parent;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
new file mode 100644
index 0000000..e60f241
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -0,0 +1,175 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.AttributeRemovalOptions;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.function.Predicate;
+
+public class AnnotationRemover {
+
+ private final AppInfoWithLiveness appInfo;
+ private final boolean minificationEnabled;
+ private final AttributeRemovalOptions keep;
+
+ public AnnotationRemover(AppInfoWithLiveness appInfo, InternalOptions options) {
+ this(appInfo, !options.skipMinification, options.attributeRemoval);
+ }
+
+ public AnnotationRemover(AppInfoWithLiveness appInfo, boolean minificationEnabled,
+ AttributeRemovalOptions keep) {
+ this.appInfo = appInfo;
+ this.minificationEnabled = minificationEnabled;
+ this.keep = keep;
+ }
+
+ /**
+ * Used to filter annotations on classes, methods and fields.
+ */
+ private boolean filterAnnotations(DexAnnotation annotation) {
+ switch (annotation.visibility) {
+ case DexAnnotation.VISIBILITY_SYSTEM:
+ // EnclosingClass and InnerClass are used in Dalvik to represent the InnerClasses
+ // attribute section from class files.
+ if (keep.innerClasses &&
+ (DexAnnotation.isInnerClassesAnnotation(annotation) ||
+ DexAnnotation.isEnclosingClassAnnotation(annotation))) {
+ return true;
+ }
+ if (keep.enclosingMethod && DexAnnotation.isEnclosingMethodAnnotation(annotation)) {
+ return true;
+ }
+ if (keep.exceptions && DexAnnotation.isThrowingAnnotation(annotation)) {
+ return true;
+ }
+ if (keep.signature && DexAnnotation.isSignatureAnnotation(annotation)) {
+ return true;
+ }
+ if (keep.sourceDebugExtension && DexAnnotation.isSourceDebugExtension(annotation)) {
+ return true;
+ }
+ return false;
+ case DexAnnotation.VISIBILITY_RUNTIME:
+ if (!keep.runtimeVisibleAnnotations) {
+ return false;
+ }
+ break;
+ case DexAnnotation.VISIBILITY_BUILD:
+ if (!keep.runtimeInvisibleAnnotations) {
+ return false;
+ }
+ break;
+ default:
+ throw new Unreachable("Unexpected annotation visiility.");
+ }
+ return appInfo.liveTypes.contains(annotation.annotation.type);
+ }
+
+ /**
+ * Used to filter annotations on parameters.
+ */
+ private boolean filterParameterAnnotations(DexAnnotation annotation) {
+ switch (annotation.visibility) {
+ case DexAnnotation.VISIBILITY_SYSTEM:
+ return false;
+ case DexAnnotation.VISIBILITY_RUNTIME:
+ if (!keep.runtimeVisibleParameterAnnotations) {
+ return false;
+ }
+ break;
+ case DexAnnotation.VISIBILITY_BUILD:
+ if (!keep.runtimeInvisibleParamterAnnotations) {
+ return false;
+ }
+ break;
+ default:
+ throw new Unreachable("Unexpected annotation visibility.");
+ }
+ return appInfo.liveTypes.contains(annotation.annotation.type);
+ }
+
+ public void run() {
+ keep.ensureValid(minificationEnabled);
+ for (DexProgramClass clazz : appInfo.classes()) {
+ clazz.annotations = stripAnnotations(clazz.annotations, this::filterAnnotations);
+ processMethods(clazz.virtualMethods());
+ processMethods(clazz.directMethods());
+ processFields(clazz.staticFields());
+ processFields(clazz.instanceFields());
+ }
+ }
+
+ private void processMethods(DexEncodedMethod[] methods) {
+ for (DexEncodedMethod method : methods) {
+ method.annotations = stripAnnotations(method.annotations, this::filterAnnotations);
+ method.parameterAnnotations = stripAnnotations(method.parameterAnnotations,
+ this::filterParameterAnnotations);
+ }
+ }
+
+ private void processFields(DexEncodedField[] fields) {
+ for (DexEncodedField field : fields) {
+ field.annotations = stripAnnotations(field.annotations, this::filterAnnotations);
+ }
+ }
+
+ private DexAnnotationSetRefList stripAnnotations(DexAnnotationSetRefList annotations,
+ Predicate<DexAnnotation> filter) {
+ DexAnnotationSet[] filtered = null;
+ for (int i = 0; i < annotations.values.length; i++) {
+ DexAnnotationSet updated = stripAnnotations(annotations.values[i], filter);
+ if (updated != annotations.values[i]) {
+ if (filtered == null) {
+ filtered = annotations.values.clone();
+ filtered[i] = updated;
+ }
+ }
+ }
+ if (filtered == null) {
+ return annotations;
+ } else {
+ if (Arrays.stream(filtered).allMatch(DexAnnotationSet::isEmpty)) {
+ return DexAnnotationSetRefList.empty();
+ }
+ return new DexAnnotationSetRefList(filtered);
+ }
+ }
+
+ private DexAnnotationSet stripAnnotations(DexAnnotationSet annotations,
+ Predicate<DexAnnotation> filter) {
+ ArrayList<DexAnnotation> filtered = null;
+ for (int i = 0; i < annotations.annotations.length; i++) {
+ DexAnnotation annotation = annotations.annotations[i];
+ if (filter.test(annotation)) {
+ if (filtered != null) {
+ filtered.add(annotation);
+ }
+ } else {
+ if (filtered == null) {
+ filtered = new ArrayList<>(annotations.annotations.length);
+ for (int j = 0; j < i; j++) {
+ filtered.add(annotations.annotations[j]);
+ }
+ }
+ }
+ }
+ if (filtered == null) {
+ return annotations;
+ } else if (filtered.isEmpty()) {
+ return DexAnnotationSet.empty();
+ } else {
+ return new DexAnnotationSet(filtered.toArray(new DexAnnotation[filtered.size()]));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
new file mode 100644
index 0000000..75b7495
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import java.util.Arrays;
+import java.util.Set;
+
+public class DiscardedChecker {
+
+ private final Set<DexItem> checkDiscarded;
+ private final DexApplication application;
+ private boolean fail = false;
+
+ public DiscardedChecker(RootSet rootSet, DexApplication application) {
+ this.checkDiscarded = rootSet.checkDiscarded;
+ this.application = application;
+ }
+
+ public void run() {
+ for (DexProgramClass clazz : application.classes()) {
+ if (checkDiscarded.contains(clazz)) {
+ report(clazz);
+ }
+ processSubItems(clazz.directMethods());
+ processSubItems(clazz.virtualMethods());
+ processSubItems(clazz.staticFields());
+ processSubItems(clazz.instanceFields());
+ }
+ if (fail) {
+ throw new CompilationError("Discard checks failed.");
+ }
+ }
+
+ private void processSubItems(DexItem[] items) {
+ Arrays.stream(items).filter(checkDiscarded::contains).forEach(this::report);
+ }
+
+ private void report(DexItem item) {
+ System.err.println("Item " + item.toSourceString() + " was not discarded.");
+ fail = true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
new file mode 100644
index 0000000..07e641a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -0,0 +1,1253 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.Descriptor;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.KeyedDexItem;
+import com.android.tools.r8.graph.PresortedComparable;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Queues;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+
+/**
+ * Approximates the runtime dependencies for the given set of roots.
+ *
+ * <p>The implementation filters the static call-graph with liveness information on classes to
+ * remove virtual methods that are reachable by their static type but are unreachable at runtime as
+ * they are not visible from any instance.
+ *
+ * <p>As result of the analysis, an instance of {@link AppInfoWithLiveness} is returned. See the
+ * field descriptions for details.
+ */
+public class Enqueuer {
+
+ private final AppInfoWithSubtyping appInfo;
+ private final RootSet rootSet;
+
+ private Map<DexType, Set<DexMethod>> virtualInvokes = Maps.newIdentityHashMap();
+ private Map<DexType, Set<DexMethod>> superInvokes = Maps.newIdentityHashMap();
+ private Map<DexType, Set<DexMethod>> directInvokes = Maps.newIdentityHashMap();
+ private Map<DexType, Set<DexMethod>> staticInvokes = Maps.newIdentityHashMap();
+ private Map<DexType, Set<DexField>> instanceFieldsWritten = Maps.newIdentityHashMap();
+ private Map<DexType, Set<DexField>> instanceFieldsRead = Maps.newIdentityHashMap();
+ private Map<DexType, Set<DexField>> staticFieldsRead = Maps.newIdentityHashMap();
+ private Map<DexType, Set<DexField>> staticFieldsWritten = Maps.newIdentityHashMap();
+
+ /**
+ * This map keeps a view of all virtual methods that are reachable from virtual invokes. A method
+ * is reachable even if no live subtypes exist, so this is not sufficient for inclusion in the
+ * live set.
+ */
+ private Map<DexType, SetWithReason<DexEncodedMethod>> reachableVirtualMethods = Maps
+ .newIdentityHashMap();
+ /**
+ * Tracks the dependency between a method and the super-method it calls, if any. Used to make
+ * super methods become live when they become reachable from a live sub-method.
+ */
+ private Map<DexEncodedMethod, Set<DexEncodedMethod>> superInvokeDependencies = Maps
+ .newIdentityHashMap();
+ /**
+ * Set of instance fields that can be reached by read/write operations.
+ */
+ private Map<DexType, SetWithReason<DexEncodedField>> reachableInstanceFields = Maps
+ .newIdentityHashMap();
+
+ /**
+ * Set of types that are mentioned in the program. We at least need an empty abstract classitem
+ * for these.
+ */
+ private Set<DexType> liveTypes = Sets.newIdentityHashSet();
+ /**
+ * Set of types that are actually instantiated. These cannot be abstract.
+ */
+ private SetWithReason<DexType> instantiatedTypes = new SetWithReason<>();
+ /**
+ * Set of methods that are the immediate target of an invoke. They might not actually be live but
+ * are required so that invokes can find the method. If a method is only a target but not live,
+ * its implementation may be removed and it may be marked abstract.
+ */
+ private SetWithReason<DexEncodedMethod> targetedMethods = new SetWithReason<>();
+ /**
+ * Set of methods that belong to live classes and can be reached by invokes. These need to be
+ * kept.
+ */
+ private SetWithReason<DexEncodedMethod> liveMethods = new SetWithReason<>();
+
+ /**
+ * Set of fields that belong to live classes and can be reached by invokes. These need to be
+ * kept.
+ */
+ private SetWithReason<DexEncodedField> liveFields = new SetWithReason<>();
+
+ /**
+ * A queue of items that need processing. Different items trigger different actions:
+ */
+ private Queue<Action> workList = Queues.newArrayDeque();
+
+ /**
+ * A cache for DexMethod that have been marked reachable.
+ */
+ private Set<DexMethod> virtualTargetsMarkedAsReachable = Sets.newIdentityHashSet();
+
+ /**
+ * A set of dexitems we have reported missing to dedupe warnings.
+ */
+ private Set<DexItem> reportedMissing = Sets.newIdentityHashSet();
+
+ /**
+ * A set of items that we are keeping due to keep rules. This may differ from the rootSet
+ * due to dependent keep rules.
+ */
+ private Set<DexItem> pinnedItems = Sets.newIdentityHashSet();
+
+ /**
+ * A map from classes to annotations that need to be processed should the classes ever
+ * become live.
+ */
+ private final Map<DexType, Set<DexAnnotation>> deferredAnnotations = new IdentityHashMap<>();
+
+ public Enqueuer(RootSet rootSet, AppInfoWithSubtyping appInfo) {
+ this.appInfo = appInfo;
+ this.rootSet = rootSet;
+ // Translate the result of root-set computation into enqueuer actions.
+ enqueueRootItems(rootSet.noShrinking);
+ }
+
+ private void enqueueRootItems(Map<DexItem, ProguardKeepRule> items) {
+ workList.addAll(
+ items.entrySet().stream().map(Action::forRootItem).collect(Collectors.toList()));
+ pinnedItems.addAll(items.keySet());
+ }
+
+ //
+ // Things to do with registering events. This is essentially the interface for byte-code
+ // traversals.
+ //
+
+ private <S extends DexItem, T extends Descriptor<S, T>> boolean registerItemWithTarget(
+ Map<DexType, Set<T>> seen, T item) {
+ DexType holder = item.getHolder();
+ if (holder.isArrayType()) {
+ holder = holder.toBaseType(appInfo.dexItemFactory);
+ }
+ if (!holder.isClassType()) {
+ return false;
+ }
+ markTypeAsLive(holder);
+ return seen.computeIfAbsent(item.getHolder(), (ignore) -> Sets.newIdentityHashSet()).add(item);
+ }
+
+ private class UseRegistry extends com.android.tools.r8.graph.UseRegistry {
+
+ private final DexEncodedMethod currentMethod;
+
+ private UseRegistry(DexEncodedMethod currentMethod) {
+ this.currentMethod = currentMethod;
+ }
+
+ @Override
+ public boolean registerInvokeVirtual(DexMethod method) {
+ if (!registerItemWithTarget(virtualInvokes, method)) {
+ return false;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Register invokeVirtual `%s`.", method);
+ }
+ workList.add(Action.markReachableVirtual(method, KeepReason.invokedFrom(currentMethod)));
+ return true;
+ }
+
+ @Override
+ public boolean registerInvokeDirect(DexMethod method) {
+ if (!registerItemWithTarget(directInvokes, method)) {
+ return false;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Register invokeDirect `%s`.", method);
+ }
+ handleInvokeOfDirectTarget(method, KeepReason.invokedFrom(currentMethod));
+ return true;
+ }
+
+ @Override
+ public boolean registerInvokeStatic(DexMethod method) {
+ if (!registerItemWithTarget(staticInvokes, method)) {
+ return false;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Register invokeStatic `%s`.", method);
+ }
+ handleInvokeOfStaticTarget(method, KeepReason.invokedFrom(currentMethod));
+ return true;
+ }
+
+ @Override
+ public boolean registerInvokeInterface(DexMethod method) {
+ if (!registerItemWithTarget(virtualInvokes, method)) {
+ return false;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Register invokeInterface `%s`.", method);
+ }
+ workList.add(Action.markReachableInterface(method, KeepReason.invokedFrom(currentMethod)));
+ return true;
+ }
+
+ @Override
+ public boolean registerInvokeSuper(DexMethod method) {
+ if (!registerItemWithTarget(superInvokes, method)) {
+ return false;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Register invokeSuper `%s`.", method);
+ }
+ workList.add(Action.markReachableSuper(method, currentMethod));
+ return true;
+ }
+
+ @Override
+ public boolean registerInstanceFieldWrite(DexField field) {
+ if (!registerItemWithTarget(instanceFieldsWritten, field)) {
+ return false;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Register Iput `%s`.", field);
+ }
+ // TODO(herhut): We have to add this, but DCR should eliminate dead writes.
+ workList.add(Action.markReachableField(field, KeepReason.fieldReferencedIn(currentMethod)));
+ return true;
+ }
+
+ @Override
+ public boolean registerInstanceFieldRead(DexField field) {
+ if (!registerItemWithTarget(instanceFieldsRead, field)) {
+ return false;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Register Iget `%s`.", field);
+ }
+ workList.add(Action.markReachableField(field, KeepReason.fieldReferencedIn(currentMethod)));
+ return true;
+ }
+
+ @Override
+ public boolean registerNewInstance(DexType type) {
+ if (instantiatedTypes.contains(type)) {
+ return false;
+ }
+ DexClass clazz = appInfo.definitionFor(type);
+ if (clazz == null) {
+ reportMissingClass(type);
+ return false;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Register new instatiation of `%s`.", clazz);
+ }
+ workList.add(Action.markInstantiated(clazz, KeepReason.instantiatedIn(currentMethod)));
+ return true;
+ }
+
+ @Override
+ public boolean registerStaticFieldRead(DexField field) {
+ if (!registerItemWithTarget(staticFieldsRead, field)) {
+ return false;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Register Sget `%s`.", field);
+ }
+ markStaticFieldAsLive(field, KeepReason.fieldReferencedIn(currentMethod));
+ return true;
+ }
+
+ @Override
+ public boolean registerStaticFieldWrite(DexField field) {
+ if (!registerItemWithTarget(staticFieldsWritten, field)) {
+ return false;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Register Sput `%s`.", field);
+ }
+ // TODO(herhut): We have to add this, but DCR should eliminate dead writes.
+ markStaticFieldAsLive(field, KeepReason.fieldReferencedIn(currentMethod));
+ return true;
+ }
+
+ @Override
+ public boolean registerTypeReference(DexType type) {
+ DexType baseType = type.toBaseType(appInfo.dexItemFactory);
+ if (baseType.isClassType()) {
+ markTypeAsLive(baseType);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ //
+ // Actual actions performed.
+ //
+
+ private void markTypeAsLive(DexType type) {
+ assert type.isClassType();
+ if (liveTypes.add(type)) {
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Type `%s` has become live.", type);
+ }
+ DexClass holder = appInfo.definitionFor(type);
+ if (holder == null) {
+ reportMissingClass(type);
+ return;
+ }
+ for (DexType iface : holder.interfaces.values) {
+ markTypeAsLive(iface);
+ }
+ if (holder.superType != null) {
+ markTypeAsLive(holder.superType);
+ }
+ if (!holder.annotations.isEmpty()) {
+ processAnnotations(holder.annotations.annotations);
+ }
+ // We also need to add the corresponding <clinit> to the set of live methods, as otherwise
+ // static field initialization (and other class-load-time sideeffects) will not happen.
+ DexEncodedMethod clinit = holder.getClassInitializer(appInfo.dexItemFactory);
+ if (clinit != null) {
+ markDirectStaticOrConstructorMethodAsLive(clinit, KeepReason.reachableFromLiveType(type));
+ }
+ // If this type has deferred annotations, we have to process those now, too.
+ Set<DexAnnotation> annotations = deferredAnnotations.remove(type);
+ if (annotations != null) {
+ annotations.forEach(this::handleAnnotationOfLiveType);
+ }
+ }
+ }
+
+ private void handleAnnotationOfLiveType(DexAnnotation annotation) {
+ AnnotationReferenceMarker referenceMarker = new AnnotationReferenceMarker(
+ annotation.annotation.type, appInfo.dexItemFactory);
+ annotation.annotation.collectIndexedItems(referenceMarker);
+ }
+
+ private void processAnnotations(DexAnnotation[] annotations) {
+ for (DexAnnotation annotation : annotations) {
+ DexType type = annotation.annotation.type;
+ if (liveTypes.contains(type)) {
+ // The type of this annotation is already live, so pick up its dependencies.
+ handleAnnotationOfLiveType(annotation);
+ } else {
+ // Remember this annotation for later.
+ deferredAnnotations.computeIfAbsent(type, ignore -> new HashSet<>()).add(annotation);
+ }
+ }
+ }
+
+ private void handleInvokeOfStaticTarget(DexMethod method, KeepReason reason) {
+ DexEncodedMethod encodedMethod = appInfo.lookupStaticTarget(method);
+ if (encodedMethod == null) {
+ reportMissingMethod(method);
+ return;
+ }
+ markDirectStaticOrConstructorMethodAsLive(encodedMethod, reason);
+ }
+
+ private void handleInvokeOfDirectTarget(DexMethod method, KeepReason reason) {
+ DexEncodedMethod encodedMethod = appInfo.lookupDirectTarget(method);
+ if (encodedMethod == null) {
+ reportMissingMethod(method);
+ return;
+ }
+ markDirectStaticOrConstructorMethodAsLive(encodedMethod, reason);
+ }
+
+ private void reportMissingClass(DexType clazz) {
+ if (Log.ENABLED && reportedMissing.add(clazz)) {
+ Log.verbose(Enqueuer.class, "Class `%s` is missing.", clazz);
+ }
+ }
+
+ private void reportMissingMethod(DexMethod method) {
+ if (Log.ENABLED && reportedMissing.add(method)) {
+ Log.verbose(Enqueuer.class, "Method `%s` is missing.", method);
+ }
+ }
+
+ private void reportMissingField(DexField field) {
+ if (Log.ENABLED && reportedMissing.add(field)) {
+ Log.verbose(Enqueuer.class, "Field `%s` is missing.", field);
+ }
+ }
+
+ private void markMethodAsTargeted(DexEncodedMethod encodedMethod, KeepReason reason) {
+ markTypeAsLive(encodedMethod.method.holder);
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Method `%s` is targeted.", encodedMethod.method);
+ }
+ targetedMethods.add(encodedMethod, reason);
+ }
+
+ /**
+ * Adds the class to the set of instantiated classes and marks its fields and methods live
+ * depending on the currently seen invokes and field reads.
+ */
+ private void processNewlyInstantiatedClass(DexClass clazz, KeepReason reason) {
+ if (!instantiatedTypes.add(clazz.type, reason)) {
+ return;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Class `%s` is instantiated, processing...", clazz);
+ }
+ // This class becomes live, so it and all its supertypes become live types.
+ markTypeAsLive(clazz.type);
+ // For all methods of the class, if we have seen a call, mark the method live.
+ // We only do this for virtual calls, as the other ones will be done directly.
+ transitionMethodsForInstantiatedClass(clazz.type);
+ // For all instance fields visible from the class, mark them live if we have seen a read.
+ transitionFieldsForInstantiatedClass(clazz.type);
+ // Add all dependent members to the workqueue.
+ enqueueRootItems(rootSet.getDependentItems(clazz.type));
+ }
+
+ /**
+ * Marks all methods live that can be reached by calls previously seen.
+ *
+ * <p>This should only be invoked if the given type newly becomes instantiated. In essence, this
+ * method replays all the invokes we have seen so far that could apply to this type and marks
+ * the corresponding methods live.
+ *
+ * <p>Only methods that are visible in this type are considered. That is, only those methods that
+ * are either defined directly on this type or that are defined on a supertype but are not
+ * shadowed by another inherited method.
+ */
+ private void transitionMethodsForInstantiatedClass(DexType type) {
+ Set<Wrapper<DexMethod>> seen = new HashSet<>();
+ MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
+ do {
+ DexClass clazz = appInfo.definitionFor(type);
+ if (clazz == null) {
+ reportMissingClass(type);
+ // TODO(herhut): In essence, our subtyping chain is broken here. Handle that case better.
+ break;
+ }
+ SetWithReason<DexEncodedMethod> reachableMethods = reachableVirtualMethods.get(type);
+ if (reachableMethods != null) {
+ for (DexEncodedMethod encodedMethod : reachableMethods.getItems()) {
+ Wrapper<DexMethod> ignoringClass = equivalence.wrap(encodedMethod.method);
+ if (!seen.contains(ignoringClass)) {
+ seen.add(ignoringClass);
+ markVirtualMethodAsLive(encodedMethod, KeepReason.reachableFromLiveType(type));
+ }
+ }
+ }
+ type = clazz.superType;
+ } while (type != null && !instantiatedTypes.contains(type));
+ }
+
+ /**
+ * Marks all fields live that can be reached by a read assuming that the given type or one of
+ * its subtypes is instantiated.
+ */
+ private void transitionFieldsForInstantiatedClass(DexType type) {
+ do {
+ DexClass clazz = appInfo.definitionFor(type);
+ if (clazz == null) {
+ // TODO(herhut) The subtype chain is broken. We need a way to deal with this better.
+ reportMissingClass(type);
+ break;
+ }
+ SetWithReason<DexEncodedField> reachableFields = reachableInstanceFields.get(type);
+ if (reachableFields != null) {
+ for (DexEncodedField field : reachableFields.getItems()) {
+ markInstanceFieldAsLive(field, KeepReason.reachableFromLiveType(type));
+ }
+ }
+ type = clazz.superType;
+ } while (type != null && !instantiatedTypes.contains(type));
+ }
+
+ private void markStaticFieldAsLive(DexField field, KeepReason reason) {
+ // Mark the type live here, so that the class exists at runtime. Note that this also marks all
+ // supertypes as live, so even if the field is actually on a supertype, its class will be live.
+ markTypeAsLive(field.clazz);
+ // Find the actual field.
+ DexEncodedField encodedField = appInfo.lookupStaticTarget(field.clazz, field);
+ if (encodedField == null) {
+ reportMissingField(field);
+ return;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Adding static field `%s` to live set.", encodedField.field);
+ }
+ liveFields.add(encodedField, reason);
+ }
+
+ private void markInstanceFieldAsLive(DexEncodedField field, KeepReason reason) {
+ assert field != null;
+ markTypeAsLive(field.field.clazz);
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Adding instance field `%s` to live set.", field.field);
+ }
+ liveFields.add(field, reason);
+ }
+
+ private void markDirectStaticOrConstructorMethodAsLive(
+ DexEncodedMethod encodedMethod, KeepReason reason) {
+ assert encodedMethod != null;
+ if (!liveMethods.contains(encodedMethod)) {
+ markTypeAsLive(encodedMethod.method.holder);
+ markMethodAsTargeted(encodedMethod, reason);
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Method `%s` has become live due to direct invoke",
+ encodedMethod.method);
+ }
+ workList.add(Action.markMethodLive(encodedMethod, reason));
+ }
+ }
+
+ private void markVirtualMethodAsLive(DexEncodedMethod method, KeepReason reason) {
+ assert method != null;
+ if (!liveMethods.contains(method)) {
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Adding virtual method `%s` to live set.", method.method);
+ }
+ workList.add(Action.markMethodLive(method, reason));
+ }
+ }
+
+ private boolean isInstantiatedOrHasInstantiatedSubtype(DexType type) {
+ return instantiatedTypes.contains(type)
+ || appInfo.subtypes(type).stream().anyMatch(instantiatedTypes::contains);
+ }
+
+ private void markFieldAsReachable(DexField field, KeepReason reason) {
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Marking instance field `%s` as reachable.", field);
+ }
+ DexEncodedField encodedField = appInfo.lookupInstanceTarget(field.clazz, field);
+ if (encodedField == null) {
+ reportMissingField(field);
+ return;
+ }
+ SetWithReason<DexEncodedField> reachable = reachableInstanceFields
+ .computeIfAbsent(encodedField.field.clazz, ignore -> new SetWithReason<>());
+ if (reachable.add(encodedField, reason) && isInstantiatedOrHasInstantiatedSubtype(
+ encodedField.field.clazz)) {
+ // We have at least one live subtype, so mark it as live.
+ markInstanceFieldAsLive(encodedField, reason);
+ }
+ }
+
+ private void markVirtualMethodAsReachable(DexMethod method, boolean interfaceInvoke,
+ KeepReason reason) {
+ if (!virtualTargetsMarkedAsReachable.add(method)) {
+ return;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Marking virtual method `%s` as reachable.", method);
+ }
+ if (method.holder.isArrayType()) {
+ // This is an array type, so the actual class will be generated at runtime. We treat this
+ // like an invoke on a direct subtype of java.lang.Object that has no further subtypes.
+ // As it has no subtypes, it cannot affect liveness of the program we are processing.
+ // Ergo, we can ignore it. We need to make sure that the element type is available, though.
+ DexType baseType = method.holder.toBaseType(appInfo.dexItemFactory);
+ if (baseType.isClassType()) {
+ markTypeAsLive(baseType);
+ }
+ return;
+ }
+ DexClass holder = appInfo.definitionFor(method.holder);
+ if (holder == null) {
+ reportMissingClass(method.holder);
+ return;
+ }
+ DexEncodedMethod topTarget = appInfo.lookupVirtualDefinition(method.holder, method);
+ if (topTarget == null) {
+ reportMissingMethod(method);
+ return;
+ }
+ // We have to mark this as targeted, as even if this specific instance never becomes live, we
+ // need at least an abstract version of it so that we have a target for the corresponding
+ // invoke.
+ markMethodAsTargeted(topTarget, reason);
+ Set<DexEncodedMethod> targets;
+ if (interfaceInvoke) {
+ if (!holder.isInterface()) {
+ throw new CompilationError(
+ "InvokeInterface on non-interface method " + method.toSourceString() + ".");
+ }
+ targets = appInfo.lookupInterfaceTargets(method);
+ } else {
+ if (holder.isInterface()) {
+ throw new CompilationError(
+ "InvokeVirtual on interface method " + method.toSourceString() + ".");
+ }
+ targets = appInfo.lookupVirtualTargets(method);
+ }
+ for (DexEncodedMethod encodedMethod : targets) {
+ SetWithReason<DexEncodedMethod> reachable = reachableVirtualMethods
+ .computeIfAbsent(encodedMethod.method.holder, (ignore) -> new SetWithReason<>());
+ if (reachable.add(encodedMethod, reason)) {
+ // If the holder type is instantiated, the method is live. Otherwise check whether we find
+ // a subtype that does not shadow this methods but is instantiated.
+ // Note that library classes are always considered instantiated, as we do not know where
+ // they are instantiated.
+ if (isInstantiatedOrHasInstantiatedSubtype(encodedMethod.method.holder)) {
+ if (instantiatedTypes.contains(encodedMethod.method.holder)) {
+ markVirtualMethodAsLive(encodedMethod,
+ KeepReason.reachableFromLiveType(encodedMethod.method.holder));
+ } else {
+ Deque<DexType> worklist = new ArrayDeque<>();
+ fillWorkList(worklist, encodedMethod.method.holder);
+ while (!worklist.isEmpty()) {
+ DexType current = worklist.pollFirst();
+ DexClass currentHolder = appInfo.definitionFor(current);
+ if (currentHolder == null
+ || currentHolder.findVirtualTarget(encodedMethod.method) != null) {
+ continue;
+ }
+ if (instantiatedTypes.contains(current)) {
+ markVirtualMethodAsLive(encodedMethod, KeepReason.reachableFromLiveType(current));
+ break;
+ }
+ fillWorkList(worklist, current);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static void fillWorkList(Deque<DexType> worklist, DexType type) {
+ if (type.isInterface()) {
+ // We need to check if the method is shadowed by a class that directly implements
+ // the interface and go recursively down to the sub interfaces to reach class
+ // implementing the interface
+ type.forAllImplementsSubtypes(worklist::addLast);
+ type.forAllExtendsSubtypes(worklist::addLast);
+ } else {
+ type.forAllExtendsSubtypes(worklist::addLast);
+ }
+ }
+
+ private void markSuperMethodAsReachable(DexMethod method, DexEncodedMethod from) {
+ DexEncodedMethod target = appInfo.lookupVirtualTarget(method.holder, method);
+ if (target == null) {
+ reportMissingMethod(method);
+ return;
+ }
+ assert !superInvokeDependencies.containsKey(from) || !superInvokeDependencies.get(from)
+ .contains(target);
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Adding super constraint from `%s` to `%s`", from.method,
+ target.method);
+ }
+ superInvokeDependencies.computeIfAbsent(from, ignore -> Sets.newIdentityHashSet()).add(target);
+ if (liveMethods.contains(from)) {
+ markVirtualMethodAsLive(target, KeepReason.invokedViaSuperFrom(from));
+ }
+ }
+
+ public ReasonPrinter getReasonPrinter(Set<DexItem> queriedItems) {
+ // If no reason was asked, just return a no-op printer to avoid computing the information.
+ // This is the common path.
+ if (queriedItems.isEmpty()) {
+ return ReasonPrinter.getNoOpPrinter();
+ }
+ Map<DexItem, KeepReason> reachability = new HashMap<>();
+ for (SetWithReason<DexEncodedMethod> mappings : reachableVirtualMethods.values()) {
+ reachability.putAll(mappings.getReasons());
+ }
+ for (SetWithReason<DexEncodedField> mappings : reachableInstanceFields.values()) {
+ reachability.putAll(mappings.getReasons());
+ }
+ return new ReasonPrinter(queriedItems, liveFields.getReasons(), liveMethods.getReasons(),
+ reachability, instantiatedTypes.getReasons());
+ }
+
+ public AppInfoWithLiveness run(Timing timing) {
+ timing.begin("Grow the tree.");
+ appInfo.libraryClasses().forEach(this::markAllVirtualMethodsReachable);
+ try {
+ while (!workList.isEmpty()) {
+ Action action = workList.poll();
+ switch (action.kind) {
+ case MARK_INSTANTIATED:
+ processNewlyInstantiatedClass((DexClass) action.target, action.reason);
+ break;
+ case MARK_REACHABLE_FIELD:
+ markFieldAsReachable((DexField) action.target, action.reason);
+ break;
+ case MARK_REACHABLE_VIRTUAL:
+ markVirtualMethodAsReachable((DexMethod) action.target, false, action.reason);
+ break;
+ case MARK_REACHABLE_INTERFACE:
+ markVirtualMethodAsReachable((DexMethod) action.target, true, action.reason);
+ break;
+ case MARK_REACHABLE_SUPER:
+ markSuperMethodAsReachable((DexMethod) action.target,
+ (DexEncodedMethod) action.context);
+ break;
+ case MARK_METHOD_KEPT:
+ markMethodAsKept((DexEncodedMethod) action.target, action.reason);
+ break;
+ case MARK_FIELD_KEPT:
+ markFieldAsKept((DexEncodedField) action.target, action.reason);
+ break;
+ case MARK_METHOD_LIVE:
+ processNewlyLiveMethod(((DexEncodedMethod) action.target), action.reason);
+ break;
+ default:
+ throw new IllegalArgumentException(action.kind.toString());
+ }
+ }
+ if (Log.ENABLED) {
+ Set<DexEncodedMethod> allLive = Sets.newIdentityHashSet();
+ for (Entry<DexType, SetWithReason<DexEncodedMethod>> entry : reachableVirtualMethods
+ .entrySet()) {
+ allLive.addAll(entry.getValue().getItems());
+ }
+ Set reachableNotLive = Sets.difference(allLive, liveMethods.getItems());
+ Log.debug(getClass(), "%s methods are reachable but not live", reachableNotLive.size());
+ Log.info(getClass(), "Only reachable: %s", reachableNotLive);
+ Set liveButNotInstantiated = Sets.difference(liveTypes, instantiatedTypes.getItems());
+ Log.debug(getClass(), "%s classes are live but not instantiated",
+ liveButNotInstantiated.size());
+ Log.info(getClass(), "Live but not instantiated: %s", liveButNotInstantiated);
+ SetView<DexEncodedMethod> targetedButNotLive = Sets
+ .difference(targetedMethods.getItems(), liveMethods.getItems());
+ Log.debug(getClass(), "%s methods are targeted but not live", targetedButNotLive.size());
+ Log.info(getClass(), "Targeted but not live: %s", targetedButNotLive);
+ }
+ assert liveTypes.stream().allMatch(DexType::isClassType);
+ assert instantiatedTypes.getItems().stream().allMatch(DexType::isClassType);
+ } finally {
+ timing.end();
+ }
+ return new AppInfoWithLiveness(appInfo, this);
+ }
+
+ private void markMethodAsKept(DexEncodedMethod target, KeepReason reason) {
+ DexClass holder = appInfo.definitionFor(target.method.holder);
+ // If this method no longer has a corresponding class then we have shaken it away before.
+ if (holder == null) {
+ return;
+ }
+ if (!target.accessFlags.isStatic()
+ && !target.accessFlags.isConstructor()
+ && !target.accessFlags.isPrivate()) {
+ // A virtual method. Mark it as reachable so that subclasses, if instantiated, keep
+ // their overrides. However, we don't mark it live, as a keep rule might not imply that
+ // the corresponding class is live.
+ markVirtualMethodAsReachable(target.method, holder.accessFlags.isInterface(), reason);
+ } else {
+ markDirectStaticOrConstructorMethodAsLive(target, reason);
+ }
+ }
+
+ private void markFieldAsKept(DexEncodedField target, KeepReason reason) {
+ // If this field no longer has a corresponding class, then we have shaken it away before.
+ if (appInfo.definitionFor(target.field.clazz) == null) {
+ return;
+ }
+ if (target.accessFlags.isStatic()) {
+ markStaticFieldAsLive(target.field, reason);
+ } else {
+ markFieldAsReachable(target.field, reason);
+ }
+ }
+
+ private void markAllVirtualMethodsReachable(DexClass clazz) {
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Marking all methods of library class `%s` as reachable.",
+ clazz.type);
+ }
+ for (DexEncodedMethod encodedMethod : clazz.virtualMethods()) {
+ markMethodAsTargeted(encodedMethod, KeepReason.isLibraryMethod());
+ markVirtualMethodAsReachable(encodedMethod.method, clazz.isInterface(),
+ KeepReason.isLibraryMethod());
+ }
+ }
+
+ private void processNewlyLiveMethod(DexEncodedMethod method, KeepReason reason) {
+ if (liveMethods.add(method, reason)) {
+ DexClass holder = appInfo.definitionFor(method.method.holder);
+ assert holder != null;
+ if (holder.isLibraryClass()) {
+ // We do not process library classes.
+ return;
+ }
+ Set<DexEncodedMethod> superCallTargets = superInvokeDependencies.get(method);
+ if (superCallTargets != null) {
+ for (DexEncodedMethod superCallTarget : superCallTargets) {
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Found super invoke constraint on `%s`.",
+ superCallTarget.method);
+ }
+ markVirtualMethodAsLive(superCallTarget, KeepReason.invokedViaSuperFrom(method));
+ }
+ }
+ processAnnotations(method.annotations.annotations);
+ for (DexAnnotationSet parameterAnnotation : method.parameterAnnotations.values) {
+ processAnnotations(parameterAnnotation.annotations);
+ }
+ method.registerReachableDefinitions(new UseRegistry(method));
+ }
+ }
+
+ private Set<DexField> collectFields(Map<DexType, Set<DexField>> map) {
+ Set<DexField> set = Sets.newIdentityHashSet();
+ map.values().forEach(set::addAll);
+ return set;
+ }
+
+ Set<DexField> collectInstanceFieldsRead() {
+ return Collections.unmodifiableSet(collectFields(instanceFieldsRead));
+ }
+
+ Set<DexField> collectInstanceFieldsWritten() {
+ return Collections.unmodifiableSet(collectFields(instanceFieldsWritten));
+ }
+
+ Set<DexField> collectStaticFieldsRead() {
+ return Collections.unmodifiableSet(collectFields(staticFieldsRead));
+ }
+
+ Set<DexField> collectStaticFieldsWritten() {
+ return Collections.unmodifiableSet(collectFields(staticFieldsWritten));
+ }
+
+ private static class Action {
+
+ final Kind kind;
+ final DexItem target;
+ final DexItem context;
+ final KeepReason reason;
+
+ private Action(Kind kind, DexItem target, DexItem context, KeepReason reason) {
+ this.kind = kind;
+ this.target = target;
+ this.context = context;
+ this.reason = reason;
+ }
+
+ public static Action markReachableVirtual(DexMethod method, KeepReason reason) {
+ return new Action(Kind.MARK_REACHABLE_VIRTUAL, method, null, reason);
+ }
+
+ public static Action markReachableInterface(DexMethod method, KeepReason reason) {
+ return new Action(Kind.MARK_REACHABLE_INTERFACE, method, null, reason);
+ }
+
+ public static Action markReachableSuper(DexMethod method, DexEncodedMethod from) {
+ return new Action(Kind.MARK_REACHABLE_SUPER, method, from, null);
+ }
+
+ public static Action markReachableField(DexField field, KeepReason reason) {
+ return new Action(Kind.MARK_REACHABLE_FIELD, field, null, reason);
+ }
+
+ public static Action markInstantiated(DexClass clazz, KeepReason reason) {
+ return new Action(Kind.MARK_INSTANTIATED, clazz, null, reason);
+ }
+
+ public static Action markMethodLive(DexEncodedMethod method, KeepReason reason) {
+ return new Action(Kind.MARK_METHOD_LIVE, method, null, reason);
+ }
+
+ public static Action markMethodKept(DexEncodedMethod method, KeepReason reason) {
+ return new Action(Kind.MARK_METHOD_KEPT, method, null, reason);
+ }
+
+ public static Action markFieldKept(DexEncodedField method, KeepReason reason) {
+ return new Action(Kind.MARK_FIELD_KEPT, method, null, reason);
+ }
+
+ public static Action forRootItem(Map.Entry<DexItem, ProguardKeepRule> root) {
+ DexItem item = root.getKey();
+ KeepReason reason = KeepReason.dueToKeepRule(root.getValue());
+ if (item instanceof DexClass) {
+ return markInstantiated((DexClass) item, reason);
+ } else if (item instanceof DexEncodedField) {
+ return markFieldKept((DexEncodedField) item, reason);
+ } else if (item instanceof DexEncodedMethod) {
+ return markMethodKept((DexEncodedMethod) item, reason);
+ } else {
+ throw new IllegalArgumentException(item.toString());
+ }
+ }
+
+ private enum Kind {
+ MARK_REACHABLE_VIRTUAL,
+ MARK_REACHABLE_INTERFACE,
+ MARK_REACHABLE_SUPER,
+ MARK_REACHABLE_FIELD,
+ MARK_INSTANTIATED,
+ MARK_METHOD_LIVE,
+ MARK_METHOD_KEPT,
+ MARK_FIELD_KEPT
+ }
+ }
+
+ /**
+ * Encapsulates liveness and reachability information for an application.
+ */
+ public static class AppInfoWithLiveness extends AppInfoWithSubtyping {
+
+ /**
+ * Set of types that are mentioned in the program. We at least need an empty abstract classitem
+ * for these.
+ */
+ final Set<DexType> liveTypes;
+ /**
+ * Set of types that are actually instantiated. These cannot be abstract.
+ */
+ final Set<DexType> instantiatedTypes;
+ /**
+ * Set of methods that are the immediate target of an invoke. They might not actually be live
+ * but are required so that invokes can find the method. If such a method is not live (i.e. not
+ * contained in {@link #liveMethods}, it may be marked as abstract and its implementation may be
+ * removed.
+ */
+ final Set<DexMethod> targetedMethods;
+ /**
+ * Set of methods that belong to live classes and can be reached by invokes. These need to be
+ * kept.
+ */
+ final Set<DexMethod> liveMethods;
+ /**
+ * Set of fields that belong to live classes and can be reached by invokes. These need to be
+ * kept.
+ */
+ final Set<DexField> liveFields;
+ /**
+ * Set of all fields which are read. Combines {@link #instanceFieldsRead} and
+ * {@link #staticFieldsRead}.
+ */
+ public final Set<DexField> fieldsRead;
+ /**
+ * Set of all fields which are written. Combines {@link #instanceFieldsWritten} and
+ * {@link #staticFieldsWritten}.
+ */
+ public final Set<DexField> fieldsWritten;
+ /**
+ * Set of all instance fields which are read.
+ */
+ public final Set<DexField> instanceFieldsRead;
+ /**
+ * Set of all instance fields which are written.
+ */
+ public final Set<DexField> instanceFieldsWritten;
+ /**
+ * Set of all static fields which are read.
+ */
+ public final Set<DexField> staticFieldsRead;
+ /**
+ * Set of all static fields which are written.
+ */
+ public final Set<DexField> staticFieldsWritten;
+ /**
+ * Set of all methods referenced in virtual invokes;
+ */
+ public final Set<DexMethod> virtualInvokes;
+ /**
+ * Set of all methods referenced in super invokes;
+ */
+ public final Set<DexMethod> superInvokes;
+ /**
+ * Set of all methods referenced in direct invokes;
+ */
+ public final Set<DexMethod> directInvokes;
+ /**
+ * Set of all methods referenced in static invokes;
+ */
+ public final Set<DexMethod> staticInvokes;
+ /**
+ * Set of all items that have to be kept independent of whether they are used.
+ */
+ public final Set<DexItem> pinnedItems;
+ /**
+ * All items with assumenosideeffects rule.
+ */
+ public final Map<DexItem, ProguardMemberRule> noSideEffects;
+ /**
+ * All items with assumevalues rule.
+ */
+ public final Map<DexItem, ProguardMemberRule> assumedValues;
+
+ private AppInfoWithLiveness(AppInfoWithSubtyping appInfo, Enqueuer enqueuer) {
+ super(appInfo);
+ this.liveTypes = Collections.unmodifiableSet(enqueuer.liveTypes);
+ this.instantiatedTypes = enqueuer.instantiatedTypes.getItems();
+ this.targetedMethods = toDescriptorSet(enqueuer.targetedMethods.getItems());
+ this.liveMethods = toDescriptorSet(enqueuer.liveMethods.getItems());
+ this.liveFields = toDescriptorSet(enqueuer.liveFields.getItems());
+ this.instanceFieldsRead = enqueuer.collectInstanceFieldsRead();
+ this.instanceFieldsWritten = enqueuer.collectInstanceFieldsWritten();
+ this.staticFieldsRead = enqueuer.collectStaticFieldsRead();
+ this.staticFieldsWritten = enqueuer.collectStaticFieldsWritten();
+ this.fieldsRead = Sets.union(staticFieldsRead, instanceFieldsRead);
+ this.fieldsWritten = Sets.union(staticFieldsWritten, instanceFieldsWritten);
+ this.pinnedItems = Collections.unmodifiableSet(enqueuer.pinnedItems);
+ this.virtualInvokes = joinInvokedMethods(enqueuer.virtualInvokes);
+ this.superInvokes = joinInvokedMethods(enqueuer.superInvokes);
+ this.directInvokes = joinInvokedMethods(enqueuer.directInvokes);
+ this.staticInvokes = joinInvokedMethods(enqueuer.staticInvokes);
+ this.noSideEffects = enqueuer.rootSet.noSideEffects;
+ this.assumedValues = enqueuer.rootSet.assumedValues;
+ assert Sets.intersection(instanceFieldsRead, staticFieldsRead).size() == 0;
+ assert Sets.intersection(instanceFieldsWritten, staticFieldsWritten).size() == 0;
+ }
+
+ private AppInfoWithLiveness(AppInfoWithLiveness previous, DexApplication application) {
+ super(application);
+ this.liveTypes = previous.liveTypes;
+ this.instantiatedTypes = previous.instantiatedTypes;
+ this.targetedMethods = previous.targetedMethods;
+ this.liveMethods = previous.liveMethods;
+ this.liveFields = previous.liveFields;
+ this.instanceFieldsRead = previous.instanceFieldsRead;
+ this.instanceFieldsWritten = previous.instanceFieldsWritten;
+ this.staticFieldsRead = previous.staticFieldsRead;
+ this.staticFieldsWritten = previous.staticFieldsWritten;
+ this.fieldsRead = previous.fieldsRead;
+ this.fieldsWritten = previous.fieldsWritten;
+ this.pinnedItems = previous.pinnedItems;
+ this.noSideEffects = previous.noSideEffects;
+ this.assumedValues = previous.assumedValues;
+ this.virtualInvokes = previous.virtualInvokes;
+ this.superInvokes = previous.superInvokes;
+ this.directInvokes = previous.directInvokes;
+ this.staticInvokes = previous.staticInvokes;
+ assert Sets.intersection(instanceFieldsRead, staticFieldsRead).size() == 0;
+ assert Sets.intersection(instanceFieldsWritten, staticFieldsWritten).size() == 0;
+ }
+
+ private AppInfoWithLiveness(AppInfoWithLiveness previous, GraphLense lense) {
+ super(previous, lense);
+ this.liveTypes = previous.liveTypes;
+ this.instantiatedTypes = rewriteItems(previous.instantiatedTypes, lense::lookupType);
+ this.targetedMethods = rewriteItems(previous.targetedMethods, lense::lookupMethod);
+ this.liveMethods = rewriteItems(previous.liveMethods, lense::lookupMethod);
+ this.liveFields = rewriteItems(previous.liveFields, lense::lookupField);
+ this.instanceFieldsRead = rewriteItems(previous.instanceFieldsRead, lense::lookupField);
+ this.instanceFieldsWritten = rewriteItems(previous.instanceFieldsWritten, lense::lookupField);
+ this.staticFieldsRead = rewriteItems(previous.staticFieldsRead, lense::lookupField);
+ this.staticFieldsWritten = rewriteItems(previous.staticFieldsWritten, lense::lookupField);
+ this.fieldsRead = Sets.union(staticFieldsRead, instanceFieldsRead);
+ this.fieldsWritten = Sets.union(staticFieldsWritten, instanceFieldsWritten);
+ // TODO(herhut): Migrate these to Descriptors, as well.
+ this.pinnedItems = previous.pinnedItems;
+ this.noSideEffects = previous.noSideEffects;
+ this.assumedValues = previous.assumedValues;
+ this.virtualInvokes = rewriteItems(previous.virtualInvokes, lense::lookupMethod);
+ this.superInvokes = rewriteItems(previous.superInvokes, lense::lookupMethod);
+ this.directInvokes = rewriteItems(previous.directInvokes, lense::lookupMethod);
+ this.staticInvokes = rewriteItems(previous.staticInvokes, lense::lookupMethod);
+ assert Sets.intersection(instanceFieldsRead, staticFieldsRead).size() == 0;
+ assert Sets.intersection(instanceFieldsWritten, staticFieldsWritten).size() == 0;
+ }
+
+ private Set<DexMethod> joinInvokedMethods(Map<DexType, Set<DexMethod>> invokes) {
+ ImmutableSet.Builder<DexMethod> builder = ImmutableSet.builder();
+ invokes.values().forEach(builder::addAll);
+ return builder.build();
+ }
+
+ private <T extends PresortedComparable<T>> Set<T> toDescriptorSet(
+ Set<? extends KeyedDexItem<T>> set) {
+ ImmutableSet.Builder<T> builder = ImmutableSet.builder();
+ for (KeyedDexItem<T> item : set) {
+ builder.add(item.getKey());
+ }
+ return builder.build();
+ }
+
+ private static <T> ImmutableSet<T> rewriteItems(Set<T> original,
+ BiFunction<T, DexEncodedMethod, T> rewrite) {
+ ImmutableSet.Builder<T> builder = ImmutableSet.builder();
+ for (T item : original) {
+ builder.add(rewrite.apply(item, null));
+ }
+ return builder.build();
+ }
+
+ @Override
+ public boolean hasLiveness() {
+ return true;
+ }
+
+ @Override
+ public AppInfoWithLiveness withLiveness() {
+ return this;
+ }
+
+ /**
+ * Returns a copy of this AppInfoWithLiveness where the set of classes is pruned using the
+ * given DexApplication object.
+ */
+ public AppInfoWithLiveness prunedCopyFrom(DexApplication application) {
+ return new AppInfoWithLiveness(this, application);
+ }
+
+ public AppInfoWithLiveness rewrittenWithLense(GraphLense lense) {
+ assert lense.isContextFree();
+ return new AppInfoWithLiveness(this, lense);
+ }
+ }
+
+ private static class SetWithReason<T> {
+
+ private final Set<T> items = Sets.newIdentityHashSet();
+ private final Map<T, KeepReason> reasons = Maps.newIdentityHashMap();
+
+ boolean add(T item, KeepReason reason) {
+ if (items.add(item)) {
+ reasons.put(item, reason);
+ return true;
+ }
+ return false;
+ }
+
+ boolean contains(T item) {
+ return items.contains(item);
+ }
+
+ Set<T> getItems() {
+ return Collections.unmodifiableSet(items);
+ }
+
+ Map<T, KeepReason> getReasons() {
+ return Collections.unmodifiableMap(reasons);
+ }
+ }
+
+ private class AnnotationReferenceMarker implements IndexedItemCollection {
+
+ private final DexItem annotationHolder;
+ private final DexItemFactory dexItemFactory;
+
+ private AnnotationReferenceMarker(DexItem annotationHolder, DexItemFactory dexItemFactory) {
+ this.annotationHolder = annotationHolder;
+ this.dexItemFactory = dexItemFactory;
+ }
+
+ @Override
+ public boolean addClass(DexProgramClass dexProgramClass) {
+ return false;
+ }
+
+ @Override
+ public boolean addField(DexField field) {
+ DexClass holder = appInfo.definitionFor(field.clazz);
+ if (holder == null) {
+ return false;
+ }
+ DexEncodedField target = holder.findStaticTarget(field);
+ if (target != null) {
+ // There is no dispatch on annotations, so only keep what is directly referenced.
+ if (target.field == field) {
+ markStaticFieldAsLive(field, KeepReason.referencedInAnnotation(annotationHolder));
+ }
+ } else {
+ target = holder.findInstanceTarget(field);
+ // There is no dispatch on annotations, so only keep what is directly referenced.
+ if (target != null && target.field != field) {
+ markFieldAsReachable(field, KeepReason.referencedInAnnotation(annotationHolder));
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean addMethod(DexMethod method) {
+ DexClass holder = appInfo.definitionFor(method.holder);
+ if (holder == null) {
+ return false;
+ }
+ DexEncodedMethod target = holder.findDirectTarget(method);
+ if (target != null) {
+ // There is no dispatch on annotations, so only keep what is directly referenced.
+ if (target.method == method) {
+ markDirectStaticOrConstructorMethodAsLive(
+ target, KeepReason.referencedInAnnotation(annotationHolder));
+ }
+ } else {
+ target = holder.findVirtualTarget(method);
+ // There is no dispatch on annotations, so only keep what is directly referenced.
+ if (target != null && target.method == method) {
+ markMethodAsTargeted(target, KeepReason.referencedInAnnotation(annotationHolder));
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean addString(DexString string) {
+ return false;
+ }
+
+ @Override
+ public boolean addProto(DexProto proto) {
+ return false;
+ }
+
+ @Override
+ public boolean addCallSite(DexCallSite callSite) {
+ return false;
+ }
+
+ @Override
+ public boolean addMethodHandle(DexMethodHandle methodHandle) {
+ return false;
+ }
+
+ @Override
+ public boolean addType(DexType type) {
+ // Annotations can also contain the void type, which is not a class type, so filter it out
+ // here.
+ if (type != dexItemFactory.voidType) {
+ markTypeAsLive(type);
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepReason.java b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
new file mode 100644
index 0000000..4e2f0b2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -0,0 +1,176 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ReasonPrinter.ReasonFormatter;
+
+// TODO(herhut): Canonicalize reason objects.
+abstract class KeepReason {
+
+ static KeepReason dueToKeepRule(ProguardKeepRule rule) {
+ return new DueToKeepRule(rule);
+ }
+
+ static KeepReason instantiatedIn(DexEncodedMethod method) {
+ return new InstatiatedIn(method);
+ }
+
+ public static KeepReason invokedViaSuperFrom(DexEncodedMethod from) {
+ return new InvokedViaSuper(from);
+ }
+
+ public static KeepReason reachableFromLiveType(DexType type) {
+ return new ReachableFromLiveType(type);
+ }
+
+ public static KeepReason invokedFrom(DexEncodedMethod method) {
+ return new InvokedFrom(method);
+ }
+
+ public static KeepReason isLibraryMethod() {
+ return new IsLibraryMethod();
+ }
+
+ public static KeepReason fieldReferencedIn(DexEncodedMethod method) {
+ return new ReferenedFrom(method);
+ }
+
+ public static KeepReason referencedInAnnotation(DexItem holder) {
+ return new ReferencedInAnnotation(holder);
+ }
+
+ public abstract void print(ReasonFormatter formatter);
+
+ private static class DueToKeepRule extends KeepReason {
+
+ private final ProguardKeepRule keepRule;
+
+ private DueToKeepRule(ProguardKeepRule keepRule) {
+ this.keepRule = keepRule;
+ }
+
+ @Override
+ public void print(ReasonFormatter formatter) {
+ formatter.addReason("referenced in keep rule:");
+ formatter.addMessage(" " + keepRule + " {");
+ int ruleCount = 0;
+ for (ProguardMemberRule memberRule : keepRule.getMemberRules()) {
+ formatter.addMessage(" " + memberRule);
+ if (++ruleCount > 10) {
+ formatter.addMessage(" <...>");
+ break;
+ }
+ }
+ formatter.addMessage(" };");
+ }
+ }
+
+ private abstract static class BasedOnOtherMethod extends KeepReason {
+
+ private final DexEncodedMethod method;
+
+ private BasedOnOtherMethod(DexEncodedMethod method) {
+ this.method = method;
+ }
+
+ abstract String getKind();
+
+ public void print(ReasonFormatter formatter) {
+ formatter.addReason("is " + getKind() + " " + method.toSourceString());
+ formatter.addMethodReferenceReason(method);
+ }
+
+ }
+
+ private static class InstatiatedIn extends BasedOnOtherMethod {
+
+ private InstatiatedIn(DexEncodedMethod method) {
+ super(method);
+ }
+
+ @Override
+ String getKind() {
+ return "instantiated in";
+ }
+ }
+
+ private static class InvokedViaSuper extends BasedOnOtherMethod {
+
+ private InvokedViaSuper(DexEncodedMethod method) {
+ super(method);
+ }
+
+ @Override
+ String getKind() {
+ return "invoked via super from";
+ }
+ }
+
+ private static class InvokedFrom extends BasedOnOtherMethod {
+
+ private InvokedFrom(DexEncodedMethod method) {
+ super(method);
+ }
+
+ @Override
+ String getKind() {
+ return "invoked from";
+ }
+ }
+
+ private static class ReferenedFrom extends BasedOnOtherMethod {
+
+ private ReferenedFrom(DexEncodedMethod method) {
+ super(method);
+ }
+
+ @Override
+ String getKind() {
+ return "referenced from";
+ }
+ }
+
+ private static class ReachableFromLiveType extends KeepReason {
+
+ private final DexType type;
+
+ private ReachableFromLiveType(DexType type) {
+ this.type = type;
+ }
+
+ @Override
+ public void print(ReasonFormatter formatter) {
+ formatter.addReason("is reachable from type " + type.toSourceString());
+ formatter.addTypeLivenessReason(type);
+ }
+ }
+
+ private static class IsLibraryMethod extends KeepReason {
+
+ private IsLibraryMethod() {
+ }
+
+ @Override
+ public void print(ReasonFormatter formatter) {
+ formatter.addReason("is defined in a library.");
+ }
+ }
+
+ private static class ReferencedInAnnotation extends KeepReason {
+
+ private final DexItem holder;
+
+ private ReferencedInAnnotation(DexItem holder) {
+ this.holder = holder;
+ }
+
+ @Override
+ public void print(ReasonFormatter formatter) {
+ formatter.addReason("is referenced in annotation on " + holder.toSourceString());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
new file mode 100644
index 0000000..cc54a49
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardAssumeNoSideEffectRule extends ProguardConfigurationRule {
+
+ public static class Builder extends ProguardClassSpecification.Builder {
+
+ private Builder() {}
+
+ public ProguardAssumeNoSideEffectRule build() {
+ return new ProguardAssumeNoSideEffectRule(classAnnotation, classAccessFlags,
+ negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
+ inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+ }
+
+ private ProguardAssumeNoSideEffectRule(
+ ProguardTypeMatcher classAnnotation,
+ DexAccessFlags classAccessFlags,
+ DexAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ List<ProguardTypeMatcher> classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ Set<ProguardMemberRule> memberRules) {
+ super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+ classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+
+ /**
+ * Create a new empty builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public boolean applyToLibraryClasses() {
+ return true;
+ }
+
+ @Override
+ String typeString() {
+ return "assumenosideeffects";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
new file mode 100644
index 0000000..73825f1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardAssumeValuesRule extends ProguardConfigurationRule {
+ public static class Builder extends ProguardClassSpecification.Builder {
+
+ private Builder() {}
+
+ public ProguardAssumeValuesRule build() {
+ return new ProguardAssumeValuesRule(classAnnotation, classAccessFlags, negatedClassAccessFlags,
+ classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+ inheritanceIsExtends, memberRules);
+ }
+ }
+
+ private ProguardAssumeValuesRule(
+ ProguardTypeMatcher classAnnotation,
+ DexAccessFlags classAccessFlags,
+ DexAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ List<ProguardTypeMatcher> classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ Set<ProguardMemberRule> memberRules) {
+ super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+ classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+
+ /**
+ * Create a new empty builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ String typeString() {
+ return "assumevalues";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
new file mode 100644
index 0000000..396d37d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -0,0 +1,288 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+public abstract class ProguardClassSpecification {
+
+ public static class Builder {
+
+ protected ProguardTypeMatcher classAnnotation;
+ protected DexAccessFlags classAccessFlags = new DexAccessFlags(0);
+ protected DexAccessFlags negatedClassAccessFlags = new DexAccessFlags(0);
+ protected boolean classTypeNegated = false;
+ protected ProguardClassType classType;
+ protected List<ProguardTypeMatcher> classNames;
+ protected ProguardTypeMatcher inheritanceAnnotation;
+ protected ProguardTypeMatcher inheritanceClassName;
+ protected boolean inheritanceIsExtends = false;
+ protected Set<ProguardMemberRule> memberRules = new LinkedHashSet<>();
+
+ protected Builder() {
+ }
+
+ public Set<ProguardMemberRule> getMemberRules() {
+ return memberRules;
+ }
+
+ public void setMemberRules(Set<ProguardMemberRule> memberRules) {
+ this.memberRules = memberRules;
+ }
+
+ public boolean getInheritanceIsExtends() {
+ return inheritanceIsExtends;
+ }
+
+ public void setInheritanceIsExtends(boolean inheritanceIsExtends) {
+ this.inheritanceIsExtends = inheritanceIsExtends;
+ }
+
+ public boolean hasInheritanceClassName() {
+ return inheritanceClassName != null;
+ }
+
+ public ProguardTypeMatcher getInheritanceClassName() {
+ return inheritanceClassName;
+ }
+
+ public void setInheritanceClassName(ProguardTypeMatcher inheritanceClassName) {
+ this.inheritanceClassName = inheritanceClassName;
+ }
+
+ public ProguardTypeMatcher getInheritanceAnnotation() {
+ return inheritanceAnnotation;
+ }
+
+ public void setInheritanceAnnotation(ProguardTypeMatcher inheritanceAnnotation) {
+ this.inheritanceAnnotation = inheritanceAnnotation;
+ }
+
+ public List<ProguardTypeMatcher> getClassNames() {
+ return classNames;
+ }
+
+ public void setClassNames(List<ProguardTypeMatcher> classNames) {
+ this.classNames = classNames;
+ }
+
+ public ProguardClassType getClassType() {
+ return classType;
+ }
+
+ public void setClassType(ProguardClassType classType) {
+ this.classType = classType;
+ }
+
+ public boolean getClassTypeNegated() {
+ return classTypeNegated;
+ }
+
+ public void setClassTypeNegated(boolean classTypeNegated) {
+ this.classTypeNegated = classTypeNegated;
+ }
+
+ public DexAccessFlags getClassAccessFlags() {
+ return classAccessFlags;
+ }
+
+ public void setClassAccessFlags(DexAccessFlags flags) {
+ classAccessFlags = flags;
+ }
+
+ public DexAccessFlags getNegatedClassAccessFlags() {
+ return negatedClassAccessFlags;
+ }
+
+ public void setNegatedClassAccessFlags(DexAccessFlags flags) {
+ negatedClassAccessFlags = flags;
+ }
+
+ public ProguardTypeMatcher getClassAnnotation() {
+ return classAnnotation;
+ }
+
+ public void setClassAnnotation(ProguardTypeMatcher classAnnotation) {
+ this.classAnnotation = classAnnotation;
+ }
+
+ protected void matchAllSpecification() {
+ setClassNames(Collections.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
+ setMemberRules(Collections.singleton(ProguardMemberRule.defaultKeepAllRule()));
+ }
+ }
+
+ private final ProguardTypeMatcher classAnnotation;
+ private final DexAccessFlags classAccessFlags;
+ private final DexAccessFlags negatedClassAccessFlags;
+ private final boolean classTypeNegated;
+ private final ProguardClassType classType;
+ private final List<ProguardTypeMatcher> classNames;
+ private final ProguardTypeMatcher inheritanceAnnotation;
+ private final ProguardTypeMatcher inheritanceClassName;
+ private final boolean inheritanceIsExtends;
+ private final Set<ProguardMemberRule> memberRules;
+
+ protected ProguardClassSpecification(
+ ProguardTypeMatcher classAnnotation,
+ DexAccessFlags classAccessFlags,
+ DexAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ List<ProguardTypeMatcher> classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ Set<ProguardMemberRule> memberRules) {
+ this.classAnnotation = classAnnotation;
+ this.classAccessFlags = classAccessFlags;
+ this.negatedClassAccessFlags = negatedClassAccessFlags;
+ this.classTypeNegated = classTypeNegated;
+ this.classType = classType;
+ this.classNames = classNames;
+ this.inheritanceAnnotation = inheritanceAnnotation;
+ this.inheritanceClassName = inheritanceClassName;
+ this.inheritanceIsExtends = inheritanceIsExtends;
+ this.memberRules = memberRules;
+ }
+
+ public Set<ProguardMemberRule> getMemberRules() {
+ return memberRules;
+ }
+
+ public boolean getInheritanceIsExtends() {
+ return inheritanceIsExtends;
+ }
+
+ public boolean hasInheritanceClassName() {
+ return inheritanceClassName != null;
+ }
+
+ public ProguardTypeMatcher getInheritanceClassName() {
+ return inheritanceClassName;
+ }
+
+ public ProguardTypeMatcher getInheritanceAnnotation() {
+ return inheritanceAnnotation;
+ }
+
+ public List<ProguardTypeMatcher> getClassNames() {
+ return classNames;
+ }
+
+ public ProguardClassType getClassType() {
+ return classType;
+ }
+
+ public boolean getClassTypeNegated() {
+ return classTypeNegated;
+ }
+
+ public DexAccessFlags getClassAccessFlags() {
+ return classAccessFlags;
+ }
+
+ public DexAccessFlags getNegatedClassAccessFlags() {
+ return negatedClassAccessFlags;
+ }
+
+ public ProguardTypeMatcher getClassAnnotation() {
+ return classAnnotation;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ProguardClassSpecification)) {
+ return false;
+ }
+ ProguardClassSpecification that = (ProguardClassSpecification) o;
+
+ if (classTypeNegated != that.classTypeNegated) {
+ return false;
+ }
+ if (inheritanceIsExtends != that.inheritanceIsExtends) {
+ return false;
+ }
+ if (!Objects.equals(classAnnotation, that.classAnnotation)) {
+ return false;
+ }
+ if (!classAccessFlags.equals(that.classAccessFlags)) {
+ return false;
+ }
+ if (!negatedClassAccessFlags.equals(that.negatedClassAccessFlags)) {
+ return false;
+ }
+ if (classType != that.classType) {
+ return false;
+ }
+ if (!classNames.equals(that.classNames)) {
+ return false;
+ }
+ if (!Objects.equals(inheritanceAnnotation, that.inheritanceAnnotation)) {
+ return false;
+ }
+ if (!Objects.equals(inheritanceClassName, that.inheritanceClassName)) {
+ return false;
+ }
+ return memberRules.equals(that.memberRules);
+ }
+
+ @Override
+ public int hashCode() {
+ // Used multiplier 3 to avoid too much overflow when computing hashCode.
+ int result = (classAnnotation != null ? classAnnotation.hashCode() : 0);
+ result = 3 * result + classAccessFlags.hashCode();
+ result = 3 * result + negatedClassAccessFlags.hashCode();
+ result = 3 * result + (classTypeNegated ? 1 : 0);
+ result = 3 * result + (classType != null ? classType.hashCode() : 0);
+ result = 3 * result + classNames.hashCode();
+ result = 3 * result + (inheritanceAnnotation != null ? inheritanceAnnotation.hashCode() : 0);
+ result = 3 * result + (inheritanceClassName != null ? inheritanceClassName.hashCode() : 0);
+ result = 3 * result + (inheritanceIsExtends ? 1 : 0);
+ result = 3 * result + memberRules.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ StringUtils.appendNonEmpty(builder, " @", classAnnotation, null);
+ StringUtils.appendNonEmpty(builder, " ", classAccessFlags, null);
+ StringUtils.appendNonEmpty(builder, " !", negatedClassAccessFlags.toString().replace(" ", " !"),
+ null);
+ if (builder.length() > 0) {
+ builder.append(' ');
+ }
+ builder.append(classType);
+ builder.append(' ');
+ boolean first = true;
+ for (ProguardTypeMatcher className : classNames) {
+ builder.append(className);
+ if (!first) {
+ builder.append(',');
+ }
+ first = false;
+ }
+ if (hasInheritanceClassName()) {
+ builder.append(inheritanceIsExtends ? " extends" : " implements");
+ StringUtils.appendNonEmpty(builder, " @", inheritanceAnnotation, null);
+ builder.append(' ');
+ builder.append(inheritanceClassName);
+ }
+ builder.append(" {\n");
+ memberRules.forEach(memberRule -> {
+ builder.append(" ");
+ builder.append(memberRule);
+ builder.append(";\n");
+ });
+ builder.append("}");
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassType.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassType.java
new file mode 100644
index 0000000..aab0413
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassType.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.errors.Unreachable;
+
+public enum ProguardClassType {
+ ANNOTATION_INTERFACE,
+ CLASS,
+ ENUM,
+ INTERFACE;
+
+ @Override
+ public String toString() {
+ switch (this) {
+ case ANNOTATION_INTERFACE: return "@interface";
+ case CLASS: return "class";
+ case ENUM: return "enum";
+ case INTERFACE: return "interface";
+ default:
+ throw new Unreachable("Invalid proguard class type '" + this + "'");
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
new file mode 100644
index 0000000..9a63008
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -0,0 +1,322 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.naming.DictionaryReader;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardConfiguration {
+
+ public static class Builder {
+
+ private final List<Path> injars = new ArrayList<>();
+ private final List<Path> libraryjars = new ArrayList<>();
+ private String packagePrefix = null;
+ private boolean allowAccessModification = false;
+ private boolean ignoreWarnings = false;
+ private boolean obfuscating = true;
+ private boolean shrinking = true;
+ private boolean printMapping;
+ private Path printMappingOutput;
+ private boolean verbose = false;
+ private final List<String> attributesRemovalPatterns = new ArrayList<>();
+ private final Set<ProguardTypeMatcher> dontWarnPatterns = new HashSet<>();
+ protected final List<ProguardConfigurationRule> rules = new ArrayList<>();
+ private final DexItemFactory dexItemFactory;
+ private boolean printSeeds;
+ private Path seedFile;
+ private Path obfuscationDictionary;
+ private Path classObfuscationDictionary;
+ private Path packageObfuscationDictionary;
+
+ private Builder(DexItemFactory dexItemFactory) {
+ this.dexItemFactory = dexItemFactory;
+ }
+
+ public void addInjars(List<Path> injars) {
+ this.injars.addAll(injars);
+ }
+
+ public void addLibraryJars(List<Path> libraryJars) {
+ this.libraryjars.addAll(libraryJars);
+ }
+
+ public void setPackagePrefix(String packagePrefix) {
+ this.packagePrefix = packagePrefix;
+ }
+
+ public void setAllowAccessModification(boolean allowAccessModification) {
+ this.allowAccessModification = allowAccessModification;
+ }
+
+ public void setIgnoreWarnings(boolean ignoreWarnings) {
+ this.ignoreWarnings = ignoreWarnings;
+ }
+
+ public void setObfuscating(boolean obfuscate) {
+ this.obfuscating = obfuscate;
+ }
+
+ public void setShrinking(boolean shrinking) {
+ this.shrinking = shrinking;
+ }
+
+ public void setPrintMapping(boolean printMapping) {
+ this.printMapping = printMapping;
+ }
+
+ public void setPrintMappingOutput(Path file) {
+ this.printMappingOutput = file;
+ }
+
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ public void addAttributeRemovalPattern(String attributesRemovalPattern) {
+ this.attributesRemovalPatterns.add(attributesRemovalPattern);
+ }
+
+ public void addRule(ProguardConfigurationRule rule) {
+ this.rules.add(rule);
+ }
+
+ public void addDontWarnPattern(ProguardTypeMatcher pattern) {
+ dontWarnPatterns.add(pattern);
+ }
+
+ public void setSeedFile(Path seedFile) {
+ this.seedFile = seedFile;
+ }
+
+ public void setPrintSeed(boolean printSeeds) {
+ this.printSeeds = printSeeds;
+ }
+
+ public void setObfuscationDictionary(Path obfuscationDictionary) {
+ this.obfuscationDictionary = obfuscationDictionary;
+ }
+
+ public void setClassObfuscationDictionary(Path classObfuscationDictionary) {
+ this.classObfuscationDictionary = classObfuscationDictionary;
+ }
+
+ public void setPackageObfuscationDictionary(Path packageObfuscationDictionary) {
+ this.packageObfuscationDictionary = packageObfuscationDictionary;
+ }
+
+ public ProguardConfiguration build() {
+ return new ProguardConfiguration(
+ dexItemFactory,
+ injars,
+ libraryjars,
+ packagePrefix,
+ allowAccessModification,
+ ignoreWarnings,
+ obfuscating,
+ shrinking,
+ printMapping,
+ printMappingOutput,
+ verbose,
+ attributesRemovalPatterns,
+ dontWarnPatterns,
+ rules,
+ printSeeds,
+ seedFile,
+ DictionaryReader.readAllNames(obfuscationDictionary),
+ DictionaryReader.readAllNames(classObfuscationDictionary),
+ DictionaryReader.readAllNames(packageObfuscationDictionary));
+ }
+ }
+
+ private final DexItemFactory dexItemFactory;
+ private final List<Path> injars;
+ private final List<Path> libraryjars;
+ private final String packagePrefix;
+ private final boolean allowAccessModification;
+ private final boolean ignoreWarnings;
+ private final boolean obfuscating;
+ private final boolean shrinking;
+ private final boolean printMapping;
+ private final Path printMappingOutput;
+ private final boolean verbose;
+ private final List<String> attributesRemovalPatterns;
+ private final ImmutableSet<ProguardTypeMatcher> dontWarnPatterns;
+ protected final ImmutableList<ProguardConfigurationRule> rules;
+ private final boolean printSeeds;
+ private final Path seedFile;
+ private final List<String> obfuscationDictionary;
+ private final List<String> classObfuscationDictionary;
+ private final List<String> packageObfuscationDictionary;
+
+ private ProguardConfiguration(
+ DexItemFactory factory,
+ List<Path> injars,
+ List<Path> libraryjars,
+ String packagePrefix,
+ boolean allowAccessModification,
+ boolean ignoreWarnings,
+ boolean obfuscating,
+ boolean shrinking,
+ boolean printMapping,
+ Path printMappingOutput,
+ boolean verbose,
+ List<String> attributesRemovalPatterns,
+ Set<ProguardTypeMatcher> dontWarnPatterns,
+ List<ProguardConfigurationRule> rules,
+ boolean printSeeds,
+ Path seedFile,
+ List<String> obfuscationDictionary,
+ List<String> classObfuscationDictionary,
+ List<String> packageObfuscationDictionary) {
+ this.dexItemFactory = factory;
+ this.injars = ImmutableList.copyOf(injars);
+ this.libraryjars = ImmutableList.copyOf(libraryjars);
+ this.packagePrefix = packagePrefix;
+ this.allowAccessModification = allowAccessModification;
+ this.ignoreWarnings = ignoreWarnings;
+ this.obfuscating = obfuscating;
+ this.shrinking = shrinking;
+ this.printMapping = printMapping;
+ this.printMappingOutput = printMappingOutput;
+ this.verbose = verbose;
+ this.attributesRemovalPatterns = ImmutableList.copyOf(attributesRemovalPatterns);
+ this.dontWarnPatterns = ImmutableSet.copyOf(dontWarnPatterns);
+ this.rules = ImmutableList.copyOf(rules);
+ this.printSeeds = printSeeds;
+ this.seedFile = seedFile;
+ this.obfuscationDictionary = obfuscationDictionary;
+ this.classObfuscationDictionary = classObfuscationDictionary;
+ this.packageObfuscationDictionary = packageObfuscationDictionary;
+ }
+
+ /**
+ * Create a new empty builder.
+ */
+ public static Builder builder(DexItemFactory dexItemFactory) {
+ return new Builder(dexItemFactory);
+ }
+
+ public DexItemFactory getDexItemFactory() {
+ return dexItemFactory;
+ }
+
+ public boolean isDefaultConfiguration() {
+ return false;
+ }
+
+ public List<Path> getInjars() {
+ return injars;
+ }
+
+ public List<Path> getLibraryjars() {
+ return libraryjars;
+ }
+
+ public String getPackagePrefix() {
+ return packagePrefix;
+ }
+
+ public boolean getAllowAccessModification() {
+ return allowAccessModification;
+ }
+
+ public boolean isPrintingMapping() {
+ return printMapping;
+ }
+
+ public Path getPrintMappingOutput() {
+ return printMappingOutput;
+ }
+
+ public boolean isIgnoreWarnings() {
+ return ignoreWarnings;
+ }
+
+ public boolean isObfuscating() {
+ return obfuscating;
+ }
+
+ public boolean isShrinking() {
+ return shrinking;
+ }
+
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ public List<String> getAttributesRemovalPatterns() {
+ return attributesRemovalPatterns;
+ }
+
+ public ImmutableSet<ProguardTypeMatcher> getDontWarnPatterns() {
+ return dontWarnPatterns;
+ }
+
+ public ImmutableList<ProguardConfigurationRule> getRules() {
+ return rules;
+ }
+
+ public List<String> getObfuscationDictionary() {
+ return obfuscationDictionary;
+ }
+
+ public List<String> getPackageObfuscationDictionary() {
+ return packageObfuscationDictionary;
+ }
+
+ public List<String> getClassObfuscationDictionary() {
+ return classObfuscationDictionary;
+ }
+
+ public static ProguardConfiguration defaultConfiguration(DexItemFactory dexItemFactory) {
+ ProguardConfiguration config = new DefaultProguardConfiguration(dexItemFactory);
+ return config;
+ }
+
+ public static class DefaultProguardConfiguration extends ProguardConfiguration {
+
+ public DefaultProguardConfiguration(DexItemFactory factory) {
+ super(factory,
+ ImmutableList.of() /* injars */,
+ ImmutableList.of() /* libraryjars */,
+ "" /* package prefix */,
+ false /* allowAccessModification */,
+ false /* ignoreWarnings */,
+ false /* obfuscating */,
+ false /* shrinking */,
+ false /* printMapping */,
+ null /* outputMapping */,
+ false /* verbose */,
+ ImmutableList.of() /* attributesRemovalPatterns */,
+ ImmutableSet.of() /* dontWarnPatterns */,
+ ImmutableList.of(ProguardKeepRule.defaultKeepAllRule()),
+ false /* printSeeds */,
+ null /* seedFile */,
+ ImmutableList.of() /* obfuscationDictionary */,
+ ImmutableList.of() /* classObfuscationDictionary */,
+ ImmutableList.of() /* packageObfucationDictionary */);
+ }
+
+ @Override
+ public boolean isDefaultConfiguration() {
+ return true;
+ }
+ }
+
+ public boolean getPrintSeeds() {
+ return printSeeds;
+ }
+
+ public Path getSeedFile() {
+ return seedFile;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
new file mode 100644
index 0000000..69db32d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -0,0 +1,963 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.shaking.ProguardConfiguration.Builder;
+import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
+import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.LongInterval;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ProguardConfigurationParser {
+
+ private final Builder configurationBuilder;
+
+ private final DexItemFactory dexItemFactory;
+
+ private static final List<String> ignoredSingleArgOptions = ImmutableList
+ .of("protomapping", "optimizationpasses");
+ private static final List<String> ignoredFlagOptions = ImmutableList
+ .of("forceprocessing", "dontusemixedcaseclassnames",
+ "dontpreverify", "experimentalshrinkunusedprotofields",
+ "filterlibraryjarswithorginalprogramjars",
+ "dontskipnonpubliclibraryclasses", "invokebasemethod");
+ private static final List<String> ignoredClassDescriptorOptions = ImmutableList
+ .of("isclassnamestring",
+ "alwaysinline", "identifiernamestring", "whyarenotsimple");
+
+ private static final List<String> warnedSingleArgOptions = ImmutableList
+ .of("printusage",
+ "renamesourcefileattribute",
+ "dontnote",
+ "printconfiguration",
+ // TODO -outjars (http://b/37137994) and -adaptresourcefilecontents (http://b/37139570)
+ // should be reported as errors, not just as warnings!
+ "outjars",
+ "adaptresourcefilecontents");
+ private static final List<String> warnedFlagOptions = ImmutableList
+ .of("dontoptimize");
+
+ // Those options are unsupported and are treated as compilation errors.
+ // Just ignoring them would produce outputs incompatible with user expectations.
+ private static final List<String> unsupportedFlagOptions = ImmutableList
+ .of("skipnonpubliclibraryclasses");
+
+ public ProguardConfigurationParser(DexItemFactory dexItemFactory) {
+ this.dexItemFactory = dexItemFactory;
+ configurationBuilder = ProguardConfiguration.builder(dexItemFactory);
+ }
+
+ public ProguardConfiguration getConfig() {
+ return configurationBuilder.build();
+ }
+
+ public void parse(Path path) throws ProguardRuleParserException, IOException {
+ parse(Collections.singletonList(path));
+ }
+
+ public void parse(List<Path> pathList) throws ProguardRuleParserException, IOException {
+ for (Path path : pathList) {
+ new ProguardFileParser(path).parse();
+ }
+ }
+
+ private class ProguardFileParser {
+
+ private final Path path;
+ private final String contents;
+ private int position = 0;
+ private Path baseDirectory;
+
+ public ProguardFileParser(Path path) throws IOException {
+ this.path = path;
+ contents = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
+ baseDirectory = path.getParent();
+ if (baseDirectory == null) {
+ // path parent can be null only if it's root dir or if its a one element path relative to
+ // current directory.
+ baseDirectory = Paths.get(".");
+ }
+ }
+
+ public void parse() throws ProguardRuleParserException {
+ do {
+ skipWhitespace();
+ } while (parseOption());
+ }
+
+ private boolean parseOption() throws ProguardRuleParserException {
+ if (eof()) {
+ return false;
+ }
+ if (acceptArobaseInclude()) {
+ return true;
+ }
+ expectChar('-');
+ String option;
+ if (Iterables.any(ignoredSingleArgOptions, this::skipOptionWithSingleArg)
+ || Iterables.any(ignoredFlagOptions, this::skipFlag)
+ || Iterables.any(ignoredClassDescriptorOptions, this::skipOptionWithClassSpec)
+ || parseOptimizationOption()) {
+ // Intentionally left empty.
+ } else if (
+ (option = Iterables.find(warnedSingleArgOptions,
+ this::skipOptionWithSingleArg, null)) != null
+ || (option = Iterables.find(warnedFlagOptions, this::skipFlag, null)) != null) {
+ System.out.println("WARNING: Ignoring option: -" + option);
+ } else if ((option = Iterables.find(unsupportedFlagOptions, this::skipFlag, null)) != null) {
+ throw parseError("Unsupported option: -" + option);
+ } else if (acceptString("keepattributes")) {
+ parseKeepAttributes();
+ } else if (acceptString("keeppackagenames")) {
+ ProguardKeepRule rule = parseKeepPackageNamesRule();
+ configurationBuilder.addRule(rule);
+ } else if (acceptString("checkdiscard")) {
+ ProguardKeepRule rule = parseCheckDiscardRule();
+ configurationBuilder.addRule(rule);
+ } else if (acceptString("keep")) {
+ ProguardKeepRule rule = parseKeepRule();
+ configurationBuilder.addRule(rule);
+ } else if (acceptString("whyareyoukeeping")) {
+ ProguardKeepRule rule = parseWhyAreYouKeepingRule();
+ configurationBuilder.addRule(rule);
+ } else if (acceptString("dontobfuscate")) {
+ configurationBuilder.setObfuscating(false);
+ } else if (acceptString("dontshrink")) {
+ configurationBuilder.setShrinking(false);
+ } else if (acceptString("verbose")) {
+ configurationBuilder.setVerbose(true);
+ } else if (acceptString("ignorewarnings")) {
+ configurationBuilder.setIgnoreWarnings(true);
+ } else if (acceptString("dontwarn")) {
+ do {
+ ProguardTypeMatcher pattern = ProguardTypeMatcher.create(parseClassName(),
+ ClassOrType.CLASS, dexItemFactory);
+ configurationBuilder.addDontWarnPattern(pattern);
+ } while (acceptChar(','));
+ } else if (acceptString("repackageclasses")) {
+ skipWhitespace();
+ if (acceptChar('\'')) {
+ configurationBuilder.setPackagePrefix(parsePackageNameOrEmptyString());
+ expectChar('\'');
+ } else {
+ configurationBuilder.setPackagePrefix("");
+ }
+ } else if (acceptString("allowaccessmodification")) {
+ configurationBuilder.setAllowAccessModification(true);
+ } else if (acceptString("printmapping")) {
+ configurationBuilder.setPrintMapping(true);
+ skipWhitespace();
+ if (!eof() && peekChar() != '-') {
+ configurationBuilder.setPrintMappingOutput(parseFileName());
+ }
+ } else if (acceptString("assumenosideeffects")) {
+ ProguardAssumeNoSideEffectRule rule = parseAssumeNoSideEffectsRule();
+ configurationBuilder.addRule(rule);
+ } else if (acceptString("assumevalues")) {
+ ProguardAssumeValuesRule rule = parseAssumeValuesRule();
+ configurationBuilder.addRule(rule);
+ } else if (acceptString("include")) {
+ skipWhitespace();
+ parseInclude();
+ } else if (acceptString("basedirectory")) {
+ skipWhitespace();
+ baseDirectory = parseFileName();
+ } else if (acceptString("injars")) {
+ configurationBuilder.addInjars(parseClassPath());
+ } else if (acceptString("libraryjars")) {
+ configurationBuilder.addLibraryJars(parseClassPath());
+ } else if (acceptString("printseeds")) {
+ configurationBuilder.setPrintSeed(true);
+ skipWhitespace();
+ if (!eof() && peekChar() != '-') {
+ configurationBuilder.setSeedFile(parseFileName());
+ }
+ } else if (acceptString("obfuscationdictionary")) {
+ configurationBuilder.setObfuscationDictionary(parseFileName());
+ } else if (acceptString("classobfuscationdictionary")) {
+ configurationBuilder.setClassObfuscationDictionary(parseFileName());
+ } else if (acceptString("packageobfuscationdictionary")) {
+ configurationBuilder.setPackageObfuscationDictionary(parseFileName());
+ } else {
+ throw parseError("Unknown option");
+ }
+ return true;
+ }
+
+ private void parseInclude() throws ProguardRuleParserException {
+ Path included = parseFileName();
+ try {
+ new ProguardFileParser(included).parse();
+ } catch (FileNotFoundException | NoSuchFileException e) {
+ throw parseError("Included file '" + included.toString() + "' not found", e);
+ } catch (IOException e) {
+ throw parseError("Failed to read included file '" + included.toString() + "'", e);
+ }
+ }
+
+ private boolean acceptArobaseInclude() throws ProguardRuleParserException {
+ if (remainingChars() < 2) {
+ return false;
+ }
+ if (!acceptChar('@')) {
+ return false;
+ }
+ parseInclude();
+ return true;
+ }
+
+ private void parseKeepAttributes() throws ProguardRuleParserException {
+ String attributesPattern = acceptPatternList();
+ if (attributesPattern == null) {
+ throw parseError("Expected attribute pattern list");
+ }
+ configurationBuilder.addAttributeRemovalPattern(attributesPattern);
+ }
+
+ private boolean skipFlag(String name) {
+ if (acceptString(name)) {
+ if (Log.ENABLED) {
+ Log.debug(ProguardConfigurationParser.class, "Skipping '-%s` flag", name);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private boolean skipOptionWithSingleArg(String name) {
+ if (acceptString(name)) {
+ if (Log.ENABLED) {
+ Log.debug(ProguardConfigurationParser.class, "Skipping '-%s` option", name);
+ }
+ skipSingleArgument();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean skipOptionWithClassSpec(String name) {
+ if (acceptString(name)) {
+ if (Log.ENABLED) {
+ Log.debug(ProguardConfigurationParser.class, "Skipping '-%s` option", name);
+ }
+ try {
+ ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
+ parseClassFlagsAndAnnotations(keepRuleBuilder);
+ keepRuleBuilder.setClassType(parseClassType());
+ keepRuleBuilder.setClassNames(parseClassNames());
+ parseInheritance(keepRuleBuilder);
+ parseMemberRules(keepRuleBuilder, true);
+ return true;
+ } catch (ProguardRuleParserException e) {
+ System.out.println(e);
+ return false;
+ }
+ }
+ return false;
+
+ }
+
+ private boolean parseOptimizationOption() {
+ if (!acceptString("optimizations")) {
+ return false;
+ }
+ skipWhitespace();
+ do {
+ skipOptimizationName();
+ skipWhitespace();
+ } while (acceptChar(','));
+ return true;
+ }
+
+ private void skipOptimizationName() {
+ if (acceptChar('!')) {
+ skipWhitespace();
+ }
+ for (char next = peekChar();
+ Character.isAlphabetic(next) || next == '/' || next == '*';
+ next = peekChar()) {
+ readChar();
+ }
+ }
+
+ private void skipSingleArgument() {
+ skipWhitespace();
+ while (!eof() && !Character.isWhitespace(peekChar())) {
+ readChar();
+ }
+ }
+
+ private ProguardKeepRule parseKeepRule()
+ throws ProguardRuleParserException {
+ ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
+ parseRuleTypeAndModifiers(keepRuleBuilder);
+ parseClassSpec(keepRuleBuilder, false);
+ return keepRuleBuilder.build();
+ }
+
+ private ProguardKeepRule parseWhyAreYouKeepingRule()
+ throws ProguardRuleParserException {
+ ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
+ keepRuleBuilder.getModifiersBuilder().setFlagsToHaveNoEffect();
+ keepRuleBuilder.getModifiersBuilder().whyAreYouKeeping = true;
+ keepRuleBuilder.setType(ProguardKeepRuleType.KEEP);
+ parseClassSpec(keepRuleBuilder, false);
+ return keepRuleBuilder.build();
+ }
+
+ private ProguardKeepRule parseKeepPackageNamesRule()
+ throws ProguardRuleParserException {
+ ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
+ keepRuleBuilder.getModifiersBuilder().setFlagsToHaveNoEffect();
+ keepRuleBuilder.getModifiersBuilder().keepPackageNames = true;
+ keepRuleBuilder.setType(ProguardKeepRuleType.KEEP);
+ keepRuleBuilder.setClassNames(parseClassNames());
+ return keepRuleBuilder.build();
+ }
+
+ private ProguardKeepRule parseCheckDiscardRule()
+ throws ProguardRuleParserException {
+ ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
+ keepRuleBuilder.getModifiersBuilder().setFlagsToHaveNoEffect();
+ keepRuleBuilder.getModifiersBuilder().checkDiscarded = true;
+ parseClassSpec(keepRuleBuilder, false);
+ keepRuleBuilder.setType(keepRuleBuilder.getMemberRules().isEmpty() ? ProguardKeepRuleType.KEEP
+ : ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
+ return keepRuleBuilder.build();
+ }
+
+ private void parseClassSpec(
+ ProguardConfigurationRule.Builder builder, boolean allowValueSpecification)
+ throws ProguardRuleParserException {
+ parseClassFlagsAndAnnotations(builder);
+ builder.setClassType(parseClassType());
+ builder.setClassNames(parseClassNames());
+ parseInheritance(builder);
+ parseMemberRules(builder, allowValueSpecification);
+ }
+
+ private void parseRuleTypeAndModifiers(ProguardKeepRule.Builder builder)
+ throws ProguardRuleParserException {
+ if (acceptString("names")) {
+ builder.setType(ProguardKeepRuleType.KEEP);
+ builder.getModifiersBuilder().allowsShrinking = true;
+ } else if (acceptString("class")) {
+ if (acceptString("members")) {
+ builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
+ } else if (acceptString("eswithmembers")) {
+ builder.setType(ProguardKeepRuleType.KEEP_CLASSES_WITH_MEMBERS);
+ } else if (acceptString("membernames")) {
+ builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
+ builder.getModifiersBuilder().allowsShrinking = true;
+ } else if (acceptString("eswithmembernames")) {
+ builder.setType(ProguardKeepRuleType.KEEP_CLASSES_WITH_MEMBERS);
+ builder.getModifiersBuilder().allowsShrinking = true;
+ }
+ } else {
+ builder.setType(ProguardKeepRuleType.KEEP);
+ }
+ parseRuleModifiers(builder);
+ }
+
+ private void parseRuleModifiers(ProguardKeepRule.Builder builder) {
+ while (acceptChar(',')) {
+ if (acceptString("allow")) {
+ if (acceptString("shrinking")) {
+ builder.getModifiersBuilder().allowsShrinking = true;
+ } else if (acceptString("optimization")) {
+ builder.getModifiersBuilder().allowsOptimization = true;
+ } else if (acceptString("obfuscation")) {
+ builder.getModifiersBuilder().allowsObfuscation = true;
+ }
+ } else if (acceptString("includedescriptorclasses")) {
+ builder.getModifiersBuilder().includeDescriptorClasses = true;
+ }
+ }
+ }
+
+ private ProguardTypeMatcher parseAnnotation() throws ProguardRuleParserException {
+ skipWhitespace();
+ int startPosition = position;
+ if (acceptChar('@')) {
+ String className = parseClassName();
+ if (className.equals("interface")) {
+ // Not an annotation after all but a class type. Move position back to start
+ // so this can be dealt with as a class type instead.
+ position = startPosition;
+ return null;
+ }
+ return ProguardTypeMatcher.create(className, ClassOrType.CLASS, dexItemFactory);
+ }
+ return null;
+ }
+
+ private boolean parseNegation() {
+ skipWhitespace();
+ return acceptChar('!');
+ }
+
+ private void parseClassFlagsAndAnnotations(ProguardClassSpecification.Builder builder)
+ throws ProguardRuleParserException {
+ while (true) {
+ skipWhitespace();
+ ProguardTypeMatcher annotation = parseAnnotation();
+ if (annotation != null) {
+ // TODO(ager): Should we only allow one annotation? It looks that way from the
+ // proguard keep rule description, but that seems like a strange restriction?
+ assert builder.getClassAnnotation() == null;
+ builder.setClassAnnotation(annotation);
+ } else {
+ DexAccessFlags flags =
+ parseNegation() ? builder.getNegatedClassAccessFlags() :
+ builder.getClassAccessFlags();
+ skipWhitespace();
+ if (acceptString("public")) {
+ flags.setPublic();
+ } else if (acceptString("final")) {
+ flags.setFinal();
+ } else if (acceptString("abstract")) {
+ flags.setAbstract();
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ private ProguardClassType parseClassType() throws ProguardRuleParserException {
+ skipWhitespace();
+ if (acceptString("interface")) {
+ return ProguardClassType.INTERFACE;
+ } else if (acceptString("@interface")) {
+ return ProguardClassType.ANNOTATION_INTERFACE;
+ } else if (acceptString("class")) {
+ return ProguardClassType.CLASS;
+ } else if (acceptString("enum")) {
+ return ProguardClassType.ENUM;
+ } else {
+ throw parseError("Expected interface|class|enum");
+ }
+ }
+
+ private void parseInheritance(ProguardClassSpecification.Builder classSpecificationBuilder)
+ throws ProguardRuleParserException {
+ skipWhitespace();
+ if (acceptString("implements")) {
+ classSpecificationBuilder.setInheritanceIsExtends(false);
+ } else if (acceptString("extends")) {
+ classSpecificationBuilder.setInheritanceIsExtends(true);
+ } else {
+ return;
+ }
+ classSpecificationBuilder.setInheritanceAnnotation(parseAnnotation());
+ classSpecificationBuilder.setInheritanceClassName(ProguardTypeMatcher.create(parseClassName(),
+ ClassOrType.CLASS, dexItemFactory));
+ }
+
+ private void parseMemberRules(ProguardClassSpecification.Builder classSpecificationBuilder,
+ boolean allowValueSpecification)
+ throws ProguardRuleParserException {
+ skipWhitespace();
+ if (!eof() && acceptChar('{')) {
+ ProguardMemberRule rule = null;
+ while ((rule = parseMemberRule(allowValueSpecification)) != null) {
+ classSpecificationBuilder.getMemberRules().add(rule);
+ }
+ skipWhitespace();
+ expectChar('}');
+ } else {
+ // If there are no member rules, a default rule for the parameterless constructor
+ // applies. So we add that here.
+ ProguardMemberRule.Builder defaultRuleBuilder = ProguardMemberRule.builder();
+ defaultRuleBuilder.setName(Constants.INSTANCE_INITIALIZER_NAME);
+ defaultRuleBuilder.setRuleType(ProguardMemberType.INIT);
+ defaultRuleBuilder.setArguments(Collections.emptyList());
+ classSpecificationBuilder.getMemberRules().add(defaultRuleBuilder.build());
+ }
+ }
+
+ private ProguardMemberRule parseMemberRule(boolean allowValueSpecification)
+ throws ProguardRuleParserException {
+ ProguardMemberRule.Builder ruleBuilder = ProguardMemberRule.builder();
+ skipWhitespace();
+ ruleBuilder.setAnnotation(parseAnnotation());
+ parseMemberAccessFlags(ruleBuilder);
+ parseMemberPattern(ruleBuilder, allowValueSpecification);
+ return ruleBuilder.isValid() ? ruleBuilder.build() : null;
+ }
+
+ private void parseMemberAccessFlags(ProguardMemberRule.Builder ruleBuilder) {
+ boolean found = true;
+ while (found && !eof()) {
+ found = false;
+ DexAccessFlags flags =
+ parseNegation() ? ruleBuilder.getNegatedAccessFlags() : ruleBuilder.getAccessFlags();
+ skipWhitespace();
+ switch (peekChar()) {
+ case 'a':
+ if (found = acceptString("abstract")) {
+ flags.setAbstract();
+ }
+ break;
+ case 'f':
+ if (found = acceptString("final")) {
+ flags.setFinal();
+ }
+ break;
+ case 'n':
+ if (found = acceptString("native")) {
+ flags.setNative();
+ }
+ break;
+ case 'p':
+ if (found = acceptString("public")) {
+ flags.setPublic();
+ } else if (found = acceptString("private")) {
+ flags.setPrivate();
+ } else if (found = acceptString("protected")) {
+ flags.setProtected();
+ }
+ break;
+ case 's':
+ if (found = acceptString("synchronized")) {
+ flags.setSynchronized();
+ } else if (found = acceptString("static")) {
+ flags.setStatic();
+ } else if (found = acceptString("strictfp")) {
+ flags.setStrict();
+ }
+ break;
+ case 't':
+ if (found = acceptString("transient")) {
+ flags.setTransient();
+ }
+ break;
+ case 'v':
+ if (found = acceptString("volatile")) {
+ flags.setVolatile();
+ }
+ break;
+ }
+ }
+ }
+
+ private void parseMemberPattern(
+ ProguardMemberRule.Builder ruleBuilder, boolean allowValueSpecification)
+ throws ProguardRuleParserException {
+ skipWhitespace();
+ if (acceptString("<methods>")) {
+ ruleBuilder.setRuleType(ProguardMemberType.ALL_METHODS);
+ } else if (acceptString("<fields>")) {
+ ruleBuilder.setRuleType(ProguardMemberType.ALL_FIELDS);
+ } else if (acceptString("<init>")) {
+ ruleBuilder.setRuleType(ProguardMemberType.INIT);
+ ruleBuilder.setName("<init>");
+ ruleBuilder.setArguments(parseArgumentList());
+ } else {
+ String first = acceptClassName();
+ if (first != null) {
+ skipWhitespace();
+ if (first.equals("*") && hasNextChar(';')) {
+ ruleBuilder.setRuleType(ProguardMemberType.ALL);
+ } else {
+ if (hasNextChar('(')) {
+ ruleBuilder.setRuleType(ProguardMemberType.CONSTRUCTOR);
+ ruleBuilder.setName(first);
+ ruleBuilder.setArguments(parseArgumentList());
+ } else {
+ String second = acceptClassName();
+ if (second != null) {
+ skipWhitespace();
+ if (hasNextChar('(')) {
+ ruleBuilder.setRuleType(ProguardMemberType.METHOD);
+ ruleBuilder.setName(second);
+ ruleBuilder
+ .setTypeMatcher(
+ ProguardTypeMatcher.create(first, ClassOrType.TYPE, dexItemFactory));
+ ruleBuilder.setArguments(parseArgumentList());
+ } else {
+ ruleBuilder.setRuleType(ProguardMemberType.FIELD);
+ ruleBuilder.setName(second);
+ ruleBuilder
+ .setTypeMatcher(
+ ProguardTypeMatcher.create(first, ClassOrType.TYPE, dexItemFactory));
+ }
+ skipWhitespace();
+ // Parse "return ..." if present.
+ if (acceptString("return")) {
+ skipWhitespace();
+ if (acceptString("true")) {
+ ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(true));
+ } else if (acceptString("false")) {
+ ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(false));
+ } else {
+ String qualifiedFieldName = acceptFieldName();
+ if (qualifiedFieldName != null) {
+ if (ruleBuilder.getTypeMatcher() instanceof MatchSpecificType) {
+ int lastDotIndex = qualifiedFieldName.lastIndexOf(".");
+ DexType fieldType = ((MatchSpecificType) ruleBuilder.getTypeMatcher()).type;
+ DexType fieldClass =
+ dexItemFactory.createType(
+ DescriptorUtils.javaTypeToDescriptor(
+ qualifiedFieldName.substring(0, lastDotIndex)));
+ DexString fieldName =
+ dexItemFactory.createString(
+ qualifiedFieldName.substring(lastDotIndex + 1));
+ DexField field = dexItemFactory
+ .createField(fieldClass, fieldType, fieldName);
+ ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(field));
+ } else {
+ throw parseError("Expected specific type");
+ }
+ } else {
+ Integer min = acceptInteger();
+ Integer max = min;
+ if (min == null) {
+ throw parseError("Expected integer value");
+ }
+ skipWhitespace();
+ if (acceptString("..")) {
+ max = acceptInteger();
+ if (max == null) {
+ throw parseError("Expected integer value");
+ }
+ }
+ if (!allowValueSpecification) {
+ throw parseError("Unexpected value specification");
+ }
+ ruleBuilder.setReturnValue(
+ new ProguardMemberRuleReturnValue(new LongInterval(min, max)));
+ }
+ }
+ }
+ } else {
+ throw parseError("Expected field or method name");
+ }
+ }
+ }
+ }
+ }
+ // If we found a member pattern eat the terminating ';'.
+ if (ruleBuilder.isValid()) {
+ skipWhitespace();
+ expectChar(';');
+ }
+ }
+
+ private List<ProguardTypeMatcher> parseArgumentList() throws ProguardRuleParserException {
+ List<ProguardTypeMatcher> arguments = new ArrayList<>();
+ skipWhitespace();
+ expectChar('(');
+ skipWhitespace();
+ if (acceptChar(')')) {
+ return arguments;
+ }
+ if (acceptString("...")) {
+ arguments
+ .add(ProguardTypeMatcher.create("...", ClassOrType.TYPE, dexItemFactory));
+ } else {
+ for (String name = parseClassName(); name != null; name =
+ acceptChar(',') ? parseClassName() : null) {
+ arguments
+ .add(ProguardTypeMatcher.create(name, ClassOrType.TYPE, dexItemFactory));
+ skipWhitespace();
+ }
+ }
+ skipWhitespace();
+ expectChar(')');
+ return arguments;
+ }
+
+ private Path parseFileName() throws ProguardRuleParserException {
+ skipWhitespace();
+ int start = position;
+ int end = position;
+ while (!eof(end)) {
+ char current = contents.charAt(end);
+ if (current != File.pathSeparatorChar && !Character.isWhitespace(current)) {
+ end++;
+ } else {
+ break;
+ }
+ }
+ if (start == end) {
+ throw parseError("File name expected");
+ }
+ position = end;
+ return baseDirectory.resolve(contents.substring(start, end));
+ }
+
+ private List<Path> parseClassPath() throws ProguardRuleParserException {
+ List<Path> classPath = new ArrayList<>();
+ skipWhitespace();
+ Path file = parseFileName();
+ classPath.add(file);
+ while (acceptChar(File.pathSeparatorChar)) {
+ file = parseFileName();
+ classPath.add(file);
+ }
+ return classPath;
+ }
+
+ private ProguardAssumeNoSideEffectRule parseAssumeNoSideEffectsRule()
+ throws ProguardRuleParserException {
+ ProguardAssumeNoSideEffectRule.Builder builder = ProguardAssumeNoSideEffectRule.builder();
+ parseClassSpec(builder, true);
+ return builder.build();
+ }
+
+ private ProguardAssumeValuesRule parseAssumeValuesRule() throws ProguardRuleParserException {
+ ProguardAssumeValuesRule.Builder builder = ProguardAssumeValuesRule.builder();
+ parseClassSpec(builder, true);
+ return builder.build();
+ }
+
+ private void skipWhitespace() {
+ while (!eof() && Character.isWhitespace(contents.charAt(position))) {
+ position++;
+ }
+ skipComment();
+ }
+
+ private void skipComment() {
+ if (eof()) {
+ return;
+ }
+ if (peekChar() == '#') {
+ while (!eof() && readChar() != '\n') {
+ ;
+ }
+ skipWhitespace();
+ }
+ }
+
+ private boolean eof() {
+ return position == contents.length();
+ }
+
+ private boolean eof(int position) {
+ return position == contents.length();
+ }
+
+ private boolean hasNextChar(char c) {
+ if (eof()) {
+ return false;
+ }
+ return peekChar() == c;
+ }
+
+ private boolean acceptChar(char c) {
+ if (hasNextChar(c)) {
+ position++;
+ return true;
+ }
+ return false;
+ }
+
+ private char peekChar() {
+ return contents.charAt(position);
+ }
+
+ private char readChar() {
+ return contents.charAt(position++);
+ }
+
+ private int remainingChars() {
+ return contents.length() - position;
+ }
+
+ private void expectChar(char c) throws ProguardRuleParserException {
+ if (eof() || readChar() != c) {
+ throw parseError("Expected char '" + c + "'");
+ }
+ }
+
+ private void expectString(String expected) throws ProguardRuleParserException {
+ if (remainingChars() < expected.length()) {
+ throw parseError("Expected string '" + expected + "'");
+ }
+ for (int i = 0; i < expected.length(); i++) {
+ if (expected.charAt(i) != readChar()) {
+ throw parseError("Expected string '" + expected + "'");
+ }
+ }
+ }
+
+ private boolean acceptString(String expected) {
+ if (remainingChars() < expected.length()) {
+ return false;
+ }
+ for (int i = 0; i < expected.length(); i++) {
+ if (expected.charAt(i) != contents.charAt(position + i)) {
+ return false;
+ }
+ }
+ position += expected.length();
+ return true;
+ }
+
+ private Integer acceptInteger() {
+ skipWhitespace();
+ int start = position;
+ int end = position;
+ while (!eof(end)) {
+ char current = contents.charAt(end);
+ if (Character.isDigit(current)) {
+ end++;
+ } else {
+ break;
+ }
+ }
+ if (start == end) {
+ return null;
+ }
+ position = end;
+ return Integer.parseInt(contents.substring(start, end));
+ }
+
+ private String acceptClassName() {
+ skipWhitespace();
+ int start = position;
+ int end = position;
+ while (!eof(end)) {
+ char current = contents.charAt(end);
+ if (Character.isJavaIdentifierPart(current) ||
+ current == '.' ||
+ current == '*' ||
+ current == '?' ||
+ current == '%' ||
+ current == '[' ||
+ current == ']') {
+ end++;
+ } else {
+ break;
+ }
+ }
+ if (start == end) {
+ return null;
+ }
+ position = end;
+ return contents.substring(start, end);
+ }
+
+ private String acceptFieldName() {
+ skipWhitespace();
+ int start = position;
+ int end = position;
+ while (!eof(end)) {
+ char current = contents.charAt(end);
+ if ((start == end && Character.isJavaIdentifierStart(current)) ||
+ (start < end) && (Character.isJavaIdentifierPart(current) || current == '.')) {
+ end++;
+ } else {
+ break;
+ }
+ }
+ if (start == end) {
+ return null;
+ }
+ position = end;
+ return contents.substring(start, end);
+ }
+
+ private String acceptPatternList() {
+ skipWhitespace();
+ int start = position;
+ int end = position;
+ while (!eof(end)) {
+ char current = contents.charAt(end);
+ if (Character.isJavaIdentifierPart(current) ||
+ current == '!' ||
+ current == '*' ||
+ current == ',') {
+ end++;
+ } else {
+ break;
+ }
+ }
+ if (start == end) {
+ return null;
+ }
+ position = end;
+ return contents.substring(start, end);
+ }
+
+ private void checkNotNegatedPattern() throws ProguardRuleParserException {
+ skipWhitespace();
+ if (acceptChar('!')) {
+ throw parseError("Negated filters are not supported");
+ }
+ }
+
+ private List<ProguardTypeMatcher> parseClassNames() throws ProguardRuleParserException {
+ List<ProguardTypeMatcher> classNames = new ArrayList<>();
+ checkNotNegatedPattern();
+ classNames
+ .add(ProguardTypeMatcher.create(parseClassName(), ClassOrType.CLASS, dexItemFactory));
+ skipWhitespace();
+ while (acceptChar(',')) {
+ checkNotNegatedPattern();
+ classNames
+ .add(ProguardTypeMatcher.create(parseClassName(), ClassOrType.CLASS, dexItemFactory));
+ skipWhitespace();
+ }
+ return classNames;
+ }
+
+ private String parsePackageNameOrEmptyString() {
+ String name = acceptClassName();
+ return name == null ? "" : name;
+ }
+
+ private String parseClassName() throws ProguardRuleParserException {
+ String name = acceptClassName();
+ if (name == null) {
+ throw parseError("Class name expected");
+ }
+ return name;
+ }
+
+ private String snippetForPosition() {
+ // TODO(ager): really should deal with \r as well to get column right.
+ String[] lines = contents.split("\n", -1); // -1 to get trailing empty lines represented.
+ int remaining = position;
+ for (int lineNumber = 0; lineNumber < lines.length; lineNumber++) {
+ String line = lines[lineNumber];
+ if (remaining <= line.length() || lineNumber == lines.length - 1) {
+ return path.toString() + ":" + (lineNumber + 1) + ":" + remaining + "\n" + line;
+ }
+ remaining -= (line.length() + 1); // include newline.
+ }
+ return path.toString();
+ }
+
+ private ProguardRuleParserException parseError(String message) {
+ return new ProguardRuleParserException(message, snippetForPosition());
+ }
+
+ private ProguardRuleParserException parseError(String message, Throwable cause) {
+ return new ProguardRuleParserException(message, snippetForPosition(), cause);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
new file mode 100644
index 0000000..deb11a1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import java.util.Set;
+
+public abstract class ProguardConfigurationRule extends ProguardClassSpecification {
+ ProguardConfigurationRule(
+ ProguardTypeMatcher classAnnotation,
+ DexAccessFlags classAccessFlags,
+ DexAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ List<ProguardTypeMatcher> classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ Set<ProguardMemberRule> memberRules) {
+ super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+ classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+
+ abstract String typeString();
+
+ String modifierString() {
+ return null;
+ }
+
+ public boolean applyToLibraryClasses() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(typeString());
+ StringUtils.appendNonEmpty(builder, ",", modifierString(), null);
+ builder.append(' ');
+ builder.append(super.toString());
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
new file mode 100644
index 0000000..fac7d99
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -0,0 +1,129 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardKeepRule extends ProguardConfigurationRule {
+
+ public static class Builder extends ProguardClassSpecification.Builder {
+
+ private ProguardKeepRuleType type;
+ private final ProguardKeepRuleModifiers.Builder modifiersBuilder
+ = ProguardKeepRuleModifiers.builder();
+
+ private Builder() {}
+
+ public void setType(ProguardKeepRuleType type) {
+ this.type = type;
+ }
+
+ public ProguardKeepRuleModifiers.Builder getModifiersBuilder() {
+ return modifiersBuilder;
+ }
+
+ public ProguardKeepRule build() {
+ return new ProguardKeepRule(classAnnotation, classAccessFlags, negatedClassAccessFlags,
+ classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+ inheritanceIsExtends, memberRules, type, modifiersBuilder.build());
+ }
+ }
+
+ private final ProguardKeepRuleType type;
+ private final ProguardKeepRuleModifiers modifiers;
+
+ private ProguardKeepRule(
+ ProguardTypeMatcher classAnnotation,
+ DexAccessFlags classAccessFlags,
+ DexAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ List<ProguardTypeMatcher> classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ Set<ProguardMemberRule> memberRules,
+ ProguardKeepRuleType type,
+ ProguardKeepRuleModifiers modifiers) {
+ super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+ classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ this.type = type;
+ this.modifiers = modifiers;
+ }
+
+ /**
+ * Create a new empty builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public ProguardKeepRuleType getType() {
+ return type;
+ }
+
+ public ProguardKeepRuleModifiers getModifiers() {
+ return modifiers;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ProguardKeepRule)) {
+ return false;
+ }
+ ProguardKeepRule that = (ProguardKeepRule) o;
+
+ if (type != that.type) {
+ return false;
+ }
+ if (!modifiers.equals(that.modifiers)) {
+ return false;
+ }
+ return super.equals(that);
+ }
+
+ @Override
+ public int hashCode() {
+ // Used multiplier 3 to avoid too much overflow when computing hashCode.
+ int result = type.hashCode();
+ result = 3 * result + modifiers.hashCode();
+ result = 3 * result + super.hashCode();
+ return result;
+ }
+
+ static void appendNonEmpty(StringBuilder builder, String pre, Object item, String post) {
+ if (item == null) {
+ return;
+ }
+ String text = item.toString();
+ if (!text.isEmpty()) {
+ if (pre != null) {
+ builder.append(pre);
+ }
+ builder.append(text);
+ if (post != null) {
+ builder.append(post);
+ }
+ }
+ }
+
+ @Override
+ String typeString() {
+ return type.toString();
+ }
+
+ @Override
+ String modifierString() {
+ return modifiers.toString();
+ }
+
+ public static ProguardKeepRule defaultKeepAllRule() {
+ ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
+ builder.matchAllSpecification();
+ builder.setType(ProguardKeepRuleType.KEEP);
+ return builder.build();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
new file mode 100644
index 0000000..e2f53b5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+public class ProguardKeepRuleModifiers {
+ public static class Builder {
+ public boolean allowsShrinking = false;
+ public boolean allowsOptimization = false;
+ public boolean allowsObfuscation = false;
+ public boolean whyAreYouKeeping = false;
+ public boolean includeDescriptorClasses = false;
+ public boolean keepPackageNames = false;
+ public boolean checkDiscarded = false;
+
+ void setFlagsToHaveNoEffect() {
+ allowsShrinking = true;
+ allowsOptimization = true;
+ allowsObfuscation = true;
+ whyAreYouKeeping = false;
+ includeDescriptorClasses = false;
+ keepPackageNames = false;
+ }
+
+ private Builder() {}
+
+ ProguardKeepRuleModifiers build() {
+ return new ProguardKeepRuleModifiers(allowsShrinking, allowsOptimization, allowsObfuscation,
+ whyAreYouKeeping, includeDescriptorClasses, keepPackageNames, checkDiscarded);
+ }
+ }
+
+ public final boolean allowsShrinking;
+ public final boolean allowsOptimization;
+ public final boolean allowsObfuscation;
+ public final boolean whyAreYouKeeping;
+ public final boolean includeDescriptorClasses;
+ public final boolean keepPackageNames;
+ public final boolean checkDiscarded;
+
+ private ProguardKeepRuleModifiers(
+ boolean allowsShrinking,
+ boolean allowsOptimization,
+ boolean allowsObfuscation,
+ boolean whyAreYouKeeping,
+ boolean includeDescriptorClasses,
+ boolean keepPackageNames,
+ boolean checkDiscarded) {
+ this.allowsShrinking = allowsShrinking;
+ this.allowsOptimization = allowsOptimization;
+ this.allowsObfuscation = allowsObfuscation;
+ this.whyAreYouKeeping = whyAreYouKeeping;
+ this.includeDescriptorClasses = includeDescriptorClasses;
+ this.keepPackageNames = keepPackageNames;
+ this.checkDiscarded = checkDiscarded;
+ }
+ /**
+ * Create a new empty builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ProguardKeepRuleModifiers)) {
+ return false;
+ }
+ ProguardKeepRuleModifiers that = (ProguardKeepRuleModifiers) o;
+
+ return allowsShrinking == that.allowsShrinking
+ && allowsOptimization == that.allowsOptimization
+ && allowsObfuscation == that.allowsObfuscation
+ && includeDescriptorClasses == that.includeDescriptorClasses
+ && keepPackageNames == that.keepPackageNames;
+ }
+
+ @Override
+ public int hashCode() {
+ return (allowsShrinking ? 1 : 0)
+ | (allowsOptimization ? 2 : 0)
+ | (allowsObfuscation ? 4 : 0)
+ | (whyAreYouKeeping ? 8 : 0)
+ | (includeDescriptorClasses ? 16 : 0)
+ | (keepPackageNames ? 32 : 0);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ appendWithComma(builder, allowsObfuscation, "allowobfuscation");
+ appendWithComma(builder, allowsShrinking, "allowshrinking");
+ appendWithComma(builder, allowsOptimization, "allowoptimization");
+ appendWithComma(builder, whyAreYouKeeping, "whyareyoukeeping");
+ appendWithComma(builder, includeDescriptorClasses, "includedescriptorclasses");
+ appendWithComma(builder, keepPackageNames, "keeppackagenames");
+ return builder.toString();
+ }
+
+ private void appendWithComma(StringBuilder builder, boolean predicate,
+ String text) {
+ if (!predicate) {
+ return;
+ }
+ if (builder.length() != 0) {
+ builder.append(',');
+ }
+ builder.append(text);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleType.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleType.java
new file mode 100644
index 0000000..4af299c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleType.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.errors.Unreachable;
+
+public enum ProguardKeepRuleType {
+ KEEP,
+ KEEP_CLASS_MEMBERS,
+ KEEP_CLASSES_WITH_MEMBERS;
+
+ @Override
+ public String toString() {
+ switch (this) {
+ case KEEP:
+ return "keep";
+ case KEEP_CLASS_MEMBERS:
+ return "keepclassmembers";
+ case KEEP_CLASSES_WITH_MEMBERS:
+ return "keepclasseswithmembers";
+ default:
+ throw new Unreachable("Unknown ProguardKeepRuleType.");
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
new file mode 100644
index 0000000..08c79aa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -0,0 +1,351 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
+public class ProguardMemberRule {
+
+ public static class Builder {
+
+ private ProguardTypeMatcher annotation;
+ private DexAccessFlags accessFlags = new DexAccessFlags(0);
+ private DexAccessFlags negatedAccessFlags = new DexAccessFlags(0);
+ private ProguardMemberType ruleType;
+ private ProguardNameMatcher name;
+ private ProguardTypeMatcher type;
+ private List<ProguardTypeMatcher> arguments;
+ private ProguardMemberRuleReturnValue returnValue;
+
+ private Builder() {}
+
+ public void setAnnotation(ProguardTypeMatcher annotation) {
+ this.annotation = annotation;
+ }
+
+ public DexAccessFlags getAccessFlags() {
+ return accessFlags;
+ }
+
+ public void setAccessFlags(DexAccessFlags flags) {
+ accessFlags = flags;
+ }
+
+ public DexAccessFlags getNegatedAccessFlags() {
+ return negatedAccessFlags;
+ }
+
+ public void setNegatedAccessFlags(DexAccessFlags flags) {
+ negatedAccessFlags = flags;
+ }
+
+ public void setRuleType(ProguardMemberType ruleType) {
+ this.ruleType = ruleType;
+ }
+
+ public void setName(String name) {
+ this.name = ProguardNameMatcher.create(name);
+ }
+
+ public ProguardTypeMatcher getTypeMatcher() {
+ return type;
+ }
+
+ public void setTypeMatcher(ProguardTypeMatcher type) {
+ this.type = type;
+ }
+
+ public void setArguments(List<ProguardTypeMatcher> arguments) {
+ this.arguments = arguments;
+ }
+
+ public void setReturnValue(ProguardMemberRuleReturnValue value) {
+ returnValue = value;
+ }
+
+ public boolean isValid() {
+ return ruleType != null;
+ }
+
+ public ProguardMemberRule build() {
+ assert isValid();
+ return new ProguardMemberRule(annotation, accessFlags, negatedAccessFlags, ruleType, name,
+ type, arguments, returnValue);
+ }
+ }
+
+ private final ProguardTypeMatcher annotation;
+ private final DexAccessFlags accessFlags;
+ private final DexAccessFlags negatedAccessFlags;
+ private final ProguardMemberType ruleType;
+ private final ProguardNameMatcher name;
+ private final ProguardTypeMatcher type;
+ private final List<ProguardTypeMatcher> arguments;
+ private final ProguardMemberRuleReturnValue returnValue;
+
+ private ProguardMemberRule(
+ ProguardTypeMatcher annotation,
+ DexAccessFlags accessFlags,
+ DexAccessFlags negatedAccessFlags,
+ ProguardMemberType ruleType,
+ ProguardNameMatcher name,
+ ProguardTypeMatcher type,
+ List<ProguardTypeMatcher> arguments,
+ ProguardMemberRuleReturnValue returnValue) {
+ this.annotation = annotation;
+ this.accessFlags = accessFlags;
+ this.negatedAccessFlags = negatedAccessFlags;
+ this.ruleType = ruleType;
+ this.name = name;
+ this.type = type;
+ this.arguments = arguments != null ? ImmutableList.copyOf(arguments) : null;
+ this.returnValue = returnValue;
+ }
+
+ /**
+ * Create a new empty builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public ProguardTypeMatcher getAnnotation() {
+ return annotation;
+ }
+
+ public DexAccessFlags getAccessFlags() {
+ return accessFlags;
+ }
+
+ public DexAccessFlags getNegatedAccessFlags() {
+ return negatedAccessFlags;
+ }
+
+ public ProguardMemberType getRuleType() {
+ return ruleType;
+ }
+
+ public ProguardNameMatcher getName() {
+ return name;
+ }
+
+ public ProguardTypeMatcher getType() {
+ return type;
+ }
+
+ public List<ProguardTypeMatcher> getArguments() {
+ return arguments;
+ }
+
+ public boolean hasReturnValue() {
+ return returnValue != null;
+ }
+
+ public ProguardMemberRuleReturnValue getReturnValue() {
+ return returnValue;
+ }
+
+ public ProguardTypeMatcher getTypeMatcher() {
+ return type;
+ }
+
+ public boolean matches(DexEncodedField field, RootSetBuilder builder) {
+ switch (getRuleType()) {
+ case ALL:
+ case ALL_FIELDS:
+ // Access flags check.
+ if (!field.accessFlags.containsAllOf(getAccessFlags()) ||
+ !field.accessFlags.containsNoneOf(getNegatedAccessFlags())) {
+ break;
+ }
+ // Annotations check.
+ return RootSetBuilder.containsAnnotation(annotation, field.annotations);
+ case FIELD:
+ // Name check.
+ String name = builder.lookupString(field.field.name);
+ if (!getName().matches(name)) {
+ break;
+ }
+ // Access flags check.
+ if (!(field.accessFlags.containsAllOf(getAccessFlags()) &&
+ field.accessFlags.containsNoneOf(getNegatedAccessFlags()))) {
+ break;
+ }
+ // Type check.
+ if (!this.type.matches(field.field.type)) {
+ break;
+ }
+ // Annotations check
+ if (!RootSetBuilder.containsAnnotation(annotation, field.annotations)) {
+ break;
+ }
+ return true;
+ case ALL_METHODS:
+ case INIT:
+ case CONSTRUCTOR:
+ case METHOD:
+ break;
+ }
+ return false;
+ }
+
+ public boolean matches(DexEncodedMethod method, RootSetBuilder builder) {
+ switch (getRuleType()) {
+ case ALL_METHODS:
+ if (method.accessFlags.isConstructor() && method.accessFlags.isStatic()) {
+ break;
+ }
+ case ALL:
+ // Access flags check.
+ if (!method.accessFlags.containsAllOf(getAccessFlags()) ||
+ !method.accessFlags.containsNoneOf(getNegatedAccessFlags())) {
+ break;
+ }
+ // Annotations check.
+ return RootSetBuilder.containsAnnotation(annotation, method.annotations);
+ case METHOD:
+ // Check return type.
+ if (!type.matches(method.method.proto.returnType)) {
+ break;
+ }
+ // Fall through for access flags, name and arguments.
+ case CONSTRUCTOR:
+ case INIT:
+ // Name check.
+ String name = builder.lookupString(method.method.name);
+ if (!getName().matches(name)) {
+ break;
+ }
+ // Access flags check.
+ if (!(method.accessFlags.containsAllOf(getAccessFlags()) &&
+ method.accessFlags.containsNoneOf(getNegatedAccessFlags()))) {
+ break;
+ }
+ // Annotations check.
+ if (!RootSetBuilder.containsAnnotation(annotation, method.annotations)) {
+ break;
+ }
+ // Parameter types check.
+ List<ProguardTypeMatcher> arguments = getArguments();
+ if (arguments.size() == 1 && arguments.get(0).isTripleDotPattern()) {
+ return true;
+ } else {
+ DexType[] parameters = method.method.proto.parameters.values;
+ if (parameters.length != arguments.size()) {
+ break;
+ }
+ int i = 0;
+ for (; i < parameters.length; i++) {
+ if (!arguments.get(i).matches(parameters[i])) {
+ break;
+ }
+ }
+ if (i == parameters.length) {
+ // All parameters matched.
+ return true;
+ }
+ }
+ break;
+ case ALL_FIELDS:
+ case FIELD:
+ break;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ProguardMemberRule)) {
+ return false;
+ }
+
+ ProguardMemberRule that = (ProguardMemberRule) o;
+
+ if (annotation != null ? !annotation.equals(that.annotation) : that.annotation != null) {
+ return false;
+ }
+ if (!accessFlags.equals(that.accessFlags)) {
+ return false;
+ }
+ if (!negatedAccessFlags.equals(that.negatedAccessFlags)) {
+ return false;
+ }
+ if (ruleType != that.ruleType) {
+ return false;
+ }
+ if (name != null ? !name.equals(that.name) : that.name != null) {
+ return false;
+ }
+ if (type != null ? !type.equals(that.type) : that.type != null) {
+ return false;
+ }
+ return arguments != null ? arguments.equals(that.arguments) : that.arguments == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = annotation != null ? annotation.hashCode() : 0;
+ result = 31 * result + accessFlags.hashCode();
+ result = 31 * result + negatedAccessFlags.hashCode();
+ result = 31 * result + (ruleType != null ? ruleType.hashCode() : 0);
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ result = 31 * result + (type != null ? type.hashCode() : 0);
+ result = 31 * result + (arguments != null ? arguments.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ ProguardKeepRule.appendNonEmpty(result, "@", annotation, " ");
+ ProguardKeepRule.appendNonEmpty(result, null, accessFlags, " ");
+ ProguardKeepRule
+ .appendNonEmpty(result, null, negatedAccessFlags.toString().replace(" ", " !"), " ");
+ switch (getRuleType()) {
+ case ALL_FIELDS:
+ result.append("<fields>");
+ break;
+ case ALL_METHODS:
+ result.append("<methods>");
+ break;
+ case METHOD:
+ result.append(getType());
+ result.append(' ');
+ case CONSTRUCTOR:
+ case INIT: {
+ result.append(getName());
+ result.append('(');
+ result.append(StringUtils.join(getArguments(), ","));
+ result.append(')');
+ break;
+ }
+ case FIELD: {
+ result.append(getType());
+ result.append(' ');
+ result.append(getName());
+ break;
+ }
+ default:
+ throw new Unreachable("Unknown kind of member rule");
+ }
+ if (hasReturnValue()) {
+ result.append(returnValue.toString());
+ }
+ return result.toString();
+ }
+
+ public static ProguardMemberRule defaultKeepAllRule() {
+ ProguardMemberRule.Builder ruleBuilder = new ProguardMemberRule.Builder();
+ ruleBuilder.setRuleType(ProguardMemberType.ALL);
+ return ruleBuilder.build();
+ }
+
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
new file mode 100644
index 0000000..b4e9596
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.utils.LongInterval;
+
+public class ProguardMemberRuleReturnValue {
+
+ private final boolean booleanValue;
+ private final LongInterval longInterval;
+ private final DexField field;
+
+ ProguardMemberRuleReturnValue(boolean value) {
+ this.booleanValue = value;
+ this.longInterval = null;
+ this.field = null;
+ }
+
+ ProguardMemberRuleReturnValue(LongInterval value) {
+ this.booleanValue = false;
+ this.longInterval = value;
+ this.field = null;
+ }
+
+ ProguardMemberRuleReturnValue(DexField field) {
+ this.booleanValue = false;
+ this.longInterval = null;
+ this.field = field;
+ }
+
+ public boolean isBoolean() {
+ return longInterval == null && field == null;
+ }
+
+ public boolean isValueRange() {
+ return longInterval != null && field == null;
+ }
+
+ public boolean isField() {
+ return field != null;
+ }
+
+ public boolean getBoolean() {
+ assert isBoolean();
+ return booleanValue;
+ }
+
+ /**
+ * Returns if this return value is a single value.
+ *
+ * Boolean values are considered a single value.
+ */
+ public boolean isSingleValue() {
+ return isBoolean() || (isValueRange() && longInterval.isSingleValue());
+ }
+
+ /**
+ * Returns the return value.
+ *
+ * Boolean values are returned as 0 for <code>false</code> and 1 for <code>true</code>.
+ */
+ public long getSingleValue() {
+ assert isSingleValue();
+ if (isBoolean()) {
+ return booleanValue ? 1 : 0;
+ }
+ return longInterval.getSingleValue();
+ }
+
+ public LongInterval getValueRange() {
+ assert isValueRange();
+ return longInterval;
+ }
+
+ public DexField getField() {
+ assert isField();
+ return field;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ result.append(" return ");
+ if (isBoolean()) {
+ result.append(booleanValue ? "true" : "false");
+ } else if (isValueRange()) {
+ result.append(longInterval.getMin());
+ if (!isSingleValue()) {
+ result.append("..");
+ result.append(longInterval.getMax());
+ }
+ } else {
+ assert isField();
+ result.append(field.clazz.toSourceString() + '.' + field.name);
+ }
+ return result.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberType.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberType.java
new file mode 100644
index 0000000..aa37a2a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberType.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+public enum ProguardMemberType {
+ // Please keep the order so fields are before methods
+ FIELD,
+ ALL_FIELDS,
+ ALL,
+ ALL_METHODS,
+ INIT,
+ CONSTRUCTOR,
+ METHOD;
+
+ public boolean includesFields() {
+ return ordinal() <= ALL.ordinal();
+ }
+
+ public boolean includesMethods() {
+ return ordinal() >= ALL.ordinal();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
new file mode 100644
index 0000000..9286d0a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
@@ -0,0 +1,139 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+public abstract class ProguardNameMatcher {
+
+ private static final ProguardNameMatcher MATCH_ALL_NAMES = new MatchAllNames();
+
+ private ProguardNameMatcher() {
+
+ }
+
+ public static ProguardNameMatcher create(String pattern) {
+ if (pattern.equals("*")) {
+ return MATCH_ALL_NAMES;
+ } else if (pattern.contains("*") || pattern.contains("?")) {
+ return new MatchNamePattern(pattern);
+ } else {
+ return new MatchSpecificName(pattern);
+ }
+ }
+
+ /**
+ * Determines if the proguard name pattern matches the given field or method name.
+ *
+ * @param pattern Proguard name pattern potentially with wildcards: ?, *.
+ * @param name The name to match against.
+ * @return true iff the pattern matches the name.
+ */
+ public static boolean matchFieldOrMethodName(String pattern, String name) {
+ return matchFieldOrMethodNameImpl(pattern, 0, name, 0);
+ }
+
+ private static boolean matchFieldOrMethodNameImpl(
+ String pattern, int patternIndex, String name, int nameIndex) {
+ for (int i = patternIndex; i < pattern.length(); i++) {
+ char patternChar = pattern.charAt(i);
+ switch (patternChar) {
+ case '*':
+ // Match the rest of the pattern against the rest of the name.
+ for (int nextNameIndex = nameIndex; nextNameIndex <= name.length(); nextNameIndex++) {
+ if (matchFieldOrMethodNameImpl(pattern, i + 1, name, nextNameIndex)) {
+ return true;
+ }
+ }
+ return false;
+ case '?':
+ if (nameIndex == name.length()) {
+ return false;
+ }
+ nameIndex++;
+ break;
+ default:
+ if (nameIndex == name.length() || patternChar != name.charAt(nameIndex++)) {
+ return false;
+ }
+ break;
+ }
+ }
+ return nameIndex == name.length();
+ }
+
+ public abstract boolean matches(String name);
+
+ private static class MatchAllNames extends ProguardNameMatcher {
+
+ @Override
+ public boolean matches(String name) {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "*";
+ }
+ }
+
+ private static class MatchNamePattern extends ProguardNameMatcher {
+
+ private final String pattern;
+
+ MatchNamePattern(String pattern) {
+ this.pattern = pattern;
+ }
+
+ @Override
+ public boolean matches(String name) {
+ return matchFieldOrMethodName(pattern, name);
+ }
+
+ @Override
+ public String toString() {
+ return pattern;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ return o instanceof MatchNamePattern && pattern.equals(((MatchNamePattern) o).pattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return pattern.hashCode();
+ }
+ }
+
+ private static class MatchSpecificName extends ProguardNameMatcher {
+
+ private final String name;
+
+ MatchSpecificName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean matches(String name) {
+ return this.name.equals(name);
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof MatchSpecificName && name.equals(((MatchSpecificName) o).name);
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardRuleParserException.java b/src/main/java/com/android/tools/r8/shaking/ProguardRuleParserException.java
new file mode 100644
index 0000000..0fb2787
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardRuleParserException.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+public class ProguardRuleParserException extends Exception {
+ public ProguardRuleParserException(String message, String snippet) {
+ this(message, snippet, null);
+ }
+
+ public ProguardRuleParserException(String message, String snippet, Throwable cause) {
+ super(message + " at " + snippet, cause);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
new file mode 100644
index 0000000..fd123d4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -0,0 +1,314 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.DescriptorUtils;
+
+public abstract class ProguardTypeMatcher {
+
+ private static final String MATCH_ALL_PATTERN = "***";
+ private static final String MATCH_ANY_ARG_SEQUENCE_PATTERN = "...";
+ private static final String LEGACY_MATCH_CLASS_PATTERN = "*";
+ private static final String MATCH_CLASS_PATTERN = "**";
+ private static final String MATCH_BASIC_PATTERN = "%";
+
+ private ProguardTypeMatcher() {
+ }
+
+ enum ClassOrType {
+ CLASS,
+ TYPE
+ }
+
+ public abstract boolean matches(DexType type);
+
+ public abstract String toString();
+
+ public boolean isTripleDotPattern() {
+ return false;
+ }
+
+ public static ProguardTypeMatcher create(String pattern, ClassOrType kind,
+ DexItemFactory dexItemFactory) {
+ if (pattern == null) {
+ return null;
+ }
+ switch (pattern) {
+ case MATCH_ALL_PATTERN:
+ return MatchAllTypes.MATCH_ALL_TYPES;
+ case MATCH_ANY_ARG_SEQUENCE_PATTERN:
+ return MatchAnyArgSequence.MATCH_ANY_ARG_SEQUENCE;
+ case MATCH_CLASS_PATTERN:
+ return MatchClassTypes.MATCH_CLASS_TYPES;
+ case LEGACY_MATCH_CLASS_PATTERN:
+ return MatchClassTypes.LEGACY_MATCH_CLASS_TYPES;
+ case MATCH_BASIC_PATTERN:
+ return MatchBasicTypes.MATCH_BASIC_TYPES;
+ }
+ if (!pattern.contains("*") && !pattern.contains("%") && !pattern.contains("?")) {
+ return new MatchSpecificType(
+ dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(pattern)));
+ }
+ return new MatchTypePattern(pattern, kind);
+ }
+
+ public static ProguardTypeMatcher defaultAllMatcher() {
+ return MatchAllTypes.MATCH_ALL_TYPES;
+ }
+
+ @Override
+ public abstract boolean equals(Object o);
+
+ @Override
+ public abstract int hashCode();
+
+ private static class MatchAllTypes extends ProguardTypeMatcher {
+
+ private static final ProguardTypeMatcher MATCH_ALL_TYPES = new MatchAllTypes();
+
+ @Override
+ public boolean matches(DexType type) {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return MATCH_ALL_PATTERN;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof MatchAllTypes;
+ }
+
+ @Override
+ public int hashCode() {
+ return getClass().hashCode();
+ }
+ }
+
+ private static class MatchAnyArgSequence extends ProguardTypeMatcher {
+
+ private static final ProguardTypeMatcher MATCH_ANY_ARG_SEQUENCE = new MatchAnyArgSequence();
+
+ @Override
+ public boolean matches(DexType type) {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public String toString() {
+ return MATCH_ANY_ARG_SEQUENCE_PATTERN;
+ }
+
+ @Override
+ public boolean isTripleDotPattern() {
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof MatchAnyArgSequence;
+ }
+
+ @Override
+ public int hashCode() {
+ return getClass().hashCode();
+ }
+ }
+
+ private static class MatchClassTypes extends ProguardTypeMatcher {
+
+ private static final ProguardTypeMatcher MATCH_CLASS_TYPES = new MatchClassTypes(
+ MATCH_CLASS_PATTERN);
+ private static final ProguardTypeMatcher LEGACY_MATCH_CLASS_TYPES = new MatchClassTypes(
+ LEGACY_MATCH_CLASS_PATTERN);
+
+ private final String pattern;
+
+ private MatchClassTypes(String pattern) {
+ assert pattern.equals(LEGACY_MATCH_CLASS_PATTERN) || pattern.equals(MATCH_CLASS_PATTERN);
+ this.pattern = pattern;
+ }
+
+ @Override
+ public boolean matches(DexType type) {
+ return type.isClassType();
+ }
+
+ @Override
+ public String toString() {
+ return pattern;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof MatchClassTypes && pattern.equals(((MatchClassTypes) o).pattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return pattern.hashCode();
+ }
+ }
+
+ private static class MatchBasicTypes extends ProguardTypeMatcher {
+
+ private static final ProguardTypeMatcher MATCH_BASIC_TYPES = new MatchBasicTypes();
+
+ @Override
+ public boolean matches(DexType type) {
+ return type.isPrimitiveType();
+ }
+
+ @Override
+ public String toString() {
+ return MATCH_BASIC_PATTERN;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof MatchBasicTypes;
+ }
+
+ @Override
+ public int hashCode() {
+ return getClass().hashCode();
+ }
+ }
+
+ public static class MatchSpecificType extends ProguardTypeMatcher {
+
+ public final DexType type;
+
+ private MatchSpecificType(DexType type) {
+ this.type = type;
+ }
+
+ @Override
+ public boolean matches(DexType type) {
+ return this.type == type;
+ }
+
+ @Override
+ public String toString() {
+ return type.toSourceString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof MatchSpecificType) {
+ return type.equals(((MatchSpecificType) o).type);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+ }
+
+ private static class MatchTypePattern extends ProguardTypeMatcher {
+
+ private final String pattern;
+ private final ClassOrType kind;
+
+ private MatchTypePattern(String pattern, ClassOrType kind) {
+ this.pattern = pattern;
+ this.kind = kind;
+ }
+
+ @Override
+ public boolean matches(DexType type) {
+ // TODO(herhut): Translate pattern to work on descriptors instead.
+ String typeName = type.toSourceString();
+ return matchClassOrTypeNameImpl(pattern, 0, typeName, 0, kind);
+ }
+
+ private static boolean matchClassOrTypeNameImpl(
+ String pattern, int patternIndex, String className, int nameIndex, ClassOrType kind) {
+ for (int i = patternIndex; i < pattern.length(); i++) {
+ char patternChar = pattern.charAt(i);
+ switch (patternChar) {
+ case '*':
+ boolean includeSeparators = pattern.length() > (i + 1) && pattern.charAt(i + 1) == '*';
+ int nextPatternIndex = i + (includeSeparators ? 2 : 1);
+ // Fast cases for the common case where a pattern ends with '**' or '*'.
+ if (nextPatternIndex == pattern.length()) {
+ if (includeSeparators) {
+ return kind == ClassOrType.CLASS || !isArrayType(className);
+ }
+ boolean hasSeparators = containsSeparatorsStartingAt(className, nameIndex);
+ return !hasSeparators && (kind == ClassOrType.CLASS || !isArrayType(className));
+ }
+ // Match the rest of the pattern against the (non-empty) rest of the class name.
+ for (int nextNameIndex = nameIndex; nextNameIndex < className.length();
+ nextNameIndex++) {
+ if (!includeSeparators && className.charAt(nextNameIndex) == '.') {
+ return matchClassOrTypeNameImpl(pattern, nextPatternIndex, className, nextNameIndex,
+ kind);
+ }
+ if (kind == ClassOrType.TYPE && className.charAt(nextNameIndex) == '[') {
+ return matchClassOrTypeNameImpl(pattern, nextPatternIndex, className, nextNameIndex,
+ kind);
+ }
+ if (matchClassOrTypeNameImpl(pattern, nextPatternIndex, className, nextNameIndex,
+ kind)) {
+ return true;
+ }
+ }
+ // Finally, check the case where the '*' or '**' eats all of the class name.
+ return matchClassOrTypeNameImpl(pattern, nextPatternIndex, className,
+ className.length(),
+ kind);
+ case '?':
+ if (nameIndex == className.length() || className.charAt(nameIndex++) == '.') {
+ return false;
+ }
+ break;
+ default:
+ if (nameIndex == className.length() || patternChar != className.charAt(nameIndex++)) {
+ return false;
+ }
+ break;
+ }
+ }
+ return nameIndex == className.length();
+ }
+
+ private static boolean containsSeparatorsStartingAt(String className, int nameIndex) {
+ return className.indexOf('.', nameIndex) != -1;
+ }
+
+ private static boolean isArrayType(String type) {
+ int length = type.length();
+ if (length < 2) {
+ return false;
+ }
+ return type.charAt(length - 1) == ']' && type.charAt(length - 2) == '[';
+ }
+
+ @Override
+ public String toString() {
+ return pattern;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof MatchTypePattern) {
+ MatchTypePattern that = (MatchTypePattern) o;
+ return kind.equals(that.kind) && pattern.equals(that.pattern);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return pattern.hashCode() * 7 + kind.hashCode();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ReasonPrinter.java b/src/main/java/com/android/tools/r8/shaking/ReasonPrinter.java
new file mode 100644
index 0000000..47893e5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ReasonPrinter.java
@@ -0,0 +1,243 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.Sets;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.Map;
+import java.util.Set;
+
+public class ReasonPrinter {
+
+ private final Set<DexItem> itemsQueried;
+ private ReasonFormatter formatter;
+
+ private final Map<DexEncodedField, KeepReason> liveFields;
+ private final Map<DexEncodedMethod, KeepReason> liveMethods;
+ private final Map<DexItem, KeepReason> reachablityReasons;
+ private final Map<DexType, KeepReason> instantiatedTypes;
+
+ ReasonPrinter(Set<DexItem> itemsQueried, Map<DexEncodedField, KeepReason> liveFields,
+ Map<DexEncodedMethod, KeepReason> liveMethods, Map<DexItem, KeepReason> reachablityReasons,
+ Map<DexType, KeepReason> instantiatedTypes) {
+ this.itemsQueried = itemsQueried;
+ this.liveFields = liveFields;
+ this.liveMethods = liveMethods;
+ this.reachablityReasons = reachablityReasons;
+ this.instantiatedTypes = instantiatedTypes;
+ }
+
+ public void run(DexApplication application) {
+ // TODO(herhut): Instead of traversing the app, sort the queried items.
+ formatter = new ReasonFormatter();
+ for (DexClass clazz : application.classes()) {
+ if (itemsQueried.contains(clazz)) {
+ printReasonFor(clazz);
+ }
+ Arrays.stream(clazz.staticFields()).filter(itemsQueried::contains)
+ .forEach(this::printReasonFor);
+ Arrays.stream(clazz.instanceFields()).filter(itemsQueried::contains)
+ .forEach(this::printReasonFor);
+ Arrays.stream(clazz.directMethods()).filter(itemsQueried::contains)
+ .forEach(this::printReasonFor);
+ Arrays.stream(clazz.virtualMethods()).filter(itemsQueried::contains)
+ .forEach(this::printReasonFor);
+ }
+ }
+
+ private void printNoIdeaWhy(DexItem item, ReasonFormatter formatter) {
+ formatter.startItem(item);
+ formatter.pushEmptyPrefix();
+ formatter.addReason("is kept for unknown reason.");
+ formatter.popPrefix();
+ formatter.endItem();
+ }
+
+ private void printOnlyAbstractShell(DexItem item, ReasonFormatter formatter) {
+ formatter.startItem(item);
+ KeepReason reachableReason = reachablityReasons.get(item);
+ if (reachableReason != null) {
+ formatter.pushPrefix(
+ "is not kept, only its abstract declaration is needed because it ");
+ reachableReason.print(formatter);
+ formatter.popPrefix();
+ } else {
+ formatter.pushEmptyPrefix();
+ formatter.addReason("is not kept, only its abstract declaration is.");
+ formatter.popPrefix();
+ }
+ formatter.endItem();
+ }
+
+ private void printReasonFor(DexClass item) {
+ KeepReason reason = instantiatedTypes.get(item.type);
+ if (reason == null) {
+ if (item.accessFlags.isAbstract()) {
+ printOnlyAbstractShell(item, formatter);
+ } else {
+ printNoIdeaWhy(item, formatter);
+ }
+ } else {
+ formatter.startItem(item);
+ formatter.pushIsLivePrefix();
+ reason.print(formatter);
+ formatter.popPrefix();
+ formatter.endItem();
+ }
+ }
+
+ private void printReasonFor(DexEncodedMethod item) {
+ KeepReason reasonLive = liveMethods.get(item);
+ if (reasonLive == null) {
+ if (item.accessFlags.isAbstract()) {
+ printOnlyAbstractShell(item, formatter);
+ } else {
+ printNoIdeaWhy(item.method, formatter);
+ }
+ } else {
+ formatter.addMethodReferenceReason(item);
+ }
+ }
+
+ private void printReasonFor(DexEncodedField item) {
+ KeepReason reason = liveFields.get(item);
+ if (reason == null) {
+ if (item.accessFlags.isAbstract()) {
+ printOnlyAbstractShell(item, formatter);
+ } else {
+ printNoIdeaWhy(item.field, formatter);
+ }
+ } else {
+ formatter.startItem(item.field);
+ formatter.pushIsLivePrefix();
+ reason.print(formatter);
+ formatter.popPrefix();
+ reason = reachablityReasons.get(item);
+ if (reason != null) {
+ formatter.pushIsReachablePrefix();
+ reason.print(formatter);
+ formatter.popPrefix();
+ }
+ formatter.endItem();
+ }
+ }
+
+ class ReasonFormatter {
+
+ private Set<DexItem> seen = Sets.newIdentityHashSet();
+ private Deque<String> prefixes = new ArrayDeque<>();
+
+ private int indentation = -1;
+
+ void pushIsLivePrefix() {
+ prefixes.push("is live because ");
+ }
+
+ void pushIsReachablePrefix() {
+ prefixes.push("is reachable because ");
+ }
+
+ void pushPrefix(String prefix) {
+ prefixes.push(prefix);
+ }
+
+ void pushEmptyPrefix() {
+ prefixes.push("");
+ }
+
+ void popPrefix() {
+ prefixes.pop();
+ }
+
+ void startItem(DexItem item) {
+ indentation++;
+ indent();
+ System.out.println(item.toSourceString());
+ }
+
+ private void indent() {
+ for (int i = 0; i < indentation; i++) {
+ System.out.print(" ");
+ }
+ }
+
+ void addReason(String thing) {
+ indent();
+ System.out.print("|- ");
+ String prefix = prefixes.peek();
+ System.out.print(prefix);
+ System.out.println(thing);
+ }
+
+ void addMessage(String thing) {
+ indent();
+ System.out.print("| ");
+ System.out.println(thing);
+ }
+
+ void endItem() {
+ indentation--;
+ }
+
+ void addMethodReferenceReason(DexEncodedMethod method) {
+ if (!seen.add(method.method)) {
+ return;
+ }
+ startItem(method);
+ KeepReason reason = reachablityReasons.get(method);
+ if (reason != null) {
+ pushIsReachablePrefix();
+ reason.print(this);
+ popPrefix();
+ }
+ reason = liveMethods.get(method);
+ if (reason != null) {
+ pushIsLivePrefix();
+ reason.print(this);
+ popPrefix();
+ }
+ endItem();
+ }
+
+ void addTypeLivenessReason(DexType type) {
+ if (!seen.add(type)) {
+ return;
+ }
+ startItem(type);
+ pushIsLivePrefix();
+ KeepReason reason = instantiatedTypes.get(type);
+ if (reason != null) {
+ reason.print(this);
+ }
+ popPrefix();
+ endItem();
+ }
+ }
+
+ public static ReasonPrinter getNoOpPrinter() {
+ return new NoOpReasonPrinter();
+ }
+
+ private static class NoOpReasonPrinter extends ReasonPrinter {
+
+ NoOpReasonPrinter() {
+ super(Collections.emptySet(), Collections.emptyMap(), Collections.emptyMap(),
+ Collections.emptyMap(), Collections.emptyMap());
+ }
+
+ @Override
+ public void run(DexApplication application) {
+ // Intentionally left empty.
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
new file mode 100644
index 0000000..848a743
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -0,0 +1,483 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.Sets;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+public class RootSetBuilder {
+
+ private DexApplication application;
+ private final List<ProguardConfigurationRule> rules;
+ private final Map<DexItem, ProguardKeepRule> noShrinking = new IdentityHashMap<>();
+ private final Set<DexItem> noOptimization = Sets.newIdentityHashSet();
+ private final Set<DexItem> noObfuscation = Sets.newIdentityHashSet();
+ private final Set<DexItem> reasonAsked = Sets.newIdentityHashSet();
+ private final Set<DexItem> keepPackageName = Sets.newIdentityHashSet();
+ private final Set<ProguardConfigurationRule> rulesThatUseExtendsOrImplementsWrong =
+ Sets.newIdentityHashSet();
+ private final Set<DexItem> checkDiscarded = Sets.newIdentityHashSet();
+ private final Map<DexType, Map<DexItem, ProguardKeepRule>> dependentNoShrinking =
+ new IdentityHashMap<>();
+ private final Map<DexItem, ProguardMemberRule> noSideEffects = new IdentityHashMap<>();
+ private final Map<DexItem, ProguardMemberRule> assumedValues = new IdentityHashMap<>();
+
+ public RootSetBuilder(DexApplication application,
+ List<ProguardConfigurationRule> rules) {
+ this.application = application;
+ this.rules = rules;
+ }
+
+ private boolean anySuperTypeMatches(DexType type, ProguardTypeMatcher name,
+ ProguardTypeMatcher annotation) {
+ while (type != null) {
+ DexClass clazz = application.definitionFor(type);
+ if (clazz == null) {
+ // TODO(herhut): Warn about broken supertype chain?
+ return false;
+ }
+ if (name.matches(clazz.type) && containsAnnotation(annotation, clazz.annotations)) {
+ return true;
+ }
+ type = clazz.superType;
+ }
+ return false;
+ }
+
+ private boolean anyImplementedInterfaceMatches(DexClass clazz,
+ ProguardTypeMatcher className, ProguardTypeMatcher annotation) {
+ if (clazz == null) {
+ return false;
+ }
+ for (DexType iface : clazz.interfaces.values) {
+ DexClass ifaceClass = application.definitionFor(iface);
+ if (ifaceClass == null) {
+ // TODO(herhut): Warn about broken supertype chain?
+ return false;
+ }
+ // TODO(herhut): Maybe it would be better to do this breadth first.
+ if (className.matches(iface) && containsAnnotation(annotation, ifaceClass.annotations)
+ || anyImplementedInterfaceMatches(ifaceClass, className, annotation)) {
+ return true;
+ }
+ }
+ if (clazz.superType == null) {
+ return false;
+ }
+ DexClass superClass = application.definitionFor(clazz.superType);
+ if (superClass == null) {
+ // TODO(herhut): Warn about broken supertype chain?
+ return false;
+ }
+ return anyImplementedInterfaceMatches(superClass, className, annotation);
+ }
+
+ // Returns a list of types iff the keep rule only contains specific type matches.
+ // Otherwise, null is returned.
+ private DexType[] specificDexTypes(ProguardConfigurationRule rule) {
+ for (ProguardTypeMatcher matcher : rule.getClassNames()) {
+ if (!(matcher instanceof MatchSpecificType)) {
+ return null;
+ }
+ }
+ final int length = rule.getClassNames().size();
+ DexType[] result = new DexType[length];
+ for (int i = 0; i < length; i++) {
+ result[i] = ((MatchSpecificType) rule.getClassNames().get(i)).type;
+ }
+ return result;
+ }
+
+ // Process a class with the keep rule.
+ private void process(DexClass clazz, ProguardConfigurationRule rule) {
+ if (!clazz.accessFlags.containsAllOf(rule.getClassAccessFlags())) {
+ return;
+ }
+ if (!clazz.accessFlags.containsNoneOf(rule.getNegatedClassAccessFlags())) {
+ return;
+ }
+ if (!containsAnnotation(rule.getClassAnnotation(), clazz.annotations)) {
+ return;
+ }
+
+ // In principle it should make a difference whether the user specified in a class
+ // spec that a class either extends or implements another type. However, proguard
+ // seems not to care, so users have started to use this inconsistently. We are thus
+ // inconsistent, as well, but tell them.
+ // TODO(herhut): One day make this do what it says.
+ if (rule.hasInheritanceClassName()) {
+ boolean extendsExpected =
+ anySuperTypeMatches(clazz.superType, rule.getInheritanceClassName(),
+ rule.getInheritanceAnnotation());
+ boolean implementsExpected = false;
+ if (!extendsExpected) {
+ implementsExpected =
+ anyImplementedInterfaceMatches(clazz, rule.getInheritanceClassName(),
+ rule.getInheritanceAnnotation());
+ }
+ if (!extendsExpected && !implementsExpected) {
+ return;
+ }
+ // Warn if users got it wrong, but only warn once.
+ if (extendsExpected && !rule.getInheritanceIsExtends()) {
+ if (rulesThatUseExtendsOrImplementsWrong.add(rule)) {
+ System.err.println(
+ "The rule `" + rule + "` uses implements but actually matches extends.");
+ }
+ } else if (implementsExpected && rule.getInheritanceIsExtends()) {
+ if (rulesThatUseExtendsOrImplementsWrong.add(rule)) {
+ System.err.println(
+ "The rule `" + rule + "` uses extends but actually matches implements.");
+ }
+ }
+ }
+
+ for (ProguardTypeMatcher className : rule.getClassNames()) {
+ if (className.matches(clazz.type)) {
+ Collection<ProguardMemberRule> memberKeepRules = rule.getMemberRules();
+ if (rule instanceof ProguardKeepRule) {
+ switch (((ProguardKeepRule) rule).getType()) {
+ case KEEP_CLASS_MEMBERS: {
+ markMatchingVisibleMethods(clazz, memberKeepRules, rule, clazz.type);
+ markMatchingFields(clazz, memberKeepRules, rule, clazz.type);
+ break;
+ }
+ case KEEP_CLASSES_WITH_MEMBERS: {
+ if (!allRulesSatisfied(memberKeepRules, clazz)) {
+ break;
+ }
+ // fallthrough;
+ }
+ case KEEP: {
+ markClass(clazz, rule);
+ markClass(clazz, rule);
+ markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+ markMatchingFields(clazz, memberKeepRules, rule, null);
+ break;
+ }
+ }
+ } else if (rule instanceof ProguardAssumeNoSideEffectRule) {
+ markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+ markMatchingFields(clazz, memberKeepRules, rule, null);
+ } else {
+ assert rule instanceof ProguardAssumeValuesRule;
+ markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+ markMatchingFields(clazz, memberKeepRules, rule, null);
+ }
+ }
+ }
+ }
+
+ public RootSet run(ExecutorService executorService) throws ExecutionException {
+ application.timing.begin("Build root set...");
+ try {
+ List<Future<?>> futures = new ArrayList<>();
+ // Mark all the things explicitly listed in keep rules.
+ if (rules != null) {
+ for (ProguardConfigurationRule rule : rules) {
+ DexType[] specifics = specificDexTypes(rule);
+ if (specifics != null) {
+ // This keep rule only lists specific type matches.
+ // This means there is no need to iterate over all classes.
+ for (DexType type : specifics) {
+ DexClass clazz = application.definitionFor(type);
+ // Ignore keep rule iff it does not reference a class in the app.
+ if (clazz != null) {
+ process(clazz, rule);
+ }
+ }
+ } else {
+ futures.add(executorService.submit(() -> {
+ for (DexProgramClass clazz : application.classes()) {
+ process(clazz, rule);
+ }
+ if (rule.applyToLibraryClasses()) {
+ for (DexLibraryClass clazz : application.libraryClasses()) {
+ process(clazz, rule);
+ }
+ }
+ }));
+ }
+ }
+ ThreadUtils.awaitFutures(futures);
+ }
+ } finally {
+ application.timing.end();
+ }
+ return new RootSet(noShrinking, noOptimization, noObfuscation, reasonAsked, keepPackageName,
+ checkDiscarded, noSideEffects, assumedValues, dependentNoShrinking);
+ }
+
+ private void markMatchingVisibleMethods(DexClass clazz,
+ Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
+ DexType onlyIfClassKept) {
+ Set<Wrapper<DexMethod>> methodsMarked = new HashSet<>();
+ while (clazz != null) {
+ markMethods(clazz.directMethods(), memberKeepRules, rule, methodsMarked, onlyIfClassKept);
+ markMethods(clazz.virtualMethods(), memberKeepRules, rule, methodsMarked, onlyIfClassKept);
+ clazz = application.definitionFor(clazz.superType);
+ }
+ }
+
+ private void markMatchingFields(DexClass clazz,
+ Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
+ DexType onlyIfClassKept) {
+ markFields(clazz.staticFields(), memberKeepRules, rule, onlyIfClassKept);
+ markFields(clazz.instanceFields(), memberKeepRules, rule, onlyIfClassKept);
+ }
+
+ public static void writeSeeds(Iterable<DexItem> seeds, PrintStream out) {
+ for (DexItem seed : seeds) {
+ if (seed instanceof DexClass) {
+ out.println(((DexClass) seed).type.toSourceString());
+ } else if (seed instanceof DexEncodedField) {
+ DexField field = ((DexEncodedField) seed).field;
+ out.println(
+ field.clazz.toSourceString() + ": " + field.type.toSourceString() + " " + field.name
+ .toSourceString());
+ } else if (seed instanceof DexEncodedMethod) {
+ DexEncodedMethod encodedMethod = (DexEncodedMethod) seed;
+ DexMethod method = encodedMethod.method;
+ out.print(method.holder.toSourceString() + ": ");
+ if (encodedMethod.accessFlags.isConstructor()) {
+ if (encodedMethod.accessFlags.isStatic()) {
+ out.print("<clinit>(");
+ } else {
+ String holderName = method.holder.toSourceString();
+ String constrName = holderName.substring(holderName.lastIndexOf('.') + 1);
+ out.print(constrName + "(");
+ }
+ } else {
+ out.print(
+ method.proto.returnType.toSourceString() + " " + method.name.toSourceString() + "(");
+ }
+ boolean first = true;
+ for (DexType param : method.proto.parameters.values) {
+ if (!first) {
+ out.print(",");
+ }
+ first = false;
+ out.print(param.toSourceString());
+ }
+ out.println(")");
+ }
+ }
+ out.close();
+ }
+
+ private boolean allRulesSatisfied(Collection<ProguardMemberRule> memberKeepRules,
+ DexClass clazz) {
+ for (ProguardMemberRule rule : memberKeepRules) {
+ if (!ruleSatisfied(rule, clazz)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks whether the given rule is satisfied bu this clazz, not taking superclasses into
+ * account.
+ */
+ private boolean ruleSatisfied(ProguardMemberRule rule, DexClass clazz) {
+ if (ruleSatisfiedByMethods(rule, clazz.directMethods()) ||
+ ruleSatisfiedByMethods(rule, clazz.virtualMethods()) ||
+ ruleSatisfiedByFields(rule, clazz.staticFields()) ||
+ ruleSatisfiedByFields(rule, clazz.instanceFields())) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean ruleSatisfiedByMethods(ProguardMemberRule rule, DexEncodedMethod[] methods) {
+ if (rule.getRuleType().includesMethods()) {
+ for (DexEncodedMethod method : methods) {
+ if (rule.matches(method, this)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean ruleSatisfiedByFields(ProguardMemberRule rule, DexEncodedField[] fields) {
+ if (rule.getRuleType().includesFields()) {
+ for (DexEncodedField field : fields) {
+ if (rule.matches(field, this)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ static boolean containsAnnotation(ProguardTypeMatcher classAnnotation,
+ DexAnnotationSet annotations) {
+ if (classAnnotation == null) {
+ return true;
+ }
+ if (annotations.isEmpty()) {
+ return false;
+ }
+ for (DexAnnotation annotation : annotations.annotations) {
+ if (classAnnotation.matches(annotation.annotation.type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private final IdentityHashMap<DexString, String> stringCache = new IdentityHashMap<>();
+ private final IdentityHashMap<DexType, String> typeCache = new IdentityHashMap<>();
+
+ public String lookupString(DexString name) {
+ return stringCache.computeIfAbsent(name, DexString::toString);
+ }
+
+ public String lookupType(DexType type) {
+ return typeCache.computeIfAbsent(type, DexType::toSourceString);
+ }
+
+ private void markMethods(DexEncodedMethod[] methods, Collection<ProguardMemberRule> rules,
+ ProguardConfigurationRule context, Set<Wrapper<DexMethod>> methodsMarked,
+ DexType onlyIfClassKept) {
+ for (DexEncodedMethod method : methods) {
+ if (methodsMarked.contains(MethodSignatureEquivalence.get().wrap(method.method))) {
+ continue;
+ }
+ for (ProguardMemberRule rule : rules) {
+ if (rule.matches(method, this)) {
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Marking method `%s` due to `%s { %s }`.", method, context,
+ rule);
+ }
+ addItemToSets(method, context, rule, onlyIfClassKept);
+ }
+ }
+ }
+ }
+
+ private void markFields(DexEncodedField[] fields, Collection<ProguardMemberRule> rules,
+ ProguardConfigurationRule context, DexType onlyIfClassKept) {
+ for (DexEncodedField field : fields) {
+ for (ProguardMemberRule rule : rules) {
+ if (rule.matches(field, this)) {
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Marking field `%s` due to `%s { %s }`.", field, context,
+ rule);
+ }
+ addItemToSets(field, context, rule, onlyIfClassKept);
+ }
+ }
+ }
+ }
+
+ private void markClass(DexClass clazz, ProguardConfigurationRule rule) {
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Marking class `%s` due to `%s`.", clazz.type, rule);
+ }
+ addItemToSets(clazz, rule, null, null);
+ }
+
+ private synchronized void addItemToSets(DexItem item, ProguardConfigurationRule context,
+ ProguardMemberRule rule, DexType onlyIfClassKept) {
+ if (context instanceof ProguardKeepRule) {
+ ProguardKeepRule keepRule = (ProguardKeepRule) context;
+ ProguardKeepRuleModifiers modifiers = keepRule.getModifiers();
+ if (!modifiers.allowsShrinking) {
+ if (onlyIfClassKept != null) {
+ dependentNoShrinking.computeIfAbsent(onlyIfClassKept, x -> new IdentityHashMap<>())
+ .put(item, keepRule);
+ } else {
+ noShrinking.put(item, keepRule);
+ }
+ }
+ if (!modifiers.allowsOptimization) {
+ noOptimization.add(item);
+ }
+ if (!modifiers.allowsObfuscation) {
+ noObfuscation.add(item);
+ }
+ if (modifiers.whyAreYouKeeping) {
+ assert onlyIfClassKept == null;
+ reasonAsked.add(item);
+ }
+ if (modifiers.keepPackageNames) {
+ assert onlyIfClassKept == null;
+ keepPackageName.add(item);
+ }
+ if (modifiers.checkDiscarded) {
+ checkDiscarded.add(item);
+ }
+ } else if (context instanceof ProguardAssumeNoSideEffectRule) {
+ noSideEffects.put(item, rule);
+ } else if (context instanceof ProguardAssumeValuesRule) {
+ assumedValues.put(item, rule);
+ }
+ }
+
+ public static class RootSet {
+
+ public final Map<DexItem, ProguardKeepRule> noShrinking;
+ public final Set<DexItem> noOptimization;
+ public final Set<DexItem> noObfuscation;
+ public final Set<DexItem> reasonAsked;
+ public final Set<DexItem> keepPackageName;
+ public final Set<DexItem> checkDiscarded;
+ public final Map<DexItem, ProguardMemberRule> noSideEffects;
+ public final Map<DexItem, ProguardMemberRule> assumedValues;
+ private final Map<DexType, Map<DexItem, ProguardKeepRule>> dependentNoShrinking;
+
+ private RootSet(Map<DexItem, ProguardKeepRule> noShrinking,
+ Set<DexItem> noOptimization, Set<DexItem> noObfuscation, Set<DexItem> reasonAsked,
+ Set<DexItem> keepPackageName, Set<DexItem> checkDiscarded,
+ Map<DexItem, ProguardMemberRule> noSideEffects,
+ Map<DexItem, ProguardMemberRule> assumedValues,
+ Map<DexType, Map<DexItem, ProguardKeepRule>> dependentNoShrinking) {
+ this.noShrinking = Collections.unmodifiableMap(noShrinking);
+ this.noOptimization = Collections.unmodifiableSet(noOptimization);
+ this.noObfuscation = Collections.unmodifiableSet(noObfuscation);
+ this.reasonAsked = Collections.unmodifiableSet(reasonAsked);
+ this.keepPackageName = Collections.unmodifiableSet(keepPackageName);
+ this.checkDiscarded = Collections.unmodifiableSet(checkDiscarded);
+ this.noSideEffects = Collections.unmodifiableMap(noSideEffects);
+ this.assumedValues = Collections.unmodifiableMap(assumedValues);
+ this.dependentNoShrinking = dependentNoShrinking;
+ }
+
+ Map<DexItem, ProguardKeepRule> getDependentItems(DexType type) {
+ return Collections
+ .unmodifiableMap(dependentNoShrinking.getOrDefault(type, Collections.emptyMap()));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
new file mode 100644
index 0000000..5f2996f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -0,0 +1,168 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassPromise;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.KeyedDexItem;
+import com.android.tools.r8.graph.PresortedComparable;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class TreePruner {
+
+ private DexApplication application;
+ private final AppInfoWithLiveness appInfo;
+ private final InternalOptions options;
+
+ public TreePruner(
+ DexApplication application, AppInfoWithLiveness appInfo, InternalOptions options) {
+ this.application = application;
+ this.appInfo = appInfo;
+ this.options = options;
+ }
+
+ public DexApplication run() {
+ application.timing.begin("Pruning application...");
+ if (options.debugKeepRules && !options.skipMinification) {
+ System.out.println(
+ "NOTE: Debugging keep rules on a minified build might yield broken builds, as\n" +
+ " minifcation also depends on the used keep rules. We recommend using\n" +
+ " --skip-minification.");
+ }
+ DexApplication result;
+ try {
+ result = removeUnused(application).build();
+ } finally {
+ application.timing.end();
+ }
+ return result;
+ }
+
+ private DexApplication.Builder removeUnused(DexApplication application) {
+ return new DexApplication.Builder(application, removeUnusedClassStructure(application));
+ }
+
+ private Map<DexType, DexClassPromise> removeUnusedClassStructure(DexApplication application) {
+ Map<DexType, DexClassPromise> classMap = new IdentityHashMap<>();
+ for (DexLibraryClass clazz : application.libraryClasses()) {
+ classMap.put(clazz.type, clazz);
+ }
+ for (DexProgramClass clazz : application.classes()) {
+ if (!appInfo.liveTypes.contains(clazz.type) && !options.debugKeepRules) {
+ // The class is completely unused and we can remove it.
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Removing class: " + clazz);
+ }
+ } else {
+ classMap.put(clazz.type, clazz);
+ if (!appInfo.instantiatedTypes.contains(clazz.type) && !options.debugKeepRules) {
+ // The class is only needed as a type but never instantiated. Make it abstract to reflect
+ // this.
+ if (clazz.accessFlags.isFinal()) {
+ // We cannot mark this class abstract, as it is final (not supported on Android).
+ // However, this might extend an abstract class and we might have removed the
+ // corresponding methods in this class. This might happen if we only keep this
+ // class around for its constants.
+ // TODO(herhut): Find alternative for abstract final classes.
+ } else {
+ clazz.accessFlags.setAbstract();
+ }
+ }
+ // The class is used and must be kept. Remove the unused fields and methods from
+ // the class.
+ clazz.directMethods = reachableMethods(clazz.directMethods(), clazz);
+ clazz.virtualMethods = reachableMethods(clazz.virtualMethods(), clazz);
+ clazz.instanceFields = reachableFields(clazz.instanceFields());
+ clazz.staticFields = reachableFields(clazz.staticFields());
+ }
+ }
+ return classMap;
+ }
+
+ private <S extends PresortedComparable<S>, T extends KeyedDexItem<S>> int firstUnreachableIndex(
+ T[] items, Set<S> live) {
+ for (int i = 0; i < items.length; i++) {
+ if (!live.contains(items[i].getKey())) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private DexEncodedMethod[] reachableMethods(DexEncodedMethod[] methods, DexClass clazz) {
+ int firstUnreachable = firstUnreachableIndex(methods, appInfo.liveMethods);
+ // Return the original array if all methods are used.
+ if (firstUnreachable == -1) {
+ return methods;
+ }
+ ArrayList<DexEncodedMethod> reachableMethods = new ArrayList<>(methods.length);
+ for (int i = 0; i < firstUnreachable; i++) {
+ reachableMethods.add(methods[i]);
+ }
+ for (int i = firstUnreachable; i < methods.length; i++) {
+ if (appInfo.liveMethods.contains(methods[i].getKey())) {
+ reachableMethods.add(methods[i]);
+ } else if (options.debugKeepRules) {
+ // Keep the method but rewrite its body, if it has one.
+ reachableMethods.add(methods[i].accessFlags.isAbstract()
+ ? methods[i]
+ : methods[i].toMethodThatLogsError(application.dexItemFactory));
+ } else if (appInfo.targetedMethods.contains(methods[i].getKey())) {
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Making method %s abstract.", methods[i].method);
+ }
+ DexEncodedMethod method = methods[i];
+ // Final classes cannot be abstract, so we have to keep the method in that case.
+ // Also some other kinds of methods cannot be abstract, so keep them around.
+ boolean allowAbstract = clazz.accessFlags.isAbstract()
+ && !method.accessFlags.isFinal()
+ && !method.accessFlags.isNative()
+ && !method.accessFlags.isStrict()
+ && !method.accessFlags.isSynchronized();
+ // By construction, private and static methods cannot be reachable but non-live.
+ assert !method.accessFlags.isPrivate() && !method.accessFlags.isStatic();
+ reachableMethods.add(allowAbstract
+ ? methods[i].toAbstractMethod()
+ : methods[i].toEmptyThrowingMethod());
+ } else if (Log.ENABLED) {
+ Log.debug(getClass(), "Removing method %s.", methods[i].method);
+ }
+ }
+ return reachableMethods.toArray(new DexEncodedMethod[reachableMethods.size()]);
+ }
+
+ private DexEncodedField[] reachableFields(DexEncodedField[] fields) {
+ int firstUnreachable = firstUnreachableIndex(fields, appInfo.liveFields);
+ if (firstUnreachable == -1) {
+ return fields;
+ }
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Removing field: " + fields[firstUnreachable]);
+ }
+ ArrayList<DexEncodedField> reachableFields = new ArrayList<>(fields.length);
+ for (int i = 0; i < firstUnreachable; i++) {
+ reachableFields.add(fields[i]);
+ }
+ for (int i = firstUnreachable + 1; i < fields.length; i++) {
+ if (appInfo.liveFields.contains(fields[i].getKey())) {
+ reachableFields.add(fields[i]);
+ } else if (Log.ENABLED) {
+ Log.debug(getClass(), "Removing field: " + fields[i]);
+ }
+ }
+ return reachableFields.toArray(new DexEncodedField[reachableFields.size()]);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
new file mode 100644
index 0000000..53680ee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -0,0 +1,580 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.isArchive;
+import static com.android.tools.r8.utils.FileUtils.isClassFile;
+import static com.android.tools.r8.utils.FileUtils.isDexFile;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.CopyOption;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Collection of program files needed for processing.
+ *
+ * <p>This abstraction is the main input and output container for a given application.
+ */
+public class AndroidApp {
+
+ public static final String DEFAULT_PROGUARD_MAP_FILE = "proguard.map";
+ public static final String DEFAULT_PROGUARD_SEEDS_FILE = "proguard.seeds";
+ public static final String DEFAULT_PACKAGE_DISTRIBUTION_FILE = "package.map";
+
+ private final ImmutableList<InternalResource> dexSources;
+ private final ImmutableList<InternalResource> classSources;
+ private final InternalResource proguardMap;
+ private final InternalResource proguardSeeds;
+ private final InternalResource packageDistribution;
+ private final InternalResource mainDexList;
+
+ // See factory methods and AndroidApp.Builder below.
+ private AndroidApp(
+ ImmutableList<InternalResource> dexSources,
+ ImmutableList<InternalResource> classSources,
+ InternalResource proguardMap,
+ InternalResource proguardSeeds,
+ InternalResource packageDistribution,
+ InternalResource mainDexList) {
+ this.dexSources = dexSources;
+ this.classSources = classSources;
+ this.proguardMap = proguardMap;
+ this.proguardSeeds = proguardSeeds;
+ this.packageDistribution = packageDistribution;
+ this.mainDexList = mainDexList;
+ }
+
+ /**
+ * Create a new empty builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Create a new builder initialized with the resources from @code{app}.
+ */
+ public static Builder builder(AndroidApp app) {
+ return new Builder(app);
+ }
+
+ /**
+ * Create an app from program files @code{files}. See also Builder::addProgramFiles.
+ */
+ public static AndroidApp fromProgramFiles(Path... files) throws IOException {
+ return fromProgramFiles(Arrays.asList(files));
+ }
+
+ /**
+ * Create an app from program files @code{files}. See also Builder::addProgramFiles.
+ */
+ public static AndroidApp fromProgramFiles(List<Path> files) throws IOException {
+ return builder().addProgramFiles(files).build();
+ }
+
+ /**
+ * Create an app from files found in @code{directory}. See also Builder::addProgramDirectory.
+ */
+ public static AndroidApp fromProgramDirectory(Path directory) throws IOException {
+ return builder().addProgramDirectory(directory).build();
+ }
+
+ /**
+ * Create an app from dex program data. See also Builder::addDexProgramData.
+ */
+ public static AndroidApp fromDexProgramData(byte[]... data) {
+ return fromDexProgramData(Arrays.asList(data));
+ }
+
+ /**
+ * Create an app from dex program data. See also Builder::addDexProgramData.
+ */
+ public static AndroidApp fromDexProgramData(List<byte[]> data) {
+ return builder().addDexProgramData(data).build();
+ }
+
+ /**
+ * Create an app from Java-bytecode program data. See also Builder::addClassProgramData.
+ */
+ public static AndroidApp fromClassProgramData(byte[]... data) {
+ return fromClassProgramData(Arrays.asList(data));
+ }
+
+ /**
+ * Create an app from Java-bytecode program data. See also Builder::addClassProgramData.
+ */
+ public static AndroidApp fromClassProgramData(List<byte[]> data) {
+ return builder().addClassProgramData(data).build();
+ }
+
+ /** Get input streams for all dex program resources. */
+ public List<InternalResource> getDexProgramResources() {
+ return filter(dexSources, InternalResource.Kind.PROGRAM);
+ }
+
+ /** Get input streams for all Java-bytecode program resources. */
+ public List<InternalResource> getClassProgramResources() {
+ return filter(classSources, InternalResource.Kind.PROGRAM);
+ }
+
+ /** Get input streams for all dex program classpath resources. */
+ public List<InternalResource> getDexClasspathResources() {
+ return filter(dexSources, InternalResource.Kind.CLASSPATH);
+ }
+
+ /** Get input streams for all Java-bytecode classpath resources. */
+ public List<InternalResource> getClassClasspathResources() {
+ return filter(classSources, InternalResource.Kind.CLASSPATH);
+ }
+
+ /** Get input streams for all dex library resources. */
+ public List<InternalResource> getDexLibraryResources() {
+ return filter(dexSources, InternalResource.Kind.LIBRARY);
+ }
+
+ /** Get input streams for all Java-bytecode library resources. */
+ public List<InternalResource> getClassLibraryResources() {
+ return filter(classSources, InternalResource.Kind.LIBRARY);
+ }
+
+ private List<InternalResource> filter(
+ List<InternalResource> resources, InternalResource.Kind kind) {
+ List<InternalResource> out = new ArrayList<>(resources.size());
+ for (InternalResource resource : resources) {
+ if (kind == resource.kind) {
+ out.add(resource);
+ }
+ }
+ return out;
+ }
+
+ /**
+ * True if the proguard-map resource exists.
+ */
+ public boolean hasProguardMap() {
+ return proguardMap != null;
+ }
+
+ /**
+ * Get the input stream of the proguard-map resource if it exists.
+ */
+ public InputStream getProguardMap(Closer closer) throws IOException {
+ return proguardMap == null ? null : proguardMap.getStream(closer);
+ }
+
+ /**
+ * True if the proguard-seeds resource exists.
+ */
+ public boolean hasProguardSeeds() {
+ return proguardSeeds != null;
+ }
+
+ /**
+ * Get the input stream of the proguard-seeds resource if it exists.
+ */
+ public InputStream getProguardSeeds(Closer closer) throws IOException {
+ return proguardSeeds == null ? null : proguardSeeds.getStream(closer);
+ }
+
+ /** True if the package distribution resource exists. */
+ public boolean hasPackageDistribution() {
+ return packageDistribution != null;
+ }
+
+ /** Get the input stream of the package distribution resource if it exists. */
+ public InputStream getPackageDistribution(Closer closer) throws IOException {
+ return packageDistribution == null ? null : packageDistribution.getStream(closer);
+ }
+
+ /**
+ * True if the main dex list resource exists.
+ */
+ public boolean hasMainDexList() {
+ return mainDexList != null;
+ }
+
+ /**
+ * Get the input stream of the main dex list resource if it exists.
+ */
+ public InputStream getMainDexList(Closer closer) throws IOException {
+ return mainDexList == null ? null : mainDexList.getStream(closer);
+ }
+
+ /**
+ * Write the dex program resources and proguard resource to @code{output}.
+ */
+ public void write(Path output) throws IOException {
+ write(output, false);
+ }
+
+ /**
+ * Write the dex program resources and proguard resource to @code{output}.
+ */
+ public void write(Path output, boolean overwrite) throws IOException {
+ if (isArchive(output)) {
+ writeToZip(output);
+ } else {
+ writeToDirectory(output);
+ }
+ }
+
+ /**
+ * Write the dex program resources and proguard resource to @code{directory}.
+ */
+ public void writeToDirectory(Path directory) throws IOException {
+ writeToDirectory(directory, false);
+ }
+
+ /**
+ * Write the dex program resources and proguard resource to @code{directory}.
+ */
+ public void writeToDirectory(Path directory, boolean overwrite) throws IOException {
+ CopyOption[] options = copyOptions(overwrite);
+ try (Closer closer = Closer.create()) {
+ List<InternalResource> dexProgramSources = getDexProgramResources();
+ for (int i = 0; i < dexProgramSources.size(); i++) {
+ Files.copy(dexProgramSources.get(i).getStream(closer),
+ directory.resolve(getDexFileName(i)), options);
+ }
+ writeProguardMap(closer, directory, overwrite);
+ writeProguardSeeds(closer, directory, overwrite);
+ }
+ }
+
+ /**
+ * Write the dex program resources to @code{archive} and the proguard resource as its sibling.
+ */
+ public void writeToZip(Path archive) throws IOException {
+ writeToZip(archive, false);
+ }
+
+ public List<byte[]> writeToMemory() throws IOException {
+ List<byte[]> dex = new ArrayList<>();
+ try (Closer closer = Closer.create()) {
+ List<InternalResource> dexProgramSources = getDexProgramResources();
+ for (int i = 0; i < dexProgramSources.size(); i++) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteStreams.copy(dexProgramSources.get(i).getStream(closer), out);
+ dex.add(out.toByteArray());
+ }
+ // TODO(sgjesse): Add Proguard map and seeds.
+ }
+ return dex;
+ }
+
+ /**
+ * Write the dex program resources to @code{archive} and the proguard resource as its sibling.
+ */
+ public void writeToZip(Path archive, boolean overwrite) throws IOException {
+ OpenOption[] options = openOptions(overwrite);
+ try (Closer closer = Closer.create()) {
+ try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(archive, options))) {
+ List<InternalResource> dexProgramSources = getDexProgramResources();
+ for (int i = 0; i < dexProgramSources.size(); i++) {
+ ZipEntry zipEntry = new ZipEntry(getDexFileName(i));
+ byte[] bytes = ByteStreams.toByteArray(dexProgramSources.get(i).getStream(closer));
+ zipEntry.setSize(bytes.length);
+ out.putNextEntry(zipEntry);
+ out.write(bytes);
+ out.closeEntry();
+ }
+ }
+ // Write the proguard map to the archives containing directory.
+ // TODO(zerny): How do we want to determine the output location for the extra resources?
+ writeProguardMap(closer, archive.getParent(), overwrite);
+ writeProguardSeeds(closer, archive.getParent(), overwrite);
+ }
+ }
+
+ private void writeProguardMap(Closer closer, Path parent, boolean overwrite) throws IOException {
+ InputStream input = getProguardMap(closer);
+ if (input != null) {
+ Files.copy(input, parent.resolve(DEFAULT_PROGUARD_MAP_FILE), copyOptions(overwrite));
+ }
+ }
+
+ public void writeProguardMap(Closer closer, OutputStream out) throws IOException {
+ InputStream input = getProguardMap(closer);
+ assert input != null;
+ out.write(ByteStreams.toByteArray(input));
+ }
+
+ private void writeProguardSeeds(Closer closer, Path parent, boolean overwrite)
+ throws IOException {
+ InputStream input = getProguardSeeds(closer);
+ if (input != null) {
+ Files.copy(input, parent.resolve(DEFAULT_PROGUARD_SEEDS_FILE), copyOptions(overwrite));
+ }
+ }
+
+ private String getDexFileName(int index) {
+ return index == 0 ? "classes.dex" : ("classes" + (index + 1) + ".dex");
+ }
+
+ private OpenOption[] openOptions(boolean overwrite) {
+ return new OpenOption[]{
+ overwrite ? StandardOpenOption.CREATE : StandardOpenOption.CREATE_NEW,
+ StandardOpenOption.TRUNCATE_EXISTING
+ };
+ }
+
+ private CopyOption[] copyOptions(boolean overwrite) {
+ return overwrite
+ ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING}
+ : new CopyOption[]{};
+ }
+
+ /**
+ * Builder interface for constructing an AndroidApp.
+ */
+ public static class Builder {
+
+ private final List<InternalResource> dexSources = new ArrayList<>();
+ private final List<InternalResource> classSources = new ArrayList<>();
+ private InternalResource proguardMap;
+ private InternalResource proguardSeeds;
+ private InternalResource packageDistribution;
+ private InternalResource mainDexList;
+
+ // See AndroidApp::builder().
+ private Builder() {
+ }
+
+ // See AndroidApp::builder(AndroidApp).
+ private Builder(AndroidApp app) {
+ dexSources.addAll(app.dexSources);
+ classSources.addAll(app.classSources);
+ proguardMap = app.proguardMap;
+ proguardSeeds = app.proguardSeeds;
+ packageDistribution = app.packageDistribution;
+ mainDexList = app.mainDexList;
+ }
+
+ /**
+ * Add dex program files and proguard-map file located in @code{directory}.
+ *
+ * <p>The program files included are the top-level files ending in .dex and the proguard-map
+ * file should it exist (see @code{DEFAULT_PROGUARD_MAP_FILE} for its assumed name).
+ *
+ * <p>This method is mostly a utility for reading in the file content produces by some external
+ * tool, eg, dx.
+ *
+ * @param directory Directory containing dex program files and optional proguard-map file.
+ */
+ public Builder addProgramDirectory(Path directory) throws IOException {
+ File[] resources = directory.toFile().listFiles(file -> isDexFile(file.toPath()));
+ for (File source : resources) {
+ addFile(source.toPath(), InternalResource.Kind.PROGRAM);
+ }
+ File mapFile = new File(directory.toFile(), DEFAULT_PROGUARD_MAP_FILE);
+ if (mapFile.exists()) {
+ setProguardMapFile(mapFile.toPath());
+ }
+ return this;
+ }
+
+ /**
+ * Add program file resources.
+ */
+ public Builder addProgramFiles(Path... files) throws IOException {
+ return addProgramFiles(Arrays.asList(files));
+ }
+
+ /** Add program file resources. */
+ public Builder addProgramFiles(Collection<Path> files) throws IOException {
+ for (Path file : files) {
+ addFile(file, InternalResource.Kind.PROGRAM);
+ }
+ return this;
+ }
+
+ /**
+ * Add classpath file resources.
+ */
+ public Builder addClasspathFiles(Path... files) throws IOException {
+ return addClasspathFiles(Arrays.asList(files));
+ }
+
+ /** Add classpath file resources. */
+ public Builder addClasspathFiles(Collection<Path> files) throws IOException {
+ for (Path file : files) {
+ addFile(file, InternalResource.Kind.CLASSPATH);
+ }
+ return this;
+ }
+
+ /**
+ * Add library file resources.
+ */
+ public Builder addLibraryFiles(Path... files) throws IOException {
+ return addLibraryFiles(Arrays.asList(files));
+ }
+
+ /** Add library file resources. */
+ public Builder addLibraryFiles(Collection<Path> files) throws IOException {
+ for (Path file : files) {
+ addFile(file, InternalResource.Kind.LIBRARY);
+ }
+ return this;
+ }
+
+ /**
+ * Add dex program-data.
+ */
+ public Builder addDexProgramData(byte[]... data) {
+ return addDexProgramData(Arrays.asList(data));
+ }
+
+ /** Add dex program-data. */
+ public Builder addDexProgramData(Collection<byte[]> data) {
+ for (byte[] datum : data) {
+ dexSources.add(InternalResource.fromBytes(InternalResource.Kind.PROGRAM, datum));
+ }
+ return this;
+ }
+
+ /**
+ * Add Java-bytecode program data.
+ */
+ public Builder addClassProgramData(byte[]... data) {
+ return addClassProgramData(Arrays.asList(data));
+ }
+
+ /** Add Java-bytecode program data. */
+ public Builder addClassProgramData(Collection<byte[]> data) {
+ for (byte[] datum : data) {
+ classSources.add(InternalResource.fromBytes(InternalResource.Kind.PROGRAM, datum));
+ }
+ return this;
+ }
+
+ /** Set proguard-map file. */
+ public Builder setProguardMapFile(Path file) {
+ proguardMap = file == null ? null : InternalResource.fromFile(null, file);
+ return this;
+ }
+
+ /** Set proguard-map data. */
+ public Builder setProguardMapData(String content) {
+ return setProguardMapData(content == null ? null : content.getBytes(StandardCharsets.UTF_8));
+ }
+
+ /** Set proguard-map data. */
+ public Builder setProguardMapData(byte[] content) {
+ proguardMap = content == null ? null : InternalResource.fromBytes(null, content);
+ return this;
+ }
+
+ /** Set proguard-seeds data. */
+ public Builder setProguardSeedsData(byte[] content) {
+ proguardSeeds = content == null ? null : InternalResource.fromBytes(null, content);
+ return this;
+ }
+
+ /** Set the package-distribution file. */
+ public Builder setPackageDistributionFile(Path file) {
+ packageDistribution = file == null ? null : InternalResource.fromFile(null, file);
+ return this;
+ }
+
+ /** Set the main-dex list file. */
+ public Builder setMainDexListFile(Path file) {
+ mainDexList = file == null ? null : InternalResource.fromFile(null, file);
+ return this;
+ }
+
+ /**
+ * Build final AndroidApp.
+ */
+ public AndroidApp build() {
+ return new AndroidApp(
+ ImmutableList.copyOf(dexSources),
+ ImmutableList.copyOf(classSources),
+ proguardMap,
+ proguardSeeds,
+ packageDistribution,
+ mainDexList);
+ }
+
+ private void addFile(Path file, InternalResource.Kind kind) throws IOException {
+ if (!Files.exists(file)) {
+ throw new FileNotFoundException("Non-existent input file: " + file);
+ }
+ if (isDexFile(file)) {
+ dexSources.add(InternalResource.fromFile(kind, file));
+ } else if (isClassFile(file)) {
+ classSources.add(InternalResource.fromFile(kind, file));
+ } else if (isArchive(file)) {
+ addArchive(file, kind);
+ } else {
+ throw new CompilationError("Unsupported source file type for file: " + file);
+ }
+ }
+
+ private void addArchive(Path archive, InternalResource.Kind kind) throws IOException {
+ assert isArchive(archive);
+ boolean containsDexData = false;
+ boolean containsClassData = false;
+ try (ZipInputStream stream = new ZipInputStream(new FileInputStream(archive.toFile()))) {
+ ZipEntry entry;
+ while ((entry = stream.getNextEntry()) != null) {
+ Path name = Paths.get(entry.getName());
+ if (isDexFile(name)) {
+ containsDexData = true;
+ dexSources.add(InternalResource.fromBytes(kind, ByteStreams.toByteArray(stream)));
+ } else if (isClassFile(name)) {
+ containsClassData = true;
+ classSources.add(InternalResource.fromBytes(
+ kind, ByteStreams.toByteArray(stream), guessTypeDescriptor(name)));
+ }
+ }
+ } catch (ZipException e) {
+ throw new CompilationError(
+ "Zip error while reading '" + archive + "': " + e.getMessage(), e);
+ }
+ if (containsDexData && containsClassData) {
+ throw new CompilationError(
+ "Cannot create android app from an archive '" + archive
+ + "' containing both DEX and Java-bytecode content");
+ }
+ }
+ }
+
+ // Guess class descriptor from location of the class file in the archive.
+ private static String guessTypeDescriptor(Path name) {
+ String fileName = name.toString();
+ assert fileName != null;
+ assert fileName.endsWith(CLASS_EXTENSION);
+ String descriptor = fileName.substring(0, fileName.length() - CLASS_EXTENSION.length());
+ if (descriptor.contains(".")) {
+ throw new CompilationError("Unexpected file name in the jar: " + name);
+ }
+ return 'L' + descriptor + ';';
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/CfgPrinter.java b/src/main/java/com/android/tools/r8/utils/CfgPrinter.java
new file mode 100644
index 0000000..0c0aa66
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/CfgPrinter.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import java.util.Stack;
+
+/**
+ * Printer abstraction for creating flow-graphs for the C1 visualizer.
+ */
+public class CfgPrinter {
+
+ private final StringBuilder builder = new StringBuilder();
+ private final Stack<String> opened = new Stack<>();
+ private final int indentSpacing = 2;
+
+ public int nextUnusedValue = 0;
+
+ public String makeUnusedValue() {
+ return "_" + nextUnusedValue++;
+ }
+
+ public void resetUnusedValue() {
+ nextUnusedValue = 0;
+ }
+
+ public CfgPrinter begin(String title) {
+ print("begin_");
+ append(title).ln();
+ opened.push(title);
+ return this;
+ }
+
+ public CfgPrinter end(String title) {
+ String top = opened.pop();
+ assert title.equals(top);
+ print("end_");
+ append(title).ln();
+ return this;
+ }
+
+ public CfgPrinter print(int i) {
+ printIndent();
+ builder.append(i);
+ return this;
+ }
+
+ public CfgPrinter print(String string) {
+ printIndent();
+ builder.append(string);
+ return this;
+ }
+
+ public CfgPrinter append(int i) {
+ builder.append(i);
+ return this;
+ }
+
+ public CfgPrinter append(String string) {
+ builder.append(string);
+ return this;
+ }
+
+ public CfgPrinter sp() {
+ builder.append(" ");
+ return this;
+ }
+
+ public CfgPrinter ln() {
+ builder.append("\n");
+ return this;
+ }
+
+ private void printIndent() {
+ for (int i = 0; i < opened.size() * indentSpacing; i++) {
+ builder.append(" ");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
new file mode 100644
index 0000000..e038f6a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -0,0 +1,224 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+public class DescriptorUtils {
+
+ public static final char DESCRIPTOR_PACKAGE_SEPARATOR = '/';
+ public static final char JAVA_PACKAGE_SEPARATOR = '.';
+ private static final Map<String, String> typeNameToLetterMap =
+ ImmutableMap.<String, String>builder()
+ .put("void", "V")
+ .put("boolean", "Z")
+ .put("byte", "B")
+ .put("short", "S")
+ .put("char", "C")
+ .put("int", "I")
+ .put("long", "J")
+ .put("float", "F")
+ .put("double", "D")
+ .build();
+
+ private static String internalToDescriptor(String typeName, boolean shorty) {
+ String descriptor = typeNameToLetterMap.get(typeName);
+ if (descriptor != null) {
+ return descriptor;
+ }
+ // Must be some array or object type.
+ if (shorty) {
+ return "L";
+ }
+ if (typeName.endsWith("[]")) {
+ return "[" + internalToDescriptor(typeName.substring(0, typeName.length() - 2), shorty);
+ }
+ // Must be an object type.
+ return "L" + typeName.replace(JAVA_PACKAGE_SEPARATOR, DESCRIPTOR_PACKAGE_SEPARATOR) + ";";
+ }
+
+ /**
+ * Convert a Java type name to a descriptor string.
+ *
+ * @param typeName the java type name
+ * @return the descriptor string
+ */
+ public static String javaTypeToDescriptor(String typeName) {
+ assert typeName.indexOf(DESCRIPTOR_PACKAGE_SEPARATOR) == -1;
+ return internalToDescriptor(typeName, false);
+ }
+
+ /**
+ * Convert a Java type name to a shorty descriptor string.
+ *
+ * @param typeName the java type name
+ * @return the shorty descriptor string
+ */
+ public static String javaTypeToShorty(String typeName) {
+ return internalToDescriptor(typeName, true);
+ }
+
+ /**
+ * Convert a type descriptor to a Java type name.
+ *
+ * @param descriptor type descriptor
+ * @return Java type name
+ */
+ public static String descriptorToJavaType(String descriptor) {
+ return descriptorToJavaType(descriptor, null);
+ }
+
+ /**
+ * Convert a type descriptor to a Java type name. Will also deobfuscate class names if a
+ * class mapper is provided.
+ *
+ * @param descriptor type descriptor
+ * @param classNameMapper class name mapper for mapping obfuscated class names
+ * @return Java type name
+ */
+ public static String descriptorToJavaType(String descriptor, ClassNameMapper classNameMapper) {
+ switch (descriptor.charAt(0)) {
+ case 'L':
+ assert descriptor.charAt(descriptor.length() - 1) == ';';
+ String clazz = descriptor.substring(1, descriptor.length() - 1)
+ .replace(DESCRIPTOR_PACKAGE_SEPARATOR, JAVA_PACKAGE_SEPARATOR);
+ String originalName =
+ classNameMapper == null ? null : classNameMapper.deobfuscateClassName(clazz);
+ if (originalName == null) {
+ // Class was not renamed, reconstruct the java name.
+ return clazz;
+ }
+ return originalName;
+ case '[':
+ return descriptorToJavaType(descriptor.substring(1, descriptor.length()), classNameMapper)
+ + "[]";
+ case 'V':
+ return "void";
+ case 'Z':
+ return "boolean";
+ case 'B':
+ return "byte";
+ case 'S':
+ return "short";
+ case 'C':
+ return "char";
+ case 'I':
+ return "int";
+ case 'J':
+ return "long";
+ case 'F':
+ return "float";
+ case 'D':
+ return "double";
+ default:
+ throw new Unreachable("Unknown type " + descriptor);
+ }
+ }
+
+ /**
+ * Get class name from its descriptor.
+ *
+ * @param classDescriptor a class descriptor i.e. "Ljava/lang/Object;"
+ * @return class name i.e. "Object"
+ */
+ public static String getSimpleClassNameFromDescriptor(String classDescriptor) {
+ return getSimpleClassNameFromBinaryName(getClassBinaryNameFromDescriptor(classDescriptor));
+ }
+
+ /**
+ * Get package java name from a class descriptor.
+ *
+ * @param descriptor a class descriptor i.e. "Ljava/lang/Object;"
+ * @return java package name i.e. "java.lang"
+ */
+ public static String getPackageNameFromDescriptor(String descriptor) {
+ return getPackageNameFromBinaryName(getClassBinaryNameFromDescriptor(descriptor));
+ }
+
+ /**
+ * Convert class descriptor to a binary name.
+ *
+ * @param classDescriptor a class descriptor i.e. "Ljava/lang/Object;"
+ * @return class binary name i.e. "java/lang/Object"
+ */
+ public static String getClassBinaryNameFromDescriptor(String classDescriptor) {
+ assert isClassDescriptor(classDescriptor) : "Invalid class descriptor "
+ + classDescriptor;
+ return classDescriptor.substring(1, classDescriptor.length() - 1);
+ }
+
+ /**
+ * Get class name from its binary name.
+ *
+ * @param classBinaryName a class binary name i.e. "java/lang/Object"
+ * @return class name i.e. "Object"
+ */
+ public static String getSimpleClassNameFromBinaryName(String classBinaryName) {
+ int simpleNameIndex = classBinaryName.lastIndexOf(DESCRIPTOR_PACKAGE_SEPARATOR);
+ return (simpleNameIndex < 0) ? classBinaryName : classBinaryName.substring(simpleNameIndex + 1);
+ }
+
+ public static boolean isClassDescriptor(String descriptor) {
+ char[] buffer = descriptor.toCharArray();
+ int length = buffer.length;
+ if (length < 3 || buffer[0] != 'L') {
+ return false;
+ }
+
+ int pos = 1;
+ char ch;
+ do {
+ // First letter of an Ident (an Ident can't be empty)
+ if (pos >= length) {
+ return false;
+ }
+
+ ch = buffer[pos++];
+ if (isInvalidChar(ch) || ch == DESCRIPTOR_PACKAGE_SEPARATOR || ch == ';') {
+ return false;
+ }
+
+ // Next letters of an Ident
+ do {
+ if (pos >= length) {
+ return false;
+ }
+
+ ch = buffer[pos++];
+ if (isInvalidChar(ch)) {
+ return false;
+ }
+ } while (ch != DESCRIPTOR_PACKAGE_SEPARATOR && ch != ';');
+
+ } while (ch != ';');
+
+ return pos == length;
+ }
+
+ /**
+ * Get package java name from a class binary name
+ *
+ * @param classBinaryName a class binary name i.e. "java/lang/Object"
+ * @return java package name i.e. "java.lang"
+ */
+ public static String getPackageNameFromBinaryName(String classBinaryName) {
+ int nameIndex = classBinaryName.lastIndexOf(DESCRIPTOR_PACKAGE_SEPARATOR);
+ return (nameIndex < 0) ? "" : classBinaryName.substring(0, nameIndex)
+ .replace(DESCRIPTOR_PACKAGE_SEPARATOR, JAVA_PACKAGE_SEPARATOR);
+ }
+
+ private static boolean isInvalidChar(char ch) {
+ switch (ch) {
+ case JAVA_PACKAGE_SEPARATOR:
+ case '[':
+ return true;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/EncodedValueUtils.java b/src/main/java/com/android/tools/r8/utils/EncodedValueUtils.java
new file mode 100644
index 0000000..8903525
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/EncodedValueUtils.java
@@ -0,0 +1,141 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.dex.DexFile;
+import com.android.tools.r8.dex.DexOutputBuffer;
+
+public class EncodedValueUtils {
+
+ public static long parseSigned(DexFile file, int numberOfBytes) {
+ assert numberOfBytes > 0;
+ long result = 0;
+ int shift = 0;
+ for (int i = 1; i < numberOfBytes; i++) {
+ result |= ((long) ((file.get() & 0xFF))) << shift;
+ shift += 8;
+ }
+ return result | (file.get() << shift);
+ }
+
+ // Inspired by com.android.dex.EncodedValueCodec
+ public static int putSigned(DexOutputBuffer outputBuffer, long value, int expectedSize) {
+ int bit_size = Long.SIZE + 1 - Long.numberOfLeadingZeros(value ^ (value >> (Long.SIZE - 1)));
+ int size = Math.max((bit_size + Byte.SIZE - 1) / Byte.SIZE, 1);
+ assert size > 0 && size <= expectedSize;
+ for (int i = 0; i < size; i++) {
+ outputBuffer.putByte((byte) value);
+ value >>= Byte.SIZE;
+ }
+ return size;
+ }
+
+ // Inspired by com.android.dex.EncodedValueCodec
+ public static byte[] encodeSigned(long value) {
+ int bit_size = Long.SIZE + 1 - Long.numberOfLeadingZeros(value ^ (value >> (Long.SIZE - 1)));
+ int size = Math.max((bit_size + Byte.SIZE - 1) / Byte.SIZE, 1);
+ byte[] result = new byte[size];
+ for (int i = 0; i < size; i++) {
+ result[i] = (byte) value;
+ value >>= Byte.SIZE;
+ }
+ return result;
+ }
+
+ public static long parseUnsigned(DexFile file, int numberOfBytes) {
+ assert numberOfBytes > 0;
+ long result = 0;
+ int shift = 0;
+ for (int i = 0; i < numberOfBytes; i++) {
+ result |= ((long) (file.get() & 0xFF)) << shift;
+ shift += 8;
+ }
+ return result;
+ }
+
+ // Inspired by com.android.dex.EncodedValueCodec
+ public static int putUnsigned(DexOutputBuffer outputBuffer, long value,
+ int expectedSize) {
+ int bit_size = Long.SIZE - Long.numberOfLeadingZeros(value);
+ int size = Math.max((bit_size + Byte.SIZE - 1) / Byte.SIZE, 1);
+ assert size > 0 && size <= expectedSize;
+ for (int i = 0; i < size; i++) {
+ outputBuffer.putByte((byte) value);
+ value >>= Byte.SIZE;
+ }
+ return size;
+ }
+
+ public static byte[] encodeUnsigned(long value) {
+ int bit_size = Long.SIZE - Long.numberOfLeadingZeros(value);
+ int size = Math.max((bit_size + Byte.SIZE - 1) / Byte.SIZE, 1);
+ byte[] result = new byte[size];
+ for (int i = 0; i < size; i++) {
+ result[i] = (byte) value;
+ value >>= Byte.SIZE;
+ }
+ return result;
+ }
+
+ public static int putBitsFromRightZeroExtended(DexOutputBuffer outputBuffer, long value,
+ int expectedSize) {
+ int bit_size = Long.SIZE - Long.numberOfTrailingZeros(value);
+ int size = (bit_size - 1) / Byte.SIZE + 1;
+ assert size > 0 && size <= expectedSize;
+ value >>= Long.SIZE - (size * Byte.SIZE); // shift trailing zeros.
+ for (int i = 0; i < size; i++) {
+ outputBuffer.putByte((byte) value);
+ value >>= Byte.SIZE;
+ }
+ return size;
+ }
+
+ private static byte[] encodeBitsFromRightZeroExtended(long value) {
+ int bit_size = Long.SIZE - Long.numberOfTrailingZeros(value);
+ int size = (bit_size - 1) / Byte.SIZE + 1;
+ value >>= Long.SIZE - (size * Byte.SIZE); // shift trailing zeros.
+ byte[] result = new byte[size];
+ for (int i = 0; i < size; i++) {
+ result[i] = (byte) value;
+ value >>= Byte.SIZE;
+ }
+ return result;
+ }
+
+ public static float parseFloat(DexFile file, int numberOfBytes) {
+ long bits = parseUnsigned(file, numberOfBytes) << ((Float.BYTES - numberOfBytes) * Byte.SIZE);
+ return Float.intBitsToFloat((int) bits);
+ }
+
+ public static int putFloat(DexOutputBuffer outputBuffer, float value) {
+ long bits = ((long) Float.floatToIntBits(value)) << 32;
+ return EncodedValueUtils.putBitsFromRightZeroExtended(outputBuffer, bits, Float.BYTES);
+ }
+
+ public static byte[] encodeFloat(float value) {
+ long tmp = ((long) Float.floatToIntBits(value)) << 32;
+ byte[] result = EncodedValueUtils.encodeBitsFromRightZeroExtended(tmp);
+ assert result.length <= Float.BYTES;
+ return result;
+ }
+
+ public static double parseDouble(DexFile file, int numberOfBytes) {
+ long bits = parseUnsigned(file, numberOfBytes) << ((Double.BYTES - numberOfBytes) * Byte.SIZE);
+ return Double.longBitsToDouble(bits);
+ }
+
+ public static int putDouble(DexOutputBuffer outputBuffer, double value) {
+ long bits = Double.doubleToLongBits(value);
+ return EncodedValueUtils.putBitsFromRightZeroExtended(outputBuffer, bits, Double.BYTES);
+ }
+
+ public static byte[] encodeDouble(double value) {
+ long tmp = Double.doubleToLongBits(value);
+ byte[] result = EncodedValueUtils.encodeBitsFromRightZeroExtended(tmp);
+ assert result.length <= Double.BYTES;
+ return result;
+ }
+
+}
+
diff --git a/src/main/java/com/android/tools/r8/utils/FileUtils.java b/src/main/java/com/android/tools/r8/utils/FileUtils.java
new file mode 100644
index 0000000..9a2b9f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/FileUtils.java
@@ -0,0 +1,92 @@
+// 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.CompilationException;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FileUtils {
+
+ public static final String APK_EXTENSION = ".apk";
+ public static final String CLASS_EXTENSION = ".class";
+ public static final String DEX_EXTENSION = ".dex";
+ public static final String JAR_EXTENSION = ".jar";
+ public static final String ZIP_EXTENSION = ".zip";
+ public static final String DEFAULT_DEX_FILENAME = "classes.dex";
+
+ public static boolean isDexFile(Path path) {
+ String name = path.getFileName().toString().toLowerCase();
+ return name.endsWith(DEX_EXTENSION);
+ }
+
+ public static boolean isClassFile(Path path) {
+ String name = path.getFileName().toString().toLowerCase();
+ return name.endsWith(CLASS_EXTENSION);
+ }
+
+ public static boolean isJarFile(Path path) {
+ String name = path.getFileName().toString().toLowerCase();
+ return name.endsWith(JAR_EXTENSION);
+ }
+
+ public static boolean isZipFile(Path path) {
+ String name = path.getFileName().toString().toLowerCase();
+ return name.endsWith(ZIP_EXTENSION);
+ }
+
+ public static boolean isApkFile(Path path) {
+ String name = path.getFileName().toString().toLowerCase();
+ return name.endsWith(APK_EXTENSION);
+ }
+
+ public static boolean isArchive(Path path) {
+ String name = path.getFileName().toString().toLowerCase();
+ return name.endsWith(APK_EXTENSION)
+ || name.endsWith(JAR_EXTENSION)
+ || name.endsWith(ZIP_EXTENSION);
+ }
+
+ public static List<String> readTextFile(Path file) throws IOException {
+ try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
+ List<String> result = new ArrayList<>();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ result.add(line);
+ }
+ return result;
+ }
+ }
+
+ public static void writeTextFile(Path file, List<String> lines) throws IOException {
+ try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
+ for (String line : lines) {
+ writer.write(line);
+ writer.write("\n");
+ }
+ }
+ }
+
+ public static Path validateOutputFile(Path path) throws CompilationException {
+ if (path != null) {
+ if (isZipFile(path)) {
+ if (Files.exists(path)) {
+ throw new CompilationException("Cannot write to existing output file: " + path);
+ }
+ } else if (!(Files.exists(path) && Files.isDirectory(path))) {
+ throw new CompilationException(
+ "Invalid output: "
+ + path
+ + "\nOutput must be a non-existing zip archive or an existing directory");
+ }
+ }
+ return path;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/HashMapInt.java b/src/main/java/com/android/tools/r8/utils/HashMapInt.java
new file mode 100644
index 0000000..7e91644
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/HashMapInt.java
@@ -0,0 +1,120 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+// Hash map based on open addressing where keys are Objects and values are basic ints.
+// Provides: put, get, and size.
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Objects;
+
+public class HashMapInt<T> extends SimpleHashMap {
+
+ private Object[] keys;
+ private int[] values;
+
+ public HashMapInt() {
+ super();
+ }
+
+ public HashMapInt(int initialCapacity) {
+ super(initialCapacity);
+ }
+
+ public HashMapInt(int initialCapacity, double loadFactor) {
+ super(initialCapacity, loadFactor);
+ }
+
+ public void put(final T key, final int value) {
+ if (key == null) {
+ throw new RuntimeException("HashMapInt does not support null as key.");
+ }
+ ensureCapacity();
+ basePut(key, value);
+ }
+
+ boolean equals(Object one, Object other) {
+ return one.equals(other);
+ }
+
+ public int get(final T key) {
+ if (key == null) {
+ throw new RuntimeException("HashMapInt does not support null as key.");
+ }
+ int count = 1;
+ int index = firstProbe(key);
+ while (!equals(key, keys[index])) {
+ if (keys[index] == null) {
+ throw new RuntimeException("HashMapInt get only works if key is present.");
+ }
+ index = nextProbe(index, count++);
+ }
+ return values[index];
+ }
+
+ int firstProbe(T key) {
+ return firstProbe(key.hashCode());
+ }
+
+ public boolean containsKey(final T key) {
+ if (key == null) {
+ throw new RuntimeException("HashMapInt does not support null as key.");
+ }
+ int count = 1;
+ for (int index = firstProbe(key); ; index = nextProbe(index, count++)) {
+ Object k = keys[index];
+ if (k == null) {
+ return false;
+ }
+ if (equals(k, key)) {
+ return true;
+ }
+ }
+ }
+
+ private void basePut(final T key, final int value) {
+ int count = 1;
+ int index = firstProbe(key);
+ while ((keys[index] != null) && (keys[index] != key)) {
+ index = nextProbe(index, count++);
+ }
+ if (keys[index] == null) {
+ keys[index] = key;
+ incrementSize();
+ }
+ values[index] = value;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Iterable<Integer> values() {
+ return () -> Arrays.stream(keys).filter(Objects::nonNull).map((key) -> get((T) key)).iterator();
+ }
+
+ @SuppressWarnings("unchecked")
+ public Iterable<T> keys() {
+ return () -> (Iterator<T>) Arrays.stream(keys).filter(Objects::nonNull).iterator();
+ }
+
+ @SuppressWarnings("unchecked")
+ void resize() {
+ final Object[] oldKeys = keys;
+ final int[] oldValues = values;
+ final int oldLength = length();
+ super.resize();
+ // Repopulate.
+ for (int index = 0; index < oldLength; index++) {
+ T key = (T) oldKeys[index];
+ if (key != null) {
+ basePut(key, oldValues[index]);
+ }
+ }
+ }
+
+ void initialize(final int length, final int limit) {
+ super.initialize(length, limit);
+ keys = new Object[length];
+ values = new int[length];
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/IdentityHashMapInt.java b/src/main/java/com/android/tools/r8/utils/IdentityHashMapInt.java
new file mode 100644
index 0000000..66e62ac
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/IdentityHashMapInt.java
@@ -0,0 +1,29 @@
+// 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;
+
+public class IdentityHashMapInt<T> extends HashMapInt<T> {
+
+ public IdentityHashMapInt() {
+ super();
+ }
+
+ public IdentityHashMapInt(int initialCapacity) {
+ super(initialCapacity);
+ }
+
+ public IdentityHashMapInt(int initialCapacity, double loadFactor) {
+ super(initialCapacity, loadFactor);
+ }
+
+ @Override
+ int firstProbe(T key) {
+ return firstProbe(System.identityHashCode(key));
+ }
+
+ @Override
+ boolean equals(Object one, Object other) {
+ return one == other;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/IntHashMap.java b/src/main/java/com/android/tools/r8/utils/IntHashMap.java
new file mode 100644
index 0000000..6a92316
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/IntHashMap.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+// Hash map based on open addressing where keys are basic ints and values are Objects.
+// Provides: put, get, and size.
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.stream.IntStream;
+
+public class IntHashMap<T> extends SimpleHashMap {
+
+ private int[] keys;
+ private Object[] values;
+
+ public IntHashMap() {
+ super();
+ }
+
+ public IntHashMap(int initialCapacity) {
+ super(initialCapacity);
+ }
+
+ public IntHashMap(int initialCapacity, double loadFactor) {
+ super(initialCapacity, loadFactor);
+ }
+
+ public void put(final int key, final T value) {
+ if (value == null) {
+ throw new RuntimeException("IntHashMap does not support null as value.");
+ }
+ ensureCapacity();
+ basePut(key, value);
+ }
+
+ public T get(final int key) {
+ int count = 1;
+ int index = firstProbe(computeHash(key));
+ // Note that unused entries in keys is 0.
+ // That means we only need to check for value != null when key == 0.
+ while ((keys[index] != key) && (values[index] != null)) {
+ index = nextProbe(index, count++);
+ }
+ assert (keys[index] == key) || (values[index] == null);
+ @SuppressWarnings("unchecked")
+ T result = (T) values[index];
+ return result;
+ }
+
+ private void basePut(final int key, final Object value) {
+ assert value != null;
+ int count = 1;
+ int index = firstProbe(computeHash(key));
+ while ((values[index] != null) && (keys[index] != key)) {
+ index = nextProbe(index, count++);
+ }
+ if (values[index] == null) {
+ keys[index] = key;
+ incrementSize();
+ }
+ values[index] = value;
+ assert value.equals(get(key));
+ }
+
+ void resize() {
+ final int[] oldKeys = keys;
+ final Object[] oldValues = values;
+ final int oldLength = length();
+ super.resize();
+ // Repopulate.
+ for (int index = 0; index < oldLength; index++) {
+ Object value = oldValues[index];
+ if (value != null) {
+ basePut(oldKeys[index], value);
+ }
+ }
+ }
+
+ void initialize(final int length, final int limit) {
+ super.initialize(length, limit);
+ keys = new int[length];
+ values = new Object[length];
+ }
+
+ @SuppressWarnings("unchecked")
+ public Iterable<T> values() {
+ return () -> (Iterator<T>) Arrays.stream(values).filter(Objects::nonNull).iterator();
+ }
+
+ public Iterable<Integer> keys() {
+ if (get(0) != null) {
+ return () -> IntStream.concat(IntStream.of(0), Arrays.stream(keys).filter(i -> i != 0))
+ .iterator();
+ }
+ return () -> Arrays.stream(keys).filter(i -> i != 0 || get(i) != null).distinct().iterator();
+ }
+
+ // Thomas Wang, Integer Hash Functions.
+ // http://www.concentric.net/~Ttwang/tech/inthash.htm
+ private static int computeHash(final int key) {
+ int hash = key;
+ hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1;
+ hash = hash ^ (hash >> 12);
+ hash = hash + (hash << 2);
+ hash = hash ^ (hash >> 4);
+ hash = hash * 2057; // hash = (hash + (hash << 3)) + (hash << 11);
+ hash = hash ^ (hash >> 16);
+ return hash & 0x3fffffff;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/IntIntHashMap.java b/src/main/java/com/android/tools/r8/utils/IntIntHashMap.java
new file mode 100644
index 0000000..03d4911
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/IntIntHashMap.java
@@ -0,0 +1,113 @@
+// 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;
+
+// Hash map based on open addressing where keys are positive basic ints and values are basic ints.
+// Provides: put, get, and size.
+
+import java.util.Arrays;
+
+public class IntIntHashMap extends SimpleHashMap {
+
+ private final int EMPTY_KEY = -1;
+
+ private int[] keys;
+ private int[] values;
+
+ public IntIntHashMap() {
+ super();
+ }
+
+ public IntIntHashMap(int initialCapacity) {
+ super(initialCapacity);
+ }
+
+ public IntIntHashMap(int initialCapacity, double loadFactor) {
+ super(initialCapacity, loadFactor);
+ }
+
+ public void put(final int key, final int value) {
+ if (key < 0) {
+ throw new RuntimeException("IntIntHashMap does not support negative ints as key.");
+ }
+ ensureCapacity();
+ basePut(key, value);
+ }
+
+ public int get(final int key) {
+ if (key < 0) {
+ throw new RuntimeException("IntIntHashMap does not support negative ints as key.");
+ }
+ int count = 1;
+ int index = firstProbe(key);
+ while (key != keys[index]) {
+ if (keys[index] == EMPTY_KEY) {
+ throw new RuntimeException("IntIntHashMap get only works if key is present.");
+ }
+ index = nextProbe(index, count++);
+ }
+ return values[index];
+ }
+
+ public boolean containsKey(final int key) {
+ if (key < 0) {
+ throw new RuntimeException("IntIntHashMap does not support negative ints as key.");
+ }
+ int count = 1;
+ for (int index = firstProbe(key); ; index = nextProbe(index, count++)) {
+ int k = keys[index];
+ if (k == EMPTY_KEY) {
+ return false;
+ }
+ if (k == key) {
+ return true;
+ }
+ }
+ }
+
+ private void basePut(final int key, final int value) {
+ int count = 1;
+ int index = firstProbe(key);
+ while ((keys[index] != EMPTY_KEY) && (keys[index] != key)) {
+ index = nextProbe(index, count++);
+ }
+ if (keys[index] != key) {
+ incrementSize();
+ keys[index] = key;
+ }
+ values[index] = value;
+ }
+
+ void resize() {
+ final int[] oldKeys = keys;
+ final int[] oldValues = values;
+ final int oldLength = length();
+ super.resize();
+ // Repopulate.
+ for (int index = 0; index < oldLength; index++) {
+ int key = oldKeys[index];
+ if (key != EMPTY_KEY) {
+ basePut(key, oldValues[index]);
+ }
+ }
+ }
+
+ void initialize(final int length, final int limit) {
+ super.initialize(length, limit);
+ keys = new int[length];
+ Arrays.fill(keys, EMPTY_KEY);
+ values = new int[length];
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public Iterable<Integer> values() {
+ return () -> Arrays.stream(keys).filter(k -> k > EMPTY_KEY).map(this::get).iterator();
+ }
+
+ @SuppressWarnings("unchecked")
+ public Iterable<Integer> keys() {
+ return () -> Arrays.stream(keys).filter(k -> k > EMPTY_KEY).iterator();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
new file mode 100644
index 0000000..d3b80e3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -0,0 +1,260 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.shaking.ProguardTypeMatcher;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.util.List;
+
+public class InternalOptions {
+
+ public final DexItemFactory itemFactory;
+
+ public InternalOptions() {
+ itemFactory = new DexItemFactory();
+ }
+
+ public InternalOptions(DexItemFactory factory) {
+ assert factory != null;
+ itemFactory = factory;
+ }
+
+ public final int NOT_SPECIFIED = -1;
+
+ public boolean printTimes = false;
+ // Skipping optimizations.
+ public boolean skipDebugInfoOpt = false;
+ public boolean skipDebugLineNumberOpt = false;
+
+ public boolean lazyClasspathLoading = false;
+ public boolean lazyLibraryLoading = false;
+
+ // Number of threads to use while processing the dex files.
+ public int numberOfThreads = NOT_SPECIFIED;
+ // Print smali disassembly.
+ public boolean useSmaliSyntax = false;
+ // Verbose output.
+ public boolean verbose = false;
+ // Silencing output.
+ public boolean quiet = false;
+ // Eagerly fill dex files as much as possible.
+ public boolean fillDexFiles = false;
+
+ public List<String> methodsFilter = ImmutableList.of();
+ public int minApiLevel = Constants.DEFAULT_ANDROID_API;
+
+ // Defines interface method rewriter behavior.
+ public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Off;
+
+ public boolean useTreeShaking = true;
+
+ public boolean printCfg = false;
+ public String printCfgFile;
+ public boolean printSeeds;
+ public Path seedsFile;
+ public boolean printMapping;
+ public Path printMappingFile;
+ public boolean ignoreMissingClasses = false;
+ public boolean skipMinification = false;
+ public String packagePrefix = "";
+ public boolean allowAccessModification = true;
+ public boolean inlineAccessors = true;
+ public final OutlineOptions outline = new OutlineOptions();
+ public boolean debugKeepRules = false;
+ public final AttributeRemovalOptions attributeRemoval = new AttributeRemovalOptions();
+
+ public boolean debug = false;
+ public final TestingOptions testing = new TestingOptions();
+
+ // TODO(zerny): These stateful dictionaries do not belong here.
+ public List<String> classObfuscationDictionary = ImmutableList.of();
+ public List<String> obfuscationDictionary = ImmutableList.of();
+
+ public ImmutableList<ProguardConfigurationRule> keepRules = ImmutableList.of();
+ public ImmutableSet<ProguardTypeMatcher> dontWarnPatterns = ImmutableSet.of();
+
+ public String warningInvalidParameterAnnotations = null;
+
+ public boolean printWarnings() {
+ boolean printed = false;
+ if (warningInvalidParameterAnnotations != null) {
+ System.out.println("Warning: " + warningInvalidParameterAnnotations);
+ printed = true;
+ }
+ return printed;
+ }
+
+ public boolean methodMatchesFilter(DexEncodedMethod method) {
+ // Not specifying a filter matches all methods.
+ if (methodsFilter.size() == 0) {
+ return true;
+ }
+ // Currently the filter is simple string equality on the qualified name.
+ String qualifiedName = method.qualifiedName();
+ return methodsFilter.indexOf(qualifiedName) >= 0;
+ }
+
+ public static class OutlineOptions {
+
+ public boolean enabled = true;
+ public static final String className = "r8.GeneratedOutlineSupport";
+ public String methodPrefix = "outline";
+ public int minSize = 3;
+ public int maxSize = 99;
+ public int threshold = 20;
+ }
+
+ public static class TestingOptions {
+
+ public boolean randomizeCallGraphLeaves = false;
+ }
+
+ public static class AttributeRemovalOptions {
+
+ public static final String INNER_CLASSES = "InnerClasses";
+ public static final String ENCLOSING_METHOD = "EnclosingMethod";
+ public static final String SIGNATURE = "Signature";
+ public static final String EXCEPTIONS = "Exceptions";
+ public static final String SOURCE_DEBUG_EXTENSION = "SourceDebugExtension";
+ public static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations";
+ public static final String RUNTIME_INVISBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
+ public static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS =
+ "RuntimeVisibleParameterAnnotations";
+ public static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS =
+ "RuntimeInvisibleParameterAnnotations";
+ public static final String RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations";
+ public static final String RUNTIME_INVISIBLE_TYPE_ANNOTATIONS =
+ "RuntimeInvisibleTypeAnnotations";
+ public static final String ANNOTATION_DEFAULT = "AnnotationDefault";
+
+ public boolean innerClasses = false;
+ public boolean enclosingMethod = false;
+ public boolean signature = false;
+ public boolean exceptions = false;
+ public boolean sourceDebugExtension = false;
+ public boolean runtimeVisibleAnnotations = false;
+ public boolean runtimeInvisibleAnnotations = false;
+ public boolean runtimeVisibleParameterAnnotations = false;
+ public boolean runtimeInvisibleParamterAnnotations = false;
+ public boolean runtimeVisibleTypeAnnotations = false;
+ public boolean runtimeInvisibleTypeAnnotations = false;
+ public boolean annotationDefault = false;
+
+ private AttributeRemovalOptions() {
+
+ }
+
+ public static AttributeRemovalOptions filterOnlySignatures() {
+ AttributeRemovalOptions result = new AttributeRemovalOptions();
+ result.applyPattern("*");
+ result.signature = false;
+ return result;
+ }
+
+ /**
+ * Implements ProGuards attribute matching rules.
+ *
+ * @see <a href="https://www.guardsquare.com/en/proguard/manual/attributes">ProGuard manual</a>.
+ */
+ private boolean update(boolean previous, String text, String[] patterns) {
+ for (String pattern : patterns) {
+ if (previous) {
+ return true;
+ }
+ if (pattern.charAt(0) == '!') {
+ if (matches(pattern, 1, text, 0)) {
+ break;
+ }
+ } else {
+ previous = matches(pattern, 0, text, 0);
+ }
+ }
+ return previous;
+ }
+
+ private boolean matches(String pattern, int patternPos, String text, int textPos) {
+ while (patternPos < pattern.length()) {
+ char next = pattern.charAt(patternPos++);
+ if (next == '*') {
+ while (textPos < text.length()) {
+ if (matches(pattern, patternPos, text, textPos++)) {
+ return true;
+ }
+ }
+ return patternPos >= pattern.length();
+ } else {
+ if (textPos >= text.length() || text.charAt(textPos) != next) {
+ return false;
+ }
+ textPos++;
+ }
+ }
+ return textPos == text.length();
+ }
+
+ public void applyPattern(String pattern) {
+ String[] patterns = pattern.split(",");
+ innerClasses = update(innerClasses, INNER_CLASSES, patterns);
+ enclosingMethod = update(enclosingMethod, ENCLOSING_METHOD, patterns);
+ signature = update(signature, SIGNATURE, patterns);
+ exceptions = update(exceptions, EXCEPTIONS, patterns);
+ sourceDebugExtension = update(sourceDebugExtension, SOURCE_DEBUG_EXTENSION, patterns);
+ runtimeVisibleAnnotations = update(runtimeVisibleAnnotations, RUNTIME_VISIBLE_ANNOTATIONS,
+ patterns);
+ runtimeInvisibleAnnotations = update(runtimeInvisibleAnnotations,
+ RUNTIME_INVISBLE_ANNOTATIONS, patterns);
+ runtimeVisibleParameterAnnotations = update(runtimeVisibleParameterAnnotations,
+ RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, patterns);
+ runtimeInvisibleParamterAnnotations = update(runtimeInvisibleParamterAnnotations,
+ RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS, patterns);
+ runtimeVisibleTypeAnnotations = update(runtimeVisibleTypeAnnotations,
+ RUNTIME_VISIBLE_TYPE_ANNOTATIONS, patterns);
+ runtimeInvisibleTypeAnnotations = update(runtimeInvisibleTypeAnnotations,
+ RUNTIME_INVISIBLE_TYPE_ANNOTATIONS, patterns);
+ annotationDefault = update(annotationDefault, ANNOTATION_DEFAULT, patterns);
+ }
+
+ public void ensureValid(boolean isMinifying) {
+ if (innerClasses && !enclosingMethod) {
+ throw new CompilationError("Attribute InnerClasses implies EnclosingMethod attribute. " +
+ "Check -keepattributes directive.");
+ } else if (!innerClasses && enclosingMethod) {
+ throw new CompilationError("Attribute EnclosingMethod requires InnerClasses attribute. " +
+ "Check -keepattributes directive.");
+ } else if (signature && isMinifying) {
+ // TODO(38188583): Allow this once we can minify signatures.
+ throw new CompilationError("Attribute Signature cannot be kept when minifying. " +
+ "Check -keepattributes directive.");
+ }
+ }
+ }
+
+ public boolean canUseInvokePolymorphic() {
+ return minApiLevel >= Constants.ANDROID_O_API;
+ }
+
+ public boolean canUseInvokeCustom() {
+ return minApiLevel >= Constants.ANDROID_O_API;
+ }
+
+ public boolean canUseDefaultAndStaticInterfaceMethods() {
+ return minApiLevel >= Constants.ANDROID_N_API;
+ }
+
+ public boolean canUseObjectsNonNull() {
+ return minApiLevel >= Constants.ANDROID_K_API;
+ }
+
+ public boolean canUsePrivateInterfaceMethods() {
+ return minApiLevel >= Constants.ANDROID_N_API;
+ }
+
+}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalResource.java b/src/main/java/com/android/tools/r8/utils/InternalResource.java
new file mode 100644
index 0000000..14656e6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/InternalResource.java
@@ -0,0 +1,89 @@
+// 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.Resource;
+import com.google.common.io.Closer;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+
+/** Internal resource class that is not intended for use from the outside.
+ *
+ * <p>This is only here to hide the creation and class descriptor methods
+ * from the javadoc for the D8 API. If we decide to expose those methods
+ * later the split between Resource and InternalResource can be removed.
+ */
+public abstract class InternalResource extends Resource {
+
+ private InternalResource(Kind kind) {
+ super(kind);
+ }
+
+ /** Returns class descriptor if known, otherwise returns null. */
+ public abstract String getClassDescriptor();
+
+ /** Create an application resource for a given file and kind. */
+ public static InternalResource fromFile(Kind kind, Path file) {
+ return new FileResource(kind, file);
+ }
+
+ /** Create an application resource for a given content and kind. */
+ public static InternalResource fromBytes(Kind kind, byte[] bytes) {
+ return fromBytes(kind, bytes, null);
+ }
+
+ /** Create an application resource for a given content, kind and type descriptor. */
+ public static InternalResource fromBytes(Kind kind, byte[] bytes, String typeDescriptor) {
+ return new ByteResource(kind, bytes, typeDescriptor);
+ }
+
+ /** File based application resource. */
+ private static class FileResource extends InternalResource {
+ final Path file;
+
+ FileResource(Kind kind, Path file) {
+ super(kind);
+ assert file != null;
+ this.file = file;
+ }
+
+ @Override
+ public String getClassDescriptor() {
+ return null;
+ }
+
+ @Override
+ public InputStream getStream(Closer closer) throws IOException {
+ return closer.register(new FileInputStream(file.toFile()));
+ }
+ }
+
+ /** Byte content based application resource. */
+ private static class ByteResource extends InternalResource {
+ final String classDescriptor;
+ final byte[] bytes;
+
+ ByteResource(Kind kind, byte[] bytes, String classDescriptor) {
+ super(kind);
+ assert bytes != null;
+ this.classDescriptor = classDescriptor;
+ this.bytes = bytes;
+ }
+
+ @Override
+ public String getClassDescriptor() {
+ return classDescriptor;
+ }
+
+ @Override
+ public InputStream getStream(Closer closer) throws IOException {
+ // Note: closing a byte-array input stream is a no-op.
+ return new ByteArrayInputStream(bytes);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/LebUtils.java b/src/main/java/com/android/tools/r8/utils/LebUtils.java
new file mode 100644
index 0000000..d4d3b1f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/LebUtils.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.dex.DexFile;
+import com.android.tools.r8.dex.DexOutputBuffer;
+import java.util.Arrays;
+
+public class LebUtils {
+ private static final int BITS_PER_ENCODED_BYTE = 7;
+ private static final int PAYLOAD_MASK = 0x7f;
+ private static final int MORE_DATA_TAG_BIT = 0x80;
+ private static final int MAX_BYTES_PER_VALUE = 5;
+
+ public static int parseUleb128(DexFile file) {
+ int result = 0;
+ byte b;
+ int shift = 0;
+ do {
+ b = file.get();
+ result |= (b & (byte) PAYLOAD_MASK) << shift;
+ shift += BITS_PER_ENCODED_BYTE;
+ } while ((b & ~(byte) PAYLOAD_MASK) == ~(byte) PAYLOAD_MASK);
+ assert shift <= MAX_BYTES_PER_VALUE * BITS_PER_ENCODED_BYTE; // At most five bytes are used.
+ assert result >= 0; // Ensure the java int didn't overflow.
+ return result;
+ }
+
+ // Inspired by com.android.dex.Leb128.java
+ public static byte[] encodeUleb128(int value) {
+ byte result[] = new byte[MAX_BYTES_PER_VALUE];
+ int remaining = value >>> BITS_PER_ENCODED_BYTE;
+ int bytes = 0;
+ while (remaining != 0) {
+ result[bytes++] = (byte) ((value & PAYLOAD_MASK) | MORE_DATA_TAG_BIT);
+ value = remaining;
+ remaining >>>= BITS_PER_ENCODED_BYTE;
+ }
+ result[bytes++] = (byte) (value & PAYLOAD_MASK);
+ return Arrays.copyOf(result, bytes);
+ }
+
+ // Inspired by com.android.dex.Leb128.java
+ public static void putUleb128(DexOutputBuffer outputBuffer, int value) {
+ int remaining = value >>> BITS_PER_ENCODED_BYTE;
+ while (remaining != 0) {
+ outputBuffer.putByte((byte) ((value & PAYLOAD_MASK) | MORE_DATA_TAG_BIT));
+ value = remaining;
+ remaining >>>= BITS_PER_ENCODED_BYTE;
+ }
+ outputBuffer.putByte((byte) (value & PAYLOAD_MASK));
+ }
+
+ public static int sizeAsUleb128(int value) {
+ return Math
+ .max(1, (Integer.SIZE - Integer.numberOfLeadingZeros(value) + 6) / BITS_PER_ENCODED_BYTE);
+ }
+
+ public static int parseSleb128(DexFile file) {
+ int result = 0;
+ byte b;
+ int shift = 0;
+ do {
+ b = file.get();
+ result |= (b & (byte) PAYLOAD_MASK) << shift;
+ shift += BITS_PER_ENCODED_BYTE;
+ } while ((b & ~(byte) PAYLOAD_MASK) == ~(byte) PAYLOAD_MASK);
+ int mask = 1 << (shift - 1);
+ assert shift <= MAX_BYTES_PER_VALUE * BITS_PER_ENCODED_BYTE; // At most five bytes are used.
+ return (result ^ mask) - mask;
+ }
+
+ // Inspired by com.android.dex.Leb128.java
+ public static byte[] encodeSleb128(int value) {
+ byte result[] = new byte[MAX_BYTES_PER_VALUE];
+ int remaining = value >> BITS_PER_ENCODED_BYTE;
+ boolean hasMore = true;
+ int end = value >= 0 ? 0 : -1;
+ int bytes = 0;
+ while (hasMore) {
+ hasMore = (remaining != end)
+ || ((remaining & 1) != ((value >> 6) & 1));
+ result[bytes++] = (byte) ((value & PAYLOAD_MASK) | (hasMore ? MORE_DATA_TAG_BIT : 0));
+ value = remaining;
+ remaining >>= BITS_PER_ENCODED_BYTE;
+ }
+ return Arrays.copyOf(result, bytes);
+ }
+
+ // Inspired by com.android.dex.Leb128.java
+ public static void putSleb128(DexOutputBuffer outputBuffer, int value) {
+ int remaining = value >> BITS_PER_ENCODED_BYTE;
+ boolean hasMore = true;
+ int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+ while (hasMore) {
+ hasMore = (remaining != end)
+ || ((remaining & 1) != ((value >> 6) & 1));
+ outputBuffer.putByte((byte) ((value & PAYLOAD_MASK) | (hasMore ? MORE_DATA_TAG_BIT : 0)));
+ value = remaining;
+ remaining >>= BITS_PER_ENCODED_BYTE;
+ }
+ }
+
+ public static int sizeAsSleb128(int value) {
+ if (value < 0) {
+ value = ~value;
+ }
+ // Note the + 7 to account for the extra bit on 7-bit boundaries.
+ return (Integer.SIZE - Integer.numberOfLeadingZeros(value) + 7) / BITS_PER_ENCODED_BYTE;
+ }
+
+ public static byte[] encodeUleb128p1(int value) {
+ return encodeUleb128(value + 1);
+ }
+
+ public static byte[][] encodeUleb128p1(int[] values) {
+ byte[][] result = new byte[values.length][];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = encodeUleb128p1(values[i]);
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
new file mode 100644
index 0000000..a2ea508
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Function;
+
+public class ListUtils {
+
+ public static <S, T> List<T> map(Collection<S> list, Function<S, T> fn) {
+ List<T> result = new ArrayList<>(list.size());
+ for (S element : list) {
+ result.add(fn.apply(element));
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/LongInterval.java b/src/main/java/com/android/tools/r8/utils/LongInterval.java
new file mode 100644
index 0000000..9c987fc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/LongInterval.java
@@ -0,0 +1,60 @@
+// 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;
+
+/**
+ * Closed interval of two longs.
+ */
+public class LongInterval {
+
+ private final long min;
+ private final long max;
+
+ public LongInterval(int min, int max) {
+ assert min <= max;
+ this.min = min;
+ this.max = max;
+ }
+
+ public LongInterval(long min, long max) {
+ assert min <= max;
+ this.min = min;
+ this.max = max;
+ }
+
+ public long getMin() {
+ return min;
+ }
+
+ public long getMax() {
+ return max;
+ }
+
+ public boolean isSingleValue() {
+ return min == max;
+ }
+
+ public long getSingleValue() {
+ assert isSingleValue();
+ return min;
+ }
+
+ public boolean containsValue(long value) {
+ return min <= value && value <= max;
+ }
+
+ public boolean doesntOverlapWith(LongInterval other) {
+ return other.max < min || max < other.min;
+ }
+
+ public boolean overlapsWith(LongInterval other) {
+ return other.max >= min && max >= other.min;
+ }
+
+ @Override
+ public String toString() {
+ return "[" + min + ", " + max + "]";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/MainDexList.java b/src/main/java/com/android/tools/r8/utils/MainDexList.java
new file mode 100644
index 0000000..b219314
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/MainDexList.java
@@ -0,0 +1,53 @@
+// 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 static com.android.tools.r8.utils.DescriptorUtils.JAVA_PACKAGE_SEPARATOR;
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.Sets;
+import com.google.common.io.Closer;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Set;
+
+public class MainDexList {
+
+ public static Set<DexType> parse(Path path, DexItemFactory itemFactory) throws IOException {
+ try (Closer closer = Closer.create()) {
+ return parse(closer.register(Files.newInputStream(path)), itemFactory);
+ }
+ }
+
+ public static Set<DexType> parse(InputStream input, DexItemFactory itemFactory) {
+ Set<DexType> result = Sets.newIdentityHashSet();
+ try {
+ BufferedReader file =
+ new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
+ String line;
+ while ((line = file.readLine()) != null) {
+ if (!line.endsWith(CLASS_EXTENSION)) {
+ throw new CompilationError("Illegal main-dex-list entry '" + line + "'.");
+ }
+ String name = line.substring(0, line.length() - CLASS_EXTENSION.length());
+ if (name.contains("" + JAVA_PACKAGE_SEPARATOR)) {
+ throw new CompilationError("Illegal main-dex-list entry '" + line + "'.");
+ }
+ String descriptor = "L" + name + ";";
+ result.add(itemFactory.createType(descriptor));
+ }
+ } catch (IOException e) {
+ throw new CompilationError("Cannot load main-dex-list.");
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/MethodSignatureEquivalence.java b/src/main/java/com/android/tools/r8/utils/MethodSignatureEquivalence.java
new file mode 100644
index 0000000..86ce165
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/MethodSignatureEquivalence.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.google.common.base.Equivalence;
+
+/**
+ * Implements an equivalence on {@link DexEncodedMethod} that does not take the holder into
+ * account.
+ *
+ * <p>Useful when comparing method implementations by their signature only.
+ */
+public class MethodSignatureEquivalence extends Equivalence<DexMethod> {
+
+ private static final MethodSignatureEquivalence THEINSTANCE = new MethodSignatureEquivalence();
+
+ private MethodSignatureEquivalence() {
+ }
+
+ public static MethodSignatureEquivalence get() {
+ return THEINSTANCE;
+ }
+
+ @Override
+ protected boolean doEquivalent(DexMethod a, DexMethod b) {
+ return a.name.equals(b.name) && a.proto.equals(b.proto);
+ }
+
+ @Override
+ protected int doHash(DexMethod method) {
+ return method.name.hashCode() * 31 + method.proto.hashCode();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/NumberUtils.java b/src/main/java/com/android/tools/r8/utils/NumberUtils.java
new file mode 100644
index 0000000..ca5dc22
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/NumberUtils.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.dex.Constants;
+
+public class NumberUtils {
+
+ public static boolean is4Bit(long value) {
+ return Constants.S4BIT_MIN <= value && value <= Constants.S4BIT_MAX;
+ }
+
+ public static boolean is8Bit(long value) {
+ return Byte.MIN_VALUE <= value && value <= Byte.MAX_VALUE;
+ }
+
+ public static boolean negativeIs8Bit(long value) {
+ return Byte.MIN_VALUE <= -value && -value <= Byte.MAX_VALUE;
+ }
+
+ public static boolean is16Bit(long value) {
+ return Short.MIN_VALUE <= value && value <= Short.MAX_VALUE;
+ }
+
+ public static boolean negativeIs16Bit(long value) {
+ return Short.MIN_VALUE <= -value && -value <= Short.MAX_VALUE;
+ }
+
+ public static boolean is32Bit(long value) {
+ return Integer.MIN_VALUE <= value && value <= Integer.MAX_VALUE;
+ }
+
+ public static boolean isIntHigh16Bit(long value) {
+ return (value & ~(0xffff << 16)) == 0;
+ }
+
+ public static boolean isLongHigh16Bit(long value) {
+ return (value & ~(0xffffL << 48)) == 0;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/OffOrAuto.java b/src/main/java/com/android/tools/r8/utils/OffOrAuto.java
new file mode 100644
index 0000000..83d6244
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/OffOrAuto.java
@@ -0,0 +1,34 @@
+// 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 joptsimple.ValueConversionException;
+import joptsimple.ValueConverter;
+
+public enum OffOrAuto {
+ Off, Auto;
+
+ static final ValueConverter<OffOrAuto> CONVERTER = new ValueConverter<OffOrAuto>() {
+ @Override
+ public OffOrAuto convert(String input) {
+ try {
+ input = Character.toUpperCase(input.charAt(0)) + input.substring(1).toLowerCase();
+ return Enum.valueOf(OffOrAuto.class, input);
+ } catch (Exception e) {
+ throw new ValueConversionException("Value must be one of: " + valuePattern());
+ }
+ }
+
+ @Override
+ public Class<OffOrAuto> valueType() {
+ return OffOrAuto.class;
+ }
+
+ @Override
+ public String valuePattern() {
+ return "off|auto";
+ }
+ };
+}
diff --git a/src/main/java/com/android/tools/r8/utils/OrderedMergingIterator.java b/src/main/java/com/android/tools/r8/utils/OrderedMergingIterator.java
new file mode 100644
index 0000000..dd4c73a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/OrderedMergingIterator.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.KeyedDexItem;
+import com.android.tools.r8.graph.PresortedComparable;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class OrderedMergingIterator<T extends KeyedDexItem<S>, S extends PresortedComparable<S>>
+ implements Iterator<T> {
+
+ private final T[] one;
+ private final T[] other;
+ private int oneIndex = 0;
+ private int otherIndex = 0;
+
+ public OrderedMergingIterator(T[] one, T[] other) {
+ this.one = one;
+ this.other = other;
+ }
+
+ private static <T> T getNextChecked(T[] array, int position) {
+ if (position >= array.length) {
+ throw new NoSuchElementException();
+ }
+ return array[position];
+ }
+
+ @Override
+ public boolean hasNext() {
+ return oneIndex < one.length || otherIndex < other.length;
+ }
+
+ @Override
+ public T next() {
+ if (oneIndex >= one.length) {
+ return getNextChecked(other, otherIndex++);
+ }
+ if (otherIndex >= other.length) {
+ return getNextChecked(one, oneIndex++);
+ }
+ int comparison = one[oneIndex].getKey().compareTo(other[otherIndex].getKey());
+ if (comparison < 0) {
+ return one[oneIndex++];
+ }
+ if (comparison == 0) {
+ throw new InternalCompilerError("Source arrays are not disjoint.");
+ }
+ return other[otherIndex++];
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/PackageDistribution.java b/src/main/java/com/android/tools/r8/utils/PackageDistribution.java
new file mode 100644
index 0000000..6d36bc3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/PackageDistribution.java
@@ -0,0 +1,171 @@
+// 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 java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class PackageDistribution {
+
+ private static final String OLDFILE_PREFIX_TEXT =
+ "\n"
+ + "# Below follow the original package to file mapping rules. These have not been\n"
+ + "# changed by R8.\n"
+ + "\n";
+
+ private static final String APPENDED_PREFIX_TEXT =
+ "# The following packages had no mapping in the supplied package file. The\n"
+ + "# mapping rules provided below reflect the mapping that was used by R8. Please\n"
+ + "# use this updated map moving forward to ensure stability of package placement\n"
+ + "# in DEX files (and thus minimize patch size).\n"
+ + "#\n"
+ + "# Note that the updated package placement might not be optimal. Shifting the new\n"
+ + "# packages to DEX files that contain related packages might yield smaller DEX\n"
+ + "# file sizes.\n"
+ + "\n";
+
+ private static final String NEW_PACKAGE_MAP_PREFIX_TEXT =
+ "# This file provides a mapping of classes to DEX files in an Android multi-dex\n"
+ +"# application. It is used in conjunction with the R8 DEX file optimizer\n"
+ + "# to enforce a fixed distribution of classes to DEX files.\n"
+ + "#\n"
+ + "# Fixing the class distribution serves two purposes:\n"
+ + "#\n"
+ + "# 1. Keeping classes in the same DEX file reduces the size of patches between\n"
+ + "# two versions of an application.\n"
+ + "# 2. Co-locating classes with their uses can reduce DEX file size. For example,\n"
+ + "# one might want to place the helper classes for credit card processing in\n"
+ + "# the same DEX file that contains the payment related logic.\n"
+ + "#\n"
+ + "# Entries in this file have the following form:\n"
+ + "#\n"
+ + "# <packageSpec>:<file number>\n"
+ + "#\n"
+ + "# Where packageSpec is either the name of a package, e.g., 'com.google.foo', or\n"
+ + "# a package wildcard of the form 'com.google.bar.*'. The former matches exactly\n"
+ + "# the classes in the given package, whereas the latter also matches classes in\n"
+ + "# subpackages. PackageSpec entries may not overlap.\n"
+ + "#\n"
+ + "# Empty lines and lines starting with a '#' are ignored.\n"
+ + "\n";
+
+ private static final String NO_PACKAGE_MAP_REQUIRED_TEXT =
+ "\n"
+ + "# Intentionally empty, as the output only has a single DEX file.\n"
+ + "\n";
+
+ private final Map<String, Integer> map;
+
+ private PackageDistribution(Map<String, Integer> map) {
+ this.map = map;
+ }
+
+ public static PackageDistribution load(InputStream input) throws IOException {
+ return read(new BufferedReader(new InputStreamReader(input)));
+ }
+
+ public static PackageDistribution load(Path path) {
+ try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
+ return read(reader);
+ } catch (IOException e) {
+ throw new RuntimeException("Error reading file " + path, e);
+ }
+ }
+
+ private static PackageDistribution read(BufferedReader reader) throws IOException {
+ String line = null;
+ try {
+ Map<String, Integer> result = new HashMap<>();
+ while ((line = reader.readLine()) != null) {
+ if (line.length() == 0 || line.startsWith("#")) {
+ continue;
+ }
+ String[] parts = line.split(":");
+ if (parts.length != 2) {
+ throw new RuntimeException("Error parsing package map line " + line);
+ }
+ String prefix = parts[0];
+ if (result.containsKey(prefix)) {
+ throw new RuntimeException("Prefix is assigned twice: " + prefix);
+ }
+ int file = Integer.parseInt(parts[1]);
+ result.put(prefix, file);
+ }
+ return new PackageDistribution(result);
+ } catch (NumberFormatException e) {
+ throw new RuntimeException("Error parsing package map line " + line, e);
+ }
+ }
+
+ public static void formatEntry(Entry<String, Integer> entry, Writer writer) throws IOException {
+ writer.write(entry.getKey());
+ writer.write(":");
+ writer.write(entry.getValue().toString());
+ }
+
+ public static void writePackageToFileMap(
+ Path target, Map<String, Integer> mappings, PackageDistribution original) throws IOException {
+ BufferedWriter writer = Files.newBufferedWriter(target, StandardCharsets.UTF_8);
+ if (mappings.isEmpty()) {
+ if (original != null) {
+ copyOriginalPackageMap(original, writer);
+ } else {
+ writer.write(NEW_PACKAGE_MAP_PREFIX_TEXT);
+ writer.write(NO_PACKAGE_MAP_REQUIRED_TEXT);
+ }
+ writer.close();
+ return;
+ }
+ if (original == null) {
+ writer.write(NEW_PACKAGE_MAP_PREFIX_TEXT);
+ } else {
+ writer.write(APPENDED_PREFIX_TEXT);
+ }
+ for (Entry<String, Integer> entry : mappings.entrySet()) {
+ formatEntry(entry, writer);
+ writer.newLine();
+ }
+ if (original != null) {
+ // Copy the original
+ writer.write(OLDFILE_PREFIX_TEXT);
+ copyOriginalPackageMap(original, writer);
+ }
+ writer.close();
+ }
+
+ private static void copyOriginalPackageMap(PackageDistribution original, BufferedWriter writer)
+ throws IOException {
+ for (Entry<String, Integer> entry : original.map.entrySet()) {
+ formatEntry(entry, writer);
+ writer.newLine();
+ }
+ }
+
+ public int maxReferencedIndex() {
+ return map.values().stream().max(Integer::compare).orElseGet(() -> 0);
+ }
+
+ public Set<String> getFiles() {
+ return map.keySet();
+ }
+
+ public int get(String file) {
+ return map.getOrDefault(file, -1);
+ }
+
+ public boolean containsFile(String file) {
+ return map.containsKey(file);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/SimpleHashMap.java b/src/main/java/com/android/tools/r8/utils/SimpleHashMap.java
new file mode 100644
index 0000000..05481c5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/SimpleHashMap.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+abstract class SimpleHashMap {
+
+ private int size = 0; // number of elements in this hash map.
+ private int limit; // resize when size reaches limit.
+ private int mask; // length - 1.
+
+ // Constants for capacity.
+ static final int MIN_CAPACITY = 2;
+ static final int DEFAULT_CAPACITY = 50;
+
+ // Constant for loadfactor.
+ static final double MIN_LOAD_FACTOR = 0.2;
+ static final double MAX_LOAD_FACTOR = 0.8;
+ static final double DEFAULT_LOAD_FACTOR = 0.6;
+
+ SimpleHashMap() {
+ this(DEFAULT_CAPACITY);
+ }
+
+ SimpleHashMap(int initialCapacity) {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+ }
+
+ SimpleHashMap(int initialCapacity, double loadFactor) {
+ initialCapacity = Math.max(MIN_CAPACITY, initialCapacity);
+ loadFactor = Math.min(MAX_LOAD_FACTOR, Math.max(MIN_LOAD_FACTOR, loadFactor));
+ final int initialLength = roundToPow2((int) ((double) initialCapacity / loadFactor));
+ initialize(initialLength, (int) ((double) initialLength * loadFactor));
+ }
+
+ public int size() {
+ return size;
+ }
+
+ public String toString() {
+ return this.getClass().getName() + ", " + size + "(length " + length() + ")";
+ }
+
+ int length() {
+ return mask + 1;
+ }
+
+ void initialize(final int length, final int limit) {
+ size = 0;
+ mask = length - 1;
+ this.limit = limit;
+ }
+
+ void resize() {
+ // Double length and limit.
+ initialize(length() << 1, limit << 1);
+ }
+
+ void ensureCapacity() {
+ if (size >= limit) {
+ resize();
+ }
+ }
+
+ void incrementSize() {
+ size++;
+ }
+
+ private int roundToPow2(int number) {
+ number--;
+ number |= number >> 1;
+ number |= number >> 2;
+ number |= number >> 4;
+ number |= number >> 8;
+ number |= number >> 16;
+ return number + 1;
+ }
+
+ int firstProbe(final int hash) {
+ return hash & mask;
+ }
+
+ int nextProbe(final int last, final int index) {
+ return (last + index) & mask;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
new file mode 100644
index 0000000..268228f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -0,0 +1,205 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.errors.Unreachable;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.function.Function;
+
+public class StringUtils {
+
+ private final static char[] IDENTIFIER_LETTERS
+ = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".toCharArray();
+ private final static int NUMBER_OF_LETTERS = IDENTIFIER_LETTERS.length;
+
+ public enum BraceType {
+ PARENS,
+ SQUARE,
+ TUBORG,
+ NONE;
+
+ public String left() {
+ switch (this) {
+ case PARENS: return "(";
+ case SQUARE: return "[";
+ case TUBORG: return "{";
+ case NONE: return "";
+ default: throw new Unreachable("Invalid brace type: " + this);
+ }
+ }
+
+ public String right() {
+ switch (this) {
+ case PARENS: return ")";
+ case SQUARE: return "]";
+ case TUBORG: return "}";
+ case NONE: return "";
+ default: throw new Unreachable("Invalid brace type: " + this);
+ }
+ }
+ }
+
+ public static void appendNonEmpty(StringBuilder builder, String pre, Object item, String post) {
+ if (item == null) {
+ return;
+ }
+ String text = item.toString();
+ if (!text.isEmpty()) {
+ if (pre != null) {
+ builder.append(pre);
+ }
+ builder.append(text);
+ if (post != null) {
+ builder.append(post);
+ }
+ }
+ }
+
+ public static StringBuilder appendIndent(StringBuilder builder, String subject, int indent) {
+ for (int i = 0; i < indent; i++) {
+ builder.append(" ");
+ }
+ builder.append(subject);
+ return builder;
+ }
+
+ public static StringBuilder appendLeftPadded(StringBuilder builder, String subject, int width) {
+ for (int i = subject.length(); i < width; i++) {
+ builder.append(" ");
+ }
+ builder.append(subject);
+ return builder;
+ }
+
+ public static StringBuilder appendRightPadded(StringBuilder builder, String subject, int width) {
+ builder.append(subject);
+ for (int i = subject.length(); i < width; i++) {
+ builder.append(" ");
+ }
+ return builder;
+ }
+
+ public static <T> StringBuilder append(StringBuilder builder, Collection<T> collection) {
+ return append(builder, collection, ", ", BraceType.PARENS);
+ }
+
+ public static <T> StringBuilder append(StringBuilder builder, Collection<T> collection,
+ String seperator, BraceType brace) {
+ builder.append(brace.left());
+ boolean first = true;
+ for (T element : collection) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(seperator);
+ }
+ builder.append(element);
+ }
+ builder.append(brace.right());
+ return builder;
+ }
+
+ public static <T> String join(Collection<T> collection, String separator) {
+ return join(collection, separator, BraceType.NONE);
+ }
+
+ public static String join(String separator, String... strings) {
+ return join(Arrays.asList(strings), separator, BraceType.NONE);
+ }
+
+ public static <T> String join(Collection<T> collection, String separator, BraceType brace) {
+ return join(collection, separator, brace, Object::toString);
+ }
+
+ public static <T> String join(Collection<T> collection, String separator, BraceType brace,
+ Function<T, String> fn) {
+ StringBuilder builder = new StringBuilder();
+ append(builder, ListUtils.map(collection, fn), separator, brace);
+ return builder.toString();
+ }
+
+ public static String zeroPrefix(int i, int width) {
+ return zeroPrefixString(Integer.toString(i), width);
+ }
+
+ private static String zeroPrefixString(String s, int width) {
+ String prefix = "0000000000000000";
+ assert(width <= prefix.length());
+ int prefixLength = width - s.length();
+ if (prefixLength > 0) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(prefix, 0, prefixLength);
+ builder.append(s);
+ return builder.toString();
+ } else {
+ return s;
+ }
+ }
+
+ public static String hexString(int value, int width) {
+ assert(0 <= width && width <= 8);
+ String hex = Integer.toHexString(value);
+ if (value >= 0) {
+ return zeroPrefixString(hex, width);
+ } else {
+ // Negative ints are always formatted as 8 characters.
+ assert(hex.length() == 8);
+ return hex;
+ }
+ }
+
+ public static String hexString(long value, int width) {
+ assert(0 <= width && width <= 16);
+ String hex = Long.toHexString(value);
+ if (value >= 0) {
+ return zeroPrefixString(hex, width);
+ } else {
+ // Negative longs are always formatted as 16 characters.
+ assert(hex.length() == 16);
+ return hex;
+ }
+ }
+
+ public static String computeMD5Hash(String name) {
+ byte[] digest = null;
+ try {
+ MessageDigest m = MessageDigest.getInstance("MD5");
+ m.reset();
+ m.update(name.getBytes());
+ digest = m.digest();
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ return Arrays.toString(digest);
+ }
+
+ public static String numberToIdentifier(char[] prefix, int nameCount, boolean addSemicolon) {
+ // TODO(herhut): Add support for using numbers.
+ int size = addSemicolon ? 1 : 0;
+ int number = nameCount;
+ while (number >= NUMBER_OF_LETTERS) {
+ number /= NUMBER_OF_LETTERS;
+ size++;
+ }
+ size++;
+ char characters[] = Arrays.copyOfRange(prefix, 0, prefix.length + size);
+ number = nameCount;
+
+ int i = prefix.length;
+ while (number >= NUMBER_OF_LETTERS) {
+ characters[i++] = IDENTIFIER_LETTERS[number % NUMBER_OF_LETTERS];
+ number /= NUMBER_OF_LETTERS;
+ }
+ characters[i++] = IDENTIFIER_LETTERS[number - 1];
+ if (addSemicolon) {
+ characters[i++] = ';';
+ }
+ assert i == characters.length;
+
+ return new String(characters);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
new file mode 100644
index 0000000..06a87aa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class ThreadUtils {
+
+ public static <T> List<T> awaitFutures(List<? extends Future<? extends T>> futures)
+ throws ExecutionException {
+ ArrayList<T> result = new ArrayList<>(futures.size());
+ for (Future<? extends T> f : futures) {
+ try {
+ result.add(f.get());
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted while waiting for future.", e);
+ }
+ }
+ return result;
+ }
+
+ public static ExecutorService getExecutorService(int threads) {
+ if (threads == 1) {
+ return Executors.newSingleThreadExecutor();
+ } else {
+ return Executors.newWorkStealingPool(threads);
+ }
+ }
+
+ public static ExecutorService getExecutorService(InternalOptions options) {
+ if (options.numberOfThreads == options.NOT_SPECIFIED) {
+ // This heuristic is based on measurements on a 32 core (hyper-threaded) machine.
+ int threads = Integer.min(Runtime.getRuntime().availableProcessors(), 16) / 2;
+ return Executors.newWorkStealingPool(threads);
+ } else if (options.numberOfThreads == 1) {
+ return Executors.newSingleThreadExecutor();
+ } else {
+ return Executors.newWorkStealingPool(options.numberOfThreads);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/Timing.java b/src/main/java/com/android/tools/r8/utils/Timing.java
new file mode 100644
index 0000000..0fa316e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/Timing.java
@@ -0,0 +1,103 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+// Helper for collecting timing information during execution.
+// Timing t = new Timing("R8");
+// A timing tree is collected by calling the following pair (nesting will create the tree):
+// t.begin("My task);
+// try { ... } finally { t.end(); }
+// or alternatively:
+// t.scope("My task", () -> { ... });
+// Finally a report is printed by:
+// t.report();
+
+import java.util.Stack;
+
+public class Timing {
+
+ private final Stack<Node> stack;
+
+ public Timing(String title) {
+ stack = new Stack<>();
+ stack.push(new Node("Recorded timings for " + title));
+ }
+
+ static class Node {
+ final String title;
+
+ final Stack<Node> sons = new Stack<>();
+ final long start_time;
+ long stop_time;
+
+ Node(String title) {
+ this.title = title;
+ this.start_time = System.nanoTime();
+ this.stop_time = -1;
+ }
+
+ void end() {
+ stop_time = System.nanoTime();
+ assert duration() >= 0;
+ }
+
+ long duration() {
+ return stop_time - start_time;
+ }
+
+ public String toString() {
+ return title + ": " + (duration() / 1000000) + "ms.";
+ }
+
+ public String toString(Node top) {
+ if (this == top) return toString();
+ long percentage = duration() * 100 / top.duration();
+ return toString() + " (" + percentage + "%)";
+ }
+
+ public void report(int depth, Node top) {
+ assert duration() >= 0;
+ if (depth > 0) {
+ for (int i = 0; i < depth; i++) {
+ System.out.print(" ");
+ }
+ System.out.print("- ");
+ }
+ System.out.println(toString(top));
+ sons.forEach(p -> { p.report(depth + 1, top); });
+ }
+ }
+
+
+ public void begin(String title) {
+ Node n = new Node(title);
+ stack.peek().sons.add(n);
+ stack.push(n);
+ }
+
+ public void end() {
+ stack.peek().end(); // record time.
+ stack.pop();
+ }
+
+ public void report() {
+ Node top = stack.peek();
+ top.end();
+ System.out.println();
+ top.report(0, top);
+ }
+
+ public void scope(String title, TimingScope fn) {
+ begin(title);
+ try {
+ fn.apply();
+ } finally {
+ end();
+ }
+ }
+
+ public interface TimingScope {
+ void apply();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
new file mode 100644
index 0000000..59f0702
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -0,0 +1,50 @@
+// 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.google.common.io.ByteStreams;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class ZipUtils {
+
+ public interface OnEntryHandler {
+ void onEntry(ZipEntry entry, ZipInputStream input) throws IOException;
+ }
+
+ public static void iter(String zipFile, OnEntryHandler handler) throws IOException {
+ try (ZipInputStream input = new ZipInputStream(new FileInputStream(zipFile))){
+ ZipEntry entry;
+ while ((entry = input.getNextEntry()) != null) {
+ handler.onEntry(entry, input);
+ }
+ }
+ }
+
+ public static List<File> unzip(String zipFile, File outDirectory) throws IOException {
+ return unzip(zipFile, outDirectory, (entry) -> true);
+ }
+
+ public static List<File> unzip(String zipFile, File outDirectory, Predicate<ZipEntry> filter)
+ throws IOException {
+ final List<File> outFiles = new ArrayList<>();
+ iter(zipFile, (entry, input) -> {
+ String name = entry.getName();
+ if (filter.test(entry)) {
+ File outFile = outDirectory.toPath().resolve(name).toFile();
+ FileOutputStream output = new FileOutputStream(outFile);
+ ByteStreams.copy(input, output);
+ outFiles.add(outFile);
+ }
+ });
+ return outFiles;
+ }
+}
diff --git a/src/test/debugTestResources/Arithmetic.java b/src/test/debugTestResources/Arithmetic.java
new file mode 100644
index 0000000..29ec2f2
--- /dev/null
+++ b/src/test/debugTestResources/Arithmetic.java
@@ -0,0 +1,18 @@
+// 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.
+
+public class Arithmetic {
+
+ public static void main(String[] args) {
+ bitwiseInts(12345, 54321);
+ }
+
+ public static void bitwiseInts(int x, int y) {
+ System.out.println(x & y);
+ System.out.println(x | y);
+ System.out.println(x ^ y);
+ System.out.println(~x);
+ }
+
+}
diff --git a/src/test/debugTestResources/Exceptions.java b/src/test/debugTestResources/Exceptions.java
new file mode 100644
index 0000000..a056c13
--- /dev/null
+++ b/src/test/debugTestResources/Exceptions.java
@@ -0,0 +1,22 @@
+// 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.
+
+public class Exceptions {
+
+ private static void catchException() {
+ try {
+ throwNullPointerException();
+ } catch (NullPointerException e) {
+ System.out.println("Caught NPE");
+ }
+ }
+
+ private static void throwNullPointerException() {
+ throw new NullPointerException();
+ }
+
+ public static void main(String[] args) {
+ catchException();
+ }
+}
diff --git a/src/test/debugTestResources/InnerAccessors.java b/src/test/debugTestResources/InnerAccessors.java
new file mode 100644
index 0000000..8507e79
--- /dev/null
+++ b/src/test/debugTestResources/InnerAccessors.java
@@ -0,0 +1,21 @@
+// 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.
+
+public class InnerAccessors {
+
+ private static void privateMethod() {
+ System.out.println("I'm a private method");
+ }
+
+ static class Inner {
+ public void callPrivateMethodInOuterClass() {
+ privateMethod();
+ }
+ }
+
+ public static void main(String[] args) {
+ new Inner().callPrivateMethodInOuterClass();
+ }
+
+}
diff --git a/src/test/debugTestResources/Locals.java b/src/test/debugTestResources/Locals.java
new file mode 100644
index 0000000..21d08c1
--- /dev/null
+++ b/src/test/debugTestResources/Locals.java
@@ -0,0 +1,45 @@
+// 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.
+
+public class Locals {
+
+ private static void noLocals() {
+ System.out.println("There's no local here");
+ }
+
+ private static void unusedLocals() {
+ int i = Integer.MAX_VALUE;
+ System.out.println("Not using local variable");
+ }
+
+ private static void constantLocals(int p) {
+ int c = 5;
+ int v = c + p;
+ System.out.println("c=" + c + ", v=" + v);
+ }
+
+ private static void zeroLocals() {
+ int i = 0;
+ float f = 0.0f;
+ System.out.println("zeroLocals");
+ }
+
+ private static void noFlowOptimization() {
+ int i = 0;
+ if (i == 0) {
+ System.out.println("i == 0");
+ } else {
+ System.out.println("i != 0");
+ }
+ }
+
+ public static void main(String[] args) {
+ noLocals();
+ unusedLocals();
+ constantLocals(10);
+ zeroLocals();
+ noFlowOptimization();
+ }
+
+}
diff --git a/src/test/debugTestResources/MultipleReturns.java b/src/test/debugTestResources/MultipleReturns.java
new file mode 100644
index 0000000..7d6dc80
--- /dev/null
+++ b/src/test/debugTestResources/MultipleReturns.java
@@ -0,0 +1,23 @@
+// 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.
+
+public class MultipleReturns {
+
+ public static void main(String[] args) {
+ int resultIfTrue = multipleReturns(true);
+ int resultIfFalse = multipleReturns(false);
+ System.out.println("resultIfTrue=" + resultIfTrue);
+ System.out.println("resultIfFalse=" + resultIfFalse);
+ }
+
+ public static int multipleReturns(boolean condition) {
+ if (condition) {
+ return Integer.MAX_VALUE;
+ } else {
+ return Integer.MIN_VALUE;
+ }
+ }
+
+
+}
diff --git a/src/test/examples/annotationremoval/Annotationremoval.java b/src/test/examples/annotationremoval/Annotationremoval.java
new file mode 100644
index 0000000..520c637
--- /dev/null
+++ b/src/test/examples/annotationremoval/Annotationremoval.java
@@ -0,0 +1,12 @@
+// 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 annotationremoval;
+
+public class Annotationremoval {
+
+ public static void main(String... args) {
+ OuterClass instance = new OuterClass();
+ System.out.print(instance.getValueFromInner(123));
+ }
+}
diff --git a/src/test/examples/annotationremoval/OuterClass.java b/src/test/examples/annotationremoval/OuterClass.java
new file mode 100644
index 0000000..d783fc0
--- /dev/null
+++ b/src/test/examples/annotationremoval/OuterClass.java
@@ -0,0 +1,50 @@
+// 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 annotationremoval;
+
+public class OuterClass {
+ public class InnerClass {
+ private int value;
+
+ public InnerClass(int x) {
+ this.value = x;
+ }
+
+ int computeAResult(int y) {
+ int result = 1;
+ for (int i = value; i < y; i++) {
+ result++;
+ if (result == 1) {
+ return result;
+ }
+ }
+ return value * y;
+ }
+ }
+
+ public abstract class MagicClass {
+ public abstract int returnAnInt();
+ }
+
+ public int getValueFromInner(int x) {
+ class LocalMagic extends MagicClass {
+
+ @Override
+ public int returnAnInt() {
+ return 123;
+ }
+ }
+
+ InnerClass inner = new InnerClass(x);
+ MagicClass magic = new MagicClass() {
+
+ @Override
+ public int returnAnInt() {
+ return 124;
+ }
+ };
+ MagicClass localMagic = new LocalMagic();
+ return inner.computeAResult(42) + magic.returnAnInt() + localMagic.returnAnInt();
+ }
+}
diff --git a/src/test/examples/annotationremoval/keep-rules-keep-innerannotation.txt b/src/test/examples/annotationremoval/keep-rules-keep-innerannotation.txt
new file mode 100644
index 0000000..94d5a60
--- /dev/null
+++ b/src/test/examples/annotationremoval/keep-rules-keep-innerannotation.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class annotationremoval.Annotationremoval {
+ public static void main(...);
+}
+
+-keepclassmembers class annotationremoval.OuterClass {
+ public int getValueFromInner(...);
+}
+
+-keepattributes InnerClasses,EnclosingMethod
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/annotationremoval/keep-rules.txt b/src/test/examples/annotationremoval/keep-rules.txt
new file mode 100644
index 0000000..7865eee
--- /dev/null
+++ b/src/test/examples/annotationremoval/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class annotationremoval.Annotationremoval {
+ public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/arithmetic/Arithmetic.java b/src/test/examples/arithmetic/Arithmetic.java
new file mode 100644
index 0000000..315aca4
--- /dev/null
+++ b/src/test/examples/arithmetic/Arithmetic.java
@@ -0,0 +1,166 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'arithmetic.dex' is what is run.
+
+package arithmetic;
+
+import java.util.Arrays;
+
+public class Arithmetic {
+ static void addInts(int[] ints) {
+ int result = 0;
+ for (int i : ints) {
+ result += i;
+ }
+ System.out.println("addInts: " + Arrays.toString(ints) + " = " + result);
+ }
+
+ static void addDoubles(double[] doubles) {
+ double result = 0;
+ for (double d : doubles) {
+ result += d;
+ }
+ System.out.println("addDoubles: " + Arrays.toString(doubles) + " = " + result);
+ }
+
+ static void addLongs(long[] longs) {
+ long result = 0;
+ for (long l : longs) {
+ result += l;
+ }
+ System.out.println("addLongs: " + Arrays.toString(longs) + " = " + result);
+ }
+
+ static void binaryOps() {
+ int i = 0;
+ System.out.println("i values:");
+ i = i + 1;
+ System.out.println(i);
+ i = 1 + i;
+ System.out.println(i);
+ i = i * 4;
+ System.out.println(i);
+ i = i * i;
+ System.out.println(i);
+ i = 4 * i;
+ System.out.println(i);
+ i = i / 4;
+ System.out.println(i);
+ i = i / i;
+ System.out.println(i);
+ i = i % i;
+ System.out.println(i);
+
+ long l = 0;
+ System.out.println("l values:");
+ l = l + 1;
+ System.out.println(l);
+ l = 1 + l;
+ System.out.println(l);
+ l = l * 4;
+ System.out.println(l);
+ l = l * l;
+ System.out.println(l);
+ l = 4 * l;
+ System.out.println(l);
+ l = l / 4;
+ System.out.println(l);
+ l = l / l;
+ System.out.println(l);
+ l = l % l;
+ System.out.println(l);
+
+ double d = 0.0;
+ System.out.println("d values: ");
+ d = d + 1.0;
+ System.out.println(d);
+ d = 1.0 + d;
+ System.out.println(d);
+ d = d * 4.0;
+ System.out.println(d);
+ d = d * d;
+ System.out.println(d);
+ d = 4.0 * d;
+ System.out.println(d);
+ d = d / 4.0;
+ System.out.println(d);
+ d = d / d;
+ System.out.println(d);
+ d = d % d;
+ System.out.println(d);
+
+ float f = 0.0f;
+ System.out.println("f values: ");
+ f = f + 1.0f;
+ System.out.println(f);
+ f = 1.0f + f;
+ System.out.println(f);
+ f = f * 4.0f;
+ System.out.println(f);
+ f = f * f;
+ System.out.println(f);
+ f = 4.0f * f;
+ System.out.println(f);
+ f = f / 4.0f;
+ System.out.println(f);
+ f = f / f;
+ System.out.println(f);
+ f = f % f;
+ System.out.println(f);
+ }
+
+ public static void moreOps() {
+ int a = 42;
+ int b = -a;
+ int shiftLeftA = a << 5;
+ int shiftRightA = a >> 5;
+ int uShiftRightA = -a >>> 5;
+ System.out.println(a + b + shiftLeftA + shiftRightA + uShiftRightA);
+ float c = 42.42f;
+ float d = -c;
+ System.out.println(c + d);
+ double e = 43.43;
+ double f = -e;
+ System.out.println(e + f);
+ long g = 5000000000L;
+ long h = -g;
+ long shiftLeftG = g << 8;
+ long shiftRightG = g >> 8;
+ long uShiftRightG = -g >>> 8;
+ System.out.println(g + h + shiftLeftG + shiftRightG + uShiftRightG);
+ }
+
+ public static void bitwiseInts(int x, int y) {
+ System.out.println(x & y);
+ System.out.println(x | y);
+ System.out.println(x ^ y);
+ System.out.println(~x);
+ }
+
+ public static void bitwiseLongs(long x, long y) {
+ System.out.println(x & y);
+ System.out.println(x | y);
+ System.out.println(x ^ y);
+ System.out.println(~x);
+ }
+
+ public static void main(String[] args) {
+ addInts(new int[] { });
+ addInts(new int[] { 1 });
+ addInts(new int[] { 0, 1, 2, 3 });
+ addDoubles(new double[] { 0.0 });
+ addDoubles(new double[] { 0.0, 1.0, 2.0 });
+ addDoubles(new double[] { 0.0, 1.0, 2.0, 3.0 });
+ long l = 0x0000000100000000L;
+ addLongs(new long[] { });
+ addLongs(new long[] { l });
+ addLongs(new long[] { l, l + 1, l + 2 });
+ binaryOps();
+ moreOps();
+ bitwiseInts(12345, 54321);
+ bitwiseLongs(54321, 12345);
+ }
+}
diff --git a/src/test/examples/arrayaccess/ArrayAccess.java b/src/test/examples/arrayaccess/ArrayAccess.java
new file mode 100644
index 0000000..f73fd91
--- /dev/null
+++ b/src/test/examples/arrayaccess/ArrayAccess.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package arrayaccess;
+
+public class ArrayAccess {
+
+ public static int loadStoreBoolean(int i, boolean b) {
+ boolean[] array = new boolean[i + 2];
+ array[i] = b;
+ array[i + 1] = !array[i];
+ return (array[i] ? 1 : 0) + (array[i + 1] ? 1 : 0);
+ }
+
+ public static int loadStoreByte(int i) {
+ byte[] array = new byte[i + 2];
+ array[i] = 1;
+ array[i + 1] = (byte) (array[i] + (byte) 1);
+ return array[i] + array[i + 1];
+ }
+
+ public static int loadStoreChar(int i) {
+ char[] array = new char[i + 2];
+ array[i] = 1;
+ array[i + 1] = (char) (array[i] + (char) 1);
+ return array[i] + array[i + 1];
+ }
+
+ public static int loadStoreShort(int i) {
+ short[] array = new short[i + 2];
+ array[i] = 1;
+ array[i + 1] = (short) (array[i] + (short) 1);
+ return array[i] + array[i + 1];
+ }
+
+ public static float loadStoreFloat(int i) {
+ float[] array = new float[i + 2];
+ array[i] = 1.0f;
+ array[i + 1] = array[i] + 1.0f;
+ return array[i] + array[i + 1];
+ }
+
+ public static double loadStoreDouble(int i) {
+ double[] array = new double[i + 2];
+ array[i] = 1.0;
+ array[i + 1] = array[i] + 1.0;
+ return array[i] + array[i + 1];
+ }
+
+ public static int loadStoreObject(int i, Object o) {
+ Object[] array = new Object[i + 2];
+ array[i] = o;
+ array[i + 1] = o;
+ return 1 + (array[i].hashCode() - array[i + 1].hashCode());
+ }
+
+ public static int loadStoreArray(int i, Object[] os) {
+ Object[][] array = new Object[i + 2][];
+ array[i] = os;
+ array[i + 1] = os;
+ return array[i].length + array[i + 1].length;
+ }
+
+ public static void main(String[] args) {
+ int i = 0;
+ i += loadStoreBoolean(1, true);
+ i += loadStoreByte(0);
+ i += loadStoreChar(1);
+ i += loadStoreShort(2);
+ i += loadStoreFloat(3);
+ i += loadStoreDouble(4);
+ i += loadStoreObject(1, "foo");
+ i += loadStoreArray(1, new Object[10]);
+ System.out.println("37=" + i);
+ }
+}
\ No newline at end of file
diff --git a/src/test/examples/assumenosideeffects1/Assumenosideeffects.java b/src/test/examples/assumenosideeffects1/Assumenosideeffects.java
new file mode 100644
index 0000000..24b85a0
--- /dev/null
+++ b/src/test/examples/assumenosideeffects1/Assumenosideeffects.java
@@ -0,0 +1,23 @@
+// 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 assumenosideeffects1;
+
+public class Assumenosideeffects {
+
+ public static void main(String[] args) {
+ noSideEffectVoid();
+ noSideEffectInt();
+ }
+
+ @CheckDiscarded
+ public static void noSideEffectVoid() {
+ System.out.println("noSideEffectVoid");
+ }
+
+ @CheckDiscarded
+ public static int noSideEffectInt() {
+ System.out.println("noSideEffectInt");
+ return 0;
+ }
+}
diff --git a/src/test/examples/assumenosideeffects1/CheckDiscarded.java b/src/test/examples/assumenosideeffects1/CheckDiscarded.java
new file mode 100644
index 0000000..c34c95c
--- /dev/null
+++ b/src/test/examples/assumenosideeffects1/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// 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 assumenosideeffects1;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/assumenosideeffects1/keep-rules-discard.txt b/src/test/examples/assumenosideeffects1/keep-rules-discard.txt
new file mode 100644
index 0000000..9c6b6f7
--- /dev/null
+++ b/src/test/examples/assumenosideeffects1/keep-rules-discard.txt
@@ -0,0 +1,23 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects1.Assumenosideeffects {
+ public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects1.Assumenosideeffects {
+ public static void noSideEffectVoid(...);
+ public static int noSideEffectInt(...);
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
+
+# Check that methods has been discarded.
+-checkdiscard class * {
+ @assumenosideeffects1.CheckDiscarded *;
+}
diff --git a/src/test/examples/assumenosideeffects1/keep-rules.txt b/src/test/examples/assumenosideeffects1/keep-rules.txt
new file mode 100644
index 0000000..d82c218
--- /dev/null
+++ b/src/test/examples/assumenosideeffects1/keep-rules.txt
@@ -0,0 +1,18 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects1.Assumenosideeffects {
+ public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects1.Assumenosideeffects {
+ public static void noSideEffectVoid(...);
+ public static int noSideEffectInt(...);
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumenosideeffects2/Assumenosideeffects.java b/src/test/examples/assumenosideeffects2/Assumenosideeffects.java
new file mode 100644
index 0000000..ab49244
--- /dev/null
+++ b/src/test/examples/assumenosideeffects2/Assumenosideeffects.java
@@ -0,0 +1,11 @@
+// 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 assumenosideeffects2;
+
+public class Assumenosideeffects {
+
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+}
diff --git a/src/test/examples/assumenosideeffects2/keep-rules.txt b/src/test/examples/assumenosideeffects2/keep-rules.txt
new file mode 100644
index 0000000..68ad8e8
--- /dev/null
+++ b/src/test/examples/assumenosideeffects2/keep-rules.txt
@@ -0,0 +1,18 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects2.Assumenosideeffects {
+ public static void main(...);
+}
+
+# Mark the println on java.io.PrintStream to have no side effects using a
+# wildcard (non-specific) rule.
+-assumenosideeffects public class java.io.PrintStr* {
+ public void println(...);
+}
+
+# Allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/assumenosideeffects3/Assumenosideeffects.java b/src/test/examples/assumenosideeffects3/Assumenosideeffects.java
new file mode 100644
index 0000000..b7aed82
--- /dev/null
+++ b/src/test/examples/assumenosideeffects3/Assumenosideeffects.java
@@ -0,0 +1,34 @@
+// 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 assumenosideeffects3;
+
+public class Assumenosideeffects {
+
+ public static void main(String[] args) {
+ System.out.println(method0());
+ System.out.println(method1());
+ System.out.println(method0L() + "L");
+ System.out.println(method1L() + "L");
+ }
+
+ @CheckDiscarded
+ public static int method0() {
+ return 0;
+ }
+
+ @CheckDiscarded
+ public static int method1() {
+ return 1;
+ }
+
+ @CheckDiscarded
+ public static long method0L() {
+ return 0;
+ }
+
+ @CheckDiscarded
+ public static long method1L() {
+ return 1;
+ }
+}
diff --git a/src/test/examples/assumenosideeffects3/CheckDiscarded.java b/src/test/examples/assumenosideeffects3/CheckDiscarded.java
new file mode 100644
index 0000000..9880dc6
--- /dev/null
+++ b/src/test/examples/assumenosideeffects3/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// 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 assumenosideeffects3;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/assumenosideeffects3/keep-rules-discard.txt b/src/test/examples/assumenosideeffects3/keep-rules-discard.txt
new file mode 100644
index 0000000..cdd4b86
--- /dev/null
+++ b/src/test/examples/assumenosideeffects3/keep-rules-discard.txt
@@ -0,0 +1,25 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects3.Assumenosideeffects {
+ public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects3.Assumenosideeffects {
+ public static int method0(...) return 1;
+ public static int method1(...) return 0;
+ public static long method0L(...) return 1;
+ public static long method1L(...) return 0;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
+
+# Check that methods has been discarded.
+-checkdiscard class * {
+ @assumenosideeffects3.CheckDiscarded *;
+}
diff --git a/src/test/examples/assumenosideeffects3/keep-rules.txt b/src/test/examples/assumenosideeffects3/keep-rules.txt
new file mode 100644
index 0000000..cb12eb1
--- /dev/null
+++ b/src/test/examples/assumenosideeffects3/keep-rules.txt
@@ -0,0 +1,20 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects3.Assumenosideeffects {
+ public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects3.Assumenosideeffects {
+ public static int method0(...) return 1;
+ public static int method1(...) return 0;
+ public static long method0L(...) return 1;
+ public static long method1L(...) return 0;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumenosideeffects4/Assumenosideeffects.java b/src/test/examples/assumenosideeffects4/Assumenosideeffects.java
new file mode 100644
index 0000000..cfeff6e
--- /dev/null
+++ b/src/test/examples/assumenosideeffects4/Assumenosideeffects.java
@@ -0,0 +1,43 @@
+// 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 assumenosideeffects4;
+
+public class Assumenosideeffects {
+
+ public static final int ASSUMED_VALUE_0 = 0;
+ public static final int ASSUMED_VALUE_1 = 1;
+ public static final long ASSUMED_VALUE_0L = 0;
+ public static final long ASSUMED_VALUE_1L = 1;
+
+ public static void main(String[] args) {
+ System.out.println(method0());
+ System.out.println(method1());
+ System.out.println(method0L() + "L");
+ System.out.println(method1L() + "L");
+ }
+
+ @CheckDiscarded
+ public static int method0() {
+ System.out.println("method0");
+ return ASSUMED_VALUE_0;
+ }
+
+ @CheckDiscarded
+ public static int method1() {
+ System.out.println("method1");
+ return ASSUMED_VALUE_1;
+ }
+
+ @CheckDiscarded
+ public static long method0L() {
+ System.out.println("method0L");
+ return ASSUMED_VALUE_0L;
+ }
+
+ @CheckDiscarded
+ public static long method1L() {
+ System.out.println("method1L");
+ return ASSUMED_VALUE_1L;
+ }
+}
diff --git a/src/test/examples/assumenosideeffects4/CheckDiscarded.java b/src/test/examples/assumenosideeffects4/CheckDiscarded.java
new file mode 100644
index 0000000..43398ab
--- /dev/null
+++ b/src/test/examples/assumenosideeffects4/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// 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 assumenosideeffects4;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/assumenosideeffects4/keep-rules-discard.txt b/src/test/examples/assumenosideeffects4/keep-rules-discard.txt
new file mode 100644
index 0000000..a6cfcd6
--- /dev/null
+++ b/src/test/examples/assumenosideeffects4/keep-rules-discard.txt
@@ -0,0 +1,29 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects4.Assumenosideeffects {
+ public static final int ASSUMED_VALUE_0;
+ public static final int ASSUMED_VALUE_1;
+ public static final long ASSUMED_VALUE_0L;
+ public static final long ASSUMED_VALUE_1L;
+ public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects4.Assumenosideeffects {
+ public static int method0(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_1;
+ public static int method1(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_0;
+ public static long method0L(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_1L;
+ public static long method1L(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_0L;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
+
+# Check that methods has been discarded.
+-checkdiscard class * {
+ @assumenosideeffects4.CheckDiscarded *;
+}
diff --git a/src/test/examples/assumenosideeffects4/keep-rules.txt b/src/test/examples/assumenosideeffects4/keep-rules.txt
new file mode 100644
index 0000000..1c66b38
--- /dev/null
+++ b/src/test/examples/assumenosideeffects4/keep-rules.txt
@@ -0,0 +1,24 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects4.Assumenosideeffects {
+ public static final int ASSUMED_VALUE_0;
+ public static final int ASSUMED_VALUE_1;
+ public static final long ASSUMED_VALUE_0L;
+ public static final long ASSUMED_VALUE_1L;
+ public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects4.Assumenosideeffects {
+ public static int method0(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_1;
+ public static int method1(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_0;
+ public static long method0L(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_1L;
+ public static long method1L(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_0L;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumenosideeffects5/Assumenosideeffects.java b/src/test/examples/assumenosideeffects5/Assumenosideeffects.java
new file mode 100644
index 0000000..91a1a0d
--- /dev/null
+++ b/src/test/examples/assumenosideeffects5/Assumenosideeffects.java
@@ -0,0 +1,24 @@
+// 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 assumenosideeffects5;
+
+public class Assumenosideeffects {
+
+ public static void main(String[] args) {
+ System.out.println(methodTrue());
+ System.out.println(methodFalse());
+ }
+
+ @CheckDiscarded
+ public static boolean methodTrue() {
+ System.out.println("methodTrue");
+ return true;
+ }
+
+ @CheckDiscarded
+ public static boolean methodFalse() {
+ System.out.println("methodFalse");
+ return false;
+ }
+}
diff --git a/src/test/examples/assumenosideeffects5/CheckDiscarded.java b/src/test/examples/assumenosideeffects5/CheckDiscarded.java
new file mode 100644
index 0000000..1c5f479
--- /dev/null
+++ b/src/test/examples/assumenosideeffects5/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// 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 assumenosideeffects5;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/assumenosideeffects5/keep-rules-discard.txt b/src/test/examples/assumenosideeffects5/keep-rules-discard.txt
new file mode 100644
index 0000000..2383b2c
--- /dev/null
+++ b/src/test/examples/assumenosideeffects5/keep-rules-discard.txt
@@ -0,0 +1,23 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects5.Assumenosideeffects {
+ public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects5.Assumenosideeffects {
+ public static boolean methodTrue(...) return false;
+ public static boolean methodFalse(...) return true;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
+
+# Check that methods has been discarded.
+-checkdiscard class * {
+ @assumenosideeffects5.CheckDiscarded *;
+}
diff --git a/src/test/examples/assumenosideeffects5/keep-rules.txt b/src/test/examples/assumenosideeffects5/keep-rules.txt
new file mode 100644
index 0000000..ac8cb71
--- /dev/null
+++ b/src/test/examples/assumenosideeffects5/keep-rules.txt
@@ -0,0 +1,18 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects5.Assumenosideeffects {
+ public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects5.Assumenosideeffects {
+ public static boolean methodTrue(...) return false;
+ public static boolean methodFalse(...) return true;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumevalues1/Assumevalues.java b/src/test/examples/assumevalues1/Assumevalues.java
new file mode 100644
index 0000000..24e753c
--- /dev/null
+++ b/src/test/examples/assumevalues1/Assumevalues.java
@@ -0,0 +1,34 @@
+// 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 assumevalues1;
+
+public class Assumevalues {
+
+ public static int value = 2;
+ public static long valueL = 2;
+
+ public static void main(String[] args) {
+ value = 3;
+ if (value == 1) {
+ System.out.println("1");
+ }
+ if (value == 2) {
+ System.out.println("2");
+ }
+ if (value == 3) {
+ System.out.println("3");
+ }
+
+ valueL = 3;
+ if (valueL == 1) {
+ System.out.println("1L");
+ }
+ if (valueL == 2) {
+ System.out.println("2L");
+ }
+ if (valueL == 3) {
+ System.out.println("3L");
+ }
+ }
+}
diff --git a/src/test/examples/assumevalues1/keep-rules.txt b/src/test/examples/assumevalues1/keep-rules.txt
new file mode 100644
index 0000000..53f4c15
--- /dev/null
+++ b/src/test/examples/assumevalues1/keep-rules.txt
@@ -0,0 +1,18 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumevalues1.Assumevalues {
+ public static void main(...);
+}
+
+# Assume values for fields.
+-assumevalues public class assumevalues1.Assumevalues {
+ public static int value return 1;
+ public static long valueL return 1;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumevalues2/Assumevalues.java b/src/test/examples/assumevalues2/Assumevalues.java
new file mode 100644
index 0000000..fc44a82
--- /dev/null
+++ b/src/test/examples/assumevalues2/Assumevalues.java
@@ -0,0 +1,82 @@
+// 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 assumevalues2;
+
+public class Assumevalues {
+
+ public static int value = 2;
+ public static long valueL = 2;
+
+ public static void main(String[] args) {
+ value = 1;
+ if (value == 1) {
+ method1();
+ }
+ value = 2;
+ if (value == 2) {
+ method2();
+ }
+ value = 3;
+ if (value == 3) {
+ method3();
+ }
+ value = 4;
+ if (value == 4) {
+ method4();
+ }
+
+ valueL = 1;
+ if (valueL == 1) {
+ method1L();
+ }
+ valueL = 2;
+ if (valueL == 2) {
+ method2L();
+ }
+ valueL = 3;
+ if (valueL == 3) {
+ method3L();
+ }
+ valueL = 4;
+ if (valueL == 4) {
+ method4L();
+ }
+ }
+
+ @CheckDiscarded
+ public static void method1() {
+ System.out.println("1");
+ }
+
+ public static void method2() {
+ System.out.println("2");
+ }
+
+ public static void method3() {
+ System.out.println("3");
+ }
+
+ @CheckDiscarded
+ public static void method4() {
+ System.out.println("4");
+ }
+
+ @CheckDiscarded
+ public static void method1L() {
+ System.out.println("1L");
+ }
+
+ public static void method2L() {
+ System.out.println("2L");
+ }
+
+ public static void method3L() {
+ System.out.println("3L");
+ }
+
+ @CheckDiscarded
+ public static void method4L() {
+ System.out.println("4L");
+ }
+}
diff --git a/src/test/examples/assumevalues2/CheckDiscarded.java b/src/test/examples/assumevalues2/CheckDiscarded.java
new file mode 100644
index 0000000..252ac6b
--- /dev/null
+++ b/src/test/examples/assumevalues2/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// 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 assumevalues2;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/assumevalues2/keep-rules-discard.txt b/src/test/examples/assumevalues2/keep-rules-discard.txt
new file mode 100644
index 0000000..7817d71
--- /dev/null
+++ b/src/test/examples/assumevalues2/keep-rules-discard.txt
@@ -0,0 +1,23 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumevalues2.Assumevalues {
+ public static void main(...);
+}
+
+# Assume values for a field.
+-assumevalues public class assumevalues2.Assumevalues {
+ public static int value return 2..3;
+ public static long valueL return 2..3;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
+
+# Check that methods has been discarded.
+-checkdiscard class * {
+ @assumevalues2.CheckDiscarded *;
+}
diff --git a/src/test/examples/assumevalues2/keep-rules.txt b/src/test/examples/assumevalues2/keep-rules.txt
new file mode 100644
index 0000000..c1fead1
--- /dev/null
+++ b/src/test/examples/assumevalues2/keep-rules.txt
@@ -0,0 +1,18 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumevalues2.Assumevalues {
+ public static void main(...);
+}
+
+# Assume values for fields.
+-assumevalues public class assumevalues2.Assumevalues {
+ public static int value return 2..3;
+ public static long valueL return 2..3;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumevalues3/Assumevalues.java b/src/test/examples/assumevalues3/Assumevalues.java
new file mode 100644
index 0000000..594f1cb
--- /dev/null
+++ b/src/test/examples/assumevalues3/Assumevalues.java
@@ -0,0 +1,37 @@
+// 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 assumevalues3;
+
+public class Assumevalues {
+
+ public static final int ASSUMED_VALUE = 1;
+ public static final long ASSUMED_VALUEL = 1;
+
+ public static int value = 2;
+ public static long valueL = 2;
+
+ public static void main(String[] args) {
+ value = 3;
+ if (value == 1) {
+ System.out.println("1");
+ }
+ if (value == 2) {
+ System.out.println("2");
+ }
+ if (value == 3) {
+ System.out.println("3");
+ }
+
+ valueL = 3;
+ if (valueL == 1) {
+ System.out.println("1L");
+ }
+ if (valueL == 2) {
+ System.out.println("2L");
+ }
+ if (valueL == 3) {
+ System.out.println("3L");
+ }
+ }
+}
diff --git a/src/test/examples/assumevalues3/keep-rules.txt b/src/test/examples/assumevalues3/keep-rules.txt
new file mode 100644
index 0000000..020b564
--- /dev/null
+++ b/src/test/examples/assumevalues3/keep-rules.txt
@@ -0,0 +1,20 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumevalues3.Assumevalues {
+ public static final int ASSUMED_VALUE;
+ public static final long ASSUMED_VALUEL;
+ public static void main(...);
+}
+
+# Assume values for fields.
+-assumevalues public class assumevalues3.Assumevalues {
+ public static int value return assumevalues3.Assumevalues.ASSUMED_VALUE;
+ public static long valueL return assumevalues3.Assumevalues.ASSUMED_VALUEL;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumevalues4/Assumevalues.java b/src/test/examples/assumevalues4/Assumevalues.java
new file mode 100644
index 0000000..f401c81
--- /dev/null
+++ b/src/test/examples/assumevalues4/Assumevalues.java
@@ -0,0 +1,39 @@
+// 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 assumevalues4;
+
+public class Assumevalues {
+
+ public static final int ASSUMED_VALUE_0 = 0;
+ public static final int ASSUMED_VALUE_1 = 1;
+ public static final long ASSUMED_VALUE_0L = 0;
+ public static final long ASSUMED_VALUE_1L = 1;
+
+ public static void main(String[] args) {
+ System.out.println(method0());
+ System.out.println(method1());
+ System.out.println(method0L() + "L");
+ System.out.println(method1L() + "L");
+ }
+
+ public static int method0() {
+ System.out.println("method0");
+ return ASSUMED_VALUE_0;
+ }
+
+ public static int method1() {
+ System.out.println("method1");
+ return ASSUMED_VALUE_1;
+ }
+
+ public static long method0L() {
+ System.out.println("method0L");
+ return ASSUMED_VALUE_0L;
+ }
+
+ public static long method1L() {
+ System.out.println("method1L");
+ return ASSUMED_VALUE_1L;
+ }
+}
diff --git a/src/test/examples/assumevalues4/keep-rules.txt b/src/test/examples/assumevalues4/keep-rules.txt
new file mode 100644
index 0000000..3f6e9f6
--- /dev/null
+++ b/src/test/examples/assumevalues4/keep-rules.txt
@@ -0,0 +1,24 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumevalues4.Assumevalues {
+ public static final int ASSUMED_VALUE_0;
+ public static final int ASSUMED_VALUE_1;
+ public static final long ASSUMED_VALUE_0L;
+ public static final long ASSUMED_VALUE_1L;
+ public static void main(...);
+}
+
+# Mark some methods to have known return value.
+-assumevalues public class assumevalues4.Assumevalues {
+ public static int method0(...) return assumevalues4.Assumevalues.ASSUMED_VALUE_1;
+ public static int method1(...) return assumevalues4.Assumevalues.ASSUMED_VALUE_0;
+ public static long method0L(...) return assumevalues4.Assumevalues.ASSUMED_VALUE_1L;
+ public static long method1L(...) return assumevalues4.Assumevalues.ASSUMED_VALUE_0L;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumevalues5/Assumevalues.java b/src/test/examples/assumevalues5/Assumevalues.java
new file mode 100644
index 0000000..714e144
--- /dev/null
+++ b/src/test/examples/assumevalues5/Assumevalues.java
@@ -0,0 +1,22 @@
+// 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 assumevalues5;
+
+public class Assumevalues {
+
+ public static void main(String[] args) {
+ System.out.println(methodTrue());
+ System.out.println(methodFalse());
+ }
+
+ public static boolean methodTrue() {
+ System.out.println("methodTrue");
+ return true;
+ }
+
+ public static boolean methodFalse() {
+ System.out.println("methodFalse");
+ return false;
+ }
+}
diff --git a/src/test/examples/assumevalues5/keep-rules.txt b/src/test/examples/assumevalues5/keep-rules.txt
new file mode 100644
index 0000000..416aa36
--- /dev/null
+++ b/src/test/examples/assumevalues5/keep-rules.txt
@@ -0,0 +1,18 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumevalues5.Assumevalues {
+ public static void main(...);
+}
+
+# Mark some methods to have known return value.
+-assumevalues public class assumevalues5.Assumevalues {
+ public static boolean methodTrue(...) return false;
+ public static boolean methodFalse(...) return true;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/barray/BArray.java b/src/test/examples/barray/BArray.java
new file mode 100644
index 0000000..0ca6505
--- /dev/null
+++ b/src/test/examples/barray/BArray.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package barray;
+
+public class BArray {
+
+ public static void main(String[] args) {
+ boolean[] boolArray = null;
+ byte[] byteArray = null;
+ boolean bool;
+ byte bits;
+ try {
+ bool = boolArray[0] || boolArray[1];
+ } catch (Throwable e) {
+ bool = true;
+ }
+ try {
+ bits = byteArray[0];
+ } catch (Throwable e) {
+ bits = 42;
+ }
+ System.out.println("bits " + bits + " and bool " + bool);
+ }
+}
diff --git a/src/test/examples/bridge/BridgeMethod.java b/src/test/examples/bridge/BridgeMethod.java
new file mode 100644
index 0000000..9e82a29
--- /dev/null
+++ b/src/test/examples/bridge/BridgeMethod.java
@@ -0,0 +1,36 @@
+// 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 bridge;
+
+abstract class Super<T> {
+ public abstract int method(T t0, T t1);
+ public abstract int rangeMethod(T t0, T t1, T t2, T t3, T t4, T t5);
+}
+
+public class BridgeMethod extends Super<Integer> {
+
+ @Override
+ public int method(Integer t0, Integer t1) {
+ if (t0 > t1) {
+ return t0;
+ }
+ return t1;
+ }
+
+ @Override
+ public int rangeMethod(Integer t0, Integer t1, Integer t2, Integer t3, Integer t4, Integer t5) {
+ if (t0 > t1) {
+ return t0;
+ }
+ return t1 + t2 + t3 + t4 + t5;
+ }
+
+ public static void main(String[] args) {
+ Super<Integer> instance = new BridgeMethod();
+ instance.method(1, 2);
+ instance.method(2, 1);
+ instance.rangeMethod(1, 2, 3, 4, 5, 6);
+ instance.rangeMethod(2, 1, 3, 4, 5, 6);
+ }
+}
diff --git a/src/test/examples/constants/Constants.java b/src/test/examples/constants/Constants.java
new file mode 100644
index 0000000..6349749
--- /dev/null
+++ b/src/test/examples/constants/Constants.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'constants.dex' is what is run.
+package constants;
+
+class Constants {
+
+ public static void printInt(int x) {
+ System.out.print(x);
+ }
+
+ public static void printLong(long x) {
+ System.out.print(x);
+ }
+
+ public static void testConst4() {
+ printInt(-8);
+ printInt(-1);
+ printInt(0);
+ printInt(1);
+ printInt(7);
+ }
+
+ public static void testConst16() {
+ printInt(Short.MIN_VALUE);
+ printInt(-9);
+ printInt(8);
+ printInt(Short.MAX_VALUE);
+ }
+
+ public static void testConstHigh16() {
+ printInt(0xffff0000);
+ printInt(0xf0000000);
+ printInt(0x0f000000);
+ printInt(0x00f00000);
+ printInt(0x000f0000);
+ printInt(0x80000000);
+ printInt(0x00010000);
+ }
+
+ public static void testConst() {
+ printInt(Short.MIN_VALUE - 1);
+ printInt(Short.MAX_VALUE + 1);
+
+ printInt(0xffff0001);
+ printInt(0xf0000001);
+ printInt(0x0f000001);
+ printInt(0x00f00001);
+ printInt(0x000f0001);
+ printInt(0x80000001);
+ printInt(0x00010001);
+ }
+
+ public static void testConstWide16() {
+ printLong((long) Short.MIN_VALUE);
+ printLong(-1L);
+ printLong(0L);
+ printLong(1L);
+ printLong((long) Short.MAX_VALUE);
+ }
+
+ public static void testConstWide32() {
+ printLong((long) Short.MIN_VALUE - 1);
+ printLong((long) Integer.MIN_VALUE);
+ printLong((long) Integer.MAX_VALUE);
+ printLong((long) Short.MAX_VALUE + 1);
+ }
+
+ public static void testConstWideHigh16() {
+ printLong(0xffff000000000000L);
+ printLong(0xf000000000000000L);
+ printLong(0x0f00000000000000L);
+ printLong(0x00f0000000000000L);
+ printLong(0x000f000000000000L);
+ printLong(0x8000000000000000L);
+ printLong(0x0001000000000000L);
+ printLong(0x7fff000000000000L);
+ }
+
+ public static void testConstWide() {
+ printLong((long) Integer.MIN_VALUE - 1);
+ printLong((long) Integer.MAX_VALUE + 1);
+
+ printLong(0xffff7fffffffffffL);
+ printLong(0xffff000000000001L);
+ printLong(0xf000000000000001L);
+ printLong(0x0f00000000000001L);
+ printLong(0x00f0000000000001L);
+ printLong(0x000f000000000001L);
+ printLong(0x8000000000000001L);
+ printLong(0x0001000000000001L);
+ printLong(0x7fffffffffffffffL);
+ printLong(0x7fff000000000001L);
+ }
+
+ public static void main(String[] args) {
+ testConst4();
+ testConst16();
+ testConstHigh16();
+ testConst();
+
+ testConstWide16();
+ testConstWide32();
+ testConstWideHigh16();
+ testConstWide();
+ }
+}
diff --git a/src/test/examples/controlflow/ControlFlow.java b/src/test/examples/controlflow/ControlFlow.java
new file mode 100644
index 0000000..ffdabe0
--- /dev/null
+++ b/src/test/examples/controlflow/ControlFlow.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'controlflow.dex' is what is run.
+
+package controlflow;
+
+public class ControlFlow {
+
+ public static void simpleIf(boolean b) {
+ String s = "Hep!";
+ if (b) {
+ s = "Fisk";
+ } else {
+ s = "Hest";
+ }
+ System.out.println(s);
+ }
+
+ public static void simpleIfMoreValues(boolean b) {
+ int i = 0;
+ double d = 0.0;
+ String s = "Hep!";
+ if (b) {
+ i = 1;
+ d = 1.1;
+ s = "Fisk";
+ b = false;
+ } else {
+ i = 2;
+ d = 2.2;
+ s = "Hest";
+ }
+ if (i == 1) {
+ b = true;
+ }
+ System.out.println(s + " " + i + " " + d + " " + b);
+ }
+
+ public static void simpleIfFallthrough(boolean b) {
+ String s = "Hep!";
+ if (b) {
+ s = "Fisk";
+ }
+ System.out.println(s);
+ }
+
+ public static void sequenceOfIfs(int i) {
+ if (i < 10) {
+ System.out.println("10");
+ }
+ if (i < 5) {
+ System.out.println("5");
+ }
+ if (i < 2) {
+ System.out.println("2");
+ }
+ }
+
+ public static void nestedIfs(int i) {
+ if (i < 10) {
+ System.out.println("10");
+ if (i < 5) {
+ System.out.println("5");
+ if (i < 2) {
+ System.out.println("2");
+ }
+ }
+ }
+ }
+
+ public static void simpleLoop(int count) {
+ System.out.println("simpleLoop");
+ for (int i = 0; i < count; i++) {
+ System.out.println("count: " + i);
+ }
+ }
+
+ public static void main(String[] args) {
+ simpleIf(true);
+ simpleIf(false);
+ simpleIfMoreValues(true);
+ simpleIfMoreValues(false);
+ simpleIfFallthrough(true);
+ simpleIfFallthrough(false);
+ sequenceOfIfs(10);
+ sequenceOfIfs(9);
+ sequenceOfIfs(4);
+ sequenceOfIfs(1);
+ nestedIfs(10);
+ nestedIfs(9);
+ nestedIfs(4);
+ nestedIfs(1);
+ simpleLoop(0);
+ simpleLoop(1);
+ simpleLoop(10);
+ }
+}
diff --git a/src/test/examples/conversions/Conversions.java b/src/test/examples/conversions/Conversions.java
new file mode 100644
index 0000000..456972d
--- /dev/null
+++ b/src/test/examples/conversions/Conversions.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package conversions;
+
+public class Conversions {
+
+ public static byte b(byte b) {
+ return b;
+ }
+
+ public static char c(char c) {
+ return c;
+ }
+
+ public static short s(short s) {
+ return s;
+ }
+
+ public static int i() {
+ return 1;
+ }
+
+ public static int i(int i) {
+ return i;
+ }
+
+ public static long l() {
+ return 1;
+ }
+
+ public static long l(long l) {
+ return l;
+ }
+
+ public static double d() {
+ return 1;
+ }
+
+ public static double d(double d) {
+ return d;
+ }
+
+ public static float f() {
+ return 1;
+ }
+
+ public static float f(float f) {
+ return f;
+ }
+
+ public static void main(String[] args) {
+ // I2L, I2F, I2D
+ System.out.println(l(i()));
+ System.out.println(f(i()));
+ System.out.println(d(i()));
+ // L2I, L2F, L2D
+ System.out.println(i((int) l()));
+ System.out.println(f(l()));
+ System.out.println(d(l()));
+ // F2I, F2L, F2D
+ System.out.println(i((int) f()));
+ System.out.println(l((long) f()));
+ System.out.println(d(f()));
+ // D2I, D2L, D2F
+ System.out.println(i((int) d()));
+ System.out.println(l((long) d()));
+ System.out.println(f((float) d()));
+ // I2B, I2C, I2S
+ System.out.println(b((byte) i()));
+ System.out.println(c((char) i()));
+ System.out.println(s((short) i()));
+ }
+}
diff --git a/src/test/examples/cse/CommonSubexpressionElimination.java b/src/test/examples/cse/CommonSubexpressionElimination.java
new file mode 100644
index 0000000..3c64a1d
--- /dev/null
+++ b/src/test/examples/cse/CommonSubexpressionElimination.java
@@ -0,0 +1,115 @@
+// 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.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'arithmetic.dex' is what is run.
+
+package cse;
+
+public class CommonSubexpressionElimination {
+
+ public static int divNoCatch(int a, int b, int c) {
+ int d = c / (a - b);
+ System.out.println(d);
+ return c / (a - b);
+ }
+
+ public static int divNoCatch2(int a, int b, int c) {
+ int d = c / (a - b);
+ int e = c / (a - b);
+ System.out.println(d + " " + e);
+ return c / (a - b);
+ }
+
+ public static int divCatch(int a, int b, int c) {
+ try {
+ int d = c / (a - b);
+ System.out.println(d);
+ return d;
+ } catch (Throwable t) {
+ return c / (a - b);
+ }
+ }
+
+ public static int divCatch2(int a, int b, int c) {
+ try {
+ int d = c / (a - b);
+ int e = c / (a - b);
+ System.out.println(d + " " + e);
+ return d;
+ } catch (Throwable t) {
+ return c / (a - b);
+ }
+ }
+
+ public static String divCatchCatch(int a, int b, int c) {
+ try {
+ int d = c / (a - b);
+ System.out.println(d);
+ return "X";
+ } catch (Throwable t) {
+ try {
+ return "" + c / (a - b);
+ } catch (Throwable t2) {
+ return "A";
+ }
+ }
+ }
+
+ public static String divCatchSharedCatchHandler(int a, int b, int c) {
+ try {
+ int d = c / (a - b);
+ System.out.println(d);
+ if (a == 0) {
+ int e = c / (a - b);
+ System.out.println(e);
+ } else {
+ int f = c / (a - b);
+ System.out.println(f);
+ }
+ return "X";
+ } catch (Throwable t) {
+ return "B";
+ }
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(divNoCatch(1, 0, 1));
+ System.out.println(divNoCatch2(1, 0, 2));
+ System.out.println(divCatch(1, 0, 3));
+ System.out.println(divCatch2(1, 0, 4));
+
+ System.out.println(divCatchCatch(0, 0, 1));
+ System.out.println(divCatchSharedCatchHandler(0, 0, 1));
+
+ try {
+ divNoCatch(0, 0, 1);
+ throw new RuntimeException("UNEXPECTED");
+ } catch (ArithmeticException e) {
+ // Expected "divide by zero".
+ }
+
+ try {
+ divNoCatch2(0, 0, 1);
+ throw new RuntimeException("UNEXPECTED");
+ } catch (ArithmeticException e) {
+ // Expected "divide by zero".
+ }
+
+ try {
+ divCatch(0, 0, 1);
+ throw new RuntimeException("UNEXPECTED");
+ } catch (ArithmeticException e) {
+ // Expected "divide by zero".
+ }
+
+ try {
+ divCatch2(0, 0, 1);
+ throw new RuntimeException("UNEXPECTED");
+ } catch (ArithmeticException e) {
+ // Expected "divide by zero".
+ }
+ }
+}
diff --git a/src/test/examples/enclosingmethod/AbstractClass.java b/src/test/examples/enclosingmethod/AbstractClass.java
new file mode 100644
index 0000000..90dfe27
--- /dev/null
+++ b/src/test/examples/enclosingmethod/AbstractClass.java
@@ -0,0 +1,8 @@
+// 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 enclosingmethod;
+
+public abstract class AbstractClass {
+ public abstract int anInt();
+}
diff --git a/src/test/examples/enclosingmethod/Main.java b/src/test/examples/enclosingmethod/Main.java
new file mode 100644
index 0000000..c1c0b75
--- /dev/null
+++ b/src/test/examples/enclosingmethod/Main.java
@@ -0,0 +1,15 @@
+// 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 enclosingmethod;
+
+public class Main {
+ public static void main(String... args) {
+ OuterClass anInstance = new OuterClass();
+ anInstance.aMethod();
+ final Class[] classes = OuterClass.class.getDeclaredClasses();
+ for (Class clazz : classes) {
+ System.out.println("InnerClass " + clazz.getName());
+ }
+ }
+}
diff --git a/src/test/examples/enclosingmethod/OuterClass.java b/src/test/examples/enclosingmethod/OuterClass.java
new file mode 100644
index 0000000..d9ab4ba
--- /dev/null
+++ b/src/test/examples/enclosingmethod/OuterClass.java
@@ -0,0 +1,37 @@
+// 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 enclosingmethod;
+
+public class OuterClass {
+ public class AClass {
+
+ }
+
+ public void aMethod() {
+ class AnotherClass extends AbstractClass {
+
+ @Override
+ public int anInt() {
+ return 48;
+ }
+ }
+
+ print(new AbstractClass() {
+ @Override
+ public int anInt() {
+ return 42;
+ }
+ });
+ print(new AnotherClass());
+ }
+
+ private static void print(AbstractClass anInstance) {
+ System.out.println(anInstance.anInt());
+ System.out.println(anInstance.getClass().getEnclosingClass());
+ System.out.println(anInstance.getClass().getEnclosingMethod());
+ System.out.println(anInstance.getClass().isAnonymousClass());
+ System.out.println(anInstance.getClass().isLocalClass());
+ System.out.println(anInstance.getClass().isMemberClass());
+ }
+}
diff --git a/src/test/examples/enclosingmethod/proguard.cfg b/src/test/examples/enclosingmethod/proguard.cfg
new file mode 100644
index 0000000..bc5bd8d
--- /dev/null
+++ b/src/test/examples/enclosingmethod/proguard.cfg
@@ -0,0 +1,10 @@
+# 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.
+
+-keep class enclosingmethod.* {
+ *;
+}
+
+# Keep only the InnerClasses attribute but not the EnclosingMethod one.
+-keepattributes InnerClasses
diff --git a/src/test/examples/filledarray/FilledArray.java b/src/test/examples/filledarray/FilledArray.java
new file mode 100644
index 0000000..04a8f83
--- /dev/null
+++ b/src/test/examples/filledarray/FilledArray.java
@@ -0,0 +1,134 @@
+// 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 filledarray;
+
+import java.util.Arrays;
+
+public class FilledArray {
+ private static boolean[] booleans = new boolean[] { true, true, false, false };
+ private static byte[] bytes = new byte[] {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, -19, -20, -96,
+ Byte.MAX_VALUE, Byte.MIN_VALUE };
+ private static char[] chars = new char[] {
+ Character.MAX_VALUE, 'a', 'b', 'c', 'd', Character.MIN_VALUE };
+ private static int[] ints = new int[] { Integer.MAX_VALUE, 0, -42, 42, Integer.MIN_VALUE };
+ private static short[] shorts = new short[] { Short.MAX_VALUE, 0, -42, 42, Short.MIN_VALUE };
+ private static long[] longs = new long[] {
+ Long.MAX_VALUE, 0x1234123412341234L, -0x1234123412341234L, Long.MIN_VALUE };
+ private static float[] floats = new float[] {
+ Float.MAX_VALUE, 23.23F, -43.123F, Float.MIN_VALUE, Float.MIN_NORMAL };
+ private static double[] doubles = new double[] {
+ Double.MAX_VALUE, 123123123.123, -43333.123, Double.MIN_VALUE, Double.MIN_NORMAL };
+
+
+ public static void filledArrays() {
+ boolean[] localBooleans = new boolean[] { true, true, false, false };
+ localBooleans[0] = false;
+ byte[] localBytes = new byte[] { 21, 22, -23 };
+ char[] localChars = new char[] { 'a', 'b', 'c', 'd' };
+ int[] localInts = new int[] { Integer.MAX_VALUE, 0, -42, 42, Integer.MIN_VALUE };
+ short[] localShorts = new short[] { Short.MAX_VALUE, 0, -42, 42, Short.MIN_VALUE };
+ long[] localLongs= new long[] { 0x1234432112341234L, -0x1234123412344321L };
+ localLongs[1] = localLongs[1] + 2;
+ float[] localFloats = new float[] { 23.23F, -43.123F };
+ double[] localDoubles = new double[] { 123123123.123, -43333.123 };
+ System.out.println("booleans");
+ for (int i = 0; i < booleans.length; i++) {
+ System.out.println(booleans[i]);
+ }
+ for (int i = 0; i < localBooleans.length; i++) {
+ System.out.println(localBooleans[i]);
+ }
+ System.out.println("bytes");
+ for (int i = 0; i < bytes.length; i++) {
+ System.out.println(bytes[i]);
+ }
+ for (int i = 0; i < localBytes.length; i++) {
+ System.out.println(localBytes[i]);
+ }
+ System.out.println("chars");
+ for (int i = 0; i < chars.length; i++) {
+ System.out.println(chars[i]);
+ }
+ for (int i = 0; i < localChars.length; i++) {
+ System.out.println(localChars[i]);
+ }
+ System.out.println("ints");
+ for (int i = 0; i < ints.length; i++) {
+ System.out.println(ints[i]);
+ }
+ for (int i = 0; i < localInts.length; i++) {
+ System.out.println(localInts[i]);
+ }
+ System.out.println("shorts");
+ for (int i = 0; i < shorts.length; i++) {
+ System.out.println(shorts[i]);
+ }
+ for (int i = 0; i < localShorts.length; i++) {
+ System.out.println(localShorts[i]);
+ }
+ System.out.println("longs");
+ for (int i = 0; i < longs.length; i++) {
+ System.out.println(longs[i]);
+ }
+ for (int i = 0; i < localLongs.length; i++) {
+ System.out.println(localLongs[i]);
+ }
+ System.out.println("floats");
+ for (int i = 0; i < floats.length; i++) {
+ System.out.println(floats[i]);
+ }
+ for (int i = 0; i < localFloats.length; i++) {
+ System.out.println(localFloats[i]);
+ }
+ System.out.println("doubles");
+ for (int i = 0; i < doubles.length; i++) {
+ System.out.println(doubles[i]);
+ }
+ for (int i = 0; i < localDoubles.length; i++) {
+ System.out.println(localDoubles[i]);
+ }
+ }
+
+ public static void filledArraysExceptions(int divisor) {
+ try {
+ // Array creations that can be turned into fill-array-data.
+ int[] ints = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ int[] ints2 = new int[5];
+ ints2[0] = 0;
+ ints2[1] = 1;
+ ints2[2] = 2;
+ ints2[3] = 3;
+ ints2[4] = 4;
+ int i = ints[1] / divisor;
+ System.out.println("i = " + i);
+ System.out.println("ints = " + Arrays.toString(ints));
+ System.out.println("ints2 = " + Arrays.toString(ints2));
+ } catch (Throwable t) {
+ t.printStackTrace(System.out);
+ }
+
+ try {
+ // Array creation that cannot be turned into fill-array-data because an exception would
+ // cause the initialization sequence to be interrupted.
+ int[] ints = new int[5];
+ ints[0] = 0;
+ ints[1] = 1;
+ ints[2] = 2;
+ ints[3] = 3;
+ int i = 7 / divisor;
+ ints[4] = 4;
+ System.out.println("i = " + i);
+ System.out.println("ints = " + Arrays.toString(ints));
+ } catch (Throwable t) {
+ t.printStackTrace(System.out);
+ }
+ }
+
+ public static void main(String[] args) {
+ filledArrays();
+ filledArraysExceptions(1);
+ filledArraysExceptions(0);
+ }
+}
diff --git a/src/test/examples/floating_point_annotations/FloatingPointValuedAnnotation.java b/src/test/examples/floating_point_annotations/FloatingPointValuedAnnotation.java
new file mode 100644
index 0000000..5d7fd8c
--- /dev/null
+++ b/src/test/examples/floating_point_annotations/FloatingPointValuedAnnotation.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package floating_point_annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface FloatingPointValuedAnnotation {
+ double doubleValue() default 42.42;
+ float floatValue() default 42.42f;
+}
diff --git a/src/test/examples/floating_point_annotations/FloatingPointValuedAnnotationTest.java b/src/test/examples/floating_point_annotations/FloatingPointValuedAnnotationTest.java
new file mode 100644
index 0000000..1772f23
--- /dev/null
+++ b/src/test/examples/floating_point_annotations/FloatingPointValuedAnnotationTest.java
@@ -0,0 +1,31 @@
+// 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 floating_point_annotations;
+
+public class FloatingPointValuedAnnotationTest {
+
+ @FloatingPointValuedAnnotation(doubleValue = -0d)
+ class A {
+ }
+
+ @FloatingPointValuedAnnotation(doubleValue = 0d)
+ class B {
+ }
+
+ @FloatingPointValuedAnnotation(floatValue = -0f)
+ class C {
+ }
+
+ @FloatingPointValuedAnnotation(floatValue = 0f)
+ class D {
+ }
+
+ public static void main(String[] args) {
+ System.out.println(A.class.getAnnotation(FloatingPointValuedAnnotation.class)
+ .equals(B.class.getAnnotation(FloatingPointValuedAnnotation.class)));
+
+ System.out.println(C.class.getAnnotation(FloatingPointValuedAnnotation.class)
+ .equals(D.class.getAnnotation(FloatingPointValuedAnnotation.class)));
+ }
+}
diff --git a/src/test/examples/hello/Hello.java b/src/test/examples/hello/Hello.java
new file mode 100644
index 0000000..c58f329
--- /dev/null
+++ b/src/test/examples/hello/Hello.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'hello.dex' is what is run.
+
+package hello;
+
+class Hello {
+ public static void main(String[] args) {
+ System.out.println("Hello, world");
+ }
+}
\ No newline at end of file
diff --git a/src/test/examples/ifstatements/IfStatements.java b/src/test/examples/ifstatements/IfStatements.java
new file mode 100644
index 0000000..5d340ab
--- /dev/null
+++ b/src/test/examples/ifstatements/IfStatements.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package ifstatements;
+
+class IfStatements {
+
+ public static void ifNull(Object a) {
+ if (a != null) {
+ System.out.println("sisnotnull");
+ }
+ if (a == null) {
+ System.out.println("sisnull");
+ }
+ }
+
+ public static void ifCond(int x) {
+ if (x == 0) {
+ System.out.println("ifCond x == 0");
+ }
+ if (x != 0) {
+ System.out.println("ifCond x != 0");
+ }
+ if (x < 0) {
+ System.out.println("ifCond x < 0");
+ }
+ if (x >= 0) {
+ System.out.println("ifCond x >= 0");
+ }
+ if (x > 0) {
+ System.out.println("ifCond x > 0");
+ }
+ if (x <= 0) {
+ System.out.println("ifCond x <= 0");
+ }
+ }
+
+ public static void ifIcmp(int x, int y) {
+ if (x == y) {
+ System.out.println("ifIcmp x == y");
+ }
+ if (x != y) {
+ System.out.println("ifIcmp x != y");
+ }
+ if (x < y) {
+ System.out.println("ifIcmp x < y");
+ }
+ if (x >= y) {
+ System.out.println("ifIcmp x >= y");
+ }
+ if (x > y) {
+ System.out.println("ifIcmp x > y");
+ }
+ if (x <= y) {
+ System.out.println("ifIcmp x <= y");
+ }
+ }
+
+ public static void ifAcmp(Object a, Object b) {
+ if (a == b) {
+ System.out.println("ifAcmp a == b");
+ }
+ if (a != b) {
+ System.out.println("ifAcmp a != b");
+ }
+ }
+
+ public static void main(String[] args) {
+ Object a = new Object();
+ ifNull(a);
+ ifNull(null);
+ ifCond(-1);
+ ifCond(0);
+ ifCond(1);
+ ifIcmp(-1, 1);
+ ifIcmp(0, 0);
+ ifIcmp(1, -1);
+ ifAcmp(a, a);
+ ifAcmp(a, null);
+ }
+}
diff --git a/src/test/examples/inlining/CheckDiscarded.java b/src/test/examples/inlining/CheckDiscarded.java
new file mode 100644
index 0000000..8ab68e0
--- /dev/null
+++ b/src/test/examples/inlining/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// 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 inlining;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/inlining/Inlining.java b/src/test/examples/inlining/Inlining.java
new file mode 100644
index 0000000..e013655
--- /dev/null
+++ b/src/test/examples/inlining/Inlining.java
@@ -0,0 +1,263 @@
+// 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 inlining;
+
+class A {
+
+ int a;
+
+ A(int a) {
+ this.a = a;
+ }
+
+ int a() {
+ return a;
+ }
+}
+
+class B extends A {
+
+ B(int a) {
+ super(a);
+ }
+}
+
+class InlineConstructor {
+
+ int a;
+
+ @CheckDiscarded
+ InlineConstructor(int a) {
+ this.a = a;
+ }
+
+ @CheckDiscarded
+ InlineConstructor(long a) {
+ this((int) a);
+ }
+
+ static InlineConstructor create() {
+ return new InlineConstructor(10L);
+ }
+}
+
+class InlineConstructorOfInner {
+
+ class Inner {
+
+ int a;
+
+ @CheckDiscarded
+ Inner(int a) {
+ this.a = a;
+ }
+
+ // This is not inlined, even though it is only called once, as it is only called from a
+ // non-constructor, and will set a field (the outer object) before calling the other
+ // constructor.
+ Inner(long a) {
+ this((int) a);
+ }
+
+ public Inner create() {
+ return new Inner(10L);
+ }
+ }
+
+ Inner inner;
+
+ InlineConstructorOfInner() {
+ inner = new Inner(10L).create();
+ }
+}
+
+public class Inlining {
+
+ private static void Assert(boolean value) {
+ if (!value) {
+ System.out.println("FAILURE");
+ }
+ }
+
+ public static void main(String[] args) {
+ // Ensure the simple methods are called at least three times, to not be inlined due to being
+ // called only once or twice.
+ Assert(intExpression());
+ Assert(intExpression());
+ Assert(intExpression());
+ Assert(longExpression());
+ Assert(longExpression());
+ Assert(longExpression());
+ Assert(doubleExpression());
+ Assert(floatExpression());
+ Assert(floatExpression());
+ Assert(floatExpression());
+ Assert(stringExpression());
+ Assert(stringExpression());
+ Assert(stringExpression());
+
+ Assert(intArgumentExpression());
+ Assert(intArgumentExpression());
+ Assert(intArgumentExpression());
+ Assert(longArgumentExpression());
+ Assert(longArgumentExpression());
+ Assert(longArgumentExpression());
+ Assert(doubleArgumentExpression());
+ Assert(doubleArgumentExpression());
+ Assert(doubleArgumentExpression());
+ Assert(floatArgumentExpression());
+ Assert(floatArgumentExpression());
+ Assert(floatArgumentExpression());
+ Assert(stringArgumentExpression());
+ Assert(stringArgumentExpression());
+ Assert(stringArgumentExpression());
+
+ Assert(intAddExpression());
+ Assert(intAddExpression());
+ Assert(intAddExpression());
+
+ A b = new B(42);
+ A a = new A(42);
+ Assert(intCmpExpression(a, b));
+ Assert(intCmpExpression(a, b));
+ Assert(intCmpExpression(a, b));
+
+ // This is only called once!
+ Assert(onlyCalledOnce(10));
+
+ // This is only called twice, and is quite small!
+ Assert(onlyCalledTwice(1) == 2);
+ Assert(onlyCalledTwice(1) == 2);
+
+ InlineConstructor ic = InlineConstructor.create();
+ Assert(ic != null);
+ InlineConstructorOfInner icoi = new InlineConstructorOfInner();
+ Assert(icoi != null);
+ }
+
+ private static boolean intCmpExpression(A a, A b) {
+ return a.a() == b.a();
+ }
+
+ @CheckDiscarded
+ private static int intConstantInline() {
+ return 42;
+ }
+
+ @CheckDiscarded
+ private static boolean intExpression() {
+ return 42 == intConstantInline();
+ }
+
+ @CheckDiscarded
+ private static long longConstantInline() {
+ return 50000000000L;
+ }
+
+ @CheckDiscarded
+ private static boolean longExpression() {
+ return 50000000000L == longConstantInline();
+ }
+
+ @CheckDiscarded
+ private static double doubleConstantInline() {
+ return 42.42;
+ }
+
+ @CheckDiscarded
+ private static boolean doubleExpression() {
+ return 42.42 == doubleConstantInline();
+ }
+
+ @CheckDiscarded
+ private static float floatConstantInline() {
+ return 21.21F;
+ }
+
+ @CheckDiscarded
+ private static boolean floatExpression() {
+ return 21.21F == floatConstantInline();
+ }
+
+ private static String stringConstantInline() {
+ return "Fisk er godt";
+ }
+
+ private static boolean stringExpression() {
+ return "Fisk er godt" == stringConstantInline();
+ }
+
+ @CheckDiscarded
+ private static int intArgumentInline(int a, int b, int c) {
+ return b;
+ }
+
+ @CheckDiscarded
+ private static boolean intArgumentExpression() {
+ return 42 == intArgumentInline(-2, 42, -1);
+ }
+
+ @CheckDiscarded
+ private static long longArgumentInline(long a, long b, long c) {
+ return c;
+ }
+
+ @CheckDiscarded
+ private static boolean longArgumentExpression() {
+ return 50000000000L == longArgumentInline(-2L, -1L, 50000000000L);
+ }
+
+ @CheckDiscarded
+ private static double doubleArgumentInline(double a, double b, double c) {
+ return a;
+ }
+
+ @CheckDiscarded
+ private static boolean doubleArgumentExpression() {
+ return 42.42 == doubleArgumentInline(42.42, -2.0, -1.0);
+ }
+
+ @CheckDiscarded
+ private static float floatArgumentInline(float a, float b, float c) {
+ return b;
+ }
+
+ @CheckDiscarded
+ private static boolean floatArgumentExpression() {
+ return 21.21F == floatArgumentInline(-2.0F, 21.21F, -1.0F);
+ }
+
+ @CheckDiscarded
+ private static String stringArgumentInline(String a, String b, String c) {
+ return c;
+ }
+
+ private static boolean stringArgumentExpression() {
+ return "Fisk er godt" == stringArgumentInline("-1", "-1", "Fisk er godt");
+ }
+
+ @CheckDiscarded
+ private static int intAddInline(int a, int b) {
+ return a + b;
+ }
+
+ @CheckDiscarded
+ private static boolean intAddExpression() {
+ return 42 == intAddInline(21, 21);
+ }
+
+ @CheckDiscarded
+ private static boolean onlyCalledOnce(int count) {
+ int anotherCounter = 0;
+ for (int i = 0; i < count; i++) {
+ anotherCounter += i;
+ }
+ return anotherCounter > count;
+ }
+
+ @CheckDiscarded
+ private static int onlyCalledTwice(int count) {
+ return count > 0 ? count + 1 : count - 1;
+ }
+}
diff --git a/src/test/examples/inlining/keep-rules-discard.txt b/src/test/examples/inlining/keep-rules-discard.txt
new file mode 100644
index 0000000..66be962
--- /dev/null
+++ b/src/test/examples/inlining/keep-rules-discard.txt
@@ -0,0 +1,17 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class inlining.Inlining {
+ public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
+
+# check that methods have been inlined
+-checkdiscard class * {
+ @inlining.CheckDiscarded *;
+}
diff --git a/src/test/examples/inlining/keep-rules.txt b/src/test/examples/inlining/keep-rules.txt
new file mode 100644
index 0000000..dd713bb
--- /dev/null
+++ b/src/test/examples/inlining/keep-rules.txt
@@ -0,0 +1,12 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class inlining.Inlining {
+ public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/instanceofstring/InstanceofString.java b/src/test/examples/instanceofstring/InstanceofString.java
new file mode 100644
index 0000000..c7ff011
--- /dev/null
+++ b/src/test/examples/instanceofstring/InstanceofString.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package instanceofstring;
+
+class InstanceofString {
+ public static void main(String[] args) {
+ String s = "foobar";
+ System.out.println("is-string:" + (s instanceof String));
+ }
+}
diff --git a/src/test/examples/instancevariable/InstanceVariable.java b/src/test/examples/instancevariable/InstanceVariable.java
new file mode 100644
index 0000000..88768f1
--- /dev/null
+++ b/src/test/examples/instancevariable/InstanceVariable.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package instancevariable;
+
+class InstanceVariable {
+ public int foo = 42;
+ public int bar;
+ public InstanceVariable parent;
+
+ private int privateFoo;
+ private int privateBar = -1;
+ private InstanceVariable privateParent;
+
+ public InstanceVariable() {}
+
+ public InstanceVariable(int bar) {
+ this.bar = bar;
+ privateFoo = 1;
+ privateBar = 43;
+ parent = new InstanceVariable();
+ privateParent = new InstanceVariable();
+ }
+
+ public int sumAll() {
+ return privateFoo + privateBar + bar + foo + parent.foo + privateParent.privateBar;
+ }
+
+ public static void main(String[] args) {
+ InstanceVariable instanceVariable = new InstanceVariable(17);
+ System.out.println(instanceVariable.sumAll() + "=144");
+ }
+}
diff --git a/src/test/examples/invoke/Invoke.java b/src/test/examples/invoke/Invoke.java
new file mode 100644
index 0000000..d936140
--- /dev/null
+++ b/src/test/examples/invoke/Invoke.java
@@ -0,0 +1,456 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'invoke.dex' is what is run.
+
+package invoke;
+
+public class Invoke extends SuperClass implements InvokeInterface {
+
+ public static void static0() {
+ System.out.println("static0");
+ }
+
+ public static void static1(int a) {
+ System.out.println("static1 " + a);
+ }
+
+ public static void static2(int a, int b) {
+ System.out.println("static2 " + a + " " + b);
+ }
+
+ public static void static3(int a, int b, int c) {
+ System.out.println("static3 " + a + " " + b + " " + c);
+ }
+
+ public static void static4(int a, int b, int c, int d) {
+ System.out.println("static4 " + a + " " + b + " " + c + " " + d);
+ }
+
+ public static void static5(int a, int b, int c, int d, int e) {
+ System.out.println("static5 " + a + " " + b + " " + c + " " + d + " " + e);
+ }
+
+ public static void staticRange(int a, int b, int c, int d, int e, int f) {
+ System.out.println("staticRange " + a + " " + b + " " + c + " " + d + " " + e + " " + f);
+ }
+
+ public static void staticDouble0(double a) {
+ System.out.println("staticDouble0 " + a);
+ }
+
+ public static void staticDouble1(double a, double b) {
+ System.out.println("staticDouble2 " + a + " " + b);
+ }
+
+ public static void staticDoubleRange(double a, double b, double c) {
+ System.out.println("staticDoubleRange " + a + " " + b + " " + c);
+ }
+
+ public static void staticMethods() {
+ static0();
+ static1(1);
+ static2(1, 2);
+ static3(1, 2, 3);
+ static4(1, 2, 3, 4);
+ static5(1, 2, 3, 4, 5);
+ staticRange(1, 2, 3, 4, 5, 6);
+ staticDouble0(0.1);
+ staticDouble1(0.1, 0.2);
+ staticDoubleRange(0.1, 0.2, 0.3);
+ }
+
+ private void direct0() {
+ System.out.println("direct0");
+ }
+
+ private void direct1(int a) {
+ System.out.println("direct1 " + a);
+ }
+
+ private void direct2(int a, int b) {
+ System.out.println("direct2 " + a + " " + b);
+ }
+
+ private void direct3(int a, int b, int c) {
+ System.out.println("direct3 " + a + " " + b + " " + c);
+ }
+
+ private void direct4(int a, int b, int c, int d) {
+ System.out.println("direct4 " + a + " " + b + " " + c + " " + d);
+ }
+
+ private void directRange(int a, int b, int c, int d, int e, int f) {
+ System.out.println("directRange " + a + " " + b + " " + c + " " + d + " " + e + " " + f);
+ }
+
+ public static void directMethods() {
+ Invoke instance = new Invoke();
+ instance.direct0();
+ instance.direct1(1);
+ instance.direct2(1, 2);
+ instance.direct3(1, 2, 3);
+ instance.direct4(1, 2, 3, 4);
+ instance.directRange(1, 2, 3, 4, 5, 6);
+ }
+
+ public void interface0() {
+ System.out.println("interface0");
+ }
+
+ public void interface1(int a) {
+ System.out.println("interface1 " + a);
+ }
+
+ public void interface2(int a, int b) {
+ System.out.println("interface2 " + a + " " + b);
+ }
+
+ public void interface3(int a, int b, int c) {
+ System.out.println("interface3 " + a + " " + b + " " + c);
+ }
+
+ public void interface4(int a, int b, int c, int d) {
+ System.out.println("interface4 " + a + " " + b + " " + c + " " + d);
+ }
+
+ public void interface5(int a, int b, int c, int d, int e) {
+ System.out.println("interface5 " + a + " " + b + " " + c + " " + d + " " + e);
+ }
+
+ public void interfaceRange(int a, int b, int c, int d, int e, int f) {
+ System.out.println("interfaceRange " + a + " " + b + " " + c + " " + d + " " + e + " " + f);
+ }
+
+ public static void interfaceMethods(InvokeInterface i) {
+ i.interface0();
+ i.interface1(1);
+ i.interface2(1, 2);
+ i.interface3(1, 2, 3);
+ i.interface4(1, 2, 3, 4);
+ i.interfaceRange(1, 2, 3, 4, 5, 6);
+ }
+
+ public void virtual0() {
+ System.out.println("virtual0");
+ }
+
+ public void virtual1(int a) {
+ System.out.println("virtual1 " + a);
+ }
+
+ public void virtual2(int a, int b) {
+ System.out.println("virtual2 " + a + " " + b);
+ }
+
+ public void virtual3(int a, int b, int c) {
+ System.out.println("virtual3 " + a + " " + b + " " + c);
+ }
+
+ public void virtual4(int a, int b, int c, int d) {
+ System.out.println("virtual4 " + a + " " + b + " " + c + " " + d);
+ }
+
+ public void virtual5(int a, int b, int c, int d, int e) {
+ System.out.println("virtual5 " + a + " " + b + " " + c + " " + d + " " + e);
+ }
+
+ public void virtualRange(int a, int b, int c, int d, int e, int f) {
+ System.out.println("virtualRange " + a + " " + b + " " + c + " " + d + " " + e + " " + f);
+ }
+
+ public static void virtualMethods() {
+ Invoke instance = new Invoke();
+ instance.virtual0();
+ instance.virtual1(1);
+ instance.virtual2(1, 2);
+ instance.virtual3(1, 2, 3);
+ instance.virtual4(1, 2, 3, 4);
+ instance.virtualRange(1, 2, 3, 4, 5, 6);
+ }
+
+ public void super0() {
+ super.super0();
+ }
+
+ public void super1(int a) {
+ super.super1(a);
+ }
+
+ public void super2(int a, int b) {
+ super.super2(a, b);
+ }
+
+ public void super3(int a, int b, int c) {
+ super.super3(a, b, c);
+ }
+
+ public void super4(int a, int b, int c, int d) {
+ super.super4(a, b, c, d);
+ }
+
+ public void super5(int a, int b, int c, int d, int e) {
+ super.super5(a, b, c, d, e);
+ }
+
+ public void superRange(int a, int b, int c, int d, int e, int f) {
+ super.superRange(a, b, c, d, e, f);
+ }
+
+ public static void superInvocations() {
+ Invoke instance = new Invoke();
+ instance.super0();
+ instance.super1(1);
+ instance.super2(1, 2);
+ instance.super3(1, 2, 3);
+ instance.super4(1, 2, 3, 4);
+ instance.superRange(1, 2, 3, 4, 5, 6);
+ }
+
+ public static void rangeInvoke0(int i, int j, double d, double e, long l) {
+ System.out.println("rangeInvoke0 i " + i + " j " + j + " d " + d + " e " + e + " l " + l);
+ }
+
+ public static void rangeInvoke1(double d, double e, int i, int j, long l) {
+ System.out.println("rangeInvoke1 i " + i + " j " + j + " d " + d + " e " + e + " l " + l);
+ }
+
+ public static void rangeInvoke2(long l, double d, double e, int i, int j) {
+ System.out.println("rangeInvoke2 i " + i + " j " + j + " d " + d + " e " + e + " l " + l);
+ }
+
+ public static void rangeInvokes() {
+ int i = 0;
+ int j = 2;
+ double d = 42.42;
+ double e = 43.43;
+ long l = 0x0000000F00000000L;
+ // Range invokes with shuffled argument orders.
+ rangeInvoke0(i, j, d, e, l);
+ rangeInvoke0(i, j, d, e, l);
+ // Different order.
+ rangeInvoke1(d, e, i, j, l);
+ rangeInvoke1(d, e, i, j, l);
+ // And different again.
+ rangeInvoke2(l, d, e, i, j);
+ rangeInvoke2(l, d, e, i, j);
+ }
+
+ public static void oneArgumentMethod(int i) {
+ System.out.println("oneArgumentMethod " + i);
+ }
+
+ public static void twoArgumentMethod(int i, int j) {
+ System.out.println("twoArgumentMethod " + i + " " + j);
+ }
+
+ public static void unusedArgument0(int i0, int i1) {
+ oneArgumentMethod(i0);
+ }
+
+ public static void unusedArgument1(int i0, int i1) {
+ oneArgumentMethod(i1);
+ }
+
+ public static void unusedArgumentRanged(int i0, int i1, int i2, int i3, int i4, int i5, int i6,
+ int i7, int i8, int i9, int i10, int i11, int i12, int i13, int i14, int i15, int i16) {
+ oneArgumentMethod(i16);
+ twoArgumentMethod(i16, i9);
+ twoArgumentMethod(i16, i10);
+ twoArgumentMethod(i16, i11);
+ }
+
+ public static void oneDoubleArgumentMethod(double d) {
+ System.out.println("oneDoubleArgumentMethod " + d);
+ }
+
+ public static void twoDoubleArgumentMethod(double d0, double d1) {
+ System.out.println("twoDoubleArgumentMethod " + d0 + " " + d1);
+ }
+
+ public static void unusedDoubleArgument0(double d0, double d1) {
+ oneDoubleArgumentMethod(d0);
+ }
+
+ public static void unusedDoubleArgument1(double d0, double d1) {
+ oneDoubleArgumentMethod(d1);
+ }
+
+ public static void unusedDoubleArgumentRanged(double d0, double d1, double d2, double d3,
+ double d4, double d5, double d6, double d7, double d8, double d9, double d10, double d11,
+ double d12, double d13, double d14, double d15, double d16) {
+ oneDoubleArgumentMethod(d16);
+ twoDoubleArgumentMethod(d16, d9);
+ twoDoubleArgumentMethod(d16, d10);
+ twoDoubleArgumentMethod(d16, d11);
+ }
+
+ public static void unusedArguments() {
+ unusedArgument0(0, 1);
+ unusedArgument1(2, 3);
+ unusedArgumentRanged(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
+ unusedDoubleArgument0(1.1, 2.2);
+ unusedDoubleArgument1(3.3, 4.4);
+ unusedDoubleArgumentRanged(
+ 0.0, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.1, 11.2, 12.3, 13.4, 14.5, 15.6, 16.6);
+ }
+
+ public static void rangeInvokesRepeatedArgument0(
+ int i0, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
+ System.out.println("rangeInvokesRepeatedArgument0 " + i0 + " " + i1 + " " + i2 + " " + i3 +
+ " " + i4 + " " + i5 + " " + i6 + " " + i7);
+ }
+
+ public static void rangeInvokesRepeatedArgument() {
+ int i = 0;
+ int j = 1;
+ rangeInvokesRepeatedArgument0(i, j, i, j, i, j, i, j);
+ rangeInvokesRepeatedArgument0(i, j, j, j, j, j, j, j);
+ }
+
+ public static <T> T identity(T a) {
+ return a;
+ }
+
+ public static void printInt(int i) {
+ System.out.println("int: " + i);
+ }
+
+ public static void printDouble(double d) {
+ System.out.println("double: " + d);
+ }
+
+ public static void genericMethod() {
+ System.out.println("int: " + identity(42));
+ System.out.println("double: " + identity(42.42));
+ printInt(identity(32));
+ printDouble(identity(32.32));
+ }
+
+ public static void manyArgs(
+ int i0, int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9, int i10,
+ int i11, int i12, int i13, int i14, int i15, int i16, int i17, int i18, int i19, int i20,
+ int i21, int i22, int i23, int i24, int i25, int i26, int i27, int i28, int i29,
+ int i30, int i31, int i32, int i33, int i34, int i35, int i36, int i37, int i38,
+ int i39, int i40, int i41, int i42, int i43, int i44, int i45, int i46, int i47,
+ int i48, int i49, int i50, int i51, int i52, int i53, int i54, int i55, int i56,
+ int i57, int i58, int i59, int i60, int i61, int i62, int i63, int i64, int i65,
+ int i66, int i67, int i68, int i69, int i70, int i71, int i72, int i73, int i74,
+ int i75, int i76, int i77, int i78, int i79, int i80, int i81, int i82, int i83,
+ int i84, int i85, int i86, int i87, int i88, int i89, int i90, int i91, int i92,
+ int i93, int i94, int i95, int i96, int i97, int i98, int i99, int i100, int i101,
+ int i102, int i103, int i104, int i105, int i106, int i107, int i108, int i109, int i110,
+ int i111, int i112, int i113, int i114, int i115, int i116, int i117, int i118, int i119,
+ int i120, int i121, int i122, int i123, int i124, int i125, int i126, int i127, int i128,
+ int i129, int i130, int i131, int i132, int i133, int i134, int i135, int i136, int i137,
+ int i138, int i139, int i140, int i141, int i142, int i143, int i144, int i145, int i146,
+ int i147, int i148, int i149, int i150, int i151, int i152, int i153, int i154, int i155,
+ int i156, int i157, int i158, int i159, int i160, int i161, int i162, int i163, int i164,
+ int i165, int i166, int i167, int i168, int i169, int i170, int i171, int i172, int i173,
+ int i174, int i175, int i176, int i177, int i178, int i179, int i180, int i181, int i182,
+ int i183, int i184, int i185, int i186, int i187, int i188, int i189, int i190, int i191,
+ int i192, int i193, int i194, int i195, int i196, int i197, int i198, int i199, int i200,
+ int i201, int i202, int i203, int i204, int i205, int i206, int i207, int i208, int i209,
+ int i210, int i211, int i212, int i213, int i214, int i215, int i216, int i217, int i218,
+ int i219, int i220, int i221, int i222, int i223, int i224, int i225, int i226, int i227,
+ int i228, int i229, int i230, int i231, int i232, int i233, int i234, int i235, int i236,
+ int i237, int i238, int i239, int i240, int i241, int i242, int i243, int i244, int i245,
+ int i246, int i247, int i248, int i249, int i250, int i251, int i252, int i253, int i254) {
+ // This is here to defeat inlining at this point.
+ System.out.println(i254 + i253);
+ }
+
+ public static void rangedNoInlining(int i0, int i1, int i2, int i3, int i4, int i5) {
+ // This is here to defeat inlining at this point.
+ System.out.println(i0 + i1 + i2 + i3 + i4 + i5);
+ }
+
+ public static void rangeInvokeWithManyLocals(int i, int j, int k) {
+ int i0 = 0; int i1 = 1; int i2 = 2; int i3 = 3; int i4 = 4; int i5 = 5; int i6 = 6;
+ int i7 = 7; int i8 = 8; int i9 = 9; int i10 = 10; int i11 = 11; int i12 = 12;
+ int i13 = 13; int i14 = 14; int i15 = 15; int i16 = 16; int i17 = 17;
+ int i18 = 18; int i19 = 19; int i20 = 20; int i21 = 21; int i22 = 22;
+ int i23 = 23; int i24 = 24; int i25 = 25; int i26 = 26; int i27 = 27;
+ int i28 = 28; int i29 = 29; int i30 = 30; int i31 = 31; int i32 = 32;
+ int i33 = 33; int i34 = 34; int i35 = 35; int i36 = 36; int i37 = 37;
+ int i38 = 38; int i39 = 39; int i40 = 40; int i41 = 41; int i42 = 42;
+ int i43 = 43; int i44 = 44; int i45 = 45; int i46 = 46; int i47 = 47;
+ int i48 = 48; int i49 = 49; int i50 = 50; int i51 = 51; int i52 = 52;
+ int i53 = 53; int i54 = 54; int i55 = 55; int i56 = 56; int i57 = 57;
+ int i58 = 58; int i59 = 59; int i60 = 60; int i61 = 61; int i62 = 62;
+ int i63 = 63; int i64 = 64; int i65 = 65; int i66 = 66; int i67 = 67;
+ int i68 = 68; int i69 = 69; int i70 = 70; int i71 = 71; int i72 = 72;
+ int i73 = 73; int i74 = 74; int i75 = 75; int i76 = 76; int i77 = 77;
+ int i78 = 78; int i79 = 79; int i80 = 80; int i81 = 81; int i82 = 82;
+ int i83 = 83; int i84 = 84; int i85 = 85; int i86 = 86; int i87 = 87;
+ int i88 = 88; int i89 = 89; int i90 = 90; int i91 = 91; int i92 = 92;
+ int i93 = 93; int i94 = 94; int i95 = 95; int i96 = 96; int i97 = 97;
+ int i98 = 98; int i99 = 99; int i100 = 100; int i101 = 101; int i102 = 102;
+ int i103 = 103; int i104 = 104; int i105 = 105; int i106 = 106; int i107 = 107;
+ int i108 = 108; int i109 = 109; int i110 = 110; int i111 = 111; int i112 = 112;
+ int i113 = 113; int i114 = 114; int i115 = 115; int i116 = 116; int i117 = 117;
+ int i118 = 118; int i119 = 119; int i120 = 120; int i121 = 121; int i122 = 122;
+ int i123 = 123; int i124 = 124; int i125 = 125; int i126 = 126; int i127 = 127;
+ int i128 = 128; int i129 = 129; int i130 = 130; int i131 = 131; int i132 = 132;
+ int i133 = 133; int i134 = 134; int i135 = 135; int i136 = 136; int i137 = 137;
+ int i138 = 138; int i139 = 139; int i140 = 140; int i141 = 141; int i142 = 142;
+ int i143 = 143; int i144 = 144; int i145 = 145; int i146 = 146; int i147 = 147;
+ int i148 = 148; int i149 = 149; int i150 = 150; int i151 = 151; int i152 = 152;
+ int i153 = 153; int i154 = 154; int i155 = 155; int i156 = 156; int i157 = 157;
+ int i158 = 158; int i159 = 159; int i160 = 160; int i161 = 161; int i162 = 162;
+ int i163 = 163; int i164 = 164; int i165 = 165; int i166 = 166; int i167 = 167;
+ int i168 = 168; int i169 = 169; int i170 = 170; int i171 = 171; int i172 = 172;
+ int i173 = 173; int i174 = 174; int i175 = 175; int i176 = 176; int i177 = 177;
+ int i178 = 178; int i179 = 179; int i180 = 180; int i181 = 181; int i182 = 182;
+ int i183 = 183; int i184 = 184; int i185 = 185; int i186 = 186; int i187 = 187;
+ int i188 = 188; int i189 = 189; int i190 = 190; int i191 = 191; int i192 = 192;
+ int i193 = 193; int i194 = 194; int i195 = 195; int i196 = 196; int i197 = 197;
+ int i198 = 198; int i199 = 199; int i200 = 200; int i201 = 201; int i202 = 202;
+ int i203 = 203; int i204 = 204; int i205 = 205; int i206 = 206; int i207 = 207;
+ int i208 = 208; int i209 = 209; int i210 = 210; int i211 = 211; int i212 = 212;
+ int i213 = 213; int i214 = 214; int i215 = 215; int i216 = 216; int i217 = 217;
+ int i218 = 218; int i219 = 219; int i220 = 220; int i221 = 221; int i222 = 222;
+ int i223 = 223; int i224 = 224; int i225 = 225; int i226 = 226; int i227 = 227;
+ int i228 = 228; int i229 = 229; int i230 = 230; int i231 = 231; int i232 = 232;
+ int i233 = 233; int i234 = 234; int i235 = 235; int i236 = 236; int i237 = 237;
+ int i238 = 238; int i239 = 239; int i240 = 240; int i241 = 241; int i242 = 242;
+ int i243 = 243; int i244 = 244; int i245 = 245; int i246 = 246; int i247 = 247;
+ int i248 = 248; int i249 = 249; int i250 = 250; int i251 = 251; int i252 = 252;
+ int i253 = 253; int i254 = 254; int i255 = 255; int i256 = 256; int i257 = 257;
+ int i258 = 258; int i259 = 259; int i260 = 260;
+ manyArgs(
+ i6, i7, i8, i9, i10, i11, i12, i13, i14, i15, i16, i17, i18, i19, i20,
+ i21, i22, i23, i24, i25, i26, i27, i28, i29, i30, i31, i32, i33, i34, i35,
+ i36, i37, i38, i39, i40, i41, i42, i43, i44, i45, i46, i47, i48, i49, i50,
+ i51, i52, i53, i54, i55, i56, i57, i58, i59, i60, i61, i62, i63, i64, i65,
+ i66, i67, i68, i69, i70, i71, i72, i73, i74, i75, i76, i77, i78, i79, i80,
+ i81, i82, i83, i84, i85, i86, i87, i88, i89, i90, i91, i92, i93, i94, i95,
+ i96, i97, i98, i99, i100, i101, i102, i103, i104, i105, i106, i107, i108, i109, i110,
+ i111, i112, i113, i114, i115, i116, i117, i118, i119, i120, i121, i122, i123, i124, i125,
+ i126, i127, i128, i129, i130, i131, i132, i133, i134, i135, i136, i137, i138, i139, i140,
+ i141, i142, i143, i144, i145, i146, i147, i148, i149, i150, i151, i152, i153, i154, i155,
+ i156, i157, i158, i159, i160, i161, i162, i163, i164, i165, i166, i167, i168, i169, i170,
+ i171, i172, i173, i174, i175, i176, i177, i178, i179, i180, i181, i182, i183, i184, i185,
+ i186, i187, i188, i189, i190, i191, i192, i193, i194, i195, i196, i197, i198, i199, i200,
+ i201, i202, i203, i204, i205, i206, i207, i208, i209, i210, i211, i212, i213, i214, i215,
+ i216, i217, i218, i219, i220, i221, i222, i223, i224, i225, i226, i227, i228, i229, i230,
+ i231, i232, i233, i234, i235, i236, i237, i238, i239, i240, i241, i242, i243, i244, i245,
+ i246, i247, i248, i249, i250, i251, i252, i253, i254, i255, i256, i257, i258, i259, i260);
+ rangedNoInlining(i0, i1, i2, i3, i4, i5);
+ }
+
+ public static void main(String[] args) {
+ staticMethods();
+ directMethods();
+ interfaceMethods(new Invoke());
+ virtualMethods();
+ superInvocations();
+ rangeInvokes();
+ unusedArguments();
+ rangeInvokesRepeatedArgument();
+ genericMethod();
+ rangeInvokeWithManyLocals(1, 2, 3);
+ }
+}
diff --git a/src/test/examples/invoke/InvokeInterface.java b/src/test/examples/invoke/InvokeInterface.java
new file mode 100644
index 0000000..7f5a08e
--- /dev/null
+++ b/src/test/examples/invoke/InvokeInterface.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package invoke;
+
+public interface InvokeInterface {
+ public void interface0();
+ public void interface1(int a);
+ public void interface2(int a, int b);
+ public void interface3(int a, int b, int c);
+ public void interface4(int a, int b, int c, int d);
+ public void interfaceRange(int a, int b, int c, int d, int e, int f);
+}
diff --git a/src/test/examples/invoke/SuperClass.java b/src/test/examples/invoke/SuperClass.java
new file mode 100644
index 0000000..c4f7ae5
--- /dev/null
+++ b/src/test/examples/invoke/SuperClass.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package invoke;
+
+public class SuperClass {
+ public void super0() {
+ System.out.println("super0");
+ }
+
+ public void super1(int a) {
+ System.out.println("super1 " + a);
+ }
+
+ public void super2(int a, int b) {
+ System.out.println("super2 " + a + " " + b);
+ }
+
+ public void super3(int a, int b, int c) {
+ System.out.println("super3 " + a + " " + b + " " + c);
+ }
+
+ public void super4(int a, int b, int c, int d) {
+ System.out.println("super4 " + a + " " + b + " " + c + " " + d);
+ }
+
+ public void super5(int a, int b, int c, int d, int e) {
+ System.out.println("super5 " + a + " " + b + " " + c + " " + d + " " + e);
+ }
+
+ public void superRange(int a, int b, int c, int d, int e, int f) {
+ System.out.println("superRange " + a + " " + b + " " + c + " " + d + " " + e + " " + f);
+ }
+}
diff --git a/src/test/examples/invokeempty/ClassA.java b/src/test/examples/invokeempty/ClassA.java
new file mode 100644
index 0000000..50ac63c
--- /dev/null
+++ b/src/test/examples/invokeempty/ClassA.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package invokeempty;
+
+public class ClassA {
+
+ // This class has no constructor, by design.
+
+ public void aMethod() {
+ System.out.print("A");
+ }
+}
diff --git a/src/test/examples/invokeempty/ClassB.java b/src/test/examples/invokeempty/ClassB.java
new file mode 100644
index 0000000..305baa6
--- /dev/null
+++ b/src/test/examples/invokeempty/ClassB.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package invokeempty;
+
+public class ClassB {
+
+ // This class has no constructor, by design.
+
+ public void aMethod() {
+ System.out.print("B");
+ }
+}
diff --git a/src/test/examples/invokeempty/InvokeEmpty.java b/src/test/examples/invokeempty/InvokeEmpty.java
new file mode 100644
index 0000000..6160eb2
--- /dev/null
+++ b/src/test/examples/invokeempty/InvokeEmpty.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package invokeempty;
+
+public class InvokeEmpty {
+
+ public static void main(String[] args) {
+ ClassA anA = new ClassA();
+ ClassB anB = new ClassB();
+ anA.aMethod();
+ anB.aMethod();
+ }
+}
diff --git a/src/test/examples/jumbostring/JumboString.java b/src/test/examples/jumbostring/JumboString.java
new file mode 100644
index 0000000..9bfd9ca
--- /dev/null
+++ b/src/test/examples/jumbostring/JumboString.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package jumbostring;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+
+class JumboString {
+ public static void main(String[] args) {
+ // Make sure this string sorts after the field names and string values in the StringPoolX.java
+ // files to ensure this is a jumbo string.
+ System.out.println("zzzz - jumbo string");
+ }
+
+ // Code for generating the StringPoolX.java files.
+ //
+ // We only need to generate two files to get jumbo strings. Each file has 16k static final fields
+ // with values, and both the field name and the value will be in the string pool.
+ public static void generate() throws IOException {
+ int stringsPerFile = (1 << 14);
+ for (int fileNumber = 0; fileNumber < 2; fileNumber++) {
+ Path path = FileSystems.getDefault().getPath("StringPool" + fileNumber + ".java");
+ PrintStream out = new PrintStream(
+ Files.newOutputStream(path, StandardOpenOption.CREATE, StandardOpenOption.APPEND));
+
+ out.println(
+ "// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file");
+ out.println(
+ "// for details. All rights reserved. Use of this source code is governed by a");
+ out.println("// BSD-style license that can be found in the LICENSE file.");
+ out.println("package jumbostring;");
+ out.println();
+ out.println("class StringPool" + fileNumber + " {");
+
+ int offset = fileNumber * stringsPerFile;
+ for (int i = offset; i < offset + stringsPerFile; i++) {
+ out.println(" public static final String s" + i + " = \"" + i + "\";");
+ }
+ out.println("}");
+ out.close();
+ }
+ }
+}
diff --git a/src/test/examples/jumbostring/StringPool0.java b/src/test/examples/jumbostring/StringPool0.java
new file mode 100644
index 0000000..e6e3b4b
--- /dev/null
+++ b/src/test/examples/jumbostring/StringPool0.java
@@ -0,0 +1,16391 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package jumbostring;
+
+class StringPool0 {
+ public static final String s0 = "0";
+ public static final String s1 = "1";
+ public static final String s2 = "2";
+ public static final String s3 = "3";
+ public static final String s4 = "4";
+ public static final String s5 = "5";
+ public static final String s6 = "6";
+ public static final String s7 = "7";
+ public static final String s8 = "8";
+ public static final String s9 = "9";
+ public static final String s10 = "10";
+ public static final String s11 = "11";
+ public static final String s12 = "12";
+ public static final String s13 = "13";
+ public static final String s14 = "14";
+ public static final String s15 = "15";
+ public static final String s16 = "16";
+ public static final String s17 = "17";
+ public static final String s18 = "18";
+ public static final String s19 = "19";
+ public static final String s20 = "20";
+ public static final String s21 = "21";
+ public static final String s22 = "22";
+ public static final String s23 = "23";
+ public static final String s24 = "24";
+ public static final String s25 = "25";
+ public static final String s26 = "26";
+ public static final String s27 = "27";
+ public static final String s28 = "28";
+ public static final String s29 = "29";
+ public static final String s30 = "30";
+ public static final String s31 = "31";
+ public static final String s32 = "32";
+ public static final String s33 = "33";
+ public static final String s34 = "34";
+ public static final String s35 = "35";
+ public static final String s36 = "36";
+ public static final String s37 = "37";
+ public static final String s38 = "38";
+ public static final String s39 = "39";
+ public static final String s40 = "40";
+ public static final String s41 = "41";
+ public static final String s42 = "42";
+ public static final String s43 = "43";
+ public static final String s44 = "44";
+ public static final String s45 = "45";
+ public static final String s46 = "46";
+ public static final String s47 = "47";
+ public static final String s48 = "48";
+ public static final String s49 = "49";
+ public static final String s50 = "50";
+ public static final String s51 = "51";
+ public static final String s52 = "52";
+ public static final String s53 = "53";
+ public static final String s54 = "54";
+ public static final String s55 = "55";
+ public static final String s56 = "56";
+ public static final String s57 = "57";
+ public static final String s58 = "58";
+ public static final String s59 = "59";
+ public static final String s60 = "60";
+ public static final String s61 = "61";
+ public static final String s62 = "62";
+ public static final String s63 = "63";
+ public static final String s64 = "64";
+ public static final String s65 = "65";
+ public static final String s66 = "66";
+ public static final String s67 = "67";
+ public static final String s68 = "68";
+ public static final String s69 = "69";
+ public static final String s70 = "70";
+ public static final String s71 = "71";
+ public static final String s72 = "72";
+ public static final String s73 = "73";
+ public static final String s74 = "74";
+ public static final String s75 = "75";
+ public static final String s76 = "76";
+ public static final String s77 = "77";
+ public static final String s78 = "78";
+ public static final String s79 = "79";
+ public static final String s80 = "80";
+ public static final String s81 = "81";
+ public static final String s82 = "82";
+ public static final String s83 = "83";
+ public static final String s84 = "84";
+ public static final String s85 = "85";
+ public static final String s86 = "86";
+ public static final String s87 = "87";
+ public static final String s88 = "88";
+ public static final String s89 = "89";
+ public static final String s90 = "90";
+ public static final String s91 = "91";
+ public static final String s92 = "92";
+ public static final String s93 = "93";
+ public static final String s94 = "94";
+ public static final String s95 = "95";
+ public static final String s96 = "96";
+ public static final String s97 = "97";
+ public static final String s98 = "98";
+ public static final String s99 = "99";
+ public static final String s100 = "100";
+ public static final String s101 = "101";
+ public static final String s102 = "102";
+ public static final String s103 = "103";
+ public static final String s104 = "104";
+ public static final String s105 = "105";
+ public static final String s106 = "106";
+ public static final String s107 = "107";
+ public static final String s108 = "108";
+ public static final String s109 = "109";
+ public static final String s110 = "110";
+ public static final String s111 = "111";
+ public static final String s112 = "112";
+ public static final String s113 = "113";
+ public static final String s114 = "114";
+ public static final String s115 = "115";
+ public static final String s116 = "116";
+ public static final String s117 = "117";
+ public static final String s118 = "118";
+ public static final String s119 = "119";
+ public static final String s120 = "120";
+ public static final String s121 = "121";
+ public static final String s122 = "122";
+ public static final String s123 = "123";
+ public static final String s124 = "124";
+ public static final String s125 = "125";
+ public static final String s126 = "126";
+ public static final String s127 = "127";
+ public static final String s128 = "128";
+ public static final String s129 = "129";
+ public static final String s130 = "130";
+ public static final String s131 = "131";
+ public static final String s132 = "132";
+ public static final String s133 = "133";
+ public static final String s134 = "134";
+ public static final String s135 = "135";
+ public static final String s136 = "136";
+ public static final String s137 = "137";
+ public static final String s138 = "138";
+ public static final String s139 = "139";
+ public static final String s140 = "140";
+ public static final String s141 = "141";
+ public static final String s142 = "142";
+ public static final String s143 = "143";
+ public static final String s144 = "144";
+ public static final String s145 = "145";
+ public static final String s146 = "146";
+ public static final String s147 = "147";
+ public static final String s148 = "148";
+ public static final String s149 = "149";
+ public static final String s150 = "150";
+ public static final String s151 = "151";
+ public static final String s152 = "152";
+ public static final String s153 = "153";
+ public static final String s154 = "154";
+ public static final String s155 = "155";
+ public static final String s156 = "156";
+ public static final String s157 = "157";
+ public static final String s158 = "158";
+ public static final String s159 = "159";
+ public static final String s160 = "160";
+ public static final String s161 = "161";
+ public static final String s162 = "162";
+ public static final String s163 = "163";
+ public static final String s164 = "164";
+ public static final String s165 = "165";
+ public static final String s166 = "166";
+ public static final String s167 = "167";
+ public static final String s168 = "168";
+ public static final String s169 = "169";
+ public static final String s170 = "170";
+ public static final String s171 = "171";
+ public static final String s172 = "172";
+ public static final String s173 = "173";
+ public static final String s174 = "174";
+ public static final String s175 = "175";
+ public static final String s176 = "176";
+ public static final String s177 = "177";
+ public static final String s178 = "178";
+ public static final String s179 = "179";
+ public static final String s180 = "180";
+ public static final String s181 = "181";
+ public static final String s182 = "182";
+ public static final String s183 = "183";
+ public static final String s184 = "184";
+ public static final String s185 = "185";
+ public static final String s186 = "186";
+ public static final String s187 = "187";
+ public static final String s188 = "188";
+ public static final String s189 = "189";
+ public static final String s190 = "190";
+ public static final String s191 = "191";
+ public static final String s192 = "192";
+ public static final String s193 = "193";
+ public static final String s194 = "194";
+ public static final String s195 = "195";
+ public static final String s196 = "196";
+ public static final String s197 = "197";
+ public static final String s198 = "198";
+ public static final String s199 = "199";
+ public static final String s200 = "200";
+ public static final String s201 = "201";
+ public static final String s202 = "202";
+ public static final String s203 = "203";
+ public static final String s204 = "204";
+ public static final String s205 = "205";
+ public static final String s206 = "206";
+ public static final String s207 = "207";
+ public static final String s208 = "208";
+ public static final String s209 = "209";
+ public static final String s210 = "210";
+ public static final String s211 = "211";
+ public static final String s212 = "212";
+ public static final String s213 = "213";
+ public static final String s214 = "214";
+ public static final String s215 = "215";
+ public static final String s216 = "216";
+ public static final String s217 = "217";
+ public static final String s218 = "218";
+ public static final String s219 = "219";
+ public static final String s220 = "220";
+ public static final String s221 = "221";
+ public static final String s222 = "222";
+ public static final String s223 = "223";
+ public static final String s224 = "224";
+ public static final String s225 = "225";
+ public static final String s226 = "226";
+ public static final String s227 = "227";
+ public static final String s228 = "228";
+ public static final String s229 = "229";
+ public static final String s230 = "230";
+ public static final String s231 = "231";
+ public static final String s232 = "232";
+ public static final String s233 = "233";
+ public static final String s234 = "234";
+ public static final String s235 = "235";
+ public static final String s236 = "236";
+ public static final String s237 = "237";
+ public static final String s238 = "238";
+ public static final String s239 = "239";
+ public static final String s240 = "240";
+ public static final String s241 = "241";
+ public static final String s242 = "242";
+ public static final String s243 = "243";
+ public static final String s244 = "244";
+ public static final String s245 = "245";
+ public static final String s246 = "246";
+ public static final String s247 = "247";
+ public static final String s248 = "248";
+ public static final String s249 = "249";
+ public static final String s250 = "250";
+ public static final String s251 = "251";
+ public static final String s252 = "252";
+ public static final String s253 = "253";
+ public static final String s254 = "254";
+ public static final String s255 = "255";
+ public static final String s256 = "256";
+ public static final String s257 = "257";
+ public static final String s258 = "258";
+ public static final String s259 = "259";
+ public static final String s260 = "260";
+ public static final String s261 = "261";
+ public static final String s262 = "262";
+ public static final String s263 = "263";
+ public static final String s264 = "264";
+ public static final String s265 = "265";
+ public static final String s266 = "266";
+ public static final String s267 = "267";
+ public static final String s268 = "268";
+ public static final String s269 = "269";
+ public static final String s270 = "270";
+ public static final String s271 = "271";
+ public static final String s272 = "272";
+ public static final String s273 = "273";
+ public static final String s274 = "274";
+ public static final String s275 = "275";
+ public static final String s276 = "276";
+ public static final String s277 = "277";
+ public static final String s278 = "278";
+ public static final String s279 = "279";
+ public static final String s280 = "280";
+ public static final String s281 = "281";
+ public static final String s282 = "282";
+ public static final String s283 = "283";
+ public static final String s284 = "284";
+ public static final String s285 = "285";
+ public static final String s286 = "286";
+ public static final String s287 = "287";
+ public static final String s288 = "288";
+ public static final String s289 = "289";
+ public static final String s290 = "290";
+ public static final String s291 = "291";
+ public static final String s292 = "292";
+ public static final String s293 = "293";
+ public static final String s294 = "294";
+ public static final String s295 = "295";
+ public static final String s296 = "296";
+ public static final String s297 = "297";
+ public static final String s298 = "298";
+ public static final String s299 = "299";
+ public static final String s300 = "300";
+ public static final String s301 = "301";
+ public static final String s302 = "302";
+ public static final String s303 = "303";
+ public static final String s304 = "304";
+ public static final String s305 = "305";
+ public static final String s306 = "306";
+ public static final String s307 = "307";
+ public static final String s308 = "308";
+ public static final String s309 = "309";
+ public static final String s310 = "310";
+ public static final String s311 = "311";
+ public static final String s312 = "312";
+ public static final String s313 = "313";
+ public static final String s314 = "314";
+ public static final String s315 = "315";
+ public static final String s316 = "316";
+ public static final String s317 = "317";
+ public static final String s318 = "318";
+ public static final String s319 = "319";
+ public static final String s320 = "320";
+ public static final String s321 = "321";
+ public static final String s322 = "322";
+ public static final String s323 = "323";
+ public static final String s324 = "324";
+ public static final String s325 = "325";
+ public static final String s326 = "326";
+ public static final String s327 = "327";
+ public static final String s328 = "328";
+ public static final String s329 = "329";
+ public static final String s330 = "330";
+ public static final String s331 = "331";
+ public static final String s332 = "332";
+ public static final String s333 = "333";
+ public static final String s334 = "334";
+ public static final String s335 = "335";
+ public static final String s336 = "336";
+ public static final String s337 = "337";
+ public static final String s338 = "338";
+ public static final String s339 = "339";
+ public static final String s340 = "340";
+ public static final String s341 = "341";
+ public static final String s342 = "342";
+ public static final String s343 = "343";
+ public static final String s344 = "344";
+ public static final String s345 = "345";
+ public static final String s346 = "346";
+ public static final String s347 = "347";
+ public static final String s348 = "348";
+ public static final String s349 = "349";
+ public static final String s350 = "350";
+ public static final String s351 = "351";
+ public static final String s352 = "352";
+ public static final String s353 = "353";
+ public static final String s354 = "354";
+ public static final String s355 = "355";
+ public static final String s356 = "356";
+ public static final String s357 = "357";
+ public static final String s358 = "358";
+ public static final String s359 = "359";
+ public static final String s360 = "360";
+ public static final String s361 = "361";
+ public static final String s362 = "362";
+ public static final String s363 = "363";
+ public static final String s364 = "364";
+ public static final String s365 = "365";
+ public static final String s366 = "366";
+ public static final String s367 = "367";
+ public static final String s368 = "368";
+ public static final String s369 = "369";
+ public static final String s370 = "370";
+ public static final String s371 = "371";
+ public static final String s372 = "372";
+ public static final String s373 = "373";
+ public static final String s374 = "374";
+ public static final String s375 = "375";
+ public static final String s376 = "376";
+ public static final String s377 = "377";
+ public static final String s378 = "378";
+ public static final String s379 = "379";
+ public static final String s380 = "380";
+ public static final String s381 = "381";
+ public static final String s382 = "382";
+ public static final String s383 = "383";
+ public static final String s384 = "384";
+ public static final String s385 = "385";
+ public static final String s386 = "386";
+ public static final String s387 = "387";
+ public static final String s388 = "388";
+ public static final String s389 = "389";
+ public static final String s390 = "390";
+ public static final String s391 = "391";
+ public static final String s392 = "392";
+ public static final String s393 = "393";
+ public static final String s394 = "394";
+ public static final String s395 = "395";
+ public static final String s396 = "396";
+ public static final String s397 = "397";
+ public static final String s398 = "398";
+ public static final String s399 = "399";
+ public static final String s400 = "400";
+ public static final String s401 = "401";
+ public static final String s402 = "402";
+ public static final String s403 = "403";
+ public static final String s404 = "404";
+ public static final String s405 = "405";
+ public static final String s406 = "406";
+ public static final String s407 = "407";
+ public static final String s408 = "408";
+ public static final String s409 = "409";
+ public static final String s410 = "410";
+ public static final String s411 = "411";
+ public static final String s412 = "412";
+ public static final String s413 = "413";
+ public static final String s414 = "414";
+ public static final String s415 = "415";
+ public static final String s416 = "416";
+ public static final String s417 = "417";
+ public static final String s418 = "418";
+ public static final String s419 = "419";
+ public static final String s420 = "420";
+ public static final String s421 = "421";
+ public static final String s422 = "422";
+ public static final String s423 = "423";
+ public static final String s424 = "424";
+ public static final String s425 = "425";
+ public static final String s426 = "426";
+ public static final String s427 = "427";
+ public static final String s428 = "428";
+ public static final String s429 = "429";
+ public static final String s430 = "430";
+ public static final String s431 = "431";
+ public static final String s432 = "432";
+ public static final String s433 = "433";
+ public static final String s434 = "434";
+ public static final String s435 = "435";
+ public static final String s436 = "436";
+ public static final String s437 = "437";
+ public static final String s438 = "438";
+ public static final String s439 = "439";
+ public static final String s440 = "440";
+ public static final String s441 = "441";
+ public static final String s442 = "442";
+ public static final String s443 = "443";
+ public static final String s444 = "444";
+ public static final String s445 = "445";
+ public static final String s446 = "446";
+ public static final String s447 = "447";
+ public static final String s448 = "448";
+ public static final String s449 = "449";
+ public static final String s450 = "450";
+ public static final String s451 = "451";
+ public static final String s452 = "452";
+ public static final String s453 = "453";
+ public static final String s454 = "454";
+ public static final String s455 = "455";
+ public static final String s456 = "456";
+ public static final String s457 = "457";
+ public static final String s458 = "458";
+ public static final String s459 = "459";
+ public static final String s460 = "460";
+ public static final String s461 = "461";
+ public static final String s462 = "462";
+ public static final String s463 = "463";
+ public static final String s464 = "464";
+ public static final String s465 = "465";
+ public static final String s466 = "466";
+ public static final String s467 = "467";
+ public static final String s468 = "468";
+ public static final String s469 = "469";
+ public static final String s470 = "470";
+ public static final String s471 = "471";
+ public static final String s472 = "472";
+ public static final String s473 = "473";
+ public static final String s474 = "474";
+ public static final String s475 = "475";
+ public static final String s476 = "476";
+ public static final String s477 = "477";
+ public static final String s478 = "478";
+ public static final String s479 = "479";
+ public static final String s480 = "480";
+ public static final String s481 = "481";
+ public static final String s482 = "482";
+ public static final String s483 = "483";
+ public static final String s484 = "484";
+ public static final String s485 = "485";
+ public static final String s486 = "486";
+ public static final String s487 = "487";
+ public static final String s488 = "488";
+ public static final String s489 = "489";
+ public static final String s490 = "490";
+ public static final String s491 = "491";
+ public static final String s492 = "492";
+ public static final String s493 = "493";
+ public static final String s494 = "494";
+ public static final String s495 = "495";
+ public static final String s496 = "496";
+ public static final String s497 = "497";
+ public static final String s498 = "498";
+ public static final String s499 = "499";
+ public static final String s500 = "500";
+ public static final String s501 = "501";
+ public static final String s502 = "502";
+ public static final String s503 = "503";
+ public static final String s504 = "504";
+ public static final String s505 = "505";
+ public static final String s506 = "506";
+ public static final String s507 = "507";
+ public static final String s508 = "508";
+ public static final String s509 = "509";
+ public static final String s510 = "510";
+ public static final String s511 = "511";
+ public static final String s512 = "512";
+ public static final String s513 = "513";
+ public static final String s514 = "514";
+ public static final String s515 = "515";
+ public static final String s516 = "516";
+ public static final String s517 = "517";
+ public static final String s518 = "518";
+ public static final String s519 = "519";
+ public static final String s520 = "520";
+ public static final String s521 = "521";
+ public static final String s522 = "522";
+ public static final String s523 = "523";
+ public static final String s524 = "524";
+ public static final String s525 = "525";
+ public static final String s526 = "526";
+ public static final String s527 = "527";
+ public static final String s528 = "528";
+ public static final String s529 = "529";
+ public static final String s530 = "530";
+ public static final String s531 = "531";
+ public static final String s532 = "532";
+ public static final String s533 = "533";
+ public static final String s534 = "534";
+ public static final String s535 = "535";
+ public static final String s536 = "536";
+ public static final String s537 = "537";
+ public static final String s538 = "538";
+ public static final String s539 = "539";
+ public static final String s540 = "540";
+ public static final String s541 = "541";
+ public static final String s542 = "542";
+ public static final String s543 = "543";
+ public static final String s544 = "544";
+ public static final String s545 = "545";
+ public static final String s546 = "546";
+ public static final String s547 = "547";
+ public static final String s548 = "548";
+ public static final String s549 = "549";
+ public static final String s550 = "550";
+ public static final String s551 = "551";
+ public static final String s552 = "552";
+ public static final String s553 = "553";
+ public static final String s554 = "554";
+ public static final String s555 = "555";
+ public static final String s556 = "556";
+ public static final String s557 = "557";
+ public static final String s558 = "558";
+ public static final String s559 = "559";
+ public static final String s560 = "560";
+ public static final String s561 = "561";
+ public static final String s562 = "562";
+ public static final String s563 = "563";
+ public static final String s564 = "564";
+ public static final String s565 = "565";
+ public static final String s566 = "566";
+ public static final String s567 = "567";
+ public static final String s568 = "568";
+ public static final String s569 = "569";
+ public static final String s570 = "570";
+ public static final String s571 = "571";
+ public static final String s572 = "572";
+ public static final String s573 = "573";
+ public static final String s574 = "574";
+ public static final String s575 = "575";
+ public static final String s576 = "576";
+ public static final String s577 = "577";
+ public static final String s578 = "578";
+ public static final String s579 = "579";
+ public static final String s580 = "580";
+ public static final String s581 = "581";
+ public static final String s582 = "582";
+ public static final String s583 = "583";
+ public static final String s584 = "584";
+ public static final String s585 = "585";
+ public static final String s586 = "586";
+ public static final String s587 = "587";
+ public static final String s588 = "588";
+ public static final String s589 = "589";
+ public static final String s590 = "590";
+ public static final String s591 = "591";
+ public static final String s592 = "592";
+ public static final String s593 = "593";
+ public static final String s594 = "594";
+ public static final String s595 = "595";
+ public static final String s596 = "596";
+ public static final String s597 = "597";
+ public static final String s598 = "598";
+ public static final String s599 = "599";
+ public static final String s600 = "600";
+ public static final String s601 = "601";
+ public static final String s602 = "602";
+ public static final String s603 = "603";
+ public static final String s604 = "604";
+ public static final String s605 = "605";
+ public static final String s606 = "606";
+ public static final String s607 = "607";
+ public static final String s608 = "608";
+ public static final String s609 = "609";
+ public static final String s610 = "610";
+ public static final String s611 = "611";
+ public static final String s612 = "612";
+ public static final String s613 = "613";
+ public static final String s614 = "614";
+ public static final String s615 = "615";
+ public static final String s616 = "616";
+ public static final String s617 = "617";
+ public static final String s618 = "618";
+ public static final String s619 = "619";
+ public static final String s620 = "620";
+ public static final String s621 = "621";
+ public static final String s622 = "622";
+ public static final String s623 = "623";
+ public static final String s624 = "624";
+ public static final String s625 = "625";
+ public static final String s626 = "626";
+ public static final String s627 = "627";
+ public static final String s628 = "628";
+ public static final String s629 = "629";
+ public static final String s630 = "630";
+ public static final String s631 = "631";
+ public static final String s632 = "632";
+ public static final String s633 = "633";
+ public static final String s634 = "634";
+ public static final String s635 = "635";
+ public static final String s636 = "636";
+ public static final String s637 = "637";
+ public static final String s638 = "638";
+ public static final String s639 = "639";
+ public static final String s640 = "640";
+ public static final String s641 = "641";
+ public static final String s642 = "642";
+ public static final String s643 = "643";
+ public static final String s644 = "644";
+ public static final String s645 = "645";
+ public static final String s646 = "646";
+ public static final String s647 = "647";
+ public static final String s648 = "648";
+ public static final String s649 = "649";
+ public static final String s650 = "650";
+ public static final String s651 = "651";
+ public static final String s652 = "652";
+ public static final String s653 = "653";
+ public static final String s654 = "654";
+ public static final String s655 = "655";
+ public static final String s656 = "656";
+ public static final String s657 = "657";
+ public static final String s658 = "658";
+ public static final String s659 = "659";
+ public static final String s660 = "660";
+ public static final String s661 = "661";
+ public static final String s662 = "662";
+ public static final String s663 = "663";
+ public static final String s664 = "664";
+ public static final String s665 = "665";
+ public static final String s666 = "666";
+ public static final String s667 = "667";
+ public static final String s668 = "668";
+ public static final String s669 = "669";
+ public static final String s670 = "670";
+ public static final String s671 = "671";
+ public static final String s672 = "672";
+ public static final String s673 = "673";
+ public static final String s674 = "674";
+ public static final String s675 = "675";
+ public static final String s676 = "676";
+ public static final String s677 = "677";
+ public static final String s678 = "678";
+ public static final String s679 = "679";
+ public static final String s680 = "680";
+ public static final String s681 = "681";
+ public static final String s682 = "682";
+ public static final String s683 = "683";
+ public static final String s684 = "684";
+ public static final String s685 = "685";
+ public static final String s686 = "686";
+ public static final String s687 = "687";
+ public static final String s688 = "688";
+ public static final String s689 = "689";
+ public static final String s690 = "690";
+ public static final String s691 = "691";
+ public static final String s692 = "692";
+ public static final String s693 = "693";
+ public static final String s694 = "694";
+ public static final String s695 = "695";
+ public static final String s696 = "696";
+ public static final String s697 = "697";
+ public static final String s698 = "698";
+ public static final String s699 = "699";
+ public static final String s700 = "700";
+ public static final String s701 = "701";
+ public static final String s702 = "702";
+ public static final String s703 = "703";
+ public static final String s704 = "704";
+ public static final String s705 = "705";
+ public static final String s706 = "706";
+ public static final String s707 = "707";
+ public static final String s708 = "708";
+ public static final String s709 = "709";
+ public static final String s710 = "710";
+ public static final String s711 = "711";
+ public static final String s712 = "712";
+ public static final String s713 = "713";
+ public static final String s714 = "714";
+ public static final String s715 = "715";
+ public static final String s716 = "716";
+ public static final String s717 = "717";
+ public static final String s718 = "718";
+ public static final String s719 = "719";
+ public static final String s720 = "720";
+ public static final String s721 = "721";
+ public static final String s722 = "722";
+ public static final String s723 = "723";
+ public static final String s724 = "724";
+ public static final String s725 = "725";
+ public static final String s726 = "726";
+ public static final String s727 = "727";
+ public static final String s728 = "728";
+ public static final String s729 = "729";
+ public static final String s730 = "730";
+ public static final String s731 = "731";
+ public static final String s732 = "732";
+ public static final String s733 = "733";
+ public static final String s734 = "734";
+ public static final String s735 = "735";
+ public static final String s736 = "736";
+ public static final String s737 = "737";
+ public static final String s738 = "738";
+ public static final String s739 = "739";
+ public static final String s740 = "740";
+ public static final String s741 = "741";
+ public static final String s742 = "742";
+ public static final String s743 = "743";
+ public static final String s744 = "744";
+ public static final String s745 = "745";
+ public static final String s746 = "746";
+ public static final String s747 = "747";
+ public static final String s748 = "748";
+ public static final String s749 = "749";
+ public static final String s750 = "750";
+ public static final String s751 = "751";
+ public static final String s752 = "752";
+ public static final String s753 = "753";
+ public static final String s754 = "754";
+ public static final String s755 = "755";
+ public static final String s756 = "756";
+ public static final String s757 = "757";
+ public static final String s758 = "758";
+ public static final String s759 = "759";
+ public static final String s760 = "760";
+ public static final String s761 = "761";
+ public static final String s762 = "762";
+ public static final String s763 = "763";
+ public static final String s764 = "764";
+ public static final String s765 = "765";
+ public static final String s766 = "766";
+ public static final String s767 = "767";
+ public static final String s768 = "768";
+ public static final String s769 = "769";
+ public static final String s770 = "770";
+ public static final String s771 = "771";
+ public static final String s772 = "772";
+ public static final String s773 = "773";
+ public static final String s774 = "774";
+ public static final String s775 = "775";
+ public static final String s776 = "776";
+ public static final String s777 = "777";
+ public static final String s778 = "778";
+ public static final String s779 = "779";
+ public static final String s780 = "780";
+ public static final String s781 = "781";
+ public static final String s782 = "782";
+ public static final String s783 = "783";
+ public static final String s784 = "784";
+ public static final String s785 = "785";
+ public static final String s786 = "786";
+ public static final String s787 = "787";
+ public static final String s788 = "788";
+ public static final String s789 = "789";
+ public static final String s790 = "790";
+ public static final String s791 = "791";
+ public static final String s792 = "792";
+ public static final String s793 = "793";
+ public static final String s794 = "794";
+ public static final String s795 = "795";
+ public static final String s796 = "796";
+ public static final String s797 = "797";
+ public static final String s798 = "798";
+ public static final String s799 = "799";
+ public static final String s800 = "800";
+ public static final String s801 = "801";
+ public static final String s802 = "802";
+ public static final String s803 = "803";
+ public static final String s804 = "804";
+ public static final String s805 = "805";
+ public static final String s806 = "806";
+ public static final String s807 = "807";
+ public static final String s808 = "808";
+ public static final String s809 = "809";
+ public static final String s810 = "810";
+ public static final String s811 = "811";
+ public static final String s812 = "812";
+ public static final String s813 = "813";
+ public static final String s814 = "814";
+ public static final String s815 = "815";
+ public static final String s816 = "816";
+ public static final String s817 = "817";
+ public static final String s818 = "818";
+ public static final String s819 = "819";
+ public static final String s820 = "820";
+ public static final String s821 = "821";
+ public static final String s822 = "822";
+ public static final String s823 = "823";
+ public static final String s824 = "824";
+ public static final String s825 = "825";
+ public static final String s826 = "826";
+ public static final String s827 = "827";
+ public static final String s828 = "828";
+ public static final String s829 = "829";
+ public static final String s830 = "830";
+ public static final String s831 = "831";
+ public static final String s832 = "832";
+ public static final String s833 = "833";
+ public static final String s834 = "834";
+ public static final String s835 = "835";
+ public static final String s836 = "836";
+ public static final String s837 = "837";
+ public static final String s838 = "838";
+ public static final String s839 = "839";
+ public static final String s840 = "840";
+ public static final String s841 = "841";
+ public static final String s842 = "842";
+ public static final String s843 = "843";
+ public static final String s844 = "844";
+ public static final String s845 = "845";
+ public static final String s846 = "846";
+ public static final String s847 = "847";
+ public static final String s848 = "848";
+ public static final String s849 = "849";
+ public static final String s850 = "850";
+ public static final String s851 = "851";
+ public static final String s852 = "852";
+ public static final String s853 = "853";
+ public static final String s854 = "854";
+ public static final String s855 = "855";
+ public static final String s856 = "856";
+ public static final String s857 = "857";
+ public static final String s858 = "858";
+ public static final String s859 = "859";
+ public static final String s860 = "860";
+ public static final String s861 = "861";
+ public static final String s862 = "862";
+ public static final String s863 = "863";
+ public static final String s864 = "864";
+ public static final String s865 = "865";
+ public static final String s866 = "866";
+ public static final String s867 = "867";
+ public static final String s868 = "868";
+ public static final String s869 = "869";
+ public static final String s870 = "870";
+ public static final String s871 = "871";
+ public static final String s872 = "872";
+ public static final String s873 = "873";
+ public static final String s874 = "874";
+ public static final String s875 = "875";
+ public static final String s876 = "876";
+ public static final String s877 = "877";
+ public static final String s878 = "878";
+ public static final String s879 = "879";
+ public static final String s880 = "880";
+ public static final String s881 = "881";
+ public static final String s882 = "882";
+ public static final String s883 = "883";
+ public static final String s884 = "884";
+ public static final String s885 = "885";
+ public static final String s886 = "886";
+ public static final String s887 = "887";
+ public static final String s888 = "888";
+ public static final String s889 = "889";
+ public static final String s890 = "890";
+ public static final String s891 = "891";
+ public static final String s892 = "892";
+ public static final String s893 = "893";
+ public static final String s894 = "894";
+ public static final String s895 = "895";
+ public static final String s896 = "896";
+ public static final String s897 = "897";
+ public static final String s898 = "898";
+ public static final String s899 = "899";
+ public static final String s900 = "900";
+ public static final String s901 = "901";
+ public static final String s902 = "902";
+ public static final String s903 = "903";
+ public static final String s904 = "904";
+ public static final String s905 = "905";
+ public static final String s906 = "906";
+ public static final String s907 = "907";
+ public static final String s908 = "908";
+ public static final String s909 = "909";
+ public static final String s910 = "910";
+ public static final String s911 = "911";
+ public static final String s912 = "912";
+ public static final String s913 = "913";
+ public static final String s914 = "914";
+ public static final String s915 = "915";
+ public static final String s916 = "916";
+ public static final String s917 = "917";
+ public static final String s918 = "918";
+ public static final String s919 = "919";
+ public static final String s920 = "920";
+ public static final String s921 = "921";
+ public static final String s922 = "922";
+ public static final String s923 = "923";
+ public static final String s924 = "924";
+ public static final String s925 = "925";
+ public static final String s926 = "926";
+ public static final String s927 = "927";
+ public static final String s928 = "928";
+ public static final String s929 = "929";
+ public static final String s930 = "930";
+ public static final String s931 = "931";
+ public static final String s932 = "932";
+ public static final String s933 = "933";
+ public static final String s934 = "934";
+ public static final String s935 = "935";
+ public static final String s936 = "936";
+ public static final String s937 = "937";
+ public static final String s938 = "938";
+ public static final String s939 = "939";
+ public static final String s940 = "940";
+ public static final String s941 = "941";
+ public static final String s942 = "942";
+ public static final String s943 = "943";
+ public static final String s944 = "944";
+ public static final String s945 = "945";
+ public static final String s946 = "946";
+ public static final String s947 = "947";
+ public static final String s948 = "948";
+ public static final String s949 = "949";
+ public static final String s950 = "950";
+ public static final String s951 = "951";
+ public static final String s952 = "952";
+ public static final String s953 = "953";
+ public static final String s954 = "954";
+ public static final String s955 = "955";
+ public static final String s956 = "956";
+ public static final String s957 = "957";
+ public static final String s958 = "958";
+ public static final String s959 = "959";
+ public static final String s960 = "960";
+ public static final String s961 = "961";
+ public static final String s962 = "962";
+ public static final String s963 = "963";
+ public static final String s964 = "964";
+ public static final String s965 = "965";
+ public static final String s966 = "966";
+ public static final String s967 = "967";
+ public static final String s968 = "968";
+ public static final String s969 = "969";
+ public static final String s970 = "970";
+ public static final String s971 = "971";
+ public static final String s972 = "972";
+ public static final String s973 = "973";
+ public static final String s974 = "974";
+ public static final String s975 = "975";
+ public static final String s976 = "976";
+ public static final String s977 = "977";
+ public static final String s978 = "978";
+ public static final String s979 = "979";
+ public static final String s980 = "980";
+ public static final String s981 = "981";
+ public static final String s982 = "982";
+ public static final String s983 = "983";
+ public static final String s984 = "984";
+ public static final String s985 = "985";
+ public static final String s986 = "986";
+ public static final String s987 = "987";
+ public static final String s988 = "988";
+ public static final String s989 = "989";
+ public static final String s990 = "990";
+ public static final String s991 = "991";
+ public static final String s992 = "992";
+ public static final String s993 = "993";
+ public static final String s994 = "994";
+ public static final String s995 = "995";
+ public static final String s996 = "996";
+ public static final String s997 = "997";
+ public static final String s998 = "998";
+ public static final String s999 = "999";
+ public static final String s1000 = "1000";
+ public static final String s1001 = "1001";
+ public static final String s1002 = "1002";
+ public static final String s1003 = "1003";
+ public static final String s1004 = "1004";
+ public static final String s1005 = "1005";
+ public static final String s1006 = "1006";
+ public static final String s1007 = "1007";
+ public static final String s1008 = "1008";
+ public static final String s1009 = "1009";
+ public static final String s1010 = "1010";
+ public static final String s1011 = "1011";
+ public static final String s1012 = "1012";
+ public static final String s1013 = "1013";
+ public static final String s1014 = "1014";
+ public static final String s1015 = "1015";
+ public static final String s1016 = "1016";
+ public static final String s1017 = "1017";
+ public static final String s1018 = "1018";
+ public static final String s1019 = "1019";
+ public static final String s1020 = "1020";
+ public static final String s1021 = "1021";
+ public static final String s1022 = "1022";
+ public static final String s1023 = "1023";
+ public static final String s1024 = "1024";
+ public static final String s1025 = "1025";
+ public static final String s1026 = "1026";
+ public static final String s1027 = "1027";
+ public static final String s1028 = "1028";
+ public static final String s1029 = "1029";
+ public static final String s1030 = "1030";
+ public static final String s1031 = "1031";
+ public static final String s1032 = "1032";
+ public static final String s1033 = "1033";
+ public static final String s1034 = "1034";
+ public static final String s1035 = "1035";
+ public static final String s1036 = "1036";
+ public static final String s1037 = "1037";
+ public static final String s1038 = "1038";
+ public static final String s1039 = "1039";
+ public static final String s1040 = "1040";
+ public static final String s1041 = "1041";
+ public static final String s1042 = "1042";
+ public static final String s1043 = "1043";
+ public static final String s1044 = "1044";
+ public static final String s1045 = "1045";
+ public static final String s1046 = "1046";
+ public static final String s1047 = "1047";
+ public static final String s1048 = "1048";
+ public static final String s1049 = "1049";
+ public static final String s1050 = "1050";
+ public static final String s1051 = "1051";
+ public static final String s1052 = "1052";
+ public static final String s1053 = "1053";
+ public static final String s1054 = "1054";
+ public static final String s1055 = "1055";
+ public static final String s1056 = "1056";
+ public static final String s1057 = "1057";
+ public static final String s1058 = "1058";
+ public static final String s1059 = "1059";
+ public static final String s1060 = "1060";
+ public static final String s1061 = "1061";
+ public static final String s1062 = "1062";
+ public static final String s1063 = "1063";
+ public static final String s1064 = "1064";
+ public static final String s1065 = "1065";
+ public static final String s1066 = "1066";
+ public static final String s1067 = "1067";
+ public static final String s1068 = "1068";
+ public static final String s1069 = "1069";
+ public static final String s1070 = "1070";
+ public static final String s1071 = "1071";
+ public static final String s1072 = "1072";
+ public static final String s1073 = "1073";
+ public static final String s1074 = "1074";
+ public static final String s1075 = "1075";
+ public static final String s1076 = "1076";
+ public static final String s1077 = "1077";
+ public static final String s1078 = "1078";
+ public static final String s1079 = "1079";
+ public static final String s1080 = "1080";
+ public static final String s1081 = "1081";
+ public static final String s1082 = "1082";
+ public static final String s1083 = "1083";
+ public static final String s1084 = "1084";
+ public static final String s1085 = "1085";
+ public static final String s1086 = "1086";
+ public static final String s1087 = "1087";
+ public static final String s1088 = "1088";
+ public static final String s1089 = "1089";
+ public static final String s1090 = "1090";
+ public static final String s1091 = "1091";
+ public static final String s1092 = "1092";
+ public static final String s1093 = "1093";
+ public static final String s1094 = "1094";
+ public static final String s1095 = "1095";
+ public static final String s1096 = "1096";
+ public static final String s1097 = "1097";
+ public static final String s1098 = "1098";
+ public static final String s1099 = "1099";
+ public static final String s1100 = "1100";
+ public static final String s1101 = "1101";
+ public static final String s1102 = "1102";
+ public static final String s1103 = "1103";
+ public static final String s1104 = "1104";
+ public static final String s1105 = "1105";
+ public static final String s1106 = "1106";
+ public static final String s1107 = "1107";
+ public static final String s1108 = "1108";
+ public static final String s1109 = "1109";
+ public static final String s1110 = "1110";
+ public static final String s1111 = "1111";
+ public static final String s1112 = "1112";
+ public static final String s1113 = "1113";
+ public static final String s1114 = "1114";
+ public static final String s1115 = "1115";
+ public static final String s1116 = "1116";
+ public static final String s1117 = "1117";
+ public static final String s1118 = "1118";
+ public static final String s1119 = "1119";
+ public static final String s1120 = "1120";
+ public static final String s1121 = "1121";
+ public static final String s1122 = "1122";
+ public static final String s1123 = "1123";
+ public static final String s1124 = "1124";
+ public static final String s1125 = "1125";
+ public static final String s1126 = "1126";
+ public static final String s1127 = "1127";
+ public static final String s1128 = "1128";
+ public static final String s1129 = "1129";
+ public static final String s1130 = "1130";
+ public static final String s1131 = "1131";
+ public static final String s1132 = "1132";
+ public static final String s1133 = "1133";
+ public static final String s1134 = "1134";
+ public static final String s1135 = "1135";
+ public static final String s1136 = "1136";
+ public static final String s1137 = "1137";
+ public static final String s1138 = "1138";
+ public static final String s1139 = "1139";
+ public static final String s1140 = "1140";
+ public static final String s1141 = "1141";
+ public static final String s1142 = "1142";
+ public static final String s1143 = "1143";
+ public static final String s1144 = "1144";
+ public static final String s1145 = "1145";
+ public static final String s1146 = "1146";
+ public static final String s1147 = "1147";
+ public static final String s1148 = "1148";
+ public static final String s1149 = "1149";
+ public static final String s1150 = "1150";
+ public static final String s1151 = "1151";
+ public static final String s1152 = "1152";
+ public static final String s1153 = "1153";
+ public static final String s1154 = "1154";
+ public static final String s1155 = "1155";
+ public static final String s1156 = "1156";
+ public static final String s1157 = "1157";
+ public static final String s1158 = "1158";
+ public static final String s1159 = "1159";
+ public static final String s1160 = "1160";
+ public static final String s1161 = "1161";
+ public static final String s1162 = "1162";
+ public static final String s1163 = "1163";
+ public static final String s1164 = "1164";
+ public static final String s1165 = "1165";
+ public static final String s1166 = "1166";
+ public static final String s1167 = "1167";
+ public static final String s1168 = "1168";
+ public static final String s1169 = "1169";
+ public static final String s1170 = "1170";
+ public static final String s1171 = "1171";
+ public static final String s1172 = "1172";
+ public static final String s1173 = "1173";
+ public static final String s1174 = "1174";
+ public static final String s1175 = "1175";
+ public static final String s1176 = "1176";
+ public static final String s1177 = "1177";
+ public static final String s1178 = "1178";
+ public static final String s1179 = "1179";
+ public static final String s1180 = "1180";
+ public static final String s1181 = "1181";
+ public static final String s1182 = "1182";
+ public static final String s1183 = "1183";
+ public static final String s1184 = "1184";
+ public static final String s1185 = "1185";
+ public static final String s1186 = "1186";
+ public static final String s1187 = "1187";
+ public static final String s1188 = "1188";
+ public static final String s1189 = "1189";
+ public static final String s1190 = "1190";
+ public static final String s1191 = "1191";
+ public static final String s1192 = "1192";
+ public static final String s1193 = "1193";
+ public static final String s1194 = "1194";
+ public static final String s1195 = "1195";
+ public static final String s1196 = "1196";
+ public static final String s1197 = "1197";
+ public static final String s1198 = "1198";
+ public static final String s1199 = "1199";
+ public static final String s1200 = "1200";
+ public static final String s1201 = "1201";
+ public static final String s1202 = "1202";
+ public static final String s1203 = "1203";
+ public static final String s1204 = "1204";
+ public static final String s1205 = "1205";
+ public static final String s1206 = "1206";
+ public static final String s1207 = "1207";
+ public static final String s1208 = "1208";
+ public static final String s1209 = "1209";
+ public static final String s1210 = "1210";
+ public static final String s1211 = "1211";
+ public static final String s1212 = "1212";
+ public static final String s1213 = "1213";
+ public static final String s1214 = "1214";
+ public static final String s1215 = "1215";
+ public static final String s1216 = "1216";
+ public static final String s1217 = "1217";
+ public static final String s1218 = "1218";
+ public static final String s1219 = "1219";
+ public static final String s1220 = "1220";
+ public static final String s1221 = "1221";
+ public static final String s1222 = "1222";
+ public static final String s1223 = "1223";
+ public static final String s1224 = "1224";
+ public static final String s1225 = "1225";
+ public static final String s1226 = "1226";
+ public static final String s1227 = "1227";
+ public static final String s1228 = "1228";
+ public static final String s1229 = "1229";
+ public static final String s1230 = "1230";
+ public static final String s1231 = "1231";
+ public static final String s1232 = "1232";
+ public static final String s1233 = "1233";
+ public static final String s1234 = "1234";
+ public static final String s1235 = "1235";
+ public static final String s1236 = "1236";
+ public static final String s1237 = "1237";
+ public static final String s1238 = "1238";
+ public static final String s1239 = "1239";
+ public static final String s1240 = "1240";
+ public static final String s1241 = "1241";
+ public static final String s1242 = "1242";
+ public static final String s1243 = "1243";
+ public static final String s1244 = "1244";
+ public static final String s1245 = "1245";
+ public static final String s1246 = "1246";
+ public static final String s1247 = "1247";
+ public static final String s1248 = "1248";
+ public static final String s1249 = "1249";
+ public static final String s1250 = "1250";
+ public static final String s1251 = "1251";
+ public static final String s1252 = "1252";
+ public static final String s1253 = "1253";
+ public static final String s1254 = "1254";
+ public static final String s1255 = "1255";
+ public static final String s1256 = "1256";
+ public static final String s1257 = "1257";
+ public static final String s1258 = "1258";
+ public static final String s1259 = "1259";
+ public static final String s1260 = "1260";
+ public static final String s1261 = "1261";
+ public static final String s1262 = "1262";
+ public static final String s1263 = "1263";
+ public static final String s1264 = "1264";
+ public static final String s1265 = "1265";
+ public static final String s1266 = "1266";
+ public static final String s1267 = "1267";
+ public static final String s1268 = "1268";
+ public static final String s1269 = "1269";
+ public static final String s1270 = "1270";
+ public static final String s1271 = "1271";
+ public static final String s1272 = "1272";
+ public static final String s1273 = "1273";
+ public static final String s1274 = "1274";
+ public static final String s1275 = "1275";
+ public static final String s1276 = "1276";
+ public static final String s1277 = "1277";
+ public static final String s1278 = "1278";
+ public static final String s1279 = "1279";
+ public static final String s1280 = "1280";
+ public static final String s1281 = "1281";
+ public static final String s1282 = "1282";
+ public static final String s1283 = "1283";
+ public static final String s1284 = "1284";
+ public static final String s1285 = "1285";
+ public static final String s1286 = "1286";
+ public static final String s1287 = "1287";
+ public static final String s1288 = "1288";
+ public static final String s1289 = "1289";
+ public static final String s1290 = "1290";
+ public static final String s1291 = "1291";
+ public static final String s1292 = "1292";
+ public static final String s1293 = "1293";
+ public static final String s1294 = "1294";
+ public static final String s1295 = "1295";
+ public static final String s1296 = "1296";
+ public static final String s1297 = "1297";
+ public static final String s1298 = "1298";
+ public static final String s1299 = "1299";
+ public static final String s1300 = "1300";
+ public static final String s1301 = "1301";
+ public static final String s1302 = "1302";
+ public static final String s1303 = "1303";
+ public static final String s1304 = "1304";
+ public static final String s1305 = "1305";
+ public static final String s1306 = "1306";
+ public static final String s1307 = "1307";
+ public static final String s1308 = "1308";
+ public static final String s1309 = "1309";
+ public static final String s1310 = "1310";
+ public static final String s1311 = "1311";
+ public static final String s1312 = "1312";
+ public static final String s1313 = "1313";
+ public static final String s1314 = "1314";
+ public static final String s1315 = "1315";
+ public static final String s1316 = "1316";
+ public static final String s1317 = "1317";
+ public static final String s1318 = "1318";
+ public static final String s1319 = "1319";
+ public static final String s1320 = "1320";
+ public static final String s1321 = "1321";
+ public static final String s1322 = "1322";
+ public static final String s1323 = "1323";
+ public static final String s1324 = "1324";
+ public static final String s1325 = "1325";
+ public static final String s1326 = "1326";
+ public static final String s1327 = "1327";
+ public static final String s1328 = "1328";
+ public static final String s1329 = "1329";
+ public static final String s1330 = "1330";
+ public static final String s1331 = "1331";
+ public static final String s1332 = "1332";
+ public static final String s1333 = "1333";
+ public static final String s1334 = "1334";
+ public static final String s1335 = "1335";
+ public static final String s1336 = "1336";
+ public static final String s1337 = "1337";
+ public static final String s1338 = "1338";
+ public static final String s1339 = "1339";
+ public static final String s1340 = "1340";
+ public static final String s1341 = "1341";
+ public static final String s1342 = "1342";
+ public static final String s1343 = "1343";
+ public static final String s1344 = "1344";
+ public static final String s1345 = "1345";
+ public static final String s1346 = "1346";
+ public static final String s1347 = "1347";
+ public static final String s1348 = "1348";
+ public static final String s1349 = "1349";
+ public static final String s1350 = "1350";
+ public static final String s1351 = "1351";
+ public static final String s1352 = "1352";
+ public static final String s1353 = "1353";
+ public static final String s1354 = "1354";
+ public static final String s1355 = "1355";
+ public static final String s1356 = "1356";
+ public static final String s1357 = "1357";
+ public static final String s1358 = "1358";
+ public static final String s1359 = "1359";
+ public static final String s1360 = "1360";
+ public static final String s1361 = "1361";
+ public static final String s1362 = "1362";
+ public static final String s1363 = "1363";
+ public static final String s1364 = "1364";
+ public static final String s1365 = "1365";
+ public static final String s1366 = "1366";
+ public static final String s1367 = "1367";
+ public static final String s1368 = "1368";
+ public static final String s1369 = "1369";
+ public static final String s1370 = "1370";
+ public static final String s1371 = "1371";
+ public static final String s1372 = "1372";
+ public static final String s1373 = "1373";
+ public static final String s1374 = "1374";
+ public static final String s1375 = "1375";
+ public static final String s1376 = "1376";
+ public static final String s1377 = "1377";
+ public static final String s1378 = "1378";
+ public static final String s1379 = "1379";
+ public static final String s1380 = "1380";
+ public static final String s1381 = "1381";
+ public static final String s1382 = "1382";
+ public static final String s1383 = "1383";
+ public static final String s1384 = "1384";
+ public static final String s1385 = "1385";
+ public static final String s1386 = "1386";
+ public static final String s1387 = "1387";
+ public static final String s1388 = "1388";
+ public static final String s1389 = "1389";
+ public static final String s1390 = "1390";
+ public static final String s1391 = "1391";
+ public static final String s1392 = "1392";
+ public static final String s1393 = "1393";
+ public static final String s1394 = "1394";
+ public static final String s1395 = "1395";
+ public static final String s1396 = "1396";
+ public static final String s1397 = "1397";
+ public static final String s1398 = "1398";
+ public static final String s1399 = "1399";
+ public static final String s1400 = "1400";
+ public static final String s1401 = "1401";
+ public static final String s1402 = "1402";
+ public static final String s1403 = "1403";
+ public static final String s1404 = "1404";
+ public static final String s1405 = "1405";
+ public static final String s1406 = "1406";
+ public static final String s1407 = "1407";
+ public static final String s1408 = "1408";
+ public static final String s1409 = "1409";
+ public static final String s1410 = "1410";
+ public static final String s1411 = "1411";
+ public static final String s1412 = "1412";
+ public static final String s1413 = "1413";
+ public static final String s1414 = "1414";
+ public static final String s1415 = "1415";
+ public static final String s1416 = "1416";
+ public static final String s1417 = "1417";
+ public static final String s1418 = "1418";
+ public static final String s1419 = "1419";
+ public static final String s1420 = "1420";
+ public static final String s1421 = "1421";
+ public static final String s1422 = "1422";
+ public static final String s1423 = "1423";
+ public static final String s1424 = "1424";
+ public static final String s1425 = "1425";
+ public static final String s1426 = "1426";
+ public static final String s1427 = "1427";
+ public static final String s1428 = "1428";
+ public static final String s1429 = "1429";
+ public static final String s1430 = "1430";
+ public static final String s1431 = "1431";
+ public static final String s1432 = "1432";
+ public static final String s1433 = "1433";
+ public static final String s1434 = "1434";
+ public static final String s1435 = "1435";
+ public static final String s1436 = "1436";
+ public static final String s1437 = "1437";
+ public static final String s1438 = "1438";
+ public static final String s1439 = "1439";
+ public static final String s1440 = "1440";
+ public static final String s1441 = "1441";
+ public static final String s1442 = "1442";
+ public static final String s1443 = "1443";
+ public static final String s1444 = "1444";
+ public static final String s1445 = "1445";
+ public static final String s1446 = "1446";
+ public static final String s1447 = "1447";
+ public static final String s1448 = "1448";
+ public static final String s1449 = "1449";
+ public static final String s1450 = "1450";
+ public static final String s1451 = "1451";
+ public static final String s1452 = "1452";
+ public static final String s1453 = "1453";
+ public static final String s1454 = "1454";
+ public static final String s1455 = "1455";
+ public static final String s1456 = "1456";
+ public static final String s1457 = "1457";
+ public static final String s1458 = "1458";
+ public static final String s1459 = "1459";
+ public static final String s1460 = "1460";
+ public static final String s1461 = "1461";
+ public static final String s1462 = "1462";
+ public static final String s1463 = "1463";
+ public static final String s1464 = "1464";
+ public static final String s1465 = "1465";
+ public static final String s1466 = "1466";
+ public static final String s1467 = "1467";
+ public static final String s1468 = "1468";
+ public static final String s1469 = "1469";
+ public static final String s1470 = "1470";
+ public static final String s1471 = "1471";
+ public static final String s1472 = "1472";
+ public static final String s1473 = "1473";
+ public static final String s1474 = "1474";
+ public static final String s1475 = "1475";
+ public static final String s1476 = "1476";
+ public static final String s1477 = "1477";
+ public static final String s1478 = "1478";
+ public static final String s1479 = "1479";
+ public static final String s1480 = "1480";
+ public static final String s1481 = "1481";
+ public static final String s1482 = "1482";
+ public static final String s1483 = "1483";
+ public static final String s1484 = "1484";
+ public static final String s1485 = "1485";
+ public static final String s1486 = "1486";
+ public static final String s1487 = "1487";
+ public static final String s1488 = "1488";
+ public static final String s1489 = "1489";
+ public static final String s1490 = "1490";
+ public static final String s1491 = "1491";
+ public static final String s1492 = "1492";
+ public static final String s1493 = "1493";
+ public static final String s1494 = "1494";
+ public static final String s1495 = "1495";
+ public static final String s1496 = "1496";
+ public static final String s1497 = "1497";
+ public static final String s1498 = "1498";
+ public static final String s1499 = "1499";
+ public static final String s1500 = "1500";
+ public static final String s1501 = "1501";
+ public static final String s1502 = "1502";
+ public static final String s1503 = "1503";
+ public static final String s1504 = "1504";
+ public static final String s1505 = "1505";
+ public static final String s1506 = "1506";
+ public static final String s1507 = "1507";
+ public static final String s1508 = "1508";
+ public static final String s1509 = "1509";
+ public static final String s1510 = "1510";
+ public static final String s1511 = "1511";
+ public static final String s1512 = "1512";
+ public static final String s1513 = "1513";
+ public static final String s1514 = "1514";
+ public static final String s1515 = "1515";
+ public static final String s1516 = "1516";
+ public static final String s1517 = "1517";
+ public static final String s1518 = "1518";
+ public static final String s1519 = "1519";
+ public static final String s1520 = "1520";
+ public static final String s1521 = "1521";
+ public static final String s1522 = "1522";
+ public static final String s1523 = "1523";
+ public static final String s1524 = "1524";
+ public static final String s1525 = "1525";
+ public static final String s1526 = "1526";
+ public static final String s1527 = "1527";
+ public static final String s1528 = "1528";
+ public static final String s1529 = "1529";
+ public static final String s1530 = "1530";
+ public static final String s1531 = "1531";
+ public static final String s1532 = "1532";
+ public static final String s1533 = "1533";
+ public static final String s1534 = "1534";
+ public static final String s1535 = "1535";
+ public static final String s1536 = "1536";
+ public static final String s1537 = "1537";
+ public static final String s1538 = "1538";
+ public static final String s1539 = "1539";
+ public static final String s1540 = "1540";
+ public static final String s1541 = "1541";
+ public static final String s1542 = "1542";
+ public static final String s1543 = "1543";
+ public static final String s1544 = "1544";
+ public static final String s1545 = "1545";
+ public static final String s1546 = "1546";
+ public static final String s1547 = "1547";
+ public static final String s1548 = "1548";
+ public static final String s1549 = "1549";
+ public static final String s1550 = "1550";
+ public static final String s1551 = "1551";
+ public static final String s1552 = "1552";
+ public static final String s1553 = "1553";
+ public static final String s1554 = "1554";
+ public static final String s1555 = "1555";
+ public static final String s1556 = "1556";
+ public static final String s1557 = "1557";
+ public static final String s1558 = "1558";
+ public static final String s1559 = "1559";
+ public static final String s1560 = "1560";
+ public static final String s1561 = "1561";
+ public static final String s1562 = "1562";
+ public static final String s1563 = "1563";
+ public static final String s1564 = "1564";
+ public static final String s1565 = "1565";
+ public static final String s1566 = "1566";
+ public static final String s1567 = "1567";
+ public static final String s1568 = "1568";
+ public static final String s1569 = "1569";
+ public static final String s1570 = "1570";
+ public static final String s1571 = "1571";
+ public static final String s1572 = "1572";
+ public static final String s1573 = "1573";
+ public static final String s1574 = "1574";
+ public static final String s1575 = "1575";
+ public static final String s1576 = "1576";
+ public static final String s1577 = "1577";
+ public static final String s1578 = "1578";
+ public static final String s1579 = "1579";
+ public static final String s1580 = "1580";
+ public static final String s1581 = "1581";
+ public static final String s1582 = "1582";
+ public static final String s1583 = "1583";
+ public static final String s1584 = "1584";
+ public static final String s1585 = "1585";
+ public static final String s1586 = "1586";
+ public static final String s1587 = "1587";
+ public static final String s1588 = "1588";
+ public static final String s1589 = "1589";
+ public static final String s1590 = "1590";
+ public static final String s1591 = "1591";
+ public static final String s1592 = "1592";
+ public static final String s1593 = "1593";
+ public static final String s1594 = "1594";
+ public static final String s1595 = "1595";
+ public static final String s1596 = "1596";
+ public static final String s1597 = "1597";
+ public static final String s1598 = "1598";
+ public static final String s1599 = "1599";
+ public static final String s1600 = "1600";
+ public static final String s1601 = "1601";
+ public static final String s1602 = "1602";
+ public static final String s1603 = "1603";
+ public static final String s1604 = "1604";
+ public static final String s1605 = "1605";
+ public static final String s1606 = "1606";
+ public static final String s1607 = "1607";
+ public static final String s1608 = "1608";
+ public static final String s1609 = "1609";
+ public static final String s1610 = "1610";
+ public static final String s1611 = "1611";
+ public static final String s1612 = "1612";
+ public static final String s1613 = "1613";
+ public static final String s1614 = "1614";
+ public static final String s1615 = "1615";
+ public static final String s1616 = "1616";
+ public static final String s1617 = "1617";
+ public static final String s1618 = "1618";
+ public static final String s1619 = "1619";
+ public static final String s1620 = "1620";
+ public static final String s1621 = "1621";
+ public static final String s1622 = "1622";
+ public static final String s1623 = "1623";
+ public static final String s1624 = "1624";
+ public static final String s1625 = "1625";
+ public static final String s1626 = "1626";
+ public static final String s1627 = "1627";
+ public static final String s1628 = "1628";
+ public static final String s1629 = "1629";
+ public static final String s1630 = "1630";
+ public static final String s1631 = "1631";
+ public static final String s1632 = "1632";
+ public static final String s1633 = "1633";
+ public static final String s1634 = "1634";
+ public static final String s1635 = "1635";
+ public static final String s1636 = "1636";
+ public static final String s1637 = "1637";
+ public static final String s1638 = "1638";
+ public static final String s1639 = "1639";
+ public static final String s1640 = "1640";
+ public static final String s1641 = "1641";
+ public static final String s1642 = "1642";
+ public static final String s1643 = "1643";
+ public static final String s1644 = "1644";
+ public static final String s1645 = "1645";
+ public static final String s1646 = "1646";
+ public static final String s1647 = "1647";
+ public static final String s1648 = "1648";
+ public static final String s1649 = "1649";
+ public static final String s1650 = "1650";
+ public static final String s1651 = "1651";
+ public static final String s1652 = "1652";
+ public static final String s1653 = "1653";
+ public static final String s1654 = "1654";
+ public static final String s1655 = "1655";
+ public static final String s1656 = "1656";
+ public static final String s1657 = "1657";
+ public static final String s1658 = "1658";
+ public static final String s1659 = "1659";
+ public static final String s1660 = "1660";
+ public static final String s1661 = "1661";
+ public static final String s1662 = "1662";
+ public static final String s1663 = "1663";
+ public static final String s1664 = "1664";
+ public static final String s1665 = "1665";
+ public static final String s1666 = "1666";
+ public static final String s1667 = "1667";
+ public static final String s1668 = "1668";
+ public static final String s1669 = "1669";
+ public static final String s1670 = "1670";
+ public static final String s1671 = "1671";
+ public static final String s1672 = "1672";
+ public static final String s1673 = "1673";
+ public static final String s1674 = "1674";
+ public static final String s1675 = "1675";
+ public static final String s1676 = "1676";
+ public static final String s1677 = "1677";
+ public static final String s1678 = "1678";
+ public static final String s1679 = "1679";
+ public static final String s1680 = "1680";
+ public static final String s1681 = "1681";
+ public static final String s1682 = "1682";
+ public static final String s1683 = "1683";
+ public static final String s1684 = "1684";
+ public static final String s1685 = "1685";
+ public static final String s1686 = "1686";
+ public static final String s1687 = "1687";
+ public static final String s1688 = "1688";
+ public static final String s1689 = "1689";
+ public static final String s1690 = "1690";
+ public static final String s1691 = "1691";
+ public static final String s1692 = "1692";
+ public static final String s1693 = "1693";
+ public static final String s1694 = "1694";
+ public static final String s1695 = "1695";
+ public static final String s1696 = "1696";
+ public static final String s1697 = "1697";
+ public static final String s1698 = "1698";
+ public static final String s1699 = "1699";
+ public static final String s1700 = "1700";
+ public static final String s1701 = "1701";
+ public static final String s1702 = "1702";
+ public static final String s1703 = "1703";
+ public static final String s1704 = "1704";
+ public static final String s1705 = "1705";
+ public static final String s1706 = "1706";
+ public static final String s1707 = "1707";
+ public static final String s1708 = "1708";
+ public static final String s1709 = "1709";
+ public static final String s1710 = "1710";
+ public static final String s1711 = "1711";
+ public static final String s1712 = "1712";
+ public static final String s1713 = "1713";
+ public static final String s1714 = "1714";
+ public static final String s1715 = "1715";
+ public static final String s1716 = "1716";
+ public static final String s1717 = "1717";
+ public static final String s1718 = "1718";
+ public static final String s1719 = "1719";
+ public static final String s1720 = "1720";
+ public static final String s1721 = "1721";
+ public static final String s1722 = "1722";
+ public static final String s1723 = "1723";
+ public static final String s1724 = "1724";
+ public static final String s1725 = "1725";
+ public static final String s1726 = "1726";
+ public static final String s1727 = "1727";
+ public static final String s1728 = "1728";
+ public static final String s1729 = "1729";
+ public static final String s1730 = "1730";
+ public static final String s1731 = "1731";
+ public static final String s1732 = "1732";
+ public static final String s1733 = "1733";
+ public static final String s1734 = "1734";
+ public static final String s1735 = "1735";
+ public static final String s1736 = "1736";
+ public static final String s1737 = "1737";
+ public static final String s1738 = "1738";
+ public static final String s1739 = "1739";
+ public static final String s1740 = "1740";
+ public static final String s1741 = "1741";
+ public static final String s1742 = "1742";
+ public static final String s1743 = "1743";
+ public static final String s1744 = "1744";
+ public static final String s1745 = "1745";
+ public static final String s1746 = "1746";
+ public static final String s1747 = "1747";
+ public static final String s1748 = "1748";
+ public static final String s1749 = "1749";
+ public static final String s1750 = "1750";
+ public static final String s1751 = "1751";
+ public static final String s1752 = "1752";
+ public static final String s1753 = "1753";
+ public static final String s1754 = "1754";
+ public static final String s1755 = "1755";
+ public static final String s1756 = "1756";
+ public static final String s1757 = "1757";
+ public static final String s1758 = "1758";
+ public static final String s1759 = "1759";
+ public static final String s1760 = "1760";
+ public static final String s1761 = "1761";
+ public static final String s1762 = "1762";
+ public static final String s1763 = "1763";
+ public static final String s1764 = "1764";
+ public static final String s1765 = "1765";
+ public static final String s1766 = "1766";
+ public static final String s1767 = "1767";
+ public static final String s1768 = "1768";
+ public static final String s1769 = "1769";
+ public static final String s1770 = "1770";
+ public static final String s1771 = "1771";
+ public static final String s1772 = "1772";
+ public static final String s1773 = "1773";
+ public static final String s1774 = "1774";
+ public static final String s1775 = "1775";
+ public static final String s1776 = "1776";
+ public static final String s1777 = "1777";
+ public static final String s1778 = "1778";
+ public static final String s1779 = "1779";
+ public static final String s1780 = "1780";
+ public static final String s1781 = "1781";
+ public static final String s1782 = "1782";
+ public static final String s1783 = "1783";
+ public static final String s1784 = "1784";
+ public static final String s1785 = "1785";
+ public static final String s1786 = "1786";
+ public static final String s1787 = "1787";
+ public static final String s1788 = "1788";
+ public static final String s1789 = "1789";
+ public static final String s1790 = "1790";
+ public static final String s1791 = "1791";
+ public static final String s1792 = "1792";
+ public static final String s1793 = "1793";
+ public static final String s1794 = "1794";
+ public static final String s1795 = "1795";
+ public static final String s1796 = "1796";
+ public static final String s1797 = "1797";
+ public static final String s1798 = "1798";
+ public static final String s1799 = "1799";
+ public static final String s1800 = "1800";
+ public static final String s1801 = "1801";
+ public static final String s1802 = "1802";
+ public static final String s1803 = "1803";
+ public static final String s1804 = "1804";
+ public static final String s1805 = "1805";
+ public static final String s1806 = "1806";
+ public static final String s1807 = "1807";
+ public static final String s1808 = "1808";
+ public static final String s1809 = "1809";
+ public static final String s1810 = "1810";
+ public static final String s1811 = "1811";
+ public static final String s1812 = "1812";
+ public static final String s1813 = "1813";
+ public static final String s1814 = "1814";
+ public static final String s1815 = "1815";
+ public static final String s1816 = "1816";
+ public static final String s1817 = "1817";
+ public static final String s1818 = "1818";
+ public static final String s1819 = "1819";
+ public static final String s1820 = "1820";
+ public static final String s1821 = "1821";
+ public static final String s1822 = "1822";
+ public static final String s1823 = "1823";
+ public static final String s1824 = "1824";
+ public static final String s1825 = "1825";
+ public static final String s1826 = "1826";
+ public static final String s1827 = "1827";
+ public static final String s1828 = "1828";
+ public static final String s1829 = "1829";
+ public static final String s1830 = "1830";
+ public static final String s1831 = "1831";
+ public static final String s1832 = "1832";
+ public static final String s1833 = "1833";
+ public static final String s1834 = "1834";
+ public static final String s1835 = "1835";
+ public static final String s1836 = "1836";
+ public static final String s1837 = "1837";
+ public static final String s1838 = "1838";
+ public static final String s1839 = "1839";
+ public static final String s1840 = "1840";
+ public static final String s1841 = "1841";
+ public static final String s1842 = "1842";
+ public static final String s1843 = "1843";
+ public static final String s1844 = "1844";
+ public static final String s1845 = "1845";
+ public static final String s1846 = "1846";
+ public static final String s1847 = "1847";
+ public static final String s1848 = "1848";
+ public static final String s1849 = "1849";
+ public static final String s1850 = "1850";
+ public static final String s1851 = "1851";
+ public static final String s1852 = "1852";
+ public static final String s1853 = "1853";
+ public static final String s1854 = "1854";
+ public static final String s1855 = "1855";
+ public static final String s1856 = "1856";
+ public static final String s1857 = "1857";
+ public static final String s1858 = "1858";
+ public static final String s1859 = "1859";
+ public static final String s1860 = "1860";
+ public static final String s1861 = "1861";
+ public static final String s1862 = "1862";
+ public static final String s1863 = "1863";
+ public static final String s1864 = "1864";
+ public static final String s1865 = "1865";
+ public static final String s1866 = "1866";
+ public static final String s1867 = "1867";
+ public static final String s1868 = "1868";
+ public static final String s1869 = "1869";
+ public static final String s1870 = "1870";
+ public static final String s1871 = "1871";
+ public static final String s1872 = "1872";
+ public static final String s1873 = "1873";
+ public static final String s1874 = "1874";
+ public static final String s1875 = "1875";
+ public static final String s1876 = "1876";
+ public static final String s1877 = "1877";
+ public static final String s1878 = "1878";
+ public static final String s1879 = "1879";
+ public static final String s1880 = "1880";
+ public static final String s1881 = "1881";
+ public static final String s1882 = "1882";
+ public static final String s1883 = "1883";
+ public static final String s1884 = "1884";
+ public static final String s1885 = "1885";
+ public static final String s1886 = "1886";
+ public static final String s1887 = "1887";
+ public static final String s1888 = "1888";
+ public static final String s1889 = "1889";
+ public static final String s1890 = "1890";
+ public static final String s1891 = "1891";
+ public static final String s1892 = "1892";
+ public static final String s1893 = "1893";
+ public static final String s1894 = "1894";
+ public static final String s1895 = "1895";
+ public static final String s1896 = "1896";
+ public static final String s1897 = "1897";
+ public static final String s1898 = "1898";
+ public static final String s1899 = "1899";
+ public static final String s1900 = "1900";
+ public static final String s1901 = "1901";
+ public static final String s1902 = "1902";
+ public static final String s1903 = "1903";
+ public static final String s1904 = "1904";
+ public static final String s1905 = "1905";
+ public static final String s1906 = "1906";
+ public static final String s1907 = "1907";
+ public static final String s1908 = "1908";
+ public static final String s1909 = "1909";
+ public static final String s1910 = "1910";
+ public static final String s1911 = "1911";
+ public static final String s1912 = "1912";
+ public static final String s1913 = "1913";
+ public static final String s1914 = "1914";
+ public static final String s1915 = "1915";
+ public static final String s1916 = "1916";
+ public static final String s1917 = "1917";
+ public static final String s1918 = "1918";
+ public static final String s1919 = "1919";
+ public static final String s1920 = "1920";
+ public static final String s1921 = "1921";
+ public static final String s1922 = "1922";
+ public static final String s1923 = "1923";
+ public static final String s1924 = "1924";
+ public static final String s1925 = "1925";
+ public static final String s1926 = "1926";
+ public static final String s1927 = "1927";
+ public static final String s1928 = "1928";
+ public static final String s1929 = "1929";
+ public static final String s1930 = "1930";
+ public static final String s1931 = "1931";
+ public static final String s1932 = "1932";
+ public static final String s1933 = "1933";
+ public static final String s1934 = "1934";
+ public static final String s1935 = "1935";
+ public static final String s1936 = "1936";
+ public static final String s1937 = "1937";
+ public static final String s1938 = "1938";
+ public static final String s1939 = "1939";
+ public static final String s1940 = "1940";
+ public static final String s1941 = "1941";
+ public static final String s1942 = "1942";
+ public static final String s1943 = "1943";
+ public static final String s1944 = "1944";
+ public static final String s1945 = "1945";
+ public static final String s1946 = "1946";
+ public static final String s1947 = "1947";
+ public static final String s1948 = "1948";
+ public static final String s1949 = "1949";
+ public static final String s1950 = "1950";
+ public static final String s1951 = "1951";
+ public static final String s1952 = "1952";
+ public static final String s1953 = "1953";
+ public static final String s1954 = "1954";
+ public static final String s1955 = "1955";
+ public static final String s1956 = "1956";
+ public static final String s1957 = "1957";
+ public static final String s1958 = "1958";
+ public static final String s1959 = "1959";
+ public static final String s1960 = "1960";
+ public static final String s1961 = "1961";
+ public static final String s1962 = "1962";
+ public static final String s1963 = "1963";
+ public static final String s1964 = "1964";
+ public static final String s1965 = "1965";
+ public static final String s1966 = "1966";
+ public static final String s1967 = "1967";
+ public static final String s1968 = "1968";
+ public static final String s1969 = "1969";
+ public static final String s1970 = "1970";
+ public static final String s1971 = "1971";
+ public static final String s1972 = "1972";
+ public static final String s1973 = "1973";
+ public static final String s1974 = "1974";
+ public static final String s1975 = "1975";
+ public static final String s1976 = "1976";
+ public static final String s1977 = "1977";
+ public static final String s1978 = "1978";
+ public static final String s1979 = "1979";
+ public static final String s1980 = "1980";
+ public static final String s1981 = "1981";
+ public static final String s1982 = "1982";
+ public static final String s1983 = "1983";
+ public static final String s1984 = "1984";
+ public static final String s1985 = "1985";
+ public static final String s1986 = "1986";
+ public static final String s1987 = "1987";
+ public static final String s1988 = "1988";
+ public static final String s1989 = "1989";
+ public static final String s1990 = "1990";
+ public static final String s1991 = "1991";
+ public static final String s1992 = "1992";
+ public static final String s1993 = "1993";
+ public static final String s1994 = "1994";
+ public static final String s1995 = "1995";
+ public static final String s1996 = "1996";
+ public static final String s1997 = "1997";
+ public static final String s1998 = "1998";
+ public static final String s1999 = "1999";
+ public static final String s2000 = "2000";
+ public static final String s2001 = "2001";
+ public static final String s2002 = "2002";
+ public static final String s2003 = "2003";
+ public static final String s2004 = "2004";
+ public static final String s2005 = "2005";
+ public static final String s2006 = "2006";
+ public static final String s2007 = "2007";
+ public static final String s2008 = "2008";
+ public static final String s2009 = "2009";
+ public static final String s2010 = "2010";
+ public static final String s2011 = "2011";
+ public static final String s2012 = "2012";
+ public static final String s2013 = "2013";
+ public static final String s2014 = "2014";
+ public static final String s2015 = "2015";
+ public static final String s2016 = "2016";
+ public static final String s2017 = "2017";
+ public static final String s2018 = "2018";
+ public static final String s2019 = "2019";
+ public static final String s2020 = "2020";
+ public static final String s2021 = "2021";
+ public static final String s2022 = "2022";
+ public static final String s2023 = "2023";
+ public static final String s2024 = "2024";
+ public static final String s2025 = "2025";
+ public static final String s2026 = "2026";
+ public static final String s2027 = "2027";
+ public static final String s2028 = "2028";
+ public static final String s2029 = "2029";
+ public static final String s2030 = "2030";
+ public static final String s2031 = "2031";
+ public static final String s2032 = "2032";
+ public static final String s2033 = "2033";
+ public static final String s2034 = "2034";
+ public static final String s2035 = "2035";
+ public static final String s2036 = "2036";
+ public static final String s2037 = "2037";
+ public static final String s2038 = "2038";
+ public static final String s2039 = "2039";
+ public static final String s2040 = "2040";
+ public static final String s2041 = "2041";
+ public static final String s2042 = "2042";
+ public static final String s2043 = "2043";
+ public static final String s2044 = "2044";
+ public static final String s2045 = "2045";
+ public static final String s2046 = "2046";
+ public static final String s2047 = "2047";
+ public static final String s2048 = "2048";
+ public static final String s2049 = "2049";
+ public static final String s2050 = "2050";
+ public static final String s2051 = "2051";
+ public static final String s2052 = "2052";
+ public static final String s2053 = "2053";
+ public static final String s2054 = "2054";
+ public static final String s2055 = "2055";
+ public static final String s2056 = "2056";
+ public static final String s2057 = "2057";
+ public static final String s2058 = "2058";
+ public static final String s2059 = "2059";
+ public static final String s2060 = "2060";
+ public static final String s2061 = "2061";
+ public static final String s2062 = "2062";
+ public static final String s2063 = "2063";
+ public static final String s2064 = "2064";
+ public static final String s2065 = "2065";
+ public static final String s2066 = "2066";
+ public static final String s2067 = "2067";
+ public static final String s2068 = "2068";
+ public static final String s2069 = "2069";
+ public static final String s2070 = "2070";
+ public static final String s2071 = "2071";
+ public static final String s2072 = "2072";
+ public static final String s2073 = "2073";
+ public static final String s2074 = "2074";
+ public static final String s2075 = "2075";
+ public static final String s2076 = "2076";
+ public static final String s2077 = "2077";
+ public static final String s2078 = "2078";
+ public static final String s2079 = "2079";
+ public static final String s2080 = "2080";
+ public static final String s2081 = "2081";
+ public static final String s2082 = "2082";
+ public static final String s2083 = "2083";
+ public static final String s2084 = "2084";
+ public static final String s2085 = "2085";
+ public static final String s2086 = "2086";
+ public static final String s2087 = "2087";
+ public static final String s2088 = "2088";
+ public static final String s2089 = "2089";
+ public static final String s2090 = "2090";
+ public static final String s2091 = "2091";
+ public static final String s2092 = "2092";
+ public static final String s2093 = "2093";
+ public static final String s2094 = "2094";
+ public static final String s2095 = "2095";
+ public static final String s2096 = "2096";
+ public static final String s2097 = "2097";
+ public static final String s2098 = "2098";
+ public static final String s2099 = "2099";
+ public static final String s2100 = "2100";
+ public static final String s2101 = "2101";
+ public static final String s2102 = "2102";
+ public static final String s2103 = "2103";
+ public static final String s2104 = "2104";
+ public static final String s2105 = "2105";
+ public static final String s2106 = "2106";
+ public static final String s2107 = "2107";
+ public static final String s2108 = "2108";
+ public static final String s2109 = "2109";
+ public static final String s2110 = "2110";
+ public static final String s2111 = "2111";
+ public static final String s2112 = "2112";
+ public static final String s2113 = "2113";
+ public static final String s2114 = "2114";
+ public static final String s2115 = "2115";
+ public static final String s2116 = "2116";
+ public static final String s2117 = "2117";
+ public static final String s2118 = "2118";
+ public static final String s2119 = "2119";
+ public static final String s2120 = "2120";
+ public static final String s2121 = "2121";
+ public static final String s2122 = "2122";
+ public static final String s2123 = "2123";
+ public static final String s2124 = "2124";
+ public static final String s2125 = "2125";
+ public static final String s2126 = "2126";
+ public static final String s2127 = "2127";
+ public static final String s2128 = "2128";
+ public static final String s2129 = "2129";
+ public static final String s2130 = "2130";
+ public static final String s2131 = "2131";
+ public static final String s2132 = "2132";
+ public static final String s2133 = "2133";
+ public static final String s2134 = "2134";
+ public static final String s2135 = "2135";
+ public static final String s2136 = "2136";
+ public static final String s2137 = "2137";
+ public static final String s2138 = "2138";
+ public static final String s2139 = "2139";
+ public static final String s2140 = "2140";
+ public static final String s2141 = "2141";
+ public static final String s2142 = "2142";
+ public static final String s2143 = "2143";
+ public static final String s2144 = "2144";
+ public static final String s2145 = "2145";
+ public static final String s2146 = "2146";
+ public static final String s2147 = "2147";
+ public static final String s2148 = "2148";
+ public static final String s2149 = "2149";
+ public static final String s2150 = "2150";
+ public static final String s2151 = "2151";
+ public static final String s2152 = "2152";
+ public static final String s2153 = "2153";
+ public static final String s2154 = "2154";
+ public static final String s2155 = "2155";
+ public static final String s2156 = "2156";
+ public static final String s2157 = "2157";
+ public static final String s2158 = "2158";
+ public static final String s2159 = "2159";
+ public static final String s2160 = "2160";
+ public static final String s2161 = "2161";
+ public static final String s2162 = "2162";
+ public static final String s2163 = "2163";
+ public static final String s2164 = "2164";
+ public static final String s2165 = "2165";
+ public static final String s2166 = "2166";
+ public static final String s2167 = "2167";
+ public static final String s2168 = "2168";
+ public static final String s2169 = "2169";
+ public static final String s2170 = "2170";
+ public static final String s2171 = "2171";
+ public static final String s2172 = "2172";
+ public static final String s2173 = "2173";
+ public static final String s2174 = "2174";
+ public static final String s2175 = "2175";
+ public static final String s2176 = "2176";
+ public static final String s2177 = "2177";
+ public static final String s2178 = "2178";
+ public static final String s2179 = "2179";
+ public static final String s2180 = "2180";
+ public static final String s2181 = "2181";
+ public static final String s2182 = "2182";
+ public static final String s2183 = "2183";
+ public static final String s2184 = "2184";
+ public static final String s2185 = "2185";
+ public static final String s2186 = "2186";
+ public static final String s2187 = "2187";
+ public static final String s2188 = "2188";
+ public static final String s2189 = "2189";
+ public static final String s2190 = "2190";
+ public static final String s2191 = "2191";
+ public static final String s2192 = "2192";
+ public static final String s2193 = "2193";
+ public static final String s2194 = "2194";
+ public static final String s2195 = "2195";
+ public static final String s2196 = "2196";
+ public static final String s2197 = "2197";
+ public static final String s2198 = "2198";
+ public static final String s2199 = "2199";
+ public static final String s2200 = "2200";
+ public static final String s2201 = "2201";
+ public static final String s2202 = "2202";
+ public static final String s2203 = "2203";
+ public static final String s2204 = "2204";
+ public static final String s2205 = "2205";
+ public static final String s2206 = "2206";
+ public static final String s2207 = "2207";
+ public static final String s2208 = "2208";
+ public static final String s2209 = "2209";
+ public static final String s2210 = "2210";
+ public static final String s2211 = "2211";
+ public static final String s2212 = "2212";
+ public static final String s2213 = "2213";
+ public static final String s2214 = "2214";
+ public static final String s2215 = "2215";
+ public static final String s2216 = "2216";
+ public static final String s2217 = "2217";
+ public static final String s2218 = "2218";
+ public static final String s2219 = "2219";
+ public static final String s2220 = "2220";
+ public static final String s2221 = "2221";
+ public static final String s2222 = "2222";
+ public static final String s2223 = "2223";
+ public static final String s2224 = "2224";
+ public static final String s2225 = "2225";
+ public static final String s2226 = "2226";
+ public static final String s2227 = "2227";
+ public static final String s2228 = "2228";
+ public static final String s2229 = "2229";
+ public static final String s2230 = "2230";
+ public static final String s2231 = "2231";
+ public static final String s2232 = "2232";
+ public static final String s2233 = "2233";
+ public static final String s2234 = "2234";
+ public static final String s2235 = "2235";
+ public static final String s2236 = "2236";
+ public static final String s2237 = "2237";
+ public static final String s2238 = "2238";
+ public static final String s2239 = "2239";
+ public static final String s2240 = "2240";
+ public static final String s2241 = "2241";
+ public static final String s2242 = "2242";
+ public static final String s2243 = "2243";
+ public static final String s2244 = "2244";
+ public static final String s2245 = "2245";
+ public static final String s2246 = "2246";
+ public static final String s2247 = "2247";
+ public static final String s2248 = "2248";
+ public static final String s2249 = "2249";
+ public static final String s2250 = "2250";
+ public static final String s2251 = "2251";
+ public static final String s2252 = "2252";
+ public static final String s2253 = "2253";
+ public static final String s2254 = "2254";
+ public static final String s2255 = "2255";
+ public static final String s2256 = "2256";
+ public static final String s2257 = "2257";
+ public static final String s2258 = "2258";
+ public static final String s2259 = "2259";
+ public static final String s2260 = "2260";
+ public static final String s2261 = "2261";
+ public static final String s2262 = "2262";
+ public static final String s2263 = "2263";
+ public static final String s2264 = "2264";
+ public static final String s2265 = "2265";
+ public static final String s2266 = "2266";
+ public static final String s2267 = "2267";
+ public static final String s2268 = "2268";
+ public static final String s2269 = "2269";
+ public static final String s2270 = "2270";
+ public static final String s2271 = "2271";
+ public static final String s2272 = "2272";
+ public static final String s2273 = "2273";
+ public static final String s2274 = "2274";
+ public static final String s2275 = "2275";
+ public static final String s2276 = "2276";
+ public static final String s2277 = "2277";
+ public static final String s2278 = "2278";
+ public static final String s2279 = "2279";
+ public static final String s2280 = "2280";
+ public static final String s2281 = "2281";
+ public static final String s2282 = "2282";
+ public static final String s2283 = "2283";
+ public static final String s2284 = "2284";
+ public static final String s2285 = "2285";
+ public static final String s2286 = "2286";
+ public static final String s2287 = "2287";
+ public static final String s2288 = "2288";
+ public static final String s2289 = "2289";
+ public static final String s2290 = "2290";
+ public static final String s2291 = "2291";
+ public static final String s2292 = "2292";
+ public static final String s2293 = "2293";
+ public static final String s2294 = "2294";
+ public static final String s2295 = "2295";
+ public static final String s2296 = "2296";
+ public static final String s2297 = "2297";
+ public static final String s2298 = "2298";
+ public static final String s2299 = "2299";
+ public static final String s2300 = "2300";
+ public static final String s2301 = "2301";
+ public static final String s2302 = "2302";
+ public static final String s2303 = "2303";
+ public static final String s2304 = "2304";
+ public static final String s2305 = "2305";
+ public static final String s2306 = "2306";
+ public static final String s2307 = "2307";
+ public static final String s2308 = "2308";
+ public static final String s2309 = "2309";
+ public static final String s2310 = "2310";
+ public static final String s2311 = "2311";
+ public static final String s2312 = "2312";
+ public static final String s2313 = "2313";
+ public static final String s2314 = "2314";
+ public static final String s2315 = "2315";
+ public static final String s2316 = "2316";
+ public static final String s2317 = "2317";
+ public static final String s2318 = "2318";
+ public static final String s2319 = "2319";
+ public static final String s2320 = "2320";
+ public static final String s2321 = "2321";
+ public static final String s2322 = "2322";
+ public static final String s2323 = "2323";
+ public static final String s2324 = "2324";
+ public static final String s2325 = "2325";
+ public static final String s2326 = "2326";
+ public static final String s2327 = "2327";
+ public static final String s2328 = "2328";
+ public static final String s2329 = "2329";
+ public static final String s2330 = "2330";
+ public static final String s2331 = "2331";
+ public static final String s2332 = "2332";
+ public static final String s2333 = "2333";
+ public static final String s2334 = "2334";
+ public static final String s2335 = "2335";
+ public static final String s2336 = "2336";
+ public static final String s2337 = "2337";
+ public static final String s2338 = "2338";
+ public static final String s2339 = "2339";
+ public static final String s2340 = "2340";
+ public static final String s2341 = "2341";
+ public static final String s2342 = "2342";
+ public static final String s2343 = "2343";
+ public static final String s2344 = "2344";
+ public static final String s2345 = "2345";
+ public static final String s2346 = "2346";
+ public static final String s2347 = "2347";
+ public static final String s2348 = "2348";
+ public static final String s2349 = "2349";
+ public static final String s2350 = "2350";
+ public static final String s2351 = "2351";
+ public static final String s2352 = "2352";
+ public static final String s2353 = "2353";
+ public static final String s2354 = "2354";
+ public static final String s2355 = "2355";
+ public static final String s2356 = "2356";
+ public static final String s2357 = "2357";
+ public static final String s2358 = "2358";
+ public static final String s2359 = "2359";
+ public static final String s2360 = "2360";
+ public static final String s2361 = "2361";
+ public static final String s2362 = "2362";
+ public static final String s2363 = "2363";
+ public static final String s2364 = "2364";
+ public static final String s2365 = "2365";
+ public static final String s2366 = "2366";
+ public static final String s2367 = "2367";
+ public static final String s2368 = "2368";
+ public static final String s2369 = "2369";
+ public static final String s2370 = "2370";
+ public static final String s2371 = "2371";
+ public static final String s2372 = "2372";
+ public static final String s2373 = "2373";
+ public static final String s2374 = "2374";
+ public static final String s2375 = "2375";
+ public static final String s2376 = "2376";
+ public static final String s2377 = "2377";
+ public static final String s2378 = "2378";
+ public static final String s2379 = "2379";
+ public static final String s2380 = "2380";
+ public static final String s2381 = "2381";
+ public static final String s2382 = "2382";
+ public static final String s2383 = "2383";
+ public static final String s2384 = "2384";
+ public static final String s2385 = "2385";
+ public static final String s2386 = "2386";
+ public static final String s2387 = "2387";
+ public static final String s2388 = "2388";
+ public static final String s2389 = "2389";
+ public static final String s2390 = "2390";
+ public static final String s2391 = "2391";
+ public static final String s2392 = "2392";
+ public static final String s2393 = "2393";
+ public static final String s2394 = "2394";
+ public static final String s2395 = "2395";
+ public static final String s2396 = "2396";
+ public static final String s2397 = "2397";
+ public static final String s2398 = "2398";
+ public static final String s2399 = "2399";
+ public static final String s2400 = "2400";
+ public static final String s2401 = "2401";
+ public static final String s2402 = "2402";
+ public static final String s2403 = "2403";
+ public static final String s2404 = "2404";
+ public static final String s2405 = "2405";
+ public static final String s2406 = "2406";
+ public static final String s2407 = "2407";
+ public static final String s2408 = "2408";
+ public static final String s2409 = "2409";
+ public static final String s2410 = "2410";
+ public static final String s2411 = "2411";
+ public static final String s2412 = "2412";
+ public static final String s2413 = "2413";
+ public static final String s2414 = "2414";
+ public static final String s2415 = "2415";
+ public static final String s2416 = "2416";
+ public static final String s2417 = "2417";
+ public static final String s2418 = "2418";
+ public static final String s2419 = "2419";
+ public static final String s2420 = "2420";
+ public static final String s2421 = "2421";
+ public static final String s2422 = "2422";
+ public static final String s2423 = "2423";
+ public static final String s2424 = "2424";
+ public static final String s2425 = "2425";
+ public static final String s2426 = "2426";
+ public static final String s2427 = "2427";
+ public static final String s2428 = "2428";
+ public static final String s2429 = "2429";
+ public static final String s2430 = "2430";
+ public static final String s2431 = "2431";
+ public static final String s2432 = "2432";
+ public static final String s2433 = "2433";
+ public static final String s2434 = "2434";
+ public static final String s2435 = "2435";
+ public static final String s2436 = "2436";
+ public static final String s2437 = "2437";
+ public static final String s2438 = "2438";
+ public static final String s2439 = "2439";
+ public static final String s2440 = "2440";
+ public static final String s2441 = "2441";
+ public static final String s2442 = "2442";
+ public static final String s2443 = "2443";
+ public static final String s2444 = "2444";
+ public static final String s2445 = "2445";
+ public static final String s2446 = "2446";
+ public static final String s2447 = "2447";
+ public static final String s2448 = "2448";
+ public static final String s2449 = "2449";
+ public static final String s2450 = "2450";
+ public static final String s2451 = "2451";
+ public static final String s2452 = "2452";
+ public static final String s2453 = "2453";
+ public static final String s2454 = "2454";
+ public static final String s2455 = "2455";
+ public static final String s2456 = "2456";
+ public static final String s2457 = "2457";
+ public static final String s2458 = "2458";
+ public static final String s2459 = "2459";
+ public static final String s2460 = "2460";
+ public static final String s2461 = "2461";
+ public static final String s2462 = "2462";
+ public static final String s2463 = "2463";
+ public static final String s2464 = "2464";
+ public static final String s2465 = "2465";
+ public static final String s2466 = "2466";
+ public static final String s2467 = "2467";
+ public static final String s2468 = "2468";
+ public static final String s2469 = "2469";
+ public static final String s2470 = "2470";
+ public static final String s2471 = "2471";
+ public static final String s2472 = "2472";
+ public static final String s2473 = "2473";
+ public static final String s2474 = "2474";
+ public static final String s2475 = "2475";
+ public static final String s2476 = "2476";
+ public static final String s2477 = "2477";
+ public static final String s2478 = "2478";
+ public static final String s2479 = "2479";
+ public static final String s2480 = "2480";
+ public static final String s2481 = "2481";
+ public static final String s2482 = "2482";
+ public static final String s2483 = "2483";
+ public static final String s2484 = "2484";
+ public static final String s2485 = "2485";
+ public static final String s2486 = "2486";
+ public static final String s2487 = "2487";
+ public static final String s2488 = "2488";
+ public static final String s2489 = "2489";
+ public static final String s2490 = "2490";
+ public static final String s2491 = "2491";
+ public static final String s2492 = "2492";
+ public static final String s2493 = "2493";
+ public static final String s2494 = "2494";
+ public static final String s2495 = "2495";
+ public static final String s2496 = "2496";
+ public static final String s2497 = "2497";
+ public static final String s2498 = "2498";
+ public static final String s2499 = "2499";
+ public static final String s2500 = "2500";
+ public static final String s2501 = "2501";
+ public static final String s2502 = "2502";
+ public static final String s2503 = "2503";
+ public static final String s2504 = "2504";
+ public static final String s2505 = "2505";
+ public static final String s2506 = "2506";
+ public static final String s2507 = "2507";
+ public static final String s2508 = "2508";
+ public static final String s2509 = "2509";
+ public static final String s2510 = "2510";
+ public static final String s2511 = "2511";
+ public static final String s2512 = "2512";
+ public static final String s2513 = "2513";
+ public static final String s2514 = "2514";
+ public static final String s2515 = "2515";
+ public static final String s2516 = "2516";
+ public static final String s2517 = "2517";
+ public static final String s2518 = "2518";
+ public static final String s2519 = "2519";
+ public static final String s2520 = "2520";
+ public static final String s2521 = "2521";
+ public static final String s2522 = "2522";
+ public static final String s2523 = "2523";
+ public static final String s2524 = "2524";
+ public static final String s2525 = "2525";
+ public static final String s2526 = "2526";
+ public static final String s2527 = "2527";
+ public static final String s2528 = "2528";
+ public static final String s2529 = "2529";
+ public static final String s2530 = "2530";
+ public static final String s2531 = "2531";
+ public static final String s2532 = "2532";
+ public static final String s2533 = "2533";
+ public static final String s2534 = "2534";
+ public static final String s2535 = "2535";
+ public static final String s2536 = "2536";
+ public static final String s2537 = "2537";
+ public static final String s2538 = "2538";
+ public static final String s2539 = "2539";
+ public static final String s2540 = "2540";
+ public static final String s2541 = "2541";
+ public static final String s2542 = "2542";
+ public static final String s2543 = "2543";
+ public static final String s2544 = "2544";
+ public static final String s2545 = "2545";
+ public static final String s2546 = "2546";
+ public static final String s2547 = "2547";
+ public static final String s2548 = "2548";
+ public static final String s2549 = "2549";
+ public static final String s2550 = "2550";
+ public static final String s2551 = "2551";
+ public static final String s2552 = "2552";
+ public static final String s2553 = "2553";
+ public static final String s2554 = "2554";
+ public static final String s2555 = "2555";
+ public static final String s2556 = "2556";
+ public static final String s2557 = "2557";
+ public static final String s2558 = "2558";
+ public static final String s2559 = "2559";
+ public static final String s2560 = "2560";
+ public static final String s2561 = "2561";
+ public static final String s2562 = "2562";
+ public static final String s2563 = "2563";
+ public static final String s2564 = "2564";
+ public static final String s2565 = "2565";
+ public static final String s2566 = "2566";
+ public static final String s2567 = "2567";
+ public static final String s2568 = "2568";
+ public static final String s2569 = "2569";
+ public static final String s2570 = "2570";
+ public static final String s2571 = "2571";
+ public static final String s2572 = "2572";
+ public static final String s2573 = "2573";
+ public static final String s2574 = "2574";
+ public static final String s2575 = "2575";
+ public static final String s2576 = "2576";
+ public static final String s2577 = "2577";
+ public static final String s2578 = "2578";
+ public static final String s2579 = "2579";
+ public static final String s2580 = "2580";
+ public static final String s2581 = "2581";
+ public static final String s2582 = "2582";
+ public static final String s2583 = "2583";
+ public static final String s2584 = "2584";
+ public static final String s2585 = "2585";
+ public static final String s2586 = "2586";
+ public static final String s2587 = "2587";
+ public static final String s2588 = "2588";
+ public static final String s2589 = "2589";
+ public static final String s2590 = "2590";
+ public static final String s2591 = "2591";
+ public static final String s2592 = "2592";
+ public static final String s2593 = "2593";
+ public static final String s2594 = "2594";
+ public static final String s2595 = "2595";
+ public static final String s2596 = "2596";
+ public static final String s2597 = "2597";
+ public static final String s2598 = "2598";
+ public static final String s2599 = "2599";
+ public static final String s2600 = "2600";
+ public static final String s2601 = "2601";
+ public static final String s2602 = "2602";
+ public static final String s2603 = "2603";
+ public static final String s2604 = "2604";
+ public static final String s2605 = "2605";
+ public static final String s2606 = "2606";
+ public static final String s2607 = "2607";
+ public static final String s2608 = "2608";
+ public static final String s2609 = "2609";
+ public static final String s2610 = "2610";
+ public static final String s2611 = "2611";
+ public static final String s2612 = "2612";
+ public static final String s2613 = "2613";
+ public static final String s2614 = "2614";
+ public static final String s2615 = "2615";
+ public static final String s2616 = "2616";
+ public static final String s2617 = "2617";
+ public static final String s2618 = "2618";
+ public static final String s2619 = "2619";
+ public static final String s2620 = "2620";
+ public static final String s2621 = "2621";
+ public static final String s2622 = "2622";
+ public static final String s2623 = "2623";
+ public static final String s2624 = "2624";
+ public static final String s2625 = "2625";
+ public static final String s2626 = "2626";
+ public static final String s2627 = "2627";
+ public static final String s2628 = "2628";
+ public static final String s2629 = "2629";
+ public static final String s2630 = "2630";
+ public static final String s2631 = "2631";
+ public static final String s2632 = "2632";
+ public static final String s2633 = "2633";
+ public static final String s2634 = "2634";
+ public static final String s2635 = "2635";
+ public static final String s2636 = "2636";
+ public static final String s2637 = "2637";
+ public static final String s2638 = "2638";
+ public static final String s2639 = "2639";
+ public static final String s2640 = "2640";
+ public static final String s2641 = "2641";
+ public static final String s2642 = "2642";
+ public static final String s2643 = "2643";
+ public static final String s2644 = "2644";
+ public static final String s2645 = "2645";
+ public static final String s2646 = "2646";
+ public static final String s2647 = "2647";
+ public static final String s2648 = "2648";
+ public static final String s2649 = "2649";
+ public static final String s2650 = "2650";
+ public static final String s2651 = "2651";
+ public static final String s2652 = "2652";
+ public static final String s2653 = "2653";
+ public static final String s2654 = "2654";
+ public static final String s2655 = "2655";
+ public static final String s2656 = "2656";
+ public static final String s2657 = "2657";
+ public static final String s2658 = "2658";
+ public static final String s2659 = "2659";
+ public static final String s2660 = "2660";
+ public static final String s2661 = "2661";
+ public static final String s2662 = "2662";
+ public static final String s2663 = "2663";
+ public static final String s2664 = "2664";
+ public static final String s2665 = "2665";
+ public static final String s2666 = "2666";
+ public static final String s2667 = "2667";
+ public static final String s2668 = "2668";
+ public static final String s2669 = "2669";
+ public static final String s2670 = "2670";
+ public static final String s2671 = "2671";
+ public static final String s2672 = "2672";
+ public static final String s2673 = "2673";
+ public static final String s2674 = "2674";
+ public static final String s2675 = "2675";
+ public static final String s2676 = "2676";
+ public static final String s2677 = "2677";
+ public static final String s2678 = "2678";
+ public static final String s2679 = "2679";
+ public static final String s2680 = "2680";
+ public static final String s2681 = "2681";
+ public static final String s2682 = "2682";
+ public static final String s2683 = "2683";
+ public static final String s2684 = "2684";
+ public static final String s2685 = "2685";
+ public static final String s2686 = "2686";
+ public static final String s2687 = "2687";
+ public static final String s2688 = "2688";
+ public static final String s2689 = "2689";
+ public static final String s2690 = "2690";
+ public static final String s2691 = "2691";
+ public static final String s2692 = "2692";
+ public static final String s2693 = "2693";
+ public static final String s2694 = "2694";
+ public static final String s2695 = "2695";
+ public static final String s2696 = "2696";
+ public static final String s2697 = "2697";
+ public static final String s2698 = "2698";
+ public static final String s2699 = "2699";
+ public static final String s2700 = "2700";
+ public static final String s2701 = "2701";
+ public static final String s2702 = "2702";
+ public static final String s2703 = "2703";
+ public static final String s2704 = "2704";
+ public static final String s2705 = "2705";
+ public static final String s2706 = "2706";
+ public static final String s2707 = "2707";
+ public static final String s2708 = "2708";
+ public static final String s2709 = "2709";
+ public static final String s2710 = "2710";
+ public static final String s2711 = "2711";
+ public static final String s2712 = "2712";
+ public static final String s2713 = "2713";
+ public static final String s2714 = "2714";
+ public static final String s2715 = "2715";
+ public static final String s2716 = "2716";
+ public static final String s2717 = "2717";
+ public static final String s2718 = "2718";
+ public static final String s2719 = "2719";
+ public static final String s2720 = "2720";
+ public static final String s2721 = "2721";
+ public static final String s2722 = "2722";
+ public static final String s2723 = "2723";
+ public static final String s2724 = "2724";
+ public static final String s2725 = "2725";
+ public static final String s2726 = "2726";
+ public static final String s2727 = "2727";
+ public static final String s2728 = "2728";
+ public static final String s2729 = "2729";
+ public static final String s2730 = "2730";
+ public static final String s2731 = "2731";
+ public static final String s2732 = "2732";
+ public static final String s2733 = "2733";
+ public static final String s2734 = "2734";
+ public static final String s2735 = "2735";
+ public static final String s2736 = "2736";
+ public static final String s2737 = "2737";
+ public static final String s2738 = "2738";
+ public static final String s2739 = "2739";
+ public static final String s2740 = "2740";
+ public static final String s2741 = "2741";
+ public static final String s2742 = "2742";
+ public static final String s2743 = "2743";
+ public static final String s2744 = "2744";
+ public static final String s2745 = "2745";
+ public static final String s2746 = "2746";
+ public static final String s2747 = "2747";
+ public static final String s2748 = "2748";
+ public static final String s2749 = "2749";
+ public static final String s2750 = "2750";
+ public static final String s2751 = "2751";
+ public static final String s2752 = "2752";
+ public static final String s2753 = "2753";
+ public static final String s2754 = "2754";
+ public static final String s2755 = "2755";
+ public static final String s2756 = "2756";
+ public static final String s2757 = "2757";
+ public static final String s2758 = "2758";
+ public static final String s2759 = "2759";
+ public static final String s2760 = "2760";
+ public static final String s2761 = "2761";
+ public static final String s2762 = "2762";
+ public static final String s2763 = "2763";
+ public static final String s2764 = "2764";
+ public static final String s2765 = "2765";
+ public static final String s2766 = "2766";
+ public static final String s2767 = "2767";
+ public static final String s2768 = "2768";
+ public static final String s2769 = "2769";
+ public static final String s2770 = "2770";
+ public static final String s2771 = "2771";
+ public static final String s2772 = "2772";
+ public static final String s2773 = "2773";
+ public static final String s2774 = "2774";
+ public static final String s2775 = "2775";
+ public static final String s2776 = "2776";
+ public static final String s2777 = "2777";
+ public static final String s2778 = "2778";
+ public static final String s2779 = "2779";
+ public static final String s2780 = "2780";
+ public static final String s2781 = "2781";
+ public static final String s2782 = "2782";
+ public static final String s2783 = "2783";
+ public static final String s2784 = "2784";
+ public static final String s2785 = "2785";
+ public static final String s2786 = "2786";
+ public static final String s2787 = "2787";
+ public static final String s2788 = "2788";
+ public static final String s2789 = "2789";
+ public static final String s2790 = "2790";
+ public static final String s2791 = "2791";
+ public static final String s2792 = "2792";
+ public static final String s2793 = "2793";
+ public static final String s2794 = "2794";
+ public static final String s2795 = "2795";
+ public static final String s2796 = "2796";
+ public static final String s2797 = "2797";
+ public static final String s2798 = "2798";
+ public static final String s2799 = "2799";
+ public static final String s2800 = "2800";
+ public static final String s2801 = "2801";
+ public static final String s2802 = "2802";
+ public static final String s2803 = "2803";
+ public static final String s2804 = "2804";
+ public static final String s2805 = "2805";
+ public static final String s2806 = "2806";
+ public static final String s2807 = "2807";
+ public static final String s2808 = "2808";
+ public static final String s2809 = "2809";
+ public static final String s2810 = "2810";
+ public static final String s2811 = "2811";
+ public static final String s2812 = "2812";
+ public static final String s2813 = "2813";
+ public static final String s2814 = "2814";
+ public static final String s2815 = "2815";
+ public static final String s2816 = "2816";
+ public static final String s2817 = "2817";
+ public static final String s2818 = "2818";
+ public static final String s2819 = "2819";
+ public static final String s2820 = "2820";
+ public static final String s2821 = "2821";
+ public static final String s2822 = "2822";
+ public static final String s2823 = "2823";
+ public static final String s2824 = "2824";
+ public static final String s2825 = "2825";
+ public static final String s2826 = "2826";
+ public static final String s2827 = "2827";
+ public static final String s2828 = "2828";
+ public static final String s2829 = "2829";
+ public static final String s2830 = "2830";
+ public static final String s2831 = "2831";
+ public static final String s2832 = "2832";
+ public static final String s2833 = "2833";
+ public static final String s2834 = "2834";
+ public static final String s2835 = "2835";
+ public static final String s2836 = "2836";
+ public static final String s2837 = "2837";
+ public static final String s2838 = "2838";
+ public static final String s2839 = "2839";
+ public static final String s2840 = "2840";
+ public static final String s2841 = "2841";
+ public static final String s2842 = "2842";
+ public static final String s2843 = "2843";
+ public static final String s2844 = "2844";
+ public static final String s2845 = "2845";
+ public static final String s2846 = "2846";
+ public static final String s2847 = "2847";
+ public static final String s2848 = "2848";
+ public static final String s2849 = "2849";
+ public static final String s2850 = "2850";
+ public static final String s2851 = "2851";
+ public static final String s2852 = "2852";
+ public static final String s2853 = "2853";
+ public static final String s2854 = "2854";
+ public static final String s2855 = "2855";
+ public static final String s2856 = "2856";
+ public static final String s2857 = "2857";
+ public static final String s2858 = "2858";
+ public static final String s2859 = "2859";
+ public static final String s2860 = "2860";
+ public static final String s2861 = "2861";
+ public static final String s2862 = "2862";
+ public static final String s2863 = "2863";
+ public static final String s2864 = "2864";
+ public static final String s2865 = "2865";
+ public static final String s2866 = "2866";
+ public static final String s2867 = "2867";
+ public static final String s2868 = "2868";
+ public static final String s2869 = "2869";
+ public static final String s2870 = "2870";
+ public static final String s2871 = "2871";
+ public static final String s2872 = "2872";
+ public static final String s2873 = "2873";
+ public static final String s2874 = "2874";
+ public static final String s2875 = "2875";
+ public static final String s2876 = "2876";
+ public static final String s2877 = "2877";
+ public static final String s2878 = "2878";
+ public static final String s2879 = "2879";
+ public static final String s2880 = "2880";
+ public static final String s2881 = "2881";
+ public static final String s2882 = "2882";
+ public static final String s2883 = "2883";
+ public static final String s2884 = "2884";
+ public static final String s2885 = "2885";
+ public static final String s2886 = "2886";
+ public static final String s2887 = "2887";
+ public static final String s2888 = "2888";
+ public static final String s2889 = "2889";
+ public static final String s2890 = "2890";
+ public static final String s2891 = "2891";
+ public static final String s2892 = "2892";
+ public static final String s2893 = "2893";
+ public static final String s2894 = "2894";
+ public static final String s2895 = "2895";
+ public static final String s2896 = "2896";
+ public static final String s2897 = "2897";
+ public static final String s2898 = "2898";
+ public static final String s2899 = "2899";
+ public static final String s2900 = "2900";
+ public static final String s2901 = "2901";
+ public static final String s2902 = "2902";
+ public static final String s2903 = "2903";
+ public static final String s2904 = "2904";
+ public static final String s2905 = "2905";
+ public static final String s2906 = "2906";
+ public static final String s2907 = "2907";
+ public static final String s2908 = "2908";
+ public static final String s2909 = "2909";
+ public static final String s2910 = "2910";
+ public static final String s2911 = "2911";
+ public static final String s2912 = "2912";
+ public static final String s2913 = "2913";
+ public static final String s2914 = "2914";
+ public static final String s2915 = "2915";
+ public static final String s2916 = "2916";
+ public static final String s2917 = "2917";
+ public static final String s2918 = "2918";
+ public static final String s2919 = "2919";
+ public static final String s2920 = "2920";
+ public static final String s2921 = "2921";
+ public static final String s2922 = "2922";
+ public static final String s2923 = "2923";
+ public static final String s2924 = "2924";
+ public static final String s2925 = "2925";
+ public static final String s2926 = "2926";
+ public static final String s2927 = "2927";
+ public static final String s2928 = "2928";
+ public static final String s2929 = "2929";
+ public static final String s2930 = "2930";
+ public static final String s2931 = "2931";
+ public static final String s2932 = "2932";
+ public static final String s2933 = "2933";
+ public static final String s2934 = "2934";
+ public static final String s2935 = "2935";
+ public static final String s2936 = "2936";
+ public static final String s2937 = "2937";
+ public static final String s2938 = "2938";
+ public static final String s2939 = "2939";
+ public static final String s2940 = "2940";
+ public static final String s2941 = "2941";
+ public static final String s2942 = "2942";
+ public static final String s2943 = "2943";
+ public static final String s2944 = "2944";
+ public static final String s2945 = "2945";
+ public static final String s2946 = "2946";
+ public static final String s2947 = "2947";
+ public static final String s2948 = "2948";
+ public static final String s2949 = "2949";
+ public static final String s2950 = "2950";
+ public static final String s2951 = "2951";
+ public static final String s2952 = "2952";
+ public static final String s2953 = "2953";
+ public static final String s2954 = "2954";
+ public static final String s2955 = "2955";
+ public static final String s2956 = "2956";
+ public static final String s2957 = "2957";
+ public static final String s2958 = "2958";
+ public static final String s2959 = "2959";
+ public static final String s2960 = "2960";
+ public static final String s2961 = "2961";
+ public static final String s2962 = "2962";
+ public static final String s2963 = "2963";
+ public static final String s2964 = "2964";
+ public static final String s2965 = "2965";
+ public static final String s2966 = "2966";
+ public static final String s2967 = "2967";
+ public static final String s2968 = "2968";
+ public static final String s2969 = "2969";
+ public static final String s2970 = "2970";
+ public static final String s2971 = "2971";
+ public static final String s2972 = "2972";
+ public static final String s2973 = "2973";
+ public static final String s2974 = "2974";
+ public static final String s2975 = "2975";
+ public static final String s2976 = "2976";
+ public static final String s2977 = "2977";
+ public static final String s2978 = "2978";
+ public static final String s2979 = "2979";
+ public static final String s2980 = "2980";
+ public static final String s2981 = "2981";
+ public static final String s2982 = "2982";
+ public static final String s2983 = "2983";
+ public static final String s2984 = "2984";
+ public static final String s2985 = "2985";
+ public static final String s2986 = "2986";
+ public static final String s2987 = "2987";
+ public static final String s2988 = "2988";
+ public static final String s2989 = "2989";
+ public static final String s2990 = "2990";
+ public static final String s2991 = "2991";
+ public static final String s2992 = "2992";
+ public static final String s2993 = "2993";
+ public static final String s2994 = "2994";
+ public static final String s2995 = "2995";
+ public static final String s2996 = "2996";
+ public static final String s2997 = "2997";
+ public static final String s2998 = "2998";
+ public static final String s2999 = "2999";
+ public static final String s3000 = "3000";
+ public static final String s3001 = "3001";
+ public static final String s3002 = "3002";
+ public static final String s3003 = "3003";
+ public static final String s3004 = "3004";
+ public static final String s3005 = "3005";
+ public static final String s3006 = "3006";
+ public static final String s3007 = "3007";
+ public static final String s3008 = "3008";
+ public static final String s3009 = "3009";
+ public static final String s3010 = "3010";
+ public static final String s3011 = "3011";
+ public static final String s3012 = "3012";
+ public static final String s3013 = "3013";
+ public static final String s3014 = "3014";
+ public static final String s3015 = "3015";
+ public static final String s3016 = "3016";
+ public static final String s3017 = "3017";
+ public static final String s3018 = "3018";
+ public static final String s3019 = "3019";
+ public static final String s3020 = "3020";
+ public static final String s3021 = "3021";
+ public static final String s3022 = "3022";
+ public static final String s3023 = "3023";
+ public static final String s3024 = "3024";
+ public static final String s3025 = "3025";
+ public static final String s3026 = "3026";
+ public static final String s3027 = "3027";
+ public static final String s3028 = "3028";
+ public static final String s3029 = "3029";
+ public static final String s3030 = "3030";
+ public static final String s3031 = "3031";
+ public static final String s3032 = "3032";
+ public static final String s3033 = "3033";
+ public static final String s3034 = "3034";
+ public static final String s3035 = "3035";
+ public static final String s3036 = "3036";
+ public static final String s3037 = "3037";
+ public static final String s3038 = "3038";
+ public static final String s3039 = "3039";
+ public static final String s3040 = "3040";
+ public static final String s3041 = "3041";
+ public static final String s3042 = "3042";
+ public static final String s3043 = "3043";
+ public static final String s3044 = "3044";
+ public static final String s3045 = "3045";
+ public static final String s3046 = "3046";
+ public static final String s3047 = "3047";
+ public static final String s3048 = "3048";
+ public static final String s3049 = "3049";
+ public static final String s3050 = "3050";
+ public static final String s3051 = "3051";
+ public static final String s3052 = "3052";
+ public static final String s3053 = "3053";
+ public static final String s3054 = "3054";
+ public static final String s3055 = "3055";
+ public static final String s3056 = "3056";
+ public static final String s3057 = "3057";
+ public static final String s3058 = "3058";
+ public static final String s3059 = "3059";
+ public static final String s3060 = "3060";
+ public static final String s3061 = "3061";
+ public static final String s3062 = "3062";
+ public static final String s3063 = "3063";
+ public static final String s3064 = "3064";
+ public static final String s3065 = "3065";
+ public static final String s3066 = "3066";
+ public static final String s3067 = "3067";
+ public static final String s3068 = "3068";
+ public static final String s3069 = "3069";
+ public static final String s3070 = "3070";
+ public static final String s3071 = "3071";
+ public static final String s3072 = "3072";
+ public static final String s3073 = "3073";
+ public static final String s3074 = "3074";
+ public static final String s3075 = "3075";
+ public static final String s3076 = "3076";
+ public static final String s3077 = "3077";
+ public static final String s3078 = "3078";
+ public static final String s3079 = "3079";
+ public static final String s3080 = "3080";
+ public static final String s3081 = "3081";
+ public static final String s3082 = "3082";
+ public static final String s3083 = "3083";
+ public static final String s3084 = "3084";
+ public static final String s3085 = "3085";
+ public static final String s3086 = "3086";
+ public static final String s3087 = "3087";
+ public static final String s3088 = "3088";
+ public static final String s3089 = "3089";
+ public static final String s3090 = "3090";
+ public static final String s3091 = "3091";
+ public static final String s3092 = "3092";
+ public static final String s3093 = "3093";
+ public static final String s3094 = "3094";
+ public static final String s3095 = "3095";
+ public static final String s3096 = "3096";
+ public static final String s3097 = "3097";
+ public static final String s3098 = "3098";
+ public static final String s3099 = "3099";
+ public static final String s3100 = "3100";
+ public static final String s3101 = "3101";
+ public static final String s3102 = "3102";
+ public static final String s3103 = "3103";
+ public static final String s3104 = "3104";
+ public static final String s3105 = "3105";
+ public static final String s3106 = "3106";
+ public static final String s3107 = "3107";
+ public static final String s3108 = "3108";
+ public static final String s3109 = "3109";
+ public static final String s3110 = "3110";
+ public static final String s3111 = "3111";
+ public static final String s3112 = "3112";
+ public static final String s3113 = "3113";
+ public static final String s3114 = "3114";
+ public static final String s3115 = "3115";
+ public static final String s3116 = "3116";
+ public static final String s3117 = "3117";
+ public static final String s3118 = "3118";
+ public static final String s3119 = "3119";
+ public static final String s3120 = "3120";
+ public static final String s3121 = "3121";
+ public static final String s3122 = "3122";
+ public static final String s3123 = "3123";
+ public static final String s3124 = "3124";
+ public static final String s3125 = "3125";
+ public static final String s3126 = "3126";
+ public static final String s3127 = "3127";
+ public static final String s3128 = "3128";
+ public static final String s3129 = "3129";
+ public static final String s3130 = "3130";
+ public static final String s3131 = "3131";
+ public static final String s3132 = "3132";
+ public static final String s3133 = "3133";
+ public static final String s3134 = "3134";
+ public static final String s3135 = "3135";
+ public static final String s3136 = "3136";
+ public static final String s3137 = "3137";
+ public static final String s3138 = "3138";
+ public static final String s3139 = "3139";
+ public static final String s3140 = "3140";
+ public static final String s3141 = "3141";
+ public static final String s3142 = "3142";
+ public static final String s3143 = "3143";
+ public static final String s3144 = "3144";
+ public static final String s3145 = "3145";
+ public static final String s3146 = "3146";
+ public static final String s3147 = "3147";
+ public static final String s3148 = "3148";
+ public static final String s3149 = "3149";
+ public static final String s3150 = "3150";
+ public static final String s3151 = "3151";
+ public static final String s3152 = "3152";
+ public static final String s3153 = "3153";
+ public static final String s3154 = "3154";
+ public static final String s3155 = "3155";
+ public static final String s3156 = "3156";
+ public static final String s3157 = "3157";
+ public static final String s3158 = "3158";
+ public static final String s3159 = "3159";
+ public static final String s3160 = "3160";
+ public static final String s3161 = "3161";
+ public static final String s3162 = "3162";
+ public static final String s3163 = "3163";
+ public static final String s3164 = "3164";
+ public static final String s3165 = "3165";
+ public static final String s3166 = "3166";
+ public static final String s3167 = "3167";
+ public static final String s3168 = "3168";
+ public static final String s3169 = "3169";
+ public static final String s3170 = "3170";
+ public static final String s3171 = "3171";
+ public static final String s3172 = "3172";
+ public static final String s3173 = "3173";
+ public static final String s3174 = "3174";
+ public static final String s3175 = "3175";
+ public static final String s3176 = "3176";
+ public static final String s3177 = "3177";
+ public static final String s3178 = "3178";
+ public static final String s3179 = "3179";
+ public static final String s3180 = "3180";
+ public static final String s3181 = "3181";
+ public static final String s3182 = "3182";
+ public static final String s3183 = "3183";
+ public static final String s3184 = "3184";
+ public static final String s3185 = "3185";
+ public static final String s3186 = "3186";
+ public static final String s3187 = "3187";
+ public static final String s3188 = "3188";
+ public static final String s3189 = "3189";
+ public static final String s3190 = "3190";
+ public static final String s3191 = "3191";
+ public static final String s3192 = "3192";
+ public static final String s3193 = "3193";
+ public static final String s3194 = "3194";
+ public static final String s3195 = "3195";
+ public static final String s3196 = "3196";
+ public static final String s3197 = "3197";
+ public static final String s3198 = "3198";
+ public static final String s3199 = "3199";
+ public static final String s3200 = "3200";
+ public static final String s3201 = "3201";
+ public static final String s3202 = "3202";
+ public static final String s3203 = "3203";
+ public static final String s3204 = "3204";
+ public static final String s3205 = "3205";
+ public static final String s3206 = "3206";
+ public static final String s3207 = "3207";
+ public static final String s3208 = "3208";
+ public static final String s3209 = "3209";
+ public static final String s3210 = "3210";
+ public static final String s3211 = "3211";
+ public static final String s3212 = "3212";
+ public static final String s3213 = "3213";
+ public static final String s3214 = "3214";
+ public static final String s3215 = "3215";
+ public static final String s3216 = "3216";
+ public static final String s3217 = "3217";
+ public static final String s3218 = "3218";
+ public static final String s3219 = "3219";
+ public static final String s3220 = "3220";
+ public static final String s3221 = "3221";
+ public static final String s3222 = "3222";
+ public static final String s3223 = "3223";
+ public static final String s3224 = "3224";
+ public static final String s3225 = "3225";
+ public static final String s3226 = "3226";
+ public static final String s3227 = "3227";
+ public static final String s3228 = "3228";
+ public static final String s3229 = "3229";
+ public static final String s3230 = "3230";
+ public static final String s3231 = "3231";
+ public static final String s3232 = "3232";
+ public static final String s3233 = "3233";
+ public static final String s3234 = "3234";
+ public static final String s3235 = "3235";
+ public static final String s3236 = "3236";
+ public static final String s3237 = "3237";
+ public static final String s3238 = "3238";
+ public static final String s3239 = "3239";
+ public static final String s3240 = "3240";
+ public static final String s3241 = "3241";
+ public static final String s3242 = "3242";
+ public static final String s3243 = "3243";
+ public static final String s3244 = "3244";
+ public static final String s3245 = "3245";
+ public static final String s3246 = "3246";
+ public static final String s3247 = "3247";
+ public static final String s3248 = "3248";
+ public static final String s3249 = "3249";
+ public static final String s3250 = "3250";
+ public static final String s3251 = "3251";
+ public static final String s3252 = "3252";
+ public static final String s3253 = "3253";
+ public static final String s3254 = "3254";
+ public static final String s3255 = "3255";
+ public static final String s3256 = "3256";
+ public static final String s3257 = "3257";
+ public static final String s3258 = "3258";
+ public static final String s3259 = "3259";
+ public static final String s3260 = "3260";
+ public static final String s3261 = "3261";
+ public static final String s3262 = "3262";
+ public static final String s3263 = "3263";
+ public static final String s3264 = "3264";
+ public static final String s3265 = "3265";
+ public static final String s3266 = "3266";
+ public static final String s3267 = "3267";
+ public static final String s3268 = "3268";
+ public static final String s3269 = "3269";
+ public static final String s3270 = "3270";
+ public static final String s3271 = "3271";
+ public static final String s3272 = "3272";
+ public static final String s3273 = "3273";
+ public static final String s3274 = "3274";
+ public static final String s3275 = "3275";
+ public static final String s3276 = "3276";
+ public static final String s3277 = "3277";
+ public static final String s3278 = "3278";
+ public static final String s3279 = "3279";
+ public static final String s3280 = "3280";
+ public static final String s3281 = "3281";
+ public static final String s3282 = "3282";
+ public static final String s3283 = "3283";
+ public static final String s3284 = "3284";
+ public static final String s3285 = "3285";
+ public static final String s3286 = "3286";
+ public static final String s3287 = "3287";
+ public static final String s3288 = "3288";
+ public static final String s3289 = "3289";
+ public static final String s3290 = "3290";
+ public static final String s3291 = "3291";
+ public static final String s3292 = "3292";
+ public static final String s3293 = "3293";
+ public static final String s3294 = "3294";
+ public static final String s3295 = "3295";
+ public static final String s3296 = "3296";
+ public static final String s3297 = "3297";
+ public static final String s3298 = "3298";
+ public static final String s3299 = "3299";
+ public static final String s3300 = "3300";
+ public static final String s3301 = "3301";
+ public static final String s3302 = "3302";
+ public static final String s3303 = "3303";
+ public static final String s3304 = "3304";
+ public static final String s3305 = "3305";
+ public static final String s3306 = "3306";
+ public static final String s3307 = "3307";
+ public static final String s3308 = "3308";
+ public static final String s3309 = "3309";
+ public static final String s3310 = "3310";
+ public static final String s3311 = "3311";
+ public static final String s3312 = "3312";
+ public static final String s3313 = "3313";
+ public static final String s3314 = "3314";
+ public static final String s3315 = "3315";
+ public static final String s3316 = "3316";
+ public static final String s3317 = "3317";
+ public static final String s3318 = "3318";
+ public static final String s3319 = "3319";
+ public static final String s3320 = "3320";
+ public static final String s3321 = "3321";
+ public static final String s3322 = "3322";
+ public static final String s3323 = "3323";
+ public static final String s3324 = "3324";
+ public static final String s3325 = "3325";
+ public static final String s3326 = "3326";
+ public static final String s3327 = "3327";
+ public static final String s3328 = "3328";
+ public static final String s3329 = "3329";
+ public static final String s3330 = "3330";
+ public static final String s3331 = "3331";
+ public static final String s3332 = "3332";
+ public static final String s3333 = "3333";
+ public static final String s3334 = "3334";
+ public static final String s3335 = "3335";
+ public static final String s3336 = "3336";
+ public static final String s3337 = "3337";
+ public static final String s3338 = "3338";
+ public static final String s3339 = "3339";
+ public static final String s3340 = "3340";
+ public static final String s3341 = "3341";
+ public static final String s3342 = "3342";
+ public static final String s3343 = "3343";
+ public static final String s3344 = "3344";
+ public static final String s3345 = "3345";
+ public static final String s3346 = "3346";
+ public static final String s3347 = "3347";
+ public static final String s3348 = "3348";
+ public static final String s3349 = "3349";
+ public static final String s3350 = "3350";
+ public static final String s3351 = "3351";
+ public static final String s3352 = "3352";
+ public static final String s3353 = "3353";
+ public static final String s3354 = "3354";
+ public static final String s3355 = "3355";
+ public static final String s3356 = "3356";
+ public static final String s3357 = "3357";
+ public static final String s3358 = "3358";
+ public static final String s3359 = "3359";
+ public static final String s3360 = "3360";
+ public static final String s3361 = "3361";
+ public static final String s3362 = "3362";
+ public static final String s3363 = "3363";
+ public static final String s3364 = "3364";
+ public static final String s3365 = "3365";
+ public static final String s3366 = "3366";
+ public static final String s3367 = "3367";
+ public static final String s3368 = "3368";
+ public static final String s3369 = "3369";
+ public static final String s3370 = "3370";
+ public static final String s3371 = "3371";
+ public static final String s3372 = "3372";
+ public static final String s3373 = "3373";
+ public static final String s3374 = "3374";
+ public static final String s3375 = "3375";
+ public static final String s3376 = "3376";
+ public static final String s3377 = "3377";
+ public static final String s3378 = "3378";
+ public static final String s3379 = "3379";
+ public static final String s3380 = "3380";
+ public static final String s3381 = "3381";
+ public static final String s3382 = "3382";
+ public static final String s3383 = "3383";
+ public static final String s3384 = "3384";
+ public static final String s3385 = "3385";
+ public static final String s3386 = "3386";
+ public static final String s3387 = "3387";
+ public static final String s3388 = "3388";
+ public static final String s3389 = "3389";
+ public static final String s3390 = "3390";
+ public static final String s3391 = "3391";
+ public static final String s3392 = "3392";
+ public static final String s3393 = "3393";
+ public static final String s3394 = "3394";
+ public static final String s3395 = "3395";
+ public static final String s3396 = "3396";
+ public static final String s3397 = "3397";
+ public static final String s3398 = "3398";
+ public static final String s3399 = "3399";
+ public static final String s3400 = "3400";
+ public static final String s3401 = "3401";
+ public static final String s3402 = "3402";
+ public static final String s3403 = "3403";
+ public static final String s3404 = "3404";
+ public static final String s3405 = "3405";
+ public static final String s3406 = "3406";
+ public static final String s3407 = "3407";
+ public static final String s3408 = "3408";
+ public static final String s3409 = "3409";
+ public static final String s3410 = "3410";
+ public static final String s3411 = "3411";
+ public static final String s3412 = "3412";
+ public static final String s3413 = "3413";
+ public static final String s3414 = "3414";
+ public static final String s3415 = "3415";
+ public static final String s3416 = "3416";
+ public static final String s3417 = "3417";
+ public static final String s3418 = "3418";
+ public static final String s3419 = "3419";
+ public static final String s3420 = "3420";
+ public static final String s3421 = "3421";
+ public static final String s3422 = "3422";
+ public static final String s3423 = "3423";
+ public static final String s3424 = "3424";
+ public static final String s3425 = "3425";
+ public static final String s3426 = "3426";
+ public static final String s3427 = "3427";
+ public static final String s3428 = "3428";
+ public static final String s3429 = "3429";
+ public static final String s3430 = "3430";
+ public static final String s3431 = "3431";
+ public static final String s3432 = "3432";
+ public static final String s3433 = "3433";
+ public static final String s3434 = "3434";
+ public static final String s3435 = "3435";
+ public static final String s3436 = "3436";
+ public static final String s3437 = "3437";
+ public static final String s3438 = "3438";
+ public static final String s3439 = "3439";
+ public static final String s3440 = "3440";
+ public static final String s3441 = "3441";
+ public static final String s3442 = "3442";
+ public static final String s3443 = "3443";
+ public static final String s3444 = "3444";
+ public static final String s3445 = "3445";
+ public static final String s3446 = "3446";
+ public static final String s3447 = "3447";
+ public static final String s3448 = "3448";
+ public static final String s3449 = "3449";
+ public static final String s3450 = "3450";
+ public static final String s3451 = "3451";
+ public static final String s3452 = "3452";
+ public static final String s3453 = "3453";
+ public static final String s3454 = "3454";
+ public static final String s3455 = "3455";
+ public static final String s3456 = "3456";
+ public static final String s3457 = "3457";
+ public static final String s3458 = "3458";
+ public static final String s3459 = "3459";
+ public static final String s3460 = "3460";
+ public static final String s3461 = "3461";
+ public static final String s3462 = "3462";
+ public static final String s3463 = "3463";
+ public static final String s3464 = "3464";
+ public static final String s3465 = "3465";
+ public static final String s3466 = "3466";
+ public static final String s3467 = "3467";
+ public static final String s3468 = "3468";
+ public static final String s3469 = "3469";
+ public static final String s3470 = "3470";
+ public static final String s3471 = "3471";
+ public static final String s3472 = "3472";
+ public static final String s3473 = "3473";
+ public static final String s3474 = "3474";
+ public static final String s3475 = "3475";
+ public static final String s3476 = "3476";
+ public static final String s3477 = "3477";
+ public static final String s3478 = "3478";
+ public static final String s3479 = "3479";
+ public static final String s3480 = "3480";
+ public static final String s3481 = "3481";
+ public static final String s3482 = "3482";
+ public static final String s3483 = "3483";
+ public static final String s3484 = "3484";
+ public static final String s3485 = "3485";
+ public static final String s3486 = "3486";
+ public static final String s3487 = "3487";
+ public static final String s3488 = "3488";
+ public static final String s3489 = "3489";
+ public static final String s3490 = "3490";
+ public static final String s3491 = "3491";
+ public static final String s3492 = "3492";
+ public static final String s3493 = "3493";
+ public static final String s3494 = "3494";
+ public static final String s3495 = "3495";
+ public static final String s3496 = "3496";
+ public static final String s3497 = "3497";
+ public static final String s3498 = "3498";
+ public static final String s3499 = "3499";
+ public static final String s3500 = "3500";
+ public static final String s3501 = "3501";
+ public static final String s3502 = "3502";
+ public static final String s3503 = "3503";
+ public static final String s3504 = "3504";
+ public static final String s3505 = "3505";
+ public static final String s3506 = "3506";
+ public static final String s3507 = "3507";
+ public static final String s3508 = "3508";
+ public static final String s3509 = "3509";
+ public static final String s3510 = "3510";
+ public static final String s3511 = "3511";
+ public static final String s3512 = "3512";
+ public static final String s3513 = "3513";
+ public static final String s3514 = "3514";
+ public static final String s3515 = "3515";
+ public static final String s3516 = "3516";
+ public static final String s3517 = "3517";
+ public static final String s3518 = "3518";
+ public static final String s3519 = "3519";
+ public static final String s3520 = "3520";
+ public static final String s3521 = "3521";
+ public static final String s3522 = "3522";
+ public static final String s3523 = "3523";
+ public static final String s3524 = "3524";
+ public static final String s3525 = "3525";
+ public static final String s3526 = "3526";
+ public static final String s3527 = "3527";
+ public static final String s3528 = "3528";
+ public static final String s3529 = "3529";
+ public static final String s3530 = "3530";
+ public static final String s3531 = "3531";
+ public static final String s3532 = "3532";
+ public static final String s3533 = "3533";
+ public static final String s3534 = "3534";
+ public static final String s3535 = "3535";
+ public static final String s3536 = "3536";
+ public static final String s3537 = "3537";
+ public static final String s3538 = "3538";
+ public static final String s3539 = "3539";
+ public static final String s3540 = "3540";
+ public static final String s3541 = "3541";
+ public static final String s3542 = "3542";
+ public static final String s3543 = "3543";
+ public static final String s3544 = "3544";
+ public static final String s3545 = "3545";
+ public static final String s3546 = "3546";
+ public static final String s3547 = "3547";
+ public static final String s3548 = "3548";
+ public static final String s3549 = "3549";
+ public static final String s3550 = "3550";
+ public static final String s3551 = "3551";
+ public static final String s3552 = "3552";
+ public static final String s3553 = "3553";
+ public static final String s3554 = "3554";
+ public static final String s3555 = "3555";
+ public static final String s3556 = "3556";
+ public static final String s3557 = "3557";
+ public static final String s3558 = "3558";
+ public static final String s3559 = "3559";
+ public static final String s3560 = "3560";
+ public static final String s3561 = "3561";
+ public static final String s3562 = "3562";
+ public static final String s3563 = "3563";
+ public static final String s3564 = "3564";
+ public static final String s3565 = "3565";
+ public static final String s3566 = "3566";
+ public static final String s3567 = "3567";
+ public static final String s3568 = "3568";
+ public static final String s3569 = "3569";
+ public static final String s3570 = "3570";
+ public static final String s3571 = "3571";
+ public static final String s3572 = "3572";
+ public static final String s3573 = "3573";
+ public static final String s3574 = "3574";
+ public static final String s3575 = "3575";
+ public static final String s3576 = "3576";
+ public static final String s3577 = "3577";
+ public static final String s3578 = "3578";
+ public static final String s3579 = "3579";
+ public static final String s3580 = "3580";
+ public static final String s3581 = "3581";
+ public static final String s3582 = "3582";
+ public static final String s3583 = "3583";
+ public static final String s3584 = "3584";
+ public static final String s3585 = "3585";
+ public static final String s3586 = "3586";
+ public static final String s3587 = "3587";
+ public static final String s3588 = "3588";
+ public static final String s3589 = "3589";
+ public static final String s3590 = "3590";
+ public static final String s3591 = "3591";
+ public static final String s3592 = "3592";
+ public static final String s3593 = "3593";
+ public static final String s3594 = "3594";
+ public static final String s3595 = "3595";
+ public static final String s3596 = "3596";
+ public static final String s3597 = "3597";
+ public static final String s3598 = "3598";
+ public static final String s3599 = "3599";
+ public static final String s3600 = "3600";
+ public static final String s3601 = "3601";
+ public static final String s3602 = "3602";
+ public static final String s3603 = "3603";
+ public static final String s3604 = "3604";
+ public static final String s3605 = "3605";
+ public static final String s3606 = "3606";
+ public static final String s3607 = "3607";
+ public static final String s3608 = "3608";
+ public static final String s3609 = "3609";
+ public static final String s3610 = "3610";
+ public static final String s3611 = "3611";
+ public static final String s3612 = "3612";
+ public static final String s3613 = "3613";
+ public static final String s3614 = "3614";
+ public static final String s3615 = "3615";
+ public static final String s3616 = "3616";
+ public static final String s3617 = "3617";
+ public static final String s3618 = "3618";
+ public static final String s3619 = "3619";
+ public static final String s3620 = "3620";
+ public static final String s3621 = "3621";
+ public static final String s3622 = "3622";
+ public static final String s3623 = "3623";
+ public static final String s3624 = "3624";
+ public static final String s3625 = "3625";
+ public static final String s3626 = "3626";
+ public static final String s3627 = "3627";
+ public static final String s3628 = "3628";
+ public static final String s3629 = "3629";
+ public static final String s3630 = "3630";
+ public static final String s3631 = "3631";
+ public static final String s3632 = "3632";
+ public static final String s3633 = "3633";
+ public static final String s3634 = "3634";
+ public static final String s3635 = "3635";
+ public static final String s3636 = "3636";
+ public static final String s3637 = "3637";
+ public static final String s3638 = "3638";
+ public static final String s3639 = "3639";
+ public static final String s3640 = "3640";
+ public static final String s3641 = "3641";
+ public static final String s3642 = "3642";
+ public static final String s3643 = "3643";
+ public static final String s3644 = "3644";
+ public static final String s3645 = "3645";
+ public static final String s3646 = "3646";
+ public static final String s3647 = "3647";
+ public static final String s3648 = "3648";
+ public static final String s3649 = "3649";
+ public static final String s3650 = "3650";
+ public static final String s3651 = "3651";
+ public static final String s3652 = "3652";
+ public static final String s3653 = "3653";
+ public static final String s3654 = "3654";
+ public static final String s3655 = "3655";
+ public static final String s3656 = "3656";
+ public static final String s3657 = "3657";
+ public static final String s3658 = "3658";
+ public static final String s3659 = "3659";
+ public static final String s3660 = "3660";
+ public static final String s3661 = "3661";
+ public static final String s3662 = "3662";
+ public static final String s3663 = "3663";
+ public static final String s3664 = "3664";
+ public static final String s3665 = "3665";
+ public static final String s3666 = "3666";
+ public static final String s3667 = "3667";
+ public static final String s3668 = "3668";
+ public static final String s3669 = "3669";
+ public static final String s3670 = "3670";
+ public static final String s3671 = "3671";
+ public static final String s3672 = "3672";
+ public static final String s3673 = "3673";
+ public static final String s3674 = "3674";
+ public static final String s3675 = "3675";
+ public static final String s3676 = "3676";
+ public static final String s3677 = "3677";
+ public static final String s3678 = "3678";
+ public static final String s3679 = "3679";
+ public static final String s3680 = "3680";
+ public static final String s3681 = "3681";
+ public static final String s3682 = "3682";
+ public static final String s3683 = "3683";
+ public static final String s3684 = "3684";
+ public static final String s3685 = "3685";
+ public static final String s3686 = "3686";
+ public static final String s3687 = "3687";
+ public static final String s3688 = "3688";
+ public static final String s3689 = "3689";
+ public static final String s3690 = "3690";
+ public static final String s3691 = "3691";
+ public static final String s3692 = "3692";
+ public static final String s3693 = "3693";
+ public static final String s3694 = "3694";
+ public static final String s3695 = "3695";
+ public static final String s3696 = "3696";
+ public static final String s3697 = "3697";
+ public static final String s3698 = "3698";
+ public static final String s3699 = "3699";
+ public static final String s3700 = "3700";
+ public static final String s3701 = "3701";
+ public static final String s3702 = "3702";
+ public static final String s3703 = "3703";
+ public static final String s3704 = "3704";
+ public static final String s3705 = "3705";
+ public static final String s3706 = "3706";
+ public static final String s3707 = "3707";
+ public static final String s3708 = "3708";
+ public static final String s3709 = "3709";
+ public static final String s3710 = "3710";
+ public static final String s3711 = "3711";
+ public static final String s3712 = "3712";
+ public static final String s3713 = "3713";
+ public static final String s3714 = "3714";
+ public static final String s3715 = "3715";
+ public static final String s3716 = "3716";
+ public static final String s3717 = "3717";
+ public static final String s3718 = "3718";
+ public static final String s3719 = "3719";
+ public static final String s3720 = "3720";
+ public static final String s3721 = "3721";
+ public static final String s3722 = "3722";
+ public static final String s3723 = "3723";
+ public static final String s3724 = "3724";
+ public static final String s3725 = "3725";
+ public static final String s3726 = "3726";
+ public static final String s3727 = "3727";
+ public static final String s3728 = "3728";
+ public static final String s3729 = "3729";
+ public static final String s3730 = "3730";
+ public static final String s3731 = "3731";
+ public static final String s3732 = "3732";
+ public static final String s3733 = "3733";
+ public static final String s3734 = "3734";
+ public static final String s3735 = "3735";
+ public static final String s3736 = "3736";
+ public static final String s3737 = "3737";
+ public static final String s3738 = "3738";
+ public static final String s3739 = "3739";
+ public static final String s3740 = "3740";
+ public static final String s3741 = "3741";
+ public static final String s3742 = "3742";
+ public static final String s3743 = "3743";
+ public static final String s3744 = "3744";
+ public static final String s3745 = "3745";
+ public static final String s3746 = "3746";
+ public static final String s3747 = "3747";
+ public static final String s3748 = "3748";
+ public static final String s3749 = "3749";
+ public static final String s3750 = "3750";
+ public static final String s3751 = "3751";
+ public static final String s3752 = "3752";
+ public static final String s3753 = "3753";
+ public static final String s3754 = "3754";
+ public static final String s3755 = "3755";
+ public static final String s3756 = "3756";
+ public static final String s3757 = "3757";
+ public static final String s3758 = "3758";
+ public static final String s3759 = "3759";
+ public static final String s3760 = "3760";
+ public static final String s3761 = "3761";
+ public static final String s3762 = "3762";
+ public static final String s3763 = "3763";
+ public static final String s3764 = "3764";
+ public static final String s3765 = "3765";
+ public static final String s3766 = "3766";
+ public static final String s3767 = "3767";
+ public static final String s3768 = "3768";
+ public static final String s3769 = "3769";
+ public static final String s3770 = "3770";
+ public static final String s3771 = "3771";
+ public static final String s3772 = "3772";
+ public static final String s3773 = "3773";
+ public static final String s3774 = "3774";
+ public static final String s3775 = "3775";
+ public static final String s3776 = "3776";
+ public static final String s3777 = "3777";
+ public static final String s3778 = "3778";
+ public static final String s3779 = "3779";
+ public static final String s3780 = "3780";
+ public static final String s3781 = "3781";
+ public static final String s3782 = "3782";
+ public static final String s3783 = "3783";
+ public static final String s3784 = "3784";
+ public static final String s3785 = "3785";
+ public static final String s3786 = "3786";
+ public static final String s3787 = "3787";
+ public static final String s3788 = "3788";
+ public static final String s3789 = "3789";
+ public static final String s3790 = "3790";
+ public static final String s3791 = "3791";
+ public static final String s3792 = "3792";
+ public static final String s3793 = "3793";
+ public static final String s3794 = "3794";
+ public static final String s3795 = "3795";
+ public static final String s3796 = "3796";
+ public static final String s3797 = "3797";
+ public static final String s3798 = "3798";
+ public static final String s3799 = "3799";
+ public static final String s3800 = "3800";
+ public static final String s3801 = "3801";
+ public static final String s3802 = "3802";
+ public static final String s3803 = "3803";
+ public static final String s3804 = "3804";
+ public static final String s3805 = "3805";
+ public static final String s3806 = "3806";
+ public static final String s3807 = "3807";
+ public static final String s3808 = "3808";
+ public static final String s3809 = "3809";
+ public static final String s3810 = "3810";
+ public static final String s3811 = "3811";
+ public static final String s3812 = "3812";
+ public static final String s3813 = "3813";
+ public static final String s3814 = "3814";
+ public static final String s3815 = "3815";
+ public static final String s3816 = "3816";
+ public static final String s3817 = "3817";
+ public static final String s3818 = "3818";
+ public static final String s3819 = "3819";
+ public static final String s3820 = "3820";
+ public static final String s3821 = "3821";
+ public static final String s3822 = "3822";
+ public static final String s3823 = "3823";
+ public static final String s3824 = "3824";
+ public static final String s3825 = "3825";
+ public static final String s3826 = "3826";
+ public static final String s3827 = "3827";
+ public static final String s3828 = "3828";
+ public static final String s3829 = "3829";
+ public static final String s3830 = "3830";
+ public static final String s3831 = "3831";
+ public static final String s3832 = "3832";
+ public static final String s3833 = "3833";
+ public static final String s3834 = "3834";
+ public static final String s3835 = "3835";
+ public static final String s3836 = "3836";
+ public static final String s3837 = "3837";
+ public static final String s3838 = "3838";
+ public static final String s3839 = "3839";
+ public static final String s3840 = "3840";
+ public static final String s3841 = "3841";
+ public static final String s3842 = "3842";
+ public static final String s3843 = "3843";
+ public static final String s3844 = "3844";
+ public static final String s3845 = "3845";
+ public static final String s3846 = "3846";
+ public static final String s3847 = "3847";
+ public static final String s3848 = "3848";
+ public static final String s3849 = "3849";
+ public static final String s3850 = "3850";
+ public static final String s3851 = "3851";
+ public static final String s3852 = "3852";
+ public static final String s3853 = "3853";
+ public static final String s3854 = "3854";
+ public static final String s3855 = "3855";
+ public static final String s3856 = "3856";
+ public static final String s3857 = "3857";
+ public static final String s3858 = "3858";
+ public static final String s3859 = "3859";
+ public static final String s3860 = "3860";
+ public static final String s3861 = "3861";
+ public static final String s3862 = "3862";
+ public static final String s3863 = "3863";
+ public static final String s3864 = "3864";
+ public static final String s3865 = "3865";
+ public static final String s3866 = "3866";
+ public static final String s3867 = "3867";
+ public static final String s3868 = "3868";
+ public static final String s3869 = "3869";
+ public static final String s3870 = "3870";
+ public static final String s3871 = "3871";
+ public static final String s3872 = "3872";
+ public static final String s3873 = "3873";
+ public static final String s3874 = "3874";
+ public static final String s3875 = "3875";
+ public static final String s3876 = "3876";
+ public static final String s3877 = "3877";
+ public static final String s3878 = "3878";
+ public static final String s3879 = "3879";
+ public static final String s3880 = "3880";
+ public static final String s3881 = "3881";
+ public static final String s3882 = "3882";
+ public static final String s3883 = "3883";
+ public static final String s3884 = "3884";
+ public static final String s3885 = "3885";
+ public static final String s3886 = "3886";
+ public static final String s3887 = "3887";
+ public static final String s3888 = "3888";
+ public static final String s3889 = "3889";
+ public static final String s3890 = "3890";
+ public static final String s3891 = "3891";
+ public static final String s3892 = "3892";
+ public static final String s3893 = "3893";
+ public static final String s3894 = "3894";
+ public static final String s3895 = "3895";
+ public static final String s3896 = "3896";
+ public static final String s3897 = "3897";
+ public static final String s3898 = "3898";
+ public static final String s3899 = "3899";
+ public static final String s3900 = "3900";
+ public static final String s3901 = "3901";
+ public static final String s3902 = "3902";
+ public static final String s3903 = "3903";
+ public static final String s3904 = "3904";
+ public static final String s3905 = "3905";
+ public static final String s3906 = "3906";
+ public static final String s3907 = "3907";
+ public static final String s3908 = "3908";
+ public static final String s3909 = "3909";
+ public static final String s3910 = "3910";
+ public static final String s3911 = "3911";
+ public static final String s3912 = "3912";
+ public static final String s3913 = "3913";
+ public static final String s3914 = "3914";
+ public static final String s3915 = "3915";
+ public static final String s3916 = "3916";
+ public static final String s3917 = "3917";
+ public static final String s3918 = "3918";
+ public static final String s3919 = "3919";
+ public static final String s3920 = "3920";
+ public static final String s3921 = "3921";
+ public static final String s3922 = "3922";
+ public static final String s3923 = "3923";
+ public static final String s3924 = "3924";
+ public static final String s3925 = "3925";
+ public static final String s3926 = "3926";
+ public static final String s3927 = "3927";
+ public static final String s3928 = "3928";
+ public static final String s3929 = "3929";
+ public static final String s3930 = "3930";
+ public static final String s3931 = "3931";
+ public static final String s3932 = "3932";
+ public static final String s3933 = "3933";
+ public static final String s3934 = "3934";
+ public static final String s3935 = "3935";
+ public static final String s3936 = "3936";
+ public static final String s3937 = "3937";
+ public static final String s3938 = "3938";
+ public static final String s3939 = "3939";
+ public static final String s3940 = "3940";
+ public static final String s3941 = "3941";
+ public static final String s3942 = "3942";
+ public static final String s3943 = "3943";
+ public static final String s3944 = "3944";
+ public static final String s3945 = "3945";
+ public static final String s3946 = "3946";
+ public static final String s3947 = "3947";
+ public static final String s3948 = "3948";
+ public static final String s3949 = "3949";
+ public static final String s3950 = "3950";
+ public static final String s3951 = "3951";
+ public static final String s3952 = "3952";
+ public static final String s3953 = "3953";
+ public static final String s3954 = "3954";
+ public static final String s3955 = "3955";
+ public static final String s3956 = "3956";
+ public static final String s3957 = "3957";
+ public static final String s3958 = "3958";
+ public static final String s3959 = "3959";
+ public static final String s3960 = "3960";
+ public static final String s3961 = "3961";
+ public static final String s3962 = "3962";
+ public static final String s3963 = "3963";
+ public static final String s3964 = "3964";
+ public static final String s3965 = "3965";
+ public static final String s3966 = "3966";
+ public static final String s3967 = "3967";
+ public static final String s3968 = "3968";
+ public static final String s3969 = "3969";
+ public static final String s3970 = "3970";
+ public static final String s3971 = "3971";
+ public static final String s3972 = "3972";
+ public static final String s3973 = "3973";
+ public static final String s3974 = "3974";
+ public static final String s3975 = "3975";
+ public static final String s3976 = "3976";
+ public static final String s3977 = "3977";
+ public static final String s3978 = "3978";
+ public static final String s3979 = "3979";
+ public static final String s3980 = "3980";
+ public static final String s3981 = "3981";
+ public static final String s3982 = "3982";
+ public static final String s3983 = "3983";
+ public static final String s3984 = "3984";
+ public static final String s3985 = "3985";
+ public static final String s3986 = "3986";
+ public static final String s3987 = "3987";
+ public static final String s3988 = "3988";
+ public static final String s3989 = "3989";
+ public static final String s3990 = "3990";
+ public static final String s3991 = "3991";
+ public static final String s3992 = "3992";
+ public static final String s3993 = "3993";
+ public static final String s3994 = "3994";
+ public static final String s3995 = "3995";
+ public static final String s3996 = "3996";
+ public static final String s3997 = "3997";
+ public static final String s3998 = "3998";
+ public static final String s3999 = "3999";
+ public static final String s4000 = "4000";
+ public static final String s4001 = "4001";
+ public static final String s4002 = "4002";
+ public static final String s4003 = "4003";
+ public static final String s4004 = "4004";
+ public static final String s4005 = "4005";
+ public static final String s4006 = "4006";
+ public static final String s4007 = "4007";
+ public static final String s4008 = "4008";
+ public static final String s4009 = "4009";
+ public static final String s4010 = "4010";
+ public static final String s4011 = "4011";
+ public static final String s4012 = "4012";
+ public static final String s4013 = "4013";
+ public static final String s4014 = "4014";
+ public static final String s4015 = "4015";
+ public static final String s4016 = "4016";
+ public static final String s4017 = "4017";
+ public static final String s4018 = "4018";
+ public static final String s4019 = "4019";
+ public static final String s4020 = "4020";
+ public static final String s4021 = "4021";
+ public static final String s4022 = "4022";
+ public static final String s4023 = "4023";
+ public static final String s4024 = "4024";
+ public static final String s4025 = "4025";
+ public static final String s4026 = "4026";
+ public static final String s4027 = "4027";
+ public static final String s4028 = "4028";
+ public static final String s4029 = "4029";
+ public static final String s4030 = "4030";
+ public static final String s4031 = "4031";
+ public static final String s4032 = "4032";
+ public static final String s4033 = "4033";
+ public static final String s4034 = "4034";
+ public static final String s4035 = "4035";
+ public static final String s4036 = "4036";
+ public static final String s4037 = "4037";
+ public static final String s4038 = "4038";
+ public static final String s4039 = "4039";
+ public static final String s4040 = "4040";
+ public static final String s4041 = "4041";
+ public static final String s4042 = "4042";
+ public static final String s4043 = "4043";
+ public static final String s4044 = "4044";
+ public static final String s4045 = "4045";
+ public static final String s4046 = "4046";
+ public static final String s4047 = "4047";
+ public static final String s4048 = "4048";
+ public static final String s4049 = "4049";
+ public static final String s4050 = "4050";
+ public static final String s4051 = "4051";
+ public static final String s4052 = "4052";
+ public static final String s4053 = "4053";
+ public static final String s4054 = "4054";
+ public static final String s4055 = "4055";
+ public static final String s4056 = "4056";
+ public static final String s4057 = "4057";
+ public static final String s4058 = "4058";
+ public static final String s4059 = "4059";
+ public static final String s4060 = "4060";
+ public static final String s4061 = "4061";
+ public static final String s4062 = "4062";
+ public static final String s4063 = "4063";
+ public static final String s4064 = "4064";
+ public static final String s4065 = "4065";
+ public static final String s4066 = "4066";
+ public static final String s4067 = "4067";
+ public static final String s4068 = "4068";
+ public static final String s4069 = "4069";
+ public static final String s4070 = "4070";
+ public static final String s4071 = "4071";
+ public static final String s4072 = "4072";
+ public static final String s4073 = "4073";
+ public static final String s4074 = "4074";
+ public static final String s4075 = "4075";
+ public static final String s4076 = "4076";
+ public static final String s4077 = "4077";
+ public static final String s4078 = "4078";
+ public static final String s4079 = "4079";
+ public static final String s4080 = "4080";
+ public static final String s4081 = "4081";
+ public static final String s4082 = "4082";
+ public static final String s4083 = "4083";
+ public static final String s4084 = "4084";
+ public static final String s4085 = "4085";
+ public static final String s4086 = "4086";
+ public static final String s4087 = "4087";
+ public static final String s4088 = "4088";
+ public static final String s4089 = "4089";
+ public static final String s4090 = "4090";
+ public static final String s4091 = "4091";
+ public static final String s4092 = "4092";
+ public static final String s4093 = "4093";
+ public static final String s4094 = "4094";
+ public static final String s4095 = "4095";
+ public static final String s4096 = "4096";
+ public static final String s4097 = "4097";
+ public static final String s4098 = "4098";
+ public static final String s4099 = "4099";
+ public static final String s4100 = "4100";
+ public static final String s4101 = "4101";
+ public static final String s4102 = "4102";
+ public static final String s4103 = "4103";
+ public static final String s4104 = "4104";
+ public static final String s4105 = "4105";
+ public static final String s4106 = "4106";
+ public static final String s4107 = "4107";
+ public static final String s4108 = "4108";
+ public static final String s4109 = "4109";
+ public static final String s4110 = "4110";
+ public static final String s4111 = "4111";
+ public static final String s4112 = "4112";
+ public static final String s4113 = "4113";
+ public static final String s4114 = "4114";
+ public static final String s4115 = "4115";
+ public static final String s4116 = "4116";
+ public static final String s4117 = "4117";
+ public static final String s4118 = "4118";
+ public static final String s4119 = "4119";
+ public static final String s4120 = "4120";
+ public static final String s4121 = "4121";
+ public static final String s4122 = "4122";
+ public static final String s4123 = "4123";
+ public static final String s4124 = "4124";
+ public static final String s4125 = "4125";
+ public static final String s4126 = "4126";
+ public static final String s4127 = "4127";
+ public static final String s4128 = "4128";
+ public static final String s4129 = "4129";
+ public static final String s4130 = "4130";
+ public static final String s4131 = "4131";
+ public static final String s4132 = "4132";
+ public static final String s4133 = "4133";
+ public static final String s4134 = "4134";
+ public static final String s4135 = "4135";
+ public static final String s4136 = "4136";
+ public static final String s4137 = "4137";
+ public static final String s4138 = "4138";
+ public static final String s4139 = "4139";
+ public static final String s4140 = "4140";
+ public static final String s4141 = "4141";
+ public static final String s4142 = "4142";
+ public static final String s4143 = "4143";
+ public static final String s4144 = "4144";
+ public static final String s4145 = "4145";
+ public static final String s4146 = "4146";
+ public static final String s4147 = "4147";
+ public static final String s4148 = "4148";
+ public static final String s4149 = "4149";
+ public static final String s4150 = "4150";
+ public static final String s4151 = "4151";
+ public static final String s4152 = "4152";
+ public static final String s4153 = "4153";
+ public static final String s4154 = "4154";
+ public static final String s4155 = "4155";
+ public static final String s4156 = "4156";
+ public static final String s4157 = "4157";
+ public static final String s4158 = "4158";
+ public static final String s4159 = "4159";
+ public static final String s4160 = "4160";
+ public static final String s4161 = "4161";
+ public static final String s4162 = "4162";
+ public static final String s4163 = "4163";
+ public static final String s4164 = "4164";
+ public static final String s4165 = "4165";
+ public static final String s4166 = "4166";
+ public static final String s4167 = "4167";
+ public static final String s4168 = "4168";
+ public static final String s4169 = "4169";
+ public static final String s4170 = "4170";
+ public static final String s4171 = "4171";
+ public static final String s4172 = "4172";
+ public static final String s4173 = "4173";
+ public static final String s4174 = "4174";
+ public static final String s4175 = "4175";
+ public static final String s4176 = "4176";
+ public static final String s4177 = "4177";
+ public static final String s4178 = "4178";
+ public static final String s4179 = "4179";
+ public static final String s4180 = "4180";
+ public static final String s4181 = "4181";
+ public static final String s4182 = "4182";
+ public static final String s4183 = "4183";
+ public static final String s4184 = "4184";
+ public static final String s4185 = "4185";
+ public static final String s4186 = "4186";
+ public static final String s4187 = "4187";
+ public static final String s4188 = "4188";
+ public static final String s4189 = "4189";
+ public static final String s4190 = "4190";
+ public static final String s4191 = "4191";
+ public static final String s4192 = "4192";
+ public static final String s4193 = "4193";
+ public static final String s4194 = "4194";
+ public static final String s4195 = "4195";
+ public static final String s4196 = "4196";
+ public static final String s4197 = "4197";
+ public static final String s4198 = "4198";
+ public static final String s4199 = "4199";
+ public static final String s4200 = "4200";
+ public static final String s4201 = "4201";
+ public static final String s4202 = "4202";
+ public static final String s4203 = "4203";
+ public static final String s4204 = "4204";
+ public static final String s4205 = "4205";
+ public static final String s4206 = "4206";
+ public static final String s4207 = "4207";
+ public static final String s4208 = "4208";
+ public static final String s4209 = "4209";
+ public static final String s4210 = "4210";
+ public static final String s4211 = "4211";
+ public static final String s4212 = "4212";
+ public static final String s4213 = "4213";
+ public static final String s4214 = "4214";
+ public static final String s4215 = "4215";
+ public static final String s4216 = "4216";
+ public static final String s4217 = "4217";
+ public static final String s4218 = "4218";
+ public static final String s4219 = "4219";
+ public static final String s4220 = "4220";
+ public static final String s4221 = "4221";
+ public static final String s4222 = "4222";
+ public static final String s4223 = "4223";
+ public static final String s4224 = "4224";
+ public static final String s4225 = "4225";
+ public static final String s4226 = "4226";
+ public static final String s4227 = "4227";
+ public static final String s4228 = "4228";
+ public static final String s4229 = "4229";
+ public static final String s4230 = "4230";
+ public static final String s4231 = "4231";
+ public static final String s4232 = "4232";
+ public static final String s4233 = "4233";
+ public static final String s4234 = "4234";
+ public static final String s4235 = "4235";
+ public static final String s4236 = "4236";
+ public static final String s4237 = "4237";
+ public static final String s4238 = "4238";
+ public static final String s4239 = "4239";
+ public static final String s4240 = "4240";
+ public static final String s4241 = "4241";
+ public static final String s4242 = "4242";
+ public static final String s4243 = "4243";
+ public static final String s4244 = "4244";
+ public static final String s4245 = "4245";
+ public static final String s4246 = "4246";
+ public static final String s4247 = "4247";
+ public static final String s4248 = "4248";
+ public static final String s4249 = "4249";
+ public static final String s4250 = "4250";
+ public static final String s4251 = "4251";
+ public static final String s4252 = "4252";
+ public static final String s4253 = "4253";
+ public static final String s4254 = "4254";
+ public static final String s4255 = "4255";
+ public static final String s4256 = "4256";
+ public static final String s4257 = "4257";
+ public static final String s4258 = "4258";
+ public static final String s4259 = "4259";
+ public static final String s4260 = "4260";
+ public static final String s4261 = "4261";
+ public static final String s4262 = "4262";
+ public static final String s4263 = "4263";
+ public static final String s4264 = "4264";
+ public static final String s4265 = "4265";
+ public static final String s4266 = "4266";
+ public static final String s4267 = "4267";
+ public static final String s4268 = "4268";
+ public static final String s4269 = "4269";
+ public static final String s4270 = "4270";
+ public static final String s4271 = "4271";
+ public static final String s4272 = "4272";
+ public static final String s4273 = "4273";
+ public static final String s4274 = "4274";
+ public static final String s4275 = "4275";
+ public static final String s4276 = "4276";
+ public static final String s4277 = "4277";
+ public static final String s4278 = "4278";
+ public static final String s4279 = "4279";
+ public static final String s4280 = "4280";
+ public static final String s4281 = "4281";
+ public static final String s4282 = "4282";
+ public static final String s4283 = "4283";
+ public static final String s4284 = "4284";
+ public static final String s4285 = "4285";
+ public static final String s4286 = "4286";
+ public static final String s4287 = "4287";
+ public static final String s4288 = "4288";
+ public static final String s4289 = "4289";
+ public static final String s4290 = "4290";
+ public static final String s4291 = "4291";
+ public static final String s4292 = "4292";
+ public static final String s4293 = "4293";
+ public static final String s4294 = "4294";
+ public static final String s4295 = "4295";
+ public static final String s4296 = "4296";
+ public static final String s4297 = "4297";
+ public static final String s4298 = "4298";
+ public static final String s4299 = "4299";
+ public static final String s4300 = "4300";
+ public static final String s4301 = "4301";
+ public static final String s4302 = "4302";
+ public static final String s4303 = "4303";
+ public static final String s4304 = "4304";
+ public static final String s4305 = "4305";
+ public static final String s4306 = "4306";
+ public static final String s4307 = "4307";
+ public static final String s4308 = "4308";
+ public static final String s4309 = "4309";
+ public static final String s4310 = "4310";
+ public static final String s4311 = "4311";
+ public static final String s4312 = "4312";
+ public static final String s4313 = "4313";
+ public static final String s4314 = "4314";
+ public static final String s4315 = "4315";
+ public static final String s4316 = "4316";
+ public static final String s4317 = "4317";
+ public static final String s4318 = "4318";
+ public static final String s4319 = "4319";
+ public static final String s4320 = "4320";
+ public static final String s4321 = "4321";
+ public static final String s4322 = "4322";
+ public static final String s4323 = "4323";
+ public static final String s4324 = "4324";
+ public static final String s4325 = "4325";
+ public static final String s4326 = "4326";
+ public static final String s4327 = "4327";
+ public static final String s4328 = "4328";
+ public static final String s4329 = "4329";
+ public static final String s4330 = "4330";
+ public static final String s4331 = "4331";
+ public static final String s4332 = "4332";
+ public static final String s4333 = "4333";
+ public static final String s4334 = "4334";
+ public static final String s4335 = "4335";
+ public static final String s4336 = "4336";
+ public static final String s4337 = "4337";
+ public static final String s4338 = "4338";
+ public static final String s4339 = "4339";
+ public static final String s4340 = "4340";
+ public static final String s4341 = "4341";
+ public static final String s4342 = "4342";
+ public static final String s4343 = "4343";
+ public static final String s4344 = "4344";
+ public static final String s4345 = "4345";
+ public static final String s4346 = "4346";
+ public static final String s4347 = "4347";
+ public static final String s4348 = "4348";
+ public static final String s4349 = "4349";
+ public static final String s4350 = "4350";
+ public static final String s4351 = "4351";
+ public static final String s4352 = "4352";
+ public static final String s4353 = "4353";
+ public static final String s4354 = "4354";
+ public static final String s4355 = "4355";
+ public static final String s4356 = "4356";
+ public static final String s4357 = "4357";
+ public static final String s4358 = "4358";
+ public static final String s4359 = "4359";
+ public static final String s4360 = "4360";
+ public static final String s4361 = "4361";
+ public static final String s4362 = "4362";
+ public static final String s4363 = "4363";
+ public static final String s4364 = "4364";
+ public static final String s4365 = "4365";
+ public static final String s4366 = "4366";
+ public static final String s4367 = "4367";
+ public static final String s4368 = "4368";
+ public static final String s4369 = "4369";
+ public static final String s4370 = "4370";
+ public static final String s4371 = "4371";
+ public static final String s4372 = "4372";
+ public static final String s4373 = "4373";
+ public static final String s4374 = "4374";
+ public static final String s4375 = "4375";
+ public static final String s4376 = "4376";
+ public static final String s4377 = "4377";
+ public static final String s4378 = "4378";
+ public static final String s4379 = "4379";
+ public static final String s4380 = "4380";
+ public static final String s4381 = "4381";
+ public static final String s4382 = "4382";
+ public static final String s4383 = "4383";
+ public static final String s4384 = "4384";
+ public static final String s4385 = "4385";
+ public static final String s4386 = "4386";
+ public static final String s4387 = "4387";
+ public static final String s4388 = "4388";
+ public static final String s4389 = "4389";
+ public static final String s4390 = "4390";
+ public static final String s4391 = "4391";
+ public static final String s4392 = "4392";
+ public static final String s4393 = "4393";
+ public static final String s4394 = "4394";
+ public static final String s4395 = "4395";
+ public static final String s4396 = "4396";
+ public static final String s4397 = "4397";
+ public static final String s4398 = "4398";
+ public static final String s4399 = "4399";
+ public static final String s4400 = "4400";
+ public static final String s4401 = "4401";
+ public static final String s4402 = "4402";
+ public static final String s4403 = "4403";
+ public static final String s4404 = "4404";
+ public static final String s4405 = "4405";
+ public static final String s4406 = "4406";
+ public static final String s4407 = "4407";
+ public static final String s4408 = "4408";
+ public static final String s4409 = "4409";
+ public static final String s4410 = "4410";
+ public static final String s4411 = "4411";
+ public static final String s4412 = "4412";
+ public static final String s4413 = "4413";
+ public static final String s4414 = "4414";
+ public static final String s4415 = "4415";
+ public static final String s4416 = "4416";
+ public static final String s4417 = "4417";
+ public static final String s4418 = "4418";
+ public static final String s4419 = "4419";
+ public static final String s4420 = "4420";
+ public static final String s4421 = "4421";
+ public static final String s4422 = "4422";
+ public static final String s4423 = "4423";
+ public static final String s4424 = "4424";
+ public static final String s4425 = "4425";
+ public static final String s4426 = "4426";
+ public static final String s4427 = "4427";
+ public static final String s4428 = "4428";
+ public static final String s4429 = "4429";
+ public static final String s4430 = "4430";
+ public static final String s4431 = "4431";
+ public static final String s4432 = "4432";
+ public static final String s4433 = "4433";
+ public static final String s4434 = "4434";
+ public static final String s4435 = "4435";
+ public static final String s4436 = "4436";
+ public static final String s4437 = "4437";
+ public static final String s4438 = "4438";
+ public static final String s4439 = "4439";
+ public static final String s4440 = "4440";
+ public static final String s4441 = "4441";
+ public static final String s4442 = "4442";
+ public static final String s4443 = "4443";
+ public static final String s4444 = "4444";
+ public static final String s4445 = "4445";
+ public static final String s4446 = "4446";
+ public static final String s4447 = "4447";
+ public static final String s4448 = "4448";
+ public static final String s4449 = "4449";
+ public static final String s4450 = "4450";
+ public static final String s4451 = "4451";
+ public static final String s4452 = "4452";
+ public static final String s4453 = "4453";
+ public static final String s4454 = "4454";
+ public static final String s4455 = "4455";
+ public static final String s4456 = "4456";
+ public static final String s4457 = "4457";
+ public static final String s4458 = "4458";
+ public static final String s4459 = "4459";
+ public static final String s4460 = "4460";
+ public static final String s4461 = "4461";
+ public static final String s4462 = "4462";
+ public static final String s4463 = "4463";
+ public static final String s4464 = "4464";
+ public static final String s4465 = "4465";
+ public static final String s4466 = "4466";
+ public static final String s4467 = "4467";
+ public static final String s4468 = "4468";
+ public static final String s4469 = "4469";
+ public static final String s4470 = "4470";
+ public static final String s4471 = "4471";
+ public static final String s4472 = "4472";
+ public static final String s4473 = "4473";
+ public static final String s4474 = "4474";
+ public static final String s4475 = "4475";
+ public static final String s4476 = "4476";
+ public static final String s4477 = "4477";
+ public static final String s4478 = "4478";
+ public static final String s4479 = "4479";
+ public static final String s4480 = "4480";
+ public static final String s4481 = "4481";
+ public static final String s4482 = "4482";
+ public static final String s4483 = "4483";
+ public static final String s4484 = "4484";
+ public static final String s4485 = "4485";
+ public static final String s4486 = "4486";
+ public static final String s4487 = "4487";
+ public static final String s4488 = "4488";
+ public static final String s4489 = "4489";
+ public static final String s4490 = "4490";
+ public static final String s4491 = "4491";
+ public static final String s4492 = "4492";
+ public static final String s4493 = "4493";
+ public static final String s4494 = "4494";
+ public static final String s4495 = "4495";
+ public static final String s4496 = "4496";
+ public static final String s4497 = "4497";
+ public static final String s4498 = "4498";
+ public static final String s4499 = "4499";
+ public static final String s4500 = "4500";
+ public static final String s4501 = "4501";
+ public static final String s4502 = "4502";
+ public static final String s4503 = "4503";
+ public static final String s4504 = "4504";
+ public static final String s4505 = "4505";
+ public static final String s4506 = "4506";
+ public static final String s4507 = "4507";
+ public static final String s4508 = "4508";
+ public static final String s4509 = "4509";
+ public static final String s4510 = "4510";
+ public static final String s4511 = "4511";
+ public static final String s4512 = "4512";
+ public static final String s4513 = "4513";
+ public static final String s4514 = "4514";
+ public static final String s4515 = "4515";
+ public static final String s4516 = "4516";
+ public static final String s4517 = "4517";
+ public static final String s4518 = "4518";
+ public static final String s4519 = "4519";
+ public static final String s4520 = "4520";
+ public static final String s4521 = "4521";
+ public static final String s4522 = "4522";
+ public static final String s4523 = "4523";
+ public static final String s4524 = "4524";
+ public static final String s4525 = "4525";
+ public static final String s4526 = "4526";
+ public static final String s4527 = "4527";
+ public static final String s4528 = "4528";
+ public static final String s4529 = "4529";
+ public static final String s4530 = "4530";
+ public static final String s4531 = "4531";
+ public static final String s4532 = "4532";
+ public static final String s4533 = "4533";
+ public static final String s4534 = "4534";
+ public static final String s4535 = "4535";
+ public static final String s4536 = "4536";
+ public static final String s4537 = "4537";
+ public static final String s4538 = "4538";
+ public static final String s4539 = "4539";
+ public static final String s4540 = "4540";
+ public static final String s4541 = "4541";
+ public static final String s4542 = "4542";
+ public static final String s4543 = "4543";
+ public static final String s4544 = "4544";
+ public static final String s4545 = "4545";
+ public static final String s4546 = "4546";
+ public static final String s4547 = "4547";
+ public static final String s4548 = "4548";
+ public static final String s4549 = "4549";
+ public static final String s4550 = "4550";
+ public static final String s4551 = "4551";
+ public static final String s4552 = "4552";
+ public static final String s4553 = "4553";
+ public static final String s4554 = "4554";
+ public static final String s4555 = "4555";
+ public static final String s4556 = "4556";
+ public static final String s4557 = "4557";
+ public static final String s4558 = "4558";
+ public static final String s4559 = "4559";
+ public static final String s4560 = "4560";
+ public static final String s4561 = "4561";
+ public static final String s4562 = "4562";
+ public static final String s4563 = "4563";
+ public static final String s4564 = "4564";
+ public static final String s4565 = "4565";
+ public static final String s4566 = "4566";
+ public static final String s4567 = "4567";
+ public static final String s4568 = "4568";
+ public static final String s4569 = "4569";
+ public static final String s4570 = "4570";
+ public static final String s4571 = "4571";
+ public static final String s4572 = "4572";
+ public static final String s4573 = "4573";
+ public static final String s4574 = "4574";
+ public static final String s4575 = "4575";
+ public static final String s4576 = "4576";
+ public static final String s4577 = "4577";
+ public static final String s4578 = "4578";
+ public static final String s4579 = "4579";
+ public static final String s4580 = "4580";
+ public static final String s4581 = "4581";
+ public static final String s4582 = "4582";
+ public static final String s4583 = "4583";
+ public static final String s4584 = "4584";
+ public static final String s4585 = "4585";
+ public static final String s4586 = "4586";
+ public static final String s4587 = "4587";
+ public static final String s4588 = "4588";
+ public static final String s4589 = "4589";
+ public static final String s4590 = "4590";
+ public static final String s4591 = "4591";
+ public static final String s4592 = "4592";
+ public static final String s4593 = "4593";
+ public static final String s4594 = "4594";
+ public static final String s4595 = "4595";
+ public static final String s4596 = "4596";
+ public static final String s4597 = "4597";
+ public static final String s4598 = "4598";
+ public static final String s4599 = "4599";
+ public static final String s4600 = "4600";
+ public static final String s4601 = "4601";
+ public static final String s4602 = "4602";
+ public static final String s4603 = "4603";
+ public static final String s4604 = "4604";
+ public static final String s4605 = "4605";
+ public static final String s4606 = "4606";
+ public static final String s4607 = "4607";
+ public static final String s4608 = "4608";
+ public static final String s4609 = "4609";
+ public static final String s4610 = "4610";
+ public static final String s4611 = "4611";
+ public static final String s4612 = "4612";
+ public static final String s4613 = "4613";
+ public static final String s4614 = "4614";
+ public static final String s4615 = "4615";
+ public static final String s4616 = "4616";
+ public static final String s4617 = "4617";
+ public static final String s4618 = "4618";
+ public static final String s4619 = "4619";
+ public static final String s4620 = "4620";
+ public static final String s4621 = "4621";
+ public static final String s4622 = "4622";
+ public static final String s4623 = "4623";
+ public static final String s4624 = "4624";
+ public static final String s4625 = "4625";
+ public static final String s4626 = "4626";
+ public static final String s4627 = "4627";
+ public static final String s4628 = "4628";
+ public static final String s4629 = "4629";
+ public static final String s4630 = "4630";
+ public static final String s4631 = "4631";
+ public static final String s4632 = "4632";
+ public static final String s4633 = "4633";
+ public static final String s4634 = "4634";
+ public static final String s4635 = "4635";
+ public static final String s4636 = "4636";
+ public static final String s4637 = "4637";
+ public static final String s4638 = "4638";
+ public static final String s4639 = "4639";
+ public static final String s4640 = "4640";
+ public static final String s4641 = "4641";
+ public static final String s4642 = "4642";
+ public static final String s4643 = "4643";
+ public static final String s4644 = "4644";
+ public static final String s4645 = "4645";
+ public static final String s4646 = "4646";
+ public static final String s4647 = "4647";
+ public static final String s4648 = "4648";
+ public static final String s4649 = "4649";
+ public static final String s4650 = "4650";
+ public static final String s4651 = "4651";
+ public static final String s4652 = "4652";
+ public static final String s4653 = "4653";
+ public static final String s4654 = "4654";
+ public static final String s4655 = "4655";
+ public static final String s4656 = "4656";
+ public static final String s4657 = "4657";
+ public static final String s4658 = "4658";
+ public static final String s4659 = "4659";
+ public static final String s4660 = "4660";
+ public static final String s4661 = "4661";
+ public static final String s4662 = "4662";
+ public static final String s4663 = "4663";
+ public static final String s4664 = "4664";
+ public static final String s4665 = "4665";
+ public static final String s4666 = "4666";
+ public static final String s4667 = "4667";
+ public static final String s4668 = "4668";
+ public static final String s4669 = "4669";
+ public static final String s4670 = "4670";
+ public static final String s4671 = "4671";
+ public static final String s4672 = "4672";
+ public static final String s4673 = "4673";
+ public static final String s4674 = "4674";
+ public static final String s4675 = "4675";
+ public static final String s4676 = "4676";
+ public static final String s4677 = "4677";
+ public static final String s4678 = "4678";
+ public static final String s4679 = "4679";
+ public static final String s4680 = "4680";
+ public static final String s4681 = "4681";
+ public static final String s4682 = "4682";
+ public static final String s4683 = "4683";
+ public static final String s4684 = "4684";
+ public static final String s4685 = "4685";
+ public static final String s4686 = "4686";
+ public static final String s4687 = "4687";
+ public static final String s4688 = "4688";
+ public static final String s4689 = "4689";
+ public static final String s4690 = "4690";
+ public static final String s4691 = "4691";
+ public static final String s4692 = "4692";
+ public static final String s4693 = "4693";
+ public static final String s4694 = "4694";
+ public static final String s4695 = "4695";
+ public static final String s4696 = "4696";
+ public static final String s4697 = "4697";
+ public static final String s4698 = "4698";
+ public static final String s4699 = "4699";
+ public static final String s4700 = "4700";
+ public static final String s4701 = "4701";
+ public static final String s4702 = "4702";
+ public static final String s4703 = "4703";
+ public static final String s4704 = "4704";
+ public static final String s4705 = "4705";
+ public static final String s4706 = "4706";
+ public static final String s4707 = "4707";
+ public static final String s4708 = "4708";
+ public static final String s4709 = "4709";
+ public static final String s4710 = "4710";
+ public static final String s4711 = "4711";
+ public static final String s4712 = "4712";
+ public static final String s4713 = "4713";
+ public static final String s4714 = "4714";
+ public static final String s4715 = "4715";
+ public static final String s4716 = "4716";
+ public static final String s4717 = "4717";
+ public static final String s4718 = "4718";
+ public static final String s4719 = "4719";
+ public static final String s4720 = "4720";
+ public static final String s4721 = "4721";
+ public static final String s4722 = "4722";
+ public static final String s4723 = "4723";
+ public static final String s4724 = "4724";
+ public static final String s4725 = "4725";
+ public static final String s4726 = "4726";
+ public static final String s4727 = "4727";
+ public static final String s4728 = "4728";
+ public static final String s4729 = "4729";
+ public static final String s4730 = "4730";
+ public static final String s4731 = "4731";
+ public static final String s4732 = "4732";
+ public static final String s4733 = "4733";
+ public static final String s4734 = "4734";
+ public static final String s4735 = "4735";
+ public static final String s4736 = "4736";
+ public static final String s4737 = "4737";
+ public static final String s4738 = "4738";
+ public static final String s4739 = "4739";
+ public static final String s4740 = "4740";
+ public static final String s4741 = "4741";
+ public static final String s4742 = "4742";
+ public static final String s4743 = "4743";
+ public static final String s4744 = "4744";
+ public static final String s4745 = "4745";
+ public static final String s4746 = "4746";
+ public static final String s4747 = "4747";
+ public static final String s4748 = "4748";
+ public static final String s4749 = "4749";
+ public static final String s4750 = "4750";
+ public static final String s4751 = "4751";
+ public static final String s4752 = "4752";
+ public static final String s4753 = "4753";
+ public static final String s4754 = "4754";
+ public static final String s4755 = "4755";
+ public static final String s4756 = "4756";
+ public static final String s4757 = "4757";
+ public static final String s4758 = "4758";
+ public static final String s4759 = "4759";
+ public static final String s4760 = "4760";
+ public static final String s4761 = "4761";
+ public static final String s4762 = "4762";
+ public static final String s4763 = "4763";
+ public static final String s4764 = "4764";
+ public static final String s4765 = "4765";
+ public static final String s4766 = "4766";
+ public static final String s4767 = "4767";
+ public static final String s4768 = "4768";
+ public static final String s4769 = "4769";
+ public static final String s4770 = "4770";
+ public static final String s4771 = "4771";
+ public static final String s4772 = "4772";
+ public static final String s4773 = "4773";
+ public static final String s4774 = "4774";
+ public static final String s4775 = "4775";
+ public static final String s4776 = "4776";
+ public static final String s4777 = "4777";
+ public static final String s4778 = "4778";
+ public static final String s4779 = "4779";
+ public static final String s4780 = "4780";
+ public static final String s4781 = "4781";
+ public static final String s4782 = "4782";
+ public static final String s4783 = "4783";
+ public static final String s4784 = "4784";
+ public static final String s4785 = "4785";
+ public static final String s4786 = "4786";
+ public static final String s4787 = "4787";
+ public static final String s4788 = "4788";
+ public static final String s4789 = "4789";
+ public static final String s4790 = "4790";
+ public static final String s4791 = "4791";
+ public static final String s4792 = "4792";
+ public static final String s4793 = "4793";
+ public static final String s4794 = "4794";
+ public static final String s4795 = "4795";
+ public static final String s4796 = "4796";
+ public static final String s4797 = "4797";
+ public static final String s4798 = "4798";
+ public static final String s4799 = "4799";
+ public static final String s4800 = "4800";
+ public static final String s4801 = "4801";
+ public static final String s4802 = "4802";
+ public static final String s4803 = "4803";
+ public static final String s4804 = "4804";
+ public static final String s4805 = "4805";
+ public static final String s4806 = "4806";
+ public static final String s4807 = "4807";
+ public static final String s4808 = "4808";
+ public static final String s4809 = "4809";
+ public static final String s4810 = "4810";
+ public static final String s4811 = "4811";
+ public static final String s4812 = "4812";
+ public static final String s4813 = "4813";
+ public static final String s4814 = "4814";
+ public static final String s4815 = "4815";
+ public static final String s4816 = "4816";
+ public static final String s4817 = "4817";
+ public static final String s4818 = "4818";
+ public static final String s4819 = "4819";
+ public static final String s4820 = "4820";
+ public static final String s4821 = "4821";
+ public static final String s4822 = "4822";
+ public static final String s4823 = "4823";
+ public static final String s4824 = "4824";
+ public static final String s4825 = "4825";
+ public static final String s4826 = "4826";
+ public static final String s4827 = "4827";
+ public static final String s4828 = "4828";
+ public static final String s4829 = "4829";
+ public static final String s4830 = "4830";
+ public static final String s4831 = "4831";
+ public static final String s4832 = "4832";
+ public static final String s4833 = "4833";
+ public static final String s4834 = "4834";
+ public static final String s4835 = "4835";
+ public static final String s4836 = "4836";
+ public static final String s4837 = "4837";
+ public static final String s4838 = "4838";
+ public static final String s4839 = "4839";
+ public static final String s4840 = "4840";
+ public static final String s4841 = "4841";
+ public static final String s4842 = "4842";
+ public static final String s4843 = "4843";
+ public static final String s4844 = "4844";
+ public static final String s4845 = "4845";
+ public static final String s4846 = "4846";
+ public static final String s4847 = "4847";
+ public static final String s4848 = "4848";
+ public static final String s4849 = "4849";
+ public static final String s4850 = "4850";
+ public static final String s4851 = "4851";
+ public static final String s4852 = "4852";
+ public static final String s4853 = "4853";
+ public static final String s4854 = "4854";
+ public static final String s4855 = "4855";
+ public static final String s4856 = "4856";
+ public static final String s4857 = "4857";
+ public static final String s4858 = "4858";
+ public static final String s4859 = "4859";
+ public static final String s4860 = "4860";
+ public static final String s4861 = "4861";
+ public static final String s4862 = "4862";
+ public static final String s4863 = "4863";
+ public static final String s4864 = "4864";
+ public static final String s4865 = "4865";
+ public static final String s4866 = "4866";
+ public static final String s4867 = "4867";
+ public static final String s4868 = "4868";
+ public static final String s4869 = "4869";
+ public static final String s4870 = "4870";
+ public static final String s4871 = "4871";
+ public static final String s4872 = "4872";
+ public static final String s4873 = "4873";
+ public static final String s4874 = "4874";
+ public static final String s4875 = "4875";
+ public static final String s4876 = "4876";
+ public static final String s4877 = "4877";
+ public static final String s4878 = "4878";
+ public static final String s4879 = "4879";
+ public static final String s4880 = "4880";
+ public static final String s4881 = "4881";
+ public static final String s4882 = "4882";
+ public static final String s4883 = "4883";
+ public static final String s4884 = "4884";
+ public static final String s4885 = "4885";
+ public static final String s4886 = "4886";
+ public static final String s4887 = "4887";
+ public static final String s4888 = "4888";
+ public static final String s4889 = "4889";
+ public static final String s4890 = "4890";
+ public static final String s4891 = "4891";
+ public static final String s4892 = "4892";
+ public static final String s4893 = "4893";
+ public static final String s4894 = "4894";
+ public static final String s4895 = "4895";
+ public static final String s4896 = "4896";
+ public static final String s4897 = "4897";
+ public static final String s4898 = "4898";
+ public static final String s4899 = "4899";
+ public static final String s4900 = "4900";
+ public static final String s4901 = "4901";
+ public static final String s4902 = "4902";
+ public static final String s4903 = "4903";
+ public static final String s4904 = "4904";
+ public static final String s4905 = "4905";
+ public static final String s4906 = "4906";
+ public static final String s4907 = "4907";
+ public static final String s4908 = "4908";
+ public static final String s4909 = "4909";
+ public static final String s4910 = "4910";
+ public static final String s4911 = "4911";
+ public static final String s4912 = "4912";
+ public static final String s4913 = "4913";
+ public static final String s4914 = "4914";
+ public static final String s4915 = "4915";
+ public static final String s4916 = "4916";
+ public static final String s4917 = "4917";
+ public static final String s4918 = "4918";
+ public static final String s4919 = "4919";
+ public static final String s4920 = "4920";
+ public static final String s4921 = "4921";
+ public static final String s4922 = "4922";
+ public static final String s4923 = "4923";
+ public static final String s4924 = "4924";
+ public static final String s4925 = "4925";
+ public static final String s4926 = "4926";
+ public static final String s4927 = "4927";
+ public static final String s4928 = "4928";
+ public static final String s4929 = "4929";
+ public static final String s4930 = "4930";
+ public static final String s4931 = "4931";
+ public static final String s4932 = "4932";
+ public static final String s4933 = "4933";
+ public static final String s4934 = "4934";
+ public static final String s4935 = "4935";
+ public static final String s4936 = "4936";
+ public static final String s4937 = "4937";
+ public static final String s4938 = "4938";
+ public static final String s4939 = "4939";
+ public static final String s4940 = "4940";
+ public static final String s4941 = "4941";
+ public static final String s4942 = "4942";
+ public static final String s4943 = "4943";
+ public static final String s4944 = "4944";
+ public static final String s4945 = "4945";
+ public static final String s4946 = "4946";
+ public static final String s4947 = "4947";
+ public static final String s4948 = "4948";
+ public static final String s4949 = "4949";
+ public static final String s4950 = "4950";
+ public static final String s4951 = "4951";
+ public static final String s4952 = "4952";
+ public static final String s4953 = "4953";
+ public static final String s4954 = "4954";
+ public static final String s4955 = "4955";
+ public static final String s4956 = "4956";
+ public static final String s4957 = "4957";
+ public static final String s4958 = "4958";
+ public static final String s4959 = "4959";
+ public static final String s4960 = "4960";
+ public static final String s4961 = "4961";
+ public static final String s4962 = "4962";
+ public static final String s4963 = "4963";
+ public static final String s4964 = "4964";
+ public static final String s4965 = "4965";
+ public static final String s4966 = "4966";
+ public static final String s4967 = "4967";
+ public static final String s4968 = "4968";
+ public static final String s4969 = "4969";
+ public static final String s4970 = "4970";
+ public static final String s4971 = "4971";
+ public static final String s4972 = "4972";
+ public static final String s4973 = "4973";
+ public static final String s4974 = "4974";
+ public static final String s4975 = "4975";
+ public static final String s4976 = "4976";
+ public static final String s4977 = "4977";
+ public static final String s4978 = "4978";
+ public static final String s4979 = "4979";
+ public static final String s4980 = "4980";
+ public static final String s4981 = "4981";
+ public static final String s4982 = "4982";
+ public static final String s4983 = "4983";
+ public static final String s4984 = "4984";
+ public static final String s4985 = "4985";
+ public static final String s4986 = "4986";
+ public static final String s4987 = "4987";
+ public static final String s4988 = "4988";
+ public static final String s4989 = "4989";
+ public static final String s4990 = "4990";
+ public static final String s4991 = "4991";
+ public static final String s4992 = "4992";
+ public static final String s4993 = "4993";
+ public static final String s4994 = "4994";
+ public static final String s4995 = "4995";
+ public static final String s4996 = "4996";
+ public static final String s4997 = "4997";
+ public static final String s4998 = "4998";
+ public static final String s4999 = "4999";
+ public static final String s5000 = "5000";
+ public static final String s5001 = "5001";
+ public static final String s5002 = "5002";
+ public static final String s5003 = "5003";
+ public static final String s5004 = "5004";
+ public static final String s5005 = "5005";
+ public static final String s5006 = "5006";
+ public static final String s5007 = "5007";
+ public static final String s5008 = "5008";
+ public static final String s5009 = "5009";
+ public static final String s5010 = "5010";
+ public static final String s5011 = "5011";
+ public static final String s5012 = "5012";
+ public static final String s5013 = "5013";
+ public static final String s5014 = "5014";
+ public static final String s5015 = "5015";
+ public static final String s5016 = "5016";
+ public static final String s5017 = "5017";
+ public static final String s5018 = "5018";
+ public static final String s5019 = "5019";
+ public static final String s5020 = "5020";
+ public static final String s5021 = "5021";
+ public static final String s5022 = "5022";
+ public static final String s5023 = "5023";
+ public static final String s5024 = "5024";
+ public static final String s5025 = "5025";
+ public static final String s5026 = "5026";
+ public static final String s5027 = "5027";
+ public static final String s5028 = "5028";
+ public static final String s5029 = "5029";
+ public static final String s5030 = "5030";
+ public static final String s5031 = "5031";
+ public static final String s5032 = "5032";
+ public static final String s5033 = "5033";
+ public static final String s5034 = "5034";
+ public static final String s5035 = "5035";
+ public static final String s5036 = "5036";
+ public static final String s5037 = "5037";
+ public static final String s5038 = "5038";
+ public static final String s5039 = "5039";
+ public static final String s5040 = "5040";
+ public static final String s5041 = "5041";
+ public static final String s5042 = "5042";
+ public static final String s5043 = "5043";
+ public static final String s5044 = "5044";
+ public static final String s5045 = "5045";
+ public static final String s5046 = "5046";
+ public static final String s5047 = "5047";
+ public static final String s5048 = "5048";
+ public static final String s5049 = "5049";
+ public static final String s5050 = "5050";
+ public static final String s5051 = "5051";
+ public static final String s5052 = "5052";
+ public static final String s5053 = "5053";
+ public static final String s5054 = "5054";
+ public static final String s5055 = "5055";
+ public static final String s5056 = "5056";
+ public static final String s5057 = "5057";
+ public static final String s5058 = "5058";
+ public static final String s5059 = "5059";
+ public static final String s5060 = "5060";
+ public static final String s5061 = "5061";
+ public static final String s5062 = "5062";
+ public static final String s5063 = "5063";
+ public static final String s5064 = "5064";
+ public static final String s5065 = "5065";
+ public static final String s5066 = "5066";
+ public static final String s5067 = "5067";
+ public static final String s5068 = "5068";
+ public static final String s5069 = "5069";
+ public static final String s5070 = "5070";
+ public static final String s5071 = "5071";
+ public static final String s5072 = "5072";
+ public static final String s5073 = "5073";
+ public static final String s5074 = "5074";
+ public static final String s5075 = "5075";
+ public static final String s5076 = "5076";
+ public static final String s5077 = "5077";
+ public static final String s5078 = "5078";
+ public static final String s5079 = "5079";
+ public static final String s5080 = "5080";
+ public static final String s5081 = "5081";
+ public static final String s5082 = "5082";
+ public static final String s5083 = "5083";
+ public static final String s5084 = "5084";
+ public static final String s5085 = "5085";
+ public static final String s5086 = "5086";
+ public static final String s5087 = "5087";
+ public static final String s5088 = "5088";
+ public static final String s5089 = "5089";
+ public static final String s5090 = "5090";
+ public static final String s5091 = "5091";
+ public static final String s5092 = "5092";
+ public static final String s5093 = "5093";
+ public static final String s5094 = "5094";
+ public static final String s5095 = "5095";
+ public static final String s5096 = "5096";
+ public static final String s5097 = "5097";
+ public static final String s5098 = "5098";
+ public static final String s5099 = "5099";
+ public static final String s5100 = "5100";
+ public static final String s5101 = "5101";
+ public static final String s5102 = "5102";
+ public static final String s5103 = "5103";
+ public static final String s5104 = "5104";
+ public static final String s5105 = "5105";
+ public static final String s5106 = "5106";
+ public static final String s5107 = "5107";
+ public static final String s5108 = "5108";
+ public static final String s5109 = "5109";
+ public static final String s5110 = "5110";
+ public static final String s5111 = "5111";
+ public static final String s5112 = "5112";
+ public static final String s5113 = "5113";
+ public static final String s5114 = "5114";
+ public static final String s5115 = "5115";
+ public static final String s5116 = "5116";
+ public static final String s5117 = "5117";
+ public static final String s5118 = "5118";
+ public static final String s5119 = "5119";
+ public static final String s5120 = "5120";
+ public static final String s5121 = "5121";
+ public static final String s5122 = "5122";
+ public static final String s5123 = "5123";
+ public static final String s5124 = "5124";
+ public static final String s5125 = "5125";
+ public static final String s5126 = "5126";
+ public static final String s5127 = "5127";
+ public static final String s5128 = "5128";
+ public static final String s5129 = "5129";
+ public static final String s5130 = "5130";
+ public static final String s5131 = "5131";
+ public static final String s5132 = "5132";
+ public static final String s5133 = "5133";
+ public static final String s5134 = "5134";
+ public static final String s5135 = "5135";
+ public static final String s5136 = "5136";
+ public static final String s5137 = "5137";
+ public static final String s5138 = "5138";
+ public static final String s5139 = "5139";
+ public static final String s5140 = "5140";
+ public static final String s5141 = "5141";
+ public static final String s5142 = "5142";
+ public static final String s5143 = "5143";
+ public static final String s5144 = "5144";
+ public static final String s5145 = "5145";
+ public static final String s5146 = "5146";
+ public static final String s5147 = "5147";
+ public static final String s5148 = "5148";
+ public static final String s5149 = "5149";
+ public static final String s5150 = "5150";
+ public static final String s5151 = "5151";
+ public static final String s5152 = "5152";
+ public static final String s5153 = "5153";
+ public static final String s5154 = "5154";
+ public static final String s5155 = "5155";
+ public static final String s5156 = "5156";
+ public static final String s5157 = "5157";
+ public static final String s5158 = "5158";
+ public static final String s5159 = "5159";
+ public static final String s5160 = "5160";
+ public static final String s5161 = "5161";
+ public static final String s5162 = "5162";
+ public static final String s5163 = "5163";
+ public static final String s5164 = "5164";
+ public static final String s5165 = "5165";
+ public static final String s5166 = "5166";
+ public static final String s5167 = "5167";
+ public static final String s5168 = "5168";
+ public static final String s5169 = "5169";
+ public static final String s5170 = "5170";
+ public static final String s5171 = "5171";
+ public static final String s5172 = "5172";
+ public static final String s5173 = "5173";
+ public static final String s5174 = "5174";
+ public static final String s5175 = "5175";
+ public static final String s5176 = "5176";
+ public static final String s5177 = "5177";
+ public static final String s5178 = "5178";
+ public static final String s5179 = "5179";
+ public static final String s5180 = "5180";
+ public static final String s5181 = "5181";
+ public static final String s5182 = "5182";
+ public static final String s5183 = "5183";
+ public static final String s5184 = "5184";
+ public static final String s5185 = "5185";
+ public static final String s5186 = "5186";
+ public static final String s5187 = "5187";
+ public static final String s5188 = "5188";
+ public static final String s5189 = "5189";
+ public static final String s5190 = "5190";
+ public static final String s5191 = "5191";
+ public static final String s5192 = "5192";
+ public static final String s5193 = "5193";
+ public static final String s5194 = "5194";
+ public static final String s5195 = "5195";
+ public static final String s5196 = "5196";
+ public static final String s5197 = "5197";
+ public static final String s5198 = "5198";
+ public static final String s5199 = "5199";
+ public static final String s5200 = "5200";
+ public static final String s5201 = "5201";
+ public static final String s5202 = "5202";
+ public static final String s5203 = "5203";
+ public static final String s5204 = "5204";
+ public static final String s5205 = "5205";
+ public static final String s5206 = "5206";
+ public static final String s5207 = "5207";
+ public static final String s5208 = "5208";
+ public static final String s5209 = "5209";
+ public static final String s5210 = "5210";
+ public static final String s5211 = "5211";
+ public static final String s5212 = "5212";
+ public static final String s5213 = "5213";
+ public static final String s5214 = "5214";
+ public static final String s5215 = "5215";
+ public static final String s5216 = "5216";
+ public static final String s5217 = "5217";
+ public static final String s5218 = "5218";
+ public static final String s5219 = "5219";
+ public static final String s5220 = "5220";
+ public static final String s5221 = "5221";
+ public static final String s5222 = "5222";
+ public static final String s5223 = "5223";
+ public static final String s5224 = "5224";
+ public static final String s5225 = "5225";
+ public static final String s5226 = "5226";
+ public static final String s5227 = "5227";
+ public static final String s5228 = "5228";
+ public static final String s5229 = "5229";
+ public static final String s5230 = "5230";
+ public static final String s5231 = "5231";
+ public static final String s5232 = "5232";
+ public static final String s5233 = "5233";
+ public static final String s5234 = "5234";
+ public static final String s5235 = "5235";
+ public static final String s5236 = "5236";
+ public static final String s5237 = "5237";
+ public static final String s5238 = "5238";
+ public static final String s5239 = "5239";
+ public static final String s5240 = "5240";
+ public static final String s5241 = "5241";
+ public static final String s5242 = "5242";
+ public static final String s5243 = "5243";
+ public static final String s5244 = "5244";
+ public static final String s5245 = "5245";
+ public static final String s5246 = "5246";
+ public static final String s5247 = "5247";
+ public static final String s5248 = "5248";
+ public static final String s5249 = "5249";
+ public static final String s5250 = "5250";
+ public static final String s5251 = "5251";
+ public static final String s5252 = "5252";
+ public static final String s5253 = "5253";
+ public static final String s5254 = "5254";
+ public static final String s5255 = "5255";
+ public static final String s5256 = "5256";
+ public static final String s5257 = "5257";
+ public static final String s5258 = "5258";
+ public static final String s5259 = "5259";
+ public static final String s5260 = "5260";
+ public static final String s5261 = "5261";
+ public static final String s5262 = "5262";
+ public static final String s5263 = "5263";
+ public static final String s5264 = "5264";
+ public static final String s5265 = "5265";
+ public static final String s5266 = "5266";
+ public static final String s5267 = "5267";
+ public static final String s5268 = "5268";
+ public static final String s5269 = "5269";
+ public static final String s5270 = "5270";
+ public static final String s5271 = "5271";
+ public static final String s5272 = "5272";
+ public static final String s5273 = "5273";
+ public static final String s5274 = "5274";
+ public static final String s5275 = "5275";
+ public static final String s5276 = "5276";
+ public static final String s5277 = "5277";
+ public static final String s5278 = "5278";
+ public static final String s5279 = "5279";
+ public static final String s5280 = "5280";
+ public static final String s5281 = "5281";
+ public static final String s5282 = "5282";
+ public static final String s5283 = "5283";
+ public static final String s5284 = "5284";
+ public static final String s5285 = "5285";
+ public static final String s5286 = "5286";
+ public static final String s5287 = "5287";
+ public static final String s5288 = "5288";
+ public static final String s5289 = "5289";
+ public static final String s5290 = "5290";
+ public static final String s5291 = "5291";
+ public static final String s5292 = "5292";
+ public static final String s5293 = "5293";
+ public static final String s5294 = "5294";
+ public static final String s5295 = "5295";
+ public static final String s5296 = "5296";
+ public static final String s5297 = "5297";
+ public static final String s5298 = "5298";
+ public static final String s5299 = "5299";
+ public static final String s5300 = "5300";
+ public static final String s5301 = "5301";
+ public static final String s5302 = "5302";
+ public static final String s5303 = "5303";
+ public static final String s5304 = "5304";
+ public static final String s5305 = "5305";
+ public static final String s5306 = "5306";
+ public static final String s5307 = "5307";
+ public static final String s5308 = "5308";
+ public static final String s5309 = "5309";
+ public static final String s5310 = "5310";
+ public static final String s5311 = "5311";
+ public static final String s5312 = "5312";
+ public static final String s5313 = "5313";
+ public static final String s5314 = "5314";
+ public static final String s5315 = "5315";
+ public static final String s5316 = "5316";
+ public static final String s5317 = "5317";
+ public static final String s5318 = "5318";
+ public static final String s5319 = "5319";
+ public static final String s5320 = "5320";
+ public static final String s5321 = "5321";
+ public static final String s5322 = "5322";
+ public static final String s5323 = "5323";
+ public static final String s5324 = "5324";
+ public static final String s5325 = "5325";
+ public static final String s5326 = "5326";
+ public static final String s5327 = "5327";
+ public static final String s5328 = "5328";
+ public static final String s5329 = "5329";
+ public static final String s5330 = "5330";
+ public static final String s5331 = "5331";
+ public static final String s5332 = "5332";
+ public static final String s5333 = "5333";
+ public static final String s5334 = "5334";
+ public static final String s5335 = "5335";
+ public static final String s5336 = "5336";
+ public static final String s5337 = "5337";
+ public static final String s5338 = "5338";
+ public static final String s5339 = "5339";
+ public static final String s5340 = "5340";
+ public static final String s5341 = "5341";
+ public static final String s5342 = "5342";
+ public static final String s5343 = "5343";
+ public static final String s5344 = "5344";
+ public static final String s5345 = "5345";
+ public static final String s5346 = "5346";
+ public static final String s5347 = "5347";
+ public static final String s5348 = "5348";
+ public static final String s5349 = "5349";
+ public static final String s5350 = "5350";
+ public static final String s5351 = "5351";
+ public static final String s5352 = "5352";
+ public static final String s5353 = "5353";
+ public static final String s5354 = "5354";
+ public static final String s5355 = "5355";
+ public static final String s5356 = "5356";
+ public static final String s5357 = "5357";
+ public static final String s5358 = "5358";
+ public static final String s5359 = "5359";
+ public static final String s5360 = "5360";
+ public static final String s5361 = "5361";
+ public static final String s5362 = "5362";
+ public static final String s5363 = "5363";
+ public static final String s5364 = "5364";
+ public static final String s5365 = "5365";
+ public static final String s5366 = "5366";
+ public static final String s5367 = "5367";
+ public static final String s5368 = "5368";
+ public static final String s5369 = "5369";
+ public static final String s5370 = "5370";
+ public static final String s5371 = "5371";
+ public static final String s5372 = "5372";
+ public static final String s5373 = "5373";
+ public static final String s5374 = "5374";
+ public static final String s5375 = "5375";
+ public static final String s5376 = "5376";
+ public static final String s5377 = "5377";
+ public static final String s5378 = "5378";
+ public static final String s5379 = "5379";
+ public static final String s5380 = "5380";
+ public static final String s5381 = "5381";
+ public static final String s5382 = "5382";
+ public static final String s5383 = "5383";
+ public static final String s5384 = "5384";
+ public static final String s5385 = "5385";
+ public static final String s5386 = "5386";
+ public static final String s5387 = "5387";
+ public static final String s5388 = "5388";
+ public static final String s5389 = "5389";
+ public static final String s5390 = "5390";
+ public static final String s5391 = "5391";
+ public static final String s5392 = "5392";
+ public static final String s5393 = "5393";
+ public static final String s5394 = "5394";
+ public static final String s5395 = "5395";
+ public static final String s5396 = "5396";
+ public static final String s5397 = "5397";
+ public static final String s5398 = "5398";
+ public static final String s5399 = "5399";
+ public static final String s5400 = "5400";
+ public static final String s5401 = "5401";
+ public static final String s5402 = "5402";
+ public static final String s5403 = "5403";
+ public static final String s5404 = "5404";
+ public static final String s5405 = "5405";
+ public static final String s5406 = "5406";
+ public static final String s5407 = "5407";
+ public static final String s5408 = "5408";
+ public static final String s5409 = "5409";
+ public static final String s5410 = "5410";
+ public static final String s5411 = "5411";
+ public static final String s5412 = "5412";
+ public static final String s5413 = "5413";
+ public static final String s5414 = "5414";
+ public static final String s5415 = "5415";
+ public static final String s5416 = "5416";
+ public static final String s5417 = "5417";
+ public static final String s5418 = "5418";
+ public static final String s5419 = "5419";
+ public static final String s5420 = "5420";
+ public static final String s5421 = "5421";
+ public static final String s5422 = "5422";
+ public static final String s5423 = "5423";
+ public static final String s5424 = "5424";
+ public static final String s5425 = "5425";
+ public static final String s5426 = "5426";
+ public static final String s5427 = "5427";
+ public static final String s5428 = "5428";
+ public static final String s5429 = "5429";
+ public static final String s5430 = "5430";
+ public static final String s5431 = "5431";
+ public static final String s5432 = "5432";
+ public static final String s5433 = "5433";
+ public static final String s5434 = "5434";
+ public static final String s5435 = "5435";
+ public static final String s5436 = "5436";
+ public static final String s5437 = "5437";
+ public static final String s5438 = "5438";
+ public static final String s5439 = "5439";
+ public static final String s5440 = "5440";
+ public static final String s5441 = "5441";
+ public static final String s5442 = "5442";
+ public static final String s5443 = "5443";
+ public static final String s5444 = "5444";
+ public static final String s5445 = "5445";
+ public static final String s5446 = "5446";
+ public static final String s5447 = "5447";
+ public static final String s5448 = "5448";
+ public static final String s5449 = "5449";
+ public static final String s5450 = "5450";
+ public static final String s5451 = "5451";
+ public static final String s5452 = "5452";
+ public static final String s5453 = "5453";
+ public static final String s5454 = "5454";
+ public static final String s5455 = "5455";
+ public static final String s5456 = "5456";
+ public static final String s5457 = "5457";
+ public static final String s5458 = "5458";
+ public static final String s5459 = "5459";
+ public static final String s5460 = "5460";
+ public static final String s5461 = "5461";
+ public static final String s5462 = "5462";
+ public static final String s5463 = "5463";
+ public static final String s5464 = "5464";
+ public static final String s5465 = "5465";
+ public static final String s5466 = "5466";
+ public static final String s5467 = "5467";
+ public static final String s5468 = "5468";
+ public static final String s5469 = "5469";
+ public static final String s5470 = "5470";
+ public static final String s5471 = "5471";
+ public static final String s5472 = "5472";
+ public static final String s5473 = "5473";
+ public static final String s5474 = "5474";
+ public static final String s5475 = "5475";
+ public static final String s5476 = "5476";
+ public static final String s5477 = "5477";
+ public static final String s5478 = "5478";
+ public static final String s5479 = "5479";
+ public static final String s5480 = "5480";
+ public static final String s5481 = "5481";
+ public static final String s5482 = "5482";
+ public static final String s5483 = "5483";
+ public static final String s5484 = "5484";
+ public static final String s5485 = "5485";
+ public static final String s5486 = "5486";
+ public static final String s5487 = "5487";
+ public static final String s5488 = "5488";
+ public static final String s5489 = "5489";
+ public static final String s5490 = "5490";
+ public static final String s5491 = "5491";
+ public static final String s5492 = "5492";
+ public static final String s5493 = "5493";
+ public static final String s5494 = "5494";
+ public static final String s5495 = "5495";
+ public static final String s5496 = "5496";
+ public static final String s5497 = "5497";
+ public static final String s5498 = "5498";
+ public static final String s5499 = "5499";
+ public static final String s5500 = "5500";
+ public static final String s5501 = "5501";
+ public static final String s5502 = "5502";
+ public static final String s5503 = "5503";
+ public static final String s5504 = "5504";
+ public static final String s5505 = "5505";
+ public static final String s5506 = "5506";
+ public static final String s5507 = "5507";
+ public static final String s5508 = "5508";
+ public static final String s5509 = "5509";
+ public static final String s5510 = "5510";
+ public static final String s5511 = "5511";
+ public static final String s5512 = "5512";
+ public static final String s5513 = "5513";
+ public static final String s5514 = "5514";
+ public static final String s5515 = "5515";
+ public static final String s5516 = "5516";
+ public static final String s5517 = "5517";
+ public static final String s5518 = "5518";
+ public static final String s5519 = "5519";
+ public static final String s5520 = "5520";
+ public static final String s5521 = "5521";
+ public static final String s5522 = "5522";
+ public static final String s5523 = "5523";
+ public static final String s5524 = "5524";
+ public static final String s5525 = "5525";
+ public static final String s5526 = "5526";
+ public static final String s5527 = "5527";
+ public static final String s5528 = "5528";
+ public static final String s5529 = "5529";
+ public static final String s5530 = "5530";
+ public static final String s5531 = "5531";
+ public static final String s5532 = "5532";
+ public static final String s5533 = "5533";
+ public static final String s5534 = "5534";
+ public static final String s5535 = "5535";
+ public static final String s5536 = "5536";
+ public static final String s5537 = "5537";
+ public static final String s5538 = "5538";
+ public static final String s5539 = "5539";
+ public static final String s5540 = "5540";
+ public static final String s5541 = "5541";
+ public static final String s5542 = "5542";
+ public static final String s5543 = "5543";
+ public static final String s5544 = "5544";
+ public static final String s5545 = "5545";
+ public static final String s5546 = "5546";
+ public static final String s5547 = "5547";
+ public static final String s5548 = "5548";
+ public static final String s5549 = "5549";
+ public static final String s5550 = "5550";
+ public static final String s5551 = "5551";
+ public static final String s5552 = "5552";
+ public static final String s5553 = "5553";
+ public static final String s5554 = "5554";
+ public static final String s5555 = "5555";
+ public static final String s5556 = "5556";
+ public static final String s5557 = "5557";
+ public static final String s5558 = "5558";
+ public static final String s5559 = "5559";
+ public static final String s5560 = "5560";
+ public static final String s5561 = "5561";
+ public static final String s5562 = "5562";
+ public static final String s5563 = "5563";
+ public static final String s5564 = "5564";
+ public static final String s5565 = "5565";
+ public static final String s5566 = "5566";
+ public static final String s5567 = "5567";
+ public static final String s5568 = "5568";
+ public static final String s5569 = "5569";
+ public static final String s5570 = "5570";
+ public static final String s5571 = "5571";
+ public static final String s5572 = "5572";
+ public static final String s5573 = "5573";
+ public static final String s5574 = "5574";
+ public static final String s5575 = "5575";
+ public static final String s5576 = "5576";
+ public static final String s5577 = "5577";
+ public static final String s5578 = "5578";
+ public static final String s5579 = "5579";
+ public static final String s5580 = "5580";
+ public static final String s5581 = "5581";
+ public static final String s5582 = "5582";
+ public static final String s5583 = "5583";
+ public static final String s5584 = "5584";
+ public static final String s5585 = "5585";
+ public static final String s5586 = "5586";
+ public static final String s5587 = "5587";
+ public static final String s5588 = "5588";
+ public static final String s5589 = "5589";
+ public static final String s5590 = "5590";
+ public static final String s5591 = "5591";
+ public static final String s5592 = "5592";
+ public static final String s5593 = "5593";
+ public static final String s5594 = "5594";
+ public static final String s5595 = "5595";
+ public static final String s5596 = "5596";
+ public static final String s5597 = "5597";
+ public static final String s5598 = "5598";
+ public static final String s5599 = "5599";
+ public static final String s5600 = "5600";
+ public static final String s5601 = "5601";
+ public static final String s5602 = "5602";
+ public static final String s5603 = "5603";
+ public static final String s5604 = "5604";
+ public static final String s5605 = "5605";
+ public static final String s5606 = "5606";
+ public static final String s5607 = "5607";
+ public static final String s5608 = "5608";
+ public static final String s5609 = "5609";
+ public static final String s5610 = "5610";
+ public static final String s5611 = "5611";
+ public static final String s5612 = "5612";
+ public static final String s5613 = "5613";
+ public static final String s5614 = "5614";
+ public static final String s5615 = "5615";
+ public static final String s5616 = "5616";
+ public static final String s5617 = "5617";
+ public static final String s5618 = "5618";
+ public static final String s5619 = "5619";
+ public static final String s5620 = "5620";
+ public static final String s5621 = "5621";
+ public static final String s5622 = "5622";
+ public static final String s5623 = "5623";
+ public static final String s5624 = "5624";
+ public static final String s5625 = "5625";
+ public static final String s5626 = "5626";
+ public static final String s5627 = "5627";
+ public static final String s5628 = "5628";
+ public static final String s5629 = "5629";
+ public static final String s5630 = "5630";
+ public static final String s5631 = "5631";
+ public static final String s5632 = "5632";
+ public static final String s5633 = "5633";
+ public static final String s5634 = "5634";
+ public static final String s5635 = "5635";
+ public static final String s5636 = "5636";
+ public static final String s5637 = "5637";
+ public static final String s5638 = "5638";
+ public static final String s5639 = "5639";
+ public static final String s5640 = "5640";
+ public static final String s5641 = "5641";
+ public static final String s5642 = "5642";
+ public static final String s5643 = "5643";
+ public static final String s5644 = "5644";
+ public static final String s5645 = "5645";
+ public static final String s5646 = "5646";
+ public static final String s5647 = "5647";
+ public static final String s5648 = "5648";
+ public static final String s5649 = "5649";
+ public static final String s5650 = "5650";
+ public static final String s5651 = "5651";
+ public static final String s5652 = "5652";
+ public static final String s5653 = "5653";
+ public static final String s5654 = "5654";
+ public static final String s5655 = "5655";
+ public static final String s5656 = "5656";
+ public static final String s5657 = "5657";
+ public static final String s5658 = "5658";
+ public static final String s5659 = "5659";
+ public static final String s5660 = "5660";
+ public static final String s5661 = "5661";
+ public static final String s5662 = "5662";
+ public static final String s5663 = "5663";
+ public static final String s5664 = "5664";
+ public static final String s5665 = "5665";
+ public static final String s5666 = "5666";
+ public static final String s5667 = "5667";
+ public static final String s5668 = "5668";
+ public static final String s5669 = "5669";
+ public static final String s5670 = "5670";
+ public static final String s5671 = "5671";
+ public static final String s5672 = "5672";
+ public static final String s5673 = "5673";
+ public static final String s5674 = "5674";
+ public static final String s5675 = "5675";
+ public static final String s5676 = "5676";
+ public static final String s5677 = "5677";
+ public static final String s5678 = "5678";
+ public static final String s5679 = "5679";
+ public static final String s5680 = "5680";
+ public static final String s5681 = "5681";
+ public static final String s5682 = "5682";
+ public static final String s5683 = "5683";
+ public static final String s5684 = "5684";
+ public static final String s5685 = "5685";
+ public static final String s5686 = "5686";
+ public static final String s5687 = "5687";
+ public static final String s5688 = "5688";
+ public static final String s5689 = "5689";
+ public static final String s5690 = "5690";
+ public static final String s5691 = "5691";
+ public static final String s5692 = "5692";
+ public static final String s5693 = "5693";
+ public static final String s5694 = "5694";
+ public static final String s5695 = "5695";
+ public static final String s5696 = "5696";
+ public static final String s5697 = "5697";
+ public static final String s5698 = "5698";
+ public static final String s5699 = "5699";
+ public static final String s5700 = "5700";
+ public static final String s5701 = "5701";
+ public static final String s5702 = "5702";
+ public static final String s5703 = "5703";
+ public static final String s5704 = "5704";
+ public static final String s5705 = "5705";
+ public static final String s5706 = "5706";
+ public static final String s5707 = "5707";
+ public static final String s5708 = "5708";
+ public static final String s5709 = "5709";
+ public static final String s5710 = "5710";
+ public static final String s5711 = "5711";
+ public static final String s5712 = "5712";
+ public static final String s5713 = "5713";
+ public static final String s5714 = "5714";
+ public static final String s5715 = "5715";
+ public static final String s5716 = "5716";
+ public static final String s5717 = "5717";
+ public static final String s5718 = "5718";
+ public static final String s5719 = "5719";
+ public static final String s5720 = "5720";
+ public static final String s5721 = "5721";
+ public static final String s5722 = "5722";
+ public static final String s5723 = "5723";
+ public static final String s5724 = "5724";
+ public static final String s5725 = "5725";
+ public static final String s5726 = "5726";
+ public static final String s5727 = "5727";
+ public static final String s5728 = "5728";
+ public static final String s5729 = "5729";
+ public static final String s5730 = "5730";
+ public static final String s5731 = "5731";
+ public static final String s5732 = "5732";
+ public static final String s5733 = "5733";
+ public static final String s5734 = "5734";
+ public static final String s5735 = "5735";
+ public static final String s5736 = "5736";
+ public static final String s5737 = "5737";
+ public static final String s5738 = "5738";
+ public static final String s5739 = "5739";
+ public static final String s5740 = "5740";
+ public static final String s5741 = "5741";
+ public static final String s5742 = "5742";
+ public static final String s5743 = "5743";
+ public static final String s5744 = "5744";
+ public static final String s5745 = "5745";
+ public static final String s5746 = "5746";
+ public static final String s5747 = "5747";
+ public static final String s5748 = "5748";
+ public static final String s5749 = "5749";
+ public static final String s5750 = "5750";
+ public static final String s5751 = "5751";
+ public static final String s5752 = "5752";
+ public static final String s5753 = "5753";
+ public static final String s5754 = "5754";
+ public static final String s5755 = "5755";
+ public static final String s5756 = "5756";
+ public static final String s5757 = "5757";
+ public static final String s5758 = "5758";
+ public static final String s5759 = "5759";
+ public static final String s5760 = "5760";
+ public static final String s5761 = "5761";
+ public static final String s5762 = "5762";
+ public static final String s5763 = "5763";
+ public static final String s5764 = "5764";
+ public static final String s5765 = "5765";
+ public static final String s5766 = "5766";
+ public static final String s5767 = "5767";
+ public static final String s5768 = "5768";
+ public static final String s5769 = "5769";
+ public static final String s5770 = "5770";
+ public static final String s5771 = "5771";
+ public static final String s5772 = "5772";
+ public static final String s5773 = "5773";
+ public static final String s5774 = "5774";
+ public static final String s5775 = "5775";
+ public static final String s5776 = "5776";
+ public static final String s5777 = "5777";
+ public static final String s5778 = "5778";
+ public static final String s5779 = "5779";
+ public static final String s5780 = "5780";
+ public static final String s5781 = "5781";
+ public static final String s5782 = "5782";
+ public static final String s5783 = "5783";
+ public static final String s5784 = "5784";
+ public static final String s5785 = "5785";
+ public static final String s5786 = "5786";
+ public static final String s5787 = "5787";
+ public static final String s5788 = "5788";
+ public static final String s5789 = "5789";
+ public static final String s5790 = "5790";
+ public static final String s5791 = "5791";
+ public static final String s5792 = "5792";
+ public static final String s5793 = "5793";
+ public static final String s5794 = "5794";
+ public static final String s5795 = "5795";
+ public static final String s5796 = "5796";
+ public static final String s5797 = "5797";
+ public static final String s5798 = "5798";
+ public static final String s5799 = "5799";
+ public static final String s5800 = "5800";
+ public static final String s5801 = "5801";
+ public static final String s5802 = "5802";
+ public static final String s5803 = "5803";
+ public static final String s5804 = "5804";
+ public static final String s5805 = "5805";
+ public static final String s5806 = "5806";
+ public static final String s5807 = "5807";
+ public static final String s5808 = "5808";
+ public static final String s5809 = "5809";
+ public static final String s5810 = "5810";
+ public static final String s5811 = "5811";
+ public static final String s5812 = "5812";
+ public static final String s5813 = "5813";
+ public static final String s5814 = "5814";
+ public static final String s5815 = "5815";
+ public static final String s5816 = "5816";
+ public static final String s5817 = "5817";
+ public static final String s5818 = "5818";
+ public static final String s5819 = "5819";
+ public static final String s5820 = "5820";
+ public static final String s5821 = "5821";
+ public static final String s5822 = "5822";
+ public static final String s5823 = "5823";
+ public static final String s5824 = "5824";
+ public static final String s5825 = "5825";
+ public static final String s5826 = "5826";
+ public static final String s5827 = "5827";
+ public static final String s5828 = "5828";
+ public static final String s5829 = "5829";
+ public static final String s5830 = "5830";
+ public static final String s5831 = "5831";
+ public static final String s5832 = "5832";
+ public static final String s5833 = "5833";
+ public static final String s5834 = "5834";
+ public static final String s5835 = "5835";
+ public static final String s5836 = "5836";
+ public static final String s5837 = "5837";
+ public static final String s5838 = "5838";
+ public static final String s5839 = "5839";
+ public static final String s5840 = "5840";
+ public static final String s5841 = "5841";
+ public static final String s5842 = "5842";
+ public static final String s5843 = "5843";
+ public static final String s5844 = "5844";
+ public static final String s5845 = "5845";
+ public static final String s5846 = "5846";
+ public static final String s5847 = "5847";
+ public static final String s5848 = "5848";
+ public static final String s5849 = "5849";
+ public static final String s5850 = "5850";
+ public static final String s5851 = "5851";
+ public static final String s5852 = "5852";
+ public static final String s5853 = "5853";
+ public static final String s5854 = "5854";
+ public static final String s5855 = "5855";
+ public static final String s5856 = "5856";
+ public static final String s5857 = "5857";
+ public static final String s5858 = "5858";
+ public static final String s5859 = "5859";
+ public static final String s5860 = "5860";
+ public static final String s5861 = "5861";
+ public static final String s5862 = "5862";
+ public static final String s5863 = "5863";
+ public static final String s5864 = "5864";
+ public static final String s5865 = "5865";
+ public static final String s5866 = "5866";
+ public static final String s5867 = "5867";
+ public static final String s5868 = "5868";
+ public static final String s5869 = "5869";
+ public static final String s5870 = "5870";
+ public static final String s5871 = "5871";
+ public static final String s5872 = "5872";
+ public static final String s5873 = "5873";
+ public static final String s5874 = "5874";
+ public static final String s5875 = "5875";
+ public static final String s5876 = "5876";
+ public static final String s5877 = "5877";
+ public static final String s5878 = "5878";
+ public static final String s5879 = "5879";
+ public static final String s5880 = "5880";
+ public static final String s5881 = "5881";
+ public static final String s5882 = "5882";
+ public static final String s5883 = "5883";
+ public static final String s5884 = "5884";
+ public static final String s5885 = "5885";
+ public static final String s5886 = "5886";
+ public static final String s5887 = "5887";
+ public static final String s5888 = "5888";
+ public static final String s5889 = "5889";
+ public static final String s5890 = "5890";
+ public static final String s5891 = "5891";
+ public static final String s5892 = "5892";
+ public static final String s5893 = "5893";
+ public static final String s5894 = "5894";
+ public static final String s5895 = "5895";
+ public static final String s5896 = "5896";
+ public static final String s5897 = "5897";
+ public static final String s5898 = "5898";
+ public static final String s5899 = "5899";
+ public static final String s5900 = "5900";
+ public static final String s5901 = "5901";
+ public static final String s5902 = "5902";
+ public static final String s5903 = "5903";
+ public static final String s5904 = "5904";
+ public static final String s5905 = "5905";
+ public static final String s5906 = "5906";
+ public static final String s5907 = "5907";
+ public static final String s5908 = "5908";
+ public static final String s5909 = "5909";
+ public static final String s5910 = "5910";
+ public static final String s5911 = "5911";
+ public static final String s5912 = "5912";
+ public static final String s5913 = "5913";
+ public static final String s5914 = "5914";
+ public static final String s5915 = "5915";
+ public static final String s5916 = "5916";
+ public static final String s5917 = "5917";
+ public static final String s5918 = "5918";
+ public static final String s5919 = "5919";
+ public static final String s5920 = "5920";
+ public static final String s5921 = "5921";
+ public static final String s5922 = "5922";
+ public static final String s5923 = "5923";
+ public static final String s5924 = "5924";
+ public static final String s5925 = "5925";
+ public static final String s5926 = "5926";
+ public static final String s5927 = "5927";
+ public static final String s5928 = "5928";
+ public static final String s5929 = "5929";
+ public static final String s5930 = "5930";
+ public static final String s5931 = "5931";
+ public static final String s5932 = "5932";
+ public static final String s5933 = "5933";
+ public static final String s5934 = "5934";
+ public static final String s5935 = "5935";
+ public static final String s5936 = "5936";
+ public static final String s5937 = "5937";
+ public static final String s5938 = "5938";
+ public static final String s5939 = "5939";
+ public static final String s5940 = "5940";
+ public static final String s5941 = "5941";
+ public static final String s5942 = "5942";
+ public static final String s5943 = "5943";
+ public static final String s5944 = "5944";
+ public static final String s5945 = "5945";
+ public static final String s5946 = "5946";
+ public static final String s5947 = "5947";
+ public static final String s5948 = "5948";
+ public static final String s5949 = "5949";
+ public static final String s5950 = "5950";
+ public static final String s5951 = "5951";
+ public static final String s5952 = "5952";
+ public static final String s5953 = "5953";
+ public static final String s5954 = "5954";
+ public static final String s5955 = "5955";
+ public static final String s5956 = "5956";
+ public static final String s5957 = "5957";
+ public static final String s5958 = "5958";
+ public static final String s5959 = "5959";
+ public static final String s5960 = "5960";
+ public static final String s5961 = "5961";
+ public static final String s5962 = "5962";
+ public static final String s5963 = "5963";
+ public static final String s5964 = "5964";
+ public static final String s5965 = "5965";
+ public static final String s5966 = "5966";
+ public static final String s5967 = "5967";
+ public static final String s5968 = "5968";
+ public static final String s5969 = "5969";
+ public static final String s5970 = "5970";
+ public static final String s5971 = "5971";
+ public static final String s5972 = "5972";
+ public static final String s5973 = "5973";
+ public static final String s5974 = "5974";
+ public static final String s5975 = "5975";
+ public static final String s5976 = "5976";
+ public static final String s5977 = "5977";
+ public static final String s5978 = "5978";
+ public static final String s5979 = "5979";
+ public static final String s5980 = "5980";
+ public static final String s5981 = "5981";
+ public static final String s5982 = "5982";
+ public static final String s5983 = "5983";
+ public static final String s5984 = "5984";
+ public static final String s5985 = "5985";
+ public static final String s5986 = "5986";
+ public static final String s5987 = "5987";
+ public static final String s5988 = "5988";
+ public static final String s5989 = "5989";
+ public static final String s5990 = "5990";
+ public static final String s5991 = "5991";
+ public static final String s5992 = "5992";
+ public static final String s5993 = "5993";
+ public static final String s5994 = "5994";
+ public static final String s5995 = "5995";
+ public static final String s5996 = "5996";
+ public static final String s5997 = "5997";
+ public static final String s5998 = "5998";
+ public static final String s5999 = "5999";
+ public static final String s6000 = "6000";
+ public static final String s6001 = "6001";
+ public static final String s6002 = "6002";
+ public static final String s6003 = "6003";
+ public static final String s6004 = "6004";
+ public static final String s6005 = "6005";
+ public static final String s6006 = "6006";
+ public static final String s6007 = "6007";
+ public static final String s6008 = "6008";
+ public static final String s6009 = "6009";
+ public static final String s6010 = "6010";
+ public static final String s6011 = "6011";
+ public static final String s6012 = "6012";
+ public static final String s6013 = "6013";
+ public static final String s6014 = "6014";
+ public static final String s6015 = "6015";
+ public static final String s6016 = "6016";
+ public static final String s6017 = "6017";
+ public static final String s6018 = "6018";
+ public static final String s6019 = "6019";
+ public static final String s6020 = "6020";
+ public static final String s6021 = "6021";
+ public static final String s6022 = "6022";
+ public static final String s6023 = "6023";
+ public static final String s6024 = "6024";
+ public static final String s6025 = "6025";
+ public static final String s6026 = "6026";
+ public static final String s6027 = "6027";
+ public static final String s6028 = "6028";
+ public static final String s6029 = "6029";
+ public static final String s6030 = "6030";
+ public static final String s6031 = "6031";
+ public static final String s6032 = "6032";
+ public static final String s6033 = "6033";
+ public static final String s6034 = "6034";
+ public static final String s6035 = "6035";
+ public static final String s6036 = "6036";
+ public static final String s6037 = "6037";
+ public static final String s6038 = "6038";
+ public static final String s6039 = "6039";
+ public static final String s6040 = "6040";
+ public static final String s6041 = "6041";
+ public static final String s6042 = "6042";
+ public static final String s6043 = "6043";
+ public static final String s6044 = "6044";
+ public static final String s6045 = "6045";
+ public static final String s6046 = "6046";
+ public static final String s6047 = "6047";
+ public static final String s6048 = "6048";
+ public static final String s6049 = "6049";
+ public static final String s6050 = "6050";
+ public static final String s6051 = "6051";
+ public static final String s6052 = "6052";
+ public static final String s6053 = "6053";
+ public static final String s6054 = "6054";
+ public static final String s6055 = "6055";
+ public static final String s6056 = "6056";
+ public static final String s6057 = "6057";
+ public static final String s6058 = "6058";
+ public static final String s6059 = "6059";
+ public static final String s6060 = "6060";
+ public static final String s6061 = "6061";
+ public static final String s6062 = "6062";
+ public static final String s6063 = "6063";
+ public static final String s6064 = "6064";
+ public static final String s6065 = "6065";
+ public static final String s6066 = "6066";
+ public static final String s6067 = "6067";
+ public static final String s6068 = "6068";
+ public static final String s6069 = "6069";
+ public static final String s6070 = "6070";
+ public static final String s6071 = "6071";
+ public static final String s6072 = "6072";
+ public static final String s6073 = "6073";
+ public static final String s6074 = "6074";
+ public static final String s6075 = "6075";
+ public static final String s6076 = "6076";
+ public static final String s6077 = "6077";
+ public static final String s6078 = "6078";
+ public static final String s6079 = "6079";
+ public static final String s6080 = "6080";
+ public static final String s6081 = "6081";
+ public static final String s6082 = "6082";
+ public static final String s6083 = "6083";
+ public static final String s6084 = "6084";
+ public static final String s6085 = "6085";
+ public static final String s6086 = "6086";
+ public static final String s6087 = "6087";
+ public static final String s6088 = "6088";
+ public static final String s6089 = "6089";
+ public static final String s6090 = "6090";
+ public static final String s6091 = "6091";
+ public static final String s6092 = "6092";
+ public static final String s6093 = "6093";
+ public static final String s6094 = "6094";
+ public static final String s6095 = "6095";
+ public static final String s6096 = "6096";
+ public static final String s6097 = "6097";
+ public static final String s6098 = "6098";
+ public static final String s6099 = "6099";
+ public static final String s6100 = "6100";
+ public static final String s6101 = "6101";
+ public static final String s6102 = "6102";
+ public static final String s6103 = "6103";
+ public static final String s6104 = "6104";
+ public static final String s6105 = "6105";
+ public static final String s6106 = "6106";
+ public static final String s6107 = "6107";
+ public static final String s6108 = "6108";
+ public static final String s6109 = "6109";
+ public static final String s6110 = "6110";
+ public static final String s6111 = "6111";
+ public static final String s6112 = "6112";
+ public static final String s6113 = "6113";
+ public static final String s6114 = "6114";
+ public static final String s6115 = "6115";
+ public static final String s6116 = "6116";
+ public static final String s6117 = "6117";
+ public static final String s6118 = "6118";
+ public static final String s6119 = "6119";
+ public static final String s6120 = "6120";
+ public static final String s6121 = "6121";
+ public static final String s6122 = "6122";
+ public static final String s6123 = "6123";
+ public static final String s6124 = "6124";
+ public static final String s6125 = "6125";
+ public static final String s6126 = "6126";
+ public static final String s6127 = "6127";
+ public static final String s6128 = "6128";
+ public static final String s6129 = "6129";
+ public static final String s6130 = "6130";
+ public static final String s6131 = "6131";
+ public static final String s6132 = "6132";
+ public static final String s6133 = "6133";
+ public static final String s6134 = "6134";
+ public static final String s6135 = "6135";
+ public static final String s6136 = "6136";
+ public static final String s6137 = "6137";
+ public static final String s6138 = "6138";
+ public static final String s6139 = "6139";
+ public static final String s6140 = "6140";
+ public static final String s6141 = "6141";
+ public static final String s6142 = "6142";
+ public static final String s6143 = "6143";
+ public static final String s6144 = "6144";
+ public static final String s6145 = "6145";
+ public static final String s6146 = "6146";
+ public static final String s6147 = "6147";
+ public static final String s6148 = "6148";
+ public static final String s6149 = "6149";
+ public static final String s6150 = "6150";
+ public static final String s6151 = "6151";
+ public static final String s6152 = "6152";
+ public static final String s6153 = "6153";
+ public static final String s6154 = "6154";
+ public static final String s6155 = "6155";
+ public static final String s6156 = "6156";
+ public static final String s6157 = "6157";
+ public static final String s6158 = "6158";
+ public static final String s6159 = "6159";
+ public static final String s6160 = "6160";
+ public static final String s6161 = "6161";
+ public static final String s6162 = "6162";
+ public static final String s6163 = "6163";
+ public static final String s6164 = "6164";
+ public static final String s6165 = "6165";
+ public static final String s6166 = "6166";
+ public static final String s6167 = "6167";
+ public static final String s6168 = "6168";
+ public static final String s6169 = "6169";
+ public static final String s6170 = "6170";
+ public static final String s6171 = "6171";
+ public static final String s6172 = "6172";
+ public static final String s6173 = "6173";
+ public static final String s6174 = "6174";
+ public static final String s6175 = "6175";
+ public static final String s6176 = "6176";
+ public static final String s6177 = "6177";
+ public static final String s6178 = "6178";
+ public static final String s6179 = "6179";
+ public static final String s6180 = "6180";
+ public static final String s6181 = "6181";
+ public static final String s6182 = "6182";
+ public static final String s6183 = "6183";
+ public static final String s6184 = "6184";
+ public static final String s6185 = "6185";
+ public static final String s6186 = "6186";
+ public static final String s6187 = "6187";
+ public static final String s6188 = "6188";
+ public static final String s6189 = "6189";
+ public static final String s6190 = "6190";
+ public static final String s6191 = "6191";
+ public static final String s6192 = "6192";
+ public static final String s6193 = "6193";
+ public static final String s6194 = "6194";
+ public static final String s6195 = "6195";
+ public static final String s6196 = "6196";
+ public static final String s6197 = "6197";
+ public static final String s6198 = "6198";
+ public static final String s6199 = "6199";
+ public static final String s6200 = "6200";
+ public static final String s6201 = "6201";
+ public static final String s6202 = "6202";
+ public static final String s6203 = "6203";
+ public static final String s6204 = "6204";
+ public static final String s6205 = "6205";
+ public static final String s6206 = "6206";
+ public static final String s6207 = "6207";
+ public static final String s6208 = "6208";
+ public static final String s6209 = "6209";
+ public static final String s6210 = "6210";
+ public static final String s6211 = "6211";
+ public static final String s6212 = "6212";
+ public static final String s6213 = "6213";
+ public static final String s6214 = "6214";
+ public static final String s6215 = "6215";
+ public static final String s6216 = "6216";
+ public static final String s6217 = "6217";
+ public static final String s6218 = "6218";
+ public static final String s6219 = "6219";
+ public static final String s6220 = "6220";
+ public static final String s6221 = "6221";
+ public static final String s6222 = "6222";
+ public static final String s6223 = "6223";
+ public static final String s6224 = "6224";
+ public static final String s6225 = "6225";
+ public static final String s6226 = "6226";
+ public static final String s6227 = "6227";
+ public static final String s6228 = "6228";
+ public static final String s6229 = "6229";
+ public static final String s6230 = "6230";
+ public static final String s6231 = "6231";
+ public static final String s6232 = "6232";
+ public static final String s6233 = "6233";
+ public static final String s6234 = "6234";
+ public static final String s6235 = "6235";
+ public static final String s6236 = "6236";
+ public static final String s6237 = "6237";
+ public static final String s6238 = "6238";
+ public static final String s6239 = "6239";
+ public static final String s6240 = "6240";
+ public static final String s6241 = "6241";
+ public static final String s6242 = "6242";
+ public static final String s6243 = "6243";
+ public static final String s6244 = "6244";
+ public static final String s6245 = "6245";
+ public static final String s6246 = "6246";
+ public static final String s6247 = "6247";
+ public static final String s6248 = "6248";
+ public static final String s6249 = "6249";
+ public static final String s6250 = "6250";
+ public static final String s6251 = "6251";
+ public static final String s6252 = "6252";
+ public static final String s6253 = "6253";
+ public static final String s6254 = "6254";
+ public static final String s6255 = "6255";
+ public static final String s6256 = "6256";
+ public static final String s6257 = "6257";
+ public static final String s6258 = "6258";
+ public static final String s6259 = "6259";
+ public static final String s6260 = "6260";
+ public static final String s6261 = "6261";
+ public static final String s6262 = "6262";
+ public static final String s6263 = "6263";
+ public static final String s6264 = "6264";
+ public static final String s6265 = "6265";
+ public static final String s6266 = "6266";
+ public static final String s6267 = "6267";
+ public static final String s6268 = "6268";
+ public static final String s6269 = "6269";
+ public static final String s6270 = "6270";
+ public static final String s6271 = "6271";
+ public static final String s6272 = "6272";
+ public static final String s6273 = "6273";
+ public static final String s6274 = "6274";
+ public static final String s6275 = "6275";
+ public static final String s6276 = "6276";
+ public static final String s6277 = "6277";
+ public static final String s6278 = "6278";
+ public static final String s6279 = "6279";
+ public static final String s6280 = "6280";
+ public static final String s6281 = "6281";
+ public static final String s6282 = "6282";
+ public static final String s6283 = "6283";
+ public static final String s6284 = "6284";
+ public static final String s6285 = "6285";
+ public static final String s6286 = "6286";
+ public static final String s6287 = "6287";
+ public static final String s6288 = "6288";
+ public static final String s6289 = "6289";
+ public static final String s6290 = "6290";
+ public static final String s6291 = "6291";
+ public static final String s6292 = "6292";
+ public static final String s6293 = "6293";
+ public static final String s6294 = "6294";
+ public static final String s6295 = "6295";
+ public static final String s6296 = "6296";
+ public static final String s6297 = "6297";
+ public static final String s6298 = "6298";
+ public static final String s6299 = "6299";
+ public static final String s6300 = "6300";
+ public static final String s6301 = "6301";
+ public static final String s6302 = "6302";
+ public static final String s6303 = "6303";
+ public static final String s6304 = "6304";
+ public static final String s6305 = "6305";
+ public static final String s6306 = "6306";
+ public static final String s6307 = "6307";
+ public static final String s6308 = "6308";
+ public static final String s6309 = "6309";
+ public static final String s6310 = "6310";
+ public static final String s6311 = "6311";
+ public static final String s6312 = "6312";
+ public static final String s6313 = "6313";
+ public static final String s6314 = "6314";
+ public static final String s6315 = "6315";
+ public static final String s6316 = "6316";
+ public static final String s6317 = "6317";
+ public static final String s6318 = "6318";
+ public static final String s6319 = "6319";
+ public static final String s6320 = "6320";
+ public static final String s6321 = "6321";
+ public static final String s6322 = "6322";
+ public static final String s6323 = "6323";
+ public static final String s6324 = "6324";
+ public static final String s6325 = "6325";
+ public static final String s6326 = "6326";
+ public static final String s6327 = "6327";
+ public static final String s6328 = "6328";
+ public static final String s6329 = "6329";
+ public static final String s6330 = "6330";
+ public static final String s6331 = "6331";
+ public static final String s6332 = "6332";
+ public static final String s6333 = "6333";
+ public static final String s6334 = "6334";
+ public static final String s6335 = "6335";
+ public static final String s6336 = "6336";
+ public static final String s6337 = "6337";
+ public static final String s6338 = "6338";
+ public static final String s6339 = "6339";
+ public static final String s6340 = "6340";
+ public static final String s6341 = "6341";
+ public static final String s6342 = "6342";
+ public static final String s6343 = "6343";
+ public static final String s6344 = "6344";
+ public static final String s6345 = "6345";
+ public static final String s6346 = "6346";
+ public static final String s6347 = "6347";
+ public static final String s6348 = "6348";
+ public static final String s6349 = "6349";
+ public static final String s6350 = "6350";
+ public static final String s6351 = "6351";
+ public static final String s6352 = "6352";
+ public static final String s6353 = "6353";
+ public static final String s6354 = "6354";
+ public static final String s6355 = "6355";
+ public static final String s6356 = "6356";
+ public static final String s6357 = "6357";
+ public static final String s6358 = "6358";
+ public static final String s6359 = "6359";
+ public static final String s6360 = "6360";
+ public static final String s6361 = "6361";
+ public static final String s6362 = "6362";
+ public static final String s6363 = "6363";
+ public static final String s6364 = "6364";
+ public static final String s6365 = "6365";
+ public static final String s6366 = "6366";
+ public static final String s6367 = "6367";
+ public static final String s6368 = "6368";
+ public static final String s6369 = "6369";
+ public static final String s6370 = "6370";
+ public static final String s6371 = "6371";
+ public static final String s6372 = "6372";
+ public static final String s6373 = "6373";
+ public static final String s6374 = "6374";
+ public static final String s6375 = "6375";
+ public static final String s6376 = "6376";
+ public static final String s6377 = "6377";
+ public static final String s6378 = "6378";
+ public static final String s6379 = "6379";
+ public static final String s6380 = "6380";
+ public static final String s6381 = "6381";
+ public static final String s6382 = "6382";
+ public static final String s6383 = "6383";
+ public static final String s6384 = "6384";
+ public static final String s6385 = "6385";
+ public static final String s6386 = "6386";
+ public static final String s6387 = "6387";
+ public static final String s6388 = "6388";
+ public static final String s6389 = "6389";
+ public static final String s6390 = "6390";
+ public static final String s6391 = "6391";
+ public static final String s6392 = "6392";
+ public static final String s6393 = "6393";
+ public static final String s6394 = "6394";
+ public static final String s6395 = "6395";
+ public static final String s6396 = "6396";
+ public static final String s6397 = "6397";
+ public static final String s6398 = "6398";
+ public static final String s6399 = "6399";
+ public static final String s6400 = "6400";
+ public static final String s6401 = "6401";
+ public static final String s6402 = "6402";
+ public static final String s6403 = "6403";
+ public static final String s6404 = "6404";
+ public static final String s6405 = "6405";
+ public static final String s6406 = "6406";
+ public static final String s6407 = "6407";
+ public static final String s6408 = "6408";
+ public static final String s6409 = "6409";
+ public static final String s6410 = "6410";
+ public static final String s6411 = "6411";
+ public static final String s6412 = "6412";
+ public static final String s6413 = "6413";
+ public static final String s6414 = "6414";
+ public static final String s6415 = "6415";
+ public static final String s6416 = "6416";
+ public static final String s6417 = "6417";
+ public static final String s6418 = "6418";
+ public static final String s6419 = "6419";
+ public static final String s6420 = "6420";
+ public static final String s6421 = "6421";
+ public static final String s6422 = "6422";
+ public static final String s6423 = "6423";
+ public static final String s6424 = "6424";
+ public static final String s6425 = "6425";
+ public static final String s6426 = "6426";
+ public static final String s6427 = "6427";
+ public static final String s6428 = "6428";
+ public static final String s6429 = "6429";
+ public static final String s6430 = "6430";
+ public static final String s6431 = "6431";
+ public static final String s6432 = "6432";
+ public static final String s6433 = "6433";
+ public static final String s6434 = "6434";
+ public static final String s6435 = "6435";
+ public static final String s6436 = "6436";
+ public static final String s6437 = "6437";
+ public static final String s6438 = "6438";
+ public static final String s6439 = "6439";
+ public static final String s6440 = "6440";
+ public static final String s6441 = "6441";
+ public static final String s6442 = "6442";
+ public static final String s6443 = "6443";
+ public static final String s6444 = "6444";
+ public static final String s6445 = "6445";
+ public static final String s6446 = "6446";
+ public static final String s6447 = "6447";
+ public static final String s6448 = "6448";
+ public static final String s6449 = "6449";
+ public static final String s6450 = "6450";
+ public static final String s6451 = "6451";
+ public static final String s6452 = "6452";
+ public static final String s6453 = "6453";
+ public static final String s6454 = "6454";
+ public static final String s6455 = "6455";
+ public static final String s6456 = "6456";
+ public static final String s6457 = "6457";
+ public static final String s6458 = "6458";
+ public static final String s6459 = "6459";
+ public static final String s6460 = "6460";
+ public static final String s6461 = "6461";
+ public static final String s6462 = "6462";
+ public static final String s6463 = "6463";
+ public static final String s6464 = "6464";
+ public static final String s6465 = "6465";
+ public static final String s6466 = "6466";
+ public static final String s6467 = "6467";
+ public static final String s6468 = "6468";
+ public static final String s6469 = "6469";
+ public static final String s6470 = "6470";
+ public static final String s6471 = "6471";
+ public static final String s6472 = "6472";
+ public static final String s6473 = "6473";
+ public static final String s6474 = "6474";
+ public static final String s6475 = "6475";
+ public static final String s6476 = "6476";
+ public static final String s6477 = "6477";
+ public static final String s6478 = "6478";
+ public static final String s6479 = "6479";
+ public static final String s6480 = "6480";
+ public static final String s6481 = "6481";
+ public static final String s6482 = "6482";
+ public static final String s6483 = "6483";
+ public static final String s6484 = "6484";
+ public static final String s6485 = "6485";
+ public static final String s6486 = "6486";
+ public static final String s6487 = "6487";
+ public static final String s6488 = "6488";
+ public static final String s6489 = "6489";
+ public static final String s6490 = "6490";
+ public static final String s6491 = "6491";
+ public static final String s6492 = "6492";
+ public static final String s6493 = "6493";
+ public static final String s6494 = "6494";
+ public static final String s6495 = "6495";
+ public static final String s6496 = "6496";
+ public static final String s6497 = "6497";
+ public static final String s6498 = "6498";
+ public static final String s6499 = "6499";
+ public static final String s6500 = "6500";
+ public static final String s6501 = "6501";
+ public static final String s6502 = "6502";
+ public static final String s6503 = "6503";
+ public static final String s6504 = "6504";
+ public static final String s6505 = "6505";
+ public static final String s6506 = "6506";
+ public static final String s6507 = "6507";
+ public static final String s6508 = "6508";
+ public static final String s6509 = "6509";
+ public static final String s6510 = "6510";
+ public static final String s6511 = "6511";
+ public static final String s6512 = "6512";
+ public static final String s6513 = "6513";
+ public static final String s6514 = "6514";
+ public static final String s6515 = "6515";
+ public static final String s6516 = "6516";
+ public static final String s6517 = "6517";
+ public static final String s6518 = "6518";
+ public static final String s6519 = "6519";
+ public static final String s6520 = "6520";
+ public static final String s6521 = "6521";
+ public static final String s6522 = "6522";
+ public static final String s6523 = "6523";
+ public static final String s6524 = "6524";
+ public static final String s6525 = "6525";
+ public static final String s6526 = "6526";
+ public static final String s6527 = "6527";
+ public static final String s6528 = "6528";
+ public static final String s6529 = "6529";
+ public static final String s6530 = "6530";
+ public static final String s6531 = "6531";
+ public static final String s6532 = "6532";
+ public static final String s6533 = "6533";
+ public static final String s6534 = "6534";
+ public static final String s6535 = "6535";
+ public static final String s6536 = "6536";
+ public static final String s6537 = "6537";
+ public static final String s6538 = "6538";
+ public static final String s6539 = "6539";
+ public static final String s6540 = "6540";
+ public static final String s6541 = "6541";
+ public static final String s6542 = "6542";
+ public static final String s6543 = "6543";
+ public static final String s6544 = "6544";
+ public static final String s6545 = "6545";
+ public static final String s6546 = "6546";
+ public static final String s6547 = "6547";
+ public static final String s6548 = "6548";
+ public static final String s6549 = "6549";
+ public static final String s6550 = "6550";
+ public static final String s6551 = "6551";
+ public static final String s6552 = "6552";
+ public static final String s6553 = "6553";
+ public static final String s6554 = "6554";
+ public static final String s6555 = "6555";
+ public static final String s6556 = "6556";
+ public static final String s6557 = "6557";
+ public static final String s6558 = "6558";
+ public static final String s6559 = "6559";
+ public static final String s6560 = "6560";
+ public static final String s6561 = "6561";
+ public static final String s6562 = "6562";
+ public static final String s6563 = "6563";
+ public static final String s6564 = "6564";
+ public static final String s6565 = "6565";
+ public static final String s6566 = "6566";
+ public static final String s6567 = "6567";
+ public static final String s6568 = "6568";
+ public static final String s6569 = "6569";
+ public static final String s6570 = "6570";
+ public static final String s6571 = "6571";
+ public static final String s6572 = "6572";
+ public static final String s6573 = "6573";
+ public static final String s6574 = "6574";
+ public static final String s6575 = "6575";
+ public static final String s6576 = "6576";
+ public static final String s6577 = "6577";
+ public static final String s6578 = "6578";
+ public static final String s6579 = "6579";
+ public static final String s6580 = "6580";
+ public static final String s6581 = "6581";
+ public static final String s6582 = "6582";
+ public static final String s6583 = "6583";
+ public static final String s6584 = "6584";
+ public static final String s6585 = "6585";
+ public static final String s6586 = "6586";
+ public static final String s6587 = "6587";
+ public static final String s6588 = "6588";
+ public static final String s6589 = "6589";
+ public static final String s6590 = "6590";
+ public static final String s6591 = "6591";
+ public static final String s6592 = "6592";
+ public static final String s6593 = "6593";
+ public static final String s6594 = "6594";
+ public static final String s6595 = "6595";
+ public static final String s6596 = "6596";
+ public static final String s6597 = "6597";
+ public static final String s6598 = "6598";
+ public static final String s6599 = "6599";
+ public static final String s6600 = "6600";
+ public static final String s6601 = "6601";
+ public static final String s6602 = "6602";
+ public static final String s6603 = "6603";
+ public static final String s6604 = "6604";
+ public static final String s6605 = "6605";
+ public static final String s6606 = "6606";
+ public static final String s6607 = "6607";
+ public static final String s6608 = "6608";
+ public static final String s6609 = "6609";
+ public static final String s6610 = "6610";
+ public static final String s6611 = "6611";
+ public static final String s6612 = "6612";
+ public static final String s6613 = "6613";
+ public static final String s6614 = "6614";
+ public static final String s6615 = "6615";
+ public static final String s6616 = "6616";
+ public static final String s6617 = "6617";
+ public static final String s6618 = "6618";
+ public static final String s6619 = "6619";
+ public static final String s6620 = "6620";
+ public static final String s6621 = "6621";
+ public static final String s6622 = "6622";
+ public static final String s6623 = "6623";
+ public static final String s6624 = "6624";
+ public static final String s6625 = "6625";
+ public static final String s6626 = "6626";
+ public static final String s6627 = "6627";
+ public static final String s6628 = "6628";
+ public static final String s6629 = "6629";
+ public static final String s6630 = "6630";
+ public static final String s6631 = "6631";
+ public static final String s6632 = "6632";
+ public static final String s6633 = "6633";
+ public static final String s6634 = "6634";
+ public static final String s6635 = "6635";
+ public static final String s6636 = "6636";
+ public static final String s6637 = "6637";
+ public static final String s6638 = "6638";
+ public static final String s6639 = "6639";
+ public static final String s6640 = "6640";
+ public static final String s6641 = "6641";
+ public static final String s6642 = "6642";
+ public static final String s6643 = "6643";
+ public static final String s6644 = "6644";
+ public static final String s6645 = "6645";
+ public static final String s6646 = "6646";
+ public static final String s6647 = "6647";
+ public static final String s6648 = "6648";
+ public static final String s6649 = "6649";
+ public static final String s6650 = "6650";
+ public static final String s6651 = "6651";
+ public static final String s6652 = "6652";
+ public static final String s6653 = "6653";
+ public static final String s6654 = "6654";
+ public static final String s6655 = "6655";
+ public static final String s6656 = "6656";
+ public static final String s6657 = "6657";
+ public static final String s6658 = "6658";
+ public static final String s6659 = "6659";
+ public static final String s6660 = "6660";
+ public static final String s6661 = "6661";
+ public static final String s6662 = "6662";
+ public static final String s6663 = "6663";
+ public static final String s6664 = "6664";
+ public static final String s6665 = "6665";
+ public static final String s6666 = "6666";
+ public static final String s6667 = "6667";
+ public static final String s6668 = "6668";
+ public static final String s6669 = "6669";
+ public static final String s6670 = "6670";
+ public static final String s6671 = "6671";
+ public static final String s6672 = "6672";
+ public static final String s6673 = "6673";
+ public static final String s6674 = "6674";
+ public static final String s6675 = "6675";
+ public static final String s6676 = "6676";
+ public static final String s6677 = "6677";
+ public static final String s6678 = "6678";
+ public static final String s6679 = "6679";
+ public static final String s6680 = "6680";
+ public static final String s6681 = "6681";
+ public static final String s6682 = "6682";
+ public static final String s6683 = "6683";
+ public static final String s6684 = "6684";
+ public static final String s6685 = "6685";
+ public static final String s6686 = "6686";
+ public static final String s6687 = "6687";
+ public static final String s6688 = "6688";
+ public static final String s6689 = "6689";
+ public static final String s6690 = "6690";
+ public static final String s6691 = "6691";
+ public static final String s6692 = "6692";
+ public static final String s6693 = "6693";
+ public static final String s6694 = "6694";
+ public static final String s6695 = "6695";
+ public static final String s6696 = "6696";
+ public static final String s6697 = "6697";
+ public static final String s6698 = "6698";
+ public static final String s6699 = "6699";
+ public static final String s6700 = "6700";
+ public static final String s6701 = "6701";
+ public static final String s6702 = "6702";
+ public static final String s6703 = "6703";
+ public static final String s6704 = "6704";
+ public static final String s6705 = "6705";
+ public static final String s6706 = "6706";
+ public static final String s6707 = "6707";
+ public static final String s6708 = "6708";
+ public static final String s6709 = "6709";
+ public static final String s6710 = "6710";
+ public static final String s6711 = "6711";
+ public static final String s6712 = "6712";
+ public static final String s6713 = "6713";
+ public static final String s6714 = "6714";
+ public static final String s6715 = "6715";
+ public static final String s6716 = "6716";
+ public static final String s6717 = "6717";
+ public static final String s6718 = "6718";
+ public static final String s6719 = "6719";
+ public static final String s6720 = "6720";
+ public static final String s6721 = "6721";
+ public static final String s6722 = "6722";
+ public static final String s6723 = "6723";
+ public static final String s6724 = "6724";
+ public static final String s6725 = "6725";
+ public static final String s6726 = "6726";
+ public static final String s6727 = "6727";
+ public static final String s6728 = "6728";
+ public static final String s6729 = "6729";
+ public static final String s6730 = "6730";
+ public static final String s6731 = "6731";
+ public static final String s6732 = "6732";
+ public static final String s6733 = "6733";
+ public static final String s6734 = "6734";
+ public static final String s6735 = "6735";
+ public static final String s6736 = "6736";
+ public static final String s6737 = "6737";
+ public static final String s6738 = "6738";
+ public static final String s6739 = "6739";
+ public static final String s6740 = "6740";
+ public static final String s6741 = "6741";
+ public static final String s6742 = "6742";
+ public static final String s6743 = "6743";
+ public static final String s6744 = "6744";
+ public static final String s6745 = "6745";
+ public static final String s6746 = "6746";
+ public static final String s6747 = "6747";
+ public static final String s6748 = "6748";
+ public static final String s6749 = "6749";
+ public static final String s6750 = "6750";
+ public static final String s6751 = "6751";
+ public static final String s6752 = "6752";
+ public static final String s6753 = "6753";
+ public static final String s6754 = "6754";
+ public static final String s6755 = "6755";
+ public static final String s6756 = "6756";
+ public static final String s6757 = "6757";
+ public static final String s6758 = "6758";
+ public static final String s6759 = "6759";
+ public static final String s6760 = "6760";
+ public static final String s6761 = "6761";
+ public static final String s6762 = "6762";
+ public static final String s6763 = "6763";
+ public static final String s6764 = "6764";
+ public static final String s6765 = "6765";
+ public static final String s6766 = "6766";
+ public static final String s6767 = "6767";
+ public static final String s6768 = "6768";
+ public static final String s6769 = "6769";
+ public static final String s6770 = "6770";
+ public static final String s6771 = "6771";
+ public static final String s6772 = "6772";
+ public static final String s6773 = "6773";
+ public static final String s6774 = "6774";
+ public static final String s6775 = "6775";
+ public static final String s6776 = "6776";
+ public static final String s6777 = "6777";
+ public static final String s6778 = "6778";
+ public static final String s6779 = "6779";
+ public static final String s6780 = "6780";
+ public static final String s6781 = "6781";
+ public static final String s6782 = "6782";
+ public static final String s6783 = "6783";
+ public static final String s6784 = "6784";
+ public static final String s6785 = "6785";
+ public static final String s6786 = "6786";
+ public static final String s6787 = "6787";
+ public static final String s6788 = "6788";
+ public static final String s6789 = "6789";
+ public static final String s6790 = "6790";
+ public static final String s6791 = "6791";
+ public static final String s6792 = "6792";
+ public static final String s6793 = "6793";
+ public static final String s6794 = "6794";
+ public static final String s6795 = "6795";
+ public static final String s6796 = "6796";
+ public static final String s6797 = "6797";
+ public static final String s6798 = "6798";
+ public static final String s6799 = "6799";
+ public static final String s6800 = "6800";
+ public static final String s6801 = "6801";
+ public static final String s6802 = "6802";
+ public static final String s6803 = "6803";
+ public static final String s6804 = "6804";
+ public static final String s6805 = "6805";
+ public static final String s6806 = "6806";
+ public static final String s6807 = "6807";
+ public static final String s6808 = "6808";
+ public static final String s6809 = "6809";
+ public static final String s6810 = "6810";
+ public static final String s6811 = "6811";
+ public static final String s6812 = "6812";
+ public static final String s6813 = "6813";
+ public static final String s6814 = "6814";
+ public static final String s6815 = "6815";
+ public static final String s6816 = "6816";
+ public static final String s6817 = "6817";
+ public static final String s6818 = "6818";
+ public static final String s6819 = "6819";
+ public static final String s6820 = "6820";
+ public static final String s6821 = "6821";
+ public static final String s6822 = "6822";
+ public static final String s6823 = "6823";
+ public static final String s6824 = "6824";
+ public static final String s6825 = "6825";
+ public static final String s6826 = "6826";
+ public static final String s6827 = "6827";
+ public static final String s6828 = "6828";
+ public static final String s6829 = "6829";
+ public static final String s6830 = "6830";
+ public static final String s6831 = "6831";
+ public static final String s6832 = "6832";
+ public static final String s6833 = "6833";
+ public static final String s6834 = "6834";
+ public static final String s6835 = "6835";
+ public static final String s6836 = "6836";
+ public static final String s6837 = "6837";
+ public static final String s6838 = "6838";
+ public static final String s6839 = "6839";
+ public static final String s6840 = "6840";
+ public static final String s6841 = "6841";
+ public static final String s6842 = "6842";
+ public static final String s6843 = "6843";
+ public static final String s6844 = "6844";
+ public static final String s6845 = "6845";
+ public static final String s6846 = "6846";
+ public static final String s6847 = "6847";
+ public static final String s6848 = "6848";
+ public static final String s6849 = "6849";
+ public static final String s6850 = "6850";
+ public static final String s6851 = "6851";
+ public static final String s6852 = "6852";
+ public static final String s6853 = "6853";
+ public static final String s6854 = "6854";
+ public static final String s6855 = "6855";
+ public static final String s6856 = "6856";
+ public static final String s6857 = "6857";
+ public static final String s6858 = "6858";
+ public static final String s6859 = "6859";
+ public static final String s6860 = "6860";
+ public static final String s6861 = "6861";
+ public static final String s6862 = "6862";
+ public static final String s6863 = "6863";
+ public static final String s6864 = "6864";
+ public static final String s6865 = "6865";
+ public static final String s6866 = "6866";
+ public static final String s6867 = "6867";
+ public static final String s6868 = "6868";
+ public static final String s6869 = "6869";
+ public static final String s6870 = "6870";
+ public static final String s6871 = "6871";
+ public static final String s6872 = "6872";
+ public static final String s6873 = "6873";
+ public static final String s6874 = "6874";
+ public static final String s6875 = "6875";
+ public static final String s6876 = "6876";
+ public static final String s6877 = "6877";
+ public static final String s6878 = "6878";
+ public static final String s6879 = "6879";
+ public static final String s6880 = "6880";
+ public static final String s6881 = "6881";
+ public static final String s6882 = "6882";
+ public static final String s6883 = "6883";
+ public static final String s6884 = "6884";
+ public static final String s6885 = "6885";
+ public static final String s6886 = "6886";
+ public static final String s6887 = "6887";
+ public static final String s6888 = "6888";
+ public static final String s6889 = "6889";
+ public static final String s6890 = "6890";
+ public static final String s6891 = "6891";
+ public static final String s6892 = "6892";
+ public static final String s6893 = "6893";
+ public static final String s6894 = "6894";
+ public static final String s6895 = "6895";
+ public static final String s6896 = "6896";
+ public static final String s6897 = "6897";
+ public static final String s6898 = "6898";
+ public static final String s6899 = "6899";
+ public static final String s6900 = "6900";
+ public static final String s6901 = "6901";
+ public static final String s6902 = "6902";
+ public static final String s6903 = "6903";
+ public static final String s6904 = "6904";
+ public static final String s6905 = "6905";
+ public static final String s6906 = "6906";
+ public static final String s6907 = "6907";
+ public static final String s6908 = "6908";
+ public static final String s6909 = "6909";
+ public static final String s6910 = "6910";
+ public static final String s6911 = "6911";
+ public static final String s6912 = "6912";
+ public static final String s6913 = "6913";
+ public static final String s6914 = "6914";
+ public static final String s6915 = "6915";
+ public static final String s6916 = "6916";
+ public static final String s6917 = "6917";
+ public static final String s6918 = "6918";
+ public static final String s6919 = "6919";
+ public static final String s6920 = "6920";
+ public static final String s6921 = "6921";
+ public static final String s6922 = "6922";
+ public static final String s6923 = "6923";
+ public static final String s6924 = "6924";
+ public static final String s6925 = "6925";
+ public static final String s6926 = "6926";
+ public static final String s6927 = "6927";
+ public static final String s6928 = "6928";
+ public static final String s6929 = "6929";
+ public static final String s6930 = "6930";
+ public static final String s6931 = "6931";
+ public static final String s6932 = "6932";
+ public static final String s6933 = "6933";
+ public static final String s6934 = "6934";
+ public static final String s6935 = "6935";
+ public static final String s6936 = "6936";
+ public static final String s6937 = "6937";
+ public static final String s6938 = "6938";
+ public static final String s6939 = "6939";
+ public static final String s6940 = "6940";
+ public static final String s6941 = "6941";
+ public static final String s6942 = "6942";
+ public static final String s6943 = "6943";
+ public static final String s6944 = "6944";
+ public static final String s6945 = "6945";
+ public static final String s6946 = "6946";
+ public static final String s6947 = "6947";
+ public static final String s6948 = "6948";
+ public static final String s6949 = "6949";
+ public static final String s6950 = "6950";
+ public static final String s6951 = "6951";
+ public static final String s6952 = "6952";
+ public static final String s6953 = "6953";
+ public static final String s6954 = "6954";
+ public static final String s6955 = "6955";
+ public static final String s6956 = "6956";
+ public static final String s6957 = "6957";
+ public static final String s6958 = "6958";
+ public static final String s6959 = "6959";
+ public static final String s6960 = "6960";
+ public static final String s6961 = "6961";
+ public static final String s6962 = "6962";
+ public static final String s6963 = "6963";
+ public static final String s6964 = "6964";
+ public static final String s6965 = "6965";
+ public static final String s6966 = "6966";
+ public static final String s6967 = "6967";
+ public static final String s6968 = "6968";
+ public static final String s6969 = "6969";
+ public static final String s6970 = "6970";
+ public static final String s6971 = "6971";
+ public static final String s6972 = "6972";
+ public static final String s6973 = "6973";
+ public static final String s6974 = "6974";
+ public static final String s6975 = "6975";
+ public static final String s6976 = "6976";
+ public static final String s6977 = "6977";
+ public static final String s6978 = "6978";
+ public static final String s6979 = "6979";
+ public static final String s6980 = "6980";
+ public static final String s6981 = "6981";
+ public static final String s6982 = "6982";
+ public static final String s6983 = "6983";
+ public static final String s6984 = "6984";
+ public static final String s6985 = "6985";
+ public static final String s6986 = "6986";
+ public static final String s6987 = "6987";
+ public static final String s6988 = "6988";
+ public static final String s6989 = "6989";
+ public static final String s6990 = "6990";
+ public static final String s6991 = "6991";
+ public static final String s6992 = "6992";
+ public static final String s6993 = "6993";
+ public static final String s6994 = "6994";
+ public static final String s6995 = "6995";
+ public static final String s6996 = "6996";
+ public static final String s6997 = "6997";
+ public static final String s6998 = "6998";
+ public static final String s6999 = "6999";
+ public static final String s7000 = "7000";
+ public static final String s7001 = "7001";
+ public static final String s7002 = "7002";
+ public static final String s7003 = "7003";
+ public static final String s7004 = "7004";
+ public static final String s7005 = "7005";
+ public static final String s7006 = "7006";
+ public static final String s7007 = "7007";
+ public static final String s7008 = "7008";
+ public static final String s7009 = "7009";
+ public static final String s7010 = "7010";
+ public static final String s7011 = "7011";
+ public static final String s7012 = "7012";
+ public static final String s7013 = "7013";
+ public static final String s7014 = "7014";
+ public static final String s7015 = "7015";
+ public static final String s7016 = "7016";
+ public static final String s7017 = "7017";
+ public static final String s7018 = "7018";
+ public static final String s7019 = "7019";
+ public static final String s7020 = "7020";
+ public static final String s7021 = "7021";
+ public static final String s7022 = "7022";
+ public static final String s7023 = "7023";
+ public static final String s7024 = "7024";
+ public static final String s7025 = "7025";
+ public static final String s7026 = "7026";
+ public static final String s7027 = "7027";
+ public static final String s7028 = "7028";
+ public static final String s7029 = "7029";
+ public static final String s7030 = "7030";
+ public static final String s7031 = "7031";
+ public static final String s7032 = "7032";
+ public static final String s7033 = "7033";
+ public static final String s7034 = "7034";
+ public static final String s7035 = "7035";
+ public static final String s7036 = "7036";
+ public static final String s7037 = "7037";
+ public static final String s7038 = "7038";
+ public static final String s7039 = "7039";
+ public static final String s7040 = "7040";
+ public static final String s7041 = "7041";
+ public static final String s7042 = "7042";
+ public static final String s7043 = "7043";
+ public static final String s7044 = "7044";
+ public static final String s7045 = "7045";
+ public static final String s7046 = "7046";
+ public static final String s7047 = "7047";
+ public static final String s7048 = "7048";
+ public static final String s7049 = "7049";
+ public static final String s7050 = "7050";
+ public static final String s7051 = "7051";
+ public static final String s7052 = "7052";
+ public static final String s7053 = "7053";
+ public static final String s7054 = "7054";
+ public static final String s7055 = "7055";
+ public static final String s7056 = "7056";
+ public static final String s7057 = "7057";
+ public static final String s7058 = "7058";
+ public static final String s7059 = "7059";
+ public static final String s7060 = "7060";
+ public static final String s7061 = "7061";
+ public static final String s7062 = "7062";
+ public static final String s7063 = "7063";
+ public static final String s7064 = "7064";
+ public static final String s7065 = "7065";
+ public static final String s7066 = "7066";
+ public static final String s7067 = "7067";
+ public static final String s7068 = "7068";
+ public static final String s7069 = "7069";
+ public static final String s7070 = "7070";
+ public static final String s7071 = "7071";
+ public static final String s7072 = "7072";
+ public static final String s7073 = "7073";
+ public static final String s7074 = "7074";
+ public static final String s7075 = "7075";
+ public static final String s7076 = "7076";
+ public static final String s7077 = "7077";
+ public static final String s7078 = "7078";
+ public static final String s7079 = "7079";
+ public static final String s7080 = "7080";
+ public static final String s7081 = "7081";
+ public static final String s7082 = "7082";
+ public static final String s7083 = "7083";
+ public static final String s7084 = "7084";
+ public static final String s7085 = "7085";
+ public static final String s7086 = "7086";
+ public static final String s7087 = "7087";
+ public static final String s7088 = "7088";
+ public static final String s7089 = "7089";
+ public static final String s7090 = "7090";
+ public static final String s7091 = "7091";
+ public static final String s7092 = "7092";
+ public static final String s7093 = "7093";
+ public static final String s7094 = "7094";
+ public static final String s7095 = "7095";
+ public static final String s7096 = "7096";
+ public static final String s7097 = "7097";
+ public static final String s7098 = "7098";
+ public static final String s7099 = "7099";
+ public static final String s7100 = "7100";
+ public static final String s7101 = "7101";
+ public static final String s7102 = "7102";
+ public static final String s7103 = "7103";
+ public static final String s7104 = "7104";
+ public static final String s7105 = "7105";
+ public static final String s7106 = "7106";
+ public static final String s7107 = "7107";
+ public static final String s7108 = "7108";
+ public static final String s7109 = "7109";
+ public static final String s7110 = "7110";
+ public static final String s7111 = "7111";
+ public static final String s7112 = "7112";
+ public static final String s7113 = "7113";
+ public static final String s7114 = "7114";
+ public static final String s7115 = "7115";
+ public static final String s7116 = "7116";
+ public static final String s7117 = "7117";
+ public static final String s7118 = "7118";
+ public static final String s7119 = "7119";
+ public static final String s7120 = "7120";
+ public static final String s7121 = "7121";
+ public static final String s7122 = "7122";
+ public static final String s7123 = "7123";
+ public static final String s7124 = "7124";
+ public static final String s7125 = "7125";
+ public static final String s7126 = "7126";
+ public static final String s7127 = "7127";
+ public static final String s7128 = "7128";
+ public static final String s7129 = "7129";
+ public static final String s7130 = "7130";
+ public static final String s7131 = "7131";
+ public static final String s7132 = "7132";
+ public static final String s7133 = "7133";
+ public static final String s7134 = "7134";
+ public static final String s7135 = "7135";
+ public static final String s7136 = "7136";
+ public static final String s7137 = "7137";
+ public static final String s7138 = "7138";
+ public static final String s7139 = "7139";
+ public static final String s7140 = "7140";
+ public static final String s7141 = "7141";
+ public static final String s7142 = "7142";
+ public static final String s7143 = "7143";
+ public static final String s7144 = "7144";
+ public static final String s7145 = "7145";
+ public static final String s7146 = "7146";
+ public static final String s7147 = "7147";
+ public static final String s7148 = "7148";
+ public static final String s7149 = "7149";
+ public static final String s7150 = "7150";
+ public static final String s7151 = "7151";
+ public static final String s7152 = "7152";
+ public static final String s7153 = "7153";
+ public static final String s7154 = "7154";
+ public static final String s7155 = "7155";
+ public static final String s7156 = "7156";
+ public static final String s7157 = "7157";
+ public static final String s7158 = "7158";
+ public static final String s7159 = "7159";
+ public static final String s7160 = "7160";
+ public static final String s7161 = "7161";
+ public static final String s7162 = "7162";
+ public static final String s7163 = "7163";
+ public static final String s7164 = "7164";
+ public static final String s7165 = "7165";
+ public static final String s7166 = "7166";
+ public static final String s7167 = "7167";
+ public static final String s7168 = "7168";
+ public static final String s7169 = "7169";
+ public static final String s7170 = "7170";
+ public static final String s7171 = "7171";
+ public static final String s7172 = "7172";
+ public static final String s7173 = "7173";
+ public static final String s7174 = "7174";
+ public static final String s7175 = "7175";
+ public static final String s7176 = "7176";
+ public static final String s7177 = "7177";
+ public static final String s7178 = "7178";
+ public static final String s7179 = "7179";
+ public static final String s7180 = "7180";
+ public static final String s7181 = "7181";
+ public static final String s7182 = "7182";
+ public static final String s7183 = "7183";
+ public static final String s7184 = "7184";
+ public static final String s7185 = "7185";
+ public static final String s7186 = "7186";
+ public static final String s7187 = "7187";
+ public static final String s7188 = "7188";
+ public static final String s7189 = "7189";
+ public static final String s7190 = "7190";
+ public static final String s7191 = "7191";
+ public static final String s7192 = "7192";
+ public static final String s7193 = "7193";
+ public static final String s7194 = "7194";
+ public static final String s7195 = "7195";
+ public static final String s7196 = "7196";
+ public static final String s7197 = "7197";
+ public static final String s7198 = "7198";
+ public static final String s7199 = "7199";
+ public static final String s7200 = "7200";
+ public static final String s7201 = "7201";
+ public static final String s7202 = "7202";
+ public static final String s7203 = "7203";
+ public static final String s7204 = "7204";
+ public static final String s7205 = "7205";
+ public static final String s7206 = "7206";
+ public static final String s7207 = "7207";
+ public static final String s7208 = "7208";
+ public static final String s7209 = "7209";
+ public static final String s7210 = "7210";
+ public static final String s7211 = "7211";
+ public static final String s7212 = "7212";
+ public static final String s7213 = "7213";
+ public static final String s7214 = "7214";
+ public static final String s7215 = "7215";
+ public static final String s7216 = "7216";
+ public static final String s7217 = "7217";
+ public static final String s7218 = "7218";
+ public static final String s7219 = "7219";
+ public static final String s7220 = "7220";
+ public static final String s7221 = "7221";
+ public static final String s7222 = "7222";
+ public static final String s7223 = "7223";
+ public static final String s7224 = "7224";
+ public static final String s7225 = "7225";
+ public static final String s7226 = "7226";
+ public static final String s7227 = "7227";
+ public static final String s7228 = "7228";
+ public static final String s7229 = "7229";
+ public static final String s7230 = "7230";
+ public static final String s7231 = "7231";
+ public static final String s7232 = "7232";
+ public static final String s7233 = "7233";
+ public static final String s7234 = "7234";
+ public static final String s7235 = "7235";
+ public static final String s7236 = "7236";
+ public static final String s7237 = "7237";
+ public static final String s7238 = "7238";
+ public static final String s7239 = "7239";
+ public static final String s7240 = "7240";
+ public static final String s7241 = "7241";
+ public static final String s7242 = "7242";
+ public static final String s7243 = "7243";
+ public static final String s7244 = "7244";
+ public static final String s7245 = "7245";
+ public static final String s7246 = "7246";
+ public static final String s7247 = "7247";
+ public static final String s7248 = "7248";
+ public static final String s7249 = "7249";
+ public static final String s7250 = "7250";
+ public static final String s7251 = "7251";
+ public static final String s7252 = "7252";
+ public static final String s7253 = "7253";
+ public static final String s7254 = "7254";
+ public static final String s7255 = "7255";
+ public static final String s7256 = "7256";
+ public static final String s7257 = "7257";
+ public static final String s7258 = "7258";
+ public static final String s7259 = "7259";
+ public static final String s7260 = "7260";
+ public static final String s7261 = "7261";
+ public static final String s7262 = "7262";
+ public static final String s7263 = "7263";
+ public static final String s7264 = "7264";
+ public static final String s7265 = "7265";
+ public static final String s7266 = "7266";
+ public static final String s7267 = "7267";
+ public static final String s7268 = "7268";
+ public static final String s7269 = "7269";
+ public static final String s7270 = "7270";
+ public static final String s7271 = "7271";
+ public static final String s7272 = "7272";
+ public static final String s7273 = "7273";
+ public static final String s7274 = "7274";
+ public static final String s7275 = "7275";
+ public static final String s7276 = "7276";
+ public static final String s7277 = "7277";
+ public static final String s7278 = "7278";
+ public static final String s7279 = "7279";
+ public static final String s7280 = "7280";
+ public static final String s7281 = "7281";
+ public static final String s7282 = "7282";
+ public static final String s7283 = "7283";
+ public static final String s7284 = "7284";
+ public static final String s7285 = "7285";
+ public static final String s7286 = "7286";
+ public static final String s7287 = "7287";
+ public static final String s7288 = "7288";
+ public static final String s7289 = "7289";
+ public static final String s7290 = "7290";
+ public static final String s7291 = "7291";
+ public static final String s7292 = "7292";
+ public static final String s7293 = "7293";
+ public static final String s7294 = "7294";
+ public static final String s7295 = "7295";
+ public static final String s7296 = "7296";
+ public static final String s7297 = "7297";
+ public static final String s7298 = "7298";
+ public static final String s7299 = "7299";
+ public static final String s7300 = "7300";
+ public static final String s7301 = "7301";
+ public static final String s7302 = "7302";
+ public static final String s7303 = "7303";
+ public static final String s7304 = "7304";
+ public static final String s7305 = "7305";
+ public static final String s7306 = "7306";
+ public static final String s7307 = "7307";
+ public static final String s7308 = "7308";
+ public static final String s7309 = "7309";
+ public static final String s7310 = "7310";
+ public static final String s7311 = "7311";
+ public static final String s7312 = "7312";
+ public static final String s7313 = "7313";
+ public static final String s7314 = "7314";
+ public static final String s7315 = "7315";
+ public static final String s7316 = "7316";
+ public static final String s7317 = "7317";
+ public static final String s7318 = "7318";
+ public static final String s7319 = "7319";
+ public static final String s7320 = "7320";
+ public static final String s7321 = "7321";
+ public static final String s7322 = "7322";
+ public static final String s7323 = "7323";
+ public static final String s7324 = "7324";
+ public static final String s7325 = "7325";
+ public static final String s7326 = "7326";
+ public static final String s7327 = "7327";
+ public static final String s7328 = "7328";
+ public static final String s7329 = "7329";
+ public static final String s7330 = "7330";
+ public static final String s7331 = "7331";
+ public static final String s7332 = "7332";
+ public static final String s7333 = "7333";
+ public static final String s7334 = "7334";
+ public static final String s7335 = "7335";
+ public static final String s7336 = "7336";
+ public static final String s7337 = "7337";
+ public static final String s7338 = "7338";
+ public static final String s7339 = "7339";
+ public static final String s7340 = "7340";
+ public static final String s7341 = "7341";
+ public static final String s7342 = "7342";
+ public static final String s7343 = "7343";
+ public static final String s7344 = "7344";
+ public static final String s7345 = "7345";
+ public static final String s7346 = "7346";
+ public static final String s7347 = "7347";
+ public static final String s7348 = "7348";
+ public static final String s7349 = "7349";
+ public static final String s7350 = "7350";
+ public static final String s7351 = "7351";
+ public static final String s7352 = "7352";
+ public static final String s7353 = "7353";
+ public static final String s7354 = "7354";
+ public static final String s7355 = "7355";
+ public static final String s7356 = "7356";
+ public static final String s7357 = "7357";
+ public static final String s7358 = "7358";
+ public static final String s7359 = "7359";
+ public static final String s7360 = "7360";
+ public static final String s7361 = "7361";
+ public static final String s7362 = "7362";
+ public static final String s7363 = "7363";
+ public static final String s7364 = "7364";
+ public static final String s7365 = "7365";
+ public static final String s7366 = "7366";
+ public static final String s7367 = "7367";
+ public static final String s7368 = "7368";
+ public static final String s7369 = "7369";
+ public static final String s7370 = "7370";
+ public static final String s7371 = "7371";
+ public static final String s7372 = "7372";
+ public static final String s7373 = "7373";
+ public static final String s7374 = "7374";
+ public static final String s7375 = "7375";
+ public static final String s7376 = "7376";
+ public static final String s7377 = "7377";
+ public static final String s7378 = "7378";
+ public static final String s7379 = "7379";
+ public static final String s7380 = "7380";
+ public static final String s7381 = "7381";
+ public static final String s7382 = "7382";
+ public static final String s7383 = "7383";
+ public static final String s7384 = "7384";
+ public static final String s7385 = "7385";
+ public static final String s7386 = "7386";
+ public static final String s7387 = "7387";
+ public static final String s7388 = "7388";
+ public static final String s7389 = "7389";
+ public static final String s7390 = "7390";
+ public static final String s7391 = "7391";
+ public static final String s7392 = "7392";
+ public static final String s7393 = "7393";
+ public static final String s7394 = "7394";
+ public static final String s7395 = "7395";
+ public static final String s7396 = "7396";
+ public static final String s7397 = "7397";
+ public static final String s7398 = "7398";
+ public static final String s7399 = "7399";
+ public static final String s7400 = "7400";
+ public static final String s7401 = "7401";
+ public static final String s7402 = "7402";
+ public static final String s7403 = "7403";
+ public static final String s7404 = "7404";
+ public static final String s7405 = "7405";
+ public static final String s7406 = "7406";
+ public static final String s7407 = "7407";
+ public static final String s7408 = "7408";
+ public static final String s7409 = "7409";
+ public static final String s7410 = "7410";
+ public static final String s7411 = "7411";
+ public static final String s7412 = "7412";
+ public static final String s7413 = "7413";
+ public static final String s7414 = "7414";
+ public static final String s7415 = "7415";
+ public static final String s7416 = "7416";
+ public static final String s7417 = "7417";
+ public static final String s7418 = "7418";
+ public static final String s7419 = "7419";
+ public static final String s7420 = "7420";
+ public static final String s7421 = "7421";
+ public static final String s7422 = "7422";
+ public static final String s7423 = "7423";
+ public static final String s7424 = "7424";
+ public static final String s7425 = "7425";
+ public static final String s7426 = "7426";
+ public static final String s7427 = "7427";
+ public static final String s7428 = "7428";
+ public static final String s7429 = "7429";
+ public static final String s7430 = "7430";
+ public static final String s7431 = "7431";
+ public static final String s7432 = "7432";
+ public static final String s7433 = "7433";
+ public static final String s7434 = "7434";
+ public static final String s7435 = "7435";
+ public static final String s7436 = "7436";
+ public static final String s7437 = "7437";
+ public static final String s7438 = "7438";
+ public static final String s7439 = "7439";
+ public static final String s7440 = "7440";
+ public static final String s7441 = "7441";
+ public static final String s7442 = "7442";
+ public static final String s7443 = "7443";
+ public static final String s7444 = "7444";
+ public static final String s7445 = "7445";
+ public static final String s7446 = "7446";
+ public static final String s7447 = "7447";
+ public static final String s7448 = "7448";
+ public static final String s7449 = "7449";
+ public static final String s7450 = "7450";
+ public static final String s7451 = "7451";
+ public static final String s7452 = "7452";
+ public static final String s7453 = "7453";
+ public static final String s7454 = "7454";
+ public static final String s7455 = "7455";
+ public static final String s7456 = "7456";
+ public static final String s7457 = "7457";
+ public static final String s7458 = "7458";
+ public static final String s7459 = "7459";
+ public static final String s7460 = "7460";
+ public static final String s7461 = "7461";
+ public static final String s7462 = "7462";
+ public static final String s7463 = "7463";
+ public static final String s7464 = "7464";
+ public static final String s7465 = "7465";
+ public static final String s7466 = "7466";
+ public static final String s7467 = "7467";
+ public static final String s7468 = "7468";
+ public static final String s7469 = "7469";
+ public static final String s7470 = "7470";
+ public static final String s7471 = "7471";
+ public static final String s7472 = "7472";
+ public static final String s7473 = "7473";
+ public static final String s7474 = "7474";
+ public static final String s7475 = "7475";
+ public static final String s7476 = "7476";
+ public static final String s7477 = "7477";
+ public static final String s7478 = "7478";
+ public static final String s7479 = "7479";
+ public static final String s7480 = "7480";
+ public static final String s7481 = "7481";
+ public static final String s7482 = "7482";
+ public static final String s7483 = "7483";
+ public static final String s7484 = "7484";
+ public static final String s7485 = "7485";
+ public static final String s7486 = "7486";
+ public static final String s7487 = "7487";
+ public static final String s7488 = "7488";
+ public static final String s7489 = "7489";
+ public static final String s7490 = "7490";
+ public static final String s7491 = "7491";
+ public static final String s7492 = "7492";
+ public static final String s7493 = "7493";
+ public static final String s7494 = "7494";
+ public static final String s7495 = "7495";
+ public static final String s7496 = "7496";
+ public static final String s7497 = "7497";
+ public static final String s7498 = "7498";
+ public static final String s7499 = "7499";
+ public static final String s7500 = "7500";
+ public static final String s7501 = "7501";
+ public static final String s7502 = "7502";
+ public static final String s7503 = "7503";
+ public static final String s7504 = "7504";
+ public static final String s7505 = "7505";
+ public static final String s7506 = "7506";
+ public static final String s7507 = "7507";
+ public static final String s7508 = "7508";
+ public static final String s7509 = "7509";
+ public static final String s7510 = "7510";
+ public static final String s7511 = "7511";
+ public static final String s7512 = "7512";
+ public static final String s7513 = "7513";
+ public static final String s7514 = "7514";
+ public static final String s7515 = "7515";
+ public static final String s7516 = "7516";
+ public static final String s7517 = "7517";
+ public static final String s7518 = "7518";
+ public static final String s7519 = "7519";
+ public static final String s7520 = "7520";
+ public static final String s7521 = "7521";
+ public static final String s7522 = "7522";
+ public static final String s7523 = "7523";
+ public static final String s7524 = "7524";
+ public static final String s7525 = "7525";
+ public static final String s7526 = "7526";
+ public static final String s7527 = "7527";
+ public static final String s7528 = "7528";
+ public static final String s7529 = "7529";
+ public static final String s7530 = "7530";
+ public static final String s7531 = "7531";
+ public static final String s7532 = "7532";
+ public static final String s7533 = "7533";
+ public static final String s7534 = "7534";
+ public static final String s7535 = "7535";
+ public static final String s7536 = "7536";
+ public static final String s7537 = "7537";
+ public static final String s7538 = "7538";
+ public static final String s7539 = "7539";
+ public static final String s7540 = "7540";
+ public static final String s7541 = "7541";
+ public static final String s7542 = "7542";
+ public static final String s7543 = "7543";
+ public static final String s7544 = "7544";
+ public static final String s7545 = "7545";
+ public static final String s7546 = "7546";
+ public static final String s7547 = "7547";
+ public static final String s7548 = "7548";
+ public static final String s7549 = "7549";
+ public static final String s7550 = "7550";
+ public static final String s7551 = "7551";
+ public static final String s7552 = "7552";
+ public static final String s7553 = "7553";
+ public static final String s7554 = "7554";
+ public static final String s7555 = "7555";
+ public static final String s7556 = "7556";
+ public static final String s7557 = "7557";
+ public static final String s7558 = "7558";
+ public static final String s7559 = "7559";
+ public static final String s7560 = "7560";
+ public static final String s7561 = "7561";
+ public static final String s7562 = "7562";
+ public static final String s7563 = "7563";
+ public static final String s7564 = "7564";
+ public static final String s7565 = "7565";
+ public static final String s7566 = "7566";
+ public static final String s7567 = "7567";
+ public static final String s7568 = "7568";
+ public static final String s7569 = "7569";
+ public static final String s7570 = "7570";
+ public static final String s7571 = "7571";
+ public static final String s7572 = "7572";
+ public static final String s7573 = "7573";
+ public static final String s7574 = "7574";
+ public static final String s7575 = "7575";
+ public static final String s7576 = "7576";
+ public static final String s7577 = "7577";
+ public static final String s7578 = "7578";
+ public static final String s7579 = "7579";
+ public static final String s7580 = "7580";
+ public static final String s7581 = "7581";
+ public static final String s7582 = "7582";
+ public static final String s7583 = "7583";
+ public static final String s7584 = "7584";
+ public static final String s7585 = "7585";
+ public static final String s7586 = "7586";
+ public static final String s7587 = "7587";
+ public static final String s7588 = "7588";
+ public static final String s7589 = "7589";
+ public static final String s7590 = "7590";
+ public static final String s7591 = "7591";
+ public static final String s7592 = "7592";
+ public static final String s7593 = "7593";
+ public static final String s7594 = "7594";
+ public static final String s7595 = "7595";
+ public static final String s7596 = "7596";
+ public static final String s7597 = "7597";
+ public static final String s7598 = "7598";
+ public static final String s7599 = "7599";
+ public static final String s7600 = "7600";
+ public static final String s7601 = "7601";
+ public static final String s7602 = "7602";
+ public static final String s7603 = "7603";
+ public static final String s7604 = "7604";
+ public static final String s7605 = "7605";
+ public static final String s7606 = "7606";
+ public static final String s7607 = "7607";
+ public static final String s7608 = "7608";
+ public static final String s7609 = "7609";
+ public static final String s7610 = "7610";
+ public static final String s7611 = "7611";
+ public static final String s7612 = "7612";
+ public static final String s7613 = "7613";
+ public static final String s7614 = "7614";
+ public static final String s7615 = "7615";
+ public static final String s7616 = "7616";
+ public static final String s7617 = "7617";
+ public static final String s7618 = "7618";
+ public static final String s7619 = "7619";
+ public static final String s7620 = "7620";
+ public static final String s7621 = "7621";
+ public static final String s7622 = "7622";
+ public static final String s7623 = "7623";
+ public static final String s7624 = "7624";
+ public static final String s7625 = "7625";
+ public static final String s7626 = "7626";
+ public static final String s7627 = "7627";
+ public static final String s7628 = "7628";
+ public static final String s7629 = "7629";
+ public static final String s7630 = "7630";
+ public static final String s7631 = "7631";
+ public static final String s7632 = "7632";
+ public static final String s7633 = "7633";
+ public static final String s7634 = "7634";
+ public static final String s7635 = "7635";
+ public static final String s7636 = "7636";
+ public static final String s7637 = "7637";
+ public static final String s7638 = "7638";
+ public static final String s7639 = "7639";
+ public static final String s7640 = "7640";
+ public static final String s7641 = "7641";
+ public static final String s7642 = "7642";
+ public static final String s7643 = "7643";
+ public static final String s7644 = "7644";
+ public static final String s7645 = "7645";
+ public static final String s7646 = "7646";
+ public static final String s7647 = "7647";
+ public static final String s7648 = "7648";
+ public static final String s7649 = "7649";
+ public static final String s7650 = "7650";
+ public static final String s7651 = "7651";
+ public static final String s7652 = "7652";
+ public static final String s7653 = "7653";
+ public static final String s7654 = "7654";
+ public static final String s7655 = "7655";
+ public static final String s7656 = "7656";
+ public static final String s7657 = "7657";
+ public static final String s7658 = "7658";
+ public static final String s7659 = "7659";
+ public static final String s7660 = "7660";
+ public static final String s7661 = "7661";
+ public static final String s7662 = "7662";
+ public static final String s7663 = "7663";
+ public static final String s7664 = "7664";
+ public static final String s7665 = "7665";
+ public static final String s7666 = "7666";
+ public static final String s7667 = "7667";
+ public static final String s7668 = "7668";
+ public static final String s7669 = "7669";
+ public static final String s7670 = "7670";
+ public static final String s7671 = "7671";
+ public static final String s7672 = "7672";
+ public static final String s7673 = "7673";
+ public static final String s7674 = "7674";
+ public static final String s7675 = "7675";
+ public static final String s7676 = "7676";
+ public static final String s7677 = "7677";
+ public static final String s7678 = "7678";
+ public static final String s7679 = "7679";
+ public static final String s7680 = "7680";
+ public static final String s7681 = "7681";
+ public static final String s7682 = "7682";
+ public static final String s7683 = "7683";
+ public static final String s7684 = "7684";
+ public static final String s7685 = "7685";
+ public static final String s7686 = "7686";
+ public static final String s7687 = "7687";
+ public static final String s7688 = "7688";
+ public static final String s7689 = "7689";
+ public static final String s7690 = "7690";
+ public static final String s7691 = "7691";
+ public static final String s7692 = "7692";
+ public static final String s7693 = "7693";
+ public static final String s7694 = "7694";
+ public static final String s7695 = "7695";
+ public static final String s7696 = "7696";
+ public static final String s7697 = "7697";
+ public static final String s7698 = "7698";
+ public static final String s7699 = "7699";
+ public static final String s7700 = "7700";
+ public static final String s7701 = "7701";
+ public static final String s7702 = "7702";
+ public static final String s7703 = "7703";
+ public static final String s7704 = "7704";
+ public static final String s7705 = "7705";
+ public static final String s7706 = "7706";
+ public static final String s7707 = "7707";
+ public static final String s7708 = "7708";
+ public static final String s7709 = "7709";
+ public static final String s7710 = "7710";
+ public static final String s7711 = "7711";
+ public static final String s7712 = "7712";
+ public static final String s7713 = "7713";
+ public static final String s7714 = "7714";
+ public static final String s7715 = "7715";
+ public static final String s7716 = "7716";
+ public static final String s7717 = "7717";
+ public static final String s7718 = "7718";
+ public static final String s7719 = "7719";
+ public static final String s7720 = "7720";
+ public static final String s7721 = "7721";
+ public static final String s7722 = "7722";
+ public static final String s7723 = "7723";
+ public static final String s7724 = "7724";
+ public static final String s7725 = "7725";
+ public static final String s7726 = "7726";
+ public static final String s7727 = "7727";
+ public static final String s7728 = "7728";
+ public static final String s7729 = "7729";
+ public static final String s7730 = "7730";
+ public static final String s7731 = "7731";
+ public static final String s7732 = "7732";
+ public static final String s7733 = "7733";
+ public static final String s7734 = "7734";
+ public static final String s7735 = "7735";
+ public static final String s7736 = "7736";
+ public static final String s7737 = "7737";
+ public static final String s7738 = "7738";
+ public static final String s7739 = "7739";
+ public static final String s7740 = "7740";
+ public static final String s7741 = "7741";
+ public static final String s7742 = "7742";
+ public static final String s7743 = "7743";
+ public static final String s7744 = "7744";
+ public static final String s7745 = "7745";
+ public static final String s7746 = "7746";
+ public static final String s7747 = "7747";
+ public static final String s7748 = "7748";
+ public static final String s7749 = "7749";
+ public static final String s7750 = "7750";
+ public static final String s7751 = "7751";
+ public static final String s7752 = "7752";
+ public static final String s7753 = "7753";
+ public static final String s7754 = "7754";
+ public static final String s7755 = "7755";
+ public static final String s7756 = "7756";
+ public static final String s7757 = "7757";
+ public static final String s7758 = "7758";
+ public static final String s7759 = "7759";
+ public static final String s7760 = "7760";
+ public static final String s7761 = "7761";
+ public static final String s7762 = "7762";
+ public static final String s7763 = "7763";
+ public static final String s7764 = "7764";
+ public static final String s7765 = "7765";
+ public static final String s7766 = "7766";
+ public static final String s7767 = "7767";
+ public static final String s7768 = "7768";
+ public static final String s7769 = "7769";
+ public static final String s7770 = "7770";
+ public static final String s7771 = "7771";
+ public static final String s7772 = "7772";
+ public static final String s7773 = "7773";
+ public static final String s7774 = "7774";
+ public static final String s7775 = "7775";
+ public static final String s7776 = "7776";
+ public static final String s7777 = "7777";
+ public static final String s7778 = "7778";
+ public static final String s7779 = "7779";
+ public static final String s7780 = "7780";
+ public static final String s7781 = "7781";
+ public static final String s7782 = "7782";
+ public static final String s7783 = "7783";
+ public static final String s7784 = "7784";
+ public static final String s7785 = "7785";
+ public static final String s7786 = "7786";
+ public static final String s7787 = "7787";
+ public static final String s7788 = "7788";
+ public static final String s7789 = "7789";
+ public static final String s7790 = "7790";
+ public static final String s7791 = "7791";
+ public static final String s7792 = "7792";
+ public static final String s7793 = "7793";
+ public static final String s7794 = "7794";
+ public static final String s7795 = "7795";
+ public static final String s7796 = "7796";
+ public static final String s7797 = "7797";
+ public static final String s7798 = "7798";
+ public static final String s7799 = "7799";
+ public static final String s7800 = "7800";
+ public static final String s7801 = "7801";
+ public static final String s7802 = "7802";
+ public static final String s7803 = "7803";
+ public static final String s7804 = "7804";
+ public static final String s7805 = "7805";
+ public static final String s7806 = "7806";
+ public static final String s7807 = "7807";
+ public static final String s7808 = "7808";
+ public static final String s7809 = "7809";
+ public static final String s7810 = "7810";
+ public static final String s7811 = "7811";
+ public static final String s7812 = "7812";
+ public static final String s7813 = "7813";
+ public static final String s7814 = "7814";
+ public static final String s7815 = "7815";
+ public static final String s7816 = "7816";
+ public static final String s7817 = "7817";
+ public static final String s7818 = "7818";
+ public static final String s7819 = "7819";
+ public static final String s7820 = "7820";
+ public static final String s7821 = "7821";
+ public static final String s7822 = "7822";
+ public static final String s7823 = "7823";
+ public static final String s7824 = "7824";
+ public static final String s7825 = "7825";
+ public static final String s7826 = "7826";
+ public static final String s7827 = "7827";
+ public static final String s7828 = "7828";
+ public static final String s7829 = "7829";
+ public static final String s7830 = "7830";
+ public static final String s7831 = "7831";
+ public static final String s7832 = "7832";
+ public static final String s7833 = "7833";
+ public static final String s7834 = "7834";
+ public static final String s7835 = "7835";
+ public static final String s7836 = "7836";
+ public static final String s7837 = "7837";
+ public static final String s7838 = "7838";
+ public static final String s7839 = "7839";
+ public static final String s7840 = "7840";
+ public static final String s7841 = "7841";
+ public static final String s7842 = "7842";
+ public static final String s7843 = "7843";
+ public static final String s7844 = "7844";
+ public static final String s7845 = "7845";
+ public static final String s7846 = "7846";
+ public static final String s7847 = "7847";
+ public static final String s7848 = "7848";
+ public static final String s7849 = "7849";
+ public static final String s7850 = "7850";
+ public static final String s7851 = "7851";
+ public static final String s7852 = "7852";
+ public static final String s7853 = "7853";
+ public static final String s7854 = "7854";
+ public static final String s7855 = "7855";
+ public static final String s7856 = "7856";
+ public static final String s7857 = "7857";
+ public static final String s7858 = "7858";
+ public static final String s7859 = "7859";
+ public static final String s7860 = "7860";
+ public static final String s7861 = "7861";
+ public static final String s7862 = "7862";
+ public static final String s7863 = "7863";
+ public static final String s7864 = "7864";
+ public static final String s7865 = "7865";
+ public static final String s7866 = "7866";
+ public static final String s7867 = "7867";
+ public static final String s7868 = "7868";
+ public static final String s7869 = "7869";
+ public static final String s7870 = "7870";
+ public static final String s7871 = "7871";
+ public static final String s7872 = "7872";
+ public static final String s7873 = "7873";
+ public static final String s7874 = "7874";
+ public static final String s7875 = "7875";
+ public static final String s7876 = "7876";
+ public static final String s7877 = "7877";
+ public static final String s7878 = "7878";
+ public static final String s7879 = "7879";
+ public static final String s7880 = "7880";
+ public static final String s7881 = "7881";
+ public static final String s7882 = "7882";
+ public static final String s7883 = "7883";
+ public static final String s7884 = "7884";
+ public static final String s7885 = "7885";
+ public static final String s7886 = "7886";
+ public static final String s7887 = "7887";
+ public static final String s7888 = "7888";
+ public static final String s7889 = "7889";
+ public static final String s7890 = "7890";
+ public static final String s7891 = "7891";
+ public static final String s7892 = "7892";
+ public static final String s7893 = "7893";
+ public static final String s7894 = "7894";
+ public static final String s7895 = "7895";
+ public static final String s7896 = "7896";
+ public static final String s7897 = "7897";
+ public static final String s7898 = "7898";
+ public static final String s7899 = "7899";
+ public static final String s7900 = "7900";
+ public static final String s7901 = "7901";
+ public static final String s7902 = "7902";
+ public static final String s7903 = "7903";
+ public static final String s7904 = "7904";
+ public static final String s7905 = "7905";
+ public static final String s7906 = "7906";
+ public static final String s7907 = "7907";
+ public static final String s7908 = "7908";
+ public static final String s7909 = "7909";
+ public static final String s7910 = "7910";
+ public static final String s7911 = "7911";
+ public static final String s7912 = "7912";
+ public static final String s7913 = "7913";
+ public static final String s7914 = "7914";
+ public static final String s7915 = "7915";
+ public static final String s7916 = "7916";
+ public static final String s7917 = "7917";
+ public static final String s7918 = "7918";
+ public static final String s7919 = "7919";
+ public static final String s7920 = "7920";
+ public static final String s7921 = "7921";
+ public static final String s7922 = "7922";
+ public static final String s7923 = "7923";
+ public static final String s7924 = "7924";
+ public static final String s7925 = "7925";
+ public static final String s7926 = "7926";
+ public static final String s7927 = "7927";
+ public static final String s7928 = "7928";
+ public static final String s7929 = "7929";
+ public static final String s7930 = "7930";
+ public static final String s7931 = "7931";
+ public static final String s7932 = "7932";
+ public static final String s7933 = "7933";
+ public static final String s7934 = "7934";
+ public static final String s7935 = "7935";
+ public static final String s7936 = "7936";
+ public static final String s7937 = "7937";
+ public static final String s7938 = "7938";
+ public static final String s7939 = "7939";
+ public static final String s7940 = "7940";
+ public static final String s7941 = "7941";
+ public static final String s7942 = "7942";
+ public static final String s7943 = "7943";
+ public static final String s7944 = "7944";
+ public static final String s7945 = "7945";
+ public static final String s7946 = "7946";
+ public static final String s7947 = "7947";
+ public static final String s7948 = "7948";
+ public static final String s7949 = "7949";
+ public static final String s7950 = "7950";
+ public static final String s7951 = "7951";
+ public static final String s7952 = "7952";
+ public static final String s7953 = "7953";
+ public static final String s7954 = "7954";
+ public static final String s7955 = "7955";
+ public static final String s7956 = "7956";
+ public static final String s7957 = "7957";
+ public static final String s7958 = "7958";
+ public static final String s7959 = "7959";
+ public static final String s7960 = "7960";
+ public static final String s7961 = "7961";
+ public static final String s7962 = "7962";
+ public static final String s7963 = "7963";
+ public static final String s7964 = "7964";
+ public static final String s7965 = "7965";
+ public static final String s7966 = "7966";
+ public static final String s7967 = "7967";
+ public static final String s7968 = "7968";
+ public static final String s7969 = "7969";
+ public static final String s7970 = "7970";
+ public static final String s7971 = "7971";
+ public static final String s7972 = "7972";
+ public static final String s7973 = "7973";
+ public static final String s7974 = "7974";
+ public static final String s7975 = "7975";
+ public static final String s7976 = "7976";
+ public static final String s7977 = "7977";
+ public static final String s7978 = "7978";
+ public static final String s7979 = "7979";
+ public static final String s7980 = "7980";
+ public static final String s7981 = "7981";
+ public static final String s7982 = "7982";
+ public static final String s7983 = "7983";
+ public static final String s7984 = "7984";
+ public static final String s7985 = "7985";
+ public static final String s7986 = "7986";
+ public static final String s7987 = "7987";
+ public static final String s7988 = "7988";
+ public static final String s7989 = "7989";
+ public static final String s7990 = "7990";
+ public static final String s7991 = "7991";
+ public static final String s7992 = "7992";
+ public static final String s7993 = "7993";
+ public static final String s7994 = "7994";
+ public static final String s7995 = "7995";
+ public static final String s7996 = "7996";
+ public static final String s7997 = "7997";
+ public static final String s7998 = "7998";
+ public static final String s7999 = "7999";
+ public static final String s8000 = "8000";
+ public static final String s8001 = "8001";
+ public static final String s8002 = "8002";
+ public static final String s8003 = "8003";
+ public static final String s8004 = "8004";
+ public static final String s8005 = "8005";
+ public static final String s8006 = "8006";
+ public static final String s8007 = "8007";
+ public static final String s8008 = "8008";
+ public static final String s8009 = "8009";
+ public static final String s8010 = "8010";
+ public static final String s8011 = "8011";
+ public static final String s8012 = "8012";
+ public static final String s8013 = "8013";
+ public static final String s8014 = "8014";
+ public static final String s8015 = "8015";
+ public static final String s8016 = "8016";
+ public static final String s8017 = "8017";
+ public static final String s8018 = "8018";
+ public static final String s8019 = "8019";
+ public static final String s8020 = "8020";
+ public static final String s8021 = "8021";
+ public static final String s8022 = "8022";
+ public static final String s8023 = "8023";
+ public static final String s8024 = "8024";
+ public static final String s8025 = "8025";
+ public static final String s8026 = "8026";
+ public static final String s8027 = "8027";
+ public static final String s8028 = "8028";
+ public static final String s8029 = "8029";
+ public static final String s8030 = "8030";
+ public static final String s8031 = "8031";
+ public static final String s8032 = "8032";
+ public static final String s8033 = "8033";
+ public static final String s8034 = "8034";
+ public static final String s8035 = "8035";
+ public static final String s8036 = "8036";
+ public static final String s8037 = "8037";
+ public static final String s8038 = "8038";
+ public static final String s8039 = "8039";
+ public static final String s8040 = "8040";
+ public static final String s8041 = "8041";
+ public static final String s8042 = "8042";
+ public static final String s8043 = "8043";
+ public static final String s8044 = "8044";
+ public static final String s8045 = "8045";
+ public static final String s8046 = "8046";
+ public static final String s8047 = "8047";
+ public static final String s8048 = "8048";
+ public static final String s8049 = "8049";
+ public static final String s8050 = "8050";
+ public static final String s8051 = "8051";
+ public static final String s8052 = "8052";
+ public static final String s8053 = "8053";
+ public static final String s8054 = "8054";
+ public static final String s8055 = "8055";
+ public static final String s8056 = "8056";
+ public static final String s8057 = "8057";
+ public static final String s8058 = "8058";
+ public static final String s8059 = "8059";
+ public static final String s8060 = "8060";
+ public static final String s8061 = "8061";
+ public static final String s8062 = "8062";
+ public static final String s8063 = "8063";
+ public static final String s8064 = "8064";
+ public static final String s8065 = "8065";
+ public static final String s8066 = "8066";
+ public static final String s8067 = "8067";
+ public static final String s8068 = "8068";
+ public static final String s8069 = "8069";
+ public static final String s8070 = "8070";
+ public static final String s8071 = "8071";
+ public static final String s8072 = "8072";
+ public static final String s8073 = "8073";
+ public static final String s8074 = "8074";
+ public static final String s8075 = "8075";
+ public static final String s8076 = "8076";
+ public static final String s8077 = "8077";
+ public static final String s8078 = "8078";
+ public static final String s8079 = "8079";
+ public static final String s8080 = "8080";
+ public static final String s8081 = "8081";
+ public static final String s8082 = "8082";
+ public static final String s8083 = "8083";
+ public static final String s8084 = "8084";
+ public static final String s8085 = "8085";
+ public static final String s8086 = "8086";
+ public static final String s8087 = "8087";
+ public static final String s8088 = "8088";
+ public static final String s8089 = "8089";
+ public static final String s8090 = "8090";
+ public static final String s8091 = "8091";
+ public static final String s8092 = "8092";
+ public static final String s8093 = "8093";
+ public static final String s8094 = "8094";
+ public static final String s8095 = "8095";
+ public static final String s8096 = "8096";
+ public static final String s8097 = "8097";
+ public static final String s8098 = "8098";
+ public static final String s8099 = "8099";
+ public static final String s8100 = "8100";
+ public static final String s8101 = "8101";
+ public static final String s8102 = "8102";
+ public static final String s8103 = "8103";
+ public static final String s8104 = "8104";
+ public static final String s8105 = "8105";
+ public static final String s8106 = "8106";
+ public static final String s8107 = "8107";
+ public static final String s8108 = "8108";
+ public static final String s8109 = "8109";
+ public static final String s8110 = "8110";
+ public static final String s8111 = "8111";
+ public static final String s8112 = "8112";
+ public static final String s8113 = "8113";
+ public static final String s8114 = "8114";
+ public static final String s8115 = "8115";
+ public static final String s8116 = "8116";
+ public static final String s8117 = "8117";
+ public static final String s8118 = "8118";
+ public static final String s8119 = "8119";
+ public static final String s8120 = "8120";
+ public static final String s8121 = "8121";
+ public static final String s8122 = "8122";
+ public static final String s8123 = "8123";
+ public static final String s8124 = "8124";
+ public static final String s8125 = "8125";
+ public static final String s8126 = "8126";
+ public static final String s8127 = "8127";
+ public static final String s8128 = "8128";
+ public static final String s8129 = "8129";
+ public static final String s8130 = "8130";
+ public static final String s8131 = "8131";
+ public static final String s8132 = "8132";
+ public static final String s8133 = "8133";
+ public static final String s8134 = "8134";
+ public static final String s8135 = "8135";
+ public static final String s8136 = "8136";
+ public static final String s8137 = "8137";
+ public static final String s8138 = "8138";
+ public static final String s8139 = "8139";
+ public static final String s8140 = "8140";
+ public static final String s8141 = "8141";
+ public static final String s8142 = "8142";
+ public static final String s8143 = "8143";
+ public static final String s8144 = "8144";
+ public static final String s8145 = "8145";
+ public static final String s8146 = "8146";
+ public static final String s8147 = "8147";
+ public static final String s8148 = "8148";
+ public static final String s8149 = "8149";
+ public static final String s8150 = "8150";
+ public static final String s8151 = "8151";
+ public static final String s8152 = "8152";
+ public static final String s8153 = "8153";
+ public static final String s8154 = "8154";
+ public static final String s8155 = "8155";
+ public static final String s8156 = "8156";
+ public static final String s8157 = "8157";
+ public static final String s8158 = "8158";
+ public static final String s8159 = "8159";
+ public static final String s8160 = "8160";
+ public static final String s8161 = "8161";
+ public static final String s8162 = "8162";
+ public static final String s8163 = "8163";
+ public static final String s8164 = "8164";
+ public static final String s8165 = "8165";
+ public static final String s8166 = "8166";
+ public static final String s8167 = "8167";
+ public static final String s8168 = "8168";
+ public static final String s8169 = "8169";
+ public static final String s8170 = "8170";
+ public static final String s8171 = "8171";
+ public static final String s8172 = "8172";
+ public static final String s8173 = "8173";
+ public static final String s8174 = "8174";
+ public static final String s8175 = "8175";
+ public static final String s8176 = "8176";
+ public static final String s8177 = "8177";
+ public static final String s8178 = "8178";
+ public static final String s8179 = "8179";
+ public static final String s8180 = "8180";
+ public static final String s8181 = "8181";
+ public static final String s8182 = "8182";
+ public static final String s8183 = "8183";
+ public static final String s8184 = "8184";
+ public static final String s8185 = "8185";
+ public static final String s8186 = "8186";
+ public static final String s8187 = "8187";
+ public static final String s8188 = "8188";
+ public static final String s8189 = "8189";
+ public static final String s8190 = "8190";
+ public static final String s8191 = "8191";
+ public static final String s8192 = "8192";
+ public static final String s8193 = "8193";
+ public static final String s8194 = "8194";
+ public static final String s8195 = "8195";
+ public static final String s8196 = "8196";
+ public static final String s8197 = "8197";
+ public static final String s8198 = "8198";
+ public static final String s8199 = "8199";
+ public static final String s8200 = "8200";
+ public static final String s8201 = "8201";
+ public static final String s8202 = "8202";
+ public static final String s8203 = "8203";
+ public static final String s8204 = "8204";
+ public static final String s8205 = "8205";
+ public static final String s8206 = "8206";
+ public static final String s8207 = "8207";
+ public static final String s8208 = "8208";
+ public static final String s8209 = "8209";
+ public static final String s8210 = "8210";
+ public static final String s8211 = "8211";
+ public static final String s8212 = "8212";
+ public static final String s8213 = "8213";
+ public static final String s8214 = "8214";
+ public static final String s8215 = "8215";
+ public static final String s8216 = "8216";
+ public static final String s8217 = "8217";
+ public static final String s8218 = "8218";
+ public static final String s8219 = "8219";
+ public static final String s8220 = "8220";
+ public static final String s8221 = "8221";
+ public static final String s8222 = "8222";
+ public static final String s8223 = "8223";
+ public static final String s8224 = "8224";
+ public static final String s8225 = "8225";
+ public static final String s8226 = "8226";
+ public static final String s8227 = "8227";
+ public static final String s8228 = "8228";
+ public static final String s8229 = "8229";
+ public static final String s8230 = "8230";
+ public static final String s8231 = "8231";
+ public static final String s8232 = "8232";
+ public static final String s8233 = "8233";
+ public static final String s8234 = "8234";
+ public static final String s8235 = "8235";
+ public static final String s8236 = "8236";
+ public static final String s8237 = "8237";
+ public static final String s8238 = "8238";
+ public static final String s8239 = "8239";
+ public static final String s8240 = "8240";
+ public static final String s8241 = "8241";
+ public static final String s8242 = "8242";
+ public static final String s8243 = "8243";
+ public static final String s8244 = "8244";
+ public static final String s8245 = "8245";
+ public static final String s8246 = "8246";
+ public static final String s8247 = "8247";
+ public static final String s8248 = "8248";
+ public static final String s8249 = "8249";
+ public static final String s8250 = "8250";
+ public static final String s8251 = "8251";
+ public static final String s8252 = "8252";
+ public static final String s8253 = "8253";
+ public static final String s8254 = "8254";
+ public static final String s8255 = "8255";
+ public static final String s8256 = "8256";
+ public static final String s8257 = "8257";
+ public static final String s8258 = "8258";
+ public static final String s8259 = "8259";
+ public static final String s8260 = "8260";
+ public static final String s8261 = "8261";
+ public static final String s8262 = "8262";
+ public static final String s8263 = "8263";
+ public static final String s8264 = "8264";
+ public static final String s8265 = "8265";
+ public static final String s8266 = "8266";
+ public static final String s8267 = "8267";
+ public static final String s8268 = "8268";
+ public static final String s8269 = "8269";
+ public static final String s8270 = "8270";
+ public static final String s8271 = "8271";
+ public static final String s8272 = "8272";
+ public static final String s8273 = "8273";
+ public static final String s8274 = "8274";
+ public static final String s8275 = "8275";
+ public static final String s8276 = "8276";
+ public static final String s8277 = "8277";
+ public static final String s8278 = "8278";
+ public static final String s8279 = "8279";
+ public static final String s8280 = "8280";
+ public static final String s8281 = "8281";
+ public static final String s8282 = "8282";
+ public static final String s8283 = "8283";
+ public static final String s8284 = "8284";
+ public static final String s8285 = "8285";
+ public static final String s8286 = "8286";
+ public static final String s8287 = "8287";
+ public static final String s8288 = "8288";
+ public static final String s8289 = "8289";
+ public static final String s8290 = "8290";
+ public static final String s8291 = "8291";
+ public static final String s8292 = "8292";
+ public static final String s8293 = "8293";
+ public static final String s8294 = "8294";
+ public static final String s8295 = "8295";
+ public static final String s8296 = "8296";
+ public static final String s8297 = "8297";
+ public static final String s8298 = "8298";
+ public static final String s8299 = "8299";
+ public static final String s8300 = "8300";
+ public static final String s8301 = "8301";
+ public static final String s8302 = "8302";
+ public static final String s8303 = "8303";
+ public static final String s8304 = "8304";
+ public static final String s8305 = "8305";
+ public static final String s8306 = "8306";
+ public static final String s8307 = "8307";
+ public static final String s8308 = "8308";
+ public static final String s8309 = "8309";
+ public static final String s8310 = "8310";
+ public static final String s8311 = "8311";
+ public static final String s8312 = "8312";
+ public static final String s8313 = "8313";
+ public static final String s8314 = "8314";
+ public static final String s8315 = "8315";
+ public static final String s8316 = "8316";
+ public static final String s8317 = "8317";
+ public static final String s8318 = "8318";
+ public static final String s8319 = "8319";
+ public static final String s8320 = "8320";
+ public static final String s8321 = "8321";
+ public static final String s8322 = "8322";
+ public static final String s8323 = "8323";
+ public static final String s8324 = "8324";
+ public static final String s8325 = "8325";
+ public static final String s8326 = "8326";
+ public static final String s8327 = "8327";
+ public static final String s8328 = "8328";
+ public static final String s8329 = "8329";
+ public static final String s8330 = "8330";
+ public static final String s8331 = "8331";
+ public static final String s8332 = "8332";
+ public static final String s8333 = "8333";
+ public static final String s8334 = "8334";
+ public static final String s8335 = "8335";
+ public static final String s8336 = "8336";
+ public static final String s8337 = "8337";
+ public static final String s8338 = "8338";
+ public static final String s8339 = "8339";
+ public static final String s8340 = "8340";
+ public static final String s8341 = "8341";
+ public static final String s8342 = "8342";
+ public static final String s8343 = "8343";
+ public static final String s8344 = "8344";
+ public static final String s8345 = "8345";
+ public static final String s8346 = "8346";
+ public static final String s8347 = "8347";
+ public static final String s8348 = "8348";
+ public static final String s8349 = "8349";
+ public static final String s8350 = "8350";
+ public static final String s8351 = "8351";
+ public static final String s8352 = "8352";
+ public static final String s8353 = "8353";
+ public static final String s8354 = "8354";
+ public static final String s8355 = "8355";
+ public static final String s8356 = "8356";
+ public static final String s8357 = "8357";
+ public static final String s8358 = "8358";
+ public static final String s8359 = "8359";
+ public static final String s8360 = "8360";
+ public static final String s8361 = "8361";
+ public static final String s8362 = "8362";
+ public static final String s8363 = "8363";
+ public static final String s8364 = "8364";
+ public static final String s8365 = "8365";
+ public static final String s8366 = "8366";
+ public static final String s8367 = "8367";
+ public static final String s8368 = "8368";
+ public static final String s8369 = "8369";
+ public static final String s8370 = "8370";
+ public static final String s8371 = "8371";
+ public static final String s8372 = "8372";
+ public static final String s8373 = "8373";
+ public static final String s8374 = "8374";
+ public static final String s8375 = "8375";
+ public static final String s8376 = "8376";
+ public static final String s8377 = "8377";
+ public static final String s8378 = "8378";
+ public static final String s8379 = "8379";
+ public static final String s8380 = "8380";
+ public static final String s8381 = "8381";
+ public static final String s8382 = "8382";
+ public static final String s8383 = "8383";
+ public static final String s8384 = "8384";
+ public static final String s8385 = "8385";
+ public static final String s8386 = "8386";
+ public static final String s8387 = "8387";
+ public static final String s8388 = "8388";
+ public static final String s8389 = "8389";
+ public static final String s8390 = "8390";
+ public static final String s8391 = "8391";
+ public static final String s8392 = "8392";
+ public static final String s8393 = "8393";
+ public static final String s8394 = "8394";
+ public static final String s8395 = "8395";
+ public static final String s8396 = "8396";
+ public static final String s8397 = "8397";
+ public static final String s8398 = "8398";
+ public static final String s8399 = "8399";
+ public static final String s8400 = "8400";
+ public static final String s8401 = "8401";
+ public static final String s8402 = "8402";
+ public static final String s8403 = "8403";
+ public static final String s8404 = "8404";
+ public static final String s8405 = "8405";
+ public static final String s8406 = "8406";
+ public static final String s8407 = "8407";
+ public static final String s8408 = "8408";
+ public static final String s8409 = "8409";
+ public static final String s8410 = "8410";
+ public static final String s8411 = "8411";
+ public static final String s8412 = "8412";
+ public static final String s8413 = "8413";
+ public static final String s8414 = "8414";
+ public static final String s8415 = "8415";
+ public static final String s8416 = "8416";
+ public static final String s8417 = "8417";
+ public static final String s8418 = "8418";
+ public static final String s8419 = "8419";
+ public static final String s8420 = "8420";
+ public static final String s8421 = "8421";
+ public static final String s8422 = "8422";
+ public static final String s8423 = "8423";
+ public static final String s8424 = "8424";
+ public static final String s8425 = "8425";
+ public static final String s8426 = "8426";
+ public static final String s8427 = "8427";
+ public static final String s8428 = "8428";
+ public static final String s8429 = "8429";
+ public static final String s8430 = "8430";
+ public static final String s8431 = "8431";
+ public static final String s8432 = "8432";
+ public static final String s8433 = "8433";
+ public static final String s8434 = "8434";
+ public static final String s8435 = "8435";
+ public static final String s8436 = "8436";
+ public static final String s8437 = "8437";
+ public static final String s8438 = "8438";
+ public static final String s8439 = "8439";
+ public static final String s8440 = "8440";
+ public static final String s8441 = "8441";
+ public static final String s8442 = "8442";
+ public static final String s8443 = "8443";
+ public static final String s8444 = "8444";
+ public static final String s8445 = "8445";
+ public static final String s8446 = "8446";
+ public static final String s8447 = "8447";
+ public static final String s8448 = "8448";
+ public static final String s8449 = "8449";
+ public static final String s8450 = "8450";
+ public static final String s8451 = "8451";
+ public static final String s8452 = "8452";
+ public static final String s8453 = "8453";
+ public static final String s8454 = "8454";
+ public static final String s8455 = "8455";
+ public static final String s8456 = "8456";
+ public static final String s8457 = "8457";
+ public static final String s8458 = "8458";
+ public static final String s8459 = "8459";
+ public static final String s8460 = "8460";
+ public static final String s8461 = "8461";
+ public static final String s8462 = "8462";
+ public static final String s8463 = "8463";
+ public static final String s8464 = "8464";
+ public static final String s8465 = "8465";
+ public static final String s8466 = "8466";
+ public static final String s8467 = "8467";
+ public static final String s8468 = "8468";
+ public static final String s8469 = "8469";
+ public static final String s8470 = "8470";
+ public static final String s8471 = "8471";
+ public static final String s8472 = "8472";
+ public static final String s8473 = "8473";
+ public static final String s8474 = "8474";
+ public static final String s8475 = "8475";
+ public static final String s8476 = "8476";
+ public static final String s8477 = "8477";
+ public static final String s8478 = "8478";
+ public static final String s8479 = "8479";
+ public static final String s8480 = "8480";
+ public static final String s8481 = "8481";
+ public static final String s8482 = "8482";
+ public static final String s8483 = "8483";
+ public static final String s8484 = "8484";
+ public static final String s8485 = "8485";
+ public static final String s8486 = "8486";
+ public static final String s8487 = "8487";
+ public static final String s8488 = "8488";
+ public static final String s8489 = "8489";
+ public static final String s8490 = "8490";
+ public static final String s8491 = "8491";
+ public static final String s8492 = "8492";
+ public static final String s8493 = "8493";
+ public static final String s8494 = "8494";
+ public static final String s8495 = "8495";
+ public static final String s8496 = "8496";
+ public static final String s8497 = "8497";
+ public static final String s8498 = "8498";
+ public static final String s8499 = "8499";
+ public static final String s8500 = "8500";
+ public static final String s8501 = "8501";
+ public static final String s8502 = "8502";
+ public static final String s8503 = "8503";
+ public static final String s8504 = "8504";
+ public static final String s8505 = "8505";
+ public static final String s8506 = "8506";
+ public static final String s8507 = "8507";
+ public static final String s8508 = "8508";
+ public static final String s8509 = "8509";
+ public static final String s8510 = "8510";
+ public static final String s8511 = "8511";
+ public static final String s8512 = "8512";
+ public static final String s8513 = "8513";
+ public static final String s8514 = "8514";
+ public static final String s8515 = "8515";
+ public static final String s8516 = "8516";
+ public static final String s8517 = "8517";
+ public static final String s8518 = "8518";
+ public static final String s8519 = "8519";
+ public static final String s8520 = "8520";
+ public static final String s8521 = "8521";
+ public static final String s8522 = "8522";
+ public static final String s8523 = "8523";
+ public static final String s8524 = "8524";
+ public static final String s8525 = "8525";
+ public static final String s8526 = "8526";
+ public static final String s8527 = "8527";
+ public static final String s8528 = "8528";
+ public static final String s8529 = "8529";
+ public static final String s8530 = "8530";
+ public static final String s8531 = "8531";
+ public static final String s8532 = "8532";
+ public static final String s8533 = "8533";
+ public static final String s8534 = "8534";
+ public static final String s8535 = "8535";
+ public static final String s8536 = "8536";
+ public static final String s8537 = "8537";
+ public static final String s8538 = "8538";
+ public static final String s8539 = "8539";
+ public static final String s8540 = "8540";
+ public static final String s8541 = "8541";
+ public static final String s8542 = "8542";
+ public static final String s8543 = "8543";
+ public static final String s8544 = "8544";
+ public static final String s8545 = "8545";
+ public static final String s8546 = "8546";
+ public static final String s8547 = "8547";
+ public static final String s8548 = "8548";
+ public static final String s8549 = "8549";
+ public static final String s8550 = "8550";
+ public static final String s8551 = "8551";
+ public static final String s8552 = "8552";
+ public static final String s8553 = "8553";
+ public static final String s8554 = "8554";
+ public static final String s8555 = "8555";
+ public static final String s8556 = "8556";
+ public static final String s8557 = "8557";
+ public static final String s8558 = "8558";
+ public static final String s8559 = "8559";
+ public static final String s8560 = "8560";
+ public static final String s8561 = "8561";
+ public static final String s8562 = "8562";
+ public static final String s8563 = "8563";
+ public static final String s8564 = "8564";
+ public static final String s8565 = "8565";
+ public static final String s8566 = "8566";
+ public static final String s8567 = "8567";
+ public static final String s8568 = "8568";
+ public static final String s8569 = "8569";
+ public static final String s8570 = "8570";
+ public static final String s8571 = "8571";
+ public static final String s8572 = "8572";
+ public static final String s8573 = "8573";
+ public static final String s8574 = "8574";
+ public static final String s8575 = "8575";
+ public static final String s8576 = "8576";
+ public static final String s8577 = "8577";
+ public static final String s8578 = "8578";
+ public static final String s8579 = "8579";
+ public static final String s8580 = "8580";
+ public static final String s8581 = "8581";
+ public static final String s8582 = "8582";
+ public static final String s8583 = "8583";
+ public static final String s8584 = "8584";
+ public static final String s8585 = "8585";
+ public static final String s8586 = "8586";
+ public static final String s8587 = "8587";
+ public static final String s8588 = "8588";
+ public static final String s8589 = "8589";
+ public static final String s8590 = "8590";
+ public static final String s8591 = "8591";
+ public static final String s8592 = "8592";
+ public static final String s8593 = "8593";
+ public static final String s8594 = "8594";
+ public static final String s8595 = "8595";
+ public static final String s8596 = "8596";
+ public static final String s8597 = "8597";
+ public static final String s8598 = "8598";
+ public static final String s8599 = "8599";
+ public static final String s8600 = "8600";
+ public static final String s8601 = "8601";
+ public static final String s8602 = "8602";
+ public static final String s8603 = "8603";
+ public static final String s8604 = "8604";
+ public static final String s8605 = "8605";
+ public static final String s8606 = "8606";
+ public static final String s8607 = "8607";
+ public static final String s8608 = "8608";
+ public static final String s8609 = "8609";
+ public static final String s8610 = "8610";
+ public static final String s8611 = "8611";
+ public static final String s8612 = "8612";
+ public static final String s8613 = "8613";
+ public static final String s8614 = "8614";
+ public static final String s8615 = "8615";
+ public static final String s8616 = "8616";
+ public static final String s8617 = "8617";
+ public static final String s8618 = "8618";
+ public static final String s8619 = "8619";
+ public static final String s8620 = "8620";
+ public static final String s8621 = "8621";
+ public static final String s8622 = "8622";
+ public static final String s8623 = "8623";
+ public static final String s8624 = "8624";
+ public static final String s8625 = "8625";
+ public static final String s8626 = "8626";
+ public static final String s8627 = "8627";
+ public static final String s8628 = "8628";
+ public static final String s8629 = "8629";
+ public static final String s8630 = "8630";
+ public static final String s8631 = "8631";
+ public static final String s8632 = "8632";
+ public static final String s8633 = "8633";
+ public static final String s8634 = "8634";
+ public static final String s8635 = "8635";
+ public static final String s8636 = "8636";
+ public static final String s8637 = "8637";
+ public static final String s8638 = "8638";
+ public static final String s8639 = "8639";
+ public static final String s8640 = "8640";
+ public static final String s8641 = "8641";
+ public static final String s8642 = "8642";
+ public static final String s8643 = "8643";
+ public static final String s8644 = "8644";
+ public static final String s8645 = "8645";
+ public static final String s8646 = "8646";
+ public static final String s8647 = "8647";
+ public static final String s8648 = "8648";
+ public static final String s8649 = "8649";
+ public static final String s8650 = "8650";
+ public static final String s8651 = "8651";
+ public static final String s8652 = "8652";
+ public static final String s8653 = "8653";
+ public static final String s8654 = "8654";
+ public static final String s8655 = "8655";
+ public static final String s8656 = "8656";
+ public static final String s8657 = "8657";
+ public static final String s8658 = "8658";
+ public static final String s8659 = "8659";
+ public static final String s8660 = "8660";
+ public static final String s8661 = "8661";
+ public static final String s8662 = "8662";
+ public static final String s8663 = "8663";
+ public static final String s8664 = "8664";
+ public static final String s8665 = "8665";
+ public static final String s8666 = "8666";
+ public static final String s8667 = "8667";
+ public static final String s8668 = "8668";
+ public static final String s8669 = "8669";
+ public static final String s8670 = "8670";
+ public static final String s8671 = "8671";
+ public static final String s8672 = "8672";
+ public static final String s8673 = "8673";
+ public static final String s8674 = "8674";
+ public static final String s8675 = "8675";
+ public static final String s8676 = "8676";
+ public static final String s8677 = "8677";
+ public static final String s8678 = "8678";
+ public static final String s8679 = "8679";
+ public static final String s8680 = "8680";
+ public static final String s8681 = "8681";
+ public static final String s8682 = "8682";
+ public static final String s8683 = "8683";
+ public static final String s8684 = "8684";
+ public static final String s8685 = "8685";
+ public static final String s8686 = "8686";
+ public static final String s8687 = "8687";
+ public static final String s8688 = "8688";
+ public static final String s8689 = "8689";
+ public static final String s8690 = "8690";
+ public static final String s8691 = "8691";
+ public static final String s8692 = "8692";
+ public static final String s8693 = "8693";
+ public static final String s8694 = "8694";
+ public static final String s8695 = "8695";
+ public static final String s8696 = "8696";
+ public static final String s8697 = "8697";
+ public static final String s8698 = "8698";
+ public static final String s8699 = "8699";
+ public static final String s8700 = "8700";
+ public static final String s8701 = "8701";
+ public static final String s8702 = "8702";
+ public static final String s8703 = "8703";
+ public static final String s8704 = "8704";
+ public static final String s8705 = "8705";
+ public static final String s8706 = "8706";
+ public static final String s8707 = "8707";
+ public static final String s8708 = "8708";
+ public static final String s8709 = "8709";
+ public static final String s8710 = "8710";
+ public static final String s8711 = "8711";
+ public static final String s8712 = "8712";
+ public static final String s8713 = "8713";
+ public static final String s8714 = "8714";
+ public static final String s8715 = "8715";
+ public static final String s8716 = "8716";
+ public static final String s8717 = "8717";
+ public static final String s8718 = "8718";
+ public static final String s8719 = "8719";
+ public static final String s8720 = "8720";
+ public static final String s8721 = "8721";
+ public static final String s8722 = "8722";
+ public static final String s8723 = "8723";
+ public static final String s8724 = "8724";
+ public static final String s8725 = "8725";
+ public static final String s8726 = "8726";
+ public static final String s8727 = "8727";
+ public static final String s8728 = "8728";
+ public static final String s8729 = "8729";
+ public static final String s8730 = "8730";
+ public static final String s8731 = "8731";
+ public static final String s8732 = "8732";
+ public static final String s8733 = "8733";
+ public static final String s8734 = "8734";
+ public static final String s8735 = "8735";
+ public static final String s8736 = "8736";
+ public static final String s8737 = "8737";
+ public static final String s8738 = "8738";
+ public static final String s8739 = "8739";
+ public static final String s8740 = "8740";
+ public static final String s8741 = "8741";
+ public static final String s8742 = "8742";
+ public static final String s8743 = "8743";
+ public static final String s8744 = "8744";
+ public static final String s8745 = "8745";
+ public static final String s8746 = "8746";
+ public static final String s8747 = "8747";
+ public static final String s8748 = "8748";
+ public static final String s8749 = "8749";
+ public static final String s8750 = "8750";
+ public static final String s8751 = "8751";
+ public static final String s8752 = "8752";
+ public static final String s8753 = "8753";
+ public static final String s8754 = "8754";
+ public static final String s8755 = "8755";
+ public static final String s8756 = "8756";
+ public static final String s8757 = "8757";
+ public static final String s8758 = "8758";
+ public static final String s8759 = "8759";
+ public static final String s8760 = "8760";
+ public static final String s8761 = "8761";
+ public static final String s8762 = "8762";
+ public static final String s8763 = "8763";
+ public static final String s8764 = "8764";
+ public static final String s8765 = "8765";
+ public static final String s8766 = "8766";
+ public static final String s8767 = "8767";
+ public static final String s8768 = "8768";
+ public static final String s8769 = "8769";
+ public static final String s8770 = "8770";
+ public static final String s8771 = "8771";
+ public static final String s8772 = "8772";
+ public static final String s8773 = "8773";
+ public static final String s8774 = "8774";
+ public static final String s8775 = "8775";
+ public static final String s8776 = "8776";
+ public static final String s8777 = "8777";
+ public static final String s8778 = "8778";
+ public static final String s8779 = "8779";
+ public static final String s8780 = "8780";
+ public static final String s8781 = "8781";
+ public static final String s8782 = "8782";
+ public static final String s8783 = "8783";
+ public static final String s8784 = "8784";
+ public static final String s8785 = "8785";
+ public static final String s8786 = "8786";
+ public static final String s8787 = "8787";
+ public static final String s8788 = "8788";
+ public static final String s8789 = "8789";
+ public static final String s8790 = "8790";
+ public static final String s8791 = "8791";
+ public static final String s8792 = "8792";
+ public static final String s8793 = "8793";
+ public static final String s8794 = "8794";
+ public static final String s8795 = "8795";
+ public static final String s8796 = "8796";
+ public static final String s8797 = "8797";
+ public static final String s8798 = "8798";
+ public static final String s8799 = "8799";
+ public static final String s8800 = "8800";
+ public static final String s8801 = "8801";
+ public static final String s8802 = "8802";
+ public static final String s8803 = "8803";
+ public static final String s8804 = "8804";
+ public static final String s8805 = "8805";
+ public static final String s8806 = "8806";
+ public static final String s8807 = "8807";
+ public static final String s8808 = "8808";
+ public static final String s8809 = "8809";
+ public static final String s8810 = "8810";
+ public static final String s8811 = "8811";
+ public static final String s8812 = "8812";
+ public static final String s8813 = "8813";
+ public static final String s8814 = "8814";
+ public static final String s8815 = "8815";
+ public static final String s8816 = "8816";
+ public static final String s8817 = "8817";
+ public static final String s8818 = "8818";
+ public static final String s8819 = "8819";
+ public static final String s8820 = "8820";
+ public static final String s8821 = "8821";
+ public static final String s8822 = "8822";
+ public static final String s8823 = "8823";
+ public static final String s8824 = "8824";
+ public static final String s8825 = "8825";
+ public static final String s8826 = "8826";
+ public static final String s8827 = "8827";
+ public static final String s8828 = "8828";
+ public static final String s8829 = "8829";
+ public static final String s8830 = "8830";
+ public static final String s8831 = "8831";
+ public static final String s8832 = "8832";
+ public static final String s8833 = "8833";
+ public static final String s8834 = "8834";
+ public static final String s8835 = "8835";
+ public static final String s8836 = "8836";
+ public static final String s8837 = "8837";
+ public static final String s8838 = "8838";
+ public static final String s8839 = "8839";
+ public static final String s8840 = "8840";
+ public static final String s8841 = "8841";
+ public static final String s8842 = "8842";
+ public static final String s8843 = "8843";
+ public static final String s8844 = "8844";
+ public static final String s8845 = "8845";
+ public static final String s8846 = "8846";
+ public static final String s8847 = "8847";
+ public static final String s8848 = "8848";
+ public static final String s8849 = "8849";
+ public static final String s8850 = "8850";
+ public static final String s8851 = "8851";
+ public static final String s8852 = "8852";
+ public static final String s8853 = "8853";
+ public static final String s8854 = "8854";
+ public static final String s8855 = "8855";
+ public static final String s8856 = "8856";
+ public static final String s8857 = "8857";
+ public static final String s8858 = "8858";
+ public static final String s8859 = "8859";
+ public static final String s8860 = "8860";
+ public static final String s8861 = "8861";
+ public static final String s8862 = "8862";
+ public static final String s8863 = "8863";
+ public static final String s8864 = "8864";
+ public static final String s8865 = "8865";
+ public static final String s8866 = "8866";
+ public static final String s8867 = "8867";
+ public static final String s8868 = "8868";
+ public static final String s8869 = "8869";
+ public static final String s8870 = "8870";
+ public static final String s8871 = "8871";
+ public static final String s8872 = "8872";
+ public static final String s8873 = "8873";
+ public static final String s8874 = "8874";
+ public static final String s8875 = "8875";
+ public static final String s8876 = "8876";
+ public static final String s8877 = "8877";
+ public static final String s8878 = "8878";
+ public static final String s8879 = "8879";
+ public static final String s8880 = "8880";
+ public static final String s8881 = "8881";
+ public static final String s8882 = "8882";
+ public static final String s8883 = "8883";
+ public static final String s8884 = "8884";
+ public static final String s8885 = "8885";
+ public static final String s8886 = "8886";
+ public static final String s8887 = "8887";
+ public static final String s8888 = "8888";
+ public static final String s8889 = "8889";
+ public static final String s8890 = "8890";
+ public static final String s8891 = "8891";
+ public static final String s8892 = "8892";
+ public static final String s8893 = "8893";
+ public static final String s8894 = "8894";
+ public static final String s8895 = "8895";
+ public static final String s8896 = "8896";
+ public static final String s8897 = "8897";
+ public static final String s8898 = "8898";
+ public static final String s8899 = "8899";
+ public static final String s8900 = "8900";
+ public static final String s8901 = "8901";
+ public static final String s8902 = "8902";
+ public static final String s8903 = "8903";
+ public static final String s8904 = "8904";
+ public static final String s8905 = "8905";
+ public static final String s8906 = "8906";
+ public static final String s8907 = "8907";
+ public static final String s8908 = "8908";
+ public static final String s8909 = "8909";
+ public static final String s8910 = "8910";
+ public static final String s8911 = "8911";
+ public static final String s8912 = "8912";
+ public static final String s8913 = "8913";
+ public static final String s8914 = "8914";
+ public static final String s8915 = "8915";
+ public static final String s8916 = "8916";
+ public static final String s8917 = "8917";
+ public static final String s8918 = "8918";
+ public static final String s8919 = "8919";
+ public static final String s8920 = "8920";
+ public static final String s8921 = "8921";
+ public static final String s8922 = "8922";
+ public static final String s8923 = "8923";
+ public static final String s8924 = "8924";
+ public static final String s8925 = "8925";
+ public static final String s8926 = "8926";
+ public static final String s8927 = "8927";
+ public static final String s8928 = "8928";
+ public static final String s8929 = "8929";
+ public static final String s8930 = "8930";
+ public static final String s8931 = "8931";
+ public static final String s8932 = "8932";
+ public static final String s8933 = "8933";
+ public static final String s8934 = "8934";
+ public static final String s8935 = "8935";
+ public static final String s8936 = "8936";
+ public static final String s8937 = "8937";
+ public static final String s8938 = "8938";
+ public static final String s8939 = "8939";
+ public static final String s8940 = "8940";
+ public static final String s8941 = "8941";
+ public static final String s8942 = "8942";
+ public static final String s8943 = "8943";
+ public static final String s8944 = "8944";
+ public static final String s8945 = "8945";
+ public static final String s8946 = "8946";
+ public static final String s8947 = "8947";
+ public static final String s8948 = "8948";
+ public static final String s8949 = "8949";
+ public static final String s8950 = "8950";
+ public static final String s8951 = "8951";
+ public static final String s8952 = "8952";
+ public static final String s8953 = "8953";
+ public static final String s8954 = "8954";
+ public static final String s8955 = "8955";
+ public static final String s8956 = "8956";
+ public static final String s8957 = "8957";
+ public static final String s8958 = "8958";
+ public static final String s8959 = "8959";
+ public static final String s8960 = "8960";
+ public static final String s8961 = "8961";
+ public static final String s8962 = "8962";
+ public static final String s8963 = "8963";
+ public static final String s8964 = "8964";
+ public static final String s8965 = "8965";
+ public static final String s8966 = "8966";
+ public static final String s8967 = "8967";
+ public static final String s8968 = "8968";
+ public static final String s8969 = "8969";
+ public static final String s8970 = "8970";
+ public static final String s8971 = "8971";
+ public static final String s8972 = "8972";
+ public static final String s8973 = "8973";
+ public static final String s8974 = "8974";
+ public static final String s8975 = "8975";
+ public static final String s8976 = "8976";
+ public static final String s8977 = "8977";
+ public static final String s8978 = "8978";
+ public static final String s8979 = "8979";
+ public static final String s8980 = "8980";
+ public static final String s8981 = "8981";
+ public static final String s8982 = "8982";
+ public static final String s8983 = "8983";
+ public static final String s8984 = "8984";
+ public static final String s8985 = "8985";
+ public static final String s8986 = "8986";
+ public static final String s8987 = "8987";
+ public static final String s8988 = "8988";
+ public static final String s8989 = "8989";
+ public static final String s8990 = "8990";
+ public static final String s8991 = "8991";
+ public static final String s8992 = "8992";
+ public static final String s8993 = "8993";
+ public static final String s8994 = "8994";
+ public static final String s8995 = "8995";
+ public static final String s8996 = "8996";
+ public static final String s8997 = "8997";
+ public static final String s8998 = "8998";
+ public static final String s8999 = "8999";
+ public static final String s9000 = "9000";
+ public static final String s9001 = "9001";
+ public static final String s9002 = "9002";
+ public static final String s9003 = "9003";
+ public static final String s9004 = "9004";
+ public static final String s9005 = "9005";
+ public static final String s9006 = "9006";
+ public static final String s9007 = "9007";
+ public static final String s9008 = "9008";
+ public static final String s9009 = "9009";
+ public static final String s9010 = "9010";
+ public static final String s9011 = "9011";
+ public static final String s9012 = "9012";
+ public static final String s9013 = "9013";
+ public static final String s9014 = "9014";
+ public static final String s9015 = "9015";
+ public static final String s9016 = "9016";
+ public static final String s9017 = "9017";
+ public static final String s9018 = "9018";
+ public static final String s9019 = "9019";
+ public static final String s9020 = "9020";
+ public static final String s9021 = "9021";
+ public static final String s9022 = "9022";
+ public static final String s9023 = "9023";
+ public static final String s9024 = "9024";
+ public static final String s9025 = "9025";
+ public static final String s9026 = "9026";
+ public static final String s9027 = "9027";
+ public static final String s9028 = "9028";
+ public static final String s9029 = "9029";
+ public static final String s9030 = "9030";
+ public static final String s9031 = "9031";
+ public static final String s9032 = "9032";
+ public static final String s9033 = "9033";
+ public static final String s9034 = "9034";
+ public static final String s9035 = "9035";
+ public static final String s9036 = "9036";
+ public static final String s9037 = "9037";
+ public static final String s9038 = "9038";
+ public static final String s9039 = "9039";
+ public static final String s9040 = "9040";
+ public static final String s9041 = "9041";
+ public static final String s9042 = "9042";
+ public static final String s9043 = "9043";
+ public static final String s9044 = "9044";
+ public static final String s9045 = "9045";
+ public static final String s9046 = "9046";
+ public static final String s9047 = "9047";
+ public static final String s9048 = "9048";
+ public static final String s9049 = "9049";
+ public static final String s9050 = "9050";
+ public static final String s9051 = "9051";
+ public static final String s9052 = "9052";
+ public static final String s9053 = "9053";
+ public static final String s9054 = "9054";
+ public static final String s9055 = "9055";
+ public static final String s9056 = "9056";
+ public static final String s9057 = "9057";
+ public static final String s9058 = "9058";
+ public static final String s9059 = "9059";
+ public static final String s9060 = "9060";
+ public static final String s9061 = "9061";
+ public static final String s9062 = "9062";
+ public static final String s9063 = "9063";
+ public static final String s9064 = "9064";
+ public static final String s9065 = "9065";
+ public static final String s9066 = "9066";
+ public static final String s9067 = "9067";
+ public static final String s9068 = "9068";
+ public static final String s9069 = "9069";
+ public static final String s9070 = "9070";
+ public static final String s9071 = "9071";
+ public static final String s9072 = "9072";
+ public static final String s9073 = "9073";
+ public static final String s9074 = "9074";
+ public static final String s9075 = "9075";
+ public static final String s9076 = "9076";
+ public static final String s9077 = "9077";
+ public static final String s9078 = "9078";
+ public static final String s9079 = "9079";
+ public static final String s9080 = "9080";
+ public static final String s9081 = "9081";
+ public static final String s9082 = "9082";
+ public static final String s9083 = "9083";
+ public static final String s9084 = "9084";
+ public static final String s9085 = "9085";
+ public static final String s9086 = "9086";
+ public static final String s9087 = "9087";
+ public static final String s9088 = "9088";
+ public static final String s9089 = "9089";
+ public static final String s9090 = "9090";
+ public static final String s9091 = "9091";
+ public static final String s9092 = "9092";
+ public static final String s9093 = "9093";
+ public static final String s9094 = "9094";
+ public static final String s9095 = "9095";
+ public static final String s9096 = "9096";
+ public static final String s9097 = "9097";
+ public static final String s9098 = "9098";
+ public static final String s9099 = "9099";
+ public static final String s9100 = "9100";
+ public static final String s9101 = "9101";
+ public static final String s9102 = "9102";
+ public static final String s9103 = "9103";
+ public static final String s9104 = "9104";
+ public static final String s9105 = "9105";
+ public static final String s9106 = "9106";
+ public static final String s9107 = "9107";
+ public static final String s9108 = "9108";
+ public static final String s9109 = "9109";
+ public static final String s9110 = "9110";
+ public static final String s9111 = "9111";
+ public static final String s9112 = "9112";
+ public static final String s9113 = "9113";
+ public static final String s9114 = "9114";
+ public static final String s9115 = "9115";
+ public static final String s9116 = "9116";
+ public static final String s9117 = "9117";
+ public static final String s9118 = "9118";
+ public static final String s9119 = "9119";
+ public static final String s9120 = "9120";
+ public static final String s9121 = "9121";
+ public static final String s9122 = "9122";
+ public static final String s9123 = "9123";
+ public static final String s9124 = "9124";
+ public static final String s9125 = "9125";
+ public static final String s9126 = "9126";
+ public static final String s9127 = "9127";
+ public static final String s9128 = "9128";
+ public static final String s9129 = "9129";
+ public static final String s9130 = "9130";
+ public static final String s9131 = "9131";
+ public static final String s9132 = "9132";
+ public static final String s9133 = "9133";
+ public static final String s9134 = "9134";
+ public static final String s9135 = "9135";
+ public static final String s9136 = "9136";
+ public static final String s9137 = "9137";
+ public static final String s9138 = "9138";
+ public static final String s9139 = "9139";
+ public static final String s9140 = "9140";
+ public static final String s9141 = "9141";
+ public static final String s9142 = "9142";
+ public static final String s9143 = "9143";
+ public static final String s9144 = "9144";
+ public static final String s9145 = "9145";
+ public static final String s9146 = "9146";
+ public static final String s9147 = "9147";
+ public static final String s9148 = "9148";
+ public static final String s9149 = "9149";
+ public static final String s9150 = "9150";
+ public static final String s9151 = "9151";
+ public static final String s9152 = "9152";
+ public static final String s9153 = "9153";
+ public static final String s9154 = "9154";
+ public static final String s9155 = "9155";
+ public static final String s9156 = "9156";
+ public static final String s9157 = "9157";
+ public static final String s9158 = "9158";
+ public static final String s9159 = "9159";
+ public static final String s9160 = "9160";
+ public static final String s9161 = "9161";
+ public static final String s9162 = "9162";
+ public static final String s9163 = "9163";
+ public static final String s9164 = "9164";
+ public static final String s9165 = "9165";
+ public static final String s9166 = "9166";
+ public static final String s9167 = "9167";
+ public static final String s9168 = "9168";
+ public static final String s9169 = "9169";
+ public static final String s9170 = "9170";
+ public static final String s9171 = "9171";
+ public static final String s9172 = "9172";
+ public static final String s9173 = "9173";
+ public static final String s9174 = "9174";
+ public static final String s9175 = "9175";
+ public static final String s9176 = "9176";
+ public static final String s9177 = "9177";
+ public static final String s9178 = "9178";
+ public static final String s9179 = "9179";
+ public static final String s9180 = "9180";
+ public static final String s9181 = "9181";
+ public static final String s9182 = "9182";
+ public static final String s9183 = "9183";
+ public static final String s9184 = "9184";
+ public static final String s9185 = "9185";
+ public static final String s9186 = "9186";
+ public static final String s9187 = "9187";
+ public static final String s9188 = "9188";
+ public static final String s9189 = "9189";
+ public static final String s9190 = "9190";
+ public static final String s9191 = "9191";
+ public static final String s9192 = "9192";
+ public static final String s9193 = "9193";
+ public static final String s9194 = "9194";
+ public static final String s9195 = "9195";
+ public static final String s9196 = "9196";
+ public static final String s9197 = "9197";
+ public static final String s9198 = "9198";
+ public static final String s9199 = "9199";
+ public static final String s9200 = "9200";
+ public static final String s9201 = "9201";
+ public static final String s9202 = "9202";
+ public static final String s9203 = "9203";
+ public static final String s9204 = "9204";
+ public static final String s9205 = "9205";
+ public static final String s9206 = "9206";
+ public static final String s9207 = "9207";
+ public static final String s9208 = "9208";
+ public static final String s9209 = "9209";
+ public static final String s9210 = "9210";
+ public static final String s9211 = "9211";
+ public static final String s9212 = "9212";
+ public static final String s9213 = "9213";
+ public static final String s9214 = "9214";
+ public static final String s9215 = "9215";
+ public static final String s9216 = "9216";
+ public static final String s9217 = "9217";
+ public static final String s9218 = "9218";
+ public static final String s9219 = "9219";
+ public static final String s9220 = "9220";
+ public static final String s9221 = "9221";
+ public static final String s9222 = "9222";
+ public static final String s9223 = "9223";
+ public static final String s9224 = "9224";
+ public static final String s9225 = "9225";
+ public static final String s9226 = "9226";
+ public static final String s9227 = "9227";
+ public static final String s9228 = "9228";
+ public static final String s9229 = "9229";
+ public static final String s9230 = "9230";
+ public static final String s9231 = "9231";
+ public static final String s9232 = "9232";
+ public static final String s9233 = "9233";
+ public static final String s9234 = "9234";
+ public static final String s9235 = "9235";
+ public static final String s9236 = "9236";
+ public static final String s9237 = "9237";
+ public static final String s9238 = "9238";
+ public static final String s9239 = "9239";
+ public static final String s9240 = "9240";
+ public static final String s9241 = "9241";
+ public static final String s9242 = "9242";
+ public static final String s9243 = "9243";
+ public static final String s9244 = "9244";
+ public static final String s9245 = "9245";
+ public static final String s9246 = "9246";
+ public static final String s9247 = "9247";
+ public static final String s9248 = "9248";
+ public static final String s9249 = "9249";
+ public static final String s9250 = "9250";
+ public static final String s9251 = "9251";
+ public static final String s9252 = "9252";
+ public static final String s9253 = "9253";
+ public static final String s9254 = "9254";
+ public static final String s9255 = "9255";
+ public static final String s9256 = "9256";
+ public static final String s9257 = "9257";
+ public static final String s9258 = "9258";
+ public static final String s9259 = "9259";
+ public static final String s9260 = "9260";
+ public static final String s9261 = "9261";
+ public static final String s9262 = "9262";
+ public static final String s9263 = "9263";
+ public static final String s9264 = "9264";
+ public static final String s9265 = "9265";
+ public static final String s9266 = "9266";
+ public static final String s9267 = "9267";
+ public static final String s9268 = "9268";
+ public static final String s9269 = "9269";
+ public static final String s9270 = "9270";
+ public static final String s9271 = "9271";
+ public static final String s9272 = "9272";
+ public static final String s9273 = "9273";
+ public static final String s9274 = "9274";
+ public static final String s9275 = "9275";
+ public static final String s9276 = "9276";
+ public static final String s9277 = "9277";
+ public static final String s9278 = "9278";
+ public static final String s9279 = "9279";
+ public static final String s9280 = "9280";
+ public static final String s9281 = "9281";
+ public static final String s9282 = "9282";
+ public static final String s9283 = "9283";
+ public static final String s9284 = "9284";
+ public static final String s9285 = "9285";
+ public static final String s9286 = "9286";
+ public static final String s9287 = "9287";
+ public static final String s9288 = "9288";
+ public static final String s9289 = "9289";
+ public static final String s9290 = "9290";
+ public static final String s9291 = "9291";
+ public static final String s9292 = "9292";
+ public static final String s9293 = "9293";
+ public static final String s9294 = "9294";
+ public static final String s9295 = "9295";
+ public static final String s9296 = "9296";
+ public static final String s9297 = "9297";
+ public static final String s9298 = "9298";
+ public static final String s9299 = "9299";
+ public static final String s9300 = "9300";
+ public static final String s9301 = "9301";
+ public static final String s9302 = "9302";
+ public static final String s9303 = "9303";
+ public static final String s9304 = "9304";
+ public static final String s9305 = "9305";
+ public static final String s9306 = "9306";
+ public static final String s9307 = "9307";
+ public static final String s9308 = "9308";
+ public static final String s9309 = "9309";
+ public static final String s9310 = "9310";
+ public static final String s9311 = "9311";
+ public static final String s9312 = "9312";
+ public static final String s9313 = "9313";
+ public static final String s9314 = "9314";
+ public static final String s9315 = "9315";
+ public static final String s9316 = "9316";
+ public static final String s9317 = "9317";
+ public static final String s9318 = "9318";
+ public static final String s9319 = "9319";
+ public static final String s9320 = "9320";
+ public static final String s9321 = "9321";
+ public static final String s9322 = "9322";
+ public static final String s9323 = "9323";
+ public static final String s9324 = "9324";
+ public static final String s9325 = "9325";
+ public static final String s9326 = "9326";
+ public static final String s9327 = "9327";
+ public static final String s9328 = "9328";
+ public static final String s9329 = "9329";
+ public static final String s9330 = "9330";
+ public static final String s9331 = "9331";
+ public static final String s9332 = "9332";
+ public static final String s9333 = "9333";
+ public static final String s9334 = "9334";
+ public static final String s9335 = "9335";
+ public static final String s9336 = "9336";
+ public static final String s9337 = "9337";
+ public static final String s9338 = "9338";
+ public static final String s9339 = "9339";
+ public static final String s9340 = "9340";
+ public static final String s9341 = "9341";
+ public static final String s9342 = "9342";
+ public static final String s9343 = "9343";
+ public static final String s9344 = "9344";
+ public static final String s9345 = "9345";
+ public static final String s9346 = "9346";
+ public static final String s9347 = "9347";
+ public static final String s9348 = "9348";
+ public static final String s9349 = "9349";
+ public static final String s9350 = "9350";
+ public static final String s9351 = "9351";
+ public static final String s9352 = "9352";
+ public static final String s9353 = "9353";
+ public static final String s9354 = "9354";
+ public static final String s9355 = "9355";
+ public static final String s9356 = "9356";
+ public static final String s9357 = "9357";
+ public static final String s9358 = "9358";
+ public static final String s9359 = "9359";
+ public static final String s9360 = "9360";
+ public static final String s9361 = "9361";
+ public static final String s9362 = "9362";
+ public static final String s9363 = "9363";
+ public static final String s9364 = "9364";
+ public static final String s9365 = "9365";
+ public static final String s9366 = "9366";
+ public static final String s9367 = "9367";
+ public static final String s9368 = "9368";
+ public static final String s9369 = "9369";
+ public static final String s9370 = "9370";
+ public static final String s9371 = "9371";
+ public static final String s9372 = "9372";
+ public static final String s9373 = "9373";
+ public static final String s9374 = "9374";
+ public static final String s9375 = "9375";
+ public static final String s9376 = "9376";
+ public static final String s9377 = "9377";
+ public static final String s9378 = "9378";
+ public static final String s9379 = "9379";
+ public static final String s9380 = "9380";
+ public static final String s9381 = "9381";
+ public static final String s9382 = "9382";
+ public static final String s9383 = "9383";
+ public static final String s9384 = "9384";
+ public static final String s9385 = "9385";
+ public static final String s9386 = "9386";
+ public static final String s9387 = "9387";
+ public static final String s9388 = "9388";
+ public static final String s9389 = "9389";
+ public static final String s9390 = "9390";
+ public static final String s9391 = "9391";
+ public static final String s9392 = "9392";
+ public static final String s9393 = "9393";
+ public static final String s9394 = "9394";
+ public static final String s9395 = "9395";
+ public static final String s9396 = "9396";
+ public static final String s9397 = "9397";
+ public static final String s9398 = "9398";
+ public static final String s9399 = "9399";
+ public static final String s9400 = "9400";
+ public static final String s9401 = "9401";
+ public static final String s9402 = "9402";
+ public static final String s9403 = "9403";
+ public static final String s9404 = "9404";
+ public static final String s9405 = "9405";
+ public static final String s9406 = "9406";
+ public static final String s9407 = "9407";
+ public static final String s9408 = "9408";
+ public static final String s9409 = "9409";
+ public static final String s9410 = "9410";
+ public static final String s9411 = "9411";
+ public static final String s9412 = "9412";
+ public static final String s9413 = "9413";
+ public static final String s9414 = "9414";
+ public static final String s9415 = "9415";
+ public static final String s9416 = "9416";
+ public static final String s9417 = "9417";
+ public static final String s9418 = "9418";
+ public static final String s9419 = "9419";
+ public static final String s9420 = "9420";
+ public static final String s9421 = "9421";
+ public static final String s9422 = "9422";
+ public static final String s9423 = "9423";
+ public static final String s9424 = "9424";
+ public static final String s9425 = "9425";
+ public static final String s9426 = "9426";
+ public static final String s9427 = "9427";
+ public static final String s9428 = "9428";
+ public static final String s9429 = "9429";
+ public static final String s9430 = "9430";
+ public static final String s9431 = "9431";
+ public static final String s9432 = "9432";
+ public static final String s9433 = "9433";
+ public static final String s9434 = "9434";
+ public static final String s9435 = "9435";
+ public static final String s9436 = "9436";
+ public static final String s9437 = "9437";
+ public static final String s9438 = "9438";
+ public static final String s9439 = "9439";
+ public static final String s9440 = "9440";
+ public static final String s9441 = "9441";
+ public static final String s9442 = "9442";
+ public static final String s9443 = "9443";
+ public static final String s9444 = "9444";
+ public static final String s9445 = "9445";
+ public static final String s9446 = "9446";
+ public static final String s9447 = "9447";
+ public static final String s9448 = "9448";
+ public static final String s9449 = "9449";
+ public static final String s9450 = "9450";
+ public static final String s9451 = "9451";
+ public static final String s9452 = "9452";
+ public static final String s9453 = "9453";
+ public static final String s9454 = "9454";
+ public static final String s9455 = "9455";
+ public static final String s9456 = "9456";
+ public static final String s9457 = "9457";
+ public static final String s9458 = "9458";
+ public static final String s9459 = "9459";
+ public static final String s9460 = "9460";
+ public static final String s9461 = "9461";
+ public static final String s9462 = "9462";
+ public static final String s9463 = "9463";
+ public static final String s9464 = "9464";
+ public static final String s9465 = "9465";
+ public static final String s9466 = "9466";
+ public static final String s9467 = "9467";
+ public static final String s9468 = "9468";
+ public static final String s9469 = "9469";
+ public static final String s9470 = "9470";
+ public static final String s9471 = "9471";
+ public static final String s9472 = "9472";
+ public static final String s9473 = "9473";
+ public static final String s9474 = "9474";
+ public static final String s9475 = "9475";
+ public static final String s9476 = "9476";
+ public static final String s9477 = "9477";
+ public static final String s9478 = "9478";
+ public static final String s9479 = "9479";
+ public static final String s9480 = "9480";
+ public static final String s9481 = "9481";
+ public static final String s9482 = "9482";
+ public static final String s9483 = "9483";
+ public static final String s9484 = "9484";
+ public static final String s9485 = "9485";
+ public static final String s9486 = "9486";
+ public static final String s9487 = "9487";
+ public static final String s9488 = "9488";
+ public static final String s9489 = "9489";
+ public static final String s9490 = "9490";
+ public static final String s9491 = "9491";
+ public static final String s9492 = "9492";
+ public static final String s9493 = "9493";
+ public static final String s9494 = "9494";
+ public static final String s9495 = "9495";
+ public static final String s9496 = "9496";
+ public static final String s9497 = "9497";
+ public static final String s9498 = "9498";
+ public static final String s9499 = "9499";
+ public static final String s9500 = "9500";
+ public static final String s9501 = "9501";
+ public static final String s9502 = "9502";
+ public static final String s9503 = "9503";
+ public static final String s9504 = "9504";
+ public static final String s9505 = "9505";
+ public static final String s9506 = "9506";
+ public static final String s9507 = "9507";
+ public static final String s9508 = "9508";
+ public static final String s9509 = "9509";
+ public static final String s9510 = "9510";
+ public static final String s9511 = "9511";
+ public static final String s9512 = "9512";
+ public static final String s9513 = "9513";
+ public static final String s9514 = "9514";
+ public static final String s9515 = "9515";
+ public static final String s9516 = "9516";
+ public static final String s9517 = "9517";
+ public static final String s9518 = "9518";
+ public static final String s9519 = "9519";
+ public static final String s9520 = "9520";
+ public static final String s9521 = "9521";
+ public static final String s9522 = "9522";
+ public static final String s9523 = "9523";
+ public static final String s9524 = "9524";
+ public static final String s9525 = "9525";
+ public static final String s9526 = "9526";
+ public static final String s9527 = "9527";
+ public static final String s9528 = "9528";
+ public static final String s9529 = "9529";
+ public static final String s9530 = "9530";
+ public static final String s9531 = "9531";
+ public static final String s9532 = "9532";
+ public static final String s9533 = "9533";
+ public static final String s9534 = "9534";
+ public static final String s9535 = "9535";
+ public static final String s9536 = "9536";
+ public static final String s9537 = "9537";
+ public static final String s9538 = "9538";
+ public static final String s9539 = "9539";
+ public static final String s9540 = "9540";
+ public static final String s9541 = "9541";
+ public static final String s9542 = "9542";
+ public static final String s9543 = "9543";
+ public static final String s9544 = "9544";
+ public static final String s9545 = "9545";
+ public static final String s9546 = "9546";
+ public static final String s9547 = "9547";
+ public static final String s9548 = "9548";
+ public static final String s9549 = "9549";
+ public static final String s9550 = "9550";
+ public static final String s9551 = "9551";
+ public static final String s9552 = "9552";
+ public static final String s9553 = "9553";
+ public static final String s9554 = "9554";
+ public static final String s9555 = "9555";
+ public static final String s9556 = "9556";
+ public static final String s9557 = "9557";
+ public static final String s9558 = "9558";
+ public static final String s9559 = "9559";
+ public static final String s9560 = "9560";
+ public static final String s9561 = "9561";
+ public static final String s9562 = "9562";
+ public static final String s9563 = "9563";
+ public static final String s9564 = "9564";
+ public static final String s9565 = "9565";
+ public static final String s9566 = "9566";
+ public static final String s9567 = "9567";
+ public static final String s9568 = "9568";
+ public static final String s9569 = "9569";
+ public static final String s9570 = "9570";
+ public static final String s9571 = "9571";
+ public static final String s9572 = "9572";
+ public static final String s9573 = "9573";
+ public static final String s9574 = "9574";
+ public static final String s9575 = "9575";
+ public static final String s9576 = "9576";
+ public static final String s9577 = "9577";
+ public static final String s9578 = "9578";
+ public static final String s9579 = "9579";
+ public static final String s9580 = "9580";
+ public static final String s9581 = "9581";
+ public static final String s9582 = "9582";
+ public static final String s9583 = "9583";
+ public static final String s9584 = "9584";
+ public static final String s9585 = "9585";
+ public static final String s9586 = "9586";
+ public static final String s9587 = "9587";
+ public static final String s9588 = "9588";
+ public static final String s9589 = "9589";
+ public static final String s9590 = "9590";
+ public static final String s9591 = "9591";
+ public static final String s9592 = "9592";
+ public static final String s9593 = "9593";
+ public static final String s9594 = "9594";
+ public static final String s9595 = "9595";
+ public static final String s9596 = "9596";
+ public static final String s9597 = "9597";
+ public static final String s9598 = "9598";
+ public static final String s9599 = "9599";
+ public static final String s9600 = "9600";
+ public static final String s9601 = "9601";
+ public static final String s9602 = "9602";
+ public static final String s9603 = "9603";
+ public static final String s9604 = "9604";
+ public static final String s9605 = "9605";
+ public static final String s9606 = "9606";
+ public static final String s9607 = "9607";
+ public static final String s9608 = "9608";
+ public static final String s9609 = "9609";
+ public static final String s9610 = "9610";
+ public static final String s9611 = "9611";
+ public static final String s9612 = "9612";
+ public static final String s9613 = "9613";
+ public static final String s9614 = "9614";
+ public static final String s9615 = "9615";
+ public static final String s9616 = "9616";
+ public static final String s9617 = "9617";
+ public static final String s9618 = "9618";
+ public static final String s9619 = "9619";
+ public static final String s9620 = "9620";
+ public static final String s9621 = "9621";
+ public static final String s9622 = "9622";
+ public static final String s9623 = "9623";
+ public static final String s9624 = "9624";
+ public static final String s9625 = "9625";
+ public static final String s9626 = "9626";
+ public static final String s9627 = "9627";
+ public static final String s9628 = "9628";
+ public static final String s9629 = "9629";
+ public static final String s9630 = "9630";
+ public static final String s9631 = "9631";
+ public static final String s9632 = "9632";
+ public static final String s9633 = "9633";
+ public static final String s9634 = "9634";
+ public static final String s9635 = "9635";
+ public static final String s9636 = "9636";
+ public static final String s9637 = "9637";
+ public static final String s9638 = "9638";
+ public static final String s9639 = "9639";
+ public static final String s9640 = "9640";
+ public static final String s9641 = "9641";
+ public static final String s9642 = "9642";
+ public static final String s9643 = "9643";
+ public static final String s9644 = "9644";
+ public static final String s9645 = "9645";
+ public static final String s9646 = "9646";
+ public static final String s9647 = "9647";
+ public static final String s9648 = "9648";
+ public static final String s9649 = "9649";
+ public static final String s9650 = "9650";
+ public static final String s9651 = "9651";
+ public static final String s9652 = "9652";
+ public static final String s9653 = "9653";
+ public static final String s9654 = "9654";
+ public static final String s9655 = "9655";
+ public static final String s9656 = "9656";
+ public static final String s9657 = "9657";
+ public static final String s9658 = "9658";
+ public static final String s9659 = "9659";
+ public static final String s9660 = "9660";
+ public static final String s9661 = "9661";
+ public static final String s9662 = "9662";
+ public static final String s9663 = "9663";
+ public static final String s9664 = "9664";
+ public static final String s9665 = "9665";
+ public static final String s9666 = "9666";
+ public static final String s9667 = "9667";
+ public static final String s9668 = "9668";
+ public static final String s9669 = "9669";
+ public static final String s9670 = "9670";
+ public static final String s9671 = "9671";
+ public static final String s9672 = "9672";
+ public static final String s9673 = "9673";
+ public static final String s9674 = "9674";
+ public static final String s9675 = "9675";
+ public static final String s9676 = "9676";
+ public static final String s9677 = "9677";
+ public static final String s9678 = "9678";
+ public static final String s9679 = "9679";
+ public static final String s9680 = "9680";
+ public static final String s9681 = "9681";
+ public static final String s9682 = "9682";
+ public static final String s9683 = "9683";
+ public static final String s9684 = "9684";
+ public static final String s9685 = "9685";
+ public static final String s9686 = "9686";
+ public static final String s9687 = "9687";
+ public static final String s9688 = "9688";
+ public static final String s9689 = "9689";
+ public static final String s9690 = "9690";
+ public static final String s9691 = "9691";
+ public static final String s9692 = "9692";
+ public static final String s9693 = "9693";
+ public static final String s9694 = "9694";
+ public static final String s9695 = "9695";
+ public static final String s9696 = "9696";
+ public static final String s9697 = "9697";
+ public static final String s9698 = "9698";
+ public static final String s9699 = "9699";
+ public static final String s9700 = "9700";
+ public static final String s9701 = "9701";
+ public static final String s9702 = "9702";
+ public static final String s9703 = "9703";
+ public static final String s9704 = "9704";
+ public static final String s9705 = "9705";
+ public static final String s9706 = "9706";
+ public static final String s9707 = "9707";
+ public static final String s9708 = "9708";
+ public static final String s9709 = "9709";
+ public static final String s9710 = "9710";
+ public static final String s9711 = "9711";
+ public static final String s9712 = "9712";
+ public static final String s9713 = "9713";
+ public static final String s9714 = "9714";
+ public static final String s9715 = "9715";
+ public static final String s9716 = "9716";
+ public static final String s9717 = "9717";
+ public static final String s9718 = "9718";
+ public static final String s9719 = "9719";
+ public static final String s9720 = "9720";
+ public static final String s9721 = "9721";
+ public static final String s9722 = "9722";
+ public static final String s9723 = "9723";
+ public static final String s9724 = "9724";
+ public static final String s9725 = "9725";
+ public static final String s9726 = "9726";
+ public static final String s9727 = "9727";
+ public static final String s9728 = "9728";
+ public static final String s9729 = "9729";
+ public static final String s9730 = "9730";
+ public static final String s9731 = "9731";
+ public static final String s9732 = "9732";
+ public static final String s9733 = "9733";
+ public static final String s9734 = "9734";
+ public static final String s9735 = "9735";
+ public static final String s9736 = "9736";
+ public static final String s9737 = "9737";
+ public static final String s9738 = "9738";
+ public static final String s9739 = "9739";
+ public static final String s9740 = "9740";
+ public static final String s9741 = "9741";
+ public static final String s9742 = "9742";
+ public static final String s9743 = "9743";
+ public static final String s9744 = "9744";
+ public static final String s9745 = "9745";
+ public static final String s9746 = "9746";
+ public static final String s9747 = "9747";
+ public static final String s9748 = "9748";
+ public static final String s9749 = "9749";
+ public static final String s9750 = "9750";
+ public static final String s9751 = "9751";
+ public static final String s9752 = "9752";
+ public static final String s9753 = "9753";
+ public static final String s9754 = "9754";
+ public static final String s9755 = "9755";
+ public static final String s9756 = "9756";
+ public static final String s9757 = "9757";
+ public static final String s9758 = "9758";
+ public static final String s9759 = "9759";
+ public static final String s9760 = "9760";
+ public static final String s9761 = "9761";
+ public static final String s9762 = "9762";
+ public static final String s9763 = "9763";
+ public static final String s9764 = "9764";
+ public static final String s9765 = "9765";
+ public static final String s9766 = "9766";
+ public static final String s9767 = "9767";
+ public static final String s9768 = "9768";
+ public static final String s9769 = "9769";
+ public static final String s9770 = "9770";
+ public static final String s9771 = "9771";
+ public static final String s9772 = "9772";
+ public static final String s9773 = "9773";
+ public static final String s9774 = "9774";
+ public static final String s9775 = "9775";
+ public static final String s9776 = "9776";
+ public static final String s9777 = "9777";
+ public static final String s9778 = "9778";
+ public static final String s9779 = "9779";
+ public static final String s9780 = "9780";
+ public static final String s9781 = "9781";
+ public static final String s9782 = "9782";
+ public static final String s9783 = "9783";
+ public static final String s9784 = "9784";
+ public static final String s9785 = "9785";
+ public static final String s9786 = "9786";
+ public static final String s9787 = "9787";
+ public static final String s9788 = "9788";
+ public static final String s9789 = "9789";
+ public static final String s9790 = "9790";
+ public static final String s9791 = "9791";
+ public static final String s9792 = "9792";
+ public static final String s9793 = "9793";
+ public static final String s9794 = "9794";
+ public static final String s9795 = "9795";
+ public static final String s9796 = "9796";
+ public static final String s9797 = "9797";
+ public static final String s9798 = "9798";
+ public static final String s9799 = "9799";
+ public static final String s9800 = "9800";
+ public static final String s9801 = "9801";
+ public static final String s9802 = "9802";
+ public static final String s9803 = "9803";
+ public static final String s9804 = "9804";
+ public static final String s9805 = "9805";
+ public static final String s9806 = "9806";
+ public static final String s9807 = "9807";
+ public static final String s9808 = "9808";
+ public static final String s9809 = "9809";
+ public static final String s9810 = "9810";
+ public static final String s9811 = "9811";
+ public static final String s9812 = "9812";
+ public static final String s9813 = "9813";
+ public static final String s9814 = "9814";
+ public static final String s9815 = "9815";
+ public static final String s9816 = "9816";
+ public static final String s9817 = "9817";
+ public static final String s9818 = "9818";
+ public static final String s9819 = "9819";
+ public static final String s9820 = "9820";
+ public static final String s9821 = "9821";
+ public static final String s9822 = "9822";
+ public static final String s9823 = "9823";
+ public static final String s9824 = "9824";
+ public static final String s9825 = "9825";
+ public static final String s9826 = "9826";
+ public static final String s9827 = "9827";
+ public static final String s9828 = "9828";
+ public static final String s9829 = "9829";
+ public static final String s9830 = "9830";
+ public static final String s9831 = "9831";
+ public static final String s9832 = "9832";
+ public static final String s9833 = "9833";
+ public static final String s9834 = "9834";
+ public static final String s9835 = "9835";
+ public static final String s9836 = "9836";
+ public static final String s9837 = "9837";
+ public static final String s9838 = "9838";
+ public static final String s9839 = "9839";
+ public static final String s9840 = "9840";
+ public static final String s9841 = "9841";
+ public static final String s9842 = "9842";
+ public static final String s9843 = "9843";
+ public static final String s9844 = "9844";
+ public static final String s9845 = "9845";
+ public static final String s9846 = "9846";
+ public static final String s9847 = "9847";
+ public static final String s9848 = "9848";
+ public static final String s9849 = "9849";
+ public static final String s9850 = "9850";
+ public static final String s9851 = "9851";
+ public static final String s9852 = "9852";
+ public static final String s9853 = "9853";
+ public static final String s9854 = "9854";
+ public static final String s9855 = "9855";
+ public static final String s9856 = "9856";
+ public static final String s9857 = "9857";
+ public static final String s9858 = "9858";
+ public static final String s9859 = "9859";
+ public static final String s9860 = "9860";
+ public static final String s9861 = "9861";
+ public static final String s9862 = "9862";
+ public static final String s9863 = "9863";
+ public static final String s9864 = "9864";
+ public static final String s9865 = "9865";
+ public static final String s9866 = "9866";
+ public static final String s9867 = "9867";
+ public static final String s9868 = "9868";
+ public static final String s9869 = "9869";
+ public static final String s9870 = "9870";
+ public static final String s9871 = "9871";
+ public static final String s9872 = "9872";
+ public static final String s9873 = "9873";
+ public static final String s9874 = "9874";
+ public static final String s9875 = "9875";
+ public static final String s9876 = "9876";
+ public static final String s9877 = "9877";
+ public static final String s9878 = "9878";
+ public static final String s9879 = "9879";
+ public static final String s9880 = "9880";
+ public static final String s9881 = "9881";
+ public static final String s9882 = "9882";
+ public static final String s9883 = "9883";
+ public static final String s9884 = "9884";
+ public static final String s9885 = "9885";
+ public static final String s9886 = "9886";
+ public static final String s9887 = "9887";
+ public static final String s9888 = "9888";
+ public static final String s9889 = "9889";
+ public static final String s9890 = "9890";
+ public static final String s9891 = "9891";
+ public static final String s9892 = "9892";
+ public static final String s9893 = "9893";
+ public static final String s9894 = "9894";
+ public static final String s9895 = "9895";
+ public static final String s9896 = "9896";
+ public static final String s9897 = "9897";
+ public static final String s9898 = "9898";
+ public static final String s9899 = "9899";
+ public static final String s9900 = "9900";
+ public static final String s9901 = "9901";
+ public static final String s9902 = "9902";
+ public static final String s9903 = "9903";
+ public static final String s9904 = "9904";
+ public static final String s9905 = "9905";
+ public static final String s9906 = "9906";
+ public static final String s9907 = "9907";
+ public static final String s9908 = "9908";
+ public static final String s9909 = "9909";
+ public static final String s9910 = "9910";
+ public static final String s9911 = "9911";
+ public static final String s9912 = "9912";
+ public static final String s9913 = "9913";
+ public static final String s9914 = "9914";
+ public static final String s9915 = "9915";
+ public static final String s9916 = "9916";
+ public static final String s9917 = "9917";
+ public static final String s9918 = "9918";
+ public static final String s9919 = "9919";
+ public static final String s9920 = "9920";
+ public static final String s9921 = "9921";
+ public static final String s9922 = "9922";
+ public static final String s9923 = "9923";
+ public static final String s9924 = "9924";
+ public static final String s9925 = "9925";
+ public static final String s9926 = "9926";
+ public static final String s9927 = "9927";
+ public static final String s9928 = "9928";
+ public static final String s9929 = "9929";
+ public static final String s9930 = "9930";
+ public static final String s9931 = "9931";
+ public static final String s9932 = "9932";
+ public static final String s9933 = "9933";
+ public static final String s9934 = "9934";
+ public static final String s9935 = "9935";
+ public static final String s9936 = "9936";
+ public static final String s9937 = "9937";
+ public static final String s9938 = "9938";
+ public static final String s9939 = "9939";
+ public static final String s9940 = "9940";
+ public static final String s9941 = "9941";
+ public static final String s9942 = "9942";
+ public static final String s9943 = "9943";
+ public static final String s9944 = "9944";
+ public static final String s9945 = "9945";
+ public static final String s9946 = "9946";
+ public static final String s9947 = "9947";
+ public static final String s9948 = "9948";
+ public static final String s9949 = "9949";
+ public static final String s9950 = "9950";
+ public static final String s9951 = "9951";
+ public static final String s9952 = "9952";
+ public static final String s9953 = "9953";
+ public static final String s9954 = "9954";
+ public static final String s9955 = "9955";
+ public static final String s9956 = "9956";
+ public static final String s9957 = "9957";
+ public static final String s9958 = "9958";
+ public static final String s9959 = "9959";
+ public static final String s9960 = "9960";
+ public static final String s9961 = "9961";
+ public static final String s9962 = "9962";
+ public static final String s9963 = "9963";
+ public static final String s9964 = "9964";
+ public static final String s9965 = "9965";
+ public static final String s9966 = "9966";
+ public static final String s9967 = "9967";
+ public static final String s9968 = "9968";
+ public static final String s9969 = "9969";
+ public static final String s9970 = "9970";
+ public static final String s9971 = "9971";
+ public static final String s9972 = "9972";
+ public static final String s9973 = "9973";
+ public static final String s9974 = "9974";
+ public static final String s9975 = "9975";
+ public static final String s9976 = "9976";
+ public static final String s9977 = "9977";
+ public static final String s9978 = "9978";
+ public static final String s9979 = "9979";
+ public static final String s9980 = "9980";
+ public static final String s9981 = "9981";
+ public static final String s9982 = "9982";
+ public static final String s9983 = "9983";
+ public static final String s9984 = "9984";
+ public static final String s9985 = "9985";
+ public static final String s9986 = "9986";
+ public static final String s9987 = "9987";
+ public static final String s9988 = "9988";
+ public static final String s9989 = "9989";
+ public static final String s9990 = "9990";
+ public static final String s9991 = "9991";
+ public static final String s9992 = "9992";
+ public static final String s9993 = "9993";
+ public static final String s9994 = "9994";
+ public static final String s9995 = "9995";
+ public static final String s9996 = "9996";
+ public static final String s9997 = "9997";
+ public static final String s9998 = "9998";
+ public static final String s9999 = "9999";
+ public static final String s10000 = "10000";
+ public static final String s10001 = "10001";
+ public static final String s10002 = "10002";
+ public static final String s10003 = "10003";
+ public static final String s10004 = "10004";
+ public static final String s10005 = "10005";
+ public static final String s10006 = "10006";
+ public static final String s10007 = "10007";
+ public static final String s10008 = "10008";
+ public static final String s10009 = "10009";
+ public static final String s10010 = "10010";
+ public static final String s10011 = "10011";
+ public static final String s10012 = "10012";
+ public static final String s10013 = "10013";
+ public static final String s10014 = "10014";
+ public static final String s10015 = "10015";
+ public static final String s10016 = "10016";
+ public static final String s10017 = "10017";
+ public static final String s10018 = "10018";
+ public static final String s10019 = "10019";
+ public static final String s10020 = "10020";
+ public static final String s10021 = "10021";
+ public static final String s10022 = "10022";
+ public static final String s10023 = "10023";
+ public static final String s10024 = "10024";
+ public static final String s10025 = "10025";
+ public static final String s10026 = "10026";
+ public static final String s10027 = "10027";
+ public static final String s10028 = "10028";
+ public static final String s10029 = "10029";
+ public static final String s10030 = "10030";
+ public static final String s10031 = "10031";
+ public static final String s10032 = "10032";
+ public static final String s10033 = "10033";
+ public static final String s10034 = "10034";
+ public static final String s10035 = "10035";
+ public static final String s10036 = "10036";
+ public static final String s10037 = "10037";
+ public static final String s10038 = "10038";
+ public static final String s10039 = "10039";
+ public static final String s10040 = "10040";
+ public static final String s10041 = "10041";
+ public static final String s10042 = "10042";
+ public static final String s10043 = "10043";
+ public static final String s10044 = "10044";
+ public static final String s10045 = "10045";
+ public static final String s10046 = "10046";
+ public static final String s10047 = "10047";
+ public static final String s10048 = "10048";
+ public static final String s10049 = "10049";
+ public static final String s10050 = "10050";
+ public static final String s10051 = "10051";
+ public static final String s10052 = "10052";
+ public static final String s10053 = "10053";
+ public static final String s10054 = "10054";
+ public static final String s10055 = "10055";
+ public static final String s10056 = "10056";
+ public static final String s10057 = "10057";
+ public static final String s10058 = "10058";
+ public static final String s10059 = "10059";
+ public static final String s10060 = "10060";
+ public static final String s10061 = "10061";
+ public static final String s10062 = "10062";
+ public static final String s10063 = "10063";
+ public static final String s10064 = "10064";
+ public static final String s10065 = "10065";
+ public static final String s10066 = "10066";
+ public static final String s10067 = "10067";
+ public static final String s10068 = "10068";
+ public static final String s10069 = "10069";
+ public static final String s10070 = "10070";
+ public static final String s10071 = "10071";
+ public static final String s10072 = "10072";
+ public static final String s10073 = "10073";
+ public static final String s10074 = "10074";
+ public static final String s10075 = "10075";
+ public static final String s10076 = "10076";
+ public static final String s10077 = "10077";
+ public static final String s10078 = "10078";
+ public static final String s10079 = "10079";
+ public static final String s10080 = "10080";
+ public static final String s10081 = "10081";
+ public static final String s10082 = "10082";
+ public static final String s10083 = "10083";
+ public static final String s10084 = "10084";
+ public static final String s10085 = "10085";
+ public static final String s10086 = "10086";
+ public static final String s10087 = "10087";
+ public static final String s10088 = "10088";
+ public static final String s10089 = "10089";
+ public static final String s10090 = "10090";
+ public static final String s10091 = "10091";
+ public static final String s10092 = "10092";
+ public static final String s10093 = "10093";
+ public static final String s10094 = "10094";
+ public static final String s10095 = "10095";
+ public static final String s10096 = "10096";
+ public static final String s10097 = "10097";
+ public static final String s10098 = "10098";
+ public static final String s10099 = "10099";
+ public static final String s10100 = "10100";
+ public static final String s10101 = "10101";
+ public static final String s10102 = "10102";
+ public static final String s10103 = "10103";
+ public static final String s10104 = "10104";
+ public static final String s10105 = "10105";
+ public static final String s10106 = "10106";
+ public static final String s10107 = "10107";
+ public static final String s10108 = "10108";
+ public static final String s10109 = "10109";
+ public static final String s10110 = "10110";
+ public static final String s10111 = "10111";
+ public static final String s10112 = "10112";
+ public static final String s10113 = "10113";
+ public static final String s10114 = "10114";
+ public static final String s10115 = "10115";
+ public static final String s10116 = "10116";
+ public static final String s10117 = "10117";
+ public static final String s10118 = "10118";
+ public static final String s10119 = "10119";
+ public static final String s10120 = "10120";
+ public static final String s10121 = "10121";
+ public static final String s10122 = "10122";
+ public static final String s10123 = "10123";
+ public static final String s10124 = "10124";
+ public static final String s10125 = "10125";
+ public static final String s10126 = "10126";
+ public static final String s10127 = "10127";
+ public static final String s10128 = "10128";
+ public static final String s10129 = "10129";
+ public static final String s10130 = "10130";
+ public static final String s10131 = "10131";
+ public static final String s10132 = "10132";
+ public static final String s10133 = "10133";
+ public static final String s10134 = "10134";
+ public static final String s10135 = "10135";
+ public static final String s10136 = "10136";
+ public static final String s10137 = "10137";
+ public static final String s10138 = "10138";
+ public static final String s10139 = "10139";
+ public static final String s10140 = "10140";
+ public static final String s10141 = "10141";
+ public static final String s10142 = "10142";
+ public static final String s10143 = "10143";
+ public static final String s10144 = "10144";
+ public static final String s10145 = "10145";
+ public static final String s10146 = "10146";
+ public static final String s10147 = "10147";
+ public static final String s10148 = "10148";
+ public static final String s10149 = "10149";
+ public static final String s10150 = "10150";
+ public static final String s10151 = "10151";
+ public static final String s10152 = "10152";
+ public static final String s10153 = "10153";
+ public static final String s10154 = "10154";
+ public static final String s10155 = "10155";
+ public static final String s10156 = "10156";
+ public static final String s10157 = "10157";
+ public static final String s10158 = "10158";
+ public static final String s10159 = "10159";
+ public static final String s10160 = "10160";
+ public static final String s10161 = "10161";
+ public static final String s10162 = "10162";
+ public static final String s10163 = "10163";
+ public static final String s10164 = "10164";
+ public static final String s10165 = "10165";
+ public static final String s10166 = "10166";
+ public static final String s10167 = "10167";
+ public static final String s10168 = "10168";
+ public static final String s10169 = "10169";
+ public static final String s10170 = "10170";
+ public static final String s10171 = "10171";
+ public static final String s10172 = "10172";
+ public static final String s10173 = "10173";
+ public static final String s10174 = "10174";
+ public static final String s10175 = "10175";
+ public static final String s10176 = "10176";
+ public static final String s10177 = "10177";
+ public static final String s10178 = "10178";
+ public static final String s10179 = "10179";
+ public static final String s10180 = "10180";
+ public static final String s10181 = "10181";
+ public static final String s10182 = "10182";
+ public static final String s10183 = "10183";
+ public static final String s10184 = "10184";
+ public static final String s10185 = "10185";
+ public static final String s10186 = "10186";
+ public static final String s10187 = "10187";
+ public static final String s10188 = "10188";
+ public static final String s10189 = "10189";
+ public static final String s10190 = "10190";
+ public static final String s10191 = "10191";
+ public static final String s10192 = "10192";
+ public static final String s10193 = "10193";
+ public static final String s10194 = "10194";
+ public static final String s10195 = "10195";
+ public static final String s10196 = "10196";
+ public static final String s10197 = "10197";
+ public static final String s10198 = "10198";
+ public static final String s10199 = "10199";
+ public static final String s10200 = "10200";
+ public static final String s10201 = "10201";
+ public static final String s10202 = "10202";
+ public static final String s10203 = "10203";
+ public static final String s10204 = "10204";
+ public static final String s10205 = "10205";
+ public static final String s10206 = "10206";
+ public static final String s10207 = "10207";
+ public static final String s10208 = "10208";
+ public static final String s10209 = "10209";
+ public static final String s10210 = "10210";
+ public static final String s10211 = "10211";
+ public static final String s10212 = "10212";
+ public static final String s10213 = "10213";
+ public static final String s10214 = "10214";
+ public static final String s10215 = "10215";
+ public static final String s10216 = "10216";
+ public static final String s10217 = "10217";
+ public static final String s10218 = "10218";
+ public static final String s10219 = "10219";
+ public static final String s10220 = "10220";
+ public static final String s10221 = "10221";
+ public static final String s10222 = "10222";
+ public static final String s10223 = "10223";
+ public static final String s10224 = "10224";
+ public static final String s10225 = "10225";
+ public static final String s10226 = "10226";
+ public static final String s10227 = "10227";
+ public static final String s10228 = "10228";
+ public static final String s10229 = "10229";
+ public static final String s10230 = "10230";
+ public static final String s10231 = "10231";
+ public static final String s10232 = "10232";
+ public static final String s10233 = "10233";
+ public static final String s10234 = "10234";
+ public static final String s10235 = "10235";
+ public static final String s10236 = "10236";
+ public static final String s10237 = "10237";
+ public static final String s10238 = "10238";
+ public static final String s10239 = "10239";
+ public static final String s10240 = "10240";
+ public static final String s10241 = "10241";
+ public static final String s10242 = "10242";
+ public static final String s10243 = "10243";
+ public static final String s10244 = "10244";
+ public static final String s10245 = "10245";
+ public static final String s10246 = "10246";
+ public static final String s10247 = "10247";
+ public static final String s10248 = "10248";
+ public static final String s10249 = "10249";
+ public static final String s10250 = "10250";
+ public static final String s10251 = "10251";
+ public static final String s10252 = "10252";
+ public static final String s10253 = "10253";
+ public static final String s10254 = "10254";
+ public static final String s10255 = "10255";
+ public static final String s10256 = "10256";
+ public static final String s10257 = "10257";
+ public static final String s10258 = "10258";
+ public static final String s10259 = "10259";
+ public static final String s10260 = "10260";
+ public static final String s10261 = "10261";
+ public static final String s10262 = "10262";
+ public static final String s10263 = "10263";
+ public static final String s10264 = "10264";
+ public static final String s10265 = "10265";
+ public static final String s10266 = "10266";
+ public static final String s10267 = "10267";
+ public static final String s10268 = "10268";
+ public static final String s10269 = "10269";
+ public static final String s10270 = "10270";
+ public static final String s10271 = "10271";
+ public static final String s10272 = "10272";
+ public static final String s10273 = "10273";
+ public static final String s10274 = "10274";
+ public static final String s10275 = "10275";
+ public static final String s10276 = "10276";
+ public static final String s10277 = "10277";
+ public static final String s10278 = "10278";
+ public static final String s10279 = "10279";
+ public static final String s10280 = "10280";
+ public static final String s10281 = "10281";
+ public static final String s10282 = "10282";
+ public static final String s10283 = "10283";
+ public static final String s10284 = "10284";
+ public static final String s10285 = "10285";
+ public static final String s10286 = "10286";
+ public static final String s10287 = "10287";
+ public static final String s10288 = "10288";
+ public static final String s10289 = "10289";
+ public static final String s10290 = "10290";
+ public static final String s10291 = "10291";
+ public static final String s10292 = "10292";
+ public static final String s10293 = "10293";
+ public static final String s10294 = "10294";
+ public static final String s10295 = "10295";
+ public static final String s10296 = "10296";
+ public static final String s10297 = "10297";
+ public static final String s10298 = "10298";
+ public static final String s10299 = "10299";
+ public static final String s10300 = "10300";
+ public static final String s10301 = "10301";
+ public static final String s10302 = "10302";
+ public static final String s10303 = "10303";
+ public static final String s10304 = "10304";
+ public static final String s10305 = "10305";
+ public static final String s10306 = "10306";
+ public static final String s10307 = "10307";
+ public static final String s10308 = "10308";
+ public static final String s10309 = "10309";
+ public static final String s10310 = "10310";
+ public static final String s10311 = "10311";
+ public static final String s10312 = "10312";
+ public static final String s10313 = "10313";
+ public static final String s10314 = "10314";
+ public static final String s10315 = "10315";
+ public static final String s10316 = "10316";
+ public static final String s10317 = "10317";
+ public static final String s10318 = "10318";
+ public static final String s10319 = "10319";
+ public static final String s10320 = "10320";
+ public static final String s10321 = "10321";
+ public static final String s10322 = "10322";
+ public static final String s10323 = "10323";
+ public static final String s10324 = "10324";
+ public static final String s10325 = "10325";
+ public static final String s10326 = "10326";
+ public static final String s10327 = "10327";
+ public static final String s10328 = "10328";
+ public static final String s10329 = "10329";
+ public static final String s10330 = "10330";
+ public static final String s10331 = "10331";
+ public static final String s10332 = "10332";
+ public static final String s10333 = "10333";
+ public static final String s10334 = "10334";
+ public static final String s10335 = "10335";
+ public static final String s10336 = "10336";
+ public static final String s10337 = "10337";
+ public static final String s10338 = "10338";
+ public static final String s10339 = "10339";
+ public static final String s10340 = "10340";
+ public static final String s10341 = "10341";
+ public static final String s10342 = "10342";
+ public static final String s10343 = "10343";
+ public static final String s10344 = "10344";
+ public static final String s10345 = "10345";
+ public static final String s10346 = "10346";
+ public static final String s10347 = "10347";
+ public static final String s10348 = "10348";
+ public static final String s10349 = "10349";
+ public static final String s10350 = "10350";
+ public static final String s10351 = "10351";
+ public static final String s10352 = "10352";
+ public static final String s10353 = "10353";
+ public static final String s10354 = "10354";
+ public static final String s10355 = "10355";
+ public static final String s10356 = "10356";
+ public static final String s10357 = "10357";
+ public static final String s10358 = "10358";
+ public static final String s10359 = "10359";
+ public static final String s10360 = "10360";
+ public static final String s10361 = "10361";
+ public static final String s10362 = "10362";
+ public static final String s10363 = "10363";
+ public static final String s10364 = "10364";
+ public static final String s10365 = "10365";
+ public static final String s10366 = "10366";
+ public static final String s10367 = "10367";
+ public static final String s10368 = "10368";
+ public static final String s10369 = "10369";
+ public static final String s10370 = "10370";
+ public static final String s10371 = "10371";
+ public static final String s10372 = "10372";
+ public static final String s10373 = "10373";
+ public static final String s10374 = "10374";
+ public static final String s10375 = "10375";
+ public static final String s10376 = "10376";
+ public static final String s10377 = "10377";
+ public static final String s10378 = "10378";
+ public static final String s10379 = "10379";
+ public static final String s10380 = "10380";
+ public static final String s10381 = "10381";
+ public static final String s10382 = "10382";
+ public static final String s10383 = "10383";
+ public static final String s10384 = "10384";
+ public static final String s10385 = "10385";
+ public static final String s10386 = "10386";
+ public static final String s10387 = "10387";
+ public static final String s10388 = "10388";
+ public static final String s10389 = "10389";
+ public static final String s10390 = "10390";
+ public static final String s10391 = "10391";
+ public static final String s10392 = "10392";
+ public static final String s10393 = "10393";
+ public static final String s10394 = "10394";
+ public static final String s10395 = "10395";
+ public static final String s10396 = "10396";
+ public static final String s10397 = "10397";
+ public static final String s10398 = "10398";
+ public static final String s10399 = "10399";
+ public static final String s10400 = "10400";
+ public static final String s10401 = "10401";
+ public static final String s10402 = "10402";
+ public static final String s10403 = "10403";
+ public static final String s10404 = "10404";
+ public static final String s10405 = "10405";
+ public static final String s10406 = "10406";
+ public static final String s10407 = "10407";
+ public static final String s10408 = "10408";
+ public static final String s10409 = "10409";
+ public static final String s10410 = "10410";
+ public static final String s10411 = "10411";
+ public static final String s10412 = "10412";
+ public static final String s10413 = "10413";
+ public static final String s10414 = "10414";
+ public static final String s10415 = "10415";
+ public static final String s10416 = "10416";
+ public static final String s10417 = "10417";
+ public static final String s10418 = "10418";
+ public static final String s10419 = "10419";
+ public static final String s10420 = "10420";
+ public static final String s10421 = "10421";
+ public static final String s10422 = "10422";
+ public static final String s10423 = "10423";
+ public static final String s10424 = "10424";
+ public static final String s10425 = "10425";
+ public static final String s10426 = "10426";
+ public static final String s10427 = "10427";
+ public static final String s10428 = "10428";
+ public static final String s10429 = "10429";
+ public static final String s10430 = "10430";
+ public static final String s10431 = "10431";
+ public static final String s10432 = "10432";
+ public static final String s10433 = "10433";
+ public static final String s10434 = "10434";
+ public static final String s10435 = "10435";
+ public static final String s10436 = "10436";
+ public static final String s10437 = "10437";
+ public static final String s10438 = "10438";
+ public static final String s10439 = "10439";
+ public static final String s10440 = "10440";
+ public static final String s10441 = "10441";
+ public static final String s10442 = "10442";
+ public static final String s10443 = "10443";
+ public static final String s10444 = "10444";
+ public static final String s10445 = "10445";
+ public static final String s10446 = "10446";
+ public static final String s10447 = "10447";
+ public static final String s10448 = "10448";
+ public static final String s10449 = "10449";
+ public static final String s10450 = "10450";
+ public static final String s10451 = "10451";
+ public static final String s10452 = "10452";
+ public static final String s10453 = "10453";
+ public static final String s10454 = "10454";
+ public static final String s10455 = "10455";
+ public static final String s10456 = "10456";
+ public static final String s10457 = "10457";
+ public static final String s10458 = "10458";
+ public static final String s10459 = "10459";
+ public static final String s10460 = "10460";
+ public static final String s10461 = "10461";
+ public static final String s10462 = "10462";
+ public static final String s10463 = "10463";
+ public static final String s10464 = "10464";
+ public static final String s10465 = "10465";
+ public static final String s10466 = "10466";
+ public static final String s10467 = "10467";
+ public static final String s10468 = "10468";
+ public static final String s10469 = "10469";
+ public static final String s10470 = "10470";
+ public static final String s10471 = "10471";
+ public static final String s10472 = "10472";
+ public static final String s10473 = "10473";
+ public static final String s10474 = "10474";
+ public static final String s10475 = "10475";
+ public static final String s10476 = "10476";
+ public static final String s10477 = "10477";
+ public static final String s10478 = "10478";
+ public static final String s10479 = "10479";
+ public static final String s10480 = "10480";
+ public static final String s10481 = "10481";
+ public static final String s10482 = "10482";
+ public static final String s10483 = "10483";
+ public static final String s10484 = "10484";
+ public static final String s10485 = "10485";
+ public static final String s10486 = "10486";
+ public static final String s10487 = "10487";
+ public static final String s10488 = "10488";
+ public static final String s10489 = "10489";
+ public static final String s10490 = "10490";
+ public static final String s10491 = "10491";
+ public static final String s10492 = "10492";
+ public static final String s10493 = "10493";
+ public static final String s10494 = "10494";
+ public static final String s10495 = "10495";
+ public static final String s10496 = "10496";
+ public static final String s10497 = "10497";
+ public static final String s10498 = "10498";
+ public static final String s10499 = "10499";
+ public static final String s10500 = "10500";
+ public static final String s10501 = "10501";
+ public static final String s10502 = "10502";
+ public static final String s10503 = "10503";
+ public static final String s10504 = "10504";
+ public static final String s10505 = "10505";
+ public static final String s10506 = "10506";
+ public static final String s10507 = "10507";
+ public static final String s10508 = "10508";
+ public static final String s10509 = "10509";
+ public static final String s10510 = "10510";
+ public static final String s10511 = "10511";
+ public static final String s10512 = "10512";
+ public static final String s10513 = "10513";
+ public static final String s10514 = "10514";
+ public static final String s10515 = "10515";
+ public static final String s10516 = "10516";
+ public static final String s10517 = "10517";
+ public static final String s10518 = "10518";
+ public static final String s10519 = "10519";
+ public static final String s10520 = "10520";
+ public static final String s10521 = "10521";
+ public static final String s10522 = "10522";
+ public static final String s10523 = "10523";
+ public static final String s10524 = "10524";
+ public static final String s10525 = "10525";
+ public static final String s10526 = "10526";
+ public static final String s10527 = "10527";
+ public static final String s10528 = "10528";
+ public static final String s10529 = "10529";
+ public static final String s10530 = "10530";
+ public static final String s10531 = "10531";
+ public static final String s10532 = "10532";
+ public static final String s10533 = "10533";
+ public static final String s10534 = "10534";
+ public static final String s10535 = "10535";
+ public static final String s10536 = "10536";
+ public static final String s10537 = "10537";
+ public static final String s10538 = "10538";
+ public static final String s10539 = "10539";
+ public static final String s10540 = "10540";
+ public static final String s10541 = "10541";
+ public static final String s10542 = "10542";
+ public static final String s10543 = "10543";
+ public static final String s10544 = "10544";
+ public static final String s10545 = "10545";
+ public static final String s10546 = "10546";
+ public static final String s10547 = "10547";
+ public static final String s10548 = "10548";
+ public static final String s10549 = "10549";
+ public static final String s10550 = "10550";
+ public static final String s10551 = "10551";
+ public static final String s10552 = "10552";
+ public static final String s10553 = "10553";
+ public static final String s10554 = "10554";
+ public static final String s10555 = "10555";
+ public static final String s10556 = "10556";
+ public static final String s10557 = "10557";
+ public static final String s10558 = "10558";
+ public static final String s10559 = "10559";
+ public static final String s10560 = "10560";
+ public static final String s10561 = "10561";
+ public static final String s10562 = "10562";
+ public static final String s10563 = "10563";
+ public static final String s10564 = "10564";
+ public static final String s10565 = "10565";
+ public static final String s10566 = "10566";
+ public static final String s10567 = "10567";
+ public static final String s10568 = "10568";
+ public static final String s10569 = "10569";
+ public static final String s10570 = "10570";
+ public static final String s10571 = "10571";
+ public static final String s10572 = "10572";
+ public static final String s10573 = "10573";
+ public static final String s10574 = "10574";
+ public static final String s10575 = "10575";
+ public static final String s10576 = "10576";
+ public static final String s10577 = "10577";
+ public static final String s10578 = "10578";
+ public static final String s10579 = "10579";
+ public static final String s10580 = "10580";
+ public static final String s10581 = "10581";
+ public static final String s10582 = "10582";
+ public static final String s10583 = "10583";
+ public static final String s10584 = "10584";
+ public static final String s10585 = "10585";
+ public static final String s10586 = "10586";
+ public static final String s10587 = "10587";
+ public static final String s10588 = "10588";
+ public static final String s10589 = "10589";
+ public static final String s10590 = "10590";
+ public static final String s10591 = "10591";
+ public static final String s10592 = "10592";
+ public static final String s10593 = "10593";
+ public static final String s10594 = "10594";
+ public static final String s10595 = "10595";
+ public static final String s10596 = "10596";
+ public static final String s10597 = "10597";
+ public static final String s10598 = "10598";
+ public static final String s10599 = "10599";
+ public static final String s10600 = "10600";
+ public static final String s10601 = "10601";
+ public static final String s10602 = "10602";
+ public static final String s10603 = "10603";
+ public static final String s10604 = "10604";
+ public static final String s10605 = "10605";
+ public static final String s10606 = "10606";
+ public static final String s10607 = "10607";
+ public static final String s10608 = "10608";
+ public static final String s10609 = "10609";
+ public static final String s10610 = "10610";
+ public static final String s10611 = "10611";
+ public static final String s10612 = "10612";
+ public static final String s10613 = "10613";
+ public static final String s10614 = "10614";
+ public static final String s10615 = "10615";
+ public static final String s10616 = "10616";
+ public static final String s10617 = "10617";
+ public static final String s10618 = "10618";
+ public static final String s10619 = "10619";
+ public static final String s10620 = "10620";
+ public static final String s10621 = "10621";
+ public static final String s10622 = "10622";
+ public static final String s10623 = "10623";
+ public static final String s10624 = "10624";
+ public static final String s10625 = "10625";
+ public static final String s10626 = "10626";
+ public static final String s10627 = "10627";
+ public static final String s10628 = "10628";
+ public static final String s10629 = "10629";
+ public static final String s10630 = "10630";
+ public static final String s10631 = "10631";
+ public static final String s10632 = "10632";
+ public static final String s10633 = "10633";
+ public static final String s10634 = "10634";
+ public static final String s10635 = "10635";
+ public static final String s10636 = "10636";
+ public static final String s10637 = "10637";
+ public static final String s10638 = "10638";
+ public static final String s10639 = "10639";
+ public static final String s10640 = "10640";
+ public static final String s10641 = "10641";
+ public static final String s10642 = "10642";
+ public static final String s10643 = "10643";
+ public static final String s10644 = "10644";
+ public static final String s10645 = "10645";
+ public static final String s10646 = "10646";
+ public static final String s10647 = "10647";
+ public static final String s10648 = "10648";
+ public static final String s10649 = "10649";
+ public static final String s10650 = "10650";
+ public static final String s10651 = "10651";
+ public static final String s10652 = "10652";
+ public static final String s10653 = "10653";
+ public static final String s10654 = "10654";
+ public static final String s10655 = "10655";
+ public static final String s10656 = "10656";
+ public static final String s10657 = "10657";
+ public static final String s10658 = "10658";
+ public static final String s10659 = "10659";
+ public static final String s10660 = "10660";
+ public static final String s10661 = "10661";
+ public static final String s10662 = "10662";
+ public static final String s10663 = "10663";
+ public static final String s10664 = "10664";
+ public static final String s10665 = "10665";
+ public static final String s10666 = "10666";
+ public static final String s10667 = "10667";
+ public static final String s10668 = "10668";
+ public static final String s10669 = "10669";
+ public static final String s10670 = "10670";
+ public static final String s10671 = "10671";
+ public static final String s10672 = "10672";
+ public static final String s10673 = "10673";
+ public static final String s10674 = "10674";
+ public static final String s10675 = "10675";
+ public static final String s10676 = "10676";
+ public static final String s10677 = "10677";
+ public static final String s10678 = "10678";
+ public static final String s10679 = "10679";
+ public static final String s10680 = "10680";
+ public static final String s10681 = "10681";
+ public static final String s10682 = "10682";
+ public static final String s10683 = "10683";
+ public static final String s10684 = "10684";
+ public static final String s10685 = "10685";
+ public static final String s10686 = "10686";
+ public static final String s10687 = "10687";
+ public static final String s10688 = "10688";
+ public static final String s10689 = "10689";
+ public static final String s10690 = "10690";
+ public static final String s10691 = "10691";
+ public static final String s10692 = "10692";
+ public static final String s10693 = "10693";
+ public static final String s10694 = "10694";
+ public static final String s10695 = "10695";
+ public static final String s10696 = "10696";
+ public static final String s10697 = "10697";
+ public static final String s10698 = "10698";
+ public static final String s10699 = "10699";
+ public static final String s10700 = "10700";
+ public static final String s10701 = "10701";
+ public static final String s10702 = "10702";
+ public static final String s10703 = "10703";
+ public static final String s10704 = "10704";
+ public static final String s10705 = "10705";
+ public static final String s10706 = "10706";
+ public static final String s10707 = "10707";
+ public static final String s10708 = "10708";
+ public static final String s10709 = "10709";
+ public static final String s10710 = "10710";
+ public static final String s10711 = "10711";
+ public static final String s10712 = "10712";
+ public static final String s10713 = "10713";
+ public static final String s10714 = "10714";
+ public static final String s10715 = "10715";
+ public static final String s10716 = "10716";
+ public static final String s10717 = "10717";
+ public static final String s10718 = "10718";
+ public static final String s10719 = "10719";
+ public static final String s10720 = "10720";
+ public static final String s10721 = "10721";
+ public static final String s10722 = "10722";
+ public static final String s10723 = "10723";
+ public static final String s10724 = "10724";
+ public static final String s10725 = "10725";
+ public static final String s10726 = "10726";
+ public static final String s10727 = "10727";
+ public static final String s10728 = "10728";
+ public static final String s10729 = "10729";
+ public static final String s10730 = "10730";
+ public static final String s10731 = "10731";
+ public static final String s10732 = "10732";
+ public static final String s10733 = "10733";
+ public static final String s10734 = "10734";
+ public static final String s10735 = "10735";
+ public static final String s10736 = "10736";
+ public static final String s10737 = "10737";
+ public static final String s10738 = "10738";
+ public static final String s10739 = "10739";
+ public static final String s10740 = "10740";
+ public static final String s10741 = "10741";
+ public static final String s10742 = "10742";
+ public static final String s10743 = "10743";
+ public static final String s10744 = "10744";
+ public static final String s10745 = "10745";
+ public static final String s10746 = "10746";
+ public static final String s10747 = "10747";
+ public static final String s10748 = "10748";
+ public static final String s10749 = "10749";
+ public static final String s10750 = "10750";
+ public static final String s10751 = "10751";
+ public static final String s10752 = "10752";
+ public static final String s10753 = "10753";
+ public static final String s10754 = "10754";
+ public static final String s10755 = "10755";
+ public static final String s10756 = "10756";
+ public static final String s10757 = "10757";
+ public static final String s10758 = "10758";
+ public static final String s10759 = "10759";
+ public static final String s10760 = "10760";
+ public static final String s10761 = "10761";
+ public static final String s10762 = "10762";
+ public static final String s10763 = "10763";
+ public static final String s10764 = "10764";
+ public static final String s10765 = "10765";
+ public static final String s10766 = "10766";
+ public static final String s10767 = "10767";
+ public static final String s10768 = "10768";
+ public static final String s10769 = "10769";
+ public static final String s10770 = "10770";
+ public static final String s10771 = "10771";
+ public static final String s10772 = "10772";
+ public static final String s10773 = "10773";
+ public static final String s10774 = "10774";
+ public static final String s10775 = "10775";
+ public static final String s10776 = "10776";
+ public static final String s10777 = "10777";
+ public static final String s10778 = "10778";
+ public static final String s10779 = "10779";
+ public static final String s10780 = "10780";
+ public static final String s10781 = "10781";
+ public static final String s10782 = "10782";
+ public static final String s10783 = "10783";
+ public static final String s10784 = "10784";
+ public static final String s10785 = "10785";
+ public static final String s10786 = "10786";
+ public static final String s10787 = "10787";
+ public static final String s10788 = "10788";
+ public static final String s10789 = "10789";
+ public static final String s10790 = "10790";
+ public static final String s10791 = "10791";
+ public static final String s10792 = "10792";
+ public static final String s10793 = "10793";
+ public static final String s10794 = "10794";
+ public static final String s10795 = "10795";
+ public static final String s10796 = "10796";
+ public static final String s10797 = "10797";
+ public static final String s10798 = "10798";
+ public static final String s10799 = "10799";
+ public static final String s10800 = "10800";
+ public static final String s10801 = "10801";
+ public static final String s10802 = "10802";
+ public static final String s10803 = "10803";
+ public static final String s10804 = "10804";
+ public static final String s10805 = "10805";
+ public static final String s10806 = "10806";
+ public static final String s10807 = "10807";
+ public static final String s10808 = "10808";
+ public static final String s10809 = "10809";
+ public static final String s10810 = "10810";
+ public static final String s10811 = "10811";
+ public static final String s10812 = "10812";
+ public static final String s10813 = "10813";
+ public static final String s10814 = "10814";
+ public static final String s10815 = "10815";
+ public static final String s10816 = "10816";
+ public static final String s10817 = "10817";
+ public static final String s10818 = "10818";
+ public static final String s10819 = "10819";
+ public static final String s10820 = "10820";
+ public static final String s10821 = "10821";
+ public static final String s10822 = "10822";
+ public static final String s10823 = "10823";
+ public static final String s10824 = "10824";
+ public static final String s10825 = "10825";
+ public static final String s10826 = "10826";
+ public static final String s10827 = "10827";
+ public static final String s10828 = "10828";
+ public static final String s10829 = "10829";
+ public static final String s10830 = "10830";
+ public static final String s10831 = "10831";
+ public static final String s10832 = "10832";
+ public static final String s10833 = "10833";
+ public static final String s10834 = "10834";
+ public static final String s10835 = "10835";
+ public static final String s10836 = "10836";
+ public static final String s10837 = "10837";
+ public static final String s10838 = "10838";
+ public static final String s10839 = "10839";
+ public static final String s10840 = "10840";
+ public static final String s10841 = "10841";
+ public static final String s10842 = "10842";
+ public static final String s10843 = "10843";
+ public static final String s10844 = "10844";
+ public static final String s10845 = "10845";
+ public static final String s10846 = "10846";
+ public static final String s10847 = "10847";
+ public static final String s10848 = "10848";
+ public static final String s10849 = "10849";
+ public static final String s10850 = "10850";
+ public static final String s10851 = "10851";
+ public static final String s10852 = "10852";
+ public static final String s10853 = "10853";
+ public static final String s10854 = "10854";
+ public static final String s10855 = "10855";
+ public static final String s10856 = "10856";
+ public static final String s10857 = "10857";
+ public static final String s10858 = "10858";
+ public static final String s10859 = "10859";
+ public static final String s10860 = "10860";
+ public static final String s10861 = "10861";
+ public static final String s10862 = "10862";
+ public static final String s10863 = "10863";
+ public static final String s10864 = "10864";
+ public static final String s10865 = "10865";
+ public static final String s10866 = "10866";
+ public static final String s10867 = "10867";
+ public static final String s10868 = "10868";
+ public static final String s10869 = "10869";
+ public static final String s10870 = "10870";
+ public static final String s10871 = "10871";
+ public static final String s10872 = "10872";
+ public static final String s10873 = "10873";
+ public static final String s10874 = "10874";
+ public static final String s10875 = "10875";
+ public static final String s10876 = "10876";
+ public static final String s10877 = "10877";
+ public static final String s10878 = "10878";
+ public static final String s10879 = "10879";
+ public static final String s10880 = "10880";
+ public static final String s10881 = "10881";
+ public static final String s10882 = "10882";
+ public static final String s10883 = "10883";
+ public static final String s10884 = "10884";
+ public static final String s10885 = "10885";
+ public static final String s10886 = "10886";
+ public static final String s10887 = "10887";
+ public static final String s10888 = "10888";
+ public static final String s10889 = "10889";
+ public static final String s10890 = "10890";
+ public static final String s10891 = "10891";
+ public static final String s10892 = "10892";
+ public static final String s10893 = "10893";
+ public static final String s10894 = "10894";
+ public static final String s10895 = "10895";
+ public static final String s10896 = "10896";
+ public static final String s10897 = "10897";
+ public static final String s10898 = "10898";
+ public static final String s10899 = "10899";
+ public static final String s10900 = "10900";
+ public static final String s10901 = "10901";
+ public static final String s10902 = "10902";
+ public static final String s10903 = "10903";
+ public static final String s10904 = "10904";
+ public static final String s10905 = "10905";
+ public static final String s10906 = "10906";
+ public static final String s10907 = "10907";
+ public static final String s10908 = "10908";
+ public static final String s10909 = "10909";
+ public static final String s10910 = "10910";
+ public static final String s10911 = "10911";
+ public static final String s10912 = "10912";
+ public static final String s10913 = "10913";
+ public static final String s10914 = "10914";
+ public static final String s10915 = "10915";
+ public static final String s10916 = "10916";
+ public static final String s10917 = "10917";
+ public static final String s10918 = "10918";
+ public static final String s10919 = "10919";
+ public static final String s10920 = "10920";
+ public static final String s10921 = "10921";
+ public static final String s10922 = "10922";
+ public static final String s10923 = "10923";
+ public static final String s10924 = "10924";
+ public static final String s10925 = "10925";
+ public static final String s10926 = "10926";
+ public static final String s10927 = "10927";
+ public static final String s10928 = "10928";
+ public static final String s10929 = "10929";
+ public static final String s10930 = "10930";
+ public static final String s10931 = "10931";
+ public static final String s10932 = "10932";
+ public static final String s10933 = "10933";
+ public static final String s10934 = "10934";
+ public static final String s10935 = "10935";
+ public static final String s10936 = "10936";
+ public static final String s10937 = "10937";
+ public static final String s10938 = "10938";
+ public static final String s10939 = "10939";
+ public static final String s10940 = "10940";
+ public static final String s10941 = "10941";
+ public static final String s10942 = "10942";
+ public static final String s10943 = "10943";
+ public static final String s10944 = "10944";
+ public static final String s10945 = "10945";
+ public static final String s10946 = "10946";
+ public static final String s10947 = "10947";
+ public static final String s10948 = "10948";
+ public static final String s10949 = "10949";
+ public static final String s10950 = "10950";
+ public static final String s10951 = "10951";
+ public static final String s10952 = "10952";
+ public static final String s10953 = "10953";
+ public static final String s10954 = "10954";
+ public static final String s10955 = "10955";
+ public static final String s10956 = "10956";
+ public static final String s10957 = "10957";
+ public static final String s10958 = "10958";
+ public static final String s10959 = "10959";
+ public static final String s10960 = "10960";
+ public static final String s10961 = "10961";
+ public static final String s10962 = "10962";
+ public static final String s10963 = "10963";
+ public static final String s10964 = "10964";
+ public static final String s10965 = "10965";
+ public static final String s10966 = "10966";
+ public static final String s10967 = "10967";
+ public static final String s10968 = "10968";
+ public static final String s10969 = "10969";
+ public static final String s10970 = "10970";
+ public static final String s10971 = "10971";
+ public static final String s10972 = "10972";
+ public static final String s10973 = "10973";
+ public static final String s10974 = "10974";
+ public static final String s10975 = "10975";
+ public static final String s10976 = "10976";
+ public static final String s10977 = "10977";
+ public static final String s10978 = "10978";
+ public static final String s10979 = "10979";
+ public static final String s10980 = "10980";
+ public static final String s10981 = "10981";
+ public static final String s10982 = "10982";
+ public static final String s10983 = "10983";
+ public static final String s10984 = "10984";
+ public static final String s10985 = "10985";
+ public static final String s10986 = "10986";
+ public static final String s10987 = "10987";
+ public static final String s10988 = "10988";
+ public static final String s10989 = "10989";
+ public static final String s10990 = "10990";
+ public static final String s10991 = "10991";
+ public static final String s10992 = "10992";
+ public static final String s10993 = "10993";
+ public static final String s10994 = "10994";
+ public static final String s10995 = "10995";
+ public static final String s10996 = "10996";
+ public static final String s10997 = "10997";
+ public static final String s10998 = "10998";
+ public static final String s10999 = "10999";
+ public static final String s11000 = "11000";
+ public static final String s11001 = "11001";
+ public static final String s11002 = "11002";
+ public static final String s11003 = "11003";
+ public static final String s11004 = "11004";
+ public static final String s11005 = "11005";
+ public static final String s11006 = "11006";
+ public static final String s11007 = "11007";
+ public static final String s11008 = "11008";
+ public static final String s11009 = "11009";
+ public static final String s11010 = "11010";
+ public static final String s11011 = "11011";
+ public static final String s11012 = "11012";
+ public static final String s11013 = "11013";
+ public static final String s11014 = "11014";
+ public static final String s11015 = "11015";
+ public static final String s11016 = "11016";
+ public static final String s11017 = "11017";
+ public static final String s11018 = "11018";
+ public static final String s11019 = "11019";
+ public static final String s11020 = "11020";
+ public static final String s11021 = "11021";
+ public static final String s11022 = "11022";
+ public static final String s11023 = "11023";
+ public static final String s11024 = "11024";
+ public static final String s11025 = "11025";
+ public static final String s11026 = "11026";
+ public static final String s11027 = "11027";
+ public static final String s11028 = "11028";
+ public static final String s11029 = "11029";
+ public static final String s11030 = "11030";
+ public static final String s11031 = "11031";
+ public static final String s11032 = "11032";
+ public static final String s11033 = "11033";
+ public static final String s11034 = "11034";
+ public static final String s11035 = "11035";
+ public static final String s11036 = "11036";
+ public static final String s11037 = "11037";
+ public static final String s11038 = "11038";
+ public static final String s11039 = "11039";
+ public static final String s11040 = "11040";
+ public static final String s11041 = "11041";
+ public static final String s11042 = "11042";
+ public static final String s11043 = "11043";
+ public static final String s11044 = "11044";
+ public static final String s11045 = "11045";
+ public static final String s11046 = "11046";
+ public static final String s11047 = "11047";
+ public static final String s11048 = "11048";
+ public static final String s11049 = "11049";
+ public static final String s11050 = "11050";
+ public static final String s11051 = "11051";
+ public static final String s11052 = "11052";
+ public static final String s11053 = "11053";
+ public static final String s11054 = "11054";
+ public static final String s11055 = "11055";
+ public static final String s11056 = "11056";
+ public static final String s11057 = "11057";
+ public static final String s11058 = "11058";
+ public static final String s11059 = "11059";
+ public static final String s11060 = "11060";
+ public static final String s11061 = "11061";
+ public static final String s11062 = "11062";
+ public static final String s11063 = "11063";
+ public static final String s11064 = "11064";
+ public static final String s11065 = "11065";
+ public static final String s11066 = "11066";
+ public static final String s11067 = "11067";
+ public static final String s11068 = "11068";
+ public static final String s11069 = "11069";
+ public static final String s11070 = "11070";
+ public static final String s11071 = "11071";
+ public static final String s11072 = "11072";
+ public static final String s11073 = "11073";
+ public static final String s11074 = "11074";
+ public static final String s11075 = "11075";
+ public static final String s11076 = "11076";
+ public static final String s11077 = "11077";
+ public static final String s11078 = "11078";
+ public static final String s11079 = "11079";
+ public static final String s11080 = "11080";
+ public static final String s11081 = "11081";
+ public static final String s11082 = "11082";
+ public static final String s11083 = "11083";
+ public static final String s11084 = "11084";
+ public static final String s11085 = "11085";
+ public static final String s11086 = "11086";
+ public static final String s11087 = "11087";
+ public static final String s11088 = "11088";
+ public static final String s11089 = "11089";
+ public static final String s11090 = "11090";
+ public static final String s11091 = "11091";
+ public static final String s11092 = "11092";
+ public static final String s11093 = "11093";
+ public static final String s11094 = "11094";
+ public static final String s11095 = "11095";
+ public static final String s11096 = "11096";
+ public static final String s11097 = "11097";
+ public static final String s11098 = "11098";
+ public static final String s11099 = "11099";
+ public static final String s11100 = "11100";
+ public static final String s11101 = "11101";
+ public static final String s11102 = "11102";
+ public static final String s11103 = "11103";
+ public static final String s11104 = "11104";
+ public static final String s11105 = "11105";
+ public static final String s11106 = "11106";
+ public static final String s11107 = "11107";
+ public static final String s11108 = "11108";
+ public static final String s11109 = "11109";
+ public static final String s11110 = "11110";
+ public static final String s11111 = "11111";
+ public static final String s11112 = "11112";
+ public static final String s11113 = "11113";
+ public static final String s11114 = "11114";
+ public static final String s11115 = "11115";
+ public static final String s11116 = "11116";
+ public static final String s11117 = "11117";
+ public static final String s11118 = "11118";
+ public static final String s11119 = "11119";
+ public static final String s11120 = "11120";
+ public static final String s11121 = "11121";
+ public static final String s11122 = "11122";
+ public static final String s11123 = "11123";
+ public static final String s11124 = "11124";
+ public static final String s11125 = "11125";
+ public static final String s11126 = "11126";
+ public static final String s11127 = "11127";
+ public static final String s11128 = "11128";
+ public static final String s11129 = "11129";
+ public static final String s11130 = "11130";
+ public static final String s11131 = "11131";
+ public static final String s11132 = "11132";
+ public static final String s11133 = "11133";
+ public static final String s11134 = "11134";
+ public static final String s11135 = "11135";
+ public static final String s11136 = "11136";
+ public static final String s11137 = "11137";
+ public static final String s11138 = "11138";
+ public static final String s11139 = "11139";
+ public static final String s11140 = "11140";
+ public static final String s11141 = "11141";
+ public static final String s11142 = "11142";
+ public static final String s11143 = "11143";
+ public static final String s11144 = "11144";
+ public static final String s11145 = "11145";
+ public static final String s11146 = "11146";
+ public static final String s11147 = "11147";
+ public static final String s11148 = "11148";
+ public static final String s11149 = "11149";
+ public static final String s11150 = "11150";
+ public static final String s11151 = "11151";
+ public static final String s11152 = "11152";
+ public static final String s11153 = "11153";
+ public static final String s11154 = "11154";
+ public static final String s11155 = "11155";
+ public static final String s11156 = "11156";
+ public static final String s11157 = "11157";
+ public static final String s11158 = "11158";
+ public static final String s11159 = "11159";
+ public static final String s11160 = "11160";
+ public static final String s11161 = "11161";
+ public static final String s11162 = "11162";
+ public static final String s11163 = "11163";
+ public static final String s11164 = "11164";
+ public static final String s11165 = "11165";
+ public static final String s11166 = "11166";
+ public static final String s11167 = "11167";
+ public static final String s11168 = "11168";
+ public static final String s11169 = "11169";
+ public static final String s11170 = "11170";
+ public static final String s11171 = "11171";
+ public static final String s11172 = "11172";
+ public static final String s11173 = "11173";
+ public static final String s11174 = "11174";
+ public static final String s11175 = "11175";
+ public static final String s11176 = "11176";
+ public static final String s11177 = "11177";
+ public static final String s11178 = "11178";
+ public static final String s11179 = "11179";
+ public static final String s11180 = "11180";
+ public static final String s11181 = "11181";
+ public static final String s11182 = "11182";
+ public static final String s11183 = "11183";
+ public static final String s11184 = "11184";
+ public static final String s11185 = "11185";
+ public static final String s11186 = "11186";
+ public static final String s11187 = "11187";
+ public static final String s11188 = "11188";
+ public static final String s11189 = "11189";
+ public static final String s11190 = "11190";
+ public static final String s11191 = "11191";
+ public static final String s11192 = "11192";
+ public static final String s11193 = "11193";
+ public static final String s11194 = "11194";
+ public static final String s11195 = "11195";
+ public static final String s11196 = "11196";
+ public static final String s11197 = "11197";
+ public static final String s11198 = "11198";
+ public static final String s11199 = "11199";
+ public static final String s11200 = "11200";
+ public static final String s11201 = "11201";
+ public static final String s11202 = "11202";
+ public static final String s11203 = "11203";
+ public static final String s11204 = "11204";
+ public static final String s11205 = "11205";
+ public static final String s11206 = "11206";
+ public static final String s11207 = "11207";
+ public static final String s11208 = "11208";
+ public static final String s11209 = "11209";
+ public static final String s11210 = "11210";
+ public static final String s11211 = "11211";
+ public static final String s11212 = "11212";
+ public static final String s11213 = "11213";
+ public static final String s11214 = "11214";
+ public static final String s11215 = "11215";
+ public static final String s11216 = "11216";
+ public static final String s11217 = "11217";
+ public static final String s11218 = "11218";
+ public static final String s11219 = "11219";
+ public static final String s11220 = "11220";
+ public static final String s11221 = "11221";
+ public static final String s11222 = "11222";
+ public static final String s11223 = "11223";
+ public static final String s11224 = "11224";
+ public static final String s11225 = "11225";
+ public static final String s11226 = "11226";
+ public static final String s11227 = "11227";
+ public static final String s11228 = "11228";
+ public static final String s11229 = "11229";
+ public static final String s11230 = "11230";
+ public static final String s11231 = "11231";
+ public static final String s11232 = "11232";
+ public static final String s11233 = "11233";
+ public static final String s11234 = "11234";
+ public static final String s11235 = "11235";
+ public static final String s11236 = "11236";
+ public static final String s11237 = "11237";
+ public static final String s11238 = "11238";
+ public static final String s11239 = "11239";
+ public static final String s11240 = "11240";
+ public static final String s11241 = "11241";
+ public static final String s11242 = "11242";
+ public static final String s11243 = "11243";
+ public static final String s11244 = "11244";
+ public static final String s11245 = "11245";
+ public static final String s11246 = "11246";
+ public static final String s11247 = "11247";
+ public static final String s11248 = "11248";
+ public static final String s11249 = "11249";
+ public static final String s11250 = "11250";
+ public static final String s11251 = "11251";
+ public static final String s11252 = "11252";
+ public static final String s11253 = "11253";
+ public static final String s11254 = "11254";
+ public static final String s11255 = "11255";
+ public static final String s11256 = "11256";
+ public static final String s11257 = "11257";
+ public static final String s11258 = "11258";
+ public static final String s11259 = "11259";
+ public static final String s11260 = "11260";
+ public static final String s11261 = "11261";
+ public static final String s11262 = "11262";
+ public static final String s11263 = "11263";
+ public static final String s11264 = "11264";
+ public static final String s11265 = "11265";
+ public static final String s11266 = "11266";
+ public static final String s11267 = "11267";
+ public static final String s11268 = "11268";
+ public static final String s11269 = "11269";
+ public static final String s11270 = "11270";
+ public static final String s11271 = "11271";
+ public static final String s11272 = "11272";
+ public static final String s11273 = "11273";
+ public static final String s11274 = "11274";
+ public static final String s11275 = "11275";
+ public static final String s11276 = "11276";
+ public static final String s11277 = "11277";
+ public static final String s11278 = "11278";
+ public static final String s11279 = "11279";
+ public static final String s11280 = "11280";
+ public static final String s11281 = "11281";
+ public static final String s11282 = "11282";
+ public static final String s11283 = "11283";
+ public static final String s11284 = "11284";
+ public static final String s11285 = "11285";
+ public static final String s11286 = "11286";
+ public static final String s11287 = "11287";
+ public static final String s11288 = "11288";
+ public static final String s11289 = "11289";
+ public static final String s11290 = "11290";
+ public static final String s11291 = "11291";
+ public static final String s11292 = "11292";
+ public static final String s11293 = "11293";
+ public static final String s11294 = "11294";
+ public static final String s11295 = "11295";
+ public static final String s11296 = "11296";
+ public static final String s11297 = "11297";
+ public static final String s11298 = "11298";
+ public static final String s11299 = "11299";
+ public static final String s11300 = "11300";
+ public static final String s11301 = "11301";
+ public static final String s11302 = "11302";
+ public static final String s11303 = "11303";
+ public static final String s11304 = "11304";
+ public static final String s11305 = "11305";
+ public static final String s11306 = "11306";
+ public static final String s11307 = "11307";
+ public static final String s11308 = "11308";
+ public static final String s11309 = "11309";
+ public static final String s11310 = "11310";
+ public static final String s11311 = "11311";
+ public static final String s11312 = "11312";
+ public static final String s11313 = "11313";
+ public static final String s11314 = "11314";
+ public static final String s11315 = "11315";
+ public static final String s11316 = "11316";
+ public static final String s11317 = "11317";
+ public static final String s11318 = "11318";
+ public static final String s11319 = "11319";
+ public static final String s11320 = "11320";
+ public static final String s11321 = "11321";
+ public static final String s11322 = "11322";
+ public static final String s11323 = "11323";
+ public static final String s11324 = "11324";
+ public static final String s11325 = "11325";
+ public static final String s11326 = "11326";
+ public static final String s11327 = "11327";
+ public static final String s11328 = "11328";
+ public static final String s11329 = "11329";
+ public static final String s11330 = "11330";
+ public static final String s11331 = "11331";
+ public static final String s11332 = "11332";
+ public static final String s11333 = "11333";
+ public static final String s11334 = "11334";
+ public static final String s11335 = "11335";
+ public static final String s11336 = "11336";
+ public static final String s11337 = "11337";
+ public static final String s11338 = "11338";
+ public static final String s11339 = "11339";
+ public static final String s11340 = "11340";
+ public static final String s11341 = "11341";
+ public static final String s11342 = "11342";
+ public static final String s11343 = "11343";
+ public static final String s11344 = "11344";
+ public static final String s11345 = "11345";
+ public static final String s11346 = "11346";
+ public static final String s11347 = "11347";
+ public static final String s11348 = "11348";
+ public static final String s11349 = "11349";
+ public static final String s11350 = "11350";
+ public static final String s11351 = "11351";
+ public static final String s11352 = "11352";
+ public static final String s11353 = "11353";
+ public static final String s11354 = "11354";
+ public static final String s11355 = "11355";
+ public static final String s11356 = "11356";
+ public static final String s11357 = "11357";
+ public static final String s11358 = "11358";
+ public static final String s11359 = "11359";
+ public static final String s11360 = "11360";
+ public static final String s11361 = "11361";
+ public static final String s11362 = "11362";
+ public static final String s11363 = "11363";
+ public static final String s11364 = "11364";
+ public static final String s11365 = "11365";
+ public static final String s11366 = "11366";
+ public static final String s11367 = "11367";
+ public static final String s11368 = "11368";
+ public static final String s11369 = "11369";
+ public static final String s11370 = "11370";
+ public static final String s11371 = "11371";
+ public static final String s11372 = "11372";
+ public static final String s11373 = "11373";
+ public static final String s11374 = "11374";
+ public static final String s11375 = "11375";
+ public static final String s11376 = "11376";
+ public static final String s11377 = "11377";
+ public static final String s11378 = "11378";
+ public static final String s11379 = "11379";
+ public static final String s11380 = "11380";
+ public static final String s11381 = "11381";
+ public static final String s11382 = "11382";
+ public static final String s11383 = "11383";
+ public static final String s11384 = "11384";
+ public static final String s11385 = "11385";
+ public static final String s11386 = "11386";
+ public static final String s11387 = "11387";
+ public static final String s11388 = "11388";
+ public static final String s11389 = "11389";
+ public static final String s11390 = "11390";
+ public static final String s11391 = "11391";
+ public static final String s11392 = "11392";
+ public static final String s11393 = "11393";
+ public static final String s11394 = "11394";
+ public static final String s11395 = "11395";
+ public static final String s11396 = "11396";
+ public static final String s11397 = "11397";
+ public static final String s11398 = "11398";
+ public static final String s11399 = "11399";
+ public static final String s11400 = "11400";
+ public static final String s11401 = "11401";
+ public static final String s11402 = "11402";
+ public static final String s11403 = "11403";
+ public static final String s11404 = "11404";
+ public static final String s11405 = "11405";
+ public static final String s11406 = "11406";
+ public static final String s11407 = "11407";
+ public static final String s11408 = "11408";
+ public static final String s11409 = "11409";
+ public static final String s11410 = "11410";
+ public static final String s11411 = "11411";
+ public static final String s11412 = "11412";
+ public static final String s11413 = "11413";
+ public static final String s11414 = "11414";
+ public static final String s11415 = "11415";
+ public static final String s11416 = "11416";
+ public static final String s11417 = "11417";
+ public static final String s11418 = "11418";
+ public static final String s11419 = "11419";
+ public static final String s11420 = "11420";
+ public static final String s11421 = "11421";
+ public static final String s11422 = "11422";
+ public static final String s11423 = "11423";
+ public static final String s11424 = "11424";
+ public static final String s11425 = "11425";
+ public static final String s11426 = "11426";
+ public static final String s11427 = "11427";
+ public static final String s11428 = "11428";
+ public static final String s11429 = "11429";
+ public static final String s11430 = "11430";
+ public static final String s11431 = "11431";
+ public static final String s11432 = "11432";
+ public static final String s11433 = "11433";
+ public static final String s11434 = "11434";
+ public static final String s11435 = "11435";
+ public static final String s11436 = "11436";
+ public static final String s11437 = "11437";
+ public static final String s11438 = "11438";
+ public static final String s11439 = "11439";
+ public static final String s11440 = "11440";
+ public static final String s11441 = "11441";
+ public static final String s11442 = "11442";
+ public static final String s11443 = "11443";
+ public static final String s11444 = "11444";
+ public static final String s11445 = "11445";
+ public static final String s11446 = "11446";
+ public static final String s11447 = "11447";
+ public static final String s11448 = "11448";
+ public static final String s11449 = "11449";
+ public static final String s11450 = "11450";
+ public static final String s11451 = "11451";
+ public static final String s11452 = "11452";
+ public static final String s11453 = "11453";
+ public static final String s11454 = "11454";
+ public static final String s11455 = "11455";
+ public static final String s11456 = "11456";
+ public static final String s11457 = "11457";
+ public static final String s11458 = "11458";
+ public static final String s11459 = "11459";
+ public static final String s11460 = "11460";
+ public static final String s11461 = "11461";
+ public static final String s11462 = "11462";
+ public static final String s11463 = "11463";
+ public static final String s11464 = "11464";
+ public static final String s11465 = "11465";
+ public static final String s11466 = "11466";
+ public static final String s11467 = "11467";
+ public static final String s11468 = "11468";
+ public static final String s11469 = "11469";
+ public static final String s11470 = "11470";
+ public static final String s11471 = "11471";
+ public static final String s11472 = "11472";
+ public static final String s11473 = "11473";
+ public static final String s11474 = "11474";
+ public static final String s11475 = "11475";
+ public static final String s11476 = "11476";
+ public static final String s11477 = "11477";
+ public static final String s11478 = "11478";
+ public static final String s11479 = "11479";
+ public static final String s11480 = "11480";
+ public static final String s11481 = "11481";
+ public static final String s11482 = "11482";
+ public static final String s11483 = "11483";
+ public static final String s11484 = "11484";
+ public static final String s11485 = "11485";
+ public static final String s11486 = "11486";
+ public static final String s11487 = "11487";
+ public static final String s11488 = "11488";
+ public static final String s11489 = "11489";
+ public static final String s11490 = "11490";
+ public static final String s11491 = "11491";
+ public static final String s11492 = "11492";
+ public static final String s11493 = "11493";
+ public static final String s11494 = "11494";
+ public static final String s11495 = "11495";
+ public static final String s11496 = "11496";
+ public static final String s11497 = "11497";
+ public static final String s11498 = "11498";
+ public static final String s11499 = "11499";
+ public static final String s11500 = "11500";
+ public static final String s11501 = "11501";
+ public static final String s11502 = "11502";
+ public static final String s11503 = "11503";
+ public static final String s11504 = "11504";
+ public static final String s11505 = "11505";
+ public static final String s11506 = "11506";
+ public static final String s11507 = "11507";
+ public static final String s11508 = "11508";
+ public static final String s11509 = "11509";
+ public static final String s11510 = "11510";
+ public static final String s11511 = "11511";
+ public static final String s11512 = "11512";
+ public static final String s11513 = "11513";
+ public static final String s11514 = "11514";
+ public static final String s11515 = "11515";
+ public static final String s11516 = "11516";
+ public static final String s11517 = "11517";
+ public static final String s11518 = "11518";
+ public static final String s11519 = "11519";
+ public static final String s11520 = "11520";
+ public static final String s11521 = "11521";
+ public static final String s11522 = "11522";
+ public static final String s11523 = "11523";
+ public static final String s11524 = "11524";
+ public static final String s11525 = "11525";
+ public static final String s11526 = "11526";
+ public static final String s11527 = "11527";
+ public static final String s11528 = "11528";
+ public static final String s11529 = "11529";
+ public static final String s11530 = "11530";
+ public static final String s11531 = "11531";
+ public static final String s11532 = "11532";
+ public static final String s11533 = "11533";
+ public static final String s11534 = "11534";
+ public static final String s11535 = "11535";
+ public static final String s11536 = "11536";
+ public static final String s11537 = "11537";
+ public static final String s11538 = "11538";
+ public static final String s11539 = "11539";
+ public static final String s11540 = "11540";
+ public static final String s11541 = "11541";
+ public static final String s11542 = "11542";
+ public static final String s11543 = "11543";
+ public static final String s11544 = "11544";
+ public static final String s11545 = "11545";
+ public static final String s11546 = "11546";
+ public static final String s11547 = "11547";
+ public static final String s11548 = "11548";
+ public static final String s11549 = "11549";
+ public static final String s11550 = "11550";
+ public static final String s11551 = "11551";
+ public static final String s11552 = "11552";
+ public static final String s11553 = "11553";
+ public static final String s11554 = "11554";
+ public static final String s11555 = "11555";
+ public static final String s11556 = "11556";
+ public static final String s11557 = "11557";
+ public static final String s11558 = "11558";
+ public static final String s11559 = "11559";
+ public static final String s11560 = "11560";
+ public static final String s11561 = "11561";
+ public static final String s11562 = "11562";
+ public static final String s11563 = "11563";
+ public static final String s11564 = "11564";
+ public static final String s11565 = "11565";
+ public static final String s11566 = "11566";
+ public static final String s11567 = "11567";
+ public static final String s11568 = "11568";
+ public static final String s11569 = "11569";
+ public static final String s11570 = "11570";
+ public static final String s11571 = "11571";
+ public static final String s11572 = "11572";
+ public static final String s11573 = "11573";
+ public static final String s11574 = "11574";
+ public static final String s11575 = "11575";
+ public static final String s11576 = "11576";
+ public static final String s11577 = "11577";
+ public static final String s11578 = "11578";
+ public static final String s11579 = "11579";
+ public static final String s11580 = "11580";
+ public static final String s11581 = "11581";
+ public static final String s11582 = "11582";
+ public static final String s11583 = "11583";
+ public static final String s11584 = "11584";
+ public static final String s11585 = "11585";
+ public static final String s11586 = "11586";
+ public static final String s11587 = "11587";
+ public static final String s11588 = "11588";
+ public static final String s11589 = "11589";
+ public static final String s11590 = "11590";
+ public static final String s11591 = "11591";
+ public static final String s11592 = "11592";
+ public static final String s11593 = "11593";
+ public static final String s11594 = "11594";
+ public static final String s11595 = "11595";
+ public static final String s11596 = "11596";
+ public static final String s11597 = "11597";
+ public static final String s11598 = "11598";
+ public static final String s11599 = "11599";
+ public static final String s11600 = "11600";
+ public static final String s11601 = "11601";
+ public static final String s11602 = "11602";
+ public static final String s11603 = "11603";
+ public static final String s11604 = "11604";
+ public static final String s11605 = "11605";
+ public static final String s11606 = "11606";
+ public static final String s11607 = "11607";
+ public static final String s11608 = "11608";
+ public static final String s11609 = "11609";
+ public static final String s11610 = "11610";
+ public static final String s11611 = "11611";
+ public static final String s11612 = "11612";
+ public static final String s11613 = "11613";
+ public static final String s11614 = "11614";
+ public static final String s11615 = "11615";
+ public static final String s11616 = "11616";
+ public static final String s11617 = "11617";
+ public static final String s11618 = "11618";
+ public static final String s11619 = "11619";
+ public static final String s11620 = "11620";
+ public static final String s11621 = "11621";
+ public static final String s11622 = "11622";
+ public static final String s11623 = "11623";
+ public static final String s11624 = "11624";
+ public static final String s11625 = "11625";
+ public static final String s11626 = "11626";
+ public static final String s11627 = "11627";
+ public static final String s11628 = "11628";
+ public static final String s11629 = "11629";
+ public static final String s11630 = "11630";
+ public static final String s11631 = "11631";
+ public static final String s11632 = "11632";
+ public static final String s11633 = "11633";
+ public static final String s11634 = "11634";
+ public static final String s11635 = "11635";
+ public static final String s11636 = "11636";
+ public static final String s11637 = "11637";
+ public static final String s11638 = "11638";
+ public static final String s11639 = "11639";
+ public static final String s11640 = "11640";
+ public static final String s11641 = "11641";
+ public static final String s11642 = "11642";
+ public static final String s11643 = "11643";
+ public static final String s11644 = "11644";
+ public static final String s11645 = "11645";
+ public static final String s11646 = "11646";
+ public static final String s11647 = "11647";
+ public static final String s11648 = "11648";
+ public static final String s11649 = "11649";
+ public static final String s11650 = "11650";
+ public static final String s11651 = "11651";
+ public static final String s11652 = "11652";
+ public static final String s11653 = "11653";
+ public static final String s11654 = "11654";
+ public static final String s11655 = "11655";
+ public static final String s11656 = "11656";
+ public static final String s11657 = "11657";
+ public static final String s11658 = "11658";
+ public static final String s11659 = "11659";
+ public static final String s11660 = "11660";
+ public static final String s11661 = "11661";
+ public static final String s11662 = "11662";
+ public static final String s11663 = "11663";
+ public static final String s11664 = "11664";
+ public static final String s11665 = "11665";
+ public static final String s11666 = "11666";
+ public static final String s11667 = "11667";
+ public static final String s11668 = "11668";
+ public static final String s11669 = "11669";
+ public static final String s11670 = "11670";
+ public static final String s11671 = "11671";
+ public static final String s11672 = "11672";
+ public static final String s11673 = "11673";
+ public static final String s11674 = "11674";
+ public static final String s11675 = "11675";
+ public static final String s11676 = "11676";
+ public static final String s11677 = "11677";
+ public static final String s11678 = "11678";
+ public static final String s11679 = "11679";
+ public static final String s11680 = "11680";
+ public static final String s11681 = "11681";
+ public static final String s11682 = "11682";
+ public static final String s11683 = "11683";
+ public static final String s11684 = "11684";
+ public static final String s11685 = "11685";
+ public static final String s11686 = "11686";
+ public static final String s11687 = "11687";
+ public static final String s11688 = "11688";
+ public static final String s11689 = "11689";
+ public static final String s11690 = "11690";
+ public static final String s11691 = "11691";
+ public static final String s11692 = "11692";
+ public static final String s11693 = "11693";
+ public static final String s11694 = "11694";
+ public static final String s11695 = "11695";
+ public static final String s11696 = "11696";
+ public static final String s11697 = "11697";
+ public static final String s11698 = "11698";
+ public static final String s11699 = "11699";
+ public static final String s11700 = "11700";
+ public static final String s11701 = "11701";
+ public static final String s11702 = "11702";
+ public static final String s11703 = "11703";
+ public static final String s11704 = "11704";
+ public static final String s11705 = "11705";
+ public static final String s11706 = "11706";
+ public static final String s11707 = "11707";
+ public static final String s11708 = "11708";
+ public static final String s11709 = "11709";
+ public static final String s11710 = "11710";
+ public static final String s11711 = "11711";
+ public static final String s11712 = "11712";
+ public static final String s11713 = "11713";
+ public static final String s11714 = "11714";
+ public static final String s11715 = "11715";
+ public static final String s11716 = "11716";
+ public static final String s11717 = "11717";
+ public static final String s11718 = "11718";
+ public static final String s11719 = "11719";
+ public static final String s11720 = "11720";
+ public static final String s11721 = "11721";
+ public static final String s11722 = "11722";
+ public static final String s11723 = "11723";
+ public static final String s11724 = "11724";
+ public static final String s11725 = "11725";
+ public static final String s11726 = "11726";
+ public static final String s11727 = "11727";
+ public static final String s11728 = "11728";
+ public static final String s11729 = "11729";
+ public static final String s11730 = "11730";
+ public static final String s11731 = "11731";
+ public static final String s11732 = "11732";
+ public static final String s11733 = "11733";
+ public static final String s11734 = "11734";
+ public static final String s11735 = "11735";
+ public static final String s11736 = "11736";
+ public static final String s11737 = "11737";
+ public static final String s11738 = "11738";
+ public static final String s11739 = "11739";
+ public static final String s11740 = "11740";
+ public static final String s11741 = "11741";
+ public static final String s11742 = "11742";
+ public static final String s11743 = "11743";
+ public static final String s11744 = "11744";
+ public static final String s11745 = "11745";
+ public static final String s11746 = "11746";
+ public static final String s11747 = "11747";
+ public static final String s11748 = "11748";
+ public static final String s11749 = "11749";
+ public static final String s11750 = "11750";
+ public static final String s11751 = "11751";
+ public static final String s11752 = "11752";
+ public static final String s11753 = "11753";
+ public static final String s11754 = "11754";
+ public static final String s11755 = "11755";
+ public static final String s11756 = "11756";
+ public static final String s11757 = "11757";
+ public static final String s11758 = "11758";
+ public static final String s11759 = "11759";
+ public static final String s11760 = "11760";
+ public static final String s11761 = "11761";
+ public static final String s11762 = "11762";
+ public static final String s11763 = "11763";
+ public static final String s11764 = "11764";
+ public static final String s11765 = "11765";
+ public static final String s11766 = "11766";
+ public static final String s11767 = "11767";
+ public static final String s11768 = "11768";
+ public static final String s11769 = "11769";
+ public static final String s11770 = "11770";
+ public static final String s11771 = "11771";
+ public static final String s11772 = "11772";
+ public static final String s11773 = "11773";
+ public static final String s11774 = "11774";
+ public static final String s11775 = "11775";
+ public static final String s11776 = "11776";
+ public static final String s11777 = "11777";
+ public static final String s11778 = "11778";
+ public static final String s11779 = "11779";
+ public static final String s11780 = "11780";
+ public static final String s11781 = "11781";
+ public static final String s11782 = "11782";
+ public static final String s11783 = "11783";
+ public static final String s11784 = "11784";
+ public static final String s11785 = "11785";
+ public static final String s11786 = "11786";
+ public static final String s11787 = "11787";
+ public static final String s11788 = "11788";
+ public static final String s11789 = "11789";
+ public static final String s11790 = "11790";
+ public static final String s11791 = "11791";
+ public static final String s11792 = "11792";
+ public static final String s11793 = "11793";
+ public static final String s11794 = "11794";
+ public static final String s11795 = "11795";
+ public static final String s11796 = "11796";
+ public static final String s11797 = "11797";
+ public static final String s11798 = "11798";
+ public static final String s11799 = "11799";
+ public static final String s11800 = "11800";
+ public static final String s11801 = "11801";
+ public static final String s11802 = "11802";
+ public static final String s11803 = "11803";
+ public static final String s11804 = "11804";
+ public static final String s11805 = "11805";
+ public static final String s11806 = "11806";
+ public static final String s11807 = "11807";
+ public static final String s11808 = "11808";
+ public static final String s11809 = "11809";
+ public static final String s11810 = "11810";
+ public static final String s11811 = "11811";
+ public static final String s11812 = "11812";
+ public static final String s11813 = "11813";
+ public static final String s11814 = "11814";
+ public static final String s11815 = "11815";
+ public static final String s11816 = "11816";
+ public static final String s11817 = "11817";
+ public static final String s11818 = "11818";
+ public static final String s11819 = "11819";
+ public static final String s11820 = "11820";
+ public static final String s11821 = "11821";
+ public static final String s11822 = "11822";
+ public static final String s11823 = "11823";
+ public static final String s11824 = "11824";
+ public static final String s11825 = "11825";
+ public static final String s11826 = "11826";
+ public static final String s11827 = "11827";
+ public static final String s11828 = "11828";
+ public static final String s11829 = "11829";
+ public static final String s11830 = "11830";
+ public static final String s11831 = "11831";
+ public static final String s11832 = "11832";
+ public static final String s11833 = "11833";
+ public static final String s11834 = "11834";
+ public static final String s11835 = "11835";
+ public static final String s11836 = "11836";
+ public static final String s11837 = "11837";
+ public static final String s11838 = "11838";
+ public static final String s11839 = "11839";
+ public static final String s11840 = "11840";
+ public static final String s11841 = "11841";
+ public static final String s11842 = "11842";
+ public static final String s11843 = "11843";
+ public static final String s11844 = "11844";
+ public static final String s11845 = "11845";
+ public static final String s11846 = "11846";
+ public static final String s11847 = "11847";
+ public static final String s11848 = "11848";
+ public static final String s11849 = "11849";
+ public static final String s11850 = "11850";
+ public static final String s11851 = "11851";
+ public static final String s11852 = "11852";
+ public static final String s11853 = "11853";
+ public static final String s11854 = "11854";
+ public static final String s11855 = "11855";
+ public static final String s11856 = "11856";
+ public static final String s11857 = "11857";
+ public static final String s11858 = "11858";
+ public static final String s11859 = "11859";
+ public static final String s11860 = "11860";
+ public static final String s11861 = "11861";
+ public static final String s11862 = "11862";
+ public static final String s11863 = "11863";
+ public static final String s11864 = "11864";
+ public static final String s11865 = "11865";
+ public static final String s11866 = "11866";
+ public static final String s11867 = "11867";
+ public static final String s11868 = "11868";
+ public static final String s11869 = "11869";
+ public static final String s11870 = "11870";
+ public static final String s11871 = "11871";
+ public static final String s11872 = "11872";
+ public static final String s11873 = "11873";
+ public static final String s11874 = "11874";
+ public static final String s11875 = "11875";
+ public static final String s11876 = "11876";
+ public static final String s11877 = "11877";
+ public static final String s11878 = "11878";
+ public static final String s11879 = "11879";
+ public static final String s11880 = "11880";
+ public static final String s11881 = "11881";
+ public static final String s11882 = "11882";
+ public static final String s11883 = "11883";
+ public static final String s11884 = "11884";
+ public static final String s11885 = "11885";
+ public static final String s11886 = "11886";
+ public static final String s11887 = "11887";
+ public static final String s11888 = "11888";
+ public static final String s11889 = "11889";
+ public static final String s11890 = "11890";
+ public static final String s11891 = "11891";
+ public static final String s11892 = "11892";
+ public static final String s11893 = "11893";
+ public static final String s11894 = "11894";
+ public static final String s11895 = "11895";
+ public static final String s11896 = "11896";
+ public static final String s11897 = "11897";
+ public static final String s11898 = "11898";
+ public static final String s11899 = "11899";
+ public static final String s11900 = "11900";
+ public static final String s11901 = "11901";
+ public static final String s11902 = "11902";
+ public static final String s11903 = "11903";
+ public static final String s11904 = "11904";
+ public static final String s11905 = "11905";
+ public static final String s11906 = "11906";
+ public static final String s11907 = "11907";
+ public static final String s11908 = "11908";
+ public static final String s11909 = "11909";
+ public static final String s11910 = "11910";
+ public static final String s11911 = "11911";
+ public static final String s11912 = "11912";
+ public static final String s11913 = "11913";
+ public static final String s11914 = "11914";
+ public static final String s11915 = "11915";
+ public static final String s11916 = "11916";
+ public static final String s11917 = "11917";
+ public static final String s11918 = "11918";
+ public static final String s11919 = "11919";
+ public static final String s11920 = "11920";
+ public static final String s11921 = "11921";
+ public static final String s11922 = "11922";
+ public static final String s11923 = "11923";
+ public static final String s11924 = "11924";
+ public static final String s11925 = "11925";
+ public static final String s11926 = "11926";
+ public static final String s11927 = "11927";
+ public static final String s11928 = "11928";
+ public static final String s11929 = "11929";
+ public static final String s11930 = "11930";
+ public static final String s11931 = "11931";
+ public static final String s11932 = "11932";
+ public static final String s11933 = "11933";
+ public static final String s11934 = "11934";
+ public static final String s11935 = "11935";
+ public static final String s11936 = "11936";
+ public static final String s11937 = "11937";
+ public static final String s11938 = "11938";
+ public static final String s11939 = "11939";
+ public static final String s11940 = "11940";
+ public static final String s11941 = "11941";
+ public static final String s11942 = "11942";
+ public static final String s11943 = "11943";
+ public static final String s11944 = "11944";
+ public static final String s11945 = "11945";
+ public static final String s11946 = "11946";
+ public static final String s11947 = "11947";
+ public static final String s11948 = "11948";
+ public static final String s11949 = "11949";
+ public static final String s11950 = "11950";
+ public static final String s11951 = "11951";
+ public static final String s11952 = "11952";
+ public static final String s11953 = "11953";
+ public static final String s11954 = "11954";
+ public static final String s11955 = "11955";
+ public static final String s11956 = "11956";
+ public static final String s11957 = "11957";
+ public static final String s11958 = "11958";
+ public static final String s11959 = "11959";
+ public static final String s11960 = "11960";
+ public static final String s11961 = "11961";
+ public static final String s11962 = "11962";
+ public static final String s11963 = "11963";
+ public static final String s11964 = "11964";
+ public static final String s11965 = "11965";
+ public static final String s11966 = "11966";
+ public static final String s11967 = "11967";
+ public static final String s11968 = "11968";
+ public static final String s11969 = "11969";
+ public static final String s11970 = "11970";
+ public static final String s11971 = "11971";
+ public static final String s11972 = "11972";
+ public static final String s11973 = "11973";
+ public static final String s11974 = "11974";
+ public static final String s11975 = "11975";
+ public static final String s11976 = "11976";
+ public static final String s11977 = "11977";
+ public static final String s11978 = "11978";
+ public static final String s11979 = "11979";
+ public static final String s11980 = "11980";
+ public static final String s11981 = "11981";
+ public static final String s11982 = "11982";
+ public static final String s11983 = "11983";
+ public static final String s11984 = "11984";
+ public static final String s11985 = "11985";
+ public static final String s11986 = "11986";
+ public static final String s11987 = "11987";
+ public static final String s11988 = "11988";
+ public static final String s11989 = "11989";
+ public static final String s11990 = "11990";
+ public static final String s11991 = "11991";
+ public static final String s11992 = "11992";
+ public static final String s11993 = "11993";
+ public static final String s11994 = "11994";
+ public static final String s11995 = "11995";
+ public static final String s11996 = "11996";
+ public static final String s11997 = "11997";
+ public static final String s11998 = "11998";
+ public static final String s11999 = "11999";
+ public static final String s12000 = "12000";
+ public static final String s12001 = "12001";
+ public static final String s12002 = "12002";
+ public static final String s12003 = "12003";
+ public static final String s12004 = "12004";
+ public static final String s12005 = "12005";
+ public static final String s12006 = "12006";
+ public static final String s12007 = "12007";
+ public static final String s12008 = "12008";
+ public static final String s12009 = "12009";
+ public static final String s12010 = "12010";
+ public static final String s12011 = "12011";
+ public static final String s12012 = "12012";
+ public static final String s12013 = "12013";
+ public static final String s12014 = "12014";
+ public static final String s12015 = "12015";
+ public static final String s12016 = "12016";
+ public static final String s12017 = "12017";
+ public static final String s12018 = "12018";
+ public static final String s12019 = "12019";
+ public static final String s12020 = "12020";
+ public static final String s12021 = "12021";
+ public static final String s12022 = "12022";
+ public static final String s12023 = "12023";
+ public static final String s12024 = "12024";
+ public static final String s12025 = "12025";
+ public static final String s12026 = "12026";
+ public static final String s12027 = "12027";
+ public static final String s12028 = "12028";
+ public static final String s12029 = "12029";
+ public static final String s12030 = "12030";
+ public static final String s12031 = "12031";
+ public static final String s12032 = "12032";
+ public static final String s12033 = "12033";
+ public static final String s12034 = "12034";
+ public static final String s12035 = "12035";
+ public static final String s12036 = "12036";
+ public static final String s12037 = "12037";
+ public static final String s12038 = "12038";
+ public static final String s12039 = "12039";
+ public static final String s12040 = "12040";
+ public static final String s12041 = "12041";
+ public static final String s12042 = "12042";
+ public static final String s12043 = "12043";
+ public static final String s12044 = "12044";
+ public static final String s12045 = "12045";
+ public static final String s12046 = "12046";
+ public static final String s12047 = "12047";
+ public static final String s12048 = "12048";
+ public static final String s12049 = "12049";
+ public static final String s12050 = "12050";
+ public static final String s12051 = "12051";
+ public static final String s12052 = "12052";
+ public static final String s12053 = "12053";
+ public static final String s12054 = "12054";
+ public static final String s12055 = "12055";
+ public static final String s12056 = "12056";
+ public static final String s12057 = "12057";
+ public static final String s12058 = "12058";
+ public static final String s12059 = "12059";
+ public static final String s12060 = "12060";
+ public static final String s12061 = "12061";
+ public static final String s12062 = "12062";
+ public static final String s12063 = "12063";
+ public static final String s12064 = "12064";
+ public static final String s12065 = "12065";
+ public static final String s12066 = "12066";
+ public static final String s12067 = "12067";
+ public static final String s12068 = "12068";
+ public static final String s12069 = "12069";
+ public static final String s12070 = "12070";
+ public static final String s12071 = "12071";
+ public static final String s12072 = "12072";
+ public static final String s12073 = "12073";
+ public static final String s12074 = "12074";
+ public static final String s12075 = "12075";
+ public static final String s12076 = "12076";
+ public static final String s12077 = "12077";
+ public static final String s12078 = "12078";
+ public static final String s12079 = "12079";
+ public static final String s12080 = "12080";
+ public static final String s12081 = "12081";
+ public static final String s12082 = "12082";
+ public static final String s12083 = "12083";
+ public static final String s12084 = "12084";
+ public static final String s12085 = "12085";
+ public static final String s12086 = "12086";
+ public static final String s12087 = "12087";
+ public static final String s12088 = "12088";
+ public static final String s12089 = "12089";
+ public static final String s12090 = "12090";
+ public static final String s12091 = "12091";
+ public static final String s12092 = "12092";
+ public static final String s12093 = "12093";
+ public static final String s12094 = "12094";
+ public static final String s12095 = "12095";
+ public static final String s12096 = "12096";
+ public static final String s12097 = "12097";
+ public static final String s12098 = "12098";
+ public static final String s12099 = "12099";
+ public static final String s12100 = "12100";
+ public static final String s12101 = "12101";
+ public static final String s12102 = "12102";
+ public static final String s12103 = "12103";
+ public static final String s12104 = "12104";
+ public static final String s12105 = "12105";
+ public static final String s12106 = "12106";
+ public static final String s12107 = "12107";
+ public static final String s12108 = "12108";
+ public static final String s12109 = "12109";
+ public static final String s12110 = "12110";
+ public static final String s12111 = "12111";
+ public static final String s12112 = "12112";
+ public static final String s12113 = "12113";
+ public static final String s12114 = "12114";
+ public static final String s12115 = "12115";
+ public static final String s12116 = "12116";
+ public static final String s12117 = "12117";
+ public static final String s12118 = "12118";
+ public static final String s12119 = "12119";
+ public static final String s12120 = "12120";
+ public static final String s12121 = "12121";
+ public static final String s12122 = "12122";
+ public static final String s12123 = "12123";
+ public static final String s12124 = "12124";
+ public static final String s12125 = "12125";
+ public static final String s12126 = "12126";
+ public static final String s12127 = "12127";
+ public static final String s12128 = "12128";
+ public static final String s12129 = "12129";
+ public static final String s12130 = "12130";
+ public static final String s12131 = "12131";
+ public static final String s12132 = "12132";
+ public static final String s12133 = "12133";
+ public static final String s12134 = "12134";
+ public static final String s12135 = "12135";
+ public static final String s12136 = "12136";
+ public static final String s12137 = "12137";
+ public static final String s12138 = "12138";
+ public static final String s12139 = "12139";
+ public static final String s12140 = "12140";
+ public static final String s12141 = "12141";
+ public static final String s12142 = "12142";
+ public static final String s12143 = "12143";
+ public static final String s12144 = "12144";
+ public static final String s12145 = "12145";
+ public static final String s12146 = "12146";
+ public static final String s12147 = "12147";
+ public static final String s12148 = "12148";
+ public static final String s12149 = "12149";
+ public static final String s12150 = "12150";
+ public static final String s12151 = "12151";
+ public static final String s12152 = "12152";
+ public static final String s12153 = "12153";
+ public static final String s12154 = "12154";
+ public static final String s12155 = "12155";
+ public static final String s12156 = "12156";
+ public static final String s12157 = "12157";
+ public static final String s12158 = "12158";
+ public static final String s12159 = "12159";
+ public static final String s12160 = "12160";
+ public static final String s12161 = "12161";
+ public static final String s12162 = "12162";
+ public static final String s12163 = "12163";
+ public static final String s12164 = "12164";
+ public static final String s12165 = "12165";
+ public static final String s12166 = "12166";
+ public static final String s12167 = "12167";
+ public static final String s12168 = "12168";
+ public static final String s12169 = "12169";
+ public static final String s12170 = "12170";
+ public static final String s12171 = "12171";
+ public static final String s12172 = "12172";
+ public static final String s12173 = "12173";
+ public static final String s12174 = "12174";
+ public static final String s12175 = "12175";
+ public static final String s12176 = "12176";
+ public static final String s12177 = "12177";
+ public static final String s12178 = "12178";
+ public static final String s12179 = "12179";
+ public static final String s12180 = "12180";
+ public static final String s12181 = "12181";
+ public static final String s12182 = "12182";
+ public static final String s12183 = "12183";
+ public static final String s12184 = "12184";
+ public static final String s12185 = "12185";
+ public static final String s12186 = "12186";
+ public static final String s12187 = "12187";
+ public static final String s12188 = "12188";
+ public static final String s12189 = "12189";
+ public static final String s12190 = "12190";
+ public static final String s12191 = "12191";
+ public static final String s12192 = "12192";
+ public static final String s12193 = "12193";
+ public static final String s12194 = "12194";
+ public static final String s12195 = "12195";
+ public static final String s12196 = "12196";
+ public static final String s12197 = "12197";
+ public static final String s12198 = "12198";
+ public static final String s12199 = "12199";
+ public static final String s12200 = "12200";
+ public static final String s12201 = "12201";
+ public static final String s12202 = "12202";
+ public static final String s12203 = "12203";
+ public static final String s12204 = "12204";
+ public static final String s12205 = "12205";
+ public static final String s12206 = "12206";
+ public static final String s12207 = "12207";
+ public static final String s12208 = "12208";
+ public static final String s12209 = "12209";
+ public static final String s12210 = "12210";
+ public static final String s12211 = "12211";
+ public static final String s12212 = "12212";
+ public static final String s12213 = "12213";
+ public static final String s12214 = "12214";
+ public static final String s12215 = "12215";
+ public static final String s12216 = "12216";
+ public static final String s12217 = "12217";
+ public static final String s12218 = "12218";
+ public static final String s12219 = "12219";
+ public static final String s12220 = "12220";
+ public static final String s12221 = "12221";
+ public static final String s12222 = "12222";
+ public static final String s12223 = "12223";
+ public static final String s12224 = "12224";
+ public static final String s12225 = "12225";
+ public static final String s12226 = "12226";
+ public static final String s12227 = "12227";
+ public static final String s12228 = "12228";
+ public static final String s12229 = "12229";
+ public static final String s12230 = "12230";
+ public static final String s12231 = "12231";
+ public static final String s12232 = "12232";
+ public static final String s12233 = "12233";
+ public static final String s12234 = "12234";
+ public static final String s12235 = "12235";
+ public static final String s12236 = "12236";
+ public static final String s12237 = "12237";
+ public static final String s12238 = "12238";
+ public static final String s12239 = "12239";
+ public static final String s12240 = "12240";
+ public static final String s12241 = "12241";
+ public static final String s12242 = "12242";
+ public static final String s12243 = "12243";
+ public static final String s12244 = "12244";
+ public static final String s12245 = "12245";
+ public static final String s12246 = "12246";
+ public static final String s12247 = "12247";
+ public static final String s12248 = "12248";
+ public static final String s12249 = "12249";
+ public static final String s12250 = "12250";
+ public static final String s12251 = "12251";
+ public static final String s12252 = "12252";
+ public static final String s12253 = "12253";
+ public static final String s12254 = "12254";
+ public static final String s12255 = "12255";
+ public static final String s12256 = "12256";
+ public static final String s12257 = "12257";
+ public static final String s12258 = "12258";
+ public static final String s12259 = "12259";
+ public static final String s12260 = "12260";
+ public static final String s12261 = "12261";
+ public static final String s12262 = "12262";
+ public static final String s12263 = "12263";
+ public static final String s12264 = "12264";
+ public static final String s12265 = "12265";
+ public static final String s12266 = "12266";
+ public static final String s12267 = "12267";
+ public static final String s12268 = "12268";
+ public static final String s12269 = "12269";
+ public static final String s12270 = "12270";
+ public static final String s12271 = "12271";
+ public static final String s12272 = "12272";
+ public static final String s12273 = "12273";
+ public static final String s12274 = "12274";
+ public static final String s12275 = "12275";
+ public static final String s12276 = "12276";
+ public static final String s12277 = "12277";
+ public static final String s12278 = "12278";
+ public static final String s12279 = "12279";
+ public static final String s12280 = "12280";
+ public static final String s12281 = "12281";
+ public static final String s12282 = "12282";
+ public static final String s12283 = "12283";
+ public static final String s12284 = "12284";
+ public static final String s12285 = "12285";
+ public static final String s12286 = "12286";
+ public static final String s12287 = "12287";
+ public static final String s12288 = "12288";
+ public static final String s12289 = "12289";
+ public static final String s12290 = "12290";
+ public static final String s12291 = "12291";
+ public static final String s12292 = "12292";
+ public static final String s12293 = "12293";
+ public static final String s12294 = "12294";
+ public static final String s12295 = "12295";
+ public static final String s12296 = "12296";
+ public static final String s12297 = "12297";
+ public static final String s12298 = "12298";
+ public static final String s12299 = "12299";
+ public static final String s12300 = "12300";
+ public static final String s12301 = "12301";
+ public static final String s12302 = "12302";
+ public static final String s12303 = "12303";
+ public static final String s12304 = "12304";
+ public static final String s12305 = "12305";
+ public static final String s12306 = "12306";
+ public static final String s12307 = "12307";
+ public static final String s12308 = "12308";
+ public static final String s12309 = "12309";
+ public static final String s12310 = "12310";
+ public static final String s12311 = "12311";
+ public static final String s12312 = "12312";
+ public static final String s12313 = "12313";
+ public static final String s12314 = "12314";
+ public static final String s12315 = "12315";
+ public static final String s12316 = "12316";
+ public static final String s12317 = "12317";
+ public static final String s12318 = "12318";
+ public static final String s12319 = "12319";
+ public static final String s12320 = "12320";
+ public static final String s12321 = "12321";
+ public static final String s12322 = "12322";
+ public static final String s12323 = "12323";
+ public static final String s12324 = "12324";
+ public static final String s12325 = "12325";
+ public static final String s12326 = "12326";
+ public static final String s12327 = "12327";
+ public static final String s12328 = "12328";
+ public static final String s12329 = "12329";
+ public static final String s12330 = "12330";
+ public static final String s12331 = "12331";
+ public static final String s12332 = "12332";
+ public static final String s12333 = "12333";
+ public static final String s12334 = "12334";
+ public static final String s12335 = "12335";
+ public static final String s12336 = "12336";
+ public static final String s12337 = "12337";
+ public static final String s12338 = "12338";
+ public static final String s12339 = "12339";
+ public static final String s12340 = "12340";
+ public static final String s12341 = "12341";
+ public static final String s12342 = "12342";
+ public static final String s12343 = "12343";
+ public static final String s12344 = "12344";
+ public static final String s12345 = "12345";
+ public static final String s12346 = "12346";
+ public static final String s12347 = "12347";
+ public static final String s12348 = "12348";
+ public static final String s12349 = "12349";
+ public static final String s12350 = "12350";
+ public static final String s12351 = "12351";
+ public static final String s12352 = "12352";
+ public static final String s12353 = "12353";
+ public static final String s12354 = "12354";
+ public static final String s12355 = "12355";
+ public static final String s12356 = "12356";
+ public static final String s12357 = "12357";
+ public static final String s12358 = "12358";
+ public static final String s12359 = "12359";
+ public static final String s12360 = "12360";
+ public static final String s12361 = "12361";
+ public static final String s12362 = "12362";
+ public static final String s12363 = "12363";
+ public static final String s12364 = "12364";
+ public static final String s12365 = "12365";
+ public static final String s12366 = "12366";
+ public static final String s12367 = "12367";
+ public static final String s12368 = "12368";
+ public static final String s12369 = "12369";
+ public static final String s12370 = "12370";
+ public static final String s12371 = "12371";
+ public static final String s12372 = "12372";
+ public static final String s12373 = "12373";
+ public static final String s12374 = "12374";
+ public static final String s12375 = "12375";
+ public static final String s12376 = "12376";
+ public static final String s12377 = "12377";
+ public static final String s12378 = "12378";
+ public static final String s12379 = "12379";
+ public static final String s12380 = "12380";
+ public static final String s12381 = "12381";
+ public static final String s12382 = "12382";
+ public static final String s12383 = "12383";
+ public static final String s12384 = "12384";
+ public static final String s12385 = "12385";
+ public static final String s12386 = "12386";
+ public static final String s12387 = "12387";
+ public static final String s12388 = "12388";
+ public static final String s12389 = "12389";
+ public static final String s12390 = "12390";
+ public static final String s12391 = "12391";
+ public static final String s12392 = "12392";
+ public static final String s12393 = "12393";
+ public static final String s12394 = "12394";
+ public static final String s12395 = "12395";
+ public static final String s12396 = "12396";
+ public static final String s12397 = "12397";
+ public static final String s12398 = "12398";
+ public static final String s12399 = "12399";
+ public static final String s12400 = "12400";
+ public static final String s12401 = "12401";
+ public static final String s12402 = "12402";
+ public static final String s12403 = "12403";
+ public static final String s12404 = "12404";
+ public static final String s12405 = "12405";
+ public static final String s12406 = "12406";
+ public static final String s12407 = "12407";
+ public static final String s12408 = "12408";
+ public static final String s12409 = "12409";
+ public static final String s12410 = "12410";
+ public static final String s12411 = "12411";
+ public static final String s12412 = "12412";
+ public static final String s12413 = "12413";
+ public static final String s12414 = "12414";
+ public static final String s12415 = "12415";
+ public static final String s12416 = "12416";
+ public static final String s12417 = "12417";
+ public static final String s12418 = "12418";
+ public static final String s12419 = "12419";
+ public static final String s12420 = "12420";
+ public static final String s12421 = "12421";
+ public static final String s12422 = "12422";
+ public static final String s12423 = "12423";
+ public static final String s12424 = "12424";
+ public static final String s12425 = "12425";
+ public static final String s12426 = "12426";
+ public static final String s12427 = "12427";
+ public static final String s12428 = "12428";
+ public static final String s12429 = "12429";
+ public static final String s12430 = "12430";
+ public static final String s12431 = "12431";
+ public static final String s12432 = "12432";
+ public static final String s12433 = "12433";
+ public static final String s12434 = "12434";
+ public static final String s12435 = "12435";
+ public static final String s12436 = "12436";
+ public static final String s12437 = "12437";
+ public static final String s12438 = "12438";
+ public static final String s12439 = "12439";
+ public static final String s12440 = "12440";
+ public static final String s12441 = "12441";
+ public static final String s12442 = "12442";
+ public static final String s12443 = "12443";
+ public static final String s12444 = "12444";
+ public static final String s12445 = "12445";
+ public static final String s12446 = "12446";
+ public static final String s12447 = "12447";
+ public static final String s12448 = "12448";
+ public static final String s12449 = "12449";
+ public static final String s12450 = "12450";
+ public static final String s12451 = "12451";
+ public static final String s12452 = "12452";
+ public static final String s12453 = "12453";
+ public static final String s12454 = "12454";
+ public static final String s12455 = "12455";
+ public static final String s12456 = "12456";
+ public static final String s12457 = "12457";
+ public static final String s12458 = "12458";
+ public static final String s12459 = "12459";
+ public static final String s12460 = "12460";
+ public static final String s12461 = "12461";
+ public static final String s12462 = "12462";
+ public static final String s12463 = "12463";
+ public static final String s12464 = "12464";
+ public static final String s12465 = "12465";
+ public static final String s12466 = "12466";
+ public static final String s12467 = "12467";
+ public static final String s12468 = "12468";
+ public static final String s12469 = "12469";
+ public static final String s12470 = "12470";
+ public static final String s12471 = "12471";
+ public static final String s12472 = "12472";
+ public static final String s12473 = "12473";
+ public static final String s12474 = "12474";
+ public static final String s12475 = "12475";
+ public static final String s12476 = "12476";
+ public static final String s12477 = "12477";
+ public static final String s12478 = "12478";
+ public static final String s12479 = "12479";
+ public static final String s12480 = "12480";
+ public static final String s12481 = "12481";
+ public static final String s12482 = "12482";
+ public static final String s12483 = "12483";
+ public static final String s12484 = "12484";
+ public static final String s12485 = "12485";
+ public static final String s12486 = "12486";
+ public static final String s12487 = "12487";
+ public static final String s12488 = "12488";
+ public static final String s12489 = "12489";
+ public static final String s12490 = "12490";
+ public static final String s12491 = "12491";
+ public static final String s12492 = "12492";
+ public static final String s12493 = "12493";
+ public static final String s12494 = "12494";
+ public static final String s12495 = "12495";
+ public static final String s12496 = "12496";
+ public static final String s12497 = "12497";
+ public static final String s12498 = "12498";
+ public static final String s12499 = "12499";
+ public static final String s12500 = "12500";
+ public static final String s12501 = "12501";
+ public static final String s12502 = "12502";
+ public static final String s12503 = "12503";
+ public static final String s12504 = "12504";
+ public static final String s12505 = "12505";
+ public static final String s12506 = "12506";
+ public static final String s12507 = "12507";
+ public static final String s12508 = "12508";
+ public static final String s12509 = "12509";
+ public static final String s12510 = "12510";
+ public static final String s12511 = "12511";
+ public static final String s12512 = "12512";
+ public static final String s12513 = "12513";
+ public static final String s12514 = "12514";
+ public static final String s12515 = "12515";
+ public static final String s12516 = "12516";
+ public static final String s12517 = "12517";
+ public static final String s12518 = "12518";
+ public static final String s12519 = "12519";
+ public static final String s12520 = "12520";
+ public static final String s12521 = "12521";
+ public static final String s12522 = "12522";
+ public static final String s12523 = "12523";
+ public static final String s12524 = "12524";
+ public static final String s12525 = "12525";
+ public static final String s12526 = "12526";
+ public static final String s12527 = "12527";
+ public static final String s12528 = "12528";
+ public static final String s12529 = "12529";
+ public static final String s12530 = "12530";
+ public static final String s12531 = "12531";
+ public static final String s12532 = "12532";
+ public static final String s12533 = "12533";
+ public static final String s12534 = "12534";
+ public static final String s12535 = "12535";
+ public static final String s12536 = "12536";
+ public static final String s12537 = "12537";
+ public static final String s12538 = "12538";
+ public static final String s12539 = "12539";
+ public static final String s12540 = "12540";
+ public static final String s12541 = "12541";
+ public static final String s12542 = "12542";
+ public static final String s12543 = "12543";
+ public static final String s12544 = "12544";
+ public static final String s12545 = "12545";
+ public static final String s12546 = "12546";
+ public static final String s12547 = "12547";
+ public static final String s12548 = "12548";
+ public static final String s12549 = "12549";
+ public static final String s12550 = "12550";
+ public static final String s12551 = "12551";
+ public static final String s12552 = "12552";
+ public static final String s12553 = "12553";
+ public static final String s12554 = "12554";
+ public static final String s12555 = "12555";
+ public static final String s12556 = "12556";
+ public static final String s12557 = "12557";
+ public static final String s12558 = "12558";
+ public static final String s12559 = "12559";
+ public static final String s12560 = "12560";
+ public static final String s12561 = "12561";
+ public static final String s12562 = "12562";
+ public static final String s12563 = "12563";
+ public static final String s12564 = "12564";
+ public static final String s12565 = "12565";
+ public static final String s12566 = "12566";
+ public static final String s12567 = "12567";
+ public static final String s12568 = "12568";
+ public static final String s12569 = "12569";
+ public static final String s12570 = "12570";
+ public static final String s12571 = "12571";
+ public static final String s12572 = "12572";
+ public static final String s12573 = "12573";
+ public static final String s12574 = "12574";
+ public static final String s12575 = "12575";
+ public static final String s12576 = "12576";
+ public static final String s12577 = "12577";
+ public static final String s12578 = "12578";
+ public static final String s12579 = "12579";
+ public static final String s12580 = "12580";
+ public static final String s12581 = "12581";
+ public static final String s12582 = "12582";
+ public static final String s12583 = "12583";
+ public static final String s12584 = "12584";
+ public static final String s12585 = "12585";
+ public static final String s12586 = "12586";
+ public static final String s12587 = "12587";
+ public static final String s12588 = "12588";
+ public static final String s12589 = "12589";
+ public static final String s12590 = "12590";
+ public static final String s12591 = "12591";
+ public static final String s12592 = "12592";
+ public static final String s12593 = "12593";
+ public static final String s12594 = "12594";
+ public static final String s12595 = "12595";
+ public static final String s12596 = "12596";
+ public static final String s12597 = "12597";
+ public static final String s12598 = "12598";
+ public static final String s12599 = "12599";
+ public static final String s12600 = "12600";
+ public static final String s12601 = "12601";
+ public static final String s12602 = "12602";
+ public static final String s12603 = "12603";
+ public static final String s12604 = "12604";
+ public static final String s12605 = "12605";
+ public static final String s12606 = "12606";
+ public static final String s12607 = "12607";
+ public static final String s12608 = "12608";
+ public static final String s12609 = "12609";
+ public static final String s12610 = "12610";
+ public static final String s12611 = "12611";
+ public static final String s12612 = "12612";
+ public static final String s12613 = "12613";
+ public static final String s12614 = "12614";
+ public static final String s12615 = "12615";
+ public static final String s12616 = "12616";
+ public static final String s12617 = "12617";
+ public static final String s12618 = "12618";
+ public static final String s12619 = "12619";
+ public static final String s12620 = "12620";
+ public static final String s12621 = "12621";
+ public static final String s12622 = "12622";
+ public static final String s12623 = "12623";
+ public static final String s12624 = "12624";
+ public static final String s12625 = "12625";
+ public static final String s12626 = "12626";
+ public static final String s12627 = "12627";
+ public static final String s12628 = "12628";
+ public static final String s12629 = "12629";
+ public static final String s12630 = "12630";
+ public static final String s12631 = "12631";
+ public static final String s12632 = "12632";
+ public static final String s12633 = "12633";
+ public static final String s12634 = "12634";
+ public static final String s12635 = "12635";
+ public static final String s12636 = "12636";
+ public static final String s12637 = "12637";
+ public static final String s12638 = "12638";
+ public static final String s12639 = "12639";
+ public static final String s12640 = "12640";
+ public static final String s12641 = "12641";
+ public static final String s12642 = "12642";
+ public static final String s12643 = "12643";
+ public static final String s12644 = "12644";
+ public static final String s12645 = "12645";
+ public static final String s12646 = "12646";
+ public static final String s12647 = "12647";
+ public static final String s12648 = "12648";
+ public static final String s12649 = "12649";
+ public static final String s12650 = "12650";
+ public static final String s12651 = "12651";
+ public static final String s12652 = "12652";
+ public static final String s12653 = "12653";
+ public static final String s12654 = "12654";
+ public static final String s12655 = "12655";
+ public static final String s12656 = "12656";
+ public static final String s12657 = "12657";
+ public static final String s12658 = "12658";
+ public static final String s12659 = "12659";
+ public static final String s12660 = "12660";
+ public static final String s12661 = "12661";
+ public static final String s12662 = "12662";
+ public static final String s12663 = "12663";
+ public static final String s12664 = "12664";
+ public static final String s12665 = "12665";
+ public static final String s12666 = "12666";
+ public static final String s12667 = "12667";
+ public static final String s12668 = "12668";
+ public static final String s12669 = "12669";
+ public static final String s12670 = "12670";
+ public static final String s12671 = "12671";
+ public static final String s12672 = "12672";
+ public static final String s12673 = "12673";
+ public static final String s12674 = "12674";
+ public static final String s12675 = "12675";
+ public static final String s12676 = "12676";
+ public static final String s12677 = "12677";
+ public static final String s12678 = "12678";
+ public static final String s12679 = "12679";
+ public static final String s12680 = "12680";
+ public static final String s12681 = "12681";
+ public static final String s12682 = "12682";
+ public static final String s12683 = "12683";
+ public static final String s12684 = "12684";
+ public static final String s12685 = "12685";
+ public static final String s12686 = "12686";
+ public static final String s12687 = "12687";
+ public static final String s12688 = "12688";
+ public static final String s12689 = "12689";
+ public static final String s12690 = "12690";
+ public static final String s12691 = "12691";
+ public static final String s12692 = "12692";
+ public static final String s12693 = "12693";
+ public static final String s12694 = "12694";
+ public static final String s12695 = "12695";
+ public static final String s12696 = "12696";
+ public static final String s12697 = "12697";
+ public static final String s12698 = "12698";
+ public static final String s12699 = "12699";
+ public static final String s12700 = "12700";
+ public static final String s12701 = "12701";
+ public static final String s12702 = "12702";
+ public static final String s12703 = "12703";
+ public static final String s12704 = "12704";
+ public static final String s12705 = "12705";
+ public static final String s12706 = "12706";
+ public static final String s12707 = "12707";
+ public static final String s12708 = "12708";
+ public static final String s12709 = "12709";
+ public static final String s12710 = "12710";
+ public static final String s12711 = "12711";
+ public static final String s12712 = "12712";
+ public static final String s12713 = "12713";
+ public static final String s12714 = "12714";
+ public static final String s12715 = "12715";
+ public static final String s12716 = "12716";
+ public static final String s12717 = "12717";
+ public static final String s12718 = "12718";
+ public static final String s12719 = "12719";
+ public static final String s12720 = "12720";
+ public static final String s12721 = "12721";
+ public static final String s12722 = "12722";
+ public static final String s12723 = "12723";
+ public static final String s12724 = "12724";
+ public static final String s12725 = "12725";
+ public static final String s12726 = "12726";
+ public static final String s12727 = "12727";
+ public static final String s12728 = "12728";
+ public static final String s12729 = "12729";
+ public static final String s12730 = "12730";
+ public static final String s12731 = "12731";
+ public static final String s12732 = "12732";
+ public static final String s12733 = "12733";
+ public static final String s12734 = "12734";
+ public static final String s12735 = "12735";
+ public static final String s12736 = "12736";
+ public static final String s12737 = "12737";
+ public static final String s12738 = "12738";
+ public static final String s12739 = "12739";
+ public static final String s12740 = "12740";
+ public static final String s12741 = "12741";
+ public static final String s12742 = "12742";
+ public static final String s12743 = "12743";
+ public static final String s12744 = "12744";
+ public static final String s12745 = "12745";
+ public static final String s12746 = "12746";
+ public static final String s12747 = "12747";
+ public static final String s12748 = "12748";
+ public static final String s12749 = "12749";
+ public static final String s12750 = "12750";
+ public static final String s12751 = "12751";
+ public static final String s12752 = "12752";
+ public static final String s12753 = "12753";
+ public static final String s12754 = "12754";
+ public static final String s12755 = "12755";
+ public static final String s12756 = "12756";
+ public static final String s12757 = "12757";
+ public static final String s12758 = "12758";
+ public static final String s12759 = "12759";
+ public static final String s12760 = "12760";
+ public static final String s12761 = "12761";
+ public static final String s12762 = "12762";
+ public static final String s12763 = "12763";
+ public static final String s12764 = "12764";
+ public static final String s12765 = "12765";
+ public static final String s12766 = "12766";
+ public static final String s12767 = "12767";
+ public static final String s12768 = "12768";
+ public static final String s12769 = "12769";
+ public static final String s12770 = "12770";
+ public static final String s12771 = "12771";
+ public static final String s12772 = "12772";
+ public static final String s12773 = "12773";
+ public static final String s12774 = "12774";
+ public static final String s12775 = "12775";
+ public static final String s12776 = "12776";
+ public static final String s12777 = "12777";
+ public static final String s12778 = "12778";
+ public static final String s12779 = "12779";
+ public static final String s12780 = "12780";
+ public static final String s12781 = "12781";
+ public static final String s12782 = "12782";
+ public static final String s12783 = "12783";
+ public static final String s12784 = "12784";
+ public static final String s12785 = "12785";
+ public static final String s12786 = "12786";
+ public static final String s12787 = "12787";
+ public static final String s12788 = "12788";
+ public static final String s12789 = "12789";
+ public static final String s12790 = "12790";
+ public static final String s12791 = "12791";
+ public static final String s12792 = "12792";
+ public static final String s12793 = "12793";
+ public static final String s12794 = "12794";
+ public static final String s12795 = "12795";
+ public static final String s12796 = "12796";
+ public static final String s12797 = "12797";
+ public static final String s12798 = "12798";
+ public static final String s12799 = "12799";
+ public static final String s12800 = "12800";
+ public static final String s12801 = "12801";
+ public static final String s12802 = "12802";
+ public static final String s12803 = "12803";
+ public static final String s12804 = "12804";
+ public static final String s12805 = "12805";
+ public static final String s12806 = "12806";
+ public static final String s12807 = "12807";
+ public static final String s12808 = "12808";
+ public static final String s12809 = "12809";
+ public static final String s12810 = "12810";
+ public static final String s12811 = "12811";
+ public static final String s12812 = "12812";
+ public static final String s12813 = "12813";
+ public static final String s12814 = "12814";
+ public static final String s12815 = "12815";
+ public static final String s12816 = "12816";
+ public static final String s12817 = "12817";
+ public static final String s12818 = "12818";
+ public static final String s12819 = "12819";
+ public static final String s12820 = "12820";
+ public static final String s12821 = "12821";
+ public static final String s12822 = "12822";
+ public static final String s12823 = "12823";
+ public static final String s12824 = "12824";
+ public static final String s12825 = "12825";
+ public static final String s12826 = "12826";
+ public static final String s12827 = "12827";
+ public static final String s12828 = "12828";
+ public static final String s12829 = "12829";
+ public static final String s12830 = "12830";
+ public static final String s12831 = "12831";
+ public static final String s12832 = "12832";
+ public static final String s12833 = "12833";
+ public static final String s12834 = "12834";
+ public static final String s12835 = "12835";
+ public static final String s12836 = "12836";
+ public static final String s12837 = "12837";
+ public static final String s12838 = "12838";
+ public static final String s12839 = "12839";
+ public static final String s12840 = "12840";
+ public static final String s12841 = "12841";
+ public static final String s12842 = "12842";
+ public static final String s12843 = "12843";
+ public static final String s12844 = "12844";
+ public static final String s12845 = "12845";
+ public static final String s12846 = "12846";
+ public static final String s12847 = "12847";
+ public static final String s12848 = "12848";
+ public static final String s12849 = "12849";
+ public static final String s12850 = "12850";
+ public static final String s12851 = "12851";
+ public static final String s12852 = "12852";
+ public static final String s12853 = "12853";
+ public static final String s12854 = "12854";
+ public static final String s12855 = "12855";
+ public static final String s12856 = "12856";
+ public static final String s12857 = "12857";
+ public static final String s12858 = "12858";
+ public static final String s12859 = "12859";
+ public static final String s12860 = "12860";
+ public static final String s12861 = "12861";
+ public static final String s12862 = "12862";
+ public static final String s12863 = "12863";
+ public static final String s12864 = "12864";
+ public static final String s12865 = "12865";
+ public static final String s12866 = "12866";
+ public static final String s12867 = "12867";
+ public static final String s12868 = "12868";
+ public static final String s12869 = "12869";
+ public static final String s12870 = "12870";
+ public static final String s12871 = "12871";
+ public static final String s12872 = "12872";
+ public static final String s12873 = "12873";
+ public static final String s12874 = "12874";
+ public static final String s12875 = "12875";
+ public static final String s12876 = "12876";
+ public static final String s12877 = "12877";
+ public static final String s12878 = "12878";
+ public static final String s12879 = "12879";
+ public static final String s12880 = "12880";
+ public static final String s12881 = "12881";
+ public static final String s12882 = "12882";
+ public static final String s12883 = "12883";
+ public static final String s12884 = "12884";
+ public static final String s12885 = "12885";
+ public static final String s12886 = "12886";
+ public static final String s12887 = "12887";
+ public static final String s12888 = "12888";
+ public static final String s12889 = "12889";
+ public static final String s12890 = "12890";
+ public static final String s12891 = "12891";
+ public static final String s12892 = "12892";
+ public static final String s12893 = "12893";
+ public static final String s12894 = "12894";
+ public static final String s12895 = "12895";
+ public static final String s12896 = "12896";
+ public static final String s12897 = "12897";
+ public static final String s12898 = "12898";
+ public static final String s12899 = "12899";
+ public static final String s12900 = "12900";
+ public static final String s12901 = "12901";
+ public static final String s12902 = "12902";
+ public static final String s12903 = "12903";
+ public static final String s12904 = "12904";
+ public static final String s12905 = "12905";
+ public static final String s12906 = "12906";
+ public static final String s12907 = "12907";
+ public static final String s12908 = "12908";
+ public static final String s12909 = "12909";
+ public static final String s12910 = "12910";
+ public static final String s12911 = "12911";
+ public static final String s12912 = "12912";
+ public static final String s12913 = "12913";
+ public static final String s12914 = "12914";
+ public static final String s12915 = "12915";
+ public static final String s12916 = "12916";
+ public static final String s12917 = "12917";
+ public static final String s12918 = "12918";
+ public static final String s12919 = "12919";
+ public static final String s12920 = "12920";
+ public static final String s12921 = "12921";
+ public static final String s12922 = "12922";
+ public static final String s12923 = "12923";
+ public static final String s12924 = "12924";
+ public static final String s12925 = "12925";
+ public static final String s12926 = "12926";
+ public static final String s12927 = "12927";
+ public static final String s12928 = "12928";
+ public static final String s12929 = "12929";
+ public static final String s12930 = "12930";
+ public static final String s12931 = "12931";
+ public static final String s12932 = "12932";
+ public static final String s12933 = "12933";
+ public static final String s12934 = "12934";
+ public static final String s12935 = "12935";
+ public static final String s12936 = "12936";
+ public static final String s12937 = "12937";
+ public static final String s12938 = "12938";
+ public static final String s12939 = "12939";
+ public static final String s12940 = "12940";
+ public static final String s12941 = "12941";
+ public static final String s12942 = "12942";
+ public static final String s12943 = "12943";
+ public static final String s12944 = "12944";
+ public static final String s12945 = "12945";
+ public static final String s12946 = "12946";
+ public static final String s12947 = "12947";
+ public static final String s12948 = "12948";
+ public static final String s12949 = "12949";
+ public static final String s12950 = "12950";
+ public static final String s12951 = "12951";
+ public static final String s12952 = "12952";
+ public static final String s12953 = "12953";
+ public static final String s12954 = "12954";
+ public static final String s12955 = "12955";
+ public static final String s12956 = "12956";
+ public static final String s12957 = "12957";
+ public static final String s12958 = "12958";
+ public static final String s12959 = "12959";
+ public static final String s12960 = "12960";
+ public static final String s12961 = "12961";
+ public static final String s12962 = "12962";
+ public static final String s12963 = "12963";
+ public static final String s12964 = "12964";
+ public static final String s12965 = "12965";
+ public static final String s12966 = "12966";
+ public static final String s12967 = "12967";
+ public static final String s12968 = "12968";
+ public static final String s12969 = "12969";
+ public static final String s12970 = "12970";
+ public static final String s12971 = "12971";
+ public static final String s12972 = "12972";
+ public static final String s12973 = "12973";
+ public static final String s12974 = "12974";
+ public static final String s12975 = "12975";
+ public static final String s12976 = "12976";
+ public static final String s12977 = "12977";
+ public static final String s12978 = "12978";
+ public static final String s12979 = "12979";
+ public static final String s12980 = "12980";
+ public static final String s12981 = "12981";
+ public static final String s12982 = "12982";
+ public static final String s12983 = "12983";
+ public static final String s12984 = "12984";
+ public static final String s12985 = "12985";
+ public static final String s12986 = "12986";
+ public static final String s12987 = "12987";
+ public static final String s12988 = "12988";
+ public static final String s12989 = "12989";
+ public static final String s12990 = "12990";
+ public static final String s12991 = "12991";
+ public static final String s12992 = "12992";
+ public static final String s12993 = "12993";
+ public static final String s12994 = "12994";
+ public static final String s12995 = "12995";
+ public static final String s12996 = "12996";
+ public static final String s12997 = "12997";
+ public static final String s12998 = "12998";
+ public static final String s12999 = "12999";
+ public static final String s13000 = "13000";
+ public static final String s13001 = "13001";
+ public static final String s13002 = "13002";
+ public static final String s13003 = "13003";
+ public static final String s13004 = "13004";
+ public static final String s13005 = "13005";
+ public static final String s13006 = "13006";
+ public static final String s13007 = "13007";
+ public static final String s13008 = "13008";
+ public static final String s13009 = "13009";
+ public static final String s13010 = "13010";
+ public static final String s13011 = "13011";
+ public static final String s13012 = "13012";
+ public static final String s13013 = "13013";
+ public static final String s13014 = "13014";
+ public static final String s13015 = "13015";
+ public static final String s13016 = "13016";
+ public static final String s13017 = "13017";
+ public static final String s13018 = "13018";
+ public static final String s13019 = "13019";
+ public static final String s13020 = "13020";
+ public static final String s13021 = "13021";
+ public static final String s13022 = "13022";
+ public static final String s13023 = "13023";
+ public static final String s13024 = "13024";
+ public static final String s13025 = "13025";
+ public static final String s13026 = "13026";
+ public static final String s13027 = "13027";
+ public static final String s13028 = "13028";
+ public static final String s13029 = "13029";
+ public static final String s13030 = "13030";
+ public static final String s13031 = "13031";
+ public static final String s13032 = "13032";
+ public static final String s13033 = "13033";
+ public static final String s13034 = "13034";
+ public static final String s13035 = "13035";
+ public static final String s13036 = "13036";
+ public static final String s13037 = "13037";
+ public static final String s13038 = "13038";
+ public static final String s13039 = "13039";
+ public static final String s13040 = "13040";
+ public static final String s13041 = "13041";
+ public static final String s13042 = "13042";
+ public static final String s13043 = "13043";
+ public static final String s13044 = "13044";
+ public static final String s13045 = "13045";
+ public static final String s13046 = "13046";
+ public static final String s13047 = "13047";
+ public static final String s13048 = "13048";
+ public static final String s13049 = "13049";
+ public static final String s13050 = "13050";
+ public static final String s13051 = "13051";
+ public static final String s13052 = "13052";
+ public static final String s13053 = "13053";
+ public static final String s13054 = "13054";
+ public static final String s13055 = "13055";
+ public static final String s13056 = "13056";
+ public static final String s13057 = "13057";
+ public static final String s13058 = "13058";
+ public static final String s13059 = "13059";
+ public static final String s13060 = "13060";
+ public static final String s13061 = "13061";
+ public static final String s13062 = "13062";
+ public static final String s13063 = "13063";
+ public static final String s13064 = "13064";
+ public static final String s13065 = "13065";
+ public static final String s13066 = "13066";
+ public static final String s13067 = "13067";
+ public static final String s13068 = "13068";
+ public static final String s13069 = "13069";
+ public static final String s13070 = "13070";
+ public static final String s13071 = "13071";
+ public static final String s13072 = "13072";
+ public static final String s13073 = "13073";
+ public static final String s13074 = "13074";
+ public static final String s13075 = "13075";
+ public static final String s13076 = "13076";
+ public static final String s13077 = "13077";
+ public static final String s13078 = "13078";
+ public static final String s13079 = "13079";
+ public static final String s13080 = "13080";
+ public static final String s13081 = "13081";
+ public static final String s13082 = "13082";
+ public static final String s13083 = "13083";
+ public static final String s13084 = "13084";
+ public static final String s13085 = "13085";
+ public static final String s13086 = "13086";
+ public static final String s13087 = "13087";
+ public static final String s13088 = "13088";
+ public static final String s13089 = "13089";
+ public static final String s13090 = "13090";
+ public static final String s13091 = "13091";
+ public static final String s13092 = "13092";
+ public static final String s13093 = "13093";
+ public static final String s13094 = "13094";
+ public static final String s13095 = "13095";
+ public static final String s13096 = "13096";
+ public static final String s13097 = "13097";
+ public static final String s13098 = "13098";
+ public static final String s13099 = "13099";
+ public static final String s13100 = "13100";
+ public static final String s13101 = "13101";
+ public static final String s13102 = "13102";
+ public static final String s13103 = "13103";
+ public static final String s13104 = "13104";
+ public static final String s13105 = "13105";
+ public static final String s13106 = "13106";
+ public static final String s13107 = "13107";
+ public static final String s13108 = "13108";
+ public static final String s13109 = "13109";
+ public static final String s13110 = "13110";
+ public static final String s13111 = "13111";
+ public static final String s13112 = "13112";
+ public static final String s13113 = "13113";
+ public static final String s13114 = "13114";
+ public static final String s13115 = "13115";
+ public static final String s13116 = "13116";
+ public static final String s13117 = "13117";
+ public static final String s13118 = "13118";
+ public static final String s13119 = "13119";
+ public static final String s13120 = "13120";
+ public static final String s13121 = "13121";
+ public static final String s13122 = "13122";
+ public static final String s13123 = "13123";
+ public static final String s13124 = "13124";
+ public static final String s13125 = "13125";
+ public static final String s13126 = "13126";
+ public static final String s13127 = "13127";
+ public static final String s13128 = "13128";
+ public static final String s13129 = "13129";
+ public static final String s13130 = "13130";
+ public static final String s13131 = "13131";
+ public static final String s13132 = "13132";
+ public static final String s13133 = "13133";
+ public static final String s13134 = "13134";
+ public static final String s13135 = "13135";
+ public static final String s13136 = "13136";
+ public static final String s13137 = "13137";
+ public static final String s13138 = "13138";
+ public static final String s13139 = "13139";
+ public static final String s13140 = "13140";
+ public static final String s13141 = "13141";
+ public static final String s13142 = "13142";
+ public static final String s13143 = "13143";
+ public static final String s13144 = "13144";
+ public static final String s13145 = "13145";
+ public static final String s13146 = "13146";
+ public static final String s13147 = "13147";
+ public static final String s13148 = "13148";
+ public static final String s13149 = "13149";
+ public static final String s13150 = "13150";
+ public static final String s13151 = "13151";
+ public static final String s13152 = "13152";
+ public static final String s13153 = "13153";
+ public static final String s13154 = "13154";
+ public static final String s13155 = "13155";
+ public static final String s13156 = "13156";
+ public static final String s13157 = "13157";
+ public static final String s13158 = "13158";
+ public static final String s13159 = "13159";
+ public static final String s13160 = "13160";
+ public static final String s13161 = "13161";
+ public static final String s13162 = "13162";
+ public static final String s13163 = "13163";
+ public static final String s13164 = "13164";
+ public static final String s13165 = "13165";
+ public static final String s13166 = "13166";
+ public static final String s13167 = "13167";
+ public static final String s13168 = "13168";
+ public static final String s13169 = "13169";
+ public static final String s13170 = "13170";
+ public static final String s13171 = "13171";
+ public static final String s13172 = "13172";
+ public static final String s13173 = "13173";
+ public static final String s13174 = "13174";
+ public static final String s13175 = "13175";
+ public static final String s13176 = "13176";
+ public static final String s13177 = "13177";
+ public static final String s13178 = "13178";
+ public static final String s13179 = "13179";
+ public static final String s13180 = "13180";
+ public static final String s13181 = "13181";
+ public static final String s13182 = "13182";
+ public static final String s13183 = "13183";
+ public static final String s13184 = "13184";
+ public static final String s13185 = "13185";
+ public static final String s13186 = "13186";
+ public static final String s13187 = "13187";
+ public static final String s13188 = "13188";
+ public static final String s13189 = "13189";
+ public static final String s13190 = "13190";
+ public static final String s13191 = "13191";
+ public static final String s13192 = "13192";
+ public static final String s13193 = "13193";
+ public static final String s13194 = "13194";
+ public static final String s13195 = "13195";
+ public static final String s13196 = "13196";
+ public static final String s13197 = "13197";
+ public static final String s13198 = "13198";
+ public static final String s13199 = "13199";
+ public static final String s13200 = "13200";
+ public static final String s13201 = "13201";
+ public static final String s13202 = "13202";
+ public static final String s13203 = "13203";
+ public static final String s13204 = "13204";
+ public static final String s13205 = "13205";
+ public static final String s13206 = "13206";
+ public static final String s13207 = "13207";
+ public static final String s13208 = "13208";
+ public static final String s13209 = "13209";
+ public static final String s13210 = "13210";
+ public static final String s13211 = "13211";
+ public static final String s13212 = "13212";
+ public static final String s13213 = "13213";
+ public static final String s13214 = "13214";
+ public static final String s13215 = "13215";
+ public static final String s13216 = "13216";
+ public static final String s13217 = "13217";
+ public static final String s13218 = "13218";
+ public static final String s13219 = "13219";
+ public static final String s13220 = "13220";
+ public static final String s13221 = "13221";
+ public static final String s13222 = "13222";
+ public static final String s13223 = "13223";
+ public static final String s13224 = "13224";
+ public static final String s13225 = "13225";
+ public static final String s13226 = "13226";
+ public static final String s13227 = "13227";
+ public static final String s13228 = "13228";
+ public static final String s13229 = "13229";
+ public static final String s13230 = "13230";
+ public static final String s13231 = "13231";
+ public static final String s13232 = "13232";
+ public static final String s13233 = "13233";
+ public static final String s13234 = "13234";
+ public static final String s13235 = "13235";
+ public static final String s13236 = "13236";
+ public static final String s13237 = "13237";
+ public static final String s13238 = "13238";
+ public static final String s13239 = "13239";
+ public static final String s13240 = "13240";
+ public static final String s13241 = "13241";
+ public static final String s13242 = "13242";
+ public static final String s13243 = "13243";
+ public static final String s13244 = "13244";
+ public static final String s13245 = "13245";
+ public static final String s13246 = "13246";
+ public static final String s13247 = "13247";
+ public static final String s13248 = "13248";
+ public static final String s13249 = "13249";
+ public static final String s13250 = "13250";
+ public static final String s13251 = "13251";
+ public static final String s13252 = "13252";
+ public static final String s13253 = "13253";
+ public static final String s13254 = "13254";
+ public static final String s13255 = "13255";
+ public static final String s13256 = "13256";
+ public static final String s13257 = "13257";
+ public static final String s13258 = "13258";
+ public static final String s13259 = "13259";
+ public static final String s13260 = "13260";
+ public static final String s13261 = "13261";
+ public static final String s13262 = "13262";
+ public static final String s13263 = "13263";
+ public static final String s13264 = "13264";
+ public static final String s13265 = "13265";
+ public static final String s13266 = "13266";
+ public static final String s13267 = "13267";
+ public static final String s13268 = "13268";
+ public static final String s13269 = "13269";
+ public static final String s13270 = "13270";
+ public static final String s13271 = "13271";
+ public static final String s13272 = "13272";
+ public static final String s13273 = "13273";
+ public static final String s13274 = "13274";
+ public static final String s13275 = "13275";
+ public static final String s13276 = "13276";
+ public static final String s13277 = "13277";
+ public static final String s13278 = "13278";
+ public static final String s13279 = "13279";
+ public static final String s13280 = "13280";
+ public static final String s13281 = "13281";
+ public static final String s13282 = "13282";
+ public static final String s13283 = "13283";
+ public static final String s13284 = "13284";
+ public static final String s13285 = "13285";
+ public static final String s13286 = "13286";
+ public static final String s13287 = "13287";
+ public static final String s13288 = "13288";
+ public static final String s13289 = "13289";
+ public static final String s13290 = "13290";
+ public static final String s13291 = "13291";
+ public static final String s13292 = "13292";
+ public static final String s13293 = "13293";
+ public static final String s13294 = "13294";
+ public static final String s13295 = "13295";
+ public static final String s13296 = "13296";
+ public static final String s13297 = "13297";
+ public static final String s13298 = "13298";
+ public static final String s13299 = "13299";
+ public static final String s13300 = "13300";
+ public static final String s13301 = "13301";
+ public static final String s13302 = "13302";
+ public static final String s13303 = "13303";
+ public static final String s13304 = "13304";
+ public static final String s13305 = "13305";
+ public static final String s13306 = "13306";
+ public static final String s13307 = "13307";
+ public static final String s13308 = "13308";
+ public static final String s13309 = "13309";
+ public static final String s13310 = "13310";
+ public static final String s13311 = "13311";
+ public static final String s13312 = "13312";
+ public static final String s13313 = "13313";
+ public static final String s13314 = "13314";
+ public static final String s13315 = "13315";
+ public static final String s13316 = "13316";
+ public static final String s13317 = "13317";
+ public static final String s13318 = "13318";
+ public static final String s13319 = "13319";
+ public static final String s13320 = "13320";
+ public static final String s13321 = "13321";
+ public static final String s13322 = "13322";
+ public static final String s13323 = "13323";
+ public static final String s13324 = "13324";
+ public static final String s13325 = "13325";
+ public static final String s13326 = "13326";
+ public static final String s13327 = "13327";
+ public static final String s13328 = "13328";
+ public static final String s13329 = "13329";
+ public static final String s13330 = "13330";
+ public static final String s13331 = "13331";
+ public static final String s13332 = "13332";
+ public static final String s13333 = "13333";
+ public static final String s13334 = "13334";
+ public static final String s13335 = "13335";
+ public static final String s13336 = "13336";
+ public static final String s13337 = "13337";
+ public static final String s13338 = "13338";
+ public static final String s13339 = "13339";
+ public static final String s13340 = "13340";
+ public static final String s13341 = "13341";
+ public static final String s13342 = "13342";
+ public static final String s13343 = "13343";
+ public static final String s13344 = "13344";
+ public static final String s13345 = "13345";
+ public static final String s13346 = "13346";
+ public static final String s13347 = "13347";
+ public static final String s13348 = "13348";
+ public static final String s13349 = "13349";
+ public static final String s13350 = "13350";
+ public static final String s13351 = "13351";
+ public static final String s13352 = "13352";
+ public static final String s13353 = "13353";
+ public static final String s13354 = "13354";
+ public static final String s13355 = "13355";
+ public static final String s13356 = "13356";
+ public static final String s13357 = "13357";
+ public static final String s13358 = "13358";
+ public static final String s13359 = "13359";
+ public static final String s13360 = "13360";
+ public static final String s13361 = "13361";
+ public static final String s13362 = "13362";
+ public static final String s13363 = "13363";
+ public static final String s13364 = "13364";
+ public static final String s13365 = "13365";
+ public static final String s13366 = "13366";
+ public static final String s13367 = "13367";
+ public static final String s13368 = "13368";
+ public static final String s13369 = "13369";
+ public static final String s13370 = "13370";
+ public static final String s13371 = "13371";
+ public static final String s13372 = "13372";
+ public static final String s13373 = "13373";
+ public static final String s13374 = "13374";
+ public static final String s13375 = "13375";
+ public static final String s13376 = "13376";
+ public static final String s13377 = "13377";
+ public static final String s13378 = "13378";
+ public static final String s13379 = "13379";
+ public static final String s13380 = "13380";
+ public static final String s13381 = "13381";
+ public static final String s13382 = "13382";
+ public static final String s13383 = "13383";
+ public static final String s13384 = "13384";
+ public static final String s13385 = "13385";
+ public static final String s13386 = "13386";
+ public static final String s13387 = "13387";
+ public static final String s13388 = "13388";
+ public static final String s13389 = "13389";
+ public static final String s13390 = "13390";
+ public static final String s13391 = "13391";
+ public static final String s13392 = "13392";
+ public static final String s13393 = "13393";
+ public static final String s13394 = "13394";
+ public static final String s13395 = "13395";
+ public static final String s13396 = "13396";
+ public static final String s13397 = "13397";
+ public static final String s13398 = "13398";
+ public static final String s13399 = "13399";
+ public static final String s13400 = "13400";
+ public static final String s13401 = "13401";
+ public static final String s13402 = "13402";
+ public static final String s13403 = "13403";
+ public static final String s13404 = "13404";
+ public static final String s13405 = "13405";
+ public static final String s13406 = "13406";
+ public static final String s13407 = "13407";
+ public static final String s13408 = "13408";
+ public static final String s13409 = "13409";
+ public static final String s13410 = "13410";
+ public static final String s13411 = "13411";
+ public static final String s13412 = "13412";
+ public static final String s13413 = "13413";
+ public static final String s13414 = "13414";
+ public static final String s13415 = "13415";
+ public static final String s13416 = "13416";
+ public static final String s13417 = "13417";
+ public static final String s13418 = "13418";
+ public static final String s13419 = "13419";
+ public static final String s13420 = "13420";
+ public static final String s13421 = "13421";
+ public static final String s13422 = "13422";
+ public static final String s13423 = "13423";
+ public static final String s13424 = "13424";
+ public static final String s13425 = "13425";
+ public static final String s13426 = "13426";
+ public static final String s13427 = "13427";
+ public static final String s13428 = "13428";
+ public static final String s13429 = "13429";
+ public static final String s13430 = "13430";
+ public static final String s13431 = "13431";
+ public static final String s13432 = "13432";
+ public static final String s13433 = "13433";
+ public static final String s13434 = "13434";
+ public static final String s13435 = "13435";
+ public static final String s13436 = "13436";
+ public static final String s13437 = "13437";
+ public static final String s13438 = "13438";
+ public static final String s13439 = "13439";
+ public static final String s13440 = "13440";
+ public static final String s13441 = "13441";
+ public static final String s13442 = "13442";
+ public static final String s13443 = "13443";
+ public static final String s13444 = "13444";
+ public static final String s13445 = "13445";
+ public static final String s13446 = "13446";
+ public static final String s13447 = "13447";
+ public static final String s13448 = "13448";
+ public static final String s13449 = "13449";
+ public static final String s13450 = "13450";
+ public static final String s13451 = "13451";
+ public static final String s13452 = "13452";
+ public static final String s13453 = "13453";
+ public static final String s13454 = "13454";
+ public static final String s13455 = "13455";
+ public static final String s13456 = "13456";
+ public static final String s13457 = "13457";
+ public static final String s13458 = "13458";
+ public static final String s13459 = "13459";
+ public static final String s13460 = "13460";
+ public static final String s13461 = "13461";
+ public static final String s13462 = "13462";
+ public static final String s13463 = "13463";
+ public static final String s13464 = "13464";
+ public static final String s13465 = "13465";
+ public static final String s13466 = "13466";
+ public static final String s13467 = "13467";
+ public static final String s13468 = "13468";
+ public static final String s13469 = "13469";
+ public static final String s13470 = "13470";
+ public static final String s13471 = "13471";
+ public static final String s13472 = "13472";
+ public static final String s13473 = "13473";
+ public static final String s13474 = "13474";
+ public static final String s13475 = "13475";
+ public static final String s13476 = "13476";
+ public static final String s13477 = "13477";
+ public static final String s13478 = "13478";
+ public static final String s13479 = "13479";
+ public static final String s13480 = "13480";
+ public static final String s13481 = "13481";
+ public static final String s13482 = "13482";
+ public static final String s13483 = "13483";
+ public static final String s13484 = "13484";
+ public static final String s13485 = "13485";
+ public static final String s13486 = "13486";
+ public static final String s13487 = "13487";
+ public static final String s13488 = "13488";
+ public static final String s13489 = "13489";
+ public static final String s13490 = "13490";
+ public static final String s13491 = "13491";
+ public static final String s13492 = "13492";
+ public static final String s13493 = "13493";
+ public static final String s13494 = "13494";
+ public static final String s13495 = "13495";
+ public static final String s13496 = "13496";
+ public static final String s13497 = "13497";
+ public static final String s13498 = "13498";
+ public static final String s13499 = "13499";
+ public static final String s13500 = "13500";
+ public static final String s13501 = "13501";
+ public static final String s13502 = "13502";
+ public static final String s13503 = "13503";
+ public static final String s13504 = "13504";
+ public static final String s13505 = "13505";
+ public static final String s13506 = "13506";
+ public static final String s13507 = "13507";
+ public static final String s13508 = "13508";
+ public static final String s13509 = "13509";
+ public static final String s13510 = "13510";
+ public static final String s13511 = "13511";
+ public static final String s13512 = "13512";
+ public static final String s13513 = "13513";
+ public static final String s13514 = "13514";
+ public static final String s13515 = "13515";
+ public static final String s13516 = "13516";
+ public static final String s13517 = "13517";
+ public static final String s13518 = "13518";
+ public static final String s13519 = "13519";
+ public static final String s13520 = "13520";
+ public static final String s13521 = "13521";
+ public static final String s13522 = "13522";
+ public static final String s13523 = "13523";
+ public static final String s13524 = "13524";
+ public static final String s13525 = "13525";
+ public static final String s13526 = "13526";
+ public static final String s13527 = "13527";
+ public static final String s13528 = "13528";
+ public static final String s13529 = "13529";
+ public static final String s13530 = "13530";
+ public static final String s13531 = "13531";
+ public static final String s13532 = "13532";
+ public static final String s13533 = "13533";
+ public static final String s13534 = "13534";
+ public static final String s13535 = "13535";
+ public static final String s13536 = "13536";
+ public static final String s13537 = "13537";
+ public static final String s13538 = "13538";
+ public static final String s13539 = "13539";
+ public static final String s13540 = "13540";
+ public static final String s13541 = "13541";
+ public static final String s13542 = "13542";
+ public static final String s13543 = "13543";
+ public static final String s13544 = "13544";
+ public static final String s13545 = "13545";
+ public static final String s13546 = "13546";
+ public static final String s13547 = "13547";
+ public static final String s13548 = "13548";
+ public static final String s13549 = "13549";
+ public static final String s13550 = "13550";
+ public static final String s13551 = "13551";
+ public static final String s13552 = "13552";
+ public static final String s13553 = "13553";
+ public static final String s13554 = "13554";
+ public static final String s13555 = "13555";
+ public static final String s13556 = "13556";
+ public static final String s13557 = "13557";
+ public static final String s13558 = "13558";
+ public static final String s13559 = "13559";
+ public static final String s13560 = "13560";
+ public static final String s13561 = "13561";
+ public static final String s13562 = "13562";
+ public static final String s13563 = "13563";
+ public static final String s13564 = "13564";
+ public static final String s13565 = "13565";
+ public static final String s13566 = "13566";
+ public static final String s13567 = "13567";
+ public static final String s13568 = "13568";
+ public static final String s13569 = "13569";
+ public static final String s13570 = "13570";
+ public static final String s13571 = "13571";
+ public static final String s13572 = "13572";
+ public static final String s13573 = "13573";
+ public static final String s13574 = "13574";
+ public static final String s13575 = "13575";
+ public static final String s13576 = "13576";
+ public static final String s13577 = "13577";
+ public static final String s13578 = "13578";
+ public static final String s13579 = "13579";
+ public static final String s13580 = "13580";
+ public static final String s13581 = "13581";
+ public static final String s13582 = "13582";
+ public static final String s13583 = "13583";
+ public static final String s13584 = "13584";
+ public static final String s13585 = "13585";
+ public static final String s13586 = "13586";
+ public static final String s13587 = "13587";
+ public static final String s13588 = "13588";
+ public static final String s13589 = "13589";
+ public static final String s13590 = "13590";
+ public static final String s13591 = "13591";
+ public static final String s13592 = "13592";
+ public static final String s13593 = "13593";
+ public static final String s13594 = "13594";
+ public static final String s13595 = "13595";
+ public static final String s13596 = "13596";
+ public static final String s13597 = "13597";
+ public static final String s13598 = "13598";
+ public static final String s13599 = "13599";
+ public static final String s13600 = "13600";
+ public static final String s13601 = "13601";
+ public static final String s13602 = "13602";
+ public static final String s13603 = "13603";
+ public static final String s13604 = "13604";
+ public static final String s13605 = "13605";
+ public static final String s13606 = "13606";
+ public static final String s13607 = "13607";
+ public static final String s13608 = "13608";
+ public static final String s13609 = "13609";
+ public static final String s13610 = "13610";
+ public static final String s13611 = "13611";
+ public static final String s13612 = "13612";
+ public static final String s13613 = "13613";
+ public static final String s13614 = "13614";
+ public static final String s13615 = "13615";
+ public static final String s13616 = "13616";
+ public static final String s13617 = "13617";
+ public static final String s13618 = "13618";
+ public static final String s13619 = "13619";
+ public static final String s13620 = "13620";
+ public static final String s13621 = "13621";
+ public static final String s13622 = "13622";
+ public static final String s13623 = "13623";
+ public static final String s13624 = "13624";
+ public static final String s13625 = "13625";
+ public static final String s13626 = "13626";
+ public static final String s13627 = "13627";
+ public static final String s13628 = "13628";
+ public static final String s13629 = "13629";
+ public static final String s13630 = "13630";
+ public static final String s13631 = "13631";
+ public static final String s13632 = "13632";
+ public static final String s13633 = "13633";
+ public static final String s13634 = "13634";
+ public static final String s13635 = "13635";
+ public static final String s13636 = "13636";
+ public static final String s13637 = "13637";
+ public static final String s13638 = "13638";
+ public static final String s13639 = "13639";
+ public static final String s13640 = "13640";
+ public static final String s13641 = "13641";
+ public static final String s13642 = "13642";
+ public static final String s13643 = "13643";
+ public static final String s13644 = "13644";
+ public static final String s13645 = "13645";
+ public static final String s13646 = "13646";
+ public static final String s13647 = "13647";
+ public static final String s13648 = "13648";
+ public static final String s13649 = "13649";
+ public static final String s13650 = "13650";
+ public static final String s13651 = "13651";
+ public static final String s13652 = "13652";
+ public static final String s13653 = "13653";
+ public static final String s13654 = "13654";
+ public static final String s13655 = "13655";
+ public static final String s13656 = "13656";
+ public static final String s13657 = "13657";
+ public static final String s13658 = "13658";
+ public static final String s13659 = "13659";
+ public static final String s13660 = "13660";
+ public static final String s13661 = "13661";
+ public static final String s13662 = "13662";
+ public static final String s13663 = "13663";
+ public static final String s13664 = "13664";
+ public static final String s13665 = "13665";
+ public static final String s13666 = "13666";
+ public static final String s13667 = "13667";
+ public static final String s13668 = "13668";
+ public static final String s13669 = "13669";
+ public static final String s13670 = "13670";
+ public static final String s13671 = "13671";
+ public static final String s13672 = "13672";
+ public static final String s13673 = "13673";
+ public static final String s13674 = "13674";
+ public static final String s13675 = "13675";
+ public static final String s13676 = "13676";
+ public static final String s13677 = "13677";
+ public static final String s13678 = "13678";
+ public static final String s13679 = "13679";
+ public static final String s13680 = "13680";
+ public static final String s13681 = "13681";
+ public static final String s13682 = "13682";
+ public static final String s13683 = "13683";
+ public static final String s13684 = "13684";
+ public static final String s13685 = "13685";
+ public static final String s13686 = "13686";
+ public static final String s13687 = "13687";
+ public static final String s13688 = "13688";
+ public static final String s13689 = "13689";
+ public static final String s13690 = "13690";
+ public static final String s13691 = "13691";
+ public static final String s13692 = "13692";
+ public static final String s13693 = "13693";
+ public static final String s13694 = "13694";
+ public static final String s13695 = "13695";
+ public static final String s13696 = "13696";
+ public static final String s13697 = "13697";
+ public static final String s13698 = "13698";
+ public static final String s13699 = "13699";
+ public static final String s13700 = "13700";
+ public static final String s13701 = "13701";
+ public static final String s13702 = "13702";
+ public static final String s13703 = "13703";
+ public static final String s13704 = "13704";
+ public static final String s13705 = "13705";
+ public static final String s13706 = "13706";
+ public static final String s13707 = "13707";
+ public static final String s13708 = "13708";
+ public static final String s13709 = "13709";
+ public static final String s13710 = "13710";
+ public static final String s13711 = "13711";
+ public static final String s13712 = "13712";
+ public static final String s13713 = "13713";
+ public static final String s13714 = "13714";
+ public static final String s13715 = "13715";
+ public static final String s13716 = "13716";
+ public static final String s13717 = "13717";
+ public static final String s13718 = "13718";
+ public static final String s13719 = "13719";
+ public static final String s13720 = "13720";
+ public static final String s13721 = "13721";
+ public static final String s13722 = "13722";
+ public static final String s13723 = "13723";
+ public static final String s13724 = "13724";
+ public static final String s13725 = "13725";
+ public static final String s13726 = "13726";
+ public static final String s13727 = "13727";
+ public static final String s13728 = "13728";
+ public static final String s13729 = "13729";
+ public static final String s13730 = "13730";
+ public static final String s13731 = "13731";
+ public static final String s13732 = "13732";
+ public static final String s13733 = "13733";
+ public static final String s13734 = "13734";
+ public static final String s13735 = "13735";
+ public static final String s13736 = "13736";
+ public static final String s13737 = "13737";
+ public static final String s13738 = "13738";
+ public static final String s13739 = "13739";
+ public static final String s13740 = "13740";
+ public static final String s13741 = "13741";
+ public static final String s13742 = "13742";
+ public static final String s13743 = "13743";
+ public static final String s13744 = "13744";
+ public static final String s13745 = "13745";
+ public static final String s13746 = "13746";
+ public static final String s13747 = "13747";
+ public static final String s13748 = "13748";
+ public static final String s13749 = "13749";
+ public static final String s13750 = "13750";
+ public static final String s13751 = "13751";
+ public static final String s13752 = "13752";
+ public static final String s13753 = "13753";
+ public static final String s13754 = "13754";
+ public static final String s13755 = "13755";
+ public static final String s13756 = "13756";
+ public static final String s13757 = "13757";
+ public static final String s13758 = "13758";
+ public static final String s13759 = "13759";
+ public static final String s13760 = "13760";
+ public static final String s13761 = "13761";
+ public static final String s13762 = "13762";
+ public static final String s13763 = "13763";
+ public static final String s13764 = "13764";
+ public static final String s13765 = "13765";
+ public static final String s13766 = "13766";
+ public static final String s13767 = "13767";
+ public static final String s13768 = "13768";
+ public static final String s13769 = "13769";
+ public static final String s13770 = "13770";
+ public static final String s13771 = "13771";
+ public static final String s13772 = "13772";
+ public static final String s13773 = "13773";
+ public static final String s13774 = "13774";
+ public static final String s13775 = "13775";
+ public static final String s13776 = "13776";
+ public static final String s13777 = "13777";
+ public static final String s13778 = "13778";
+ public static final String s13779 = "13779";
+ public static final String s13780 = "13780";
+ public static final String s13781 = "13781";
+ public static final String s13782 = "13782";
+ public static final String s13783 = "13783";
+ public static final String s13784 = "13784";
+ public static final String s13785 = "13785";
+ public static final String s13786 = "13786";
+ public static final String s13787 = "13787";
+ public static final String s13788 = "13788";
+ public static final String s13789 = "13789";
+ public static final String s13790 = "13790";
+ public static final String s13791 = "13791";
+ public static final String s13792 = "13792";
+ public static final String s13793 = "13793";
+ public static final String s13794 = "13794";
+ public static final String s13795 = "13795";
+ public static final String s13796 = "13796";
+ public static final String s13797 = "13797";
+ public static final String s13798 = "13798";
+ public static final String s13799 = "13799";
+ public static final String s13800 = "13800";
+ public static final String s13801 = "13801";
+ public static final String s13802 = "13802";
+ public static final String s13803 = "13803";
+ public static final String s13804 = "13804";
+ public static final String s13805 = "13805";
+ public static final String s13806 = "13806";
+ public static final String s13807 = "13807";
+ public static final String s13808 = "13808";
+ public static final String s13809 = "13809";
+ public static final String s13810 = "13810";
+ public static final String s13811 = "13811";
+ public static final String s13812 = "13812";
+ public static final String s13813 = "13813";
+ public static final String s13814 = "13814";
+ public static final String s13815 = "13815";
+ public static final String s13816 = "13816";
+ public static final String s13817 = "13817";
+ public static final String s13818 = "13818";
+ public static final String s13819 = "13819";
+ public static final String s13820 = "13820";
+ public static final String s13821 = "13821";
+ public static final String s13822 = "13822";
+ public static final String s13823 = "13823";
+ public static final String s13824 = "13824";
+ public static final String s13825 = "13825";
+ public static final String s13826 = "13826";
+ public static final String s13827 = "13827";
+ public static final String s13828 = "13828";
+ public static final String s13829 = "13829";
+ public static final String s13830 = "13830";
+ public static final String s13831 = "13831";
+ public static final String s13832 = "13832";
+ public static final String s13833 = "13833";
+ public static final String s13834 = "13834";
+ public static final String s13835 = "13835";
+ public static final String s13836 = "13836";
+ public static final String s13837 = "13837";
+ public static final String s13838 = "13838";
+ public static final String s13839 = "13839";
+ public static final String s13840 = "13840";
+ public static final String s13841 = "13841";
+ public static final String s13842 = "13842";
+ public static final String s13843 = "13843";
+ public static final String s13844 = "13844";
+ public static final String s13845 = "13845";
+ public static final String s13846 = "13846";
+ public static final String s13847 = "13847";
+ public static final String s13848 = "13848";
+ public static final String s13849 = "13849";
+ public static final String s13850 = "13850";
+ public static final String s13851 = "13851";
+ public static final String s13852 = "13852";
+ public static final String s13853 = "13853";
+ public static final String s13854 = "13854";
+ public static final String s13855 = "13855";
+ public static final String s13856 = "13856";
+ public static final String s13857 = "13857";
+ public static final String s13858 = "13858";
+ public static final String s13859 = "13859";
+ public static final String s13860 = "13860";
+ public static final String s13861 = "13861";
+ public static final String s13862 = "13862";
+ public static final String s13863 = "13863";
+ public static final String s13864 = "13864";
+ public static final String s13865 = "13865";
+ public static final String s13866 = "13866";
+ public static final String s13867 = "13867";
+ public static final String s13868 = "13868";
+ public static final String s13869 = "13869";
+ public static final String s13870 = "13870";
+ public static final String s13871 = "13871";
+ public static final String s13872 = "13872";
+ public static final String s13873 = "13873";
+ public static final String s13874 = "13874";
+ public static final String s13875 = "13875";
+ public static final String s13876 = "13876";
+ public static final String s13877 = "13877";
+ public static final String s13878 = "13878";
+ public static final String s13879 = "13879";
+ public static final String s13880 = "13880";
+ public static final String s13881 = "13881";
+ public static final String s13882 = "13882";
+ public static final String s13883 = "13883";
+ public static final String s13884 = "13884";
+ public static final String s13885 = "13885";
+ public static final String s13886 = "13886";
+ public static final String s13887 = "13887";
+ public static final String s13888 = "13888";
+ public static final String s13889 = "13889";
+ public static final String s13890 = "13890";
+ public static final String s13891 = "13891";
+ public static final String s13892 = "13892";
+ public static final String s13893 = "13893";
+ public static final String s13894 = "13894";
+ public static final String s13895 = "13895";
+ public static final String s13896 = "13896";
+ public static final String s13897 = "13897";
+ public static final String s13898 = "13898";
+ public static final String s13899 = "13899";
+ public static final String s13900 = "13900";
+ public static final String s13901 = "13901";
+ public static final String s13902 = "13902";
+ public static final String s13903 = "13903";
+ public static final String s13904 = "13904";
+ public static final String s13905 = "13905";
+ public static final String s13906 = "13906";
+ public static final String s13907 = "13907";
+ public static final String s13908 = "13908";
+ public static final String s13909 = "13909";
+ public static final String s13910 = "13910";
+ public static final String s13911 = "13911";
+ public static final String s13912 = "13912";
+ public static final String s13913 = "13913";
+ public static final String s13914 = "13914";
+ public static final String s13915 = "13915";
+ public static final String s13916 = "13916";
+ public static final String s13917 = "13917";
+ public static final String s13918 = "13918";
+ public static final String s13919 = "13919";
+ public static final String s13920 = "13920";
+ public static final String s13921 = "13921";
+ public static final String s13922 = "13922";
+ public static final String s13923 = "13923";
+ public static final String s13924 = "13924";
+ public static final String s13925 = "13925";
+ public static final String s13926 = "13926";
+ public static final String s13927 = "13927";
+ public static final String s13928 = "13928";
+ public static final String s13929 = "13929";
+ public static final String s13930 = "13930";
+ public static final String s13931 = "13931";
+ public static final String s13932 = "13932";
+ public static final String s13933 = "13933";
+ public static final String s13934 = "13934";
+ public static final String s13935 = "13935";
+ public static final String s13936 = "13936";
+ public static final String s13937 = "13937";
+ public static final String s13938 = "13938";
+ public static final String s13939 = "13939";
+ public static final String s13940 = "13940";
+ public static final String s13941 = "13941";
+ public static final String s13942 = "13942";
+ public static final String s13943 = "13943";
+ public static final String s13944 = "13944";
+ public static final String s13945 = "13945";
+ public static final String s13946 = "13946";
+ public static final String s13947 = "13947";
+ public static final String s13948 = "13948";
+ public static final String s13949 = "13949";
+ public static final String s13950 = "13950";
+ public static final String s13951 = "13951";
+ public static final String s13952 = "13952";
+ public static final String s13953 = "13953";
+ public static final String s13954 = "13954";
+ public static final String s13955 = "13955";
+ public static final String s13956 = "13956";
+ public static final String s13957 = "13957";
+ public static final String s13958 = "13958";
+ public static final String s13959 = "13959";
+ public static final String s13960 = "13960";
+ public static final String s13961 = "13961";
+ public static final String s13962 = "13962";
+ public static final String s13963 = "13963";
+ public static final String s13964 = "13964";
+ public static final String s13965 = "13965";
+ public static final String s13966 = "13966";
+ public static final String s13967 = "13967";
+ public static final String s13968 = "13968";
+ public static final String s13969 = "13969";
+ public static final String s13970 = "13970";
+ public static final String s13971 = "13971";
+ public static final String s13972 = "13972";
+ public static final String s13973 = "13973";
+ public static final String s13974 = "13974";
+ public static final String s13975 = "13975";
+ public static final String s13976 = "13976";
+ public static final String s13977 = "13977";
+ public static final String s13978 = "13978";
+ public static final String s13979 = "13979";
+ public static final String s13980 = "13980";
+ public static final String s13981 = "13981";
+ public static final String s13982 = "13982";
+ public static final String s13983 = "13983";
+ public static final String s13984 = "13984";
+ public static final String s13985 = "13985";
+ public static final String s13986 = "13986";
+ public static final String s13987 = "13987";
+ public static final String s13988 = "13988";
+ public static final String s13989 = "13989";
+ public static final String s13990 = "13990";
+ public static final String s13991 = "13991";
+ public static final String s13992 = "13992";
+ public static final String s13993 = "13993";
+ public static final String s13994 = "13994";
+ public static final String s13995 = "13995";
+ public static final String s13996 = "13996";
+ public static final String s13997 = "13997";
+ public static final String s13998 = "13998";
+ public static final String s13999 = "13999";
+ public static final String s14000 = "14000";
+ public static final String s14001 = "14001";
+ public static final String s14002 = "14002";
+ public static final String s14003 = "14003";
+ public static final String s14004 = "14004";
+ public static final String s14005 = "14005";
+ public static final String s14006 = "14006";
+ public static final String s14007 = "14007";
+ public static final String s14008 = "14008";
+ public static final String s14009 = "14009";
+ public static final String s14010 = "14010";
+ public static final String s14011 = "14011";
+ public static final String s14012 = "14012";
+ public static final String s14013 = "14013";
+ public static final String s14014 = "14014";
+ public static final String s14015 = "14015";
+ public static final String s14016 = "14016";
+ public static final String s14017 = "14017";
+ public static final String s14018 = "14018";
+ public static final String s14019 = "14019";
+ public static final String s14020 = "14020";
+ public static final String s14021 = "14021";
+ public static final String s14022 = "14022";
+ public static final String s14023 = "14023";
+ public static final String s14024 = "14024";
+ public static final String s14025 = "14025";
+ public static final String s14026 = "14026";
+ public static final String s14027 = "14027";
+ public static final String s14028 = "14028";
+ public static final String s14029 = "14029";
+ public static final String s14030 = "14030";
+ public static final String s14031 = "14031";
+ public static final String s14032 = "14032";
+ public static final String s14033 = "14033";
+ public static final String s14034 = "14034";
+ public static final String s14035 = "14035";
+ public static final String s14036 = "14036";
+ public static final String s14037 = "14037";
+ public static final String s14038 = "14038";
+ public static final String s14039 = "14039";
+ public static final String s14040 = "14040";
+ public static final String s14041 = "14041";
+ public static final String s14042 = "14042";
+ public static final String s14043 = "14043";
+ public static final String s14044 = "14044";
+ public static final String s14045 = "14045";
+ public static final String s14046 = "14046";
+ public static final String s14047 = "14047";
+ public static final String s14048 = "14048";
+ public static final String s14049 = "14049";
+ public static final String s14050 = "14050";
+ public static final String s14051 = "14051";
+ public static final String s14052 = "14052";
+ public static final String s14053 = "14053";
+ public static final String s14054 = "14054";
+ public static final String s14055 = "14055";
+ public static final String s14056 = "14056";
+ public static final String s14057 = "14057";
+ public static final String s14058 = "14058";
+ public static final String s14059 = "14059";
+ public static final String s14060 = "14060";
+ public static final String s14061 = "14061";
+ public static final String s14062 = "14062";
+ public static final String s14063 = "14063";
+ public static final String s14064 = "14064";
+ public static final String s14065 = "14065";
+ public static final String s14066 = "14066";
+ public static final String s14067 = "14067";
+ public static final String s14068 = "14068";
+ public static final String s14069 = "14069";
+ public static final String s14070 = "14070";
+ public static final String s14071 = "14071";
+ public static final String s14072 = "14072";
+ public static final String s14073 = "14073";
+ public static final String s14074 = "14074";
+ public static final String s14075 = "14075";
+ public static final String s14076 = "14076";
+ public static final String s14077 = "14077";
+ public static final String s14078 = "14078";
+ public static final String s14079 = "14079";
+ public static final String s14080 = "14080";
+ public static final String s14081 = "14081";
+ public static final String s14082 = "14082";
+ public static final String s14083 = "14083";
+ public static final String s14084 = "14084";
+ public static final String s14085 = "14085";
+ public static final String s14086 = "14086";
+ public static final String s14087 = "14087";
+ public static final String s14088 = "14088";
+ public static final String s14089 = "14089";
+ public static final String s14090 = "14090";
+ public static final String s14091 = "14091";
+ public static final String s14092 = "14092";
+ public static final String s14093 = "14093";
+ public static final String s14094 = "14094";
+ public static final String s14095 = "14095";
+ public static final String s14096 = "14096";
+ public static final String s14097 = "14097";
+ public static final String s14098 = "14098";
+ public static final String s14099 = "14099";
+ public static final String s14100 = "14100";
+ public static final String s14101 = "14101";
+ public static final String s14102 = "14102";
+ public static final String s14103 = "14103";
+ public static final String s14104 = "14104";
+ public static final String s14105 = "14105";
+ public static final String s14106 = "14106";
+ public static final String s14107 = "14107";
+ public static final String s14108 = "14108";
+ public static final String s14109 = "14109";
+ public static final String s14110 = "14110";
+ public static final String s14111 = "14111";
+ public static final String s14112 = "14112";
+ public static final String s14113 = "14113";
+ public static final String s14114 = "14114";
+ public static final String s14115 = "14115";
+ public static final String s14116 = "14116";
+ public static final String s14117 = "14117";
+ public static final String s14118 = "14118";
+ public static final String s14119 = "14119";
+ public static final String s14120 = "14120";
+ public static final String s14121 = "14121";
+ public static final String s14122 = "14122";
+ public static final String s14123 = "14123";
+ public static final String s14124 = "14124";
+ public static final String s14125 = "14125";
+ public static final String s14126 = "14126";
+ public static final String s14127 = "14127";
+ public static final String s14128 = "14128";
+ public static final String s14129 = "14129";
+ public static final String s14130 = "14130";
+ public static final String s14131 = "14131";
+ public static final String s14132 = "14132";
+ public static final String s14133 = "14133";
+ public static final String s14134 = "14134";
+ public static final String s14135 = "14135";
+ public static final String s14136 = "14136";
+ public static final String s14137 = "14137";
+ public static final String s14138 = "14138";
+ public static final String s14139 = "14139";
+ public static final String s14140 = "14140";
+ public static final String s14141 = "14141";
+ public static final String s14142 = "14142";
+ public static final String s14143 = "14143";
+ public static final String s14144 = "14144";
+ public static final String s14145 = "14145";
+ public static final String s14146 = "14146";
+ public static final String s14147 = "14147";
+ public static final String s14148 = "14148";
+ public static final String s14149 = "14149";
+ public static final String s14150 = "14150";
+ public static final String s14151 = "14151";
+ public static final String s14152 = "14152";
+ public static final String s14153 = "14153";
+ public static final String s14154 = "14154";
+ public static final String s14155 = "14155";
+ public static final String s14156 = "14156";
+ public static final String s14157 = "14157";
+ public static final String s14158 = "14158";
+ public static final String s14159 = "14159";
+ public static final String s14160 = "14160";
+ public static final String s14161 = "14161";
+ public static final String s14162 = "14162";
+ public static final String s14163 = "14163";
+ public static final String s14164 = "14164";
+ public static final String s14165 = "14165";
+ public static final String s14166 = "14166";
+ public static final String s14167 = "14167";
+ public static final String s14168 = "14168";
+ public static final String s14169 = "14169";
+ public static final String s14170 = "14170";
+ public static final String s14171 = "14171";
+ public static final String s14172 = "14172";
+ public static final String s14173 = "14173";
+ public static final String s14174 = "14174";
+ public static final String s14175 = "14175";
+ public static final String s14176 = "14176";
+ public static final String s14177 = "14177";
+ public static final String s14178 = "14178";
+ public static final String s14179 = "14179";
+ public static final String s14180 = "14180";
+ public static final String s14181 = "14181";
+ public static final String s14182 = "14182";
+ public static final String s14183 = "14183";
+ public static final String s14184 = "14184";
+ public static final String s14185 = "14185";
+ public static final String s14186 = "14186";
+ public static final String s14187 = "14187";
+ public static final String s14188 = "14188";
+ public static final String s14189 = "14189";
+ public static final String s14190 = "14190";
+ public static final String s14191 = "14191";
+ public static final String s14192 = "14192";
+ public static final String s14193 = "14193";
+ public static final String s14194 = "14194";
+ public static final String s14195 = "14195";
+ public static final String s14196 = "14196";
+ public static final String s14197 = "14197";
+ public static final String s14198 = "14198";
+ public static final String s14199 = "14199";
+ public static final String s14200 = "14200";
+ public static final String s14201 = "14201";
+ public static final String s14202 = "14202";
+ public static final String s14203 = "14203";
+ public static final String s14204 = "14204";
+ public static final String s14205 = "14205";
+ public static final String s14206 = "14206";
+ public static final String s14207 = "14207";
+ public static final String s14208 = "14208";
+ public static final String s14209 = "14209";
+ public static final String s14210 = "14210";
+ public static final String s14211 = "14211";
+ public static final String s14212 = "14212";
+ public static final String s14213 = "14213";
+ public static final String s14214 = "14214";
+ public static final String s14215 = "14215";
+ public static final String s14216 = "14216";
+ public static final String s14217 = "14217";
+ public static final String s14218 = "14218";
+ public static final String s14219 = "14219";
+ public static final String s14220 = "14220";
+ public static final String s14221 = "14221";
+ public static final String s14222 = "14222";
+ public static final String s14223 = "14223";
+ public static final String s14224 = "14224";
+ public static final String s14225 = "14225";
+ public static final String s14226 = "14226";
+ public static final String s14227 = "14227";
+ public static final String s14228 = "14228";
+ public static final String s14229 = "14229";
+ public static final String s14230 = "14230";
+ public static final String s14231 = "14231";
+ public static final String s14232 = "14232";
+ public static final String s14233 = "14233";
+ public static final String s14234 = "14234";
+ public static final String s14235 = "14235";
+ public static final String s14236 = "14236";
+ public static final String s14237 = "14237";
+ public static final String s14238 = "14238";
+ public static final String s14239 = "14239";
+ public static final String s14240 = "14240";
+ public static final String s14241 = "14241";
+ public static final String s14242 = "14242";
+ public static final String s14243 = "14243";
+ public static final String s14244 = "14244";
+ public static final String s14245 = "14245";
+ public static final String s14246 = "14246";
+ public static final String s14247 = "14247";
+ public static final String s14248 = "14248";
+ public static final String s14249 = "14249";
+ public static final String s14250 = "14250";
+ public static final String s14251 = "14251";
+ public static final String s14252 = "14252";
+ public static final String s14253 = "14253";
+ public static final String s14254 = "14254";
+ public static final String s14255 = "14255";
+ public static final String s14256 = "14256";
+ public static final String s14257 = "14257";
+ public static final String s14258 = "14258";
+ public static final String s14259 = "14259";
+ public static final String s14260 = "14260";
+ public static final String s14261 = "14261";
+ public static final String s14262 = "14262";
+ public static final String s14263 = "14263";
+ public static final String s14264 = "14264";
+ public static final String s14265 = "14265";
+ public static final String s14266 = "14266";
+ public static final String s14267 = "14267";
+ public static final String s14268 = "14268";
+ public static final String s14269 = "14269";
+ public static final String s14270 = "14270";
+ public static final String s14271 = "14271";
+ public static final String s14272 = "14272";
+ public static final String s14273 = "14273";
+ public static final String s14274 = "14274";
+ public static final String s14275 = "14275";
+ public static final String s14276 = "14276";
+ public static final String s14277 = "14277";
+ public static final String s14278 = "14278";
+ public static final String s14279 = "14279";
+ public static final String s14280 = "14280";
+ public static final String s14281 = "14281";
+ public static final String s14282 = "14282";
+ public static final String s14283 = "14283";
+ public static final String s14284 = "14284";
+ public static final String s14285 = "14285";
+ public static final String s14286 = "14286";
+ public static final String s14287 = "14287";
+ public static final String s14288 = "14288";
+ public static final String s14289 = "14289";
+ public static final String s14290 = "14290";
+ public static final String s14291 = "14291";
+ public static final String s14292 = "14292";
+ public static final String s14293 = "14293";
+ public static final String s14294 = "14294";
+ public static final String s14295 = "14295";
+ public static final String s14296 = "14296";
+ public static final String s14297 = "14297";
+ public static final String s14298 = "14298";
+ public static final String s14299 = "14299";
+ public static final String s14300 = "14300";
+ public static final String s14301 = "14301";
+ public static final String s14302 = "14302";
+ public static final String s14303 = "14303";
+ public static final String s14304 = "14304";
+ public static final String s14305 = "14305";
+ public static final String s14306 = "14306";
+ public static final String s14307 = "14307";
+ public static final String s14308 = "14308";
+ public static final String s14309 = "14309";
+ public static final String s14310 = "14310";
+ public static final String s14311 = "14311";
+ public static final String s14312 = "14312";
+ public static final String s14313 = "14313";
+ public static final String s14314 = "14314";
+ public static final String s14315 = "14315";
+ public static final String s14316 = "14316";
+ public static final String s14317 = "14317";
+ public static final String s14318 = "14318";
+ public static final String s14319 = "14319";
+ public static final String s14320 = "14320";
+ public static final String s14321 = "14321";
+ public static final String s14322 = "14322";
+ public static final String s14323 = "14323";
+ public static final String s14324 = "14324";
+ public static final String s14325 = "14325";
+ public static final String s14326 = "14326";
+ public static final String s14327 = "14327";
+ public static final String s14328 = "14328";
+ public static final String s14329 = "14329";
+ public static final String s14330 = "14330";
+ public static final String s14331 = "14331";
+ public static final String s14332 = "14332";
+ public static final String s14333 = "14333";
+ public static final String s14334 = "14334";
+ public static final String s14335 = "14335";
+ public static final String s14336 = "14336";
+ public static final String s14337 = "14337";
+ public static final String s14338 = "14338";
+ public static final String s14339 = "14339";
+ public static final String s14340 = "14340";
+ public static final String s14341 = "14341";
+ public static final String s14342 = "14342";
+ public static final String s14343 = "14343";
+ public static final String s14344 = "14344";
+ public static final String s14345 = "14345";
+ public static final String s14346 = "14346";
+ public static final String s14347 = "14347";
+ public static final String s14348 = "14348";
+ public static final String s14349 = "14349";
+ public static final String s14350 = "14350";
+ public static final String s14351 = "14351";
+ public static final String s14352 = "14352";
+ public static final String s14353 = "14353";
+ public static final String s14354 = "14354";
+ public static final String s14355 = "14355";
+ public static final String s14356 = "14356";
+ public static final String s14357 = "14357";
+ public static final String s14358 = "14358";
+ public static final String s14359 = "14359";
+ public static final String s14360 = "14360";
+ public static final String s14361 = "14361";
+ public static final String s14362 = "14362";
+ public static final String s14363 = "14363";
+ public static final String s14364 = "14364";
+ public static final String s14365 = "14365";
+ public static final String s14366 = "14366";
+ public static final String s14367 = "14367";
+ public static final String s14368 = "14368";
+ public static final String s14369 = "14369";
+ public static final String s14370 = "14370";
+ public static final String s14371 = "14371";
+ public static final String s14372 = "14372";
+ public static final String s14373 = "14373";
+ public static final String s14374 = "14374";
+ public static final String s14375 = "14375";
+ public static final String s14376 = "14376";
+ public static final String s14377 = "14377";
+ public static final String s14378 = "14378";
+ public static final String s14379 = "14379";
+ public static final String s14380 = "14380";
+ public static final String s14381 = "14381";
+ public static final String s14382 = "14382";
+ public static final String s14383 = "14383";
+ public static final String s14384 = "14384";
+ public static final String s14385 = "14385";
+ public static final String s14386 = "14386";
+ public static final String s14387 = "14387";
+ public static final String s14388 = "14388";
+ public static final String s14389 = "14389";
+ public static final String s14390 = "14390";
+ public static final String s14391 = "14391";
+ public static final String s14392 = "14392";
+ public static final String s14393 = "14393";
+ public static final String s14394 = "14394";
+ public static final String s14395 = "14395";
+ public static final String s14396 = "14396";
+ public static final String s14397 = "14397";
+ public static final String s14398 = "14398";
+ public static final String s14399 = "14399";
+ public static final String s14400 = "14400";
+ public static final String s14401 = "14401";
+ public static final String s14402 = "14402";
+ public static final String s14403 = "14403";
+ public static final String s14404 = "14404";
+ public static final String s14405 = "14405";
+ public static final String s14406 = "14406";
+ public static final String s14407 = "14407";
+ public static final String s14408 = "14408";
+ public static final String s14409 = "14409";
+ public static final String s14410 = "14410";
+ public static final String s14411 = "14411";
+ public static final String s14412 = "14412";
+ public static final String s14413 = "14413";
+ public static final String s14414 = "14414";
+ public static final String s14415 = "14415";
+ public static final String s14416 = "14416";
+ public static final String s14417 = "14417";
+ public static final String s14418 = "14418";
+ public static final String s14419 = "14419";
+ public static final String s14420 = "14420";
+ public static final String s14421 = "14421";
+ public static final String s14422 = "14422";
+ public static final String s14423 = "14423";
+ public static final String s14424 = "14424";
+ public static final String s14425 = "14425";
+ public static final String s14426 = "14426";
+ public static final String s14427 = "14427";
+ public static final String s14428 = "14428";
+ public static final String s14429 = "14429";
+ public static final String s14430 = "14430";
+ public static final String s14431 = "14431";
+ public static final String s14432 = "14432";
+ public static final String s14433 = "14433";
+ public static final String s14434 = "14434";
+ public static final String s14435 = "14435";
+ public static final String s14436 = "14436";
+ public static final String s14437 = "14437";
+ public static final String s14438 = "14438";
+ public static final String s14439 = "14439";
+ public static final String s14440 = "14440";
+ public static final String s14441 = "14441";
+ public static final String s14442 = "14442";
+ public static final String s14443 = "14443";
+ public static final String s14444 = "14444";
+ public static final String s14445 = "14445";
+ public static final String s14446 = "14446";
+ public static final String s14447 = "14447";
+ public static final String s14448 = "14448";
+ public static final String s14449 = "14449";
+ public static final String s14450 = "14450";
+ public static final String s14451 = "14451";
+ public static final String s14452 = "14452";
+ public static final String s14453 = "14453";
+ public static final String s14454 = "14454";
+ public static final String s14455 = "14455";
+ public static final String s14456 = "14456";
+ public static final String s14457 = "14457";
+ public static final String s14458 = "14458";
+ public static final String s14459 = "14459";
+ public static final String s14460 = "14460";
+ public static final String s14461 = "14461";
+ public static final String s14462 = "14462";
+ public static final String s14463 = "14463";
+ public static final String s14464 = "14464";
+ public static final String s14465 = "14465";
+ public static final String s14466 = "14466";
+ public static final String s14467 = "14467";
+ public static final String s14468 = "14468";
+ public static final String s14469 = "14469";
+ public static final String s14470 = "14470";
+ public static final String s14471 = "14471";
+ public static final String s14472 = "14472";
+ public static final String s14473 = "14473";
+ public static final String s14474 = "14474";
+ public static final String s14475 = "14475";
+ public static final String s14476 = "14476";
+ public static final String s14477 = "14477";
+ public static final String s14478 = "14478";
+ public static final String s14479 = "14479";
+ public static final String s14480 = "14480";
+ public static final String s14481 = "14481";
+ public static final String s14482 = "14482";
+ public static final String s14483 = "14483";
+ public static final String s14484 = "14484";
+ public static final String s14485 = "14485";
+ public static final String s14486 = "14486";
+ public static final String s14487 = "14487";
+ public static final String s14488 = "14488";
+ public static final String s14489 = "14489";
+ public static final String s14490 = "14490";
+ public static final String s14491 = "14491";
+ public static final String s14492 = "14492";
+ public static final String s14493 = "14493";
+ public static final String s14494 = "14494";
+ public static final String s14495 = "14495";
+ public static final String s14496 = "14496";
+ public static final String s14497 = "14497";
+ public static final String s14498 = "14498";
+ public static final String s14499 = "14499";
+ public static final String s14500 = "14500";
+ public static final String s14501 = "14501";
+ public static final String s14502 = "14502";
+ public static final String s14503 = "14503";
+ public static final String s14504 = "14504";
+ public static final String s14505 = "14505";
+ public static final String s14506 = "14506";
+ public static final String s14507 = "14507";
+ public static final String s14508 = "14508";
+ public static final String s14509 = "14509";
+ public static final String s14510 = "14510";
+ public static final String s14511 = "14511";
+ public static final String s14512 = "14512";
+ public static final String s14513 = "14513";
+ public static final String s14514 = "14514";
+ public static final String s14515 = "14515";
+ public static final String s14516 = "14516";
+ public static final String s14517 = "14517";
+ public static final String s14518 = "14518";
+ public static final String s14519 = "14519";
+ public static final String s14520 = "14520";
+ public static final String s14521 = "14521";
+ public static final String s14522 = "14522";
+ public static final String s14523 = "14523";
+ public static final String s14524 = "14524";
+ public static final String s14525 = "14525";
+ public static final String s14526 = "14526";
+ public static final String s14527 = "14527";
+ public static final String s14528 = "14528";
+ public static final String s14529 = "14529";
+ public static final String s14530 = "14530";
+ public static final String s14531 = "14531";
+ public static final String s14532 = "14532";
+ public static final String s14533 = "14533";
+ public static final String s14534 = "14534";
+ public static final String s14535 = "14535";
+ public static final String s14536 = "14536";
+ public static final String s14537 = "14537";
+ public static final String s14538 = "14538";
+ public static final String s14539 = "14539";
+ public static final String s14540 = "14540";
+ public static final String s14541 = "14541";
+ public static final String s14542 = "14542";
+ public static final String s14543 = "14543";
+ public static final String s14544 = "14544";
+ public static final String s14545 = "14545";
+ public static final String s14546 = "14546";
+ public static final String s14547 = "14547";
+ public static final String s14548 = "14548";
+ public static final String s14549 = "14549";
+ public static final String s14550 = "14550";
+ public static final String s14551 = "14551";
+ public static final String s14552 = "14552";
+ public static final String s14553 = "14553";
+ public static final String s14554 = "14554";
+ public static final String s14555 = "14555";
+ public static final String s14556 = "14556";
+ public static final String s14557 = "14557";
+ public static final String s14558 = "14558";
+ public static final String s14559 = "14559";
+ public static final String s14560 = "14560";
+ public static final String s14561 = "14561";
+ public static final String s14562 = "14562";
+ public static final String s14563 = "14563";
+ public static final String s14564 = "14564";
+ public static final String s14565 = "14565";
+ public static final String s14566 = "14566";
+ public static final String s14567 = "14567";
+ public static final String s14568 = "14568";
+ public static final String s14569 = "14569";
+ public static final String s14570 = "14570";
+ public static final String s14571 = "14571";
+ public static final String s14572 = "14572";
+ public static final String s14573 = "14573";
+ public static final String s14574 = "14574";
+ public static final String s14575 = "14575";
+ public static final String s14576 = "14576";
+ public static final String s14577 = "14577";
+ public static final String s14578 = "14578";
+ public static final String s14579 = "14579";
+ public static final String s14580 = "14580";
+ public static final String s14581 = "14581";
+ public static final String s14582 = "14582";
+ public static final String s14583 = "14583";
+ public static final String s14584 = "14584";
+ public static final String s14585 = "14585";
+ public static final String s14586 = "14586";
+ public static final String s14587 = "14587";
+ public static final String s14588 = "14588";
+ public static final String s14589 = "14589";
+ public static final String s14590 = "14590";
+ public static final String s14591 = "14591";
+ public static final String s14592 = "14592";
+ public static final String s14593 = "14593";
+ public static final String s14594 = "14594";
+ public static final String s14595 = "14595";
+ public static final String s14596 = "14596";
+ public static final String s14597 = "14597";
+ public static final String s14598 = "14598";
+ public static final String s14599 = "14599";
+ public static final String s14600 = "14600";
+ public static final String s14601 = "14601";
+ public static final String s14602 = "14602";
+ public static final String s14603 = "14603";
+ public static final String s14604 = "14604";
+ public static final String s14605 = "14605";
+ public static final String s14606 = "14606";
+ public static final String s14607 = "14607";
+ public static final String s14608 = "14608";
+ public static final String s14609 = "14609";
+ public static final String s14610 = "14610";
+ public static final String s14611 = "14611";
+ public static final String s14612 = "14612";
+ public static final String s14613 = "14613";
+ public static final String s14614 = "14614";
+ public static final String s14615 = "14615";
+ public static final String s14616 = "14616";
+ public static final String s14617 = "14617";
+ public static final String s14618 = "14618";
+ public static final String s14619 = "14619";
+ public static final String s14620 = "14620";
+ public static final String s14621 = "14621";
+ public static final String s14622 = "14622";
+ public static final String s14623 = "14623";
+ public static final String s14624 = "14624";
+ public static final String s14625 = "14625";
+ public static final String s14626 = "14626";
+ public static final String s14627 = "14627";
+ public static final String s14628 = "14628";
+ public static final String s14629 = "14629";
+ public static final String s14630 = "14630";
+ public static final String s14631 = "14631";
+ public static final String s14632 = "14632";
+ public static final String s14633 = "14633";
+ public static final String s14634 = "14634";
+ public static final String s14635 = "14635";
+ public static final String s14636 = "14636";
+ public static final String s14637 = "14637";
+ public static final String s14638 = "14638";
+ public static final String s14639 = "14639";
+ public static final String s14640 = "14640";
+ public static final String s14641 = "14641";
+ public static final String s14642 = "14642";
+ public static final String s14643 = "14643";
+ public static final String s14644 = "14644";
+ public static final String s14645 = "14645";
+ public static final String s14646 = "14646";
+ public static final String s14647 = "14647";
+ public static final String s14648 = "14648";
+ public static final String s14649 = "14649";
+ public static final String s14650 = "14650";
+ public static final String s14651 = "14651";
+ public static final String s14652 = "14652";
+ public static final String s14653 = "14653";
+ public static final String s14654 = "14654";
+ public static final String s14655 = "14655";
+ public static final String s14656 = "14656";
+ public static final String s14657 = "14657";
+ public static final String s14658 = "14658";
+ public static final String s14659 = "14659";
+ public static final String s14660 = "14660";
+ public static final String s14661 = "14661";
+ public static final String s14662 = "14662";
+ public static final String s14663 = "14663";
+ public static final String s14664 = "14664";
+ public static final String s14665 = "14665";
+ public static final String s14666 = "14666";
+ public static final String s14667 = "14667";
+ public static final String s14668 = "14668";
+ public static final String s14669 = "14669";
+ public static final String s14670 = "14670";
+ public static final String s14671 = "14671";
+ public static final String s14672 = "14672";
+ public static final String s14673 = "14673";
+ public static final String s14674 = "14674";
+ public static final String s14675 = "14675";
+ public static final String s14676 = "14676";
+ public static final String s14677 = "14677";
+ public static final String s14678 = "14678";
+ public static final String s14679 = "14679";
+ public static final String s14680 = "14680";
+ public static final String s14681 = "14681";
+ public static final String s14682 = "14682";
+ public static final String s14683 = "14683";
+ public static final String s14684 = "14684";
+ public static final String s14685 = "14685";
+ public static final String s14686 = "14686";
+ public static final String s14687 = "14687";
+ public static final String s14688 = "14688";
+ public static final String s14689 = "14689";
+ public static final String s14690 = "14690";
+ public static final String s14691 = "14691";
+ public static final String s14692 = "14692";
+ public static final String s14693 = "14693";
+ public static final String s14694 = "14694";
+ public static final String s14695 = "14695";
+ public static final String s14696 = "14696";
+ public static final String s14697 = "14697";
+ public static final String s14698 = "14698";
+ public static final String s14699 = "14699";
+ public static final String s14700 = "14700";
+ public static final String s14701 = "14701";
+ public static final String s14702 = "14702";
+ public static final String s14703 = "14703";
+ public static final String s14704 = "14704";
+ public static final String s14705 = "14705";
+ public static final String s14706 = "14706";
+ public static final String s14707 = "14707";
+ public static final String s14708 = "14708";
+ public static final String s14709 = "14709";
+ public static final String s14710 = "14710";
+ public static final String s14711 = "14711";
+ public static final String s14712 = "14712";
+ public static final String s14713 = "14713";
+ public static final String s14714 = "14714";
+ public static final String s14715 = "14715";
+ public static final String s14716 = "14716";
+ public static final String s14717 = "14717";
+ public static final String s14718 = "14718";
+ public static final String s14719 = "14719";
+ public static final String s14720 = "14720";
+ public static final String s14721 = "14721";
+ public static final String s14722 = "14722";
+ public static final String s14723 = "14723";
+ public static final String s14724 = "14724";
+ public static final String s14725 = "14725";
+ public static final String s14726 = "14726";
+ public static final String s14727 = "14727";
+ public static final String s14728 = "14728";
+ public static final String s14729 = "14729";
+ public static final String s14730 = "14730";
+ public static final String s14731 = "14731";
+ public static final String s14732 = "14732";
+ public static final String s14733 = "14733";
+ public static final String s14734 = "14734";
+ public static final String s14735 = "14735";
+ public static final String s14736 = "14736";
+ public static final String s14737 = "14737";
+ public static final String s14738 = "14738";
+ public static final String s14739 = "14739";
+ public static final String s14740 = "14740";
+ public static final String s14741 = "14741";
+ public static final String s14742 = "14742";
+ public static final String s14743 = "14743";
+ public static final String s14744 = "14744";
+ public static final String s14745 = "14745";
+ public static final String s14746 = "14746";
+ public static final String s14747 = "14747";
+ public static final String s14748 = "14748";
+ public static final String s14749 = "14749";
+ public static final String s14750 = "14750";
+ public static final String s14751 = "14751";
+ public static final String s14752 = "14752";
+ public static final String s14753 = "14753";
+ public static final String s14754 = "14754";
+ public static final String s14755 = "14755";
+ public static final String s14756 = "14756";
+ public static final String s14757 = "14757";
+ public static final String s14758 = "14758";
+ public static final String s14759 = "14759";
+ public static final String s14760 = "14760";
+ public static final String s14761 = "14761";
+ public static final String s14762 = "14762";
+ public static final String s14763 = "14763";
+ public static final String s14764 = "14764";
+ public static final String s14765 = "14765";
+ public static final String s14766 = "14766";
+ public static final String s14767 = "14767";
+ public static final String s14768 = "14768";
+ public static final String s14769 = "14769";
+ public static final String s14770 = "14770";
+ public static final String s14771 = "14771";
+ public static final String s14772 = "14772";
+ public static final String s14773 = "14773";
+ public static final String s14774 = "14774";
+ public static final String s14775 = "14775";
+ public static final String s14776 = "14776";
+ public static final String s14777 = "14777";
+ public static final String s14778 = "14778";
+ public static final String s14779 = "14779";
+ public static final String s14780 = "14780";
+ public static final String s14781 = "14781";
+ public static final String s14782 = "14782";
+ public static final String s14783 = "14783";
+ public static final String s14784 = "14784";
+ public static final String s14785 = "14785";
+ public static final String s14786 = "14786";
+ public static final String s14787 = "14787";
+ public static final String s14788 = "14788";
+ public static final String s14789 = "14789";
+ public static final String s14790 = "14790";
+ public static final String s14791 = "14791";
+ public static final String s14792 = "14792";
+ public static final String s14793 = "14793";
+ public static final String s14794 = "14794";
+ public static final String s14795 = "14795";
+ public static final String s14796 = "14796";
+ public static final String s14797 = "14797";
+ public static final String s14798 = "14798";
+ public static final String s14799 = "14799";
+ public static final String s14800 = "14800";
+ public static final String s14801 = "14801";
+ public static final String s14802 = "14802";
+ public static final String s14803 = "14803";
+ public static final String s14804 = "14804";
+ public static final String s14805 = "14805";
+ public static final String s14806 = "14806";
+ public static final String s14807 = "14807";
+ public static final String s14808 = "14808";
+ public static final String s14809 = "14809";
+ public static final String s14810 = "14810";
+ public static final String s14811 = "14811";
+ public static final String s14812 = "14812";
+ public static final String s14813 = "14813";
+ public static final String s14814 = "14814";
+ public static final String s14815 = "14815";
+ public static final String s14816 = "14816";
+ public static final String s14817 = "14817";
+ public static final String s14818 = "14818";
+ public static final String s14819 = "14819";
+ public static final String s14820 = "14820";
+ public static final String s14821 = "14821";
+ public static final String s14822 = "14822";
+ public static final String s14823 = "14823";
+ public static final String s14824 = "14824";
+ public static final String s14825 = "14825";
+ public static final String s14826 = "14826";
+ public static final String s14827 = "14827";
+ public static final String s14828 = "14828";
+ public static final String s14829 = "14829";
+ public static final String s14830 = "14830";
+ public static final String s14831 = "14831";
+ public static final String s14832 = "14832";
+ public static final String s14833 = "14833";
+ public static final String s14834 = "14834";
+ public static final String s14835 = "14835";
+ public static final String s14836 = "14836";
+ public static final String s14837 = "14837";
+ public static final String s14838 = "14838";
+ public static final String s14839 = "14839";
+ public static final String s14840 = "14840";
+ public static final String s14841 = "14841";
+ public static final String s14842 = "14842";
+ public static final String s14843 = "14843";
+ public static final String s14844 = "14844";
+ public static final String s14845 = "14845";
+ public static final String s14846 = "14846";
+ public static final String s14847 = "14847";
+ public static final String s14848 = "14848";
+ public static final String s14849 = "14849";
+ public static final String s14850 = "14850";
+ public static final String s14851 = "14851";
+ public static final String s14852 = "14852";
+ public static final String s14853 = "14853";
+ public static final String s14854 = "14854";
+ public static final String s14855 = "14855";
+ public static final String s14856 = "14856";
+ public static final String s14857 = "14857";
+ public static final String s14858 = "14858";
+ public static final String s14859 = "14859";
+ public static final String s14860 = "14860";
+ public static final String s14861 = "14861";
+ public static final String s14862 = "14862";
+ public static final String s14863 = "14863";
+ public static final String s14864 = "14864";
+ public static final String s14865 = "14865";
+ public static final String s14866 = "14866";
+ public static final String s14867 = "14867";
+ public static final String s14868 = "14868";
+ public static final String s14869 = "14869";
+ public static final String s14870 = "14870";
+ public static final String s14871 = "14871";
+ public static final String s14872 = "14872";
+ public static final String s14873 = "14873";
+ public static final String s14874 = "14874";
+ public static final String s14875 = "14875";
+ public static final String s14876 = "14876";
+ public static final String s14877 = "14877";
+ public static final String s14878 = "14878";
+ public static final String s14879 = "14879";
+ public static final String s14880 = "14880";
+ public static final String s14881 = "14881";
+ public static final String s14882 = "14882";
+ public static final String s14883 = "14883";
+ public static final String s14884 = "14884";
+ public static final String s14885 = "14885";
+ public static final String s14886 = "14886";
+ public static final String s14887 = "14887";
+ public static final String s14888 = "14888";
+ public static final String s14889 = "14889";
+ public static final String s14890 = "14890";
+ public static final String s14891 = "14891";
+ public static final String s14892 = "14892";
+ public static final String s14893 = "14893";
+ public static final String s14894 = "14894";
+ public static final String s14895 = "14895";
+ public static final String s14896 = "14896";
+ public static final String s14897 = "14897";
+ public static final String s14898 = "14898";
+ public static final String s14899 = "14899";
+ public static final String s14900 = "14900";
+ public static final String s14901 = "14901";
+ public static final String s14902 = "14902";
+ public static final String s14903 = "14903";
+ public static final String s14904 = "14904";
+ public static final String s14905 = "14905";
+ public static final String s14906 = "14906";
+ public static final String s14907 = "14907";
+ public static final String s14908 = "14908";
+ public static final String s14909 = "14909";
+ public static final String s14910 = "14910";
+ public static final String s14911 = "14911";
+ public static final String s14912 = "14912";
+ public static final String s14913 = "14913";
+ public static final String s14914 = "14914";
+ public static final String s14915 = "14915";
+ public static final String s14916 = "14916";
+ public static final String s14917 = "14917";
+ public static final String s14918 = "14918";
+ public static final String s14919 = "14919";
+ public static final String s14920 = "14920";
+ public static final String s14921 = "14921";
+ public static final String s14922 = "14922";
+ public static final String s14923 = "14923";
+ public static final String s14924 = "14924";
+ public static final String s14925 = "14925";
+ public static final String s14926 = "14926";
+ public static final String s14927 = "14927";
+ public static final String s14928 = "14928";
+ public static final String s14929 = "14929";
+ public static final String s14930 = "14930";
+ public static final String s14931 = "14931";
+ public static final String s14932 = "14932";
+ public static final String s14933 = "14933";
+ public static final String s14934 = "14934";
+ public static final String s14935 = "14935";
+ public static final String s14936 = "14936";
+ public static final String s14937 = "14937";
+ public static final String s14938 = "14938";
+ public static final String s14939 = "14939";
+ public static final String s14940 = "14940";
+ public static final String s14941 = "14941";
+ public static final String s14942 = "14942";
+ public static final String s14943 = "14943";
+ public static final String s14944 = "14944";
+ public static final String s14945 = "14945";
+ public static final String s14946 = "14946";
+ public static final String s14947 = "14947";
+ public static final String s14948 = "14948";
+ public static final String s14949 = "14949";
+ public static final String s14950 = "14950";
+ public static final String s14951 = "14951";
+ public static final String s14952 = "14952";
+ public static final String s14953 = "14953";
+ public static final String s14954 = "14954";
+ public static final String s14955 = "14955";
+ public static final String s14956 = "14956";
+ public static final String s14957 = "14957";
+ public static final String s14958 = "14958";
+ public static final String s14959 = "14959";
+ public static final String s14960 = "14960";
+ public static final String s14961 = "14961";
+ public static final String s14962 = "14962";
+ public static final String s14963 = "14963";
+ public static final String s14964 = "14964";
+ public static final String s14965 = "14965";
+ public static final String s14966 = "14966";
+ public static final String s14967 = "14967";
+ public static final String s14968 = "14968";
+ public static final String s14969 = "14969";
+ public static final String s14970 = "14970";
+ public static final String s14971 = "14971";
+ public static final String s14972 = "14972";
+ public static final String s14973 = "14973";
+ public static final String s14974 = "14974";
+ public static final String s14975 = "14975";
+ public static final String s14976 = "14976";
+ public static final String s14977 = "14977";
+ public static final String s14978 = "14978";
+ public static final String s14979 = "14979";
+ public static final String s14980 = "14980";
+ public static final String s14981 = "14981";
+ public static final String s14982 = "14982";
+ public static final String s14983 = "14983";
+ public static final String s14984 = "14984";
+ public static final String s14985 = "14985";
+ public static final String s14986 = "14986";
+ public static final String s14987 = "14987";
+ public static final String s14988 = "14988";
+ public static final String s14989 = "14989";
+ public static final String s14990 = "14990";
+ public static final String s14991 = "14991";
+ public static final String s14992 = "14992";
+ public static final String s14993 = "14993";
+ public static final String s14994 = "14994";
+ public static final String s14995 = "14995";
+ public static final String s14996 = "14996";
+ public static final String s14997 = "14997";
+ public static final String s14998 = "14998";
+ public static final String s14999 = "14999";
+ public static final String s15000 = "15000";
+ public static final String s15001 = "15001";
+ public static final String s15002 = "15002";
+ public static final String s15003 = "15003";
+ public static final String s15004 = "15004";
+ public static final String s15005 = "15005";
+ public static final String s15006 = "15006";
+ public static final String s15007 = "15007";
+ public static final String s15008 = "15008";
+ public static final String s15009 = "15009";
+ public static final String s15010 = "15010";
+ public static final String s15011 = "15011";
+ public static final String s15012 = "15012";
+ public static final String s15013 = "15013";
+ public static final String s15014 = "15014";
+ public static final String s15015 = "15015";
+ public static final String s15016 = "15016";
+ public static final String s15017 = "15017";
+ public static final String s15018 = "15018";
+ public static final String s15019 = "15019";
+ public static final String s15020 = "15020";
+ public static final String s15021 = "15021";
+ public static final String s15022 = "15022";
+ public static final String s15023 = "15023";
+ public static final String s15024 = "15024";
+ public static final String s15025 = "15025";
+ public static final String s15026 = "15026";
+ public static final String s15027 = "15027";
+ public static final String s15028 = "15028";
+ public static final String s15029 = "15029";
+ public static final String s15030 = "15030";
+ public static final String s15031 = "15031";
+ public static final String s15032 = "15032";
+ public static final String s15033 = "15033";
+ public static final String s15034 = "15034";
+ public static final String s15035 = "15035";
+ public static final String s15036 = "15036";
+ public static final String s15037 = "15037";
+ public static final String s15038 = "15038";
+ public static final String s15039 = "15039";
+ public static final String s15040 = "15040";
+ public static final String s15041 = "15041";
+ public static final String s15042 = "15042";
+ public static final String s15043 = "15043";
+ public static final String s15044 = "15044";
+ public static final String s15045 = "15045";
+ public static final String s15046 = "15046";
+ public static final String s15047 = "15047";
+ public static final String s15048 = "15048";
+ public static final String s15049 = "15049";
+ public static final String s15050 = "15050";
+ public static final String s15051 = "15051";
+ public static final String s15052 = "15052";
+ public static final String s15053 = "15053";
+ public static final String s15054 = "15054";
+ public static final String s15055 = "15055";
+ public static final String s15056 = "15056";
+ public static final String s15057 = "15057";
+ public static final String s15058 = "15058";
+ public static final String s15059 = "15059";
+ public static final String s15060 = "15060";
+ public static final String s15061 = "15061";
+ public static final String s15062 = "15062";
+ public static final String s15063 = "15063";
+ public static final String s15064 = "15064";
+ public static final String s15065 = "15065";
+ public static final String s15066 = "15066";
+ public static final String s15067 = "15067";
+ public static final String s15068 = "15068";
+ public static final String s15069 = "15069";
+ public static final String s15070 = "15070";
+ public static final String s15071 = "15071";
+ public static final String s15072 = "15072";
+ public static final String s15073 = "15073";
+ public static final String s15074 = "15074";
+ public static final String s15075 = "15075";
+ public static final String s15076 = "15076";
+ public static final String s15077 = "15077";
+ public static final String s15078 = "15078";
+ public static final String s15079 = "15079";
+ public static final String s15080 = "15080";
+ public static final String s15081 = "15081";
+ public static final String s15082 = "15082";
+ public static final String s15083 = "15083";
+ public static final String s15084 = "15084";
+ public static final String s15085 = "15085";
+ public static final String s15086 = "15086";
+ public static final String s15087 = "15087";
+ public static final String s15088 = "15088";
+ public static final String s15089 = "15089";
+ public static final String s15090 = "15090";
+ public static final String s15091 = "15091";
+ public static final String s15092 = "15092";
+ public static final String s15093 = "15093";
+ public static final String s15094 = "15094";
+ public static final String s15095 = "15095";
+ public static final String s15096 = "15096";
+ public static final String s15097 = "15097";
+ public static final String s15098 = "15098";
+ public static final String s15099 = "15099";
+ public static final String s15100 = "15100";
+ public static final String s15101 = "15101";
+ public static final String s15102 = "15102";
+ public static final String s15103 = "15103";
+ public static final String s15104 = "15104";
+ public static final String s15105 = "15105";
+ public static final String s15106 = "15106";
+ public static final String s15107 = "15107";
+ public static final String s15108 = "15108";
+ public static final String s15109 = "15109";
+ public static final String s15110 = "15110";
+ public static final String s15111 = "15111";
+ public static final String s15112 = "15112";
+ public static final String s15113 = "15113";
+ public static final String s15114 = "15114";
+ public static final String s15115 = "15115";
+ public static final String s15116 = "15116";
+ public static final String s15117 = "15117";
+ public static final String s15118 = "15118";
+ public static final String s15119 = "15119";
+ public static final String s15120 = "15120";
+ public static final String s15121 = "15121";
+ public static final String s15122 = "15122";
+ public static final String s15123 = "15123";
+ public static final String s15124 = "15124";
+ public static final String s15125 = "15125";
+ public static final String s15126 = "15126";
+ public static final String s15127 = "15127";
+ public static final String s15128 = "15128";
+ public static final String s15129 = "15129";
+ public static final String s15130 = "15130";
+ public static final String s15131 = "15131";
+ public static final String s15132 = "15132";
+ public static final String s15133 = "15133";
+ public static final String s15134 = "15134";
+ public static final String s15135 = "15135";
+ public static final String s15136 = "15136";
+ public static final String s15137 = "15137";
+ public static final String s15138 = "15138";
+ public static final String s15139 = "15139";
+ public static final String s15140 = "15140";
+ public static final String s15141 = "15141";
+ public static final String s15142 = "15142";
+ public static final String s15143 = "15143";
+ public static final String s15144 = "15144";
+ public static final String s15145 = "15145";
+ public static final String s15146 = "15146";
+ public static final String s15147 = "15147";
+ public static final String s15148 = "15148";
+ public static final String s15149 = "15149";
+ public static final String s15150 = "15150";
+ public static final String s15151 = "15151";
+ public static final String s15152 = "15152";
+ public static final String s15153 = "15153";
+ public static final String s15154 = "15154";
+ public static final String s15155 = "15155";
+ public static final String s15156 = "15156";
+ public static final String s15157 = "15157";
+ public static final String s15158 = "15158";
+ public static final String s15159 = "15159";
+ public static final String s15160 = "15160";
+ public static final String s15161 = "15161";
+ public static final String s15162 = "15162";
+ public static final String s15163 = "15163";
+ public static final String s15164 = "15164";
+ public static final String s15165 = "15165";
+ public static final String s15166 = "15166";
+ public static final String s15167 = "15167";
+ public static final String s15168 = "15168";
+ public static final String s15169 = "15169";
+ public static final String s15170 = "15170";
+ public static final String s15171 = "15171";
+ public static final String s15172 = "15172";
+ public static final String s15173 = "15173";
+ public static final String s15174 = "15174";
+ public static final String s15175 = "15175";
+ public static final String s15176 = "15176";
+ public static final String s15177 = "15177";
+ public static final String s15178 = "15178";
+ public static final String s15179 = "15179";
+ public static final String s15180 = "15180";
+ public static final String s15181 = "15181";
+ public static final String s15182 = "15182";
+ public static final String s15183 = "15183";
+ public static final String s15184 = "15184";
+ public static final String s15185 = "15185";
+ public static final String s15186 = "15186";
+ public static final String s15187 = "15187";
+ public static final String s15188 = "15188";
+ public static final String s15189 = "15189";
+ public static final String s15190 = "15190";
+ public static final String s15191 = "15191";
+ public static final String s15192 = "15192";
+ public static final String s15193 = "15193";
+ public static final String s15194 = "15194";
+ public static final String s15195 = "15195";
+ public static final String s15196 = "15196";
+ public static final String s15197 = "15197";
+ public static final String s15198 = "15198";
+ public static final String s15199 = "15199";
+ public static final String s15200 = "15200";
+ public static final String s15201 = "15201";
+ public static final String s15202 = "15202";
+ public static final String s15203 = "15203";
+ public static final String s15204 = "15204";
+ public static final String s15205 = "15205";
+ public static final String s15206 = "15206";
+ public static final String s15207 = "15207";
+ public static final String s15208 = "15208";
+ public static final String s15209 = "15209";
+ public static final String s15210 = "15210";
+ public static final String s15211 = "15211";
+ public static final String s15212 = "15212";
+ public static final String s15213 = "15213";
+ public static final String s15214 = "15214";
+ public static final String s15215 = "15215";
+ public static final String s15216 = "15216";
+ public static final String s15217 = "15217";
+ public static final String s15218 = "15218";
+ public static final String s15219 = "15219";
+ public static final String s15220 = "15220";
+ public static final String s15221 = "15221";
+ public static final String s15222 = "15222";
+ public static final String s15223 = "15223";
+ public static final String s15224 = "15224";
+ public static final String s15225 = "15225";
+ public static final String s15226 = "15226";
+ public static final String s15227 = "15227";
+ public static final String s15228 = "15228";
+ public static final String s15229 = "15229";
+ public static final String s15230 = "15230";
+ public static final String s15231 = "15231";
+ public static final String s15232 = "15232";
+ public static final String s15233 = "15233";
+ public static final String s15234 = "15234";
+ public static final String s15235 = "15235";
+ public static final String s15236 = "15236";
+ public static final String s15237 = "15237";
+ public static final String s15238 = "15238";
+ public static final String s15239 = "15239";
+ public static final String s15240 = "15240";
+ public static final String s15241 = "15241";
+ public static final String s15242 = "15242";
+ public static final String s15243 = "15243";
+ public static final String s15244 = "15244";
+ public static final String s15245 = "15245";
+ public static final String s15246 = "15246";
+ public static final String s15247 = "15247";
+ public static final String s15248 = "15248";
+ public static final String s15249 = "15249";
+ public static final String s15250 = "15250";
+ public static final String s15251 = "15251";
+ public static final String s15252 = "15252";
+ public static final String s15253 = "15253";
+ public static final String s15254 = "15254";
+ public static final String s15255 = "15255";
+ public static final String s15256 = "15256";
+ public static final String s15257 = "15257";
+ public static final String s15258 = "15258";
+ public static final String s15259 = "15259";
+ public static final String s15260 = "15260";
+ public static final String s15261 = "15261";
+ public static final String s15262 = "15262";
+ public static final String s15263 = "15263";
+ public static final String s15264 = "15264";
+ public static final String s15265 = "15265";
+ public static final String s15266 = "15266";
+ public static final String s15267 = "15267";
+ public static final String s15268 = "15268";
+ public static final String s15269 = "15269";
+ public static final String s15270 = "15270";
+ public static final String s15271 = "15271";
+ public static final String s15272 = "15272";
+ public static final String s15273 = "15273";
+ public static final String s15274 = "15274";
+ public static final String s15275 = "15275";
+ public static final String s15276 = "15276";
+ public static final String s15277 = "15277";
+ public static final String s15278 = "15278";
+ public static final String s15279 = "15279";
+ public static final String s15280 = "15280";
+ public static final String s15281 = "15281";
+ public static final String s15282 = "15282";
+ public static final String s15283 = "15283";
+ public static final String s15284 = "15284";
+ public static final String s15285 = "15285";
+ public static final String s15286 = "15286";
+ public static final String s15287 = "15287";
+ public static final String s15288 = "15288";
+ public static final String s15289 = "15289";
+ public static final String s15290 = "15290";
+ public static final String s15291 = "15291";
+ public static final String s15292 = "15292";
+ public static final String s15293 = "15293";
+ public static final String s15294 = "15294";
+ public static final String s15295 = "15295";
+ public static final String s15296 = "15296";
+ public static final String s15297 = "15297";
+ public static final String s15298 = "15298";
+ public static final String s15299 = "15299";
+ public static final String s15300 = "15300";
+ public static final String s15301 = "15301";
+ public static final String s15302 = "15302";
+ public static final String s15303 = "15303";
+ public static final String s15304 = "15304";
+ public static final String s15305 = "15305";
+ public static final String s15306 = "15306";
+ public static final String s15307 = "15307";
+ public static final String s15308 = "15308";
+ public static final String s15309 = "15309";
+ public static final String s15310 = "15310";
+ public static final String s15311 = "15311";
+ public static final String s15312 = "15312";
+ public static final String s15313 = "15313";
+ public static final String s15314 = "15314";
+ public static final String s15315 = "15315";
+ public static final String s15316 = "15316";
+ public static final String s15317 = "15317";
+ public static final String s15318 = "15318";
+ public static final String s15319 = "15319";
+ public static final String s15320 = "15320";
+ public static final String s15321 = "15321";
+ public static final String s15322 = "15322";
+ public static final String s15323 = "15323";
+ public static final String s15324 = "15324";
+ public static final String s15325 = "15325";
+ public static final String s15326 = "15326";
+ public static final String s15327 = "15327";
+ public static final String s15328 = "15328";
+ public static final String s15329 = "15329";
+ public static final String s15330 = "15330";
+ public static final String s15331 = "15331";
+ public static final String s15332 = "15332";
+ public static final String s15333 = "15333";
+ public static final String s15334 = "15334";
+ public static final String s15335 = "15335";
+ public static final String s15336 = "15336";
+ public static final String s15337 = "15337";
+ public static final String s15338 = "15338";
+ public static final String s15339 = "15339";
+ public static final String s15340 = "15340";
+ public static final String s15341 = "15341";
+ public static final String s15342 = "15342";
+ public static final String s15343 = "15343";
+ public static final String s15344 = "15344";
+ public static final String s15345 = "15345";
+ public static final String s15346 = "15346";
+ public static final String s15347 = "15347";
+ public static final String s15348 = "15348";
+ public static final String s15349 = "15349";
+ public static final String s15350 = "15350";
+ public static final String s15351 = "15351";
+ public static final String s15352 = "15352";
+ public static final String s15353 = "15353";
+ public static final String s15354 = "15354";
+ public static final String s15355 = "15355";
+ public static final String s15356 = "15356";
+ public static final String s15357 = "15357";
+ public static final String s15358 = "15358";
+ public static final String s15359 = "15359";
+ public static final String s15360 = "15360";
+ public static final String s15361 = "15361";
+ public static final String s15362 = "15362";
+ public static final String s15363 = "15363";
+ public static final String s15364 = "15364";
+ public static final String s15365 = "15365";
+ public static final String s15366 = "15366";
+ public static final String s15367 = "15367";
+ public static final String s15368 = "15368";
+ public static final String s15369 = "15369";
+ public static final String s15370 = "15370";
+ public static final String s15371 = "15371";
+ public static final String s15372 = "15372";
+ public static final String s15373 = "15373";
+ public static final String s15374 = "15374";
+ public static final String s15375 = "15375";
+ public static final String s15376 = "15376";
+ public static final String s15377 = "15377";
+ public static final String s15378 = "15378";
+ public static final String s15379 = "15379";
+ public static final String s15380 = "15380";
+ public static final String s15381 = "15381";
+ public static final String s15382 = "15382";
+ public static final String s15383 = "15383";
+ public static final String s15384 = "15384";
+ public static final String s15385 = "15385";
+ public static final String s15386 = "15386";
+ public static final String s15387 = "15387";
+ public static final String s15388 = "15388";
+ public static final String s15389 = "15389";
+ public static final String s15390 = "15390";
+ public static final String s15391 = "15391";
+ public static final String s15392 = "15392";
+ public static final String s15393 = "15393";
+ public static final String s15394 = "15394";
+ public static final String s15395 = "15395";
+ public static final String s15396 = "15396";
+ public static final String s15397 = "15397";
+ public static final String s15398 = "15398";
+ public static final String s15399 = "15399";
+ public static final String s15400 = "15400";
+ public static final String s15401 = "15401";
+ public static final String s15402 = "15402";
+ public static final String s15403 = "15403";
+ public static final String s15404 = "15404";
+ public static final String s15405 = "15405";
+ public static final String s15406 = "15406";
+ public static final String s15407 = "15407";
+ public static final String s15408 = "15408";
+ public static final String s15409 = "15409";
+ public static final String s15410 = "15410";
+ public static final String s15411 = "15411";
+ public static final String s15412 = "15412";
+ public static final String s15413 = "15413";
+ public static final String s15414 = "15414";
+ public static final String s15415 = "15415";
+ public static final String s15416 = "15416";
+ public static final String s15417 = "15417";
+ public static final String s15418 = "15418";
+ public static final String s15419 = "15419";
+ public static final String s15420 = "15420";
+ public static final String s15421 = "15421";
+ public static final String s15422 = "15422";
+ public static final String s15423 = "15423";
+ public static final String s15424 = "15424";
+ public static final String s15425 = "15425";
+ public static final String s15426 = "15426";
+ public static final String s15427 = "15427";
+ public static final String s15428 = "15428";
+ public static final String s15429 = "15429";
+ public static final String s15430 = "15430";
+ public static final String s15431 = "15431";
+ public static final String s15432 = "15432";
+ public static final String s15433 = "15433";
+ public static final String s15434 = "15434";
+ public static final String s15435 = "15435";
+ public static final String s15436 = "15436";
+ public static final String s15437 = "15437";
+ public static final String s15438 = "15438";
+ public static final String s15439 = "15439";
+ public static final String s15440 = "15440";
+ public static final String s15441 = "15441";
+ public static final String s15442 = "15442";
+ public static final String s15443 = "15443";
+ public static final String s15444 = "15444";
+ public static final String s15445 = "15445";
+ public static final String s15446 = "15446";
+ public static final String s15447 = "15447";
+ public static final String s15448 = "15448";
+ public static final String s15449 = "15449";
+ public static final String s15450 = "15450";
+ public static final String s15451 = "15451";
+ public static final String s15452 = "15452";
+ public static final String s15453 = "15453";
+ public static final String s15454 = "15454";
+ public static final String s15455 = "15455";
+ public static final String s15456 = "15456";
+ public static final String s15457 = "15457";
+ public static final String s15458 = "15458";
+ public static final String s15459 = "15459";
+ public static final String s15460 = "15460";
+ public static final String s15461 = "15461";
+ public static final String s15462 = "15462";
+ public static final String s15463 = "15463";
+ public static final String s15464 = "15464";
+ public static final String s15465 = "15465";
+ public static final String s15466 = "15466";
+ public static final String s15467 = "15467";
+ public static final String s15468 = "15468";
+ public static final String s15469 = "15469";
+ public static final String s15470 = "15470";
+ public static final String s15471 = "15471";
+ public static final String s15472 = "15472";
+ public static final String s15473 = "15473";
+ public static final String s15474 = "15474";
+ public static final String s15475 = "15475";
+ public static final String s15476 = "15476";
+ public static final String s15477 = "15477";
+ public static final String s15478 = "15478";
+ public static final String s15479 = "15479";
+ public static final String s15480 = "15480";
+ public static final String s15481 = "15481";
+ public static final String s15482 = "15482";
+ public static final String s15483 = "15483";
+ public static final String s15484 = "15484";
+ public static final String s15485 = "15485";
+ public static final String s15486 = "15486";
+ public static final String s15487 = "15487";
+ public static final String s15488 = "15488";
+ public static final String s15489 = "15489";
+ public static final String s15490 = "15490";
+ public static final String s15491 = "15491";
+ public static final String s15492 = "15492";
+ public static final String s15493 = "15493";
+ public static final String s15494 = "15494";
+ public static final String s15495 = "15495";
+ public static final String s15496 = "15496";
+ public static final String s15497 = "15497";
+ public static final String s15498 = "15498";
+ public static final String s15499 = "15499";
+ public static final String s15500 = "15500";
+ public static final String s15501 = "15501";
+ public static final String s15502 = "15502";
+ public static final String s15503 = "15503";
+ public static final String s15504 = "15504";
+ public static final String s15505 = "15505";
+ public static final String s15506 = "15506";
+ public static final String s15507 = "15507";
+ public static final String s15508 = "15508";
+ public static final String s15509 = "15509";
+ public static final String s15510 = "15510";
+ public static final String s15511 = "15511";
+ public static final String s15512 = "15512";
+ public static final String s15513 = "15513";
+ public static final String s15514 = "15514";
+ public static final String s15515 = "15515";
+ public static final String s15516 = "15516";
+ public static final String s15517 = "15517";
+ public static final String s15518 = "15518";
+ public static final String s15519 = "15519";
+ public static final String s15520 = "15520";
+ public static final String s15521 = "15521";
+ public static final String s15522 = "15522";
+ public static final String s15523 = "15523";
+ public static final String s15524 = "15524";
+ public static final String s15525 = "15525";
+ public static final String s15526 = "15526";
+ public static final String s15527 = "15527";
+ public static final String s15528 = "15528";
+ public static final String s15529 = "15529";
+ public static final String s15530 = "15530";
+ public static final String s15531 = "15531";
+ public static final String s15532 = "15532";
+ public static final String s15533 = "15533";
+ public static final String s15534 = "15534";
+ public static final String s15535 = "15535";
+ public static final String s15536 = "15536";
+ public static final String s15537 = "15537";
+ public static final String s15538 = "15538";
+ public static final String s15539 = "15539";
+ public static final String s15540 = "15540";
+ public static final String s15541 = "15541";
+ public static final String s15542 = "15542";
+ public static final String s15543 = "15543";
+ public static final String s15544 = "15544";
+ public static final String s15545 = "15545";
+ public static final String s15546 = "15546";
+ public static final String s15547 = "15547";
+ public static final String s15548 = "15548";
+ public static final String s15549 = "15549";
+ public static final String s15550 = "15550";
+ public static final String s15551 = "15551";
+ public static final String s15552 = "15552";
+ public static final String s15553 = "15553";
+ public static final String s15554 = "15554";
+ public static final String s15555 = "15555";
+ public static final String s15556 = "15556";
+ public static final String s15557 = "15557";
+ public static final String s15558 = "15558";
+ public static final String s15559 = "15559";
+ public static final String s15560 = "15560";
+ public static final String s15561 = "15561";
+ public static final String s15562 = "15562";
+ public static final String s15563 = "15563";
+ public static final String s15564 = "15564";
+ public static final String s15565 = "15565";
+ public static final String s15566 = "15566";
+ public static final String s15567 = "15567";
+ public static final String s15568 = "15568";
+ public static final String s15569 = "15569";
+ public static final String s15570 = "15570";
+ public static final String s15571 = "15571";
+ public static final String s15572 = "15572";
+ public static final String s15573 = "15573";
+ public static final String s15574 = "15574";
+ public static final String s15575 = "15575";
+ public static final String s15576 = "15576";
+ public static final String s15577 = "15577";
+ public static final String s15578 = "15578";
+ public static final String s15579 = "15579";
+ public static final String s15580 = "15580";
+ public static final String s15581 = "15581";
+ public static final String s15582 = "15582";
+ public static final String s15583 = "15583";
+ public static final String s15584 = "15584";
+ public static final String s15585 = "15585";
+ public static final String s15586 = "15586";
+ public static final String s15587 = "15587";
+ public static final String s15588 = "15588";
+ public static final String s15589 = "15589";
+ public static final String s15590 = "15590";
+ public static final String s15591 = "15591";
+ public static final String s15592 = "15592";
+ public static final String s15593 = "15593";
+ public static final String s15594 = "15594";
+ public static final String s15595 = "15595";
+ public static final String s15596 = "15596";
+ public static final String s15597 = "15597";
+ public static final String s15598 = "15598";
+ public static final String s15599 = "15599";
+ public static final String s15600 = "15600";
+ public static final String s15601 = "15601";
+ public static final String s15602 = "15602";
+ public static final String s15603 = "15603";
+ public static final String s15604 = "15604";
+ public static final String s15605 = "15605";
+ public static final String s15606 = "15606";
+ public static final String s15607 = "15607";
+ public static final String s15608 = "15608";
+ public static final String s15609 = "15609";
+ public static final String s15610 = "15610";
+ public static final String s15611 = "15611";
+ public static final String s15612 = "15612";
+ public static final String s15613 = "15613";
+ public static final String s15614 = "15614";
+ public static final String s15615 = "15615";
+ public static final String s15616 = "15616";
+ public static final String s15617 = "15617";
+ public static final String s15618 = "15618";
+ public static final String s15619 = "15619";
+ public static final String s15620 = "15620";
+ public static final String s15621 = "15621";
+ public static final String s15622 = "15622";
+ public static final String s15623 = "15623";
+ public static final String s15624 = "15624";
+ public static final String s15625 = "15625";
+ public static final String s15626 = "15626";
+ public static final String s15627 = "15627";
+ public static final String s15628 = "15628";
+ public static final String s15629 = "15629";
+ public static final String s15630 = "15630";
+ public static final String s15631 = "15631";
+ public static final String s15632 = "15632";
+ public static final String s15633 = "15633";
+ public static final String s15634 = "15634";
+ public static final String s15635 = "15635";
+ public static final String s15636 = "15636";
+ public static final String s15637 = "15637";
+ public static final String s15638 = "15638";
+ public static final String s15639 = "15639";
+ public static final String s15640 = "15640";
+ public static final String s15641 = "15641";
+ public static final String s15642 = "15642";
+ public static final String s15643 = "15643";
+ public static final String s15644 = "15644";
+ public static final String s15645 = "15645";
+ public static final String s15646 = "15646";
+ public static final String s15647 = "15647";
+ public static final String s15648 = "15648";
+ public static final String s15649 = "15649";
+ public static final String s15650 = "15650";
+ public static final String s15651 = "15651";
+ public static final String s15652 = "15652";
+ public static final String s15653 = "15653";
+ public static final String s15654 = "15654";
+ public static final String s15655 = "15655";
+ public static final String s15656 = "15656";
+ public static final String s15657 = "15657";
+ public static final String s15658 = "15658";
+ public static final String s15659 = "15659";
+ public static final String s15660 = "15660";
+ public static final String s15661 = "15661";
+ public static final String s15662 = "15662";
+ public static final String s15663 = "15663";
+ public static final String s15664 = "15664";
+ public static final String s15665 = "15665";
+ public static final String s15666 = "15666";
+ public static final String s15667 = "15667";
+ public static final String s15668 = "15668";
+ public static final String s15669 = "15669";
+ public static final String s15670 = "15670";
+ public static final String s15671 = "15671";
+ public static final String s15672 = "15672";
+ public static final String s15673 = "15673";
+ public static final String s15674 = "15674";
+ public static final String s15675 = "15675";
+ public static final String s15676 = "15676";
+ public static final String s15677 = "15677";
+ public static final String s15678 = "15678";
+ public static final String s15679 = "15679";
+ public static final String s15680 = "15680";
+ public static final String s15681 = "15681";
+ public static final String s15682 = "15682";
+ public static final String s15683 = "15683";
+ public static final String s15684 = "15684";
+ public static final String s15685 = "15685";
+ public static final String s15686 = "15686";
+ public static final String s15687 = "15687";
+ public static final String s15688 = "15688";
+ public static final String s15689 = "15689";
+ public static final String s15690 = "15690";
+ public static final String s15691 = "15691";
+ public static final String s15692 = "15692";
+ public static final String s15693 = "15693";
+ public static final String s15694 = "15694";
+ public static final String s15695 = "15695";
+ public static final String s15696 = "15696";
+ public static final String s15697 = "15697";
+ public static final String s15698 = "15698";
+ public static final String s15699 = "15699";
+ public static final String s15700 = "15700";
+ public static final String s15701 = "15701";
+ public static final String s15702 = "15702";
+ public static final String s15703 = "15703";
+ public static final String s15704 = "15704";
+ public static final String s15705 = "15705";
+ public static final String s15706 = "15706";
+ public static final String s15707 = "15707";
+ public static final String s15708 = "15708";
+ public static final String s15709 = "15709";
+ public static final String s15710 = "15710";
+ public static final String s15711 = "15711";
+ public static final String s15712 = "15712";
+ public static final String s15713 = "15713";
+ public static final String s15714 = "15714";
+ public static final String s15715 = "15715";
+ public static final String s15716 = "15716";
+ public static final String s15717 = "15717";
+ public static final String s15718 = "15718";
+ public static final String s15719 = "15719";
+ public static final String s15720 = "15720";
+ public static final String s15721 = "15721";
+ public static final String s15722 = "15722";
+ public static final String s15723 = "15723";
+ public static final String s15724 = "15724";
+ public static final String s15725 = "15725";
+ public static final String s15726 = "15726";
+ public static final String s15727 = "15727";
+ public static final String s15728 = "15728";
+ public static final String s15729 = "15729";
+ public static final String s15730 = "15730";
+ public static final String s15731 = "15731";
+ public static final String s15732 = "15732";
+ public static final String s15733 = "15733";
+ public static final String s15734 = "15734";
+ public static final String s15735 = "15735";
+ public static final String s15736 = "15736";
+ public static final String s15737 = "15737";
+ public static final String s15738 = "15738";
+ public static final String s15739 = "15739";
+ public static final String s15740 = "15740";
+ public static final String s15741 = "15741";
+ public static final String s15742 = "15742";
+ public static final String s15743 = "15743";
+ public static final String s15744 = "15744";
+ public static final String s15745 = "15745";
+ public static final String s15746 = "15746";
+ public static final String s15747 = "15747";
+ public static final String s15748 = "15748";
+ public static final String s15749 = "15749";
+ public static final String s15750 = "15750";
+ public static final String s15751 = "15751";
+ public static final String s15752 = "15752";
+ public static final String s15753 = "15753";
+ public static final String s15754 = "15754";
+ public static final String s15755 = "15755";
+ public static final String s15756 = "15756";
+ public static final String s15757 = "15757";
+ public static final String s15758 = "15758";
+ public static final String s15759 = "15759";
+ public static final String s15760 = "15760";
+ public static final String s15761 = "15761";
+ public static final String s15762 = "15762";
+ public static final String s15763 = "15763";
+ public static final String s15764 = "15764";
+ public static final String s15765 = "15765";
+ public static final String s15766 = "15766";
+ public static final String s15767 = "15767";
+ public static final String s15768 = "15768";
+ public static final String s15769 = "15769";
+ public static final String s15770 = "15770";
+ public static final String s15771 = "15771";
+ public static final String s15772 = "15772";
+ public static final String s15773 = "15773";
+ public static final String s15774 = "15774";
+ public static final String s15775 = "15775";
+ public static final String s15776 = "15776";
+ public static final String s15777 = "15777";
+ public static final String s15778 = "15778";
+ public static final String s15779 = "15779";
+ public static final String s15780 = "15780";
+ public static final String s15781 = "15781";
+ public static final String s15782 = "15782";
+ public static final String s15783 = "15783";
+ public static final String s15784 = "15784";
+ public static final String s15785 = "15785";
+ public static final String s15786 = "15786";
+ public static final String s15787 = "15787";
+ public static final String s15788 = "15788";
+ public static final String s15789 = "15789";
+ public static final String s15790 = "15790";
+ public static final String s15791 = "15791";
+ public static final String s15792 = "15792";
+ public static final String s15793 = "15793";
+ public static final String s15794 = "15794";
+ public static final String s15795 = "15795";
+ public static final String s15796 = "15796";
+ public static final String s15797 = "15797";
+ public static final String s15798 = "15798";
+ public static final String s15799 = "15799";
+ public static final String s15800 = "15800";
+ public static final String s15801 = "15801";
+ public static final String s15802 = "15802";
+ public static final String s15803 = "15803";
+ public static final String s15804 = "15804";
+ public static final String s15805 = "15805";
+ public static final String s15806 = "15806";
+ public static final String s15807 = "15807";
+ public static final String s15808 = "15808";
+ public static final String s15809 = "15809";
+ public static final String s15810 = "15810";
+ public static final String s15811 = "15811";
+ public static final String s15812 = "15812";
+ public static final String s15813 = "15813";
+ public static final String s15814 = "15814";
+ public static final String s15815 = "15815";
+ public static final String s15816 = "15816";
+ public static final String s15817 = "15817";
+ public static final String s15818 = "15818";
+ public static final String s15819 = "15819";
+ public static final String s15820 = "15820";
+ public static final String s15821 = "15821";
+ public static final String s15822 = "15822";
+ public static final String s15823 = "15823";
+ public static final String s15824 = "15824";
+ public static final String s15825 = "15825";
+ public static final String s15826 = "15826";
+ public static final String s15827 = "15827";
+ public static final String s15828 = "15828";
+ public static final String s15829 = "15829";
+ public static final String s15830 = "15830";
+ public static final String s15831 = "15831";
+ public static final String s15832 = "15832";
+ public static final String s15833 = "15833";
+ public static final String s15834 = "15834";
+ public static final String s15835 = "15835";
+ public static final String s15836 = "15836";
+ public static final String s15837 = "15837";
+ public static final String s15838 = "15838";
+ public static final String s15839 = "15839";
+ public static final String s15840 = "15840";
+ public static final String s15841 = "15841";
+ public static final String s15842 = "15842";
+ public static final String s15843 = "15843";
+ public static final String s15844 = "15844";
+ public static final String s15845 = "15845";
+ public static final String s15846 = "15846";
+ public static final String s15847 = "15847";
+ public static final String s15848 = "15848";
+ public static final String s15849 = "15849";
+ public static final String s15850 = "15850";
+ public static final String s15851 = "15851";
+ public static final String s15852 = "15852";
+ public static final String s15853 = "15853";
+ public static final String s15854 = "15854";
+ public static final String s15855 = "15855";
+ public static final String s15856 = "15856";
+ public static final String s15857 = "15857";
+ public static final String s15858 = "15858";
+ public static final String s15859 = "15859";
+ public static final String s15860 = "15860";
+ public static final String s15861 = "15861";
+ public static final String s15862 = "15862";
+ public static final String s15863 = "15863";
+ public static final String s15864 = "15864";
+ public static final String s15865 = "15865";
+ public static final String s15866 = "15866";
+ public static final String s15867 = "15867";
+ public static final String s15868 = "15868";
+ public static final String s15869 = "15869";
+ public static final String s15870 = "15870";
+ public static final String s15871 = "15871";
+ public static final String s15872 = "15872";
+ public static final String s15873 = "15873";
+ public static final String s15874 = "15874";
+ public static final String s15875 = "15875";
+ public static final String s15876 = "15876";
+ public static final String s15877 = "15877";
+ public static final String s15878 = "15878";
+ public static final String s15879 = "15879";
+ public static final String s15880 = "15880";
+ public static final String s15881 = "15881";
+ public static final String s15882 = "15882";
+ public static final String s15883 = "15883";
+ public static final String s15884 = "15884";
+ public static final String s15885 = "15885";
+ public static final String s15886 = "15886";
+ public static final String s15887 = "15887";
+ public static final String s15888 = "15888";
+ public static final String s15889 = "15889";
+ public static final String s15890 = "15890";
+ public static final String s15891 = "15891";
+ public static final String s15892 = "15892";
+ public static final String s15893 = "15893";
+ public static final String s15894 = "15894";
+ public static final String s15895 = "15895";
+ public static final String s15896 = "15896";
+ public static final String s15897 = "15897";
+ public static final String s15898 = "15898";
+ public static final String s15899 = "15899";
+ public static final String s15900 = "15900";
+ public static final String s15901 = "15901";
+ public static final String s15902 = "15902";
+ public static final String s15903 = "15903";
+ public static final String s15904 = "15904";
+ public static final String s15905 = "15905";
+ public static final String s15906 = "15906";
+ public static final String s15907 = "15907";
+ public static final String s15908 = "15908";
+ public static final String s15909 = "15909";
+ public static final String s15910 = "15910";
+ public static final String s15911 = "15911";
+ public static final String s15912 = "15912";
+ public static final String s15913 = "15913";
+ public static final String s15914 = "15914";
+ public static final String s15915 = "15915";
+ public static final String s15916 = "15916";
+ public static final String s15917 = "15917";
+ public static final String s15918 = "15918";
+ public static final String s15919 = "15919";
+ public static final String s15920 = "15920";
+ public static final String s15921 = "15921";
+ public static final String s15922 = "15922";
+ public static final String s15923 = "15923";
+ public static final String s15924 = "15924";
+ public static final String s15925 = "15925";
+ public static final String s15926 = "15926";
+ public static final String s15927 = "15927";
+ public static final String s15928 = "15928";
+ public static final String s15929 = "15929";
+ public static final String s15930 = "15930";
+ public static final String s15931 = "15931";
+ public static final String s15932 = "15932";
+ public static final String s15933 = "15933";
+ public static final String s15934 = "15934";
+ public static final String s15935 = "15935";
+ public static final String s15936 = "15936";
+ public static final String s15937 = "15937";
+ public static final String s15938 = "15938";
+ public static final String s15939 = "15939";
+ public static final String s15940 = "15940";
+ public static final String s15941 = "15941";
+ public static final String s15942 = "15942";
+ public static final String s15943 = "15943";
+ public static final String s15944 = "15944";
+ public static final String s15945 = "15945";
+ public static final String s15946 = "15946";
+ public static final String s15947 = "15947";
+ public static final String s15948 = "15948";
+ public static final String s15949 = "15949";
+ public static final String s15950 = "15950";
+ public static final String s15951 = "15951";
+ public static final String s15952 = "15952";
+ public static final String s15953 = "15953";
+ public static final String s15954 = "15954";
+ public static final String s15955 = "15955";
+ public static final String s15956 = "15956";
+ public static final String s15957 = "15957";
+ public static final String s15958 = "15958";
+ public static final String s15959 = "15959";
+ public static final String s15960 = "15960";
+ public static final String s15961 = "15961";
+ public static final String s15962 = "15962";
+ public static final String s15963 = "15963";
+ public static final String s15964 = "15964";
+ public static final String s15965 = "15965";
+ public static final String s15966 = "15966";
+ public static final String s15967 = "15967";
+ public static final String s15968 = "15968";
+ public static final String s15969 = "15969";
+ public static final String s15970 = "15970";
+ public static final String s15971 = "15971";
+ public static final String s15972 = "15972";
+ public static final String s15973 = "15973";
+ public static final String s15974 = "15974";
+ public static final String s15975 = "15975";
+ public static final String s15976 = "15976";
+ public static final String s15977 = "15977";
+ public static final String s15978 = "15978";
+ public static final String s15979 = "15979";
+ public static final String s15980 = "15980";
+ public static final String s15981 = "15981";
+ public static final String s15982 = "15982";
+ public static final String s15983 = "15983";
+ public static final String s15984 = "15984";
+ public static final String s15985 = "15985";
+ public static final String s15986 = "15986";
+ public static final String s15987 = "15987";
+ public static final String s15988 = "15988";
+ public static final String s15989 = "15989";
+ public static final String s15990 = "15990";
+ public static final String s15991 = "15991";
+ public static final String s15992 = "15992";
+ public static final String s15993 = "15993";
+ public static final String s15994 = "15994";
+ public static final String s15995 = "15995";
+ public static final String s15996 = "15996";
+ public static final String s15997 = "15997";
+ public static final String s15998 = "15998";
+ public static final String s15999 = "15999";
+ public static final String s16000 = "16000";
+ public static final String s16001 = "16001";
+ public static final String s16002 = "16002";
+ public static final String s16003 = "16003";
+ public static final String s16004 = "16004";
+ public static final String s16005 = "16005";
+ public static final String s16006 = "16006";
+ public static final String s16007 = "16007";
+ public static final String s16008 = "16008";
+ public static final String s16009 = "16009";
+ public static final String s16010 = "16010";
+ public static final String s16011 = "16011";
+ public static final String s16012 = "16012";
+ public static final String s16013 = "16013";
+ public static final String s16014 = "16014";
+ public static final String s16015 = "16015";
+ public static final String s16016 = "16016";
+ public static final String s16017 = "16017";
+ public static final String s16018 = "16018";
+ public static final String s16019 = "16019";
+ public static final String s16020 = "16020";
+ public static final String s16021 = "16021";
+ public static final String s16022 = "16022";
+ public static final String s16023 = "16023";
+ public static final String s16024 = "16024";
+ public static final String s16025 = "16025";
+ public static final String s16026 = "16026";
+ public static final String s16027 = "16027";
+ public static final String s16028 = "16028";
+ public static final String s16029 = "16029";
+ public static final String s16030 = "16030";
+ public static final String s16031 = "16031";
+ public static final String s16032 = "16032";
+ public static final String s16033 = "16033";
+ public static final String s16034 = "16034";
+ public static final String s16035 = "16035";
+ public static final String s16036 = "16036";
+ public static final String s16037 = "16037";
+ public static final String s16038 = "16038";
+ public static final String s16039 = "16039";
+ public static final String s16040 = "16040";
+ public static final String s16041 = "16041";
+ public static final String s16042 = "16042";
+ public static final String s16043 = "16043";
+ public static final String s16044 = "16044";
+ public static final String s16045 = "16045";
+ public static final String s16046 = "16046";
+ public static final String s16047 = "16047";
+ public static final String s16048 = "16048";
+ public static final String s16049 = "16049";
+ public static final String s16050 = "16050";
+ public static final String s16051 = "16051";
+ public static final String s16052 = "16052";
+ public static final String s16053 = "16053";
+ public static final String s16054 = "16054";
+ public static final String s16055 = "16055";
+ public static final String s16056 = "16056";
+ public static final String s16057 = "16057";
+ public static final String s16058 = "16058";
+ public static final String s16059 = "16059";
+ public static final String s16060 = "16060";
+ public static final String s16061 = "16061";
+ public static final String s16062 = "16062";
+ public static final String s16063 = "16063";
+ public static final String s16064 = "16064";
+ public static final String s16065 = "16065";
+ public static final String s16066 = "16066";
+ public static final String s16067 = "16067";
+ public static final String s16068 = "16068";
+ public static final String s16069 = "16069";
+ public static final String s16070 = "16070";
+ public static final String s16071 = "16071";
+ public static final String s16072 = "16072";
+ public static final String s16073 = "16073";
+ public static final String s16074 = "16074";
+ public static final String s16075 = "16075";
+ public static final String s16076 = "16076";
+ public static final String s16077 = "16077";
+ public static final String s16078 = "16078";
+ public static final String s16079 = "16079";
+ public static final String s16080 = "16080";
+ public static final String s16081 = "16081";
+ public static final String s16082 = "16082";
+ public static final String s16083 = "16083";
+ public static final String s16084 = "16084";
+ public static final String s16085 = "16085";
+ public static final String s16086 = "16086";
+ public static final String s16087 = "16087";
+ public static final String s16088 = "16088";
+ public static final String s16089 = "16089";
+ public static final String s16090 = "16090";
+ public static final String s16091 = "16091";
+ public static final String s16092 = "16092";
+ public static final String s16093 = "16093";
+ public static final String s16094 = "16094";
+ public static final String s16095 = "16095";
+ public static final String s16096 = "16096";
+ public static final String s16097 = "16097";
+ public static final String s16098 = "16098";
+ public static final String s16099 = "16099";
+ public static final String s16100 = "16100";
+ public static final String s16101 = "16101";
+ public static final String s16102 = "16102";
+ public static final String s16103 = "16103";
+ public static final String s16104 = "16104";
+ public static final String s16105 = "16105";
+ public static final String s16106 = "16106";
+ public static final String s16107 = "16107";
+ public static final String s16108 = "16108";
+ public static final String s16109 = "16109";
+ public static final String s16110 = "16110";
+ public static final String s16111 = "16111";
+ public static final String s16112 = "16112";
+ public static final String s16113 = "16113";
+ public static final String s16114 = "16114";
+ public static final String s16115 = "16115";
+ public static final String s16116 = "16116";
+ public static final String s16117 = "16117";
+ public static final String s16118 = "16118";
+ public static final String s16119 = "16119";
+ public static final String s16120 = "16120";
+ public static final String s16121 = "16121";
+ public static final String s16122 = "16122";
+ public static final String s16123 = "16123";
+ public static final String s16124 = "16124";
+ public static final String s16125 = "16125";
+ public static final String s16126 = "16126";
+ public static final String s16127 = "16127";
+ public static final String s16128 = "16128";
+ public static final String s16129 = "16129";
+ public static final String s16130 = "16130";
+ public static final String s16131 = "16131";
+ public static final String s16132 = "16132";
+ public static final String s16133 = "16133";
+ public static final String s16134 = "16134";
+ public static final String s16135 = "16135";
+ public static final String s16136 = "16136";
+ public static final String s16137 = "16137";
+ public static final String s16138 = "16138";
+ public static final String s16139 = "16139";
+ public static final String s16140 = "16140";
+ public static final String s16141 = "16141";
+ public static final String s16142 = "16142";
+ public static final String s16143 = "16143";
+ public static final String s16144 = "16144";
+ public static final String s16145 = "16145";
+ public static final String s16146 = "16146";
+ public static final String s16147 = "16147";
+ public static final String s16148 = "16148";
+ public static final String s16149 = "16149";
+ public static final String s16150 = "16150";
+ public static final String s16151 = "16151";
+ public static final String s16152 = "16152";
+ public static final String s16153 = "16153";
+ public static final String s16154 = "16154";
+ public static final String s16155 = "16155";
+ public static final String s16156 = "16156";
+ public static final String s16157 = "16157";
+ public static final String s16158 = "16158";
+ public static final String s16159 = "16159";
+ public static final String s16160 = "16160";
+ public static final String s16161 = "16161";
+ public static final String s16162 = "16162";
+ public static final String s16163 = "16163";
+ public static final String s16164 = "16164";
+ public static final String s16165 = "16165";
+ public static final String s16166 = "16166";
+ public static final String s16167 = "16167";
+ public static final String s16168 = "16168";
+ public static final String s16169 = "16169";
+ public static final String s16170 = "16170";
+ public static final String s16171 = "16171";
+ public static final String s16172 = "16172";
+ public static final String s16173 = "16173";
+ public static final String s16174 = "16174";
+ public static final String s16175 = "16175";
+ public static final String s16176 = "16176";
+ public static final String s16177 = "16177";
+ public static final String s16178 = "16178";
+ public static final String s16179 = "16179";
+ public static final String s16180 = "16180";
+ public static final String s16181 = "16181";
+ public static final String s16182 = "16182";
+ public static final String s16183 = "16183";
+ public static final String s16184 = "16184";
+ public static final String s16185 = "16185";
+ public static final String s16186 = "16186";
+ public static final String s16187 = "16187";
+ public static final String s16188 = "16188";
+ public static final String s16189 = "16189";
+ public static final String s16190 = "16190";
+ public static final String s16191 = "16191";
+ public static final String s16192 = "16192";
+ public static final String s16193 = "16193";
+ public static final String s16194 = "16194";
+ public static final String s16195 = "16195";
+ public static final String s16196 = "16196";
+ public static final String s16197 = "16197";
+ public static final String s16198 = "16198";
+ public static final String s16199 = "16199";
+ public static final String s16200 = "16200";
+ public static final String s16201 = "16201";
+ public static final String s16202 = "16202";
+ public static final String s16203 = "16203";
+ public static final String s16204 = "16204";
+ public static final String s16205 = "16205";
+ public static final String s16206 = "16206";
+ public static final String s16207 = "16207";
+ public static final String s16208 = "16208";
+ public static final String s16209 = "16209";
+ public static final String s16210 = "16210";
+ public static final String s16211 = "16211";
+ public static final String s16212 = "16212";
+ public static final String s16213 = "16213";
+ public static final String s16214 = "16214";
+ public static final String s16215 = "16215";
+ public static final String s16216 = "16216";
+ public static final String s16217 = "16217";
+ public static final String s16218 = "16218";
+ public static final String s16219 = "16219";
+ public static final String s16220 = "16220";
+ public static final String s16221 = "16221";
+ public static final String s16222 = "16222";
+ public static final String s16223 = "16223";
+ public static final String s16224 = "16224";
+ public static final String s16225 = "16225";
+ public static final String s16226 = "16226";
+ public static final String s16227 = "16227";
+ public static final String s16228 = "16228";
+ public static final String s16229 = "16229";
+ public static final String s16230 = "16230";
+ public static final String s16231 = "16231";
+ public static final String s16232 = "16232";
+ public static final String s16233 = "16233";
+ public static final String s16234 = "16234";
+ public static final String s16235 = "16235";
+ public static final String s16236 = "16236";
+ public static final String s16237 = "16237";
+ public static final String s16238 = "16238";
+ public static final String s16239 = "16239";
+ public static final String s16240 = "16240";
+ public static final String s16241 = "16241";
+ public static final String s16242 = "16242";
+ public static final String s16243 = "16243";
+ public static final String s16244 = "16244";
+ public static final String s16245 = "16245";
+ public static final String s16246 = "16246";
+ public static final String s16247 = "16247";
+ public static final String s16248 = "16248";
+ public static final String s16249 = "16249";
+ public static final String s16250 = "16250";
+ public static final String s16251 = "16251";
+ public static final String s16252 = "16252";
+ public static final String s16253 = "16253";
+ public static final String s16254 = "16254";
+ public static final String s16255 = "16255";
+ public static final String s16256 = "16256";
+ public static final String s16257 = "16257";
+ public static final String s16258 = "16258";
+ public static final String s16259 = "16259";
+ public static final String s16260 = "16260";
+ public static final String s16261 = "16261";
+ public static final String s16262 = "16262";
+ public static final String s16263 = "16263";
+ public static final String s16264 = "16264";
+ public static final String s16265 = "16265";
+ public static final String s16266 = "16266";
+ public static final String s16267 = "16267";
+ public static final String s16268 = "16268";
+ public static final String s16269 = "16269";
+ public static final String s16270 = "16270";
+ public static final String s16271 = "16271";
+ public static final String s16272 = "16272";
+ public static final String s16273 = "16273";
+ public static final String s16274 = "16274";
+ public static final String s16275 = "16275";
+ public static final String s16276 = "16276";
+ public static final String s16277 = "16277";
+ public static final String s16278 = "16278";
+ public static final String s16279 = "16279";
+ public static final String s16280 = "16280";
+ public static final String s16281 = "16281";
+ public static final String s16282 = "16282";
+ public static final String s16283 = "16283";
+ public static final String s16284 = "16284";
+ public static final String s16285 = "16285";
+ public static final String s16286 = "16286";
+ public static final String s16287 = "16287";
+ public static final String s16288 = "16288";
+ public static final String s16289 = "16289";
+ public static final String s16290 = "16290";
+ public static final String s16291 = "16291";
+ public static final String s16292 = "16292";
+ public static final String s16293 = "16293";
+ public static final String s16294 = "16294";
+ public static final String s16295 = "16295";
+ public static final String s16296 = "16296";
+ public static final String s16297 = "16297";
+ public static final String s16298 = "16298";
+ public static final String s16299 = "16299";
+ public static final String s16300 = "16300";
+ public static final String s16301 = "16301";
+ public static final String s16302 = "16302";
+ public static final String s16303 = "16303";
+ public static final String s16304 = "16304";
+ public static final String s16305 = "16305";
+ public static final String s16306 = "16306";
+ public static final String s16307 = "16307";
+ public static final String s16308 = "16308";
+ public static final String s16309 = "16309";
+ public static final String s16310 = "16310";
+ public static final String s16311 = "16311";
+ public static final String s16312 = "16312";
+ public static final String s16313 = "16313";
+ public static final String s16314 = "16314";
+ public static final String s16315 = "16315";
+ public static final String s16316 = "16316";
+ public static final String s16317 = "16317";
+ public static final String s16318 = "16318";
+ public static final String s16319 = "16319";
+ public static final String s16320 = "16320";
+ public static final String s16321 = "16321";
+ public static final String s16322 = "16322";
+ public static final String s16323 = "16323";
+ public static final String s16324 = "16324";
+ public static final String s16325 = "16325";
+ public static final String s16326 = "16326";
+ public static final String s16327 = "16327";
+ public static final String s16328 = "16328";
+ public static final String s16329 = "16329";
+ public static final String s16330 = "16330";
+ public static final String s16331 = "16331";
+ public static final String s16332 = "16332";
+ public static final String s16333 = "16333";
+ public static final String s16334 = "16334";
+ public static final String s16335 = "16335";
+ public static final String s16336 = "16336";
+ public static final String s16337 = "16337";
+ public static final String s16338 = "16338";
+ public static final String s16339 = "16339";
+ public static final String s16340 = "16340";
+ public static final String s16341 = "16341";
+ public static final String s16342 = "16342";
+ public static final String s16343 = "16343";
+ public static final String s16344 = "16344";
+ public static final String s16345 = "16345";
+ public static final String s16346 = "16346";
+ public static final String s16347 = "16347";
+ public static final String s16348 = "16348";
+ public static final String s16349 = "16349";
+ public static final String s16350 = "16350";
+ public static final String s16351 = "16351";
+ public static final String s16352 = "16352";
+ public static final String s16353 = "16353";
+ public static final String s16354 = "16354";
+ public static final String s16355 = "16355";
+ public static final String s16356 = "16356";
+ public static final String s16357 = "16357";
+ public static final String s16358 = "16358";
+ public static final String s16359 = "16359";
+ public static final String s16360 = "16360";
+ public static final String s16361 = "16361";
+ public static final String s16362 = "16362";
+ public static final String s16363 = "16363";
+ public static final String s16364 = "16364";
+ public static final String s16365 = "16365";
+ public static final String s16366 = "16366";
+ public static final String s16367 = "16367";
+ public static final String s16368 = "16368";
+ public static final String s16369 = "16369";
+ public static final String s16370 = "16370";
+ public static final String s16371 = "16371";
+ public static final String s16372 = "16372";
+ public static final String s16373 = "16373";
+ public static final String s16374 = "16374";
+ public static final String s16375 = "16375";
+ public static final String s16376 = "16376";
+ public static final String s16377 = "16377";
+ public static final String s16378 = "16378";
+ public static final String s16379 = "16379";
+ public static final String s16380 = "16380";
+ public static final String s16381 = "16381";
+ public static final String s16382 = "16382";
+ public static final String s16383 = "16383";
+}
diff --git a/src/test/examples/jumbostring/StringPool1.java b/src/test/examples/jumbostring/StringPool1.java
new file mode 100644
index 0000000..8539fb2
--- /dev/null
+++ b/src/test/examples/jumbostring/StringPool1.java
@@ -0,0 +1,16391 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package jumbostring;
+
+class StringPool1 {
+ public static final String s16384 = "16384";
+ public static final String s16385 = "16385";
+ public static final String s16386 = "16386";
+ public static final String s16387 = "16387";
+ public static final String s16388 = "16388";
+ public static final String s16389 = "16389";
+ public static final String s16390 = "16390";
+ public static final String s16391 = "16391";
+ public static final String s16392 = "16392";
+ public static final String s16393 = "16393";
+ public static final String s16394 = "16394";
+ public static final String s16395 = "16395";
+ public static final String s16396 = "16396";
+ public static final String s16397 = "16397";
+ public static final String s16398 = "16398";
+ public static final String s16399 = "16399";
+ public static final String s16400 = "16400";
+ public static final String s16401 = "16401";
+ public static final String s16402 = "16402";
+ public static final String s16403 = "16403";
+ public static final String s16404 = "16404";
+ public static final String s16405 = "16405";
+ public static final String s16406 = "16406";
+ public static final String s16407 = "16407";
+ public static final String s16408 = "16408";
+ public static final String s16409 = "16409";
+ public static final String s16410 = "16410";
+ public static final String s16411 = "16411";
+ public static final String s16412 = "16412";
+ public static final String s16413 = "16413";
+ public static final String s16414 = "16414";
+ public static final String s16415 = "16415";
+ public static final String s16416 = "16416";
+ public static final String s16417 = "16417";
+ public static final String s16418 = "16418";
+ public static final String s16419 = "16419";
+ public static final String s16420 = "16420";
+ public static final String s16421 = "16421";
+ public static final String s16422 = "16422";
+ public static final String s16423 = "16423";
+ public static final String s16424 = "16424";
+ public static final String s16425 = "16425";
+ public static final String s16426 = "16426";
+ public static final String s16427 = "16427";
+ public static final String s16428 = "16428";
+ public static final String s16429 = "16429";
+ public static final String s16430 = "16430";
+ public static final String s16431 = "16431";
+ public static final String s16432 = "16432";
+ public static final String s16433 = "16433";
+ public static final String s16434 = "16434";
+ public static final String s16435 = "16435";
+ public static final String s16436 = "16436";
+ public static final String s16437 = "16437";
+ public static final String s16438 = "16438";
+ public static final String s16439 = "16439";
+ public static final String s16440 = "16440";
+ public static final String s16441 = "16441";
+ public static final String s16442 = "16442";
+ public static final String s16443 = "16443";
+ public static final String s16444 = "16444";
+ public static final String s16445 = "16445";
+ public static final String s16446 = "16446";
+ public static final String s16447 = "16447";
+ public static final String s16448 = "16448";
+ public static final String s16449 = "16449";
+ public static final String s16450 = "16450";
+ public static final String s16451 = "16451";
+ public static final String s16452 = "16452";
+ public static final String s16453 = "16453";
+ public static final String s16454 = "16454";
+ public static final String s16455 = "16455";
+ public static final String s16456 = "16456";
+ public static final String s16457 = "16457";
+ public static final String s16458 = "16458";
+ public static final String s16459 = "16459";
+ public static final String s16460 = "16460";
+ public static final String s16461 = "16461";
+ public static final String s16462 = "16462";
+ public static final String s16463 = "16463";
+ public static final String s16464 = "16464";
+ public static final String s16465 = "16465";
+ public static final String s16466 = "16466";
+ public static final String s16467 = "16467";
+ public static final String s16468 = "16468";
+ public static final String s16469 = "16469";
+ public static final String s16470 = "16470";
+ public static final String s16471 = "16471";
+ public static final String s16472 = "16472";
+ public static final String s16473 = "16473";
+ public static final String s16474 = "16474";
+ public static final String s16475 = "16475";
+ public static final String s16476 = "16476";
+ public static final String s16477 = "16477";
+ public static final String s16478 = "16478";
+ public static final String s16479 = "16479";
+ public static final String s16480 = "16480";
+ public static final String s16481 = "16481";
+ public static final String s16482 = "16482";
+ public static final String s16483 = "16483";
+ public static final String s16484 = "16484";
+ public static final String s16485 = "16485";
+ public static final String s16486 = "16486";
+ public static final String s16487 = "16487";
+ public static final String s16488 = "16488";
+ public static final String s16489 = "16489";
+ public static final String s16490 = "16490";
+ public static final String s16491 = "16491";
+ public static final String s16492 = "16492";
+ public static final String s16493 = "16493";
+ public static final String s16494 = "16494";
+ public static final String s16495 = "16495";
+ public static final String s16496 = "16496";
+ public static final String s16497 = "16497";
+ public static final String s16498 = "16498";
+ public static final String s16499 = "16499";
+ public static final String s16500 = "16500";
+ public static final String s16501 = "16501";
+ public static final String s16502 = "16502";
+ public static final String s16503 = "16503";
+ public static final String s16504 = "16504";
+ public static final String s16505 = "16505";
+ public static final String s16506 = "16506";
+ public static final String s16507 = "16507";
+ public static final String s16508 = "16508";
+ public static final String s16509 = "16509";
+ public static final String s16510 = "16510";
+ public static final String s16511 = "16511";
+ public static final String s16512 = "16512";
+ public static final String s16513 = "16513";
+ public static final String s16514 = "16514";
+ public static final String s16515 = "16515";
+ public static final String s16516 = "16516";
+ public static final String s16517 = "16517";
+ public static final String s16518 = "16518";
+ public static final String s16519 = "16519";
+ public static final String s16520 = "16520";
+ public static final String s16521 = "16521";
+ public static final String s16522 = "16522";
+ public static final String s16523 = "16523";
+ public static final String s16524 = "16524";
+ public static final String s16525 = "16525";
+ public static final String s16526 = "16526";
+ public static final String s16527 = "16527";
+ public static final String s16528 = "16528";
+ public static final String s16529 = "16529";
+ public static final String s16530 = "16530";
+ public static final String s16531 = "16531";
+ public static final String s16532 = "16532";
+ public static final String s16533 = "16533";
+ public static final String s16534 = "16534";
+ public static final String s16535 = "16535";
+ public static final String s16536 = "16536";
+ public static final String s16537 = "16537";
+ public static final String s16538 = "16538";
+ public static final String s16539 = "16539";
+ public static final String s16540 = "16540";
+ public static final String s16541 = "16541";
+ public static final String s16542 = "16542";
+ public static final String s16543 = "16543";
+ public static final String s16544 = "16544";
+ public static final String s16545 = "16545";
+ public static final String s16546 = "16546";
+ public static final String s16547 = "16547";
+ public static final String s16548 = "16548";
+ public static final String s16549 = "16549";
+ public static final String s16550 = "16550";
+ public static final String s16551 = "16551";
+ public static final String s16552 = "16552";
+ public static final String s16553 = "16553";
+ public static final String s16554 = "16554";
+ public static final String s16555 = "16555";
+ public static final String s16556 = "16556";
+ public static final String s16557 = "16557";
+ public static final String s16558 = "16558";
+ public static final String s16559 = "16559";
+ public static final String s16560 = "16560";
+ public static final String s16561 = "16561";
+ public static final String s16562 = "16562";
+ public static final String s16563 = "16563";
+ public static final String s16564 = "16564";
+ public static final String s16565 = "16565";
+ public static final String s16566 = "16566";
+ public static final String s16567 = "16567";
+ public static final String s16568 = "16568";
+ public static final String s16569 = "16569";
+ public static final String s16570 = "16570";
+ public static final String s16571 = "16571";
+ public static final String s16572 = "16572";
+ public static final String s16573 = "16573";
+ public static final String s16574 = "16574";
+ public static final String s16575 = "16575";
+ public static final String s16576 = "16576";
+ public static final String s16577 = "16577";
+ public static final String s16578 = "16578";
+ public static final String s16579 = "16579";
+ public static final String s16580 = "16580";
+ public static final String s16581 = "16581";
+ public static final String s16582 = "16582";
+ public static final String s16583 = "16583";
+ public static final String s16584 = "16584";
+ public static final String s16585 = "16585";
+ public static final String s16586 = "16586";
+ public static final String s16587 = "16587";
+ public static final String s16588 = "16588";
+ public static final String s16589 = "16589";
+ public static final String s16590 = "16590";
+ public static final String s16591 = "16591";
+ public static final String s16592 = "16592";
+ public static final String s16593 = "16593";
+ public static final String s16594 = "16594";
+ public static final String s16595 = "16595";
+ public static final String s16596 = "16596";
+ public static final String s16597 = "16597";
+ public static final String s16598 = "16598";
+ public static final String s16599 = "16599";
+ public static final String s16600 = "16600";
+ public static final String s16601 = "16601";
+ public static final String s16602 = "16602";
+ public static final String s16603 = "16603";
+ public static final String s16604 = "16604";
+ public static final String s16605 = "16605";
+ public static final String s16606 = "16606";
+ public static final String s16607 = "16607";
+ public static final String s16608 = "16608";
+ public static final String s16609 = "16609";
+ public static final String s16610 = "16610";
+ public static final String s16611 = "16611";
+ public static final String s16612 = "16612";
+ public static final String s16613 = "16613";
+ public static final String s16614 = "16614";
+ public static final String s16615 = "16615";
+ public static final String s16616 = "16616";
+ public static final String s16617 = "16617";
+ public static final String s16618 = "16618";
+ public static final String s16619 = "16619";
+ public static final String s16620 = "16620";
+ public static final String s16621 = "16621";
+ public static final String s16622 = "16622";
+ public static final String s16623 = "16623";
+ public static final String s16624 = "16624";
+ public static final String s16625 = "16625";
+ public static final String s16626 = "16626";
+ public static final String s16627 = "16627";
+ public static final String s16628 = "16628";
+ public static final String s16629 = "16629";
+ public static final String s16630 = "16630";
+ public static final String s16631 = "16631";
+ public static final String s16632 = "16632";
+ public static final String s16633 = "16633";
+ public static final String s16634 = "16634";
+ public static final String s16635 = "16635";
+ public static final String s16636 = "16636";
+ public static final String s16637 = "16637";
+ public static final String s16638 = "16638";
+ public static final String s16639 = "16639";
+ public static final String s16640 = "16640";
+ public static final String s16641 = "16641";
+ public static final String s16642 = "16642";
+ public static final String s16643 = "16643";
+ public static final String s16644 = "16644";
+ public static final String s16645 = "16645";
+ public static final String s16646 = "16646";
+ public static final String s16647 = "16647";
+ public static final String s16648 = "16648";
+ public static final String s16649 = "16649";
+ public static final String s16650 = "16650";
+ public static final String s16651 = "16651";
+ public static final String s16652 = "16652";
+ public static final String s16653 = "16653";
+ public static final String s16654 = "16654";
+ public static final String s16655 = "16655";
+ public static final String s16656 = "16656";
+ public static final String s16657 = "16657";
+ public static final String s16658 = "16658";
+ public static final String s16659 = "16659";
+ public static final String s16660 = "16660";
+ public static final String s16661 = "16661";
+ public static final String s16662 = "16662";
+ public static final String s16663 = "16663";
+ public static final String s16664 = "16664";
+ public static final String s16665 = "16665";
+ public static final String s16666 = "16666";
+ public static final String s16667 = "16667";
+ public static final String s16668 = "16668";
+ public static final String s16669 = "16669";
+ public static final String s16670 = "16670";
+ public static final String s16671 = "16671";
+ public static final String s16672 = "16672";
+ public static final String s16673 = "16673";
+ public static final String s16674 = "16674";
+ public static final String s16675 = "16675";
+ public static final String s16676 = "16676";
+ public static final String s16677 = "16677";
+ public static final String s16678 = "16678";
+ public static final String s16679 = "16679";
+ public static final String s16680 = "16680";
+ public static final String s16681 = "16681";
+ public static final String s16682 = "16682";
+ public static final String s16683 = "16683";
+ public static final String s16684 = "16684";
+ public static final String s16685 = "16685";
+ public static final String s16686 = "16686";
+ public static final String s16687 = "16687";
+ public static final String s16688 = "16688";
+ public static final String s16689 = "16689";
+ public static final String s16690 = "16690";
+ public static final String s16691 = "16691";
+ public static final String s16692 = "16692";
+ public static final String s16693 = "16693";
+ public static final String s16694 = "16694";
+ public static final String s16695 = "16695";
+ public static final String s16696 = "16696";
+ public static final String s16697 = "16697";
+ public static final String s16698 = "16698";
+ public static final String s16699 = "16699";
+ public static final String s16700 = "16700";
+ public static final String s16701 = "16701";
+ public static final String s16702 = "16702";
+ public static final String s16703 = "16703";
+ public static final String s16704 = "16704";
+ public static final String s16705 = "16705";
+ public static final String s16706 = "16706";
+ public static final String s16707 = "16707";
+ public static final String s16708 = "16708";
+ public static final String s16709 = "16709";
+ public static final String s16710 = "16710";
+ public static final String s16711 = "16711";
+ public static final String s16712 = "16712";
+ public static final String s16713 = "16713";
+ public static final String s16714 = "16714";
+ public static final String s16715 = "16715";
+ public static final String s16716 = "16716";
+ public static final String s16717 = "16717";
+ public static final String s16718 = "16718";
+ public static final String s16719 = "16719";
+ public static final String s16720 = "16720";
+ public static final String s16721 = "16721";
+ public static final String s16722 = "16722";
+ public static final String s16723 = "16723";
+ public static final String s16724 = "16724";
+ public static final String s16725 = "16725";
+ public static final String s16726 = "16726";
+ public static final String s16727 = "16727";
+ public static final String s16728 = "16728";
+ public static final String s16729 = "16729";
+ public static final String s16730 = "16730";
+ public static final String s16731 = "16731";
+ public static final String s16732 = "16732";
+ public static final String s16733 = "16733";
+ public static final String s16734 = "16734";
+ public static final String s16735 = "16735";
+ public static final String s16736 = "16736";
+ public static final String s16737 = "16737";
+ public static final String s16738 = "16738";
+ public static final String s16739 = "16739";
+ public static final String s16740 = "16740";
+ public static final String s16741 = "16741";
+ public static final String s16742 = "16742";
+ public static final String s16743 = "16743";
+ public static final String s16744 = "16744";
+ public static final String s16745 = "16745";
+ public static final String s16746 = "16746";
+ public static final String s16747 = "16747";
+ public static final String s16748 = "16748";
+ public static final String s16749 = "16749";
+ public static final String s16750 = "16750";
+ public static final String s16751 = "16751";
+ public static final String s16752 = "16752";
+ public static final String s16753 = "16753";
+ public static final String s16754 = "16754";
+ public static final String s16755 = "16755";
+ public static final String s16756 = "16756";
+ public static final String s16757 = "16757";
+ public static final String s16758 = "16758";
+ public static final String s16759 = "16759";
+ public static final String s16760 = "16760";
+ public static final String s16761 = "16761";
+ public static final String s16762 = "16762";
+ public static final String s16763 = "16763";
+ public static final String s16764 = "16764";
+ public static final String s16765 = "16765";
+ public static final String s16766 = "16766";
+ public static final String s16767 = "16767";
+ public static final String s16768 = "16768";
+ public static final String s16769 = "16769";
+ public static final String s16770 = "16770";
+ public static final String s16771 = "16771";
+ public static final String s16772 = "16772";
+ public static final String s16773 = "16773";
+ public static final String s16774 = "16774";
+ public static final String s16775 = "16775";
+ public static final String s16776 = "16776";
+ public static final String s16777 = "16777";
+ public static final String s16778 = "16778";
+ public static final String s16779 = "16779";
+ public static final String s16780 = "16780";
+ public static final String s16781 = "16781";
+ public static final String s16782 = "16782";
+ public static final String s16783 = "16783";
+ public static final String s16784 = "16784";
+ public static final String s16785 = "16785";
+ public static final String s16786 = "16786";
+ public static final String s16787 = "16787";
+ public static final String s16788 = "16788";
+ public static final String s16789 = "16789";
+ public static final String s16790 = "16790";
+ public static final String s16791 = "16791";
+ public static final String s16792 = "16792";
+ public static final String s16793 = "16793";
+ public static final String s16794 = "16794";
+ public static final String s16795 = "16795";
+ public static final String s16796 = "16796";
+ public static final String s16797 = "16797";
+ public static final String s16798 = "16798";
+ public static final String s16799 = "16799";
+ public static final String s16800 = "16800";
+ public static final String s16801 = "16801";
+ public static final String s16802 = "16802";
+ public static final String s16803 = "16803";
+ public static final String s16804 = "16804";
+ public static final String s16805 = "16805";
+ public static final String s16806 = "16806";
+ public static final String s16807 = "16807";
+ public static final String s16808 = "16808";
+ public static final String s16809 = "16809";
+ public static final String s16810 = "16810";
+ public static final String s16811 = "16811";
+ public static final String s16812 = "16812";
+ public static final String s16813 = "16813";
+ public static final String s16814 = "16814";
+ public static final String s16815 = "16815";
+ public static final String s16816 = "16816";
+ public static final String s16817 = "16817";
+ public static final String s16818 = "16818";
+ public static final String s16819 = "16819";
+ public static final String s16820 = "16820";
+ public static final String s16821 = "16821";
+ public static final String s16822 = "16822";
+ public static final String s16823 = "16823";
+ public static final String s16824 = "16824";
+ public static final String s16825 = "16825";
+ public static final String s16826 = "16826";
+ public static final String s16827 = "16827";
+ public static final String s16828 = "16828";
+ public static final String s16829 = "16829";
+ public static final String s16830 = "16830";
+ public static final String s16831 = "16831";
+ public static final String s16832 = "16832";
+ public static final String s16833 = "16833";
+ public static final String s16834 = "16834";
+ public static final String s16835 = "16835";
+ public static final String s16836 = "16836";
+ public static final String s16837 = "16837";
+ public static final String s16838 = "16838";
+ public static final String s16839 = "16839";
+ public static final String s16840 = "16840";
+ public static final String s16841 = "16841";
+ public static final String s16842 = "16842";
+ public static final String s16843 = "16843";
+ public static final String s16844 = "16844";
+ public static final String s16845 = "16845";
+ public static final String s16846 = "16846";
+ public static final String s16847 = "16847";
+ public static final String s16848 = "16848";
+ public static final String s16849 = "16849";
+ public static final String s16850 = "16850";
+ public static final String s16851 = "16851";
+ public static final String s16852 = "16852";
+ public static final String s16853 = "16853";
+ public static final String s16854 = "16854";
+ public static final String s16855 = "16855";
+ public static final String s16856 = "16856";
+ public static final String s16857 = "16857";
+ public static final String s16858 = "16858";
+ public static final String s16859 = "16859";
+ public static final String s16860 = "16860";
+ public static final String s16861 = "16861";
+ public static final String s16862 = "16862";
+ public static final String s16863 = "16863";
+ public static final String s16864 = "16864";
+ public static final String s16865 = "16865";
+ public static final String s16866 = "16866";
+ public static final String s16867 = "16867";
+ public static final String s16868 = "16868";
+ public static final String s16869 = "16869";
+ public static final String s16870 = "16870";
+ public static final String s16871 = "16871";
+ public static final String s16872 = "16872";
+ public static final String s16873 = "16873";
+ public static final String s16874 = "16874";
+ public static final String s16875 = "16875";
+ public static final String s16876 = "16876";
+ public static final String s16877 = "16877";
+ public static final String s16878 = "16878";
+ public static final String s16879 = "16879";
+ public static final String s16880 = "16880";
+ public static final String s16881 = "16881";
+ public static final String s16882 = "16882";
+ public static final String s16883 = "16883";
+ public static final String s16884 = "16884";
+ public static final String s16885 = "16885";
+ public static final String s16886 = "16886";
+ public static final String s16887 = "16887";
+ public static final String s16888 = "16888";
+ public static final String s16889 = "16889";
+ public static final String s16890 = "16890";
+ public static final String s16891 = "16891";
+ public static final String s16892 = "16892";
+ public static final String s16893 = "16893";
+ public static final String s16894 = "16894";
+ public static final String s16895 = "16895";
+ public static final String s16896 = "16896";
+ public static final String s16897 = "16897";
+ public static final String s16898 = "16898";
+ public static final String s16899 = "16899";
+ public static final String s16900 = "16900";
+ public static final String s16901 = "16901";
+ public static final String s16902 = "16902";
+ public static final String s16903 = "16903";
+ public static final String s16904 = "16904";
+ public static final String s16905 = "16905";
+ public static final String s16906 = "16906";
+ public static final String s16907 = "16907";
+ public static final String s16908 = "16908";
+ public static final String s16909 = "16909";
+ public static final String s16910 = "16910";
+ public static final String s16911 = "16911";
+ public static final String s16912 = "16912";
+ public static final String s16913 = "16913";
+ public static final String s16914 = "16914";
+ public static final String s16915 = "16915";
+ public static final String s16916 = "16916";
+ public static final String s16917 = "16917";
+ public static final String s16918 = "16918";
+ public static final String s16919 = "16919";
+ public static final String s16920 = "16920";
+ public static final String s16921 = "16921";
+ public static final String s16922 = "16922";
+ public static final String s16923 = "16923";
+ public static final String s16924 = "16924";
+ public static final String s16925 = "16925";
+ public static final String s16926 = "16926";
+ public static final String s16927 = "16927";
+ public static final String s16928 = "16928";
+ public static final String s16929 = "16929";
+ public static final String s16930 = "16930";
+ public static final String s16931 = "16931";
+ public static final String s16932 = "16932";
+ public static final String s16933 = "16933";
+ public static final String s16934 = "16934";
+ public static final String s16935 = "16935";
+ public static final String s16936 = "16936";
+ public static final String s16937 = "16937";
+ public static final String s16938 = "16938";
+ public static final String s16939 = "16939";
+ public static final String s16940 = "16940";
+ public static final String s16941 = "16941";
+ public static final String s16942 = "16942";
+ public static final String s16943 = "16943";
+ public static final String s16944 = "16944";
+ public static final String s16945 = "16945";
+ public static final String s16946 = "16946";
+ public static final String s16947 = "16947";
+ public static final String s16948 = "16948";
+ public static final String s16949 = "16949";
+ public static final String s16950 = "16950";
+ public static final String s16951 = "16951";
+ public static final String s16952 = "16952";
+ public static final String s16953 = "16953";
+ public static final String s16954 = "16954";
+ public static final String s16955 = "16955";
+ public static final String s16956 = "16956";
+ public static final String s16957 = "16957";
+ public static final String s16958 = "16958";
+ public static final String s16959 = "16959";
+ public static final String s16960 = "16960";
+ public static final String s16961 = "16961";
+ public static final String s16962 = "16962";
+ public static final String s16963 = "16963";
+ public static final String s16964 = "16964";
+ public static final String s16965 = "16965";
+ public static final String s16966 = "16966";
+ public static final String s16967 = "16967";
+ public static final String s16968 = "16968";
+ public static final String s16969 = "16969";
+ public static final String s16970 = "16970";
+ public static final String s16971 = "16971";
+ public static final String s16972 = "16972";
+ public static final String s16973 = "16973";
+ public static final String s16974 = "16974";
+ public static final String s16975 = "16975";
+ public static final String s16976 = "16976";
+ public static final String s16977 = "16977";
+ public static final String s16978 = "16978";
+ public static final String s16979 = "16979";
+ public static final String s16980 = "16980";
+ public static final String s16981 = "16981";
+ public static final String s16982 = "16982";
+ public static final String s16983 = "16983";
+ public static final String s16984 = "16984";
+ public static final String s16985 = "16985";
+ public static final String s16986 = "16986";
+ public static final String s16987 = "16987";
+ public static final String s16988 = "16988";
+ public static final String s16989 = "16989";
+ public static final String s16990 = "16990";
+ public static final String s16991 = "16991";
+ public static final String s16992 = "16992";
+ public static final String s16993 = "16993";
+ public static final String s16994 = "16994";
+ public static final String s16995 = "16995";
+ public static final String s16996 = "16996";
+ public static final String s16997 = "16997";
+ public static final String s16998 = "16998";
+ public static final String s16999 = "16999";
+ public static final String s17000 = "17000";
+ public static final String s17001 = "17001";
+ public static final String s17002 = "17002";
+ public static final String s17003 = "17003";
+ public static final String s17004 = "17004";
+ public static final String s17005 = "17005";
+ public static final String s17006 = "17006";
+ public static final String s17007 = "17007";
+ public static final String s17008 = "17008";
+ public static final String s17009 = "17009";
+ public static final String s17010 = "17010";
+ public static final String s17011 = "17011";
+ public static final String s17012 = "17012";
+ public static final String s17013 = "17013";
+ public static final String s17014 = "17014";
+ public static final String s17015 = "17015";
+ public static final String s17016 = "17016";
+ public static final String s17017 = "17017";
+ public static final String s17018 = "17018";
+ public static final String s17019 = "17019";
+ public static final String s17020 = "17020";
+ public static final String s17021 = "17021";
+ public static final String s17022 = "17022";
+ public static final String s17023 = "17023";
+ public static final String s17024 = "17024";
+ public static final String s17025 = "17025";
+ public static final String s17026 = "17026";
+ public static final String s17027 = "17027";
+ public static final String s17028 = "17028";
+ public static final String s17029 = "17029";
+ public static final String s17030 = "17030";
+ public static final String s17031 = "17031";
+ public static final String s17032 = "17032";
+ public static final String s17033 = "17033";
+ public static final String s17034 = "17034";
+ public static final String s17035 = "17035";
+ public static final String s17036 = "17036";
+ public static final String s17037 = "17037";
+ public static final String s17038 = "17038";
+ public static final String s17039 = "17039";
+ public static final String s17040 = "17040";
+ public static final String s17041 = "17041";
+ public static final String s17042 = "17042";
+ public static final String s17043 = "17043";
+ public static final String s17044 = "17044";
+ public static final String s17045 = "17045";
+ public static final String s17046 = "17046";
+ public static final String s17047 = "17047";
+ public static final String s17048 = "17048";
+ public static final String s17049 = "17049";
+ public static final String s17050 = "17050";
+ public static final String s17051 = "17051";
+ public static final String s17052 = "17052";
+ public static final String s17053 = "17053";
+ public static final String s17054 = "17054";
+ public static final String s17055 = "17055";
+ public static final String s17056 = "17056";
+ public static final String s17057 = "17057";
+ public static final String s17058 = "17058";
+ public static final String s17059 = "17059";
+ public static final String s17060 = "17060";
+ public static final String s17061 = "17061";
+ public static final String s17062 = "17062";
+ public static final String s17063 = "17063";
+ public static final String s17064 = "17064";
+ public static final String s17065 = "17065";
+ public static final String s17066 = "17066";
+ public static final String s17067 = "17067";
+ public static final String s17068 = "17068";
+ public static final String s17069 = "17069";
+ public static final String s17070 = "17070";
+ public static final String s17071 = "17071";
+ public static final String s17072 = "17072";
+ public static final String s17073 = "17073";
+ public static final String s17074 = "17074";
+ public static final String s17075 = "17075";
+ public static final String s17076 = "17076";
+ public static final String s17077 = "17077";
+ public static final String s17078 = "17078";
+ public static final String s17079 = "17079";
+ public static final String s17080 = "17080";
+ public static final String s17081 = "17081";
+ public static final String s17082 = "17082";
+ public static final String s17083 = "17083";
+ public static final String s17084 = "17084";
+ public static final String s17085 = "17085";
+ public static final String s17086 = "17086";
+ public static final String s17087 = "17087";
+ public static final String s17088 = "17088";
+ public static final String s17089 = "17089";
+ public static final String s17090 = "17090";
+ public static final String s17091 = "17091";
+ public static final String s17092 = "17092";
+ public static final String s17093 = "17093";
+ public static final String s17094 = "17094";
+ public static final String s17095 = "17095";
+ public static final String s17096 = "17096";
+ public static final String s17097 = "17097";
+ public static final String s17098 = "17098";
+ public static final String s17099 = "17099";
+ public static final String s17100 = "17100";
+ public static final String s17101 = "17101";
+ public static final String s17102 = "17102";
+ public static final String s17103 = "17103";
+ public static final String s17104 = "17104";
+ public static final String s17105 = "17105";
+ public static final String s17106 = "17106";
+ public static final String s17107 = "17107";
+ public static final String s17108 = "17108";
+ public static final String s17109 = "17109";
+ public static final String s17110 = "17110";
+ public static final String s17111 = "17111";
+ public static final String s17112 = "17112";
+ public static final String s17113 = "17113";
+ public static final String s17114 = "17114";
+ public static final String s17115 = "17115";
+ public static final String s17116 = "17116";
+ public static final String s17117 = "17117";
+ public static final String s17118 = "17118";
+ public static final String s17119 = "17119";
+ public static final String s17120 = "17120";
+ public static final String s17121 = "17121";
+ public static final String s17122 = "17122";
+ public static final String s17123 = "17123";
+ public static final String s17124 = "17124";
+ public static final String s17125 = "17125";
+ public static final String s17126 = "17126";
+ public static final String s17127 = "17127";
+ public static final String s17128 = "17128";
+ public static final String s17129 = "17129";
+ public static final String s17130 = "17130";
+ public static final String s17131 = "17131";
+ public static final String s17132 = "17132";
+ public static final String s17133 = "17133";
+ public static final String s17134 = "17134";
+ public static final String s17135 = "17135";
+ public static final String s17136 = "17136";
+ public static final String s17137 = "17137";
+ public static final String s17138 = "17138";
+ public static final String s17139 = "17139";
+ public static final String s17140 = "17140";
+ public static final String s17141 = "17141";
+ public static final String s17142 = "17142";
+ public static final String s17143 = "17143";
+ public static final String s17144 = "17144";
+ public static final String s17145 = "17145";
+ public static final String s17146 = "17146";
+ public static final String s17147 = "17147";
+ public static final String s17148 = "17148";
+ public static final String s17149 = "17149";
+ public static final String s17150 = "17150";
+ public static final String s17151 = "17151";
+ public static final String s17152 = "17152";
+ public static final String s17153 = "17153";
+ public static final String s17154 = "17154";
+ public static final String s17155 = "17155";
+ public static final String s17156 = "17156";
+ public static final String s17157 = "17157";
+ public static final String s17158 = "17158";
+ public static final String s17159 = "17159";
+ public static final String s17160 = "17160";
+ public static final String s17161 = "17161";
+ public static final String s17162 = "17162";
+ public static final String s17163 = "17163";
+ public static final String s17164 = "17164";
+ public static final String s17165 = "17165";
+ public static final String s17166 = "17166";
+ public static final String s17167 = "17167";
+ public static final String s17168 = "17168";
+ public static final String s17169 = "17169";
+ public static final String s17170 = "17170";
+ public static final String s17171 = "17171";
+ public static final String s17172 = "17172";
+ public static final String s17173 = "17173";
+ public static final String s17174 = "17174";
+ public static final String s17175 = "17175";
+ public static final String s17176 = "17176";
+ public static final String s17177 = "17177";
+ public static final String s17178 = "17178";
+ public static final String s17179 = "17179";
+ public static final String s17180 = "17180";
+ public static final String s17181 = "17181";
+ public static final String s17182 = "17182";
+ public static final String s17183 = "17183";
+ public static final String s17184 = "17184";
+ public static final String s17185 = "17185";
+ public static final String s17186 = "17186";
+ public static final String s17187 = "17187";
+ public static final String s17188 = "17188";
+ public static final String s17189 = "17189";
+ public static final String s17190 = "17190";
+ public static final String s17191 = "17191";
+ public static final String s17192 = "17192";
+ public static final String s17193 = "17193";
+ public static final String s17194 = "17194";
+ public static final String s17195 = "17195";
+ public static final String s17196 = "17196";
+ public static final String s17197 = "17197";
+ public static final String s17198 = "17198";
+ public static final String s17199 = "17199";
+ public static final String s17200 = "17200";
+ public static final String s17201 = "17201";
+ public static final String s17202 = "17202";
+ public static final String s17203 = "17203";
+ public static final String s17204 = "17204";
+ public static final String s17205 = "17205";
+ public static final String s17206 = "17206";
+ public static final String s17207 = "17207";
+ public static final String s17208 = "17208";
+ public static final String s17209 = "17209";
+ public static final String s17210 = "17210";
+ public static final String s17211 = "17211";
+ public static final String s17212 = "17212";
+ public static final String s17213 = "17213";
+ public static final String s17214 = "17214";
+ public static final String s17215 = "17215";
+ public static final String s17216 = "17216";
+ public static final String s17217 = "17217";
+ public static final String s17218 = "17218";
+ public static final String s17219 = "17219";
+ public static final String s17220 = "17220";
+ public static final String s17221 = "17221";
+ public static final String s17222 = "17222";
+ public static final String s17223 = "17223";
+ public static final String s17224 = "17224";
+ public static final String s17225 = "17225";
+ public static final String s17226 = "17226";
+ public static final String s17227 = "17227";
+ public static final String s17228 = "17228";
+ public static final String s17229 = "17229";
+ public static final String s17230 = "17230";
+ public static final String s17231 = "17231";
+ public static final String s17232 = "17232";
+ public static final String s17233 = "17233";
+ public static final String s17234 = "17234";
+ public static final String s17235 = "17235";
+ public static final String s17236 = "17236";
+ public static final String s17237 = "17237";
+ public static final String s17238 = "17238";
+ public static final String s17239 = "17239";
+ public static final String s17240 = "17240";
+ public static final String s17241 = "17241";
+ public static final String s17242 = "17242";
+ public static final String s17243 = "17243";
+ public static final String s17244 = "17244";
+ public static final String s17245 = "17245";
+ public static final String s17246 = "17246";
+ public static final String s17247 = "17247";
+ public static final String s17248 = "17248";
+ public static final String s17249 = "17249";
+ public static final String s17250 = "17250";
+ public static final String s17251 = "17251";
+ public static final String s17252 = "17252";
+ public static final String s17253 = "17253";
+ public static final String s17254 = "17254";
+ public static final String s17255 = "17255";
+ public static final String s17256 = "17256";
+ public static final String s17257 = "17257";
+ public static final String s17258 = "17258";
+ public static final String s17259 = "17259";
+ public static final String s17260 = "17260";
+ public static final String s17261 = "17261";
+ public static final String s17262 = "17262";
+ public static final String s17263 = "17263";
+ public static final String s17264 = "17264";
+ public static final String s17265 = "17265";
+ public static final String s17266 = "17266";
+ public static final String s17267 = "17267";
+ public static final String s17268 = "17268";
+ public static final String s17269 = "17269";
+ public static final String s17270 = "17270";
+ public static final String s17271 = "17271";
+ public static final String s17272 = "17272";
+ public static final String s17273 = "17273";
+ public static final String s17274 = "17274";
+ public static final String s17275 = "17275";
+ public static final String s17276 = "17276";
+ public static final String s17277 = "17277";
+ public static final String s17278 = "17278";
+ public static final String s17279 = "17279";
+ public static final String s17280 = "17280";
+ public static final String s17281 = "17281";
+ public static final String s17282 = "17282";
+ public static final String s17283 = "17283";
+ public static final String s17284 = "17284";
+ public static final String s17285 = "17285";
+ public static final String s17286 = "17286";
+ public static final String s17287 = "17287";
+ public static final String s17288 = "17288";
+ public static final String s17289 = "17289";
+ public static final String s17290 = "17290";
+ public static final String s17291 = "17291";
+ public static final String s17292 = "17292";
+ public static final String s17293 = "17293";
+ public static final String s17294 = "17294";
+ public static final String s17295 = "17295";
+ public static final String s17296 = "17296";
+ public static final String s17297 = "17297";
+ public static final String s17298 = "17298";
+ public static final String s17299 = "17299";
+ public static final String s17300 = "17300";
+ public static final String s17301 = "17301";
+ public static final String s17302 = "17302";
+ public static final String s17303 = "17303";
+ public static final String s17304 = "17304";
+ public static final String s17305 = "17305";
+ public static final String s17306 = "17306";
+ public static final String s17307 = "17307";
+ public static final String s17308 = "17308";
+ public static final String s17309 = "17309";
+ public static final String s17310 = "17310";
+ public static final String s17311 = "17311";
+ public static final String s17312 = "17312";
+ public static final String s17313 = "17313";
+ public static final String s17314 = "17314";
+ public static final String s17315 = "17315";
+ public static final String s17316 = "17316";
+ public static final String s17317 = "17317";
+ public static final String s17318 = "17318";
+ public static final String s17319 = "17319";
+ public static final String s17320 = "17320";
+ public static final String s17321 = "17321";
+ public static final String s17322 = "17322";
+ public static final String s17323 = "17323";
+ public static final String s17324 = "17324";
+ public static final String s17325 = "17325";
+ public static final String s17326 = "17326";
+ public static final String s17327 = "17327";
+ public static final String s17328 = "17328";
+ public static final String s17329 = "17329";
+ public static final String s17330 = "17330";
+ public static final String s17331 = "17331";
+ public static final String s17332 = "17332";
+ public static final String s17333 = "17333";
+ public static final String s17334 = "17334";
+ public static final String s17335 = "17335";
+ public static final String s17336 = "17336";
+ public static final String s17337 = "17337";
+ public static final String s17338 = "17338";
+ public static final String s17339 = "17339";
+ public static final String s17340 = "17340";
+ public static final String s17341 = "17341";
+ public static final String s17342 = "17342";
+ public static final String s17343 = "17343";
+ public static final String s17344 = "17344";
+ public static final String s17345 = "17345";
+ public static final String s17346 = "17346";
+ public static final String s17347 = "17347";
+ public static final String s17348 = "17348";
+ public static final String s17349 = "17349";
+ public static final String s17350 = "17350";
+ public static final String s17351 = "17351";
+ public static final String s17352 = "17352";
+ public static final String s17353 = "17353";
+ public static final String s17354 = "17354";
+ public static final String s17355 = "17355";
+ public static final String s17356 = "17356";
+ public static final String s17357 = "17357";
+ public static final String s17358 = "17358";
+ public static final String s17359 = "17359";
+ public static final String s17360 = "17360";
+ public static final String s17361 = "17361";
+ public static final String s17362 = "17362";
+ public static final String s17363 = "17363";
+ public static final String s17364 = "17364";
+ public static final String s17365 = "17365";
+ public static final String s17366 = "17366";
+ public static final String s17367 = "17367";
+ public static final String s17368 = "17368";
+ public static final String s17369 = "17369";
+ public static final String s17370 = "17370";
+ public static final String s17371 = "17371";
+ public static final String s17372 = "17372";
+ public static final String s17373 = "17373";
+ public static final String s17374 = "17374";
+ public static final String s17375 = "17375";
+ public static final String s17376 = "17376";
+ public static final String s17377 = "17377";
+ public static final String s17378 = "17378";
+ public static final String s17379 = "17379";
+ public static final String s17380 = "17380";
+ public static final String s17381 = "17381";
+ public static final String s17382 = "17382";
+ public static final String s17383 = "17383";
+ public static final String s17384 = "17384";
+ public static final String s17385 = "17385";
+ public static final String s17386 = "17386";
+ public static final String s17387 = "17387";
+ public static final String s17388 = "17388";
+ public static final String s17389 = "17389";
+ public static final String s17390 = "17390";
+ public static final String s17391 = "17391";
+ public static final String s17392 = "17392";
+ public static final String s17393 = "17393";
+ public static final String s17394 = "17394";
+ public static final String s17395 = "17395";
+ public static final String s17396 = "17396";
+ public static final String s17397 = "17397";
+ public static final String s17398 = "17398";
+ public static final String s17399 = "17399";
+ public static final String s17400 = "17400";
+ public static final String s17401 = "17401";
+ public static final String s17402 = "17402";
+ public static final String s17403 = "17403";
+ public static final String s17404 = "17404";
+ public static final String s17405 = "17405";
+ public static final String s17406 = "17406";
+ public static final String s17407 = "17407";
+ public static final String s17408 = "17408";
+ public static final String s17409 = "17409";
+ public static final String s17410 = "17410";
+ public static final String s17411 = "17411";
+ public static final String s17412 = "17412";
+ public static final String s17413 = "17413";
+ public static final String s17414 = "17414";
+ public static final String s17415 = "17415";
+ public static final String s17416 = "17416";
+ public static final String s17417 = "17417";
+ public static final String s17418 = "17418";
+ public static final String s17419 = "17419";
+ public static final String s17420 = "17420";
+ public static final String s17421 = "17421";
+ public static final String s17422 = "17422";
+ public static final String s17423 = "17423";
+ public static final String s17424 = "17424";
+ public static final String s17425 = "17425";
+ public static final String s17426 = "17426";
+ public static final String s17427 = "17427";
+ public static final String s17428 = "17428";
+ public static final String s17429 = "17429";
+ public static final String s17430 = "17430";
+ public static final String s17431 = "17431";
+ public static final String s17432 = "17432";
+ public static final String s17433 = "17433";
+ public static final String s17434 = "17434";
+ public static final String s17435 = "17435";
+ public static final String s17436 = "17436";
+ public static final String s17437 = "17437";
+ public static final String s17438 = "17438";
+ public static final String s17439 = "17439";
+ public static final String s17440 = "17440";
+ public static final String s17441 = "17441";
+ public static final String s17442 = "17442";
+ public static final String s17443 = "17443";
+ public static final String s17444 = "17444";
+ public static final String s17445 = "17445";
+ public static final String s17446 = "17446";
+ public static final String s17447 = "17447";
+ public static final String s17448 = "17448";
+ public static final String s17449 = "17449";
+ public static final String s17450 = "17450";
+ public static final String s17451 = "17451";
+ public static final String s17452 = "17452";
+ public static final String s17453 = "17453";
+ public static final String s17454 = "17454";
+ public static final String s17455 = "17455";
+ public static final String s17456 = "17456";
+ public static final String s17457 = "17457";
+ public static final String s17458 = "17458";
+ public static final String s17459 = "17459";
+ public static final String s17460 = "17460";
+ public static final String s17461 = "17461";
+ public static final String s17462 = "17462";
+ public static final String s17463 = "17463";
+ public static final String s17464 = "17464";
+ public static final String s17465 = "17465";
+ public static final String s17466 = "17466";
+ public static final String s17467 = "17467";
+ public static final String s17468 = "17468";
+ public static final String s17469 = "17469";
+ public static final String s17470 = "17470";
+ public static final String s17471 = "17471";
+ public static final String s17472 = "17472";
+ public static final String s17473 = "17473";
+ public static final String s17474 = "17474";
+ public static final String s17475 = "17475";
+ public static final String s17476 = "17476";
+ public static final String s17477 = "17477";
+ public static final String s17478 = "17478";
+ public static final String s17479 = "17479";
+ public static final String s17480 = "17480";
+ public static final String s17481 = "17481";
+ public static final String s17482 = "17482";
+ public static final String s17483 = "17483";
+ public static final String s17484 = "17484";
+ public static final String s17485 = "17485";
+ public static final String s17486 = "17486";
+ public static final String s17487 = "17487";
+ public static final String s17488 = "17488";
+ public static final String s17489 = "17489";
+ public static final String s17490 = "17490";
+ public static final String s17491 = "17491";
+ public static final String s17492 = "17492";
+ public static final String s17493 = "17493";
+ public static final String s17494 = "17494";
+ public static final String s17495 = "17495";
+ public static final String s17496 = "17496";
+ public static final String s17497 = "17497";
+ public static final String s17498 = "17498";
+ public static final String s17499 = "17499";
+ public static final String s17500 = "17500";
+ public static final String s17501 = "17501";
+ public static final String s17502 = "17502";
+ public static final String s17503 = "17503";
+ public static final String s17504 = "17504";
+ public static final String s17505 = "17505";
+ public static final String s17506 = "17506";
+ public static final String s17507 = "17507";
+ public static final String s17508 = "17508";
+ public static final String s17509 = "17509";
+ public static final String s17510 = "17510";
+ public static final String s17511 = "17511";
+ public static final String s17512 = "17512";
+ public static final String s17513 = "17513";
+ public static final String s17514 = "17514";
+ public static final String s17515 = "17515";
+ public static final String s17516 = "17516";
+ public static final String s17517 = "17517";
+ public static final String s17518 = "17518";
+ public static final String s17519 = "17519";
+ public static final String s17520 = "17520";
+ public static final String s17521 = "17521";
+ public static final String s17522 = "17522";
+ public static final String s17523 = "17523";
+ public static final String s17524 = "17524";
+ public static final String s17525 = "17525";
+ public static final String s17526 = "17526";
+ public static final String s17527 = "17527";
+ public static final String s17528 = "17528";
+ public static final String s17529 = "17529";
+ public static final String s17530 = "17530";
+ public static final String s17531 = "17531";
+ public static final String s17532 = "17532";
+ public static final String s17533 = "17533";
+ public static final String s17534 = "17534";
+ public static final String s17535 = "17535";
+ public static final String s17536 = "17536";
+ public static final String s17537 = "17537";
+ public static final String s17538 = "17538";
+ public static final String s17539 = "17539";
+ public static final String s17540 = "17540";
+ public static final String s17541 = "17541";
+ public static final String s17542 = "17542";
+ public static final String s17543 = "17543";
+ public static final String s17544 = "17544";
+ public static final String s17545 = "17545";
+ public static final String s17546 = "17546";
+ public static final String s17547 = "17547";
+ public static final String s17548 = "17548";
+ public static final String s17549 = "17549";
+ public static final String s17550 = "17550";
+ public static final String s17551 = "17551";
+ public static final String s17552 = "17552";
+ public static final String s17553 = "17553";
+ public static final String s17554 = "17554";
+ public static final String s17555 = "17555";
+ public static final String s17556 = "17556";
+ public static final String s17557 = "17557";
+ public static final String s17558 = "17558";
+ public static final String s17559 = "17559";
+ public static final String s17560 = "17560";
+ public static final String s17561 = "17561";
+ public static final String s17562 = "17562";
+ public static final String s17563 = "17563";
+ public static final String s17564 = "17564";
+ public static final String s17565 = "17565";
+ public static final String s17566 = "17566";
+ public static final String s17567 = "17567";
+ public static final String s17568 = "17568";
+ public static final String s17569 = "17569";
+ public static final String s17570 = "17570";
+ public static final String s17571 = "17571";
+ public static final String s17572 = "17572";
+ public static final String s17573 = "17573";
+ public static final String s17574 = "17574";
+ public static final String s17575 = "17575";
+ public static final String s17576 = "17576";
+ public static final String s17577 = "17577";
+ public static final String s17578 = "17578";
+ public static final String s17579 = "17579";
+ public static final String s17580 = "17580";
+ public static final String s17581 = "17581";
+ public static final String s17582 = "17582";
+ public static final String s17583 = "17583";
+ public static final String s17584 = "17584";
+ public static final String s17585 = "17585";
+ public static final String s17586 = "17586";
+ public static final String s17587 = "17587";
+ public static final String s17588 = "17588";
+ public static final String s17589 = "17589";
+ public static final String s17590 = "17590";
+ public static final String s17591 = "17591";
+ public static final String s17592 = "17592";
+ public static final String s17593 = "17593";
+ public static final String s17594 = "17594";
+ public static final String s17595 = "17595";
+ public static final String s17596 = "17596";
+ public static final String s17597 = "17597";
+ public static final String s17598 = "17598";
+ public static final String s17599 = "17599";
+ public static final String s17600 = "17600";
+ public static final String s17601 = "17601";
+ public static final String s17602 = "17602";
+ public static final String s17603 = "17603";
+ public static final String s17604 = "17604";
+ public static final String s17605 = "17605";
+ public static final String s17606 = "17606";
+ public static final String s17607 = "17607";
+ public static final String s17608 = "17608";
+ public static final String s17609 = "17609";
+ public static final String s17610 = "17610";
+ public static final String s17611 = "17611";
+ public static final String s17612 = "17612";
+ public static final String s17613 = "17613";
+ public static final String s17614 = "17614";
+ public static final String s17615 = "17615";
+ public static final String s17616 = "17616";
+ public static final String s17617 = "17617";
+ public static final String s17618 = "17618";
+ public static final String s17619 = "17619";
+ public static final String s17620 = "17620";
+ public static final String s17621 = "17621";
+ public static final String s17622 = "17622";
+ public static final String s17623 = "17623";
+ public static final String s17624 = "17624";
+ public static final String s17625 = "17625";
+ public static final String s17626 = "17626";
+ public static final String s17627 = "17627";
+ public static final String s17628 = "17628";
+ public static final String s17629 = "17629";
+ public static final String s17630 = "17630";
+ public static final String s17631 = "17631";
+ public static final String s17632 = "17632";
+ public static final String s17633 = "17633";
+ public static final String s17634 = "17634";
+ public static final String s17635 = "17635";
+ public static final String s17636 = "17636";
+ public static final String s17637 = "17637";
+ public static final String s17638 = "17638";
+ public static final String s17639 = "17639";
+ public static final String s17640 = "17640";
+ public static final String s17641 = "17641";
+ public static final String s17642 = "17642";
+ public static final String s17643 = "17643";
+ public static final String s17644 = "17644";
+ public static final String s17645 = "17645";
+ public static final String s17646 = "17646";
+ public static final String s17647 = "17647";
+ public static final String s17648 = "17648";
+ public static final String s17649 = "17649";
+ public static final String s17650 = "17650";
+ public static final String s17651 = "17651";
+ public static final String s17652 = "17652";
+ public static final String s17653 = "17653";
+ public static final String s17654 = "17654";
+ public static final String s17655 = "17655";
+ public static final String s17656 = "17656";
+ public static final String s17657 = "17657";
+ public static final String s17658 = "17658";
+ public static final String s17659 = "17659";
+ public static final String s17660 = "17660";
+ public static final String s17661 = "17661";
+ public static final String s17662 = "17662";
+ public static final String s17663 = "17663";
+ public static final String s17664 = "17664";
+ public static final String s17665 = "17665";
+ public static final String s17666 = "17666";
+ public static final String s17667 = "17667";
+ public static final String s17668 = "17668";
+ public static final String s17669 = "17669";
+ public static final String s17670 = "17670";
+ public static final String s17671 = "17671";
+ public static final String s17672 = "17672";
+ public static final String s17673 = "17673";
+ public static final String s17674 = "17674";
+ public static final String s17675 = "17675";
+ public static final String s17676 = "17676";
+ public static final String s17677 = "17677";
+ public static final String s17678 = "17678";
+ public static final String s17679 = "17679";
+ public static final String s17680 = "17680";
+ public static final String s17681 = "17681";
+ public static final String s17682 = "17682";
+ public static final String s17683 = "17683";
+ public static final String s17684 = "17684";
+ public static final String s17685 = "17685";
+ public static final String s17686 = "17686";
+ public static final String s17687 = "17687";
+ public static final String s17688 = "17688";
+ public static final String s17689 = "17689";
+ public static final String s17690 = "17690";
+ public static final String s17691 = "17691";
+ public static final String s17692 = "17692";
+ public static final String s17693 = "17693";
+ public static final String s17694 = "17694";
+ public static final String s17695 = "17695";
+ public static final String s17696 = "17696";
+ public static final String s17697 = "17697";
+ public static final String s17698 = "17698";
+ public static final String s17699 = "17699";
+ public static final String s17700 = "17700";
+ public static final String s17701 = "17701";
+ public static final String s17702 = "17702";
+ public static final String s17703 = "17703";
+ public static final String s17704 = "17704";
+ public static final String s17705 = "17705";
+ public static final String s17706 = "17706";
+ public static final String s17707 = "17707";
+ public static final String s17708 = "17708";
+ public static final String s17709 = "17709";
+ public static final String s17710 = "17710";
+ public static final String s17711 = "17711";
+ public static final String s17712 = "17712";
+ public static final String s17713 = "17713";
+ public static final String s17714 = "17714";
+ public static final String s17715 = "17715";
+ public static final String s17716 = "17716";
+ public static final String s17717 = "17717";
+ public static final String s17718 = "17718";
+ public static final String s17719 = "17719";
+ public static final String s17720 = "17720";
+ public static final String s17721 = "17721";
+ public static final String s17722 = "17722";
+ public static final String s17723 = "17723";
+ public static final String s17724 = "17724";
+ public static final String s17725 = "17725";
+ public static final String s17726 = "17726";
+ public static final String s17727 = "17727";
+ public static final String s17728 = "17728";
+ public static final String s17729 = "17729";
+ public static final String s17730 = "17730";
+ public static final String s17731 = "17731";
+ public static final String s17732 = "17732";
+ public static final String s17733 = "17733";
+ public static final String s17734 = "17734";
+ public static final String s17735 = "17735";
+ public static final String s17736 = "17736";
+ public static final String s17737 = "17737";
+ public static final String s17738 = "17738";
+ public static final String s17739 = "17739";
+ public static final String s17740 = "17740";
+ public static final String s17741 = "17741";
+ public static final String s17742 = "17742";
+ public static final String s17743 = "17743";
+ public static final String s17744 = "17744";
+ public static final String s17745 = "17745";
+ public static final String s17746 = "17746";
+ public static final String s17747 = "17747";
+ public static final String s17748 = "17748";
+ public static final String s17749 = "17749";
+ public static final String s17750 = "17750";
+ public static final String s17751 = "17751";
+ public static final String s17752 = "17752";
+ public static final String s17753 = "17753";
+ public static final String s17754 = "17754";
+ public static final String s17755 = "17755";
+ public static final String s17756 = "17756";
+ public static final String s17757 = "17757";
+ public static final String s17758 = "17758";
+ public static final String s17759 = "17759";
+ public static final String s17760 = "17760";
+ public static final String s17761 = "17761";
+ public static final String s17762 = "17762";
+ public static final String s17763 = "17763";
+ public static final String s17764 = "17764";
+ public static final String s17765 = "17765";
+ public static final String s17766 = "17766";
+ public static final String s17767 = "17767";
+ public static final String s17768 = "17768";
+ public static final String s17769 = "17769";
+ public static final String s17770 = "17770";
+ public static final String s17771 = "17771";
+ public static final String s17772 = "17772";
+ public static final String s17773 = "17773";
+ public static final String s17774 = "17774";
+ public static final String s17775 = "17775";
+ public static final String s17776 = "17776";
+ public static final String s17777 = "17777";
+ public static final String s17778 = "17778";
+ public static final String s17779 = "17779";
+ public static final String s17780 = "17780";
+ public static final String s17781 = "17781";
+ public static final String s17782 = "17782";
+ public static final String s17783 = "17783";
+ public static final String s17784 = "17784";
+ public static final String s17785 = "17785";
+ public static final String s17786 = "17786";
+ public static final String s17787 = "17787";
+ public static final String s17788 = "17788";
+ public static final String s17789 = "17789";
+ public static final String s17790 = "17790";
+ public static final String s17791 = "17791";
+ public static final String s17792 = "17792";
+ public static final String s17793 = "17793";
+ public static final String s17794 = "17794";
+ public static final String s17795 = "17795";
+ public static final String s17796 = "17796";
+ public static final String s17797 = "17797";
+ public static final String s17798 = "17798";
+ public static final String s17799 = "17799";
+ public static final String s17800 = "17800";
+ public static final String s17801 = "17801";
+ public static final String s17802 = "17802";
+ public static final String s17803 = "17803";
+ public static final String s17804 = "17804";
+ public static final String s17805 = "17805";
+ public static final String s17806 = "17806";
+ public static final String s17807 = "17807";
+ public static final String s17808 = "17808";
+ public static final String s17809 = "17809";
+ public static final String s17810 = "17810";
+ public static final String s17811 = "17811";
+ public static final String s17812 = "17812";
+ public static final String s17813 = "17813";
+ public static final String s17814 = "17814";
+ public static final String s17815 = "17815";
+ public static final String s17816 = "17816";
+ public static final String s17817 = "17817";
+ public static final String s17818 = "17818";
+ public static final String s17819 = "17819";
+ public static final String s17820 = "17820";
+ public static final String s17821 = "17821";
+ public static final String s17822 = "17822";
+ public static final String s17823 = "17823";
+ public static final String s17824 = "17824";
+ public static final String s17825 = "17825";
+ public static final String s17826 = "17826";
+ public static final String s17827 = "17827";
+ public static final String s17828 = "17828";
+ public static final String s17829 = "17829";
+ public static final String s17830 = "17830";
+ public static final String s17831 = "17831";
+ public static final String s17832 = "17832";
+ public static final String s17833 = "17833";
+ public static final String s17834 = "17834";
+ public static final String s17835 = "17835";
+ public static final String s17836 = "17836";
+ public static final String s17837 = "17837";
+ public static final String s17838 = "17838";
+ public static final String s17839 = "17839";
+ public static final String s17840 = "17840";
+ public static final String s17841 = "17841";
+ public static final String s17842 = "17842";
+ public static final String s17843 = "17843";
+ public static final String s17844 = "17844";
+ public static final String s17845 = "17845";
+ public static final String s17846 = "17846";
+ public static final String s17847 = "17847";
+ public static final String s17848 = "17848";
+ public static final String s17849 = "17849";
+ public static final String s17850 = "17850";
+ public static final String s17851 = "17851";
+ public static final String s17852 = "17852";
+ public static final String s17853 = "17853";
+ public static final String s17854 = "17854";
+ public static final String s17855 = "17855";
+ public static final String s17856 = "17856";
+ public static final String s17857 = "17857";
+ public static final String s17858 = "17858";
+ public static final String s17859 = "17859";
+ public static final String s17860 = "17860";
+ public static final String s17861 = "17861";
+ public static final String s17862 = "17862";
+ public static final String s17863 = "17863";
+ public static final String s17864 = "17864";
+ public static final String s17865 = "17865";
+ public static final String s17866 = "17866";
+ public static final String s17867 = "17867";
+ public static final String s17868 = "17868";
+ public static final String s17869 = "17869";
+ public static final String s17870 = "17870";
+ public static final String s17871 = "17871";
+ public static final String s17872 = "17872";
+ public static final String s17873 = "17873";
+ public static final String s17874 = "17874";
+ public static final String s17875 = "17875";
+ public static final String s17876 = "17876";
+ public static final String s17877 = "17877";
+ public static final String s17878 = "17878";
+ public static final String s17879 = "17879";
+ public static final String s17880 = "17880";
+ public static final String s17881 = "17881";
+ public static final String s17882 = "17882";
+ public static final String s17883 = "17883";
+ public static final String s17884 = "17884";
+ public static final String s17885 = "17885";
+ public static final String s17886 = "17886";
+ public static final String s17887 = "17887";
+ public static final String s17888 = "17888";
+ public static final String s17889 = "17889";
+ public static final String s17890 = "17890";
+ public static final String s17891 = "17891";
+ public static final String s17892 = "17892";
+ public static final String s17893 = "17893";
+ public static final String s17894 = "17894";
+ public static final String s17895 = "17895";
+ public static final String s17896 = "17896";
+ public static final String s17897 = "17897";
+ public static final String s17898 = "17898";
+ public static final String s17899 = "17899";
+ public static final String s17900 = "17900";
+ public static final String s17901 = "17901";
+ public static final String s17902 = "17902";
+ public static final String s17903 = "17903";
+ public static final String s17904 = "17904";
+ public static final String s17905 = "17905";
+ public static final String s17906 = "17906";
+ public static final String s17907 = "17907";
+ public static final String s17908 = "17908";
+ public static final String s17909 = "17909";
+ public static final String s17910 = "17910";
+ public static final String s17911 = "17911";
+ public static final String s17912 = "17912";
+ public static final String s17913 = "17913";
+ public static final String s17914 = "17914";
+ public static final String s17915 = "17915";
+ public static final String s17916 = "17916";
+ public static final String s17917 = "17917";
+ public static final String s17918 = "17918";
+ public static final String s17919 = "17919";
+ public static final String s17920 = "17920";
+ public static final String s17921 = "17921";
+ public static final String s17922 = "17922";
+ public static final String s17923 = "17923";
+ public static final String s17924 = "17924";
+ public static final String s17925 = "17925";
+ public static final String s17926 = "17926";
+ public static final String s17927 = "17927";
+ public static final String s17928 = "17928";
+ public static final String s17929 = "17929";
+ public static final String s17930 = "17930";
+ public static final String s17931 = "17931";
+ public static final String s17932 = "17932";
+ public static final String s17933 = "17933";
+ public static final String s17934 = "17934";
+ public static final String s17935 = "17935";
+ public static final String s17936 = "17936";
+ public static final String s17937 = "17937";
+ public static final String s17938 = "17938";
+ public static final String s17939 = "17939";
+ public static final String s17940 = "17940";
+ public static final String s17941 = "17941";
+ public static final String s17942 = "17942";
+ public static final String s17943 = "17943";
+ public static final String s17944 = "17944";
+ public static final String s17945 = "17945";
+ public static final String s17946 = "17946";
+ public static final String s17947 = "17947";
+ public static final String s17948 = "17948";
+ public static final String s17949 = "17949";
+ public static final String s17950 = "17950";
+ public static final String s17951 = "17951";
+ public static final String s17952 = "17952";
+ public static final String s17953 = "17953";
+ public static final String s17954 = "17954";
+ public static final String s17955 = "17955";
+ public static final String s17956 = "17956";
+ public static final String s17957 = "17957";
+ public static final String s17958 = "17958";
+ public static final String s17959 = "17959";
+ public static final String s17960 = "17960";
+ public static final String s17961 = "17961";
+ public static final String s17962 = "17962";
+ public static final String s17963 = "17963";
+ public static final String s17964 = "17964";
+ public static final String s17965 = "17965";
+ public static final String s17966 = "17966";
+ public static final String s17967 = "17967";
+ public static final String s17968 = "17968";
+ public static final String s17969 = "17969";
+ public static final String s17970 = "17970";
+ public static final String s17971 = "17971";
+ public static final String s17972 = "17972";
+ public static final String s17973 = "17973";
+ public static final String s17974 = "17974";
+ public static final String s17975 = "17975";
+ public static final String s17976 = "17976";
+ public static final String s17977 = "17977";
+ public static final String s17978 = "17978";
+ public static final String s17979 = "17979";
+ public static final String s17980 = "17980";
+ public static final String s17981 = "17981";
+ public static final String s17982 = "17982";
+ public static final String s17983 = "17983";
+ public static final String s17984 = "17984";
+ public static final String s17985 = "17985";
+ public static final String s17986 = "17986";
+ public static final String s17987 = "17987";
+ public static final String s17988 = "17988";
+ public static final String s17989 = "17989";
+ public static final String s17990 = "17990";
+ public static final String s17991 = "17991";
+ public static final String s17992 = "17992";
+ public static final String s17993 = "17993";
+ public static final String s17994 = "17994";
+ public static final String s17995 = "17995";
+ public static final String s17996 = "17996";
+ public static final String s17997 = "17997";
+ public static final String s17998 = "17998";
+ public static final String s17999 = "17999";
+ public static final String s18000 = "18000";
+ public static final String s18001 = "18001";
+ public static final String s18002 = "18002";
+ public static final String s18003 = "18003";
+ public static final String s18004 = "18004";
+ public static final String s18005 = "18005";
+ public static final String s18006 = "18006";
+ public static final String s18007 = "18007";
+ public static final String s18008 = "18008";
+ public static final String s18009 = "18009";
+ public static final String s18010 = "18010";
+ public static final String s18011 = "18011";
+ public static final String s18012 = "18012";
+ public static final String s18013 = "18013";
+ public static final String s18014 = "18014";
+ public static final String s18015 = "18015";
+ public static final String s18016 = "18016";
+ public static final String s18017 = "18017";
+ public static final String s18018 = "18018";
+ public static final String s18019 = "18019";
+ public static final String s18020 = "18020";
+ public static final String s18021 = "18021";
+ public static final String s18022 = "18022";
+ public static final String s18023 = "18023";
+ public static final String s18024 = "18024";
+ public static final String s18025 = "18025";
+ public static final String s18026 = "18026";
+ public static final String s18027 = "18027";
+ public static final String s18028 = "18028";
+ public static final String s18029 = "18029";
+ public static final String s18030 = "18030";
+ public static final String s18031 = "18031";
+ public static final String s18032 = "18032";
+ public static final String s18033 = "18033";
+ public static final String s18034 = "18034";
+ public static final String s18035 = "18035";
+ public static final String s18036 = "18036";
+ public static final String s18037 = "18037";
+ public static final String s18038 = "18038";
+ public static final String s18039 = "18039";
+ public static final String s18040 = "18040";
+ public static final String s18041 = "18041";
+ public static final String s18042 = "18042";
+ public static final String s18043 = "18043";
+ public static final String s18044 = "18044";
+ public static final String s18045 = "18045";
+ public static final String s18046 = "18046";
+ public static final String s18047 = "18047";
+ public static final String s18048 = "18048";
+ public static final String s18049 = "18049";
+ public static final String s18050 = "18050";
+ public static final String s18051 = "18051";
+ public static final String s18052 = "18052";
+ public static final String s18053 = "18053";
+ public static final String s18054 = "18054";
+ public static final String s18055 = "18055";
+ public static final String s18056 = "18056";
+ public static final String s18057 = "18057";
+ public static final String s18058 = "18058";
+ public static final String s18059 = "18059";
+ public static final String s18060 = "18060";
+ public static final String s18061 = "18061";
+ public static final String s18062 = "18062";
+ public static final String s18063 = "18063";
+ public static final String s18064 = "18064";
+ public static final String s18065 = "18065";
+ public static final String s18066 = "18066";
+ public static final String s18067 = "18067";
+ public static final String s18068 = "18068";
+ public static final String s18069 = "18069";
+ public static final String s18070 = "18070";
+ public static final String s18071 = "18071";
+ public static final String s18072 = "18072";
+ public static final String s18073 = "18073";
+ public static final String s18074 = "18074";
+ public static final String s18075 = "18075";
+ public static final String s18076 = "18076";
+ public static final String s18077 = "18077";
+ public static final String s18078 = "18078";
+ public static final String s18079 = "18079";
+ public static final String s18080 = "18080";
+ public static final String s18081 = "18081";
+ public static final String s18082 = "18082";
+ public static final String s18083 = "18083";
+ public static final String s18084 = "18084";
+ public static final String s18085 = "18085";
+ public static final String s18086 = "18086";
+ public static final String s18087 = "18087";
+ public static final String s18088 = "18088";
+ public static final String s18089 = "18089";
+ public static final String s18090 = "18090";
+ public static final String s18091 = "18091";
+ public static final String s18092 = "18092";
+ public static final String s18093 = "18093";
+ public static final String s18094 = "18094";
+ public static final String s18095 = "18095";
+ public static final String s18096 = "18096";
+ public static final String s18097 = "18097";
+ public static final String s18098 = "18098";
+ public static final String s18099 = "18099";
+ public static final String s18100 = "18100";
+ public static final String s18101 = "18101";
+ public static final String s18102 = "18102";
+ public static final String s18103 = "18103";
+ public static final String s18104 = "18104";
+ public static final String s18105 = "18105";
+ public static final String s18106 = "18106";
+ public static final String s18107 = "18107";
+ public static final String s18108 = "18108";
+ public static final String s18109 = "18109";
+ public static final String s18110 = "18110";
+ public static final String s18111 = "18111";
+ public static final String s18112 = "18112";
+ public static final String s18113 = "18113";
+ public static final String s18114 = "18114";
+ public static final String s18115 = "18115";
+ public static final String s18116 = "18116";
+ public static final String s18117 = "18117";
+ public static final String s18118 = "18118";
+ public static final String s18119 = "18119";
+ public static final String s18120 = "18120";
+ public static final String s18121 = "18121";
+ public static final String s18122 = "18122";
+ public static final String s18123 = "18123";
+ public static final String s18124 = "18124";
+ public static final String s18125 = "18125";
+ public static final String s18126 = "18126";
+ public static final String s18127 = "18127";
+ public static final String s18128 = "18128";
+ public static final String s18129 = "18129";
+ public static final String s18130 = "18130";
+ public static final String s18131 = "18131";
+ public static final String s18132 = "18132";
+ public static final String s18133 = "18133";
+ public static final String s18134 = "18134";
+ public static final String s18135 = "18135";
+ public static final String s18136 = "18136";
+ public static final String s18137 = "18137";
+ public static final String s18138 = "18138";
+ public static final String s18139 = "18139";
+ public static final String s18140 = "18140";
+ public static final String s18141 = "18141";
+ public static final String s18142 = "18142";
+ public static final String s18143 = "18143";
+ public static final String s18144 = "18144";
+ public static final String s18145 = "18145";
+ public static final String s18146 = "18146";
+ public static final String s18147 = "18147";
+ public static final String s18148 = "18148";
+ public static final String s18149 = "18149";
+ public static final String s18150 = "18150";
+ public static final String s18151 = "18151";
+ public static final String s18152 = "18152";
+ public static final String s18153 = "18153";
+ public static final String s18154 = "18154";
+ public static final String s18155 = "18155";
+ public static final String s18156 = "18156";
+ public static final String s18157 = "18157";
+ public static final String s18158 = "18158";
+ public static final String s18159 = "18159";
+ public static final String s18160 = "18160";
+ public static final String s18161 = "18161";
+ public static final String s18162 = "18162";
+ public static final String s18163 = "18163";
+ public static final String s18164 = "18164";
+ public static final String s18165 = "18165";
+ public static final String s18166 = "18166";
+ public static final String s18167 = "18167";
+ public static final String s18168 = "18168";
+ public static final String s18169 = "18169";
+ public static final String s18170 = "18170";
+ public static final String s18171 = "18171";
+ public static final String s18172 = "18172";
+ public static final String s18173 = "18173";
+ public static final String s18174 = "18174";
+ public static final String s18175 = "18175";
+ public static final String s18176 = "18176";
+ public static final String s18177 = "18177";
+ public static final String s18178 = "18178";
+ public static final String s18179 = "18179";
+ public static final String s18180 = "18180";
+ public static final String s18181 = "18181";
+ public static final String s18182 = "18182";
+ public static final String s18183 = "18183";
+ public static final String s18184 = "18184";
+ public static final String s18185 = "18185";
+ public static final String s18186 = "18186";
+ public static final String s18187 = "18187";
+ public static final String s18188 = "18188";
+ public static final String s18189 = "18189";
+ public static final String s18190 = "18190";
+ public static final String s18191 = "18191";
+ public static final String s18192 = "18192";
+ public static final String s18193 = "18193";
+ public static final String s18194 = "18194";
+ public static final String s18195 = "18195";
+ public static final String s18196 = "18196";
+ public static final String s18197 = "18197";
+ public static final String s18198 = "18198";
+ public static final String s18199 = "18199";
+ public static final String s18200 = "18200";
+ public static final String s18201 = "18201";
+ public static final String s18202 = "18202";
+ public static final String s18203 = "18203";
+ public static final String s18204 = "18204";
+ public static final String s18205 = "18205";
+ public static final String s18206 = "18206";
+ public static final String s18207 = "18207";
+ public static final String s18208 = "18208";
+ public static final String s18209 = "18209";
+ public static final String s18210 = "18210";
+ public static final String s18211 = "18211";
+ public static final String s18212 = "18212";
+ public static final String s18213 = "18213";
+ public static final String s18214 = "18214";
+ public static final String s18215 = "18215";
+ public static final String s18216 = "18216";
+ public static final String s18217 = "18217";
+ public static final String s18218 = "18218";
+ public static final String s18219 = "18219";
+ public static final String s18220 = "18220";
+ public static final String s18221 = "18221";
+ public static final String s18222 = "18222";
+ public static final String s18223 = "18223";
+ public static final String s18224 = "18224";
+ public static final String s18225 = "18225";
+ public static final String s18226 = "18226";
+ public static final String s18227 = "18227";
+ public static final String s18228 = "18228";
+ public static final String s18229 = "18229";
+ public static final String s18230 = "18230";
+ public static final String s18231 = "18231";
+ public static final String s18232 = "18232";
+ public static final String s18233 = "18233";
+ public static final String s18234 = "18234";
+ public static final String s18235 = "18235";
+ public static final String s18236 = "18236";
+ public static final String s18237 = "18237";
+ public static final String s18238 = "18238";
+ public static final String s18239 = "18239";
+ public static final String s18240 = "18240";
+ public static final String s18241 = "18241";
+ public static final String s18242 = "18242";
+ public static final String s18243 = "18243";
+ public static final String s18244 = "18244";
+ public static final String s18245 = "18245";
+ public static final String s18246 = "18246";
+ public static final String s18247 = "18247";
+ public static final String s18248 = "18248";
+ public static final String s18249 = "18249";
+ public static final String s18250 = "18250";
+ public static final String s18251 = "18251";
+ public static final String s18252 = "18252";
+ public static final String s18253 = "18253";
+ public static final String s18254 = "18254";
+ public static final String s18255 = "18255";
+ public static final String s18256 = "18256";
+ public static final String s18257 = "18257";
+ public static final String s18258 = "18258";
+ public static final String s18259 = "18259";
+ public static final String s18260 = "18260";
+ public static final String s18261 = "18261";
+ public static final String s18262 = "18262";
+ public static final String s18263 = "18263";
+ public static final String s18264 = "18264";
+ public static final String s18265 = "18265";
+ public static final String s18266 = "18266";
+ public static final String s18267 = "18267";
+ public static final String s18268 = "18268";
+ public static final String s18269 = "18269";
+ public static final String s18270 = "18270";
+ public static final String s18271 = "18271";
+ public static final String s18272 = "18272";
+ public static final String s18273 = "18273";
+ public static final String s18274 = "18274";
+ public static final String s18275 = "18275";
+ public static final String s18276 = "18276";
+ public static final String s18277 = "18277";
+ public static final String s18278 = "18278";
+ public static final String s18279 = "18279";
+ public static final String s18280 = "18280";
+ public static final String s18281 = "18281";
+ public static final String s18282 = "18282";
+ public static final String s18283 = "18283";
+ public static final String s18284 = "18284";
+ public static final String s18285 = "18285";
+ public static final String s18286 = "18286";
+ public static final String s18287 = "18287";
+ public static final String s18288 = "18288";
+ public static final String s18289 = "18289";
+ public static final String s18290 = "18290";
+ public static final String s18291 = "18291";
+ public static final String s18292 = "18292";
+ public static final String s18293 = "18293";
+ public static final String s18294 = "18294";
+ public static final String s18295 = "18295";
+ public static final String s18296 = "18296";
+ public static final String s18297 = "18297";
+ public static final String s18298 = "18298";
+ public static final String s18299 = "18299";
+ public static final String s18300 = "18300";
+ public static final String s18301 = "18301";
+ public static final String s18302 = "18302";
+ public static final String s18303 = "18303";
+ public static final String s18304 = "18304";
+ public static final String s18305 = "18305";
+ public static final String s18306 = "18306";
+ public static final String s18307 = "18307";
+ public static final String s18308 = "18308";
+ public static final String s18309 = "18309";
+ public static final String s18310 = "18310";
+ public static final String s18311 = "18311";
+ public static final String s18312 = "18312";
+ public static final String s18313 = "18313";
+ public static final String s18314 = "18314";
+ public static final String s18315 = "18315";
+ public static final String s18316 = "18316";
+ public static final String s18317 = "18317";
+ public static final String s18318 = "18318";
+ public static final String s18319 = "18319";
+ public static final String s18320 = "18320";
+ public static final String s18321 = "18321";
+ public static final String s18322 = "18322";
+ public static final String s18323 = "18323";
+ public static final String s18324 = "18324";
+ public static final String s18325 = "18325";
+ public static final String s18326 = "18326";
+ public static final String s18327 = "18327";
+ public static final String s18328 = "18328";
+ public static final String s18329 = "18329";
+ public static final String s18330 = "18330";
+ public static final String s18331 = "18331";
+ public static final String s18332 = "18332";
+ public static final String s18333 = "18333";
+ public static final String s18334 = "18334";
+ public static final String s18335 = "18335";
+ public static final String s18336 = "18336";
+ public static final String s18337 = "18337";
+ public static final String s18338 = "18338";
+ public static final String s18339 = "18339";
+ public static final String s18340 = "18340";
+ public static final String s18341 = "18341";
+ public static final String s18342 = "18342";
+ public static final String s18343 = "18343";
+ public static final String s18344 = "18344";
+ public static final String s18345 = "18345";
+ public static final String s18346 = "18346";
+ public static final String s18347 = "18347";
+ public static final String s18348 = "18348";
+ public static final String s18349 = "18349";
+ public static final String s18350 = "18350";
+ public static final String s18351 = "18351";
+ public static final String s18352 = "18352";
+ public static final String s18353 = "18353";
+ public static final String s18354 = "18354";
+ public static final String s18355 = "18355";
+ public static final String s18356 = "18356";
+ public static final String s18357 = "18357";
+ public static final String s18358 = "18358";
+ public static final String s18359 = "18359";
+ public static final String s18360 = "18360";
+ public static final String s18361 = "18361";
+ public static final String s18362 = "18362";
+ public static final String s18363 = "18363";
+ public static final String s18364 = "18364";
+ public static final String s18365 = "18365";
+ public static final String s18366 = "18366";
+ public static final String s18367 = "18367";
+ public static final String s18368 = "18368";
+ public static final String s18369 = "18369";
+ public static final String s18370 = "18370";
+ public static final String s18371 = "18371";
+ public static final String s18372 = "18372";
+ public static final String s18373 = "18373";
+ public static final String s18374 = "18374";
+ public static final String s18375 = "18375";
+ public static final String s18376 = "18376";
+ public static final String s18377 = "18377";
+ public static final String s18378 = "18378";
+ public static final String s18379 = "18379";
+ public static final String s18380 = "18380";
+ public static final String s18381 = "18381";
+ public static final String s18382 = "18382";
+ public static final String s18383 = "18383";
+ public static final String s18384 = "18384";
+ public static final String s18385 = "18385";
+ public static final String s18386 = "18386";
+ public static final String s18387 = "18387";
+ public static final String s18388 = "18388";
+ public static final String s18389 = "18389";
+ public static final String s18390 = "18390";
+ public static final String s18391 = "18391";
+ public static final String s18392 = "18392";
+ public static final String s18393 = "18393";
+ public static final String s18394 = "18394";
+ public static final String s18395 = "18395";
+ public static final String s18396 = "18396";
+ public static final String s18397 = "18397";
+ public static final String s18398 = "18398";
+ public static final String s18399 = "18399";
+ public static final String s18400 = "18400";
+ public static final String s18401 = "18401";
+ public static final String s18402 = "18402";
+ public static final String s18403 = "18403";
+ public static final String s18404 = "18404";
+ public static final String s18405 = "18405";
+ public static final String s18406 = "18406";
+ public static final String s18407 = "18407";
+ public static final String s18408 = "18408";
+ public static final String s18409 = "18409";
+ public static final String s18410 = "18410";
+ public static final String s18411 = "18411";
+ public static final String s18412 = "18412";
+ public static final String s18413 = "18413";
+ public static final String s18414 = "18414";
+ public static final String s18415 = "18415";
+ public static final String s18416 = "18416";
+ public static final String s18417 = "18417";
+ public static final String s18418 = "18418";
+ public static final String s18419 = "18419";
+ public static final String s18420 = "18420";
+ public static final String s18421 = "18421";
+ public static final String s18422 = "18422";
+ public static final String s18423 = "18423";
+ public static final String s18424 = "18424";
+ public static final String s18425 = "18425";
+ public static final String s18426 = "18426";
+ public static final String s18427 = "18427";
+ public static final String s18428 = "18428";
+ public static final String s18429 = "18429";
+ public static final String s18430 = "18430";
+ public static final String s18431 = "18431";
+ public static final String s18432 = "18432";
+ public static final String s18433 = "18433";
+ public static final String s18434 = "18434";
+ public static final String s18435 = "18435";
+ public static final String s18436 = "18436";
+ public static final String s18437 = "18437";
+ public static final String s18438 = "18438";
+ public static final String s18439 = "18439";
+ public static final String s18440 = "18440";
+ public static final String s18441 = "18441";
+ public static final String s18442 = "18442";
+ public static final String s18443 = "18443";
+ public static final String s18444 = "18444";
+ public static final String s18445 = "18445";
+ public static final String s18446 = "18446";
+ public static final String s18447 = "18447";
+ public static final String s18448 = "18448";
+ public static final String s18449 = "18449";
+ public static final String s18450 = "18450";
+ public static final String s18451 = "18451";
+ public static final String s18452 = "18452";
+ public static final String s18453 = "18453";
+ public static final String s18454 = "18454";
+ public static final String s18455 = "18455";
+ public static final String s18456 = "18456";
+ public static final String s18457 = "18457";
+ public static final String s18458 = "18458";
+ public static final String s18459 = "18459";
+ public static final String s18460 = "18460";
+ public static final String s18461 = "18461";
+ public static final String s18462 = "18462";
+ public static final String s18463 = "18463";
+ public static final String s18464 = "18464";
+ public static final String s18465 = "18465";
+ public static final String s18466 = "18466";
+ public static final String s18467 = "18467";
+ public static final String s18468 = "18468";
+ public static final String s18469 = "18469";
+ public static final String s18470 = "18470";
+ public static final String s18471 = "18471";
+ public static final String s18472 = "18472";
+ public static final String s18473 = "18473";
+ public static final String s18474 = "18474";
+ public static final String s18475 = "18475";
+ public static final String s18476 = "18476";
+ public static final String s18477 = "18477";
+ public static final String s18478 = "18478";
+ public static final String s18479 = "18479";
+ public static final String s18480 = "18480";
+ public static final String s18481 = "18481";
+ public static final String s18482 = "18482";
+ public static final String s18483 = "18483";
+ public static final String s18484 = "18484";
+ public static final String s18485 = "18485";
+ public static final String s18486 = "18486";
+ public static final String s18487 = "18487";
+ public static final String s18488 = "18488";
+ public static final String s18489 = "18489";
+ public static final String s18490 = "18490";
+ public static final String s18491 = "18491";
+ public static final String s18492 = "18492";
+ public static final String s18493 = "18493";
+ public static final String s18494 = "18494";
+ public static final String s18495 = "18495";
+ public static final String s18496 = "18496";
+ public static final String s18497 = "18497";
+ public static final String s18498 = "18498";
+ public static final String s18499 = "18499";
+ public static final String s18500 = "18500";
+ public static final String s18501 = "18501";
+ public static final String s18502 = "18502";
+ public static final String s18503 = "18503";
+ public static final String s18504 = "18504";
+ public static final String s18505 = "18505";
+ public static final String s18506 = "18506";
+ public static final String s18507 = "18507";
+ public static final String s18508 = "18508";
+ public static final String s18509 = "18509";
+ public static final String s18510 = "18510";
+ public static final String s18511 = "18511";
+ public static final String s18512 = "18512";
+ public static final String s18513 = "18513";
+ public static final String s18514 = "18514";
+ public static final String s18515 = "18515";
+ public static final String s18516 = "18516";
+ public static final String s18517 = "18517";
+ public static final String s18518 = "18518";
+ public static final String s18519 = "18519";
+ public static final String s18520 = "18520";
+ public static final String s18521 = "18521";
+ public static final String s18522 = "18522";
+ public static final String s18523 = "18523";
+ public static final String s18524 = "18524";
+ public static final String s18525 = "18525";
+ public static final String s18526 = "18526";
+ public static final String s18527 = "18527";
+ public static final String s18528 = "18528";
+ public static final String s18529 = "18529";
+ public static final String s18530 = "18530";
+ public static final String s18531 = "18531";
+ public static final String s18532 = "18532";
+ public static final String s18533 = "18533";
+ public static final String s18534 = "18534";
+ public static final String s18535 = "18535";
+ public static final String s18536 = "18536";
+ public static final String s18537 = "18537";
+ public static final String s18538 = "18538";
+ public static final String s18539 = "18539";
+ public static final String s18540 = "18540";
+ public static final String s18541 = "18541";
+ public static final String s18542 = "18542";
+ public static final String s18543 = "18543";
+ public static final String s18544 = "18544";
+ public static final String s18545 = "18545";
+ public static final String s18546 = "18546";
+ public static final String s18547 = "18547";
+ public static final String s18548 = "18548";
+ public static final String s18549 = "18549";
+ public static final String s18550 = "18550";
+ public static final String s18551 = "18551";
+ public static final String s18552 = "18552";
+ public static final String s18553 = "18553";
+ public static final String s18554 = "18554";
+ public static final String s18555 = "18555";
+ public static final String s18556 = "18556";
+ public static final String s18557 = "18557";
+ public static final String s18558 = "18558";
+ public static final String s18559 = "18559";
+ public static final String s18560 = "18560";
+ public static final String s18561 = "18561";
+ public static final String s18562 = "18562";
+ public static final String s18563 = "18563";
+ public static final String s18564 = "18564";
+ public static final String s18565 = "18565";
+ public static final String s18566 = "18566";
+ public static final String s18567 = "18567";
+ public static final String s18568 = "18568";
+ public static final String s18569 = "18569";
+ public static final String s18570 = "18570";
+ public static final String s18571 = "18571";
+ public static final String s18572 = "18572";
+ public static final String s18573 = "18573";
+ public static final String s18574 = "18574";
+ public static final String s18575 = "18575";
+ public static final String s18576 = "18576";
+ public static final String s18577 = "18577";
+ public static final String s18578 = "18578";
+ public static final String s18579 = "18579";
+ public static final String s18580 = "18580";
+ public static final String s18581 = "18581";
+ public static final String s18582 = "18582";
+ public static final String s18583 = "18583";
+ public static final String s18584 = "18584";
+ public static final String s18585 = "18585";
+ public static final String s18586 = "18586";
+ public static final String s18587 = "18587";
+ public static final String s18588 = "18588";
+ public static final String s18589 = "18589";
+ public static final String s18590 = "18590";
+ public static final String s18591 = "18591";
+ public static final String s18592 = "18592";
+ public static final String s18593 = "18593";
+ public static final String s18594 = "18594";
+ public static final String s18595 = "18595";
+ public static final String s18596 = "18596";
+ public static final String s18597 = "18597";
+ public static final String s18598 = "18598";
+ public static final String s18599 = "18599";
+ public static final String s18600 = "18600";
+ public static final String s18601 = "18601";
+ public static final String s18602 = "18602";
+ public static final String s18603 = "18603";
+ public static final String s18604 = "18604";
+ public static final String s18605 = "18605";
+ public static final String s18606 = "18606";
+ public static final String s18607 = "18607";
+ public static final String s18608 = "18608";
+ public static final String s18609 = "18609";
+ public static final String s18610 = "18610";
+ public static final String s18611 = "18611";
+ public static final String s18612 = "18612";
+ public static final String s18613 = "18613";
+ public static final String s18614 = "18614";
+ public static final String s18615 = "18615";
+ public static final String s18616 = "18616";
+ public static final String s18617 = "18617";
+ public static final String s18618 = "18618";
+ public static final String s18619 = "18619";
+ public static final String s18620 = "18620";
+ public static final String s18621 = "18621";
+ public static final String s18622 = "18622";
+ public static final String s18623 = "18623";
+ public static final String s18624 = "18624";
+ public static final String s18625 = "18625";
+ public static final String s18626 = "18626";
+ public static final String s18627 = "18627";
+ public static final String s18628 = "18628";
+ public static final String s18629 = "18629";
+ public static final String s18630 = "18630";
+ public static final String s18631 = "18631";
+ public static final String s18632 = "18632";
+ public static final String s18633 = "18633";
+ public static final String s18634 = "18634";
+ public static final String s18635 = "18635";
+ public static final String s18636 = "18636";
+ public static final String s18637 = "18637";
+ public static final String s18638 = "18638";
+ public static final String s18639 = "18639";
+ public static final String s18640 = "18640";
+ public static final String s18641 = "18641";
+ public static final String s18642 = "18642";
+ public static final String s18643 = "18643";
+ public static final String s18644 = "18644";
+ public static final String s18645 = "18645";
+ public static final String s18646 = "18646";
+ public static final String s18647 = "18647";
+ public static final String s18648 = "18648";
+ public static final String s18649 = "18649";
+ public static final String s18650 = "18650";
+ public static final String s18651 = "18651";
+ public static final String s18652 = "18652";
+ public static final String s18653 = "18653";
+ public static final String s18654 = "18654";
+ public static final String s18655 = "18655";
+ public static final String s18656 = "18656";
+ public static final String s18657 = "18657";
+ public static final String s18658 = "18658";
+ public static final String s18659 = "18659";
+ public static final String s18660 = "18660";
+ public static final String s18661 = "18661";
+ public static final String s18662 = "18662";
+ public static final String s18663 = "18663";
+ public static final String s18664 = "18664";
+ public static final String s18665 = "18665";
+ public static final String s18666 = "18666";
+ public static final String s18667 = "18667";
+ public static final String s18668 = "18668";
+ public static final String s18669 = "18669";
+ public static final String s18670 = "18670";
+ public static final String s18671 = "18671";
+ public static final String s18672 = "18672";
+ public static final String s18673 = "18673";
+ public static final String s18674 = "18674";
+ public static final String s18675 = "18675";
+ public static final String s18676 = "18676";
+ public static final String s18677 = "18677";
+ public static final String s18678 = "18678";
+ public static final String s18679 = "18679";
+ public static final String s18680 = "18680";
+ public static final String s18681 = "18681";
+ public static final String s18682 = "18682";
+ public static final String s18683 = "18683";
+ public static final String s18684 = "18684";
+ public static final String s18685 = "18685";
+ public static final String s18686 = "18686";
+ public static final String s18687 = "18687";
+ public static final String s18688 = "18688";
+ public static final String s18689 = "18689";
+ public static final String s18690 = "18690";
+ public static final String s18691 = "18691";
+ public static final String s18692 = "18692";
+ public static final String s18693 = "18693";
+ public static final String s18694 = "18694";
+ public static final String s18695 = "18695";
+ public static final String s18696 = "18696";
+ public static final String s18697 = "18697";
+ public static final String s18698 = "18698";
+ public static final String s18699 = "18699";
+ public static final String s18700 = "18700";
+ public static final String s18701 = "18701";
+ public static final String s18702 = "18702";
+ public static final String s18703 = "18703";
+ public static final String s18704 = "18704";
+ public static final String s18705 = "18705";
+ public static final String s18706 = "18706";
+ public static final String s18707 = "18707";
+ public static final String s18708 = "18708";
+ public static final String s18709 = "18709";
+ public static final String s18710 = "18710";
+ public static final String s18711 = "18711";
+ public static final String s18712 = "18712";
+ public static final String s18713 = "18713";
+ public static final String s18714 = "18714";
+ public static final String s18715 = "18715";
+ public static final String s18716 = "18716";
+ public static final String s18717 = "18717";
+ public static final String s18718 = "18718";
+ public static final String s18719 = "18719";
+ public static final String s18720 = "18720";
+ public static final String s18721 = "18721";
+ public static final String s18722 = "18722";
+ public static final String s18723 = "18723";
+ public static final String s18724 = "18724";
+ public static final String s18725 = "18725";
+ public static final String s18726 = "18726";
+ public static final String s18727 = "18727";
+ public static final String s18728 = "18728";
+ public static final String s18729 = "18729";
+ public static final String s18730 = "18730";
+ public static final String s18731 = "18731";
+ public static final String s18732 = "18732";
+ public static final String s18733 = "18733";
+ public static final String s18734 = "18734";
+ public static final String s18735 = "18735";
+ public static final String s18736 = "18736";
+ public static final String s18737 = "18737";
+ public static final String s18738 = "18738";
+ public static final String s18739 = "18739";
+ public static final String s18740 = "18740";
+ public static final String s18741 = "18741";
+ public static final String s18742 = "18742";
+ public static final String s18743 = "18743";
+ public static final String s18744 = "18744";
+ public static final String s18745 = "18745";
+ public static final String s18746 = "18746";
+ public static final String s18747 = "18747";
+ public static final String s18748 = "18748";
+ public static final String s18749 = "18749";
+ public static final String s18750 = "18750";
+ public static final String s18751 = "18751";
+ public static final String s18752 = "18752";
+ public static final String s18753 = "18753";
+ public static final String s18754 = "18754";
+ public static final String s18755 = "18755";
+ public static final String s18756 = "18756";
+ public static final String s18757 = "18757";
+ public static final String s18758 = "18758";
+ public static final String s18759 = "18759";
+ public static final String s18760 = "18760";
+ public static final String s18761 = "18761";
+ public static final String s18762 = "18762";
+ public static final String s18763 = "18763";
+ public static final String s18764 = "18764";
+ public static final String s18765 = "18765";
+ public static final String s18766 = "18766";
+ public static final String s18767 = "18767";
+ public static final String s18768 = "18768";
+ public static final String s18769 = "18769";
+ public static final String s18770 = "18770";
+ public static final String s18771 = "18771";
+ public static final String s18772 = "18772";
+ public static final String s18773 = "18773";
+ public static final String s18774 = "18774";
+ public static final String s18775 = "18775";
+ public static final String s18776 = "18776";
+ public static final String s18777 = "18777";
+ public static final String s18778 = "18778";
+ public static final String s18779 = "18779";
+ public static final String s18780 = "18780";
+ public static final String s18781 = "18781";
+ public static final String s18782 = "18782";
+ public static final String s18783 = "18783";
+ public static final String s18784 = "18784";
+ public static final String s18785 = "18785";
+ public static final String s18786 = "18786";
+ public static final String s18787 = "18787";
+ public static final String s18788 = "18788";
+ public static final String s18789 = "18789";
+ public static final String s18790 = "18790";
+ public static final String s18791 = "18791";
+ public static final String s18792 = "18792";
+ public static final String s18793 = "18793";
+ public static final String s18794 = "18794";
+ public static final String s18795 = "18795";
+ public static final String s18796 = "18796";
+ public static final String s18797 = "18797";
+ public static final String s18798 = "18798";
+ public static final String s18799 = "18799";
+ public static final String s18800 = "18800";
+ public static final String s18801 = "18801";
+ public static final String s18802 = "18802";
+ public static final String s18803 = "18803";
+ public static final String s18804 = "18804";
+ public static final String s18805 = "18805";
+ public static final String s18806 = "18806";
+ public static final String s18807 = "18807";
+ public static final String s18808 = "18808";
+ public static final String s18809 = "18809";
+ public static final String s18810 = "18810";
+ public static final String s18811 = "18811";
+ public static final String s18812 = "18812";
+ public static final String s18813 = "18813";
+ public static final String s18814 = "18814";
+ public static final String s18815 = "18815";
+ public static final String s18816 = "18816";
+ public static final String s18817 = "18817";
+ public static final String s18818 = "18818";
+ public static final String s18819 = "18819";
+ public static final String s18820 = "18820";
+ public static final String s18821 = "18821";
+ public static final String s18822 = "18822";
+ public static final String s18823 = "18823";
+ public static final String s18824 = "18824";
+ public static final String s18825 = "18825";
+ public static final String s18826 = "18826";
+ public static final String s18827 = "18827";
+ public static final String s18828 = "18828";
+ public static final String s18829 = "18829";
+ public static final String s18830 = "18830";
+ public static final String s18831 = "18831";
+ public static final String s18832 = "18832";
+ public static final String s18833 = "18833";
+ public static final String s18834 = "18834";
+ public static final String s18835 = "18835";
+ public static final String s18836 = "18836";
+ public static final String s18837 = "18837";
+ public static final String s18838 = "18838";
+ public static final String s18839 = "18839";
+ public static final String s18840 = "18840";
+ public static final String s18841 = "18841";
+ public static final String s18842 = "18842";
+ public static final String s18843 = "18843";
+ public static final String s18844 = "18844";
+ public static final String s18845 = "18845";
+ public static final String s18846 = "18846";
+ public static final String s18847 = "18847";
+ public static final String s18848 = "18848";
+ public static final String s18849 = "18849";
+ public static final String s18850 = "18850";
+ public static final String s18851 = "18851";
+ public static final String s18852 = "18852";
+ public static final String s18853 = "18853";
+ public static final String s18854 = "18854";
+ public static final String s18855 = "18855";
+ public static final String s18856 = "18856";
+ public static final String s18857 = "18857";
+ public static final String s18858 = "18858";
+ public static final String s18859 = "18859";
+ public static final String s18860 = "18860";
+ public static final String s18861 = "18861";
+ public static final String s18862 = "18862";
+ public static final String s18863 = "18863";
+ public static final String s18864 = "18864";
+ public static final String s18865 = "18865";
+ public static final String s18866 = "18866";
+ public static final String s18867 = "18867";
+ public static final String s18868 = "18868";
+ public static final String s18869 = "18869";
+ public static final String s18870 = "18870";
+ public static final String s18871 = "18871";
+ public static final String s18872 = "18872";
+ public static final String s18873 = "18873";
+ public static final String s18874 = "18874";
+ public static final String s18875 = "18875";
+ public static final String s18876 = "18876";
+ public static final String s18877 = "18877";
+ public static final String s18878 = "18878";
+ public static final String s18879 = "18879";
+ public static final String s18880 = "18880";
+ public static final String s18881 = "18881";
+ public static final String s18882 = "18882";
+ public static final String s18883 = "18883";
+ public static final String s18884 = "18884";
+ public static final String s18885 = "18885";
+ public static final String s18886 = "18886";
+ public static final String s18887 = "18887";
+ public static final String s18888 = "18888";
+ public static final String s18889 = "18889";
+ public static final String s18890 = "18890";
+ public static final String s18891 = "18891";
+ public static final String s18892 = "18892";
+ public static final String s18893 = "18893";
+ public static final String s18894 = "18894";
+ public static final String s18895 = "18895";
+ public static final String s18896 = "18896";
+ public static final String s18897 = "18897";
+ public static final String s18898 = "18898";
+ public static final String s18899 = "18899";
+ public static final String s18900 = "18900";
+ public static final String s18901 = "18901";
+ public static final String s18902 = "18902";
+ public static final String s18903 = "18903";
+ public static final String s18904 = "18904";
+ public static final String s18905 = "18905";
+ public static final String s18906 = "18906";
+ public static final String s18907 = "18907";
+ public static final String s18908 = "18908";
+ public static final String s18909 = "18909";
+ public static final String s18910 = "18910";
+ public static final String s18911 = "18911";
+ public static final String s18912 = "18912";
+ public static final String s18913 = "18913";
+ public static final String s18914 = "18914";
+ public static final String s18915 = "18915";
+ public static final String s18916 = "18916";
+ public static final String s18917 = "18917";
+ public static final String s18918 = "18918";
+ public static final String s18919 = "18919";
+ public static final String s18920 = "18920";
+ public static final String s18921 = "18921";
+ public static final String s18922 = "18922";
+ public static final String s18923 = "18923";
+ public static final String s18924 = "18924";
+ public static final String s18925 = "18925";
+ public static final String s18926 = "18926";
+ public static final String s18927 = "18927";
+ public static final String s18928 = "18928";
+ public static final String s18929 = "18929";
+ public static final String s18930 = "18930";
+ public static final String s18931 = "18931";
+ public static final String s18932 = "18932";
+ public static final String s18933 = "18933";
+ public static final String s18934 = "18934";
+ public static final String s18935 = "18935";
+ public static final String s18936 = "18936";
+ public static final String s18937 = "18937";
+ public static final String s18938 = "18938";
+ public static final String s18939 = "18939";
+ public static final String s18940 = "18940";
+ public static final String s18941 = "18941";
+ public static final String s18942 = "18942";
+ public static final String s18943 = "18943";
+ public static final String s18944 = "18944";
+ public static final String s18945 = "18945";
+ public static final String s18946 = "18946";
+ public static final String s18947 = "18947";
+ public static final String s18948 = "18948";
+ public static final String s18949 = "18949";
+ public static final String s18950 = "18950";
+ public static final String s18951 = "18951";
+ public static final String s18952 = "18952";
+ public static final String s18953 = "18953";
+ public static final String s18954 = "18954";
+ public static final String s18955 = "18955";
+ public static final String s18956 = "18956";
+ public static final String s18957 = "18957";
+ public static final String s18958 = "18958";
+ public static final String s18959 = "18959";
+ public static final String s18960 = "18960";
+ public static final String s18961 = "18961";
+ public static final String s18962 = "18962";
+ public static final String s18963 = "18963";
+ public static final String s18964 = "18964";
+ public static final String s18965 = "18965";
+ public static final String s18966 = "18966";
+ public static final String s18967 = "18967";
+ public static final String s18968 = "18968";
+ public static final String s18969 = "18969";
+ public static final String s18970 = "18970";
+ public static final String s18971 = "18971";
+ public static final String s18972 = "18972";
+ public static final String s18973 = "18973";
+ public static final String s18974 = "18974";
+ public static final String s18975 = "18975";
+ public static final String s18976 = "18976";
+ public static final String s18977 = "18977";
+ public static final String s18978 = "18978";
+ public static final String s18979 = "18979";
+ public static final String s18980 = "18980";
+ public static final String s18981 = "18981";
+ public static final String s18982 = "18982";
+ public static final String s18983 = "18983";
+ public static final String s18984 = "18984";
+ public static final String s18985 = "18985";
+ public static final String s18986 = "18986";
+ public static final String s18987 = "18987";
+ public static final String s18988 = "18988";
+ public static final String s18989 = "18989";
+ public static final String s18990 = "18990";
+ public static final String s18991 = "18991";
+ public static final String s18992 = "18992";
+ public static final String s18993 = "18993";
+ public static final String s18994 = "18994";
+ public static final String s18995 = "18995";
+ public static final String s18996 = "18996";
+ public static final String s18997 = "18997";
+ public static final String s18998 = "18998";
+ public static final String s18999 = "18999";
+ public static final String s19000 = "19000";
+ public static final String s19001 = "19001";
+ public static final String s19002 = "19002";
+ public static final String s19003 = "19003";
+ public static final String s19004 = "19004";
+ public static final String s19005 = "19005";
+ public static final String s19006 = "19006";
+ public static final String s19007 = "19007";
+ public static final String s19008 = "19008";
+ public static final String s19009 = "19009";
+ public static final String s19010 = "19010";
+ public static final String s19011 = "19011";
+ public static final String s19012 = "19012";
+ public static final String s19013 = "19013";
+ public static final String s19014 = "19014";
+ public static final String s19015 = "19015";
+ public static final String s19016 = "19016";
+ public static final String s19017 = "19017";
+ public static final String s19018 = "19018";
+ public static final String s19019 = "19019";
+ public static final String s19020 = "19020";
+ public static final String s19021 = "19021";
+ public static final String s19022 = "19022";
+ public static final String s19023 = "19023";
+ public static final String s19024 = "19024";
+ public static final String s19025 = "19025";
+ public static final String s19026 = "19026";
+ public static final String s19027 = "19027";
+ public static final String s19028 = "19028";
+ public static final String s19029 = "19029";
+ public static final String s19030 = "19030";
+ public static final String s19031 = "19031";
+ public static final String s19032 = "19032";
+ public static final String s19033 = "19033";
+ public static final String s19034 = "19034";
+ public static final String s19035 = "19035";
+ public static final String s19036 = "19036";
+ public static final String s19037 = "19037";
+ public static final String s19038 = "19038";
+ public static final String s19039 = "19039";
+ public static final String s19040 = "19040";
+ public static final String s19041 = "19041";
+ public static final String s19042 = "19042";
+ public static final String s19043 = "19043";
+ public static final String s19044 = "19044";
+ public static final String s19045 = "19045";
+ public static final String s19046 = "19046";
+ public static final String s19047 = "19047";
+ public static final String s19048 = "19048";
+ public static final String s19049 = "19049";
+ public static final String s19050 = "19050";
+ public static final String s19051 = "19051";
+ public static final String s19052 = "19052";
+ public static final String s19053 = "19053";
+ public static final String s19054 = "19054";
+ public static final String s19055 = "19055";
+ public static final String s19056 = "19056";
+ public static final String s19057 = "19057";
+ public static final String s19058 = "19058";
+ public static final String s19059 = "19059";
+ public static final String s19060 = "19060";
+ public static final String s19061 = "19061";
+ public static final String s19062 = "19062";
+ public static final String s19063 = "19063";
+ public static final String s19064 = "19064";
+ public static final String s19065 = "19065";
+ public static final String s19066 = "19066";
+ public static final String s19067 = "19067";
+ public static final String s19068 = "19068";
+ public static final String s19069 = "19069";
+ public static final String s19070 = "19070";
+ public static final String s19071 = "19071";
+ public static final String s19072 = "19072";
+ public static final String s19073 = "19073";
+ public static final String s19074 = "19074";
+ public static final String s19075 = "19075";
+ public static final String s19076 = "19076";
+ public static final String s19077 = "19077";
+ public static final String s19078 = "19078";
+ public static final String s19079 = "19079";
+ public static final String s19080 = "19080";
+ public static final String s19081 = "19081";
+ public static final String s19082 = "19082";
+ public static final String s19083 = "19083";
+ public static final String s19084 = "19084";
+ public static final String s19085 = "19085";
+ public static final String s19086 = "19086";
+ public static final String s19087 = "19087";
+ public static final String s19088 = "19088";
+ public static final String s19089 = "19089";
+ public static final String s19090 = "19090";
+ public static final String s19091 = "19091";
+ public static final String s19092 = "19092";
+ public static final String s19093 = "19093";
+ public static final String s19094 = "19094";
+ public static final String s19095 = "19095";
+ public static final String s19096 = "19096";
+ public static final String s19097 = "19097";
+ public static final String s19098 = "19098";
+ public static final String s19099 = "19099";
+ public static final String s19100 = "19100";
+ public static final String s19101 = "19101";
+ public static final String s19102 = "19102";
+ public static final String s19103 = "19103";
+ public static final String s19104 = "19104";
+ public static final String s19105 = "19105";
+ public static final String s19106 = "19106";
+ public static final String s19107 = "19107";
+ public static final String s19108 = "19108";
+ public static final String s19109 = "19109";
+ public static final String s19110 = "19110";
+ public static final String s19111 = "19111";
+ public static final String s19112 = "19112";
+ public static final String s19113 = "19113";
+ public static final String s19114 = "19114";
+ public static final String s19115 = "19115";
+ public static final String s19116 = "19116";
+ public static final String s19117 = "19117";
+ public static final String s19118 = "19118";
+ public static final String s19119 = "19119";
+ public static final String s19120 = "19120";
+ public static final String s19121 = "19121";
+ public static final String s19122 = "19122";
+ public static final String s19123 = "19123";
+ public static final String s19124 = "19124";
+ public static final String s19125 = "19125";
+ public static final String s19126 = "19126";
+ public static final String s19127 = "19127";
+ public static final String s19128 = "19128";
+ public static final String s19129 = "19129";
+ public static final String s19130 = "19130";
+ public static final String s19131 = "19131";
+ public static final String s19132 = "19132";
+ public static final String s19133 = "19133";
+ public static final String s19134 = "19134";
+ public static final String s19135 = "19135";
+ public static final String s19136 = "19136";
+ public static final String s19137 = "19137";
+ public static final String s19138 = "19138";
+ public static final String s19139 = "19139";
+ public static final String s19140 = "19140";
+ public static final String s19141 = "19141";
+ public static final String s19142 = "19142";
+ public static final String s19143 = "19143";
+ public static final String s19144 = "19144";
+ public static final String s19145 = "19145";
+ public static final String s19146 = "19146";
+ public static final String s19147 = "19147";
+ public static final String s19148 = "19148";
+ public static final String s19149 = "19149";
+ public static final String s19150 = "19150";
+ public static final String s19151 = "19151";
+ public static final String s19152 = "19152";
+ public static final String s19153 = "19153";
+ public static final String s19154 = "19154";
+ public static final String s19155 = "19155";
+ public static final String s19156 = "19156";
+ public static final String s19157 = "19157";
+ public static final String s19158 = "19158";
+ public static final String s19159 = "19159";
+ public static final String s19160 = "19160";
+ public static final String s19161 = "19161";
+ public static final String s19162 = "19162";
+ public static final String s19163 = "19163";
+ public static final String s19164 = "19164";
+ public static final String s19165 = "19165";
+ public static final String s19166 = "19166";
+ public static final String s19167 = "19167";
+ public static final String s19168 = "19168";
+ public static final String s19169 = "19169";
+ public static final String s19170 = "19170";
+ public static final String s19171 = "19171";
+ public static final String s19172 = "19172";
+ public static final String s19173 = "19173";
+ public static final String s19174 = "19174";
+ public static final String s19175 = "19175";
+ public static final String s19176 = "19176";
+ public static final String s19177 = "19177";
+ public static final String s19178 = "19178";
+ public static final String s19179 = "19179";
+ public static final String s19180 = "19180";
+ public static final String s19181 = "19181";
+ public static final String s19182 = "19182";
+ public static final String s19183 = "19183";
+ public static final String s19184 = "19184";
+ public static final String s19185 = "19185";
+ public static final String s19186 = "19186";
+ public static final String s19187 = "19187";
+ public static final String s19188 = "19188";
+ public static final String s19189 = "19189";
+ public static final String s19190 = "19190";
+ public static final String s19191 = "19191";
+ public static final String s19192 = "19192";
+ public static final String s19193 = "19193";
+ public static final String s19194 = "19194";
+ public static final String s19195 = "19195";
+ public static final String s19196 = "19196";
+ public static final String s19197 = "19197";
+ public static final String s19198 = "19198";
+ public static final String s19199 = "19199";
+ public static final String s19200 = "19200";
+ public static final String s19201 = "19201";
+ public static final String s19202 = "19202";
+ public static final String s19203 = "19203";
+ public static final String s19204 = "19204";
+ public static final String s19205 = "19205";
+ public static final String s19206 = "19206";
+ public static final String s19207 = "19207";
+ public static final String s19208 = "19208";
+ public static final String s19209 = "19209";
+ public static final String s19210 = "19210";
+ public static final String s19211 = "19211";
+ public static final String s19212 = "19212";
+ public static final String s19213 = "19213";
+ public static final String s19214 = "19214";
+ public static final String s19215 = "19215";
+ public static final String s19216 = "19216";
+ public static final String s19217 = "19217";
+ public static final String s19218 = "19218";
+ public static final String s19219 = "19219";
+ public static final String s19220 = "19220";
+ public static final String s19221 = "19221";
+ public static final String s19222 = "19222";
+ public static final String s19223 = "19223";
+ public static final String s19224 = "19224";
+ public static final String s19225 = "19225";
+ public static final String s19226 = "19226";
+ public static final String s19227 = "19227";
+ public static final String s19228 = "19228";
+ public static final String s19229 = "19229";
+ public static final String s19230 = "19230";
+ public static final String s19231 = "19231";
+ public static final String s19232 = "19232";
+ public static final String s19233 = "19233";
+ public static final String s19234 = "19234";
+ public static final String s19235 = "19235";
+ public static final String s19236 = "19236";
+ public static final String s19237 = "19237";
+ public static final String s19238 = "19238";
+ public static final String s19239 = "19239";
+ public static final String s19240 = "19240";
+ public static final String s19241 = "19241";
+ public static final String s19242 = "19242";
+ public static final String s19243 = "19243";
+ public static final String s19244 = "19244";
+ public static final String s19245 = "19245";
+ public static final String s19246 = "19246";
+ public static final String s19247 = "19247";
+ public static final String s19248 = "19248";
+ public static final String s19249 = "19249";
+ public static final String s19250 = "19250";
+ public static final String s19251 = "19251";
+ public static final String s19252 = "19252";
+ public static final String s19253 = "19253";
+ public static final String s19254 = "19254";
+ public static final String s19255 = "19255";
+ public static final String s19256 = "19256";
+ public static final String s19257 = "19257";
+ public static final String s19258 = "19258";
+ public static final String s19259 = "19259";
+ public static final String s19260 = "19260";
+ public static final String s19261 = "19261";
+ public static final String s19262 = "19262";
+ public static final String s19263 = "19263";
+ public static final String s19264 = "19264";
+ public static final String s19265 = "19265";
+ public static final String s19266 = "19266";
+ public static final String s19267 = "19267";
+ public static final String s19268 = "19268";
+ public static final String s19269 = "19269";
+ public static final String s19270 = "19270";
+ public static final String s19271 = "19271";
+ public static final String s19272 = "19272";
+ public static final String s19273 = "19273";
+ public static final String s19274 = "19274";
+ public static final String s19275 = "19275";
+ public static final String s19276 = "19276";
+ public static final String s19277 = "19277";
+ public static final String s19278 = "19278";
+ public static final String s19279 = "19279";
+ public static final String s19280 = "19280";
+ public static final String s19281 = "19281";
+ public static final String s19282 = "19282";
+ public static final String s19283 = "19283";
+ public static final String s19284 = "19284";
+ public static final String s19285 = "19285";
+ public static final String s19286 = "19286";
+ public static final String s19287 = "19287";
+ public static final String s19288 = "19288";
+ public static final String s19289 = "19289";
+ public static final String s19290 = "19290";
+ public static final String s19291 = "19291";
+ public static final String s19292 = "19292";
+ public static final String s19293 = "19293";
+ public static final String s19294 = "19294";
+ public static final String s19295 = "19295";
+ public static final String s19296 = "19296";
+ public static final String s19297 = "19297";
+ public static final String s19298 = "19298";
+ public static final String s19299 = "19299";
+ public static final String s19300 = "19300";
+ public static final String s19301 = "19301";
+ public static final String s19302 = "19302";
+ public static final String s19303 = "19303";
+ public static final String s19304 = "19304";
+ public static final String s19305 = "19305";
+ public static final String s19306 = "19306";
+ public static final String s19307 = "19307";
+ public static final String s19308 = "19308";
+ public static final String s19309 = "19309";
+ public static final String s19310 = "19310";
+ public static final String s19311 = "19311";
+ public static final String s19312 = "19312";
+ public static final String s19313 = "19313";
+ public static final String s19314 = "19314";
+ public static final String s19315 = "19315";
+ public static final String s19316 = "19316";
+ public static final String s19317 = "19317";
+ public static final String s19318 = "19318";
+ public static final String s19319 = "19319";
+ public static final String s19320 = "19320";
+ public static final String s19321 = "19321";
+ public static final String s19322 = "19322";
+ public static final String s19323 = "19323";
+ public static final String s19324 = "19324";
+ public static final String s19325 = "19325";
+ public static final String s19326 = "19326";
+ public static final String s19327 = "19327";
+ public static final String s19328 = "19328";
+ public static final String s19329 = "19329";
+ public static final String s19330 = "19330";
+ public static final String s19331 = "19331";
+ public static final String s19332 = "19332";
+ public static final String s19333 = "19333";
+ public static final String s19334 = "19334";
+ public static final String s19335 = "19335";
+ public static final String s19336 = "19336";
+ public static final String s19337 = "19337";
+ public static final String s19338 = "19338";
+ public static final String s19339 = "19339";
+ public static final String s19340 = "19340";
+ public static final String s19341 = "19341";
+ public static final String s19342 = "19342";
+ public static final String s19343 = "19343";
+ public static final String s19344 = "19344";
+ public static final String s19345 = "19345";
+ public static final String s19346 = "19346";
+ public static final String s19347 = "19347";
+ public static final String s19348 = "19348";
+ public static final String s19349 = "19349";
+ public static final String s19350 = "19350";
+ public static final String s19351 = "19351";
+ public static final String s19352 = "19352";
+ public static final String s19353 = "19353";
+ public static final String s19354 = "19354";
+ public static final String s19355 = "19355";
+ public static final String s19356 = "19356";
+ public static final String s19357 = "19357";
+ public static final String s19358 = "19358";
+ public static final String s19359 = "19359";
+ public static final String s19360 = "19360";
+ public static final String s19361 = "19361";
+ public static final String s19362 = "19362";
+ public static final String s19363 = "19363";
+ public static final String s19364 = "19364";
+ public static final String s19365 = "19365";
+ public static final String s19366 = "19366";
+ public static final String s19367 = "19367";
+ public static final String s19368 = "19368";
+ public static final String s19369 = "19369";
+ public static final String s19370 = "19370";
+ public static final String s19371 = "19371";
+ public static final String s19372 = "19372";
+ public static final String s19373 = "19373";
+ public static final String s19374 = "19374";
+ public static final String s19375 = "19375";
+ public static final String s19376 = "19376";
+ public static final String s19377 = "19377";
+ public static final String s19378 = "19378";
+ public static final String s19379 = "19379";
+ public static final String s19380 = "19380";
+ public static final String s19381 = "19381";
+ public static final String s19382 = "19382";
+ public static final String s19383 = "19383";
+ public static final String s19384 = "19384";
+ public static final String s19385 = "19385";
+ public static final String s19386 = "19386";
+ public static final String s19387 = "19387";
+ public static final String s19388 = "19388";
+ public static final String s19389 = "19389";
+ public static final String s19390 = "19390";
+ public static final String s19391 = "19391";
+ public static final String s19392 = "19392";
+ public static final String s19393 = "19393";
+ public static final String s19394 = "19394";
+ public static final String s19395 = "19395";
+ public static final String s19396 = "19396";
+ public static final String s19397 = "19397";
+ public static final String s19398 = "19398";
+ public static final String s19399 = "19399";
+ public static final String s19400 = "19400";
+ public static final String s19401 = "19401";
+ public static final String s19402 = "19402";
+ public static final String s19403 = "19403";
+ public static final String s19404 = "19404";
+ public static final String s19405 = "19405";
+ public static final String s19406 = "19406";
+ public static final String s19407 = "19407";
+ public static final String s19408 = "19408";
+ public static final String s19409 = "19409";
+ public static final String s19410 = "19410";
+ public static final String s19411 = "19411";
+ public static final String s19412 = "19412";
+ public static final String s19413 = "19413";
+ public static final String s19414 = "19414";
+ public static final String s19415 = "19415";
+ public static final String s19416 = "19416";
+ public static final String s19417 = "19417";
+ public static final String s19418 = "19418";
+ public static final String s19419 = "19419";
+ public static final String s19420 = "19420";
+ public static final String s19421 = "19421";
+ public static final String s19422 = "19422";
+ public static final String s19423 = "19423";
+ public static final String s19424 = "19424";
+ public static final String s19425 = "19425";
+ public static final String s19426 = "19426";
+ public static final String s19427 = "19427";
+ public static final String s19428 = "19428";
+ public static final String s19429 = "19429";
+ public static final String s19430 = "19430";
+ public static final String s19431 = "19431";
+ public static final String s19432 = "19432";
+ public static final String s19433 = "19433";
+ public static final String s19434 = "19434";
+ public static final String s19435 = "19435";
+ public static final String s19436 = "19436";
+ public static final String s19437 = "19437";
+ public static final String s19438 = "19438";
+ public static final String s19439 = "19439";
+ public static final String s19440 = "19440";
+ public static final String s19441 = "19441";
+ public static final String s19442 = "19442";
+ public static final String s19443 = "19443";
+ public static final String s19444 = "19444";
+ public static final String s19445 = "19445";
+ public static final String s19446 = "19446";
+ public static final String s19447 = "19447";
+ public static final String s19448 = "19448";
+ public static final String s19449 = "19449";
+ public static final String s19450 = "19450";
+ public static final String s19451 = "19451";
+ public static final String s19452 = "19452";
+ public static final String s19453 = "19453";
+ public static final String s19454 = "19454";
+ public static final String s19455 = "19455";
+ public static final String s19456 = "19456";
+ public static final String s19457 = "19457";
+ public static final String s19458 = "19458";
+ public static final String s19459 = "19459";
+ public static final String s19460 = "19460";
+ public static final String s19461 = "19461";
+ public static final String s19462 = "19462";
+ public static final String s19463 = "19463";
+ public static final String s19464 = "19464";
+ public static final String s19465 = "19465";
+ public static final String s19466 = "19466";
+ public static final String s19467 = "19467";
+ public static final String s19468 = "19468";
+ public static final String s19469 = "19469";
+ public static final String s19470 = "19470";
+ public static final String s19471 = "19471";
+ public static final String s19472 = "19472";
+ public static final String s19473 = "19473";
+ public static final String s19474 = "19474";
+ public static final String s19475 = "19475";
+ public static final String s19476 = "19476";
+ public static final String s19477 = "19477";
+ public static final String s19478 = "19478";
+ public static final String s19479 = "19479";
+ public static final String s19480 = "19480";
+ public static final String s19481 = "19481";
+ public static final String s19482 = "19482";
+ public static final String s19483 = "19483";
+ public static final String s19484 = "19484";
+ public static final String s19485 = "19485";
+ public static final String s19486 = "19486";
+ public static final String s19487 = "19487";
+ public static final String s19488 = "19488";
+ public static final String s19489 = "19489";
+ public static final String s19490 = "19490";
+ public static final String s19491 = "19491";
+ public static final String s19492 = "19492";
+ public static final String s19493 = "19493";
+ public static final String s19494 = "19494";
+ public static final String s19495 = "19495";
+ public static final String s19496 = "19496";
+ public static final String s19497 = "19497";
+ public static final String s19498 = "19498";
+ public static final String s19499 = "19499";
+ public static final String s19500 = "19500";
+ public static final String s19501 = "19501";
+ public static final String s19502 = "19502";
+ public static final String s19503 = "19503";
+ public static final String s19504 = "19504";
+ public static final String s19505 = "19505";
+ public static final String s19506 = "19506";
+ public static final String s19507 = "19507";
+ public static final String s19508 = "19508";
+ public static final String s19509 = "19509";
+ public static final String s19510 = "19510";
+ public static final String s19511 = "19511";
+ public static final String s19512 = "19512";
+ public static final String s19513 = "19513";
+ public static final String s19514 = "19514";
+ public static final String s19515 = "19515";
+ public static final String s19516 = "19516";
+ public static final String s19517 = "19517";
+ public static final String s19518 = "19518";
+ public static final String s19519 = "19519";
+ public static final String s19520 = "19520";
+ public static final String s19521 = "19521";
+ public static final String s19522 = "19522";
+ public static final String s19523 = "19523";
+ public static final String s19524 = "19524";
+ public static final String s19525 = "19525";
+ public static final String s19526 = "19526";
+ public static final String s19527 = "19527";
+ public static final String s19528 = "19528";
+ public static final String s19529 = "19529";
+ public static final String s19530 = "19530";
+ public static final String s19531 = "19531";
+ public static final String s19532 = "19532";
+ public static final String s19533 = "19533";
+ public static final String s19534 = "19534";
+ public static final String s19535 = "19535";
+ public static final String s19536 = "19536";
+ public static final String s19537 = "19537";
+ public static final String s19538 = "19538";
+ public static final String s19539 = "19539";
+ public static final String s19540 = "19540";
+ public static final String s19541 = "19541";
+ public static final String s19542 = "19542";
+ public static final String s19543 = "19543";
+ public static final String s19544 = "19544";
+ public static final String s19545 = "19545";
+ public static final String s19546 = "19546";
+ public static final String s19547 = "19547";
+ public static final String s19548 = "19548";
+ public static final String s19549 = "19549";
+ public static final String s19550 = "19550";
+ public static final String s19551 = "19551";
+ public static final String s19552 = "19552";
+ public static final String s19553 = "19553";
+ public static final String s19554 = "19554";
+ public static final String s19555 = "19555";
+ public static final String s19556 = "19556";
+ public static final String s19557 = "19557";
+ public static final String s19558 = "19558";
+ public static final String s19559 = "19559";
+ public static final String s19560 = "19560";
+ public static final String s19561 = "19561";
+ public static final String s19562 = "19562";
+ public static final String s19563 = "19563";
+ public static final String s19564 = "19564";
+ public static final String s19565 = "19565";
+ public static final String s19566 = "19566";
+ public static final String s19567 = "19567";
+ public static final String s19568 = "19568";
+ public static final String s19569 = "19569";
+ public static final String s19570 = "19570";
+ public static final String s19571 = "19571";
+ public static final String s19572 = "19572";
+ public static final String s19573 = "19573";
+ public static final String s19574 = "19574";
+ public static final String s19575 = "19575";
+ public static final String s19576 = "19576";
+ public static final String s19577 = "19577";
+ public static final String s19578 = "19578";
+ public static final String s19579 = "19579";
+ public static final String s19580 = "19580";
+ public static final String s19581 = "19581";
+ public static final String s19582 = "19582";
+ public static final String s19583 = "19583";
+ public static final String s19584 = "19584";
+ public static final String s19585 = "19585";
+ public static final String s19586 = "19586";
+ public static final String s19587 = "19587";
+ public static final String s19588 = "19588";
+ public static final String s19589 = "19589";
+ public static final String s19590 = "19590";
+ public static final String s19591 = "19591";
+ public static final String s19592 = "19592";
+ public static final String s19593 = "19593";
+ public static final String s19594 = "19594";
+ public static final String s19595 = "19595";
+ public static final String s19596 = "19596";
+ public static final String s19597 = "19597";
+ public static final String s19598 = "19598";
+ public static final String s19599 = "19599";
+ public static final String s19600 = "19600";
+ public static final String s19601 = "19601";
+ public static final String s19602 = "19602";
+ public static final String s19603 = "19603";
+ public static final String s19604 = "19604";
+ public static final String s19605 = "19605";
+ public static final String s19606 = "19606";
+ public static final String s19607 = "19607";
+ public static final String s19608 = "19608";
+ public static final String s19609 = "19609";
+ public static final String s19610 = "19610";
+ public static final String s19611 = "19611";
+ public static final String s19612 = "19612";
+ public static final String s19613 = "19613";
+ public static final String s19614 = "19614";
+ public static final String s19615 = "19615";
+ public static final String s19616 = "19616";
+ public static final String s19617 = "19617";
+ public static final String s19618 = "19618";
+ public static final String s19619 = "19619";
+ public static final String s19620 = "19620";
+ public static final String s19621 = "19621";
+ public static final String s19622 = "19622";
+ public static final String s19623 = "19623";
+ public static final String s19624 = "19624";
+ public static final String s19625 = "19625";
+ public static final String s19626 = "19626";
+ public static final String s19627 = "19627";
+ public static final String s19628 = "19628";
+ public static final String s19629 = "19629";
+ public static final String s19630 = "19630";
+ public static final String s19631 = "19631";
+ public static final String s19632 = "19632";
+ public static final String s19633 = "19633";
+ public static final String s19634 = "19634";
+ public static final String s19635 = "19635";
+ public static final String s19636 = "19636";
+ public static final String s19637 = "19637";
+ public static final String s19638 = "19638";
+ public static final String s19639 = "19639";
+ public static final String s19640 = "19640";
+ public static final String s19641 = "19641";
+ public static final String s19642 = "19642";
+ public static final String s19643 = "19643";
+ public static final String s19644 = "19644";
+ public static final String s19645 = "19645";
+ public static final String s19646 = "19646";
+ public static final String s19647 = "19647";
+ public static final String s19648 = "19648";
+ public static final String s19649 = "19649";
+ public static final String s19650 = "19650";
+ public static final String s19651 = "19651";
+ public static final String s19652 = "19652";
+ public static final String s19653 = "19653";
+ public static final String s19654 = "19654";
+ public static final String s19655 = "19655";
+ public static final String s19656 = "19656";
+ public static final String s19657 = "19657";
+ public static final String s19658 = "19658";
+ public static final String s19659 = "19659";
+ public static final String s19660 = "19660";
+ public static final String s19661 = "19661";
+ public static final String s19662 = "19662";
+ public static final String s19663 = "19663";
+ public static final String s19664 = "19664";
+ public static final String s19665 = "19665";
+ public static final String s19666 = "19666";
+ public static final String s19667 = "19667";
+ public static final String s19668 = "19668";
+ public static final String s19669 = "19669";
+ public static final String s19670 = "19670";
+ public static final String s19671 = "19671";
+ public static final String s19672 = "19672";
+ public static final String s19673 = "19673";
+ public static final String s19674 = "19674";
+ public static final String s19675 = "19675";
+ public static final String s19676 = "19676";
+ public static final String s19677 = "19677";
+ public static final String s19678 = "19678";
+ public static final String s19679 = "19679";
+ public static final String s19680 = "19680";
+ public static final String s19681 = "19681";
+ public static final String s19682 = "19682";
+ public static final String s19683 = "19683";
+ public static final String s19684 = "19684";
+ public static final String s19685 = "19685";
+ public static final String s19686 = "19686";
+ public static final String s19687 = "19687";
+ public static final String s19688 = "19688";
+ public static final String s19689 = "19689";
+ public static final String s19690 = "19690";
+ public static final String s19691 = "19691";
+ public static final String s19692 = "19692";
+ public static final String s19693 = "19693";
+ public static final String s19694 = "19694";
+ public static final String s19695 = "19695";
+ public static final String s19696 = "19696";
+ public static final String s19697 = "19697";
+ public static final String s19698 = "19698";
+ public static final String s19699 = "19699";
+ public static final String s19700 = "19700";
+ public static final String s19701 = "19701";
+ public static final String s19702 = "19702";
+ public static final String s19703 = "19703";
+ public static final String s19704 = "19704";
+ public static final String s19705 = "19705";
+ public static final String s19706 = "19706";
+ public static final String s19707 = "19707";
+ public static final String s19708 = "19708";
+ public static final String s19709 = "19709";
+ public static final String s19710 = "19710";
+ public static final String s19711 = "19711";
+ public static final String s19712 = "19712";
+ public static final String s19713 = "19713";
+ public static final String s19714 = "19714";
+ public static final String s19715 = "19715";
+ public static final String s19716 = "19716";
+ public static final String s19717 = "19717";
+ public static final String s19718 = "19718";
+ public static final String s19719 = "19719";
+ public static final String s19720 = "19720";
+ public static final String s19721 = "19721";
+ public static final String s19722 = "19722";
+ public static final String s19723 = "19723";
+ public static final String s19724 = "19724";
+ public static final String s19725 = "19725";
+ public static final String s19726 = "19726";
+ public static final String s19727 = "19727";
+ public static final String s19728 = "19728";
+ public static final String s19729 = "19729";
+ public static final String s19730 = "19730";
+ public static final String s19731 = "19731";
+ public static final String s19732 = "19732";
+ public static final String s19733 = "19733";
+ public static final String s19734 = "19734";
+ public static final String s19735 = "19735";
+ public static final String s19736 = "19736";
+ public static final String s19737 = "19737";
+ public static final String s19738 = "19738";
+ public static final String s19739 = "19739";
+ public static final String s19740 = "19740";
+ public static final String s19741 = "19741";
+ public static final String s19742 = "19742";
+ public static final String s19743 = "19743";
+ public static final String s19744 = "19744";
+ public static final String s19745 = "19745";
+ public static final String s19746 = "19746";
+ public static final String s19747 = "19747";
+ public static final String s19748 = "19748";
+ public static final String s19749 = "19749";
+ public static final String s19750 = "19750";
+ public static final String s19751 = "19751";
+ public static final String s19752 = "19752";
+ public static final String s19753 = "19753";
+ public static final String s19754 = "19754";
+ public static final String s19755 = "19755";
+ public static final String s19756 = "19756";
+ public static final String s19757 = "19757";
+ public static final String s19758 = "19758";
+ public static final String s19759 = "19759";
+ public static final String s19760 = "19760";
+ public static final String s19761 = "19761";
+ public static final String s19762 = "19762";
+ public static final String s19763 = "19763";
+ public static final String s19764 = "19764";
+ public static final String s19765 = "19765";
+ public static final String s19766 = "19766";
+ public static final String s19767 = "19767";
+ public static final String s19768 = "19768";
+ public static final String s19769 = "19769";
+ public static final String s19770 = "19770";
+ public static final String s19771 = "19771";
+ public static final String s19772 = "19772";
+ public static final String s19773 = "19773";
+ public static final String s19774 = "19774";
+ public static final String s19775 = "19775";
+ public static final String s19776 = "19776";
+ public static final String s19777 = "19777";
+ public static final String s19778 = "19778";
+ public static final String s19779 = "19779";
+ public static final String s19780 = "19780";
+ public static final String s19781 = "19781";
+ public static final String s19782 = "19782";
+ public static final String s19783 = "19783";
+ public static final String s19784 = "19784";
+ public static final String s19785 = "19785";
+ public static final String s19786 = "19786";
+ public static final String s19787 = "19787";
+ public static final String s19788 = "19788";
+ public static final String s19789 = "19789";
+ public static final String s19790 = "19790";
+ public static final String s19791 = "19791";
+ public static final String s19792 = "19792";
+ public static final String s19793 = "19793";
+ public static final String s19794 = "19794";
+ public static final String s19795 = "19795";
+ public static final String s19796 = "19796";
+ public static final String s19797 = "19797";
+ public static final String s19798 = "19798";
+ public static final String s19799 = "19799";
+ public static final String s19800 = "19800";
+ public static final String s19801 = "19801";
+ public static final String s19802 = "19802";
+ public static final String s19803 = "19803";
+ public static final String s19804 = "19804";
+ public static final String s19805 = "19805";
+ public static final String s19806 = "19806";
+ public static final String s19807 = "19807";
+ public static final String s19808 = "19808";
+ public static final String s19809 = "19809";
+ public static final String s19810 = "19810";
+ public static final String s19811 = "19811";
+ public static final String s19812 = "19812";
+ public static final String s19813 = "19813";
+ public static final String s19814 = "19814";
+ public static final String s19815 = "19815";
+ public static final String s19816 = "19816";
+ public static final String s19817 = "19817";
+ public static final String s19818 = "19818";
+ public static final String s19819 = "19819";
+ public static final String s19820 = "19820";
+ public static final String s19821 = "19821";
+ public static final String s19822 = "19822";
+ public static final String s19823 = "19823";
+ public static final String s19824 = "19824";
+ public static final String s19825 = "19825";
+ public static final String s19826 = "19826";
+ public static final String s19827 = "19827";
+ public static final String s19828 = "19828";
+ public static final String s19829 = "19829";
+ public static final String s19830 = "19830";
+ public static final String s19831 = "19831";
+ public static final String s19832 = "19832";
+ public static final String s19833 = "19833";
+ public static final String s19834 = "19834";
+ public static final String s19835 = "19835";
+ public static final String s19836 = "19836";
+ public static final String s19837 = "19837";
+ public static final String s19838 = "19838";
+ public static final String s19839 = "19839";
+ public static final String s19840 = "19840";
+ public static final String s19841 = "19841";
+ public static final String s19842 = "19842";
+ public static final String s19843 = "19843";
+ public static final String s19844 = "19844";
+ public static final String s19845 = "19845";
+ public static final String s19846 = "19846";
+ public static final String s19847 = "19847";
+ public static final String s19848 = "19848";
+ public static final String s19849 = "19849";
+ public static final String s19850 = "19850";
+ public static final String s19851 = "19851";
+ public static final String s19852 = "19852";
+ public static final String s19853 = "19853";
+ public static final String s19854 = "19854";
+ public static final String s19855 = "19855";
+ public static final String s19856 = "19856";
+ public static final String s19857 = "19857";
+ public static final String s19858 = "19858";
+ public static final String s19859 = "19859";
+ public static final String s19860 = "19860";
+ public static final String s19861 = "19861";
+ public static final String s19862 = "19862";
+ public static final String s19863 = "19863";
+ public static final String s19864 = "19864";
+ public static final String s19865 = "19865";
+ public static final String s19866 = "19866";
+ public static final String s19867 = "19867";
+ public static final String s19868 = "19868";
+ public static final String s19869 = "19869";
+ public static final String s19870 = "19870";
+ public static final String s19871 = "19871";
+ public static final String s19872 = "19872";
+ public static final String s19873 = "19873";
+ public static final String s19874 = "19874";
+ public static final String s19875 = "19875";
+ public static final String s19876 = "19876";
+ public static final String s19877 = "19877";
+ public static final String s19878 = "19878";
+ public static final String s19879 = "19879";
+ public static final String s19880 = "19880";
+ public static final String s19881 = "19881";
+ public static final String s19882 = "19882";
+ public static final String s19883 = "19883";
+ public static final String s19884 = "19884";
+ public static final String s19885 = "19885";
+ public static final String s19886 = "19886";
+ public static final String s19887 = "19887";
+ public static final String s19888 = "19888";
+ public static final String s19889 = "19889";
+ public static final String s19890 = "19890";
+ public static final String s19891 = "19891";
+ public static final String s19892 = "19892";
+ public static final String s19893 = "19893";
+ public static final String s19894 = "19894";
+ public static final String s19895 = "19895";
+ public static final String s19896 = "19896";
+ public static final String s19897 = "19897";
+ public static final String s19898 = "19898";
+ public static final String s19899 = "19899";
+ public static final String s19900 = "19900";
+ public static final String s19901 = "19901";
+ public static final String s19902 = "19902";
+ public static final String s19903 = "19903";
+ public static final String s19904 = "19904";
+ public static final String s19905 = "19905";
+ public static final String s19906 = "19906";
+ public static final String s19907 = "19907";
+ public static final String s19908 = "19908";
+ public static final String s19909 = "19909";
+ public static final String s19910 = "19910";
+ public static final String s19911 = "19911";
+ public static final String s19912 = "19912";
+ public static final String s19913 = "19913";
+ public static final String s19914 = "19914";
+ public static final String s19915 = "19915";
+ public static final String s19916 = "19916";
+ public static final String s19917 = "19917";
+ public static final String s19918 = "19918";
+ public static final String s19919 = "19919";
+ public static final String s19920 = "19920";
+ public static final String s19921 = "19921";
+ public static final String s19922 = "19922";
+ public static final String s19923 = "19923";
+ public static final String s19924 = "19924";
+ public static final String s19925 = "19925";
+ public static final String s19926 = "19926";
+ public static final String s19927 = "19927";
+ public static final String s19928 = "19928";
+ public static final String s19929 = "19929";
+ public static final String s19930 = "19930";
+ public static final String s19931 = "19931";
+ public static final String s19932 = "19932";
+ public static final String s19933 = "19933";
+ public static final String s19934 = "19934";
+ public static final String s19935 = "19935";
+ public static final String s19936 = "19936";
+ public static final String s19937 = "19937";
+ public static final String s19938 = "19938";
+ public static final String s19939 = "19939";
+ public static final String s19940 = "19940";
+ public static final String s19941 = "19941";
+ public static final String s19942 = "19942";
+ public static final String s19943 = "19943";
+ public static final String s19944 = "19944";
+ public static final String s19945 = "19945";
+ public static final String s19946 = "19946";
+ public static final String s19947 = "19947";
+ public static final String s19948 = "19948";
+ public static final String s19949 = "19949";
+ public static final String s19950 = "19950";
+ public static final String s19951 = "19951";
+ public static final String s19952 = "19952";
+ public static final String s19953 = "19953";
+ public static final String s19954 = "19954";
+ public static final String s19955 = "19955";
+ public static final String s19956 = "19956";
+ public static final String s19957 = "19957";
+ public static final String s19958 = "19958";
+ public static final String s19959 = "19959";
+ public static final String s19960 = "19960";
+ public static final String s19961 = "19961";
+ public static final String s19962 = "19962";
+ public static final String s19963 = "19963";
+ public static final String s19964 = "19964";
+ public static final String s19965 = "19965";
+ public static final String s19966 = "19966";
+ public static final String s19967 = "19967";
+ public static final String s19968 = "19968";
+ public static final String s19969 = "19969";
+ public static final String s19970 = "19970";
+ public static final String s19971 = "19971";
+ public static final String s19972 = "19972";
+ public static final String s19973 = "19973";
+ public static final String s19974 = "19974";
+ public static final String s19975 = "19975";
+ public static final String s19976 = "19976";
+ public static final String s19977 = "19977";
+ public static final String s19978 = "19978";
+ public static final String s19979 = "19979";
+ public static final String s19980 = "19980";
+ public static final String s19981 = "19981";
+ public static final String s19982 = "19982";
+ public static final String s19983 = "19983";
+ public static final String s19984 = "19984";
+ public static final String s19985 = "19985";
+ public static final String s19986 = "19986";
+ public static final String s19987 = "19987";
+ public static final String s19988 = "19988";
+ public static final String s19989 = "19989";
+ public static final String s19990 = "19990";
+ public static final String s19991 = "19991";
+ public static final String s19992 = "19992";
+ public static final String s19993 = "19993";
+ public static final String s19994 = "19994";
+ public static final String s19995 = "19995";
+ public static final String s19996 = "19996";
+ public static final String s19997 = "19997";
+ public static final String s19998 = "19998";
+ public static final String s19999 = "19999";
+ public static final String s20000 = "20000";
+ public static final String s20001 = "20001";
+ public static final String s20002 = "20002";
+ public static final String s20003 = "20003";
+ public static final String s20004 = "20004";
+ public static final String s20005 = "20005";
+ public static final String s20006 = "20006";
+ public static final String s20007 = "20007";
+ public static final String s20008 = "20008";
+ public static final String s20009 = "20009";
+ public static final String s20010 = "20010";
+ public static final String s20011 = "20011";
+ public static final String s20012 = "20012";
+ public static final String s20013 = "20013";
+ public static final String s20014 = "20014";
+ public static final String s20015 = "20015";
+ public static final String s20016 = "20016";
+ public static final String s20017 = "20017";
+ public static final String s20018 = "20018";
+ public static final String s20019 = "20019";
+ public static final String s20020 = "20020";
+ public static final String s20021 = "20021";
+ public static final String s20022 = "20022";
+ public static final String s20023 = "20023";
+ public static final String s20024 = "20024";
+ public static final String s20025 = "20025";
+ public static final String s20026 = "20026";
+ public static final String s20027 = "20027";
+ public static final String s20028 = "20028";
+ public static final String s20029 = "20029";
+ public static final String s20030 = "20030";
+ public static final String s20031 = "20031";
+ public static final String s20032 = "20032";
+ public static final String s20033 = "20033";
+ public static final String s20034 = "20034";
+ public static final String s20035 = "20035";
+ public static final String s20036 = "20036";
+ public static final String s20037 = "20037";
+ public static final String s20038 = "20038";
+ public static final String s20039 = "20039";
+ public static final String s20040 = "20040";
+ public static final String s20041 = "20041";
+ public static final String s20042 = "20042";
+ public static final String s20043 = "20043";
+ public static final String s20044 = "20044";
+ public static final String s20045 = "20045";
+ public static final String s20046 = "20046";
+ public static final String s20047 = "20047";
+ public static final String s20048 = "20048";
+ public static final String s20049 = "20049";
+ public static final String s20050 = "20050";
+ public static final String s20051 = "20051";
+ public static final String s20052 = "20052";
+ public static final String s20053 = "20053";
+ public static final String s20054 = "20054";
+ public static final String s20055 = "20055";
+ public static final String s20056 = "20056";
+ public static final String s20057 = "20057";
+ public static final String s20058 = "20058";
+ public static final String s20059 = "20059";
+ public static final String s20060 = "20060";
+ public static final String s20061 = "20061";
+ public static final String s20062 = "20062";
+ public static final String s20063 = "20063";
+ public static final String s20064 = "20064";
+ public static final String s20065 = "20065";
+ public static final String s20066 = "20066";
+ public static final String s20067 = "20067";
+ public static final String s20068 = "20068";
+ public static final String s20069 = "20069";
+ public static final String s20070 = "20070";
+ public static final String s20071 = "20071";
+ public static final String s20072 = "20072";
+ public static final String s20073 = "20073";
+ public static final String s20074 = "20074";
+ public static final String s20075 = "20075";
+ public static final String s20076 = "20076";
+ public static final String s20077 = "20077";
+ public static final String s20078 = "20078";
+ public static final String s20079 = "20079";
+ public static final String s20080 = "20080";
+ public static final String s20081 = "20081";
+ public static final String s20082 = "20082";
+ public static final String s20083 = "20083";
+ public static final String s20084 = "20084";
+ public static final String s20085 = "20085";
+ public static final String s20086 = "20086";
+ public static final String s20087 = "20087";
+ public static final String s20088 = "20088";
+ public static final String s20089 = "20089";
+ public static final String s20090 = "20090";
+ public static final String s20091 = "20091";
+ public static final String s20092 = "20092";
+ public static final String s20093 = "20093";
+ public static final String s20094 = "20094";
+ public static final String s20095 = "20095";
+ public static final String s20096 = "20096";
+ public static final String s20097 = "20097";
+ public static final String s20098 = "20098";
+ public static final String s20099 = "20099";
+ public static final String s20100 = "20100";
+ public static final String s20101 = "20101";
+ public static final String s20102 = "20102";
+ public static final String s20103 = "20103";
+ public static final String s20104 = "20104";
+ public static final String s20105 = "20105";
+ public static final String s20106 = "20106";
+ public static final String s20107 = "20107";
+ public static final String s20108 = "20108";
+ public static final String s20109 = "20109";
+ public static final String s20110 = "20110";
+ public static final String s20111 = "20111";
+ public static final String s20112 = "20112";
+ public static final String s20113 = "20113";
+ public static final String s20114 = "20114";
+ public static final String s20115 = "20115";
+ public static final String s20116 = "20116";
+ public static final String s20117 = "20117";
+ public static final String s20118 = "20118";
+ public static final String s20119 = "20119";
+ public static final String s20120 = "20120";
+ public static final String s20121 = "20121";
+ public static final String s20122 = "20122";
+ public static final String s20123 = "20123";
+ public static final String s20124 = "20124";
+ public static final String s20125 = "20125";
+ public static final String s20126 = "20126";
+ public static final String s20127 = "20127";
+ public static final String s20128 = "20128";
+ public static final String s20129 = "20129";
+ public static final String s20130 = "20130";
+ public static final String s20131 = "20131";
+ public static final String s20132 = "20132";
+ public static final String s20133 = "20133";
+ public static final String s20134 = "20134";
+ public static final String s20135 = "20135";
+ public static final String s20136 = "20136";
+ public static final String s20137 = "20137";
+ public static final String s20138 = "20138";
+ public static final String s20139 = "20139";
+ public static final String s20140 = "20140";
+ public static final String s20141 = "20141";
+ public static final String s20142 = "20142";
+ public static final String s20143 = "20143";
+ public static final String s20144 = "20144";
+ public static final String s20145 = "20145";
+ public static final String s20146 = "20146";
+ public static final String s20147 = "20147";
+ public static final String s20148 = "20148";
+ public static final String s20149 = "20149";
+ public static final String s20150 = "20150";
+ public static final String s20151 = "20151";
+ public static final String s20152 = "20152";
+ public static final String s20153 = "20153";
+ public static final String s20154 = "20154";
+ public static final String s20155 = "20155";
+ public static final String s20156 = "20156";
+ public static final String s20157 = "20157";
+ public static final String s20158 = "20158";
+ public static final String s20159 = "20159";
+ public static final String s20160 = "20160";
+ public static final String s20161 = "20161";
+ public static final String s20162 = "20162";
+ public static final String s20163 = "20163";
+ public static final String s20164 = "20164";
+ public static final String s20165 = "20165";
+ public static final String s20166 = "20166";
+ public static final String s20167 = "20167";
+ public static final String s20168 = "20168";
+ public static final String s20169 = "20169";
+ public static final String s20170 = "20170";
+ public static final String s20171 = "20171";
+ public static final String s20172 = "20172";
+ public static final String s20173 = "20173";
+ public static final String s20174 = "20174";
+ public static final String s20175 = "20175";
+ public static final String s20176 = "20176";
+ public static final String s20177 = "20177";
+ public static final String s20178 = "20178";
+ public static final String s20179 = "20179";
+ public static final String s20180 = "20180";
+ public static final String s20181 = "20181";
+ public static final String s20182 = "20182";
+ public static final String s20183 = "20183";
+ public static final String s20184 = "20184";
+ public static final String s20185 = "20185";
+ public static final String s20186 = "20186";
+ public static final String s20187 = "20187";
+ public static final String s20188 = "20188";
+ public static final String s20189 = "20189";
+ public static final String s20190 = "20190";
+ public static final String s20191 = "20191";
+ public static final String s20192 = "20192";
+ public static final String s20193 = "20193";
+ public static final String s20194 = "20194";
+ public static final String s20195 = "20195";
+ public static final String s20196 = "20196";
+ public static final String s20197 = "20197";
+ public static final String s20198 = "20198";
+ public static final String s20199 = "20199";
+ public static final String s20200 = "20200";
+ public static final String s20201 = "20201";
+ public static final String s20202 = "20202";
+ public static final String s20203 = "20203";
+ public static final String s20204 = "20204";
+ public static final String s20205 = "20205";
+ public static final String s20206 = "20206";
+ public static final String s20207 = "20207";
+ public static final String s20208 = "20208";
+ public static final String s20209 = "20209";
+ public static final String s20210 = "20210";
+ public static final String s20211 = "20211";
+ public static final String s20212 = "20212";
+ public static final String s20213 = "20213";
+ public static final String s20214 = "20214";
+ public static final String s20215 = "20215";
+ public static final String s20216 = "20216";
+ public static final String s20217 = "20217";
+ public static final String s20218 = "20218";
+ public static final String s20219 = "20219";
+ public static final String s20220 = "20220";
+ public static final String s20221 = "20221";
+ public static final String s20222 = "20222";
+ public static final String s20223 = "20223";
+ public static final String s20224 = "20224";
+ public static final String s20225 = "20225";
+ public static final String s20226 = "20226";
+ public static final String s20227 = "20227";
+ public static final String s20228 = "20228";
+ public static final String s20229 = "20229";
+ public static final String s20230 = "20230";
+ public static final String s20231 = "20231";
+ public static final String s20232 = "20232";
+ public static final String s20233 = "20233";
+ public static final String s20234 = "20234";
+ public static final String s20235 = "20235";
+ public static final String s20236 = "20236";
+ public static final String s20237 = "20237";
+ public static final String s20238 = "20238";
+ public static final String s20239 = "20239";
+ public static final String s20240 = "20240";
+ public static final String s20241 = "20241";
+ public static final String s20242 = "20242";
+ public static final String s20243 = "20243";
+ public static final String s20244 = "20244";
+ public static final String s20245 = "20245";
+ public static final String s20246 = "20246";
+ public static final String s20247 = "20247";
+ public static final String s20248 = "20248";
+ public static final String s20249 = "20249";
+ public static final String s20250 = "20250";
+ public static final String s20251 = "20251";
+ public static final String s20252 = "20252";
+ public static final String s20253 = "20253";
+ public static final String s20254 = "20254";
+ public static final String s20255 = "20255";
+ public static final String s20256 = "20256";
+ public static final String s20257 = "20257";
+ public static final String s20258 = "20258";
+ public static final String s20259 = "20259";
+ public static final String s20260 = "20260";
+ public static final String s20261 = "20261";
+ public static final String s20262 = "20262";
+ public static final String s20263 = "20263";
+ public static final String s20264 = "20264";
+ public static final String s20265 = "20265";
+ public static final String s20266 = "20266";
+ public static final String s20267 = "20267";
+ public static final String s20268 = "20268";
+ public static final String s20269 = "20269";
+ public static final String s20270 = "20270";
+ public static final String s20271 = "20271";
+ public static final String s20272 = "20272";
+ public static final String s20273 = "20273";
+ public static final String s20274 = "20274";
+ public static final String s20275 = "20275";
+ public static final String s20276 = "20276";
+ public static final String s20277 = "20277";
+ public static final String s20278 = "20278";
+ public static final String s20279 = "20279";
+ public static final String s20280 = "20280";
+ public static final String s20281 = "20281";
+ public static final String s20282 = "20282";
+ public static final String s20283 = "20283";
+ public static final String s20284 = "20284";
+ public static final String s20285 = "20285";
+ public static final String s20286 = "20286";
+ public static final String s20287 = "20287";
+ public static final String s20288 = "20288";
+ public static final String s20289 = "20289";
+ public static final String s20290 = "20290";
+ public static final String s20291 = "20291";
+ public static final String s20292 = "20292";
+ public static final String s20293 = "20293";
+ public static final String s20294 = "20294";
+ public static final String s20295 = "20295";
+ public static final String s20296 = "20296";
+ public static final String s20297 = "20297";
+ public static final String s20298 = "20298";
+ public static final String s20299 = "20299";
+ public static final String s20300 = "20300";
+ public static final String s20301 = "20301";
+ public static final String s20302 = "20302";
+ public static final String s20303 = "20303";
+ public static final String s20304 = "20304";
+ public static final String s20305 = "20305";
+ public static final String s20306 = "20306";
+ public static final String s20307 = "20307";
+ public static final String s20308 = "20308";
+ public static final String s20309 = "20309";
+ public static final String s20310 = "20310";
+ public static final String s20311 = "20311";
+ public static final String s20312 = "20312";
+ public static final String s20313 = "20313";
+ public static final String s20314 = "20314";
+ public static final String s20315 = "20315";
+ public static final String s20316 = "20316";
+ public static final String s20317 = "20317";
+ public static final String s20318 = "20318";
+ public static final String s20319 = "20319";
+ public static final String s20320 = "20320";
+ public static final String s20321 = "20321";
+ public static final String s20322 = "20322";
+ public static final String s20323 = "20323";
+ public static final String s20324 = "20324";
+ public static final String s20325 = "20325";
+ public static final String s20326 = "20326";
+ public static final String s20327 = "20327";
+ public static final String s20328 = "20328";
+ public static final String s20329 = "20329";
+ public static final String s20330 = "20330";
+ public static final String s20331 = "20331";
+ public static final String s20332 = "20332";
+ public static final String s20333 = "20333";
+ public static final String s20334 = "20334";
+ public static final String s20335 = "20335";
+ public static final String s20336 = "20336";
+ public static final String s20337 = "20337";
+ public static final String s20338 = "20338";
+ public static final String s20339 = "20339";
+ public static final String s20340 = "20340";
+ public static final String s20341 = "20341";
+ public static final String s20342 = "20342";
+ public static final String s20343 = "20343";
+ public static final String s20344 = "20344";
+ public static final String s20345 = "20345";
+ public static final String s20346 = "20346";
+ public static final String s20347 = "20347";
+ public static final String s20348 = "20348";
+ public static final String s20349 = "20349";
+ public static final String s20350 = "20350";
+ public static final String s20351 = "20351";
+ public static final String s20352 = "20352";
+ public static final String s20353 = "20353";
+ public static final String s20354 = "20354";
+ public static final String s20355 = "20355";
+ public static final String s20356 = "20356";
+ public static final String s20357 = "20357";
+ public static final String s20358 = "20358";
+ public static final String s20359 = "20359";
+ public static final String s20360 = "20360";
+ public static final String s20361 = "20361";
+ public static final String s20362 = "20362";
+ public static final String s20363 = "20363";
+ public static final String s20364 = "20364";
+ public static final String s20365 = "20365";
+ public static final String s20366 = "20366";
+ public static final String s20367 = "20367";
+ public static final String s20368 = "20368";
+ public static final String s20369 = "20369";
+ public static final String s20370 = "20370";
+ public static final String s20371 = "20371";
+ public static final String s20372 = "20372";
+ public static final String s20373 = "20373";
+ public static final String s20374 = "20374";
+ public static final String s20375 = "20375";
+ public static final String s20376 = "20376";
+ public static final String s20377 = "20377";
+ public static final String s20378 = "20378";
+ public static final String s20379 = "20379";
+ public static final String s20380 = "20380";
+ public static final String s20381 = "20381";
+ public static final String s20382 = "20382";
+ public static final String s20383 = "20383";
+ public static final String s20384 = "20384";
+ public static final String s20385 = "20385";
+ public static final String s20386 = "20386";
+ public static final String s20387 = "20387";
+ public static final String s20388 = "20388";
+ public static final String s20389 = "20389";
+ public static final String s20390 = "20390";
+ public static final String s20391 = "20391";
+ public static final String s20392 = "20392";
+ public static final String s20393 = "20393";
+ public static final String s20394 = "20394";
+ public static final String s20395 = "20395";
+ public static final String s20396 = "20396";
+ public static final String s20397 = "20397";
+ public static final String s20398 = "20398";
+ public static final String s20399 = "20399";
+ public static final String s20400 = "20400";
+ public static final String s20401 = "20401";
+ public static final String s20402 = "20402";
+ public static final String s20403 = "20403";
+ public static final String s20404 = "20404";
+ public static final String s20405 = "20405";
+ public static final String s20406 = "20406";
+ public static final String s20407 = "20407";
+ public static final String s20408 = "20408";
+ public static final String s20409 = "20409";
+ public static final String s20410 = "20410";
+ public static final String s20411 = "20411";
+ public static final String s20412 = "20412";
+ public static final String s20413 = "20413";
+ public static final String s20414 = "20414";
+ public static final String s20415 = "20415";
+ public static final String s20416 = "20416";
+ public static final String s20417 = "20417";
+ public static final String s20418 = "20418";
+ public static final String s20419 = "20419";
+ public static final String s20420 = "20420";
+ public static final String s20421 = "20421";
+ public static final String s20422 = "20422";
+ public static final String s20423 = "20423";
+ public static final String s20424 = "20424";
+ public static final String s20425 = "20425";
+ public static final String s20426 = "20426";
+ public static final String s20427 = "20427";
+ public static final String s20428 = "20428";
+ public static final String s20429 = "20429";
+ public static final String s20430 = "20430";
+ public static final String s20431 = "20431";
+ public static final String s20432 = "20432";
+ public static final String s20433 = "20433";
+ public static final String s20434 = "20434";
+ public static final String s20435 = "20435";
+ public static final String s20436 = "20436";
+ public static final String s20437 = "20437";
+ public static final String s20438 = "20438";
+ public static final String s20439 = "20439";
+ public static final String s20440 = "20440";
+ public static final String s20441 = "20441";
+ public static final String s20442 = "20442";
+ public static final String s20443 = "20443";
+ public static final String s20444 = "20444";
+ public static final String s20445 = "20445";
+ public static final String s20446 = "20446";
+ public static final String s20447 = "20447";
+ public static final String s20448 = "20448";
+ public static final String s20449 = "20449";
+ public static final String s20450 = "20450";
+ public static final String s20451 = "20451";
+ public static final String s20452 = "20452";
+ public static final String s20453 = "20453";
+ public static final String s20454 = "20454";
+ public static final String s20455 = "20455";
+ public static final String s20456 = "20456";
+ public static final String s20457 = "20457";
+ public static final String s20458 = "20458";
+ public static final String s20459 = "20459";
+ public static final String s20460 = "20460";
+ public static final String s20461 = "20461";
+ public static final String s20462 = "20462";
+ public static final String s20463 = "20463";
+ public static final String s20464 = "20464";
+ public static final String s20465 = "20465";
+ public static final String s20466 = "20466";
+ public static final String s20467 = "20467";
+ public static final String s20468 = "20468";
+ public static final String s20469 = "20469";
+ public static final String s20470 = "20470";
+ public static final String s20471 = "20471";
+ public static final String s20472 = "20472";
+ public static final String s20473 = "20473";
+ public static final String s20474 = "20474";
+ public static final String s20475 = "20475";
+ public static final String s20476 = "20476";
+ public static final String s20477 = "20477";
+ public static final String s20478 = "20478";
+ public static final String s20479 = "20479";
+ public static final String s20480 = "20480";
+ public static final String s20481 = "20481";
+ public static final String s20482 = "20482";
+ public static final String s20483 = "20483";
+ public static final String s20484 = "20484";
+ public static final String s20485 = "20485";
+ public static final String s20486 = "20486";
+ public static final String s20487 = "20487";
+ public static final String s20488 = "20488";
+ public static final String s20489 = "20489";
+ public static final String s20490 = "20490";
+ public static final String s20491 = "20491";
+ public static final String s20492 = "20492";
+ public static final String s20493 = "20493";
+ public static final String s20494 = "20494";
+ public static final String s20495 = "20495";
+ public static final String s20496 = "20496";
+ public static final String s20497 = "20497";
+ public static final String s20498 = "20498";
+ public static final String s20499 = "20499";
+ public static final String s20500 = "20500";
+ public static final String s20501 = "20501";
+ public static final String s20502 = "20502";
+ public static final String s20503 = "20503";
+ public static final String s20504 = "20504";
+ public static final String s20505 = "20505";
+ public static final String s20506 = "20506";
+ public static final String s20507 = "20507";
+ public static final String s20508 = "20508";
+ public static final String s20509 = "20509";
+ public static final String s20510 = "20510";
+ public static final String s20511 = "20511";
+ public static final String s20512 = "20512";
+ public static final String s20513 = "20513";
+ public static final String s20514 = "20514";
+ public static final String s20515 = "20515";
+ public static final String s20516 = "20516";
+ public static final String s20517 = "20517";
+ public static final String s20518 = "20518";
+ public static final String s20519 = "20519";
+ public static final String s20520 = "20520";
+ public static final String s20521 = "20521";
+ public static final String s20522 = "20522";
+ public static final String s20523 = "20523";
+ public static final String s20524 = "20524";
+ public static final String s20525 = "20525";
+ public static final String s20526 = "20526";
+ public static final String s20527 = "20527";
+ public static final String s20528 = "20528";
+ public static final String s20529 = "20529";
+ public static final String s20530 = "20530";
+ public static final String s20531 = "20531";
+ public static final String s20532 = "20532";
+ public static final String s20533 = "20533";
+ public static final String s20534 = "20534";
+ public static final String s20535 = "20535";
+ public static final String s20536 = "20536";
+ public static final String s20537 = "20537";
+ public static final String s20538 = "20538";
+ public static final String s20539 = "20539";
+ public static final String s20540 = "20540";
+ public static final String s20541 = "20541";
+ public static final String s20542 = "20542";
+ public static final String s20543 = "20543";
+ public static final String s20544 = "20544";
+ public static final String s20545 = "20545";
+ public static final String s20546 = "20546";
+ public static final String s20547 = "20547";
+ public static final String s20548 = "20548";
+ public static final String s20549 = "20549";
+ public static final String s20550 = "20550";
+ public static final String s20551 = "20551";
+ public static final String s20552 = "20552";
+ public static final String s20553 = "20553";
+ public static final String s20554 = "20554";
+ public static final String s20555 = "20555";
+ public static final String s20556 = "20556";
+ public static final String s20557 = "20557";
+ public static final String s20558 = "20558";
+ public static final String s20559 = "20559";
+ public static final String s20560 = "20560";
+ public static final String s20561 = "20561";
+ public static final String s20562 = "20562";
+ public static final String s20563 = "20563";
+ public static final String s20564 = "20564";
+ public static final String s20565 = "20565";
+ public static final String s20566 = "20566";
+ public static final String s20567 = "20567";
+ public static final String s20568 = "20568";
+ public static final String s20569 = "20569";
+ public static final String s20570 = "20570";
+ public static final String s20571 = "20571";
+ public static final String s20572 = "20572";
+ public static final String s20573 = "20573";
+ public static final String s20574 = "20574";
+ public static final String s20575 = "20575";
+ public static final String s20576 = "20576";
+ public static final String s20577 = "20577";
+ public static final String s20578 = "20578";
+ public static final String s20579 = "20579";
+ public static final String s20580 = "20580";
+ public static final String s20581 = "20581";
+ public static final String s20582 = "20582";
+ public static final String s20583 = "20583";
+ public static final String s20584 = "20584";
+ public static final String s20585 = "20585";
+ public static final String s20586 = "20586";
+ public static final String s20587 = "20587";
+ public static final String s20588 = "20588";
+ public static final String s20589 = "20589";
+ public static final String s20590 = "20590";
+ public static final String s20591 = "20591";
+ public static final String s20592 = "20592";
+ public static final String s20593 = "20593";
+ public static final String s20594 = "20594";
+ public static final String s20595 = "20595";
+ public static final String s20596 = "20596";
+ public static final String s20597 = "20597";
+ public static final String s20598 = "20598";
+ public static final String s20599 = "20599";
+ public static final String s20600 = "20600";
+ public static final String s20601 = "20601";
+ public static final String s20602 = "20602";
+ public static final String s20603 = "20603";
+ public static final String s20604 = "20604";
+ public static final String s20605 = "20605";
+ public static final String s20606 = "20606";
+ public static final String s20607 = "20607";
+ public static final String s20608 = "20608";
+ public static final String s20609 = "20609";
+ public static final String s20610 = "20610";
+ public static final String s20611 = "20611";
+ public static final String s20612 = "20612";
+ public static final String s20613 = "20613";
+ public static final String s20614 = "20614";
+ public static final String s20615 = "20615";
+ public static final String s20616 = "20616";
+ public static final String s20617 = "20617";
+ public static final String s20618 = "20618";
+ public static final String s20619 = "20619";
+ public static final String s20620 = "20620";
+ public static final String s20621 = "20621";
+ public static final String s20622 = "20622";
+ public static final String s20623 = "20623";
+ public static final String s20624 = "20624";
+ public static final String s20625 = "20625";
+ public static final String s20626 = "20626";
+ public static final String s20627 = "20627";
+ public static final String s20628 = "20628";
+ public static final String s20629 = "20629";
+ public static final String s20630 = "20630";
+ public static final String s20631 = "20631";
+ public static final String s20632 = "20632";
+ public static final String s20633 = "20633";
+ public static final String s20634 = "20634";
+ public static final String s20635 = "20635";
+ public static final String s20636 = "20636";
+ public static final String s20637 = "20637";
+ public static final String s20638 = "20638";
+ public static final String s20639 = "20639";
+ public static final String s20640 = "20640";
+ public static final String s20641 = "20641";
+ public static final String s20642 = "20642";
+ public static final String s20643 = "20643";
+ public static final String s20644 = "20644";
+ public static final String s20645 = "20645";
+ public static final String s20646 = "20646";
+ public static final String s20647 = "20647";
+ public static final String s20648 = "20648";
+ public static final String s20649 = "20649";
+ public static final String s20650 = "20650";
+ public static final String s20651 = "20651";
+ public static final String s20652 = "20652";
+ public static final String s20653 = "20653";
+ public static final String s20654 = "20654";
+ public static final String s20655 = "20655";
+ public static final String s20656 = "20656";
+ public static final String s20657 = "20657";
+ public static final String s20658 = "20658";
+ public static final String s20659 = "20659";
+ public static final String s20660 = "20660";
+ public static final String s20661 = "20661";
+ public static final String s20662 = "20662";
+ public static final String s20663 = "20663";
+ public static final String s20664 = "20664";
+ public static final String s20665 = "20665";
+ public static final String s20666 = "20666";
+ public static final String s20667 = "20667";
+ public static final String s20668 = "20668";
+ public static final String s20669 = "20669";
+ public static final String s20670 = "20670";
+ public static final String s20671 = "20671";
+ public static final String s20672 = "20672";
+ public static final String s20673 = "20673";
+ public static final String s20674 = "20674";
+ public static final String s20675 = "20675";
+ public static final String s20676 = "20676";
+ public static final String s20677 = "20677";
+ public static final String s20678 = "20678";
+ public static final String s20679 = "20679";
+ public static final String s20680 = "20680";
+ public static final String s20681 = "20681";
+ public static final String s20682 = "20682";
+ public static final String s20683 = "20683";
+ public static final String s20684 = "20684";
+ public static final String s20685 = "20685";
+ public static final String s20686 = "20686";
+ public static final String s20687 = "20687";
+ public static final String s20688 = "20688";
+ public static final String s20689 = "20689";
+ public static final String s20690 = "20690";
+ public static final String s20691 = "20691";
+ public static final String s20692 = "20692";
+ public static final String s20693 = "20693";
+ public static final String s20694 = "20694";
+ public static final String s20695 = "20695";
+ public static final String s20696 = "20696";
+ public static final String s20697 = "20697";
+ public static final String s20698 = "20698";
+ public static final String s20699 = "20699";
+ public static final String s20700 = "20700";
+ public static final String s20701 = "20701";
+ public static final String s20702 = "20702";
+ public static final String s20703 = "20703";
+ public static final String s20704 = "20704";
+ public static final String s20705 = "20705";
+ public static final String s20706 = "20706";
+ public static final String s20707 = "20707";
+ public static final String s20708 = "20708";
+ public static final String s20709 = "20709";
+ public static final String s20710 = "20710";
+ public static final String s20711 = "20711";
+ public static final String s20712 = "20712";
+ public static final String s20713 = "20713";
+ public static final String s20714 = "20714";
+ public static final String s20715 = "20715";
+ public static final String s20716 = "20716";
+ public static final String s20717 = "20717";
+ public static final String s20718 = "20718";
+ public static final String s20719 = "20719";
+ public static final String s20720 = "20720";
+ public static final String s20721 = "20721";
+ public static final String s20722 = "20722";
+ public static final String s20723 = "20723";
+ public static final String s20724 = "20724";
+ public static final String s20725 = "20725";
+ public static final String s20726 = "20726";
+ public static final String s20727 = "20727";
+ public static final String s20728 = "20728";
+ public static final String s20729 = "20729";
+ public static final String s20730 = "20730";
+ public static final String s20731 = "20731";
+ public static final String s20732 = "20732";
+ public static final String s20733 = "20733";
+ public static final String s20734 = "20734";
+ public static final String s20735 = "20735";
+ public static final String s20736 = "20736";
+ public static final String s20737 = "20737";
+ public static final String s20738 = "20738";
+ public static final String s20739 = "20739";
+ public static final String s20740 = "20740";
+ public static final String s20741 = "20741";
+ public static final String s20742 = "20742";
+ public static final String s20743 = "20743";
+ public static final String s20744 = "20744";
+ public static final String s20745 = "20745";
+ public static final String s20746 = "20746";
+ public static final String s20747 = "20747";
+ public static final String s20748 = "20748";
+ public static final String s20749 = "20749";
+ public static final String s20750 = "20750";
+ public static final String s20751 = "20751";
+ public static final String s20752 = "20752";
+ public static final String s20753 = "20753";
+ public static final String s20754 = "20754";
+ public static final String s20755 = "20755";
+ public static final String s20756 = "20756";
+ public static final String s20757 = "20757";
+ public static final String s20758 = "20758";
+ public static final String s20759 = "20759";
+ public static final String s20760 = "20760";
+ public static final String s20761 = "20761";
+ public static final String s20762 = "20762";
+ public static final String s20763 = "20763";
+ public static final String s20764 = "20764";
+ public static final String s20765 = "20765";
+ public static final String s20766 = "20766";
+ public static final String s20767 = "20767";
+ public static final String s20768 = "20768";
+ public static final String s20769 = "20769";
+ public static final String s20770 = "20770";
+ public static final String s20771 = "20771";
+ public static final String s20772 = "20772";
+ public static final String s20773 = "20773";
+ public static final String s20774 = "20774";
+ public static final String s20775 = "20775";
+ public static final String s20776 = "20776";
+ public static final String s20777 = "20777";
+ public static final String s20778 = "20778";
+ public static final String s20779 = "20779";
+ public static final String s20780 = "20780";
+ public static final String s20781 = "20781";
+ public static final String s20782 = "20782";
+ public static final String s20783 = "20783";
+ public static final String s20784 = "20784";
+ public static final String s20785 = "20785";
+ public static final String s20786 = "20786";
+ public static final String s20787 = "20787";
+ public static final String s20788 = "20788";
+ public static final String s20789 = "20789";
+ public static final String s20790 = "20790";
+ public static final String s20791 = "20791";
+ public static final String s20792 = "20792";
+ public static final String s20793 = "20793";
+ public static final String s20794 = "20794";
+ public static final String s20795 = "20795";
+ public static final String s20796 = "20796";
+ public static final String s20797 = "20797";
+ public static final String s20798 = "20798";
+ public static final String s20799 = "20799";
+ public static final String s20800 = "20800";
+ public static final String s20801 = "20801";
+ public static final String s20802 = "20802";
+ public static final String s20803 = "20803";
+ public static final String s20804 = "20804";
+ public static final String s20805 = "20805";
+ public static final String s20806 = "20806";
+ public static final String s20807 = "20807";
+ public static final String s20808 = "20808";
+ public static final String s20809 = "20809";
+ public static final String s20810 = "20810";
+ public static final String s20811 = "20811";
+ public static final String s20812 = "20812";
+ public static final String s20813 = "20813";
+ public static final String s20814 = "20814";
+ public static final String s20815 = "20815";
+ public static final String s20816 = "20816";
+ public static final String s20817 = "20817";
+ public static final String s20818 = "20818";
+ public static final String s20819 = "20819";
+ public static final String s20820 = "20820";
+ public static final String s20821 = "20821";
+ public static final String s20822 = "20822";
+ public static final String s20823 = "20823";
+ public static final String s20824 = "20824";
+ public static final String s20825 = "20825";
+ public static final String s20826 = "20826";
+ public static final String s20827 = "20827";
+ public static final String s20828 = "20828";
+ public static final String s20829 = "20829";
+ public static final String s20830 = "20830";
+ public static final String s20831 = "20831";
+ public static final String s20832 = "20832";
+ public static final String s20833 = "20833";
+ public static final String s20834 = "20834";
+ public static final String s20835 = "20835";
+ public static final String s20836 = "20836";
+ public static final String s20837 = "20837";
+ public static final String s20838 = "20838";
+ public static final String s20839 = "20839";
+ public static final String s20840 = "20840";
+ public static final String s20841 = "20841";
+ public static final String s20842 = "20842";
+ public static final String s20843 = "20843";
+ public static final String s20844 = "20844";
+ public static final String s20845 = "20845";
+ public static final String s20846 = "20846";
+ public static final String s20847 = "20847";
+ public static final String s20848 = "20848";
+ public static final String s20849 = "20849";
+ public static final String s20850 = "20850";
+ public static final String s20851 = "20851";
+ public static final String s20852 = "20852";
+ public static final String s20853 = "20853";
+ public static final String s20854 = "20854";
+ public static final String s20855 = "20855";
+ public static final String s20856 = "20856";
+ public static final String s20857 = "20857";
+ public static final String s20858 = "20858";
+ public static final String s20859 = "20859";
+ public static final String s20860 = "20860";
+ public static final String s20861 = "20861";
+ public static final String s20862 = "20862";
+ public static final String s20863 = "20863";
+ public static final String s20864 = "20864";
+ public static final String s20865 = "20865";
+ public static final String s20866 = "20866";
+ public static final String s20867 = "20867";
+ public static final String s20868 = "20868";
+ public static final String s20869 = "20869";
+ public static final String s20870 = "20870";
+ public static final String s20871 = "20871";
+ public static final String s20872 = "20872";
+ public static final String s20873 = "20873";
+ public static final String s20874 = "20874";
+ public static final String s20875 = "20875";
+ public static final String s20876 = "20876";
+ public static final String s20877 = "20877";
+ public static final String s20878 = "20878";
+ public static final String s20879 = "20879";
+ public static final String s20880 = "20880";
+ public static final String s20881 = "20881";
+ public static final String s20882 = "20882";
+ public static final String s20883 = "20883";
+ public static final String s20884 = "20884";
+ public static final String s20885 = "20885";
+ public static final String s20886 = "20886";
+ public static final String s20887 = "20887";
+ public static final String s20888 = "20888";
+ public static final String s20889 = "20889";
+ public static final String s20890 = "20890";
+ public static final String s20891 = "20891";
+ public static final String s20892 = "20892";
+ public static final String s20893 = "20893";
+ public static final String s20894 = "20894";
+ public static final String s20895 = "20895";
+ public static final String s20896 = "20896";
+ public static final String s20897 = "20897";
+ public static final String s20898 = "20898";
+ public static final String s20899 = "20899";
+ public static final String s20900 = "20900";
+ public static final String s20901 = "20901";
+ public static final String s20902 = "20902";
+ public static final String s20903 = "20903";
+ public static final String s20904 = "20904";
+ public static final String s20905 = "20905";
+ public static final String s20906 = "20906";
+ public static final String s20907 = "20907";
+ public static final String s20908 = "20908";
+ public static final String s20909 = "20909";
+ public static final String s20910 = "20910";
+ public static final String s20911 = "20911";
+ public static final String s20912 = "20912";
+ public static final String s20913 = "20913";
+ public static final String s20914 = "20914";
+ public static final String s20915 = "20915";
+ public static final String s20916 = "20916";
+ public static final String s20917 = "20917";
+ public static final String s20918 = "20918";
+ public static final String s20919 = "20919";
+ public static final String s20920 = "20920";
+ public static final String s20921 = "20921";
+ public static final String s20922 = "20922";
+ public static final String s20923 = "20923";
+ public static final String s20924 = "20924";
+ public static final String s20925 = "20925";
+ public static final String s20926 = "20926";
+ public static final String s20927 = "20927";
+ public static final String s20928 = "20928";
+ public static final String s20929 = "20929";
+ public static final String s20930 = "20930";
+ public static final String s20931 = "20931";
+ public static final String s20932 = "20932";
+ public static final String s20933 = "20933";
+ public static final String s20934 = "20934";
+ public static final String s20935 = "20935";
+ public static final String s20936 = "20936";
+ public static final String s20937 = "20937";
+ public static final String s20938 = "20938";
+ public static final String s20939 = "20939";
+ public static final String s20940 = "20940";
+ public static final String s20941 = "20941";
+ public static final String s20942 = "20942";
+ public static final String s20943 = "20943";
+ public static final String s20944 = "20944";
+ public static final String s20945 = "20945";
+ public static final String s20946 = "20946";
+ public static final String s20947 = "20947";
+ public static final String s20948 = "20948";
+ public static final String s20949 = "20949";
+ public static final String s20950 = "20950";
+ public static final String s20951 = "20951";
+ public static final String s20952 = "20952";
+ public static final String s20953 = "20953";
+ public static final String s20954 = "20954";
+ public static final String s20955 = "20955";
+ public static final String s20956 = "20956";
+ public static final String s20957 = "20957";
+ public static final String s20958 = "20958";
+ public static final String s20959 = "20959";
+ public static final String s20960 = "20960";
+ public static final String s20961 = "20961";
+ public static final String s20962 = "20962";
+ public static final String s20963 = "20963";
+ public static final String s20964 = "20964";
+ public static final String s20965 = "20965";
+ public static final String s20966 = "20966";
+ public static final String s20967 = "20967";
+ public static final String s20968 = "20968";
+ public static final String s20969 = "20969";
+ public static final String s20970 = "20970";
+ public static final String s20971 = "20971";
+ public static final String s20972 = "20972";
+ public static final String s20973 = "20973";
+ public static final String s20974 = "20974";
+ public static final String s20975 = "20975";
+ public static final String s20976 = "20976";
+ public static final String s20977 = "20977";
+ public static final String s20978 = "20978";
+ public static final String s20979 = "20979";
+ public static final String s20980 = "20980";
+ public static final String s20981 = "20981";
+ public static final String s20982 = "20982";
+ public static final String s20983 = "20983";
+ public static final String s20984 = "20984";
+ public static final String s20985 = "20985";
+ public static final String s20986 = "20986";
+ public static final String s20987 = "20987";
+ public static final String s20988 = "20988";
+ public static final String s20989 = "20989";
+ public static final String s20990 = "20990";
+ public static final String s20991 = "20991";
+ public static final String s20992 = "20992";
+ public static final String s20993 = "20993";
+ public static final String s20994 = "20994";
+ public static final String s20995 = "20995";
+ public static final String s20996 = "20996";
+ public static final String s20997 = "20997";
+ public static final String s20998 = "20998";
+ public static final String s20999 = "20999";
+ public static final String s21000 = "21000";
+ public static final String s21001 = "21001";
+ public static final String s21002 = "21002";
+ public static final String s21003 = "21003";
+ public static final String s21004 = "21004";
+ public static final String s21005 = "21005";
+ public static final String s21006 = "21006";
+ public static final String s21007 = "21007";
+ public static final String s21008 = "21008";
+ public static final String s21009 = "21009";
+ public static final String s21010 = "21010";
+ public static final String s21011 = "21011";
+ public static final String s21012 = "21012";
+ public static final String s21013 = "21013";
+ public static final String s21014 = "21014";
+ public static final String s21015 = "21015";
+ public static final String s21016 = "21016";
+ public static final String s21017 = "21017";
+ public static final String s21018 = "21018";
+ public static final String s21019 = "21019";
+ public static final String s21020 = "21020";
+ public static final String s21021 = "21021";
+ public static final String s21022 = "21022";
+ public static final String s21023 = "21023";
+ public static final String s21024 = "21024";
+ public static final String s21025 = "21025";
+ public static final String s21026 = "21026";
+ public static final String s21027 = "21027";
+ public static final String s21028 = "21028";
+ public static final String s21029 = "21029";
+ public static final String s21030 = "21030";
+ public static final String s21031 = "21031";
+ public static final String s21032 = "21032";
+ public static final String s21033 = "21033";
+ public static final String s21034 = "21034";
+ public static final String s21035 = "21035";
+ public static final String s21036 = "21036";
+ public static final String s21037 = "21037";
+ public static final String s21038 = "21038";
+ public static final String s21039 = "21039";
+ public static final String s21040 = "21040";
+ public static final String s21041 = "21041";
+ public static final String s21042 = "21042";
+ public static final String s21043 = "21043";
+ public static final String s21044 = "21044";
+ public static final String s21045 = "21045";
+ public static final String s21046 = "21046";
+ public static final String s21047 = "21047";
+ public static final String s21048 = "21048";
+ public static final String s21049 = "21049";
+ public static final String s21050 = "21050";
+ public static final String s21051 = "21051";
+ public static final String s21052 = "21052";
+ public static final String s21053 = "21053";
+ public static final String s21054 = "21054";
+ public static final String s21055 = "21055";
+ public static final String s21056 = "21056";
+ public static final String s21057 = "21057";
+ public static final String s21058 = "21058";
+ public static final String s21059 = "21059";
+ public static final String s21060 = "21060";
+ public static final String s21061 = "21061";
+ public static final String s21062 = "21062";
+ public static final String s21063 = "21063";
+ public static final String s21064 = "21064";
+ public static final String s21065 = "21065";
+ public static final String s21066 = "21066";
+ public static final String s21067 = "21067";
+ public static final String s21068 = "21068";
+ public static final String s21069 = "21069";
+ public static final String s21070 = "21070";
+ public static final String s21071 = "21071";
+ public static final String s21072 = "21072";
+ public static final String s21073 = "21073";
+ public static final String s21074 = "21074";
+ public static final String s21075 = "21075";
+ public static final String s21076 = "21076";
+ public static final String s21077 = "21077";
+ public static final String s21078 = "21078";
+ public static final String s21079 = "21079";
+ public static final String s21080 = "21080";
+ public static final String s21081 = "21081";
+ public static final String s21082 = "21082";
+ public static final String s21083 = "21083";
+ public static final String s21084 = "21084";
+ public static final String s21085 = "21085";
+ public static final String s21086 = "21086";
+ public static final String s21087 = "21087";
+ public static final String s21088 = "21088";
+ public static final String s21089 = "21089";
+ public static final String s21090 = "21090";
+ public static final String s21091 = "21091";
+ public static final String s21092 = "21092";
+ public static final String s21093 = "21093";
+ public static final String s21094 = "21094";
+ public static final String s21095 = "21095";
+ public static final String s21096 = "21096";
+ public static final String s21097 = "21097";
+ public static final String s21098 = "21098";
+ public static final String s21099 = "21099";
+ public static final String s21100 = "21100";
+ public static final String s21101 = "21101";
+ public static final String s21102 = "21102";
+ public static final String s21103 = "21103";
+ public static final String s21104 = "21104";
+ public static final String s21105 = "21105";
+ public static final String s21106 = "21106";
+ public static final String s21107 = "21107";
+ public static final String s21108 = "21108";
+ public static final String s21109 = "21109";
+ public static final String s21110 = "21110";
+ public static final String s21111 = "21111";
+ public static final String s21112 = "21112";
+ public static final String s21113 = "21113";
+ public static final String s21114 = "21114";
+ public static final String s21115 = "21115";
+ public static final String s21116 = "21116";
+ public static final String s21117 = "21117";
+ public static final String s21118 = "21118";
+ public static final String s21119 = "21119";
+ public static final String s21120 = "21120";
+ public static final String s21121 = "21121";
+ public static final String s21122 = "21122";
+ public static final String s21123 = "21123";
+ public static final String s21124 = "21124";
+ public static final String s21125 = "21125";
+ public static final String s21126 = "21126";
+ public static final String s21127 = "21127";
+ public static final String s21128 = "21128";
+ public static final String s21129 = "21129";
+ public static final String s21130 = "21130";
+ public static final String s21131 = "21131";
+ public static final String s21132 = "21132";
+ public static final String s21133 = "21133";
+ public static final String s21134 = "21134";
+ public static final String s21135 = "21135";
+ public static final String s21136 = "21136";
+ public static final String s21137 = "21137";
+ public static final String s21138 = "21138";
+ public static final String s21139 = "21139";
+ public static final String s21140 = "21140";
+ public static final String s21141 = "21141";
+ public static final String s21142 = "21142";
+ public static final String s21143 = "21143";
+ public static final String s21144 = "21144";
+ public static final String s21145 = "21145";
+ public static final String s21146 = "21146";
+ public static final String s21147 = "21147";
+ public static final String s21148 = "21148";
+ public static final String s21149 = "21149";
+ public static final String s21150 = "21150";
+ public static final String s21151 = "21151";
+ public static final String s21152 = "21152";
+ public static final String s21153 = "21153";
+ public static final String s21154 = "21154";
+ public static final String s21155 = "21155";
+ public static final String s21156 = "21156";
+ public static final String s21157 = "21157";
+ public static final String s21158 = "21158";
+ public static final String s21159 = "21159";
+ public static final String s21160 = "21160";
+ public static final String s21161 = "21161";
+ public static final String s21162 = "21162";
+ public static final String s21163 = "21163";
+ public static final String s21164 = "21164";
+ public static final String s21165 = "21165";
+ public static final String s21166 = "21166";
+ public static final String s21167 = "21167";
+ public static final String s21168 = "21168";
+ public static final String s21169 = "21169";
+ public static final String s21170 = "21170";
+ public static final String s21171 = "21171";
+ public static final String s21172 = "21172";
+ public static final String s21173 = "21173";
+ public static final String s21174 = "21174";
+ public static final String s21175 = "21175";
+ public static final String s21176 = "21176";
+ public static final String s21177 = "21177";
+ public static final String s21178 = "21178";
+ public static final String s21179 = "21179";
+ public static final String s21180 = "21180";
+ public static final String s21181 = "21181";
+ public static final String s21182 = "21182";
+ public static final String s21183 = "21183";
+ public static final String s21184 = "21184";
+ public static final String s21185 = "21185";
+ public static final String s21186 = "21186";
+ public static final String s21187 = "21187";
+ public static final String s21188 = "21188";
+ public static final String s21189 = "21189";
+ public static final String s21190 = "21190";
+ public static final String s21191 = "21191";
+ public static final String s21192 = "21192";
+ public static final String s21193 = "21193";
+ public static final String s21194 = "21194";
+ public static final String s21195 = "21195";
+ public static final String s21196 = "21196";
+ public static final String s21197 = "21197";
+ public static final String s21198 = "21198";
+ public static final String s21199 = "21199";
+ public static final String s21200 = "21200";
+ public static final String s21201 = "21201";
+ public static final String s21202 = "21202";
+ public static final String s21203 = "21203";
+ public static final String s21204 = "21204";
+ public static final String s21205 = "21205";
+ public static final String s21206 = "21206";
+ public static final String s21207 = "21207";
+ public static final String s21208 = "21208";
+ public static final String s21209 = "21209";
+ public static final String s21210 = "21210";
+ public static final String s21211 = "21211";
+ public static final String s21212 = "21212";
+ public static final String s21213 = "21213";
+ public static final String s21214 = "21214";
+ public static final String s21215 = "21215";
+ public static final String s21216 = "21216";
+ public static final String s21217 = "21217";
+ public static final String s21218 = "21218";
+ public static final String s21219 = "21219";
+ public static final String s21220 = "21220";
+ public static final String s21221 = "21221";
+ public static final String s21222 = "21222";
+ public static final String s21223 = "21223";
+ public static final String s21224 = "21224";
+ public static final String s21225 = "21225";
+ public static final String s21226 = "21226";
+ public static final String s21227 = "21227";
+ public static final String s21228 = "21228";
+ public static final String s21229 = "21229";
+ public static final String s21230 = "21230";
+ public static final String s21231 = "21231";
+ public static final String s21232 = "21232";
+ public static final String s21233 = "21233";
+ public static final String s21234 = "21234";
+ public static final String s21235 = "21235";
+ public static final String s21236 = "21236";
+ public static final String s21237 = "21237";
+ public static final String s21238 = "21238";
+ public static final String s21239 = "21239";
+ public static final String s21240 = "21240";
+ public static final String s21241 = "21241";
+ public static final String s21242 = "21242";
+ public static final String s21243 = "21243";
+ public static final String s21244 = "21244";
+ public static final String s21245 = "21245";
+ public static final String s21246 = "21246";
+ public static final String s21247 = "21247";
+ public static final String s21248 = "21248";
+ public static final String s21249 = "21249";
+ public static final String s21250 = "21250";
+ public static final String s21251 = "21251";
+ public static final String s21252 = "21252";
+ public static final String s21253 = "21253";
+ public static final String s21254 = "21254";
+ public static final String s21255 = "21255";
+ public static final String s21256 = "21256";
+ public static final String s21257 = "21257";
+ public static final String s21258 = "21258";
+ public static final String s21259 = "21259";
+ public static final String s21260 = "21260";
+ public static final String s21261 = "21261";
+ public static final String s21262 = "21262";
+ public static final String s21263 = "21263";
+ public static final String s21264 = "21264";
+ public static final String s21265 = "21265";
+ public static final String s21266 = "21266";
+ public static final String s21267 = "21267";
+ public static final String s21268 = "21268";
+ public static final String s21269 = "21269";
+ public static final String s21270 = "21270";
+ public static final String s21271 = "21271";
+ public static final String s21272 = "21272";
+ public static final String s21273 = "21273";
+ public static final String s21274 = "21274";
+ public static final String s21275 = "21275";
+ public static final String s21276 = "21276";
+ public static final String s21277 = "21277";
+ public static final String s21278 = "21278";
+ public static final String s21279 = "21279";
+ public static final String s21280 = "21280";
+ public static final String s21281 = "21281";
+ public static final String s21282 = "21282";
+ public static final String s21283 = "21283";
+ public static final String s21284 = "21284";
+ public static final String s21285 = "21285";
+ public static final String s21286 = "21286";
+ public static final String s21287 = "21287";
+ public static final String s21288 = "21288";
+ public static final String s21289 = "21289";
+ public static final String s21290 = "21290";
+ public static final String s21291 = "21291";
+ public static final String s21292 = "21292";
+ public static final String s21293 = "21293";
+ public static final String s21294 = "21294";
+ public static final String s21295 = "21295";
+ public static final String s21296 = "21296";
+ public static final String s21297 = "21297";
+ public static final String s21298 = "21298";
+ public static final String s21299 = "21299";
+ public static final String s21300 = "21300";
+ public static final String s21301 = "21301";
+ public static final String s21302 = "21302";
+ public static final String s21303 = "21303";
+ public static final String s21304 = "21304";
+ public static final String s21305 = "21305";
+ public static final String s21306 = "21306";
+ public static final String s21307 = "21307";
+ public static final String s21308 = "21308";
+ public static final String s21309 = "21309";
+ public static final String s21310 = "21310";
+ public static final String s21311 = "21311";
+ public static final String s21312 = "21312";
+ public static final String s21313 = "21313";
+ public static final String s21314 = "21314";
+ public static final String s21315 = "21315";
+ public static final String s21316 = "21316";
+ public static final String s21317 = "21317";
+ public static final String s21318 = "21318";
+ public static final String s21319 = "21319";
+ public static final String s21320 = "21320";
+ public static final String s21321 = "21321";
+ public static final String s21322 = "21322";
+ public static final String s21323 = "21323";
+ public static final String s21324 = "21324";
+ public static final String s21325 = "21325";
+ public static final String s21326 = "21326";
+ public static final String s21327 = "21327";
+ public static final String s21328 = "21328";
+ public static final String s21329 = "21329";
+ public static final String s21330 = "21330";
+ public static final String s21331 = "21331";
+ public static final String s21332 = "21332";
+ public static final String s21333 = "21333";
+ public static final String s21334 = "21334";
+ public static final String s21335 = "21335";
+ public static final String s21336 = "21336";
+ public static final String s21337 = "21337";
+ public static final String s21338 = "21338";
+ public static final String s21339 = "21339";
+ public static final String s21340 = "21340";
+ public static final String s21341 = "21341";
+ public static final String s21342 = "21342";
+ public static final String s21343 = "21343";
+ public static final String s21344 = "21344";
+ public static final String s21345 = "21345";
+ public static final String s21346 = "21346";
+ public static final String s21347 = "21347";
+ public static final String s21348 = "21348";
+ public static final String s21349 = "21349";
+ public static final String s21350 = "21350";
+ public static final String s21351 = "21351";
+ public static final String s21352 = "21352";
+ public static final String s21353 = "21353";
+ public static final String s21354 = "21354";
+ public static final String s21355 = "21355";
+ public static final String s21356 = "21356";
+ public static final String s21357 = "21357";
+ public static final String s21358 = "21358";
+ public static final String s21359 = "21359";
+ public static final String s21360 = "21360";
+ public static final String s21361 = "21361";
+ public static final String s21362 = "21362";
+ public static final String s21363 = "21363";
+ public static final String s21364 = "21364";
+ public static final String s21365 = "21365";
+ public static final String s21366 = "21366";
+ public static final String s21367 = "21367";
+ public static final String s21368 = "21368";
+ public static final String s21369 = "21369";
+ public static final String s21370 = "21370";
+ public static final String s21371 = "21371";
+ public static final String s21372 = "21372";
+ public static final String s21373 = "21373";
+ public static final String s21374 = "21374";
+ public static final String s21375 = "21375";
+ public static final String s21376 = "21376";
+ public static final String s21377 = "21377";
+ public static final String s21378 = "21378";
+ public static final String s21379 = "21379";
+ public static final String s21380 = "21380";
+ public static final String s21381 = "21381";
+ public static final String s21382 = "21382";
+ public static final String s21383 = "21383";
+ public static final String s21384 = "21384";
+ public static final String s21385 = "21385";
+ public static final String s21386 = "21386";
+ public static final String s21387 = "21387";
+ public static final String s21388 = "21388";
+ public static final String s21389 = "21389";
+ public static final String s21390 = "21390";
+ public static final String s21391 = "21391";
+ public static final String s21392 = "21392";
+ public static final String s21393 = "21393";
+ public static final String s21394 = "21394";
+ public static final String s21395 = "21395";
+ public static final String s21396 = "21396";
+ public static final String s21397 = "21397";
+ public static final String s21398 = "21398";
+ public static final String s21399 = "21399";
+ public static final String s21400 = "21400";
+ public static final String s21401 = "21401";
+ public static final String s21402 = "21402";
+ public static final String s21403 = "21403";
+ public static final String s21404 = "21404";
+ public static final String s21405 = "21405";
+ public static final String s21406 = "21406";
+ public static final String s21407 = "21407";
+ public static final String s21408 = "21408";
+ public static final String s21409 = "21409";
+ public static final String s21410 = "21410";
+ public static final String s21411 = "21411";
+ public static final String s21412 = "21412";
+ public static final String s21413 = "21413";
+ public static final String s21414 = "21414";
+ public static final String s21415 = "21415";
+ public static final String s21416 = "21416";
+ public static final String s21417 = "21417";
+ public static final String s21418 = "21418";
+ public static final String s21419 = "21419";
+ public static final String s21420 = "21420";
+ public static final String s21421 = "21421";
+ public static final String s21422 = "21422";
+ public static final String s21423 = "21423";
+ public static final String s21424 = "21424";
+ public static final String s21425 = "21425";
+ public static final String s21426 = "21426";
+ public static final String s21427 = "21427";
+ public static final String s21428 = "21428";
+ public static final String s21429 = "21429";
+ public static final String s21430 = "21430";
+ public static final String s21431 = "21431";
+ public static final String s21432 = "21432";
+ public static final String s21433 = "21433";
+ public static final String s21434 = "21434";
+ public static final String s21435 = "21435";
+ public static final String s21436 = "21436";
+ public static final String s21437 = "21437";
+ public static final String s21438 = "21438";
+ public static final String s21439 = "21439";
+ public static final String s21440 = "21440";
+ public static final String s21441 = "21441";
+ public static final String s21442 = "21442";
+ public static final String s21443 = "21443";
+ public static final String s21444 = "21444";
+ public static final String s21445 = "21445";
+ public static final String s21446 = "21446";
+ public static final String s21447 = "21447";
+ public static final String s21448 = "21448";
+ public static final String s21449 = "21449";
+ public static final String s21450 = "21450";
+ public static final String s21451 = "21451";
+ public static final String s21452 = "21452";
+ public static final String s21453 = "21453";
+ public static final String s21454 = "21454";
+ public static final String s21455 = "21455";
+ public static final String s21456 = "21456";
+ public static final String s21457 = "21457";
+ public static final String s21458 = "21458";
+ public static final String s21459 = "21459";
+ public static final String s21460 = "21460";
+ public static final String s21461 = "21461";
+ public static final String s21462 = "21462";
+ public static final String s21463 = "21463";
+ public static final String s21464 = "21464";
+ public static final String s21465 = "21465";
+ public static final String s21466 = "21466";
+ public static final String s21467 = "21467";
+ public static final String s21468 = "21468";
+ public static final String s21469 = "21469";
+ public static final String s21470 = "21470";
+ public static final String s21471 = "21471";
+ public static final String s21472 = "21472";
+ public static final String s21473 = "21473";
+ public static final String s21474 = "21474";
+ public static final String s21475 = "21475";
+ public static final String s21476 = "21476";
+ public static final String s21477 = "21477";
+ public static final String s21478 = "21478";
+ public static final String s21479 = "21479";
+ public static final String s21480 = "21480";
+ public static final String s21481 = "21481";
+ public static final String s21482 = "21482";
+ public static final String s21483 = "21483";
+ public static final String s21484 = "21484";
+ public static final String s21485 = "21485";
+ public static final String s21486 = "21486";
+ public static final String s21487 = "21487";
+ public static final String s21488 = "21488";
+ public static final String s21489 = "21489";
+ public static final String s21490 = "21490";
+ public static final String s21491 = "21491";
+ public static final String s21492 = "21492";
+ public static final String s21493 = "21493";
+ public static final String s21494 = "21494";
+ public static final String s21495 = "21495";
+ public static final String s21496 = "21496";
+ public static final String s21497 = "21497";
+ public static final String s21498 = "21498";
+ public static final String s21499 = "21499";
+ public static final String s21500 = "21500";
+ public static final String s21501 = "21501";
+ public static final String s21502 = "21502";
+ public static final String s21503 = "21503";
+ public static final String s21504 = "21504";
+ public static final String s21505 = "21505";
+ public static final String s21506 = "21506";
+ public static final String s21507 = "21507";
+ public static final String s21508 = "21508";
+ public static final String s21509 = "21509";
+ public static final String s21510 = "21510";
+ public static final String s21511 = "21511";
+ public static final String s21512 = "21512";
+ public static final String s21513 = "21513";
+ public static final String s21514 = "21514";
+ public static final String s21515 = "21515";
+ public static final String s21516 = "21516";
+ public static final String s21517 = "21517";
+ public static final String s21518 = "21518";
+ public static final String s21519 = "21519";
+ public static final String s21520 = "21520";
+ public static final String s21521 = "21521";
+ public static final String s21522 = "21522";
+ public static final String s21523 = "21523";
+ public static final String s21524 = "21524";
+ public static final String s21525 = "21525";
+ public static final String s21526 = "21526";
+ public static final String s21527 = "21527";
+ public static final String s21528 = "21528";
+ public static final String s21529 = "21529";
+ public static final String s21530 = "21530";
+ public static final String s21531 = "21531";
+ public static final String s21532 = "21532";
+ public static final String s21533 = "21533";
+ public static final String s21534 = "21534";
+ public static final String s21535 = "21535";
+ public static final String s21536 = "21536";
+ public static final String s21537 = "21537";
+ public static final String s21538 = "21538";
+ public static final String s21539 = "21539";
+ public static final String s21540 = "21540";
+ public static final String s21541 = "21541";
+ public static final String s21542 = "21542";
+ public static final String s21543 = "21543";
+ public static final String s21544 = "21544";
+ public static final String s21545 = "21545";
+ public static final String s21546 = "21546";
+ public static final String s21547 = "21547";
+ public static final String s21548 = "21548";
+ public static final String s21549 = "21549";
+ public static final String s21550 = "21550";
+ public static final String s21551 = "21551";
+ public static final String s21552 = "21552";
+ public static final String s21553 = "21553";
+ public static final String s21554 = "21554";
+ public static final String s21555 = "21555";
+ public static final String s21556 = "21556";
+ public static final String s21557 = "21557";
+ public static final String s21558 = "21558";
+ public static final String s21559 = "21559";
+ public static final String s21560 = "21560";
+ public static final String s21561 = "21561";
+ public static final String s21562 = "21562";
+ public static final String s21563 = "21563";
+ public static final String s21564 = "21564";
+ public static final String s21565 = "21565";
+ public static final String s21566 = "21566";
+ public static final String s21567 = "21567";
+ public static final String s21568 = "21568";
+ public static final String s21569 = "21569";
+ public static final String s21570 = "21570";
+ public static final String s21571 = "21571";
+ public static final String s21572 = "21572";
+ public static final String s21573 = "21573";
+ public static final String s21574 = "21574";
+ public static final String s21575 = "21575";
+ public static final String s21576 = "21576";
+ public static final String s21577 = "21577";
+ public static final String s21578 = "21578";
+ public static final String s21579 = "21579";
+ public static final String s21580 = "21580";
+ public static final String s21581 = "21581";
+ public static final String s21582 = "21582";
+ public static final String s21583 = "21583";
+ public static final String s21584 = "21584";
+ public static final String s21585 = "21585";
+ public static final String s21586 = "21586";
+ public static final String s21587 = "21587";
+ public static final String s21588 = "21588";
+ public static final String s21589 = "21589";
+ public static final String s21590 = "21590";
+ public static final String s21591 = "21591";
+ public static final String s21592 = "21592";
+ public static final String s21593 = "21593";
+ public static final String s21594 = "21594";
+ public static final String s21595 = "21595";
+ public static final String s21596 = "21596";
+ public static final String s21597 = "21597";
+ public static final String s21598 = "21598";
+ public static final String s21599 = "21599";
+ public static final String s21600 = "21600";
+ public static final String s21601 = "21601";
+ public static final String s21602 = "21602";
+ public static final String s21603 = "21603";
+ public static final String s21604 = "21604";
+ public static final String s21605 = "21605";
+ public static final String s21606 = "21606";
+ public static final String s21607 = "21607";
+ public static final String s21608 = "21608";
+ public static final String s21609 = "21609";
+ public static final String s21610 = "21610";
+ public static final String s21611 = "21611";
+ public static final String s21612 = "21612";
+ public static final String s21613 = "21613";
+ public static final String s21614 = "21614";
+ public static final String s21615 = "21615";
+ public static final String s21616 = "21616";
+ public static final String s21617 = "21617";
+ public static final String s21618 = "21618";
+ public static final String s21619 = "21619";
+ public static final String s21620 = "21620";
+ public static final String s21621 = "21621";
+ public static final String s21622 = "21622";
+ public static final String s21623 = "21623";
+ public static final String s21624 = "21624";
+ public static final String s21625 = "21625";
+ public static final String s21626 = "21626";
+ public static final String s21627 = "21627";
+ public static final String s21628 = "21628";
+ public static final String s21629 = "21629";
+ public static final String s21630 = "21630";
+ public static final String s21631 = "21631";
+ public static final String s21632 = "21632";
+ public static final String s21633 = "21633";
+ public static final String s21634 = "21634";
+ public static final String s21635 = "21635";
+ public static final String s21636 = "21636";
+ public static final String s21637 = "21637";
+ public static final String s21638 = "21638";
+ public static final String s21639 = "21639";
+ public static final String s21640 = "21640";
+ public static final String s21641 = "21641";
+ public static final String s21642 = "21642";
+ public static final String s21643 = "21643";
+ public static final String s21644 = "21644";
+ public static final String s21645 = "21645";
+ public static final String s21646 = "21646";
+ public static final String s21647 = "21647";
+ public static final String s21648 = "21648";
+ public static final String s21649 = "21649";
+ public static final String s21650 = "21650";
+ public static final String s21651 = "21651";
+ public static final String s21652 = "21652";
+ public static final String s21653 = "21653";
+ public static final String s21654 = "21654";
+ public static final String s21655 = "21655";
+ public static final String s21656 = "21656";
+ public static final String s21657 = "21657";
+ public static final String s21658 = "21658";
+ public static final String s21659 = "21659";
+ public static final String s21660 = "21660";
+ public static final String s21661 = "21661";
+ public static final String s21662 = "21662";
+ public static final String s21663 = "21663";
+ public static final String s21664 = "21664";
+ public static final String s21665 = "21665";
+ public static final String s21666 = "21666";
+ public static final String s21667 = "21667";
+ public static final String s21668 = "21668";
+ public static final String s21669 = "21669";
+ public static final String s21670 = "21670";
+ public static final String s21671 = "21671";
+ public static final String s21672 = "21672";
+ public static final String s21673 = "21673";
+ public static final String s21674 = "21674";
+ public static final String s21675 = "21675";
+ public static final String s21676 = "21676";
+ public static final String s21677 = "21677";
+ public static final String s21678 = "21678";
+ public static final String s21679 = "21679";
+ public static final String s21680 = "21680";
+ public static final String s21681 = "21681";
+ public static final String s21682 = "21682";
+ public static final String s21683 = "21683";
+ public static final String s21684 = "21684";
+ public static final String s21685 = "21685";
+ public static final String s21686 = "21686";
+ public static final String s21687 = "21687";
+ public static final String s21688 = "21688";
+ public static final String s21689 = "21689";
+ public static final String s21690 = "21690";
+ public static final String s21691 = "21691";
+ public static final String s21692 = "21692";
+ public static final String s21693 = "21693";
+ public static final String s21694 = "21694";
+ public static final String s21695 = "21695";
+ public static final String s21696 = "21696";
+ public static final String s21697 = "21697";
+ public static final String s21698 = "21698";
+ public static final String s21699 = "21699";
+ public static final String s21700 = "21700";
+ public static final String s21701 = "21701";
+ public static final String s21702 = "21702";
+ public static final String s21703 = "21703";
+ public static final String s21704 = "21704";
+ public static final String s21705 = "21705";
+ public static final String s21706 = "21706";
+ public static final String s21707 = "21707";
+ public static final String s21708 = "21708";
+ public static final String s21709 = "21709";
+ public static final String s21710 = "21710";
+ public static final String s21711 = "21711";
+ public static final String s21712 = "21712";
+ public static final String s21713 = "21713";
+ public static final String s21714 = "21714";
+ public static final String s21715 = "21715";
+ public static final String s21716 = "21716";
+ public static final String s21717 = "21717";
+ public static final String s21718 = "21718";
+ public static final String s21719 = "21719";
+ public static final String s21720 = "21720";
+ public static final String s21721 = "21721";
+ public static final String s21722 = "21722";
+ public static final String s21723 = "21723";
+ public static final String s21724 = "21724";
+ public static final String s21725 = "21725";
+ public static final String s21726 = "21726";
+ public static final String s21727 = "21727";
+ public static final String s21728 = "21728";
+ public static final String s21729 = "21729";
+ public static final String s21730 = "21730";
+ public static final String s21731 = "21731";
+ public static final String s21732 = "21732";
+ public static final String s21733 = "21733";
+ public static final String s21734 = "21734";
+ public static final String s21735 = "21735";
+ public static final String s21736 = "21736";
+ public static final String s21737 = "21737";
+ public static final String s21738 = "21738";
+ public static final String s21739 = "21739";
+ public static final String s21740 = "21740";
+ public static final String s21741 = "21741";
+ public static final String s21742 = "21742";
+ public static final String s21743 = "21743";
+ public static final String s21744 = "21744";
+ public static final String s21745 = "21745";
+ public static final String s21746 = "21746";
+ public static final String s21747 = "21747";
+ public static final String s21748 = "21748";
+ public static final String s21749 = "21749";
+ public static final String s21750 = "21750";
+ public static final String s21751 = "21751";
+ public static final String s21752 = "21752";
+ public static final String s21753 = "21753";
+ public static final String s21754 = "21754";
+ public static final String s21755 = "21755";
+ public static final String s21756 = "21756";
+ public static final String s21757 = "21757";
+ public static final String s21758 = "21758";
+ public static final String s21759 = "21759";
+ public static final String s21760 = "21760";
+ public static final String s21761 = "21761";
+ public static final String s21762 = "21762";
+ public static final String s21763 = "21763";
+ public static final String s21764 = "21764";
+ public static final String s21765 = "21765";
+ public static final String s21766 = "21766";
+ public static final String s21767 = "21767";
+ public static final String s21768 = "21768";
+ public static final String s21769 = "21769";
+ public static final String s21770 = "21770";
+ public static final String s21771 = "21771";
+ public static final String s21772 = "21772";
+ public static final String s21773 = "21773";
+ public static final String s21774 = "21774";
+ public static final String s21775 = "21775";
+ public static final String s21776 = "21776";
+ public static final String s21777 = "21777";
+ public static final String s21778 = "21778";
+ public static final String s21779 = "21779";
+ public static final String s21780 = "21780";
+ public static final String s21781 = "21781";
+ public static final String s21782 = "21782";
+ public static final String s21783 = "21783";
+ public static final String s21784 = "21784";
+ public static final String s21785 = "21785";
+ public static final String s21786 = "21786";
+ public static final String s21787 = "21787";
+ public static final String s21788 = "21788";
+ public static final String s21789 = "21789";
+ public static final String s21790 = "21790";
+ public static final String s21791 = "21791";
+ public static final String s21792 = "21792";
+ public static final String s21793 = "21793";
+ public static final String s21794 = "21794";
+ public static final String s21795 = "21795";
+ public static final String s21796 = "21796";
+ public static final String s21797 = "21797";
+ public static final String s21798 = "21798";
+ public static final String s21799 = "21799";
+ public static final String s21800 = "21800";
+ public static final String s21801 = "21801";
+ public static final String s21802 = "21802";
+ public static final String s21803 = "21803";
+ public static final String s21804 = "21804";
+ public static final String s21805 = "21805";
+ public static final String s21806 = "21806";
+ public static final String s21807 = "21807";
+ public static final String s21808 = "21808";
+ public static final String s21809 = "21809";
+ public static final String s21810 = "21810";
+ public static final String s21811 = "21811";
+ public static final String s21812 = "21812";
+ public static final String s21813 = "21813";
+ public static final String s21814 = "21814";
+ public static final String s21815 = "21815";
+ public static final String s21816 = "21816";
+ public static final String s21817 = "21817";
+ public static final String s21818 = "21818";
+ public static final String s21819 = "21819";
+ public static final String s21820 = "21820";
+ public static final String s21821 = "21821";
+ public static final String s21822 = "21822";
+ public static final String s21823 = "21823";
+ public static final String s21824 = "21824";
+ public static final String s21825 = "21825";
+ public static final String s21826 = "21826";
+ public static final String s21827 = "21827";
+ public static final String s21828 = "21828";
+ public static final String s21829 = "21829";
+ public static final String s21830 = "21830";
+ public static final String s21831 = "21831";
+ public static final String s21832 = "21832";
+ public static final String s21833 = "21833";
+ public static final String s21834 = "21834";
+ public static final String s21835 = "21835";
+ public static final String s21836 = "21836";
+ public static final String s21837 = "21837";
+ public static final String s21838 = "21838";
+ public static final String s21839 = "21839";
+ public static final String s21840 = "21840";
+ public static final String s21841 = "21841";
+ public static final String s21842 = "21842";
+ public static final String s21843 = "21843";
+ public static final String s21844 = "21844";
+ public static final String s21845 = "21845";
+ public static final String s21846 = "21846";
+ public static final String s21847 = "21847";
+ public static final String s21848 = "21848";
+ public static final String s21849 = "21849";
+ public static final String s21850 = "21850";
+ public static final String s21851 = "21851";
+ public static final String s21852 = "21852";
+ public static final String s21853 = "21853";
+ public static final String s21854 = "21854";
+ public static final String s21855 = "21855";
+ public static final String s21856 = "21856";
+ public static final String s21857 = "21857";
+ public static final String s21858 = "21858";
+ public static final String s21859 = "21859";
+ public static final String s21860 = "21860";
+ public static final String s21861 = "21861";
+ public static final String s21862 = "21862";
+ public static final String s21863 = "21863";
+ public static final String s21864 = "21864";
+ public static final String s21865 = "21865";
+ public static final String s21866 = "21866";
+ public static final String s21867 = "21867";
+ public static final String s21868 = "21868";
+ public static final String s21869 = "21869";
+ public static final String s21870 = "21870";
+ public static final String s21871 = "21871";
+ public static final String s21872 = "21872";
+ public static final String s21873 = "21873";
+ public static final String s21874 = "21874";
+ public static final String s21875 = "21875";
+ public static final String s21876 = "21876";
+ public static final String s21877 = "21877";
+ public static final String s21878 = "21878";
+ public static final String s21879 = "21879";
+ public static final String s21880 = "21880";
+ public static final String s21881 = "21881";
+ public static final String s21882 = "21882";
+ public static final String s21883 = "21883";
+ public static final String s21884 = "21884";
+ public static final String s21885 = "21885";
+ public static final String s21886 = "21886";
+ public static final String s21887 = "21887";
+ public static final String s21888 = "21888";
+ public static final String s21889 = "21889";
+ public static final String s21890 = "21890";
+ public static final String s21891 = "21891";
+ public static final String s21892 = "21892";
+ public static final String s21893 = "21893";
+ public static final String s21894 = "21894";
+ public static final String s21895 = "21895";
+ public static final String s21896 = "21896";
+ public static final String s21897 = "21897";
+ public static final String s21898 = "21898";
+ public static final String s21899 = "21899";
+ public static final String s21900 = "21900";
+ public static final String s21901 = "21901";
+ public static final String s21902 = "21902";
+ public static final String s21903 = "21903";
+ public static final String s21904 = "21904";
+ public static final String s21905 = "21905";
+ public static final String s21906 = "21906";
+ public static final String s21907 = "21907";
+ public static final String s21908 = "21908";
+ public static final String s21909 = "21909";
+ public static final String s21910 = "21910";
+ public static final String s21911 = "21911";
+ public static final String s21912 = "21912";
+ public static final String s21913 = "21913";
+ public static final String s21914 = "21914";
+ public static final String s21915 = "21915";
+ public static final String s21916 = "21916";
+ public static final String s21917 = "21917";
+ public static final String s21918 = "21918";
+ public static final String s21919 = "21919";
+ public static final String s21920 = "21920";
+ public static final String s21921 = "21921";
+ public static final String s21922 = "21922";
+ public static final String s21923 = "21923";
+ public static final String s21924 = "21924";
+ public static final String s21925 = "21925";
+ public static final String s21926 = "21926";
+ public static final String s21927 = "21927";
+ public static final String s21928 = "21928";
+ public static final String s21929 = "21929";
+ public static final String s21930 = "21930";
+ public static final String s21931 = "21931";
+ public static final String s21932 = "21932";
+ public static final String s21933 = "21933";
+ public static final String s21934 = "21934";
+ public static final String s21935 = "21935";
+ public static final String s21936 = "21936";
+ public static final String s21937 = "21937";
+ public static final String s21938 = "21938";
+ public static final String s21939 = "21939";
+ public static final String s21940 = "21940";
+ public static final String s21941 = "21941";
+ public static final String s21942 = "21942";
+ public static final String s21943 = "21943";
+ public static final String s21944 = "21944";
+ public static final String s21945 = "21945";
+ public static final String s21946 = "21946";
+ public static final String s21947 = "21947";
+ public static final String s21948 = "21948";
+ public static final String s21949 = "21949";
+ public static final String s21950 = "21950";
+ public static final String s21951 = "21951";
+ public static final String s21952 = "21952";
+ public static final String s21953 = "21953";
+ public static final String s21954 = "21954";
+ public static final String s21955 = "21955";
+ public static final String s21956 = "21956";
+ public static final String s21957 = "21957";
+ public static final String s21958 = "21958";
+ public static final String s21959 = "21959";
+ public static final String s21960 = "21960";
+ public static final String s21961 = "21961";
+ public static final String s21962 = "21962";
+ public static final String s21963 = "21963";
+ public static final String s21964 = "21964";
+ public static final String s21965 = "21965";
+ public static final String s21966 = "21966";
+ public static final String s21967 = "21967";
+ public static final String s21968 = "21968";
+ public static final String s21969 = "21969";
+ public static final String s21970 = "21970";
+ public static final String s21971 = "21971";
+ public static final String s21972 = "21972";
+ public static final String s21973 = "21973";
+ public static final String s21974 = "21974";
+ public static final String s21975 = "21975";
+ public static final String s21976 = "21976";
+ public static final String s21977 = "21977";
+ public static final String s21978 = "21978";
+ public static final String s21979 = "21979";
+ public static final String s21980 = "21980";
+ public static final String s21981 = "21981";
+ public static final String s21982 = "21982";
+ public static final String s21983 = "21983";
+ public static final String s21984 = "21984";
+ public static final String s21985 = "21985";
+ public static final String s21986 = "21986";
+ public static final String s21987 = "21987";
+ public static final String s21988 = "21988";
+ public static final String s21989 = "21989";
+ public static final String s21990 = "21990";
+ public static final String s21991 = "21991";
+ public static final String s21992 = "21992";
+ public static final String s21993 = "21993";
+ public static final String s21994 = "21994";
+ public static final String s21995 = "21995";
+ public static final String s21996 = "21996";
+ public static final String s21997 = "21997";
+ public static final String s21998 = "21998";
+ public static final String s21999 = "21999";
+ public static final String s22000 = "22000";
+ public static final String s22001 = "22001";
+ public static final String s22002 = "22002";
+ public static final String s22003 = "22003";
+ public static final String s22004 = "22004";
+ public static final String s22005 = "22005";
+ public static final String s22006 = "22006";
+ public static final String s22007 = "22007";
+ public static final String s22008 = "22008";
+ public static final String s22009 = "22009";
+ public static final String s22010 = "22010";
+ public static final String s22011 = "22011";
+ public static final String s22012 = "22012";
+ public static final String s22013 = "22013";
+ public static final String s22014 = "22014";
+ public static final String s22015 = "22015";
+ public static final String s22016 = "22016";
+ public static final String s22017 = "22017";
+ public static final String s22018 = "22018";
+ public static final String s22019 = "22019";
+ public static final String s22020 = "22020";
+ public static final String s22021 = "22021";
+ public static final String s22022 = "22022";
+ public static final String s22023 = "22023";
+ public static final String s22024 = "22024";
+ public static final String s22025 = "22025";
+ public static final String s22026 = "22026";
+ public static final String s22027 = "22027";
+ public static final String s22028 = "22028";
+ public static final String s22029 = "22029";
+ public static final String s22030 = "22030";
+ public static final String s22031 = "22031";
+ public static final String s22032 = "22032";
+ public static final String s22033 = "22033";
+ public static final String s22034 = "22034";
+ public static final String s22035 = "22035";
+ public static final String s22036 = "22036";
+ public static final String s22037 = "22037";
+ public static final String s22038 = "22038";
+ public static final String s22039 = "22039";
+ public static final String s22040 = "22040";
+ public static final String s22041 = "22041";
+ public static final String s22042 = "22042";
+ public static final String s22043 = "22043";
+ public static final String s22044 = "22044";
+ public static final String s22045 = "22045";
+ public static final String s22046 = "22046";
+ public static final String s22047 = "22047";
+ public static final String s22048 = "22048";
+ public static final String s22049 = "22049";
+ public static final String s22050 = "22050";
+ public static final String s22051 = "22051";
+ public static final String s22052 = "22052";
+ public static final String s22053 = "22053";
+ public static final String s22054 = "22054";
+ public static final String s22055 = "22055";
+ public static final String s22056 = "22056";
+ public static final String s22057 = "22057";
+ public static final String s22058 = "22058";
+ public static final String s22059 = "22059";
+ public static final String s22060 = "22060";
+ public static final String s22061 = "22061";
+ public static final String s22062 = "22062";
+ public static final String s22063 = "22063";
+ public static final String s22064 = "22064";
+ public static final String s22065 = "22065";
+ public static final String s22066 = "22066";
+ public static final String s22067 = "22067";
+ public static final String s22068 = "22068";
+ public static final String s22069 = "22069";
+ public static final String s22070 = "22070";
+ public static final String s22071 = "22071";
+ public static final String s22072 = "22072";
+ public static final String s22073 = "22073";
+ public static final String s22074 = "22074";
+ public static final String s22075 = "22075";
+ public static final String s22076 = "22076";
+ public static final String s22077 = "22077";
+ public static final String s22078 = "22078";
+ public static final String s22079 = "22079";
+ public static final String s22080 = "22080";
+ public static final String s22081 = "22081";
+ public static final String s22082 = "22082";
+ public static final String s22083 = "22083";
+ public static final String s22084 = "22084";
+ public static final String s22085 = "22085";
+ public static final String s22086 = "22086";
+ public static final String s22087 = "22087";
+ public static final String s22088 = "22088";
+ public static final String s22089 = "22089";
+ public static final String s22090 = "22090";
+ public static final String s22091 = "22091";
+ public static final String s22092 = "22092";
+ public static final String s22093 = "22093";
+ public static final String s22094 = "22094";
+ public static final String s22095 = "22095";
+ public static final String s22096 = "22096";
+ public static final String s22097 = "22097";
+ public static final String s22098 = "22098";
+ public static final String s22099 = "22099";
+ public static final String s22100 = "22100";
+ public static final String s22101 = "22101";
+ public static final String s22102 = "22102";
+ public static final String s22103 = "22103";
+ public static final String s22104 = "22104";
+ public static final String s22105 = "22105";
+ public static final String s22106 = "22106";
+ public static final String s22107 = "22107";
+ public static final String s22108 = "22108";
+ public static final String s22109 = "22109";
+ public static final String s22110 = "22110";
+ public static final String s22111 = "22111";
+ public static final String s22112 = "22112";
+ public static final String s22113 = "22113";
+ public static final String s22114 = "22114";
+ public static final String s22115 = "22115";
+ public static final String s22116 = "22116";
+ public static final String s22117 = "22117";
+ public static final String s22118 = "22118";
+ public static final String s22119 = "22119";
+ public static final String s22120 = "22120";
+ public static final String s22121 = "22121";
+ public static final String s22122 = "22122";
+ public static final String s22123 = "22123";
+ public static final String s22124 = "22124";
+ public static final String s22125 = "22125";
+ public static final String s22126 = "22126";
+ public static final String s22127 = "22127";
+ public static final String s22128 = "22128";
+ public static final String s22129 = "22129";
+ public static final String s22130 = "22130";
+ public static final String s22131 = "22131";
+ public static final String s22132 = "22132";
+ public static final String s22133 = "22133";
+ public static final String s22134 = "22134";
+ public static final String s22135 = "22135";
+ public static final String s22136 = "22136";
+ public static final String s22137 = "22137";
+ public static final String s22138 = "22138";
+ public static final String s22139 = "22139";
+ public static final String s22140 = "22140";
+ public static final String s22141 = "22141";
+ public static final String s22142 = "22142";
+ public static final String s22143 = "22143";
+ public static final String s22144 = "22144";
+ public static final String s22145 = "22145";
+ public static final String s22146 = "22146";
+ public static final String s22147 = "22147";
+ public static final String s22148 = "22148";
+ public static final String s22149 = "22149";
+ public static final String s22150 = "22150";
+ public static final String s22151 = "22151";
+ public static final String s22152 = "22152";
+ public static final String s22153 = "22153";
+ public static final String s22154 = "22154";
+ public static final String s22155 = "22155";
+ public static final String s22156 = "22156";
+ public static final String s22157 = "22157";
+ public static final String s22158 = "22158";
+ public static final String s22159 = "22159";
+ public static final String s22160 = "22160";
+ public static final String s22161 = "22161";
+ public static final String s22162 = "22162";
+ public static final String s22163 = "22163";
+ public static final String s22164 = "22164";
+ public static final String s22165 = "22165";
+ public static final String s22166 = "22166";
+ public static final String s22167 = "22167";
+ public static final String s22168 = "22168";
+ public static final String s22169 = "22169";
+ public static final String s22170 = "22170";
+ public static final String s22171 = "22171";
+ public static final String s22172 = "22172";
+ public static final String s22173 = "22173";
+ public static final String s22174 = "22174";
+ public static final String s22175 = "22175";
+ public static final String s22176 = "22176";
+ public static final String s22177 = "22177";
+ public static final String s22178 = "22178";
+ public static final String s22179 = "22179";
+ public static final String s22180 = "22180";
+ public static final String s22181 = "22181";
+ public static final String s22182 = "22182";
+ public static final String s22183 = "22183";
+ public static final String s22184 = "22184";
+ public static final String s22185 = "22185";
+ public static final String s22186 = "22186";
+ public static final String s22187 = "22187";
+ public static final String s22188 = "22188";
+ public static final String s22189 = "22189";
+ public static final String s22190 = "22190";
+ public static final String s22191 = "22191";
+ public static final String s22192 = "22192";
+ public static final String s22193 = "22193";
+ public static final String s22194 = "22194";
+ public static final String s22195 = "22195";
+ public static final String s22196 = "22196";
+ public static final String s22197 = "22197";
+ public static final String s22198 = "22198";
+ public static final String s22199 = "22199";
+ public static final String s22200 = "22200";
+ public static final String s22201 = "22201";
+ public static final String s22202 = "22202";
+ public static final String s22203 = "22203";
+ public static final String s22204 = "22204";
+ public static final String s22205 = "22205";
+ public static final String s22206 = "22206";
+ public static final String s22207 = "22207";
+ public static final String s22208 = "22208";
+ public static final String s22209 = "22209";
+ public static final String s22210 = "22210";
+ public static final String s22211 = "22211";
+ public static final String s22212 = "22212";
+ public static final String s22213 = "22213";
+ public static final String s22214 = "22214";
+ public static final String s22215 = "22215";
+ public static final String s22216 = "22216";
+ public static final String s22217 = "22217";
+ public static final String s22218 = "22218";
+ public static final String s22219 = "22219";
+ public static final String s22220 = "22220";
+ public static final String s22221 = "22221";
+ public static final String s22222 = "22222";
+ public static final String s22223 = "22223";
+ public static final String s22224 = "22224";
+ public static final String s22225 = "22225";
+ public static final String s22226 = "22226";
+ public static final String s22227 = "22227";
+ public static final String s22228 = "22228";
+ public static final String s22229 = "22229";
+ public static final String s22230 = "22230";
+ public static final String s22231 = "22231";
+ public static final String s22232 = "22232";
+ public static final String s22233 = "22233";
+ public static final String s22234 = "22234";
+ public static final String s22235 = "22235";
+ public static final String s22236 = "22236";
+ public static final String s22237 = "22237";
+ public static final String s22238 = "22238";
+ public static final String s22239 = "22239";
+ public static final String s22240 = "22240";
+ public static final String s22241 = "22241";
+ public static final String s22242 = "22242";
+ public static final String s22243 = "22243";
+ public static final String s22244 = "22244";
+ public static final String s22245 = "22245";
+ public static final String s22246 = "22246";
+ public static final String s22247 = "22247";
+ public static final String s22248 = "22248";
+ public static final String s22249 = "22249";
+ public static final String s22250 = "22250";
+ public static final String s22251 = "22251";
+ public static final String s22252 = "22252";
+ public static final String s22253 = "22253";
+ public static final String s22254 = "22254";
+ public static final String s22255 = "22255";
+ public static final String s22256 = "22256";
+ public static final String s22257 = "22257";
+ public static final String s22258 = "22258";
+ public static final String s22259 = "22259";
+ public static final String s22260 = "22260";
+ public static final String s22261 = "22261";
+ public static final String s22262 = "22262";
+ public static final String s22263 = "22263";
+ public static final String s22264 = "22264";
+ public static final String s22265 = "22265";
+ public static final String s22266 = "22266";
+ public static final String s22267 = "22267";
+ public static final String s22268 = "22268";
+ public static final String s22269 = "22269";
+ public static final String s22270 = "22270";
+ public static final String s22271 = "22271";
+ public static final String s22272 = "22272";
+ public static final String s22273 = "22273";
+ public static final String s22274 = "22274";
+ public static final String s22275 = "22275";
+ public static final String s22276 = "22276";
+ public static final String s22277 = "22277";
+ public static final String s22278 = "22278";
+ public static final String s22279 = "22279";
+ public static final String s22280 = "22280";
+ public static final String s22281 = "22281";
+ public static final String s22282 = "22282";
+ public static final String s22283 = "22283";
+ public static final String s22284 = "22284";
+ public static final String s22285 = "22285";
+ public static final String s22286 = "22286";
+ public static final String s22287 = "22287";
+ public static final String s22288 = "22288";
+ public static final String s22289 = "22289";
+ public static final String s22290 = "22290";
+ public static final String s22291 = "22291";
+ public static final String s22292 = "22292";
+ public static final String s22293 = "22293";
+ public static final String s22294 = "22294";
+ public static final String s22295 = "22295";
+ public static final String s22296 = "22296";
+ public static final String s22297 = "22297";
+ public static final String s22298 = "22298";
+ public static final String s22299 = "22299";
+ public static final String s22300 = "22300";
+ public static final String s22301 = "22301";
+ public static final String s22302 = "22302";
+ public static final String s22303 = "22303";
+ public static final String s22304 = "22304";
+ public static final String s22305 = "22305";
+ public static final String s22306 = "22306";
+ public static final String s22307 = "22307";
+ public static final String s22308 = "22308";
+ public static final String s22309 = "22309";
+ public static final String s22310 = "22310";
+ public static final String s22311 = "22311";
+ public static final String s22312 = "22312";
+ public static final String s22313 = "22313";
+ public static final String s22314 = "22314";
+ public static final String s22315 = "22315";
+ public static final String s22316 = "22316";
+ public static final String s22317 = "22317";
+ public static final String s22318 = "22318";
+ public static final String s22319 = "22319";
+ public static final String s22320 = "22320";
+ public static final String s22321 = "22321";
+ public static final String s22322 = "22322";
+ public static final String s22323 = "22323";
+ public static final String s22324 = "22324";
+ public static final String s22325 = "22325";
+ public static final String s22326 = "22326";
+ public static final String s22327 = "22327";
+ public static final String s22328 = "22328";
+ public static final String s22329 = "22329";
+ public static final String s22330 = "22330";
+ public static final String s22331 = "22331";
+ public static final String s22332 = "22332";
+ public static final String s22333 = "22333";
+ public static final String s22334 = "22334";
+ public static final String s22335 = "22335";
+ public static final String s22336 = "22336";
+ public static final String s22337 = "22337";
+ public static final String s22338 = "22338";
+ public static final String s22339 = "22339";
+ public static final String s22340 = "22340";
+ public static final String s22341 = "22341";
+ public static final String s22342 = "22342";
+ public static final String s22343 = "22343";
+ public static final String s22344 = "22344";
+ public static final String s22345 = "22345";
+ public static final String s22346 = "22346";
+ public static final String s22347 = "22347";
+ public static final String s22348 = "22348";
+ public static final String s22349 = "22349";
+ public static final String s22350 = "22350";
+ public static final String s22351 = "22351";
+ public static final String s22352 = "22352";
+ public static final String s22353 = "22353";
+ public static final String s22354 = "22354";
+ public static final String s22355 = "22355";
+ public static final String s22356 = "22356";
+ public static final String s22357 = "22357";
+ public static final String s22358 = "22358";
+ public static final String s22359 = "22359";
+ public static final String s22360 = "22360";
+ public static final String s22361 = "22361";
+ public static final String s22362 = "22362";
+ public static final String s22363 = "22363";
+ public static final String s22364 = "22364";
+ public static final String s22365 = "22365";
+ public static final String s22366 = "22366";
+ public static final String s22367 = "22367";
+ public static final String s22368 = "22368";
+ public static final String s22369 = "22369";
+ public static final String s22370 = "22370";
+ public static final String s22371 = "22371";
+ public static final String s22372 = "22372";
+ public static final String s22373 = "22373";
+ public static final String s22374 = "22374";
+ public static final String s22375 = "22375";
+ public static final String s22376 = "22376";
+ public static final String s22377 = "22377";
+ public static final String s22378 = "22378";
+ public static final String s22379 = "22379";
+ public static final String s22380 = "22380";
+ public static final String s22381 = "22381";
+ public static final String s22382 = "22382";
+ public static final String s22383 = "22383";
+ public static final String s22384 = "22384";
+ public static final String s22385 = "22385";
+ public static final String s22386 = "22386";
+ public static final String s22387 = "22387";
+ public static final String s22388 = "22388";
+ public static final String s22389 = "22389";
+ public static final String s22390 = "22390";
+ public static final String s22391 = "22391";
+ public static final String s22392 = "22392";
+ public static final String s22393 = "22393";
+ public static final String s22394 = "22394";
+ public static final String s22395 = "22395";
+ public static final String s22396 = "22396";
+ public static final String s22397 = "22397";
+ public static final String s22398 = "22398";
+ public static final String s22399 = "22399";
+ public static final String s22400 = "22400";
+ public static final String s22401 = "22401";
+ public static final String s22402 = "22402";
+ public static final String s22403 = "22403";
+ public static final String s22404 = "22404";
+ public static final String s22405 = "22405";
+ public static final String s22406 = "22406";
+ public static final String s22407 = "22407";
+ public static final String s22408 = "22408";
+ public static final String s22409 = "22409";
+ public static final String s22410 = "22410";
+ public static final String s22411 = "22411";
+ public static final String s22412 = "22412";
+ public static final String s22413 = "22413";
+ public static final String s22414 = "22414";
+ public static final String s22415 = "22415";
+ public static final String s22416 = "22416";
+ public static final String s22417 = "22417";
+ public static final String s22418 = "22418";
+ public static final String s22419 = "22419";
+ public static final String s22420 = "22420";
+ public static final String s22421 = "22421";
+ public static final String s22422 = "22422";
+ public static final String s22423 = "22423";
+ public static final String s22424 = "22424";
+ public static final String s22425 = "22425";
+ public static final String s22426 = "22426";
+ public static final String s22427 = "22427";
+ public static final String s22428 = "22428";
+ public static final String s22429 = "22429";
+ public static final String s22430 = "22430";
+ public static final String s22431 = "22431";
+ public static final String s22432 = "22432";
+ public static final String s22433 = "22433";
+ public static final String s22434 = "22434";
+ public static final String s22435 = "22435";
+ public static final String s22436 = "22436";
+ public static final String s22437 = "22437";
+ public static final String s22438 = "22438";
+ public static final String s22439 = "22439";
+ public static final String s22440 = "22440";
+ public static final String s22441 = "22441";
+ public static final String s22442 = "22442";
+ public static final String s22443 = "22443";
+ public static final String s22444 = "22444";
+ public static final String s22445 = "22445";
+ public static final String s22446 = "22446";
+ public static final String s22447 = "22447";
+ public static final String s22448 = "22448";
+ public static final String s22449 = "22449";
+ public static final String s22450 = "22450";
+ public static final String s22451 = "22451";
+ public static final String s22452 = "22452";
+ public static final String s22453 = "22453";
+ public static final String s22454 = "22454";
+ public static final String s22455 = "22455";
+ public static final String s22456 = "22456";
+ public static final String s22457 = "22457";
+ public static final String s22458 = "22458";
+ public static final String s22459 = "22459";
+ public static final String s22460 = "22460";
+ public static final String s22461 = "22461";
+ public static final String s22462 = "22462";
+ public static final String s22463 = "22463";
+ public static final String s22464 = "22464";
+ public static final String s22465 = "22465";
+ public static final String s22466 = "22466";
+ public static final String s22467 = "22467";
+ public static final String s22468 = "22468";
+ public static final String s22469 = "22469";
+ public static final String s22470 = "22470";
+ public static final String s22471 = "22471";
+ public static final String s22472 = "22472";
+ public static final String s22473 = "22473";
+ public static final String s22474 = "22474";
+ public static final String s22475 = "22475";
+ public static final String s22476 = "22476";
+ public static final String s22477 = "22477";
+ public static final String s22478 = "22478";
+ public static final String s22479 = "22479";
+ public static final String s22480 = "22480";
+ public static final String s22481 = "22481";
+ public static final String s22482 = "22482";
+ public static final String s22483 = "22483";
+ public static final String s22484 = "22484";
+ public static final String s22485 = "22485";
+ public static final String s22486 = "22486";
+ public static final String s22487 = "22487";
+ public static final String s22488 = "22488";
+ public static final String s22489 = "22489";
+ public static final String s22490 = "22490";
+ public static final String s22491 = "22491";
+ public static final String s22492 = "22492";
+ public static final String s22493 = "22493";
+ public static final String s22494 = "22494";
+ public static final String s22495 = "22495";
+ public static final String s22496 = "22496";
+ public static final String s22497 = "22497";
+ public static final String s22498 = "22498";
+ public static final String s22499 = "22499";
+ public static final String s22500 = "22500";
+ public static final String s22501 = "22501";
+ public static final String s22502 = "22502";
+ public static final String s22503 = "22503";
+ public static final String s22504 = "22504";
+ public static final String s22505 = "22505";
+ public static final String s22506 = "22506";
+ public static final String s22507 = "22507";
+ public static final String s22508 = "22508";
+ public static final String s22509 = "22509";
+ public static final String s22510 = "22510";
+ public static final String s22511 = "22511";
+ public static final String s22512 = "22512";
+ public static final String s22513 = "22513";
+ public static final String s22514 = "22514";
+ public static final String s22515 = "22515";
+ public static final String s22516 = "22516";
+ public static final String s22517 = "22517";
+ public static final String s22518 = "22518";
+ public static final String s22519 = "22519";
+ public static final String s22520 = "22520";
+ public static final String s22521 = "22521";
+ public static final String s22522 = "22522";
+ public static final String s22523 = "22523";
+ public static final String s22524 = "22524";
+ public static final String s22525 = "22525";
+ public static final String s22526 = "22526";
+ public static final String s22527 = "22527";
+ public static final String s22528 = "22528";
+ public static final String s22529 = "22529";
+ public static final String s22530 = "22530";
+ public static final String s22531 = "22531";
+ public static final String s22532 = "22532";
+ public static final String s22533 = "22533";
+ public static final String s22534 = "22534";
+ public static final String s22535 = "22535";
+ public static final String s22536 = "22536";
+ public static final String s22537 = "22537";
+ public static final String s22538 = "22538";
+ public static final String s22539 = "22539";
+ public static final String s22540 = "22540";
+ public static final String s22541 = "22541";
+ public static final String s22542 = "22542";
+ public static final String s22543 = "22543";
+ public static final String s22544 = "22544";
+ public static final String s22545 = "22545";
+ public static final String s22546 = "22546";
+ public static final String s22547 = "22547";
+ public static final String s22548 = "22548";
+ public static final String s22549 = "22549";
+ public static final String s22550 = "22550";
+ public static final String s22551 = "22551";
+ public static final String s22552 = "22552";
+ public static final String s22553 = "22553";
+ public static final String s22554 = "22554";
+ public static final String s22555 = "22555";
+ public static final String s22556 = "22556";
+ public static final String s22557 = "22557";
+ public static final String s22558 = "22558";
+ public static final String s22559 = "22559";
+ public static final String s22560 = "22560";
+ public static final String s22561 = "22561";
+ public static final String s22562 = "22562";
+ public static final String s22563 = "22563";
+ public static final String s22564 = "22564";
+ public static final String s22565 = "22565";
+ public static final String s22566 = "22566";
+ public static final String s22567 = "22567";
+ public static final String s22568 = "22568";
+ public static final String s22569 = "22569";
+ public static final String s22570 = "22570";
+ public static final String s22571 = "22571";
+ public static final String s22572 = "22572";
+ public static final String s22573 = "22573";
+ public static final String s22574 = "22574";
+ public static final String s22575 = "22575";
+ public static final String s22576 = "22576";
+ public static final String s22577 = "22577";
+ public static final String s22578 = "22578";
+ public static final String s22579 = "22579";
+ public static final String s22580 = "22580";
+ public static final String s22581 = "22581";
+ public static final String s22582 = "22582";
+ public static final String s22583 = "22583";
+ public static final String s22584 = "22584";
+ public static final String s22585 = "22585";
+ public static final String s22586 = "22586";
+ public static final String s22587 = "22587";
+ public static final String s22588 = "22588";
+ public static final String s22589 = "22589";
+ public static final String s22590 = "22590";
+ public static final String s22591 = "22591";
+ public static final String s22592 = "22592";
+ public static final String s22593 = "22593";
+ public static final String s22594 = "22594";
+ public static final String s22595 = "22595";
+ public static final String s22596 = "22596";
+ public static final String s22597 = "22597";
+ public static final String s22598 = "22598";
+ public static final String s22599 = "22599";
+ public static final String s22600 = "22600";
+ public static final String s22601 = "22601";
+ public static final String s22602 = "22602";
+ public static final String s22603 = "22603";
+ public static final String s22604 = "22604";
+ public static final String s22605 = "22605";
+ public static final String s22606 = "22606";
+ public static final String s22607 = "22607";
+ public static final String s22608 = "22608";
+ public static final String s22609 = "22609";
+ public static final String s22610 = "22610";
+ public static final String s22611 = "22611";
+ public static final String s22612 = "22612";
+ public static final String s22613 = "22613";
+ public static final String s22614 = "22614";
+ public static final String s22615 = "22615";
+ public static final String s22616 = "22616";
+ public static final String s22617 = "22617";
+ public static final String s22618 = "22618";
+ public static final String s22619 = "22619";
+ public static final String s22620 = "22620";
+ public static final String s22621 = "22621";
+ public static final String s22622 = "22622";
+ public static final String s22623 = "22623";
+ public static final String s22624 = "22624";
+ public static final String s22625 = "22625";
+ public static final String s22626 = "22626";
+ public static final String s22627 = "22627";
+ public static final String s22628 = "22628";
+ public static final String s22629 = "22629";
+ public static final String s22630 = "22630";
+ public static final String s22631 = "22631";
+ public static final String s22632 = "22632";
+ public static final String s22633 = "22633";
+ public static final String s22634 = "22634";
+ public static final String s22635 = "22635";
+ public static final String s22636 = "22636";
+ public static final String s22637 = "22637";
+ public static final String s22638 = "22638";
+ public static final String s22639 = "22639";
+ public static final String s22640 = "22640";
+ public static final String s22641 = "22641";
+ public static final String s22642 = "22642";
+ public static final String s22643 = "22643";
+ public static final String s22644 = "22644";
+ public static final String s22645 = "22645";
+ public static final String s22646 = "22646";
+ public static final String s22647 = "22647";
+ public static final String s22648 = "22648";
+ public static final String s22649 = "22649";
+ public static final String s22650 = "22650";
+ public static final String s22651 = "22651";
+ public static final String s22652 = "22652";
+ public static final String s22653 = "22653";
+ public static final String s22654 = "22654";
+ public static final String s22655 = "22655";
+ public static final String s22656 = "22656";
+ public static final String s22657 = "22657";
+ public static final String s22658 = "22658";
+ public static final String s22659 = "22659";
+ public static final String s22660 = "22660";
+ public static final String s22661 = "22661";
+ public static final String s22662 = "22662";
+ public static final String s22663 = "22663";
+ public static final String s22664 = "22664";
+ public static final String s22665 = "22665";
+ public static final String s22666 = "22666";
+ public static final String s22667 = "22667";
+ public static final String s22668 = "22668";
+ public static final String s22669 = "22669";
+ public static final String s22670 = "22670";
+ public static final String s22671 = "22671";
+ public static final String s22672 = "22672";
+ public static final String s22673 = "22673";
+ public static final String s22674 = "22674";
+ public static final String s22675 = "22675";
+ public static final String s22676 = "22676";
+ public static final String s22677 = "22677";
+ public static final String s22678 = "22678";
+ public static final String s22679 = "22679";
+ public static final String s22680 = "22680";
+ public static final String s22681 = "22681";
+ public static final String s22682 = "22682";
+ public static final String s22683 = "22683";
+ public static final String s22684 = "22684";
+ public static final String s22685 = "22685";
+ public static final String s22686 = "22686";
+ public static final String s22687 = "22687";
+ public static final String s22688 = "22688";
+ public static final String s22689 = "22689";
+ public static final String s22690 = "22690";
+ public static final String s22691 = "22691";
+ public static final String s22692 = "22692";
+ public static final String s22693 = "22693";
+ public static final String s22694 = "22694";
+ public static final String s22695 = "22695";
+ public static final String s22696 = "22696";
+ public static final String s22697 = "22697";
+ public static final String s22698 = "22698";
+ public static final String s22699 = "22699";
+ public static final String s22700 = "22700";
+ public static final String s22701 = "22701";
+ public static final String s22702 = "22702";
+ public static final String s22703 = "22703";
+ public static final String s22704 = "22704";
+ public static final String s22705 = "22705";
+ public static final String s22706 = "22706";
+ public static final String s22707 = "22707";
+ public static final String s22708 = "22708";
+ public static final String s22709 = "22709";
+ public static final String s22710 = "22710";
+ public static final String s22711 = "22711";
+ public static final String s22712 = "22712";
+ public static final String s22713 = "22713";
+ public static final String s22714 = "22714";
+ public static final String s22715 = "22715";
+ public static final String s22716 = "22716";
+ public static final String s22717 = "22717";
+ public static final String s22718 = "22718";
+ public static final String s22719 = "22719";
+ public static final String s22720 = "22720";
+ public static final String s22721 = "22721";
+ public static final String s22722 = "22722";
+ public static final String s22723 = "22723";
+ public static final String s22724 = "22724";
+ public static final String s22725 = "22725";
+ public static final String s22726 = "22726";
+ public static final String s22727 = "22727";
+ public static final String s22728 = "22728";
+ public static final String s22729 = "22729";
+ public static final String s22730 = "22730";
+ public static final String s22731 = "22731";
+ public static final String s22732 = "22732";
+ public static final String s22733 = "22733";
+ public static final String s22734 = "22734";
+ public static final String s22735 = "22735";
+ public static final String s22736 = "22736";
+ public static final String s22737 = "22737";
+ public static final String s22738 = "22738";
+ public static final String s22739 = "22739";
+ public static final String s22740 = "22740";
+ public static final String s22741 = "22741";
+ public static final String s22742 = "22742";
+ public static final String s22743 = "22743";
+ public static final String s22744 = "22744";
+ public static final String s22745 = "22745";
+ public static final String s22746 = "22746";
+ public static final String s22747 = "22747";
+ public static final String s22748 = "22748";
+ public static final String s22749 = "22749";
+ public static final String s22750 = "22750";
+ public static final String s22751 = "22751";
+ public static final String s22752 = "22752";
+ public static final String s22753 = "22753";
+ public static final String s22754 = "22754";
+ public static final String s22755 = "22755";
+ public static final String s22756 = "22756";
+ public static final String s22757 = "22757";
+ public static final String s22758 = "22758";
+ public static final String s22759 = "22759";
+ public static final String s22760 = "22760";
+ public static final String s22761 = "22761";
+ public static final String s22762 = "22762";
+ public static final String s22763 = "22763";
+ public static final String s22764 = "22764";
+ public static final String s22765 = "22765";
+ public static final String s22766 = "22766";
+ public static final String s22767 = "22767";
+ public static final String s22768 = "22768";
+ public static final String s22769 = "22769";
+ public static final String s22770 = "22770";
+ public static final String s22771 = "22771";
+ public static final String s22772 = "22772";
+ public static final String s22773 = "22773";
+ public static final String s22774 = "22774";
+ public static final String s22775 = "22775";
+ public static final String s22776 = "22776";
+ public static final String s22777 = "22777";
+ public static final String s22778 = "22778";
+ public static final String s22779 = "22779";
+ public static final String s22780 = "22780";
+ public static final String s22781 = "22781";
+ public static final String s22782 = "22782";
+ public static final String s22783 = "22783";
+ public static final String s22784 = "22784";
+ public static final String s22785 = "22785";
+ public static final String s22786 = "22786";
+ public static final String s22787 = "22787";
+ public static final String s22788 = "22788";
+ public static final String s22789 = "22789";
+ public static final String s22790 = "22790";
+ public static final String s22791 = "22791";
+ public static final String s22792 = "22792";
+ public static final String s22793 = "22793";
+ public static final String s22794 = "22794";
+ public static final String s22795 = "22795";
+ public static final String s22796 = "22796";
+ public static final String s22797 = "22797";
+ public static final String s22798 = "22798";
+ public static final String s22799 = "22799";
+ public static final String s22800 = "22800";
+ public static final String s22801 = "22801";
+ public static final String s22802 = "22802";
+ public static final String s22803 = "22803";
+ public static final String s22804 = "22804";
+ public static final String s22805 = "22805";
+ public static final String s22806 = "22806";
+ public static final String s22807 = "22807";
+ public static final String s22808 = "22808";
+ public static final String s22809 = "22809";
+ public static final String s22810 = "22810";
+ public static final String s22811 = "22811";
+ public static final String s22812 = "22812";
+ public static final String s22813 = "22813";
+ public static final String s22814 = "22814";
+ public static final String s22815 = "22815";
+ public static final String s22816 = "22816";
+ public static final String s22817 = "22817";
+ public static final String s22818 = "22818";
+ public static final String s22819 = "22819";
+ public static final String s22820 = "22820";
+ public static final String s22821 = "22821";
+ public static final String s22822 = "22822";
+ public static final String s22823 = "22823";
+ public static final String s22824 = "22824";
+ public static final String s22825 = "22825";
+ public static final String s22826 = "22826";
+ public static final String s22827 = "22827";
+ public static final String s22828 = "22828";
+ public static final String s22829 = "22829";
+ public static final String s22830 = "22830";
+ public static final String s22831 = "22831";
+ public static final String s22832 = "22832";
+ public static final String s22833 = "22833";
+ public static final String s22834 = "22834";
+ public static final String s22835 = "22835";
+ public static final String s22836 = "22836";
+ public static final String s22837 = "22837";
+ public static final String s22838 = "22838";
+ public static final String s22839 = "22839";
+ public static final String s22840 = "22840";
+ public static final String s22841 = "22841";
+ public static final String s22842 = "22842";
+ public static final String s22843 = "22843";
+ public static final String s22844 = "22844";
+ public static final String s22845 = "22845";
+ public static final String s22846 = "22846";
+ public static final String s22847 = "22847";
+ public static final String s22848 = "22848";
+ public static final String s22849 = "22849";
+ public static final String s22850 = "22850";
+ public static final String s22851 = "22851";
+ public static final String s22852 = "22852";
+ public static final String s22853 = "22853";
+ public static final String s22854 = "22854";
+ public static final String s22855 = "22855";
+ public static final String s22856 = "22856";
+ public static final String s22857 = "22857";
+ public static final String s22858 = "22858";
+ public static final String s22859 = "22859";
+ public static final String s22860 = "22860";
+ public static final String s22861 = "22861";
+ public static final String s22862 = "22862";
+ public static final String s22863 = "22863";
+ public static final String s22864 = "22864";
+ public static final String s22865 = "22865";
+ public static final String s22866 = "22866";
+ public static final String s22867 = "22867";
+ public static final String s22868 = "22868";
+ public static final String s22869 = "22869";
+ public static final String s22870 = "22870";
+ public static final String s22871 = "22871";
+ public static final String s22872 = "22872";
+ public static final String s22873 = "22873";
+ public static final String s22874 = "22874";
+ public static final String s22875 = "22875";
+ public static final String s22876 = "22876";
+ public static final String s22877 = "22877";
+ public static final String s22878 = "22878";
+ public static final String s22879 = "22879";
+ public static final String s22880 = "22880";
+ public static final String s22881 = "22881";
+ public static final String s22882 = "22882";
+ public static final String s22883 = "22883";
+ public static final String s22884 = "22884";
+ public static final String s22885 = "22885";
+ public static final String s22886 = "22886";
+ public static final String s22887 = "22887";
+ public static final String s22888 = "22888";
+ public static final String s22889 = "22889";
+ public static final String s22890 = "22890";
+ public static final String s22891 = "22891";
+ public static final String s22892 = "22892";
+ public static final String s22893 = "22893";
+ public static final String s22894 = "22894";
+ public static final String s22895 = "22895";
+ public static final String s22896 = "22896";
+ public static final String s22897 = "22897";
+ public static final String s22898 = "22898";
+ public static final String s22899 = "22899";
+ public static final String s22900 = "22900";
+ public static final String s22901 = "22901";
+ public static final String s22902 = "22902";
+ public static final String s22903 = "22903";
+ public static final String s22904 = "22904";
+ public static final String s22905 = "22905";
+ public static final String s22906 = "22906";
+ public static final String s22907 = "22907";
+ public static final String s22908 = "22908";
+ public static final String s22909 = "22909";
+ public static final String s22910 = "22910";
+ public static final String s22911 = "22911";
+ public static final String s22912 = "22912";
+ public static final String s22913 = "22913";
+ public static final String s22914 = "22914";
+ public static final String s22915 = "22915";
+ public static final String s22916 = "22916";
+ public static final String s22917 = "22917";
+ public static final String s22918 = "22918";
+ public static final String s22919 = "22919";
+ public static final String s22920 = "22920";
+ public static final String s22921 = "22921";
+ public static final String s22922 = "22922";
+ public static final String s22923 = "22923";
+ public static final String s22924 = "22924";
+ public static final String s22925 = "22925";
+ public static final String s22926 = "22926";
+ public static final String s22927 = "22927";
+ public static final String s22928 = "22928";
+ public static final String s22929 = "22929";
+ public static final String s22930 = "22930";
+ public static final String s22931 = "22931";
+ public static final String s22932 = "22932";
+ public static final String s22933 = "22933";
+ public static final String s22934 = "22934";
+ public static final String s22935 = "22935";
+ public static final String s22936 = "22936";
+ public static final String s22937 = "22937";
+ public static final String s22938 = "22938";
+ public static final String s22939 = "22939";
+ public static final String s22940 = "22940";
+ public static final String s22941 = "22941";
+ public static final String s22942 = "22942";
+ public static final String s22943 = "22943";
+ public static final String s22944 = "22944";
+ public static final String s22945 = "22945";
+ public static final String s22946 = "22946";
+ public static final String s22947 = "22947";
+ public static final String s22948 = "22948";
+ public static final String s22949 = "22949";
+ public static final String s22950 = "22950";
+ public static final String s22951 = "22951";
+ public static final String s22952 = "22952";
+ public static final String s22953 = "22953";
+ public static final String s22954 = "22954";
+ public static final String s22955 = "22955";
+ public static final String s22956 = "22956";
+ public static final String s22957 = "22957";
+ public static final String s22958 = "22958";
+ public static final String s22959 = "22959";
+ public static final String s22960 = "22960";
+ public static final String s22961 = "22961";
+ public static final String s22962 = "22962";
+ public static final String s22963 = "22963";
+ public static final String s22964 = "22964";
+ public static final String s22965 = "22965";
+ public static final String s22966 = "22966";
+ public static final String s22967 = "22967";
+ public static final String s22968 = "22968";
+ public static final String s22969 = "22969";
+ public static final String s22970 = "22970";
+ public static final String s22971 = "22971";
+ public static final String s22972 = "22972";
+ public static final String s22973 = "22973";
+ public static final String s22974 = "22974";
+ public static final String s22975 = "22975";
+ public static final String s22976 = "22976";
+ public static final String s22977 = "22977";
+ public static final String s22978 = "22978";
+ public static final String s22979 = "22979";
+ public static final String s22980 = "22980";
+ public static final String s22981 = "22981";
+ public static final String s22982 = "22982";
+ public static final String s22983 = "22983";
+ public static final String s22984 = "22984";
+ public static final String s22985 = "22985";
+ public static final String s22986 = "22986";
+ public static final String s22987 = "22987";
+ public static final String s22988 = "22988";
+ public static final String s22989 = "22989";
+ public static final String s22990 = "22990";
+ public static final String s22991 = "22991";
+ public static final String s22992 = "22992";
+ public static final String s22993 = "22993";
+ public static final String s22994 = "22994";
+ public static final String s22995 = "22995";
+ public static final String s22996 = "22996";
+ public static final String s22997 = "22997";
+ public static final String s22998 = "22998";
+ public static final String s22999 = "22999";
+ public static final String s23000 = "23000";
+ public static final String s23001 = "23001";
+ public static final String s23002 = "23002";
+ public static final String s23003 = "23003";
+ public static final String s23004 = "23004";
+ public static final String s23005 = "23005";
+ public static final String s23006 = "23006";
+ public static final String s23007 = "23007";
+ public static final String s23008 = "23008";
+ public static final String s23009 = "23009";
+ public static final String s23010 = "23010";
+ public static final String s23011 = "23011";
+ public static final String s23012 = "23012";
+ public static final String s23013 = "23013";
+ public static final String s23014 = "23014";
+ public static final String s23015 = "23015";
+ public static final String s23016 = "23016";
+ public static final String s23017 = "23017";
+ public static final String s23018 = "23018";
+ public static final String s23019 = "23019";
+ public static final String s23020 = "23020";
+ public static final String s23021 = "23021";
+ public static final String s23022 = "23022";
+ public static final String s23023 = "23023";
+ public static final String s23024 = "23024";
+ public static final String s23025 = "23025";
+ public static final String s23026 = "23026";
+ public static final String s23027 = "23027";
+ public static final String s23028 = "23028";
+ public static final String s23029 = "23029";
+ public static final String s23030 = "23030";
+ public static final String s23031 = "23031";
+ public static final String s23032 = "23032";
+ public static final String s23033 = "23033";
+ public static final String s23034 = "23034";
+ public static final String s23035 = "23035";
+ public static final String s23036 = "23036";
+ public static final String s23037 = "23037";
+ public static final String s23038 = "23038";
+ public static final String s23039 = "23039";
+ public static final String s23040 = "23040";
+ public static final String s23041 = "23041";
+ public static final String s23042 = "23042";
+ public static final String s23043 = "23043";
+ public static final String s23044 = "23044";
+ public static final String s23045 = "23045";
+ public static final String s23046 = "23046";
+ public static final String s23047 = "23047";
+ public static final String s23048 = "23048";
+ public static final String s23049 = "23049";
+ public static final String s23050 = "23050";
+ public static final String s23051 = "23051";
+ public static final String s23052 = "23052";
+ public static final String s23053 = "23053";
+ public static final String s23054 = "23054";
+ public static final String s23055 = "23055";
+ public static final String s23056 = "23056";
+ public static final String s23057 = "23057";
+ public static final String s23058 = "23058";
+ public static final String s23059 = "23059";
+ public static final String s23060 = "23060";
+ public static final String s23061 = "23061";
+ public static final String s23062 = "23062";
+ public static final String s23063 = "23063";
+ public static final String s23064 = "23064";
+ public static final String s23065 = "23065";
+ public static final String s23066 = "23066";
+ public static final String s23067 = "23067";
+ public static final String s23068 = "23068";
+ public static final String s23069 = "23069";
+ public static final String s23070 = "23070";
+ public static final String s23071 = "23071";
+ public static final String s23072 = "23072";
+ public static final String s23073 = "23073";
+ public static final String s23074 = "23074";
+ public static final String s23075 = "23075";
+ public static final String s23076 = "23076";
+ public static final String s23077 = "23077";
+ public static final String s23078 = "23078";
+ public static final String s23079 = "23079";
+ public static final String s23080 = "23080";
+ public static final String s23081 = "23081";
+ public static final String s23082 = "23082";
+ public static final String s23083 = "23083";
+ public static final String s23084 = "23084";
+ public static final String s23085 = "23085";
+ public static final String s23086 = "23086";
+ public static final String s23087 = "23087";
+ public static final String s23088 = "23088";
+ public static final String s23089 = "23089";
+ public static final String s23090 = "23090";
+ public static final String s23091 = "23091";
+ public static final String s23092 = "23092";
+ public static final String s23093 = "23093";
+ public static final String s23094 = "23094";
+ public static final String s23095 = "23095";
+ public static final String s23096 = "23096";
+ public static final String s23097 = "23097";
+ public static final String s23098 = "23098";
+ public static final String s23099 = "23099";
+ public static final String s23100 = "23100";
+ public static final String s23101 = "23101";
+ public static final String s23102 = "23102";
+ public static final String s23103 = "23103";
+ public static final String s23104 = "23104";
+ public static final String s23105 = "23105";
+ public static final String s23106 = "23106";
+ public static final String s23107 = "23107";
+ public static final String s23108 = "23108";
+ public static final String s23109 = "23109";
+ public static final String s23110 = "23110";
+ public static final String s23111 = "23111";
+ public static final String s23112 = "23112";
+ public static final String s23113 = "23113";
+ public static final String s23114 = "23114";
+ public static final String s23115 = "23115";
+ public static final String s23116 = "23116";
+ public static final String s23117 = "23117";
+ public static final String s23118 = "23118";
+ public static final String s23119 = "23119";
+ public static final String s23120 = "23120";
+ public static final String s23121 = "23121";
+ public static final String s23122 = "23122";
+ public static final String s23123 = "23123";
+ public static final String s23124 = "23124";
+ public static final String s23125 = "23125";
+ public static final String s23126 = "23126";
+ public static final String s23127 = "23127";
+ public static final String s23128 = "23128";
+ public static final String s23129 = "23129";
+ public static final String s23130 = "23130";
+ public static final String s23131 = "23131";
+ public static final String s23132 = "23132";
+ public static final String s23133 = "23133";
+ public static final String s23134 = "23134";
+ public static final String s23135 = "23135";
+ public static final String s23136 = "23136";
+ public static final String s23137 = "23137";
+ public static final String s23138 = "23138";
+ public static final String s23139 = "23139";
+ public static final String s23140 = "23140";
+ public static final String s23141 = "23141";
+ public static final String s23142 = "23142";
+ public static final String s23143 = "23143";
+ public static final String s23144 = "23144";
+ public static final String s23145 = "23145";
+ public static final String s23146 = "23146";
+ public static final String s23147 = "23147";
+ public static final String s23148 = "23148";
+ public static final String s23149 = "23149";
+ public static final String s23150 = "23150";
+ public static final String s23151 = "23151";
+ public static final String s23152 = "23152";
+ public static final String s23153 = "23153";
+ public static final String s23154 = "23154";
+ public static final String s23155 = "23155";
+ public static final String s23156 = "23156";
+ public static final String s23157 = "23157";
+ public static final String s23158 = "23158";
+ public static final String s23159 = "23159";
+ public static final String s23160 = "23160";
+ public static final String s23161 = "23161";
+ public static final String s23162 = "23162";
+ public static final String s23163 = "23163";
+ public static final String s23164 = "23164";
+ public static final String s23165 = "23165";
+ public static final String s23166 = "23166";
+ public static final String s23167 = "23167";
+ public static final String s23168 = "23168";
+ public static final String s23169 = "23169";
+ public static final String s23170 = "23170";
+ public static final String s23171 = "23171";
+ public static final String s23172 = "23172";
+ public static final String s23173 = "23173";
+ public static final String s23174 = "23174";
+ public static final String s23175 = "23175";
+ public static final String s23176 = "23176";
+ public static final String s23177 = "23177";
+ public static final String s23178 = "23178";
+ public static final String s23179 = "23179";
+ public static final String s23180 = "23180";
+ public static final String s23181 = "23181";
+ public static final String s23182 = "23182";
+ public static final String s23183 = "23183";
+ public static final String s23184 = "23184";
+ public static final String s23185 = "23185";
+ public static final String s23186 = "23186";
+ public static final String s23187 = "23187";
+ public static final String s23188 = "23188";
+ public static final String s23189 = "23189";
+ public static final String s23190 = "23190";
+ public static final String s23191 = "23191";
+ public static final String s23192 = "23192";
+ public static final String s23193 = "23193";
+ public static final String s23194 = "23194";
+ public static final String s23195 = "23195";
+ public static final String s23196 = "23196";
+ public static final String s23197 = "23197";
+ public static final String s23198 = "23198";
+ public static final String s23199 = "23199";
+ public static final String s23200 = "23200";
+ public static final String s23201 = "23201";
+ public static final String s23202 = "23202";
+ public static final String s23203 = "23203";
+ public static final String s23204 = "23204";
+ public static final String s23205 = "23205";
+ public static final String s23206 = "23206";
+ public static final String s23207 = "23207";
+ public static final String s23208 = "23208";
+ public static final String s23209 = "23209";
+ public static final String s23210 = "23210";
+ public static final String s23211 = "23211";
+ public static final String s23212 = "23212";
+ public static final String s23213 = "23213";
+ public static final String s23214 = "23214";
+ public static final String s23215 = "23215";
+ public static final String s23216 = "23216";
+ public static final String s23217 = "23217";
+ public static final String s23218 = "23218";
+ public static final String s23219 = "23219";
+ public static final String s23220 = "23220";
+ public static final String s23221 = "23221";
+ public static final String s23222 = "23222";
+ public static final String s23223 = "23223";
+ public static final String s23224 = "23224";
+ public static final String s23225 = "23225";
+ public static final String s23226 = "23226";
+ public static final String s23227 = "23227";
+ public static final String s23228 = "23228";
+ public static final String s23229 = "23229";
+ public static final String s23230 = "23230";
+ public static final String s23231 = "23231";
+ public static final String s23232 = "23232";
+ public static final String s23233 = "23233";
+ public static final String s23234 = "23234";
+ public static final String s23235 = "23235";
+ public static final String s23236 = "23236";
+ public static final String s23237 = "23237";
+ public static final String s23238 = "23238";
+ public static final String s23239 = "23239";
+ public static final String s23240 = "23240";
+ public static final String s23241 = "23241";
+ public static final String s23242 = "23242";
+ public static final String s23243 = "23243";
+ public static final String s23244 = "23244";
+ public static final String s23245 = "23245";
+ public static final String s23246 = "23246";
+ public static final String s23247 = "23247";
+ public static final String s23248 = "23248";
+ public static final String s23249 = "23249";
+ public static final String s23250 = "23250";
+ public static final String s23251 = "23251";
+ public static final String s23252 = "23252";
+ public static final String s23253 = "23253";
+ public static final String s23254 = "23254";
+ public static final String s23255 = "23255";
+ public static final String s23256 = "23256";
+ public static final String s23257 = "23257";
+ public static final String s23258 = "23258";
+ public static final String s23259 = "23259";
+ public static final String s23260 = "23260";
+ public static final String s23261 = "23261";
+ public static final String s23262 = "23262";
+ public static final String s23263 = "23263";
+ public static final String s23264 = "23264";
+ public static final String s23265 = "23265";
+ public static final String s23266 = "23266";
+ public static final String s23267 = "23267";
+ public static final String s23268 = "23268";
+ public static final String s23269 = "23269";
+ public static final String s23270 = "23270";
+ public static final String s23271 = "23271";
+ public static final String s23272 = "23272";
+ public static final String s23273 = "23273";
+ public static final String s23274 = "23274";
+ public static final String s23275 = "23275";
+ public static final String s23276 = "23276";
+ public static final String s23277 = "23277";
+ public static final String s23278 = "23278";
+ public static final String s23279 = "23279";
+ public static final String s23280 = "23280";
+ public static final String s23281 = "23281";
+ public static final String s23282 = "23282";
+ public static final String s23283 = "23283";
+ public static final String s23284 = "23284";
+ public static final String s23285 = "23285";
+ public static final String s23286 = "23286";
+ public static final String s23287 = "23287";
+ public static final String s23288 = "23288";
+ public static final String s23289 = "23289";
+ public static final String s23290 = "23290";
+ public static final String s23291 = "23291";
+ public static final String s23292 = "23292";
+ public static final String s23293 = "23293";
+ public static final String s23294 = "23294";
+ public static final String s23295 = "23295";
+ public static final String s23296 = "23296";
+ public static final String s23297 = "23297";
+ public static final String s23298 = "23298";
+ public static final String s23299 = "23299";
+ public static final String s23300 = "23300";
+ public static final String s23301 = "23301";
+ public static final String s23302 = "23302";
+ public static final String s23303 = "23303";
+ public static final String s23304 = "23304";
+ public static final String s23305 = "23305";
+ public static final String s23306 = "23306";
+ public static final String s23307 = "23307";
+ public static final String s23308 = "23308";
+ public static final String s23309 = "23309";
+ public static final String s23310 = "23310";
+ public static final String s23311 = "23311";
+ public static final String s23312 = "23312";
+ public static final String s23313 = "23313";
+ public static final String s23314 = "23314";
+ public static final String s23315 = "23315";
+ public static final String s23316 = "23316";
+ public static final String s23317 = "23317";
+ public static final String s23318 = "23318";
+ public static final String s23319 = "23319";
+ public static final String s23320 = "23320";
+ public static final String s23321 = "23321";
+ public static final String s23322 = "23322";
+ public static final String s23323 = "23323";
+ public static final String s23324 = "23324";
+ public static final String s23325 = "23325";
+ public static final String s23326 = "23326";
+ public static final String s23327 = "23327";
+ public static final String s23328 = "23328";
+ public static final String s23329 = "23329";
+ public static final String s23330 = "23330";
+ public static final String s23331 = "23331";
+ public static final String s23332 = "23332";
+ public static final String s23333 = "23333";
+ public static final String s23334 = "23334";
+ public static final String s23335 = "23335";
+ public static final String s23336 = "23336";
+ public static final String s23337 = "23337";
+ public static final String s23338 = "23338";
+ public static final String s23339 = "23339";
+ public static final String s23340 = "23340";
+ public static final String s23341 = "23341";
+ public static final String s23342 = "23342";
+ public static final String s23343 = "23343";
+ public static final String s23344 = "23344";
+ public static final String s23345 = "23345";
+ public static final String s23346 = "23346";
+ public static final String s23347 = "23347";
+ public static final String s23348 = "23348";
+ public static final String s23349 = "23349";
+ public static final String s23350 = "23350";
+ public static final String s23351 = "23351";
+ public static final String s23352 = "23352";
+ public static final String s23353 = "23353";
+ public static final String s23354 = "23354";
+ public static final String s23355 = "23355";
+ public static final String s23356 = "23356";
+ public static final String s23357 = "23357";
+ public static final String s23358 = "23358";
+ public static final String s23359 = "23359";
+ public static final String s23360 = "23360";
+ public static final String s23361 = "23361";
+ public static final String s23362 = "23362";
+ public static final String s23363 = "23363";
+ public static final String s23364 = "23364";
+ public static final String s23365 = "23365";
+ public static final String s23366 = "23366";
+ public static final String s23367 = "23367";
+ public static final String s23368 = "23368";
+ public static final String s23369 = "23369";
+ public static final String s23370 = "23370";
+ public static final String s23371 = "23371";
+ public static final String s23372 = "23372";
+ public static final String s23373 = "23373";
+ public static final String s23374 = "23374";
+ public static final String s23375 = "23375";
+ public static final String s23376 = "23376";
+ public static final String s23377 = "23377";
+ public static final String s23378 = "23378";
+ public static final String s23379 = "23379";
+ public static final String s23380 = "23380";
+ public static final String s23381 = "23381";
+ public static final String s23382 = "23382";
+ public static final String s23383 = "23383";
+ public static final String s23384 = "23384";
+ public static final String s23385 = "23385";
+ public static final String s23386 = "23386";
+ public static final String s23387 = "23387";
+ public static final String s23388 = "23388";
+ public static final String s23389 = "23389";
+ public static final String s23390 = "23390";
+ public static final String s23391 = "23391";
+ public static final String s23392 = "23392";
+ public static final String s23393 = "23393";
+ public static final String s23394 = "23394";
+ public static final String s23395 = "23395";
+ public static final String s23396 = "23396";
+ public static final String s23397 = "23397";
+ public static final String s23398 = "23398";
+ public static final String s23399 = "23399";
+ public static final String s23400 = "23400";
+ public static final String s23401 = "23401";
+ public static final String s23402 = "23402";
+ public static final String s23403 = "23403";
+ public static final String s23404 = "23404";
+ public static final String s23405 = "23405";
+ public static final String s23406 = "23406";
+ public static final String s23407 = "23407";
+ public static final String s23408 = "23408";
+ public static final String s23409 = "23409";
+ public static final String s23410 = "23410";
+ public static final String s23411 = "23411";
+ public static final String s23412 = "23412";
+ public static final String s23413 = "23413";
+ public static final String s23414 = "23414";
+ public static final String s23415 = "23415";
+ public static final String s23416 = "23416";
+ public static final String s23417 = "23417";
+ public static final String s23418 = "23418";
+ public static final String s23419 = "23419";
+ public static final String s23420 = "23420";
+ public static final String s23421 = "23421";
+ public static final String s23422 = "23422";
+ public static final String s23423 = "23423";
+ public static final String s23424 = "23424";
+ public static final String s23425 = "23425";
+ public static final String s23426 = "23426";
+ public static final String s23427 = "23427";
+ public static final String s23428 = "23428";
+ public static final String s23429 = "23429";
+ public static final String s23430 = "23430";
+ public static final String s23431 = "23431";
+ public static final String s23432 = "23432";
+ public static final String s23433 = "23433";
+ public static final String s23434 = "23434";
+ public static final String s23435 = "23435";
+ public static final String s23436 = "23436";
+ public static final String s23437 = "23437";
+ public static final String s23438 = "23438";
+ public static final String s23439 = "23439";
+ public static final String s23440 = "23440";
+ public static final String s23441 = "23441";
+ public static final String s23442 = "23442";
+ public static final String s23443 = "23443";
+ public static final String s23444 = "23444";
+ public static final String s23445 = "23445";
+ public static final String s23446 = "23446";
+ public static final String s23447 = "23447";
+ public static final String s23448 = "23448";
+ public static final String s23449 = "23449";
+ public static final String s23450 = "23450";
+ public static final String s23451 = "23451";
+ public static final String s23452 = "23452";
+ public static final String s23453 = "23453";
+ public static final String s23454 = "23454";
+ public static final String s23455 = "23455";
+ public static final String s23456 = "23456";
+ public static final String s23457 = "23457";
+ public static final String s23458 = "23458";
+ public static final String s23459 = "23459";
+ public static final String s23460 = "23460";
+ public static final String s23461 = "23461";
+ public static final String s23462 = "23462";
+ public static final String s23463 = "23463";
+ public static final String s23464 = "23464";
+ public static final String s23465 = "23465";
+ public static final String s23466 = "23466";
+ public static final String s23467 = "23467";
+ public static final String s23468 = "23468";
+ public static final String s23469 = "23469";
+ public static final String s23470 = "23470";
+ public static final String s23471 = "23471";
+ public static final String s23472 = "23472";
+ public static final String s23473 = "23473";
+ public static final String s23474 = "23474";
+ public static final String s23475 = "23475";
+ public static final String s23476 = "23476";
+ public static final String s23477 = "23477";
+ public static final String s23478 = "23478";
+ public static final String s23479 = "23479";
+ public static final String s23480 = "23480";
+ public static final String s23481 = "23481";
+ public static final String s23482 = "23482";
+ public static final String s23483 = "23483";
+ public static final String s23484 = "23484";
+ public static final String s23485 = "23485";
+ public static final String s23486 = "23486";
+ public static final String s23487 = "23487";
+ public static final String s23488 = "23488";
+ public static final String s23489 = "23489";
+ public static final String s23490 = "23490";
+ public static final String s23491 = "23491";
+ public static final String s23492 = "23492";
+ public static final String s23493 = "23493";
+ public static final String s23494 = "23494";
+ public static final String s23495 = "23495";
+ public static final String s23496 = "23496";
+ public static final String s23497 = "23497";
+ public static final String s23498 = "23498";
+ public static final String s23499 = "23499";
+ public static final String s23500 = "23500";
+ public static final String s23501 = "23501";
+ public static final String s23502 = "23502";
+ public static final String s23503 = "23503";
+ public static final String s23504 = "23504";
+ public static final String s23505 = "23505";
+ public static final String s23506 = "23506";
+ public static final String s23507 = "23507";
+ public static final String s23508 = "23508";
+ public static final String s23509 = "23509";
+ public static final String s23510 = "23510";
+ public static final String s23511 = "23511";
+ public static final String s23512 = "23512";
+ public static final String s23513 = "23513";
+ public static final String s23514 = "23514";
+ public static final String s23515 = "23515";
+ public static final String s23516 = "23516";
+ public static final String s23517 = "23517";
+ public static final String s23518 = "23518";
+ public static final String s23519 = "23519";
+ public static final String s23520 = "23520";
+ public static final String s23521 = "23521";
+ public static final String s23522 = "23522";
+ public static final String s23523 = "23523";
+ public static final String s23524 = "23524";
+ public static final String s23525 = "23525";
+ public static final String s23526 = "23526";
+ public static final String s23527 = "23527";
+ public static final String s23528 = "23528";
+ public static final String s23529 = "23529";
+ public static final String s23530 = "23530";
+ public static final String s23531 = "23531";
+ public static final String s23532 = "23532";
+ public static final String s23533 = "23533";
+ public static final String s23534 = "23534";
+ public static final String s23535 = "23535";
+ public static final String s23536 = "23536";
+ public static final String s23537 = "23537";
+ public static final String s23538 = "23538";
+ public static final String s23539 = "23539";
+ public static final String s23540 = "23540";
+ public static final String s23541 = "23541";
+ public static final String s23542 = "23542";
+ public static final String s23543 = "23543";
+ public static final String s23544 = "23544";
+ public static final String s23545 = "23545";
+ public static final String s23546 = "23546";
+ public static final String s23547 = "23547";
+ public static final String s23548 = "23548";
+ public static final String s23549 = "23549";
+ public static final String s23550 = "23550";
+ public static final String s23551 = "23551";
+ public static final String s23552 = "23552";
+ public static final String s23553 = "23553";
+ public static final String s23554 = "23554";
+ public static final String s23555 = "23555";
+ public static final String s23556 = "23556";
+ public static final String s23557 = "23557";
+ public static final String s23558 = "23558";
+ public static final String s23559 = "23559";
+ public static final String s23560 = "23560";
+ public static final String s23561 = "23561";
+ public static final String s23562 = "23562";
+ public static final String s23563 = "23563";
+ public static final String s23564 = "23564";
+ public static final String s23565 = "23565";
+ public static final String s23566 = "23566";
+ public static final String s23567 = "23567";
+ public static final String s23568 = "23568";
+ public static final String s23569 = "23569";
+ public static final String s23570 = "23570";
+ public static final String s23571 = "23571";
+ public static final String s23572 = "23572";
+ public static final String s23573 = "23573";
+ public static final String s23574 = "23574";
+ public static final String s23575 = "23575";
+ public static final String s23576 = "23576";
+ public static final String s23577 = "23577";
+ public static final String s23578 = "23578";
+ public static final String s23579 = "23579";
+ public static final String s23580 = "23580";
+ public static final String s23581 = "23581";
+ public static final String s23582 = "23582";
+ public static final String s23583 = "23583";
+ public static final String s23584 = "23584";
+ public static final String s23585 = "23585";
+ public static final String s23586 = "23586";
+ public static final String s23587 = "23587";
+ public static final String s23588 = "23588";
+ public static final String s23589 = "23589";
+ public static final String s23590 = "23590";
+ public static final String s23591 = "23591";
+ public static final String s23592 = "23592";
+ public static final String s23593 = "23593";
+ public static final String s23594 = "23594";
+ public static final String s23595 = "23595";
+ public static final String s23596 = "23596";
+ public static final String s23597 = "23597";
+ public static final String s23598 = "23598";
+ public static final String s23599 = "23599";
+ public static final String s23600 = "23600";
+ public static final String s23601 = "23601";
+ public static final String s23602 = "23602";
+ public static final String s23603 = "23603";
+ public static final String s23604 = "23604";
+ public static final String s23605 = "23605";
+ public static final String s23606 = "23606";
+ public static final String s23607 = "23607";
+ public static final String s23608 = "23608";
+ public static final String s23609 = "23609";
+ public static final String s23610 = "23610";
+ public static final String s23611 = "23611";
+ public static final String s23612 = "23612";
+ public static final String s23613 = "23613";
+ public static final String s23614 = "23614";
+ public static final String s23615 = "23615";
+ public static final String s23616 = "23616";
+ public static final String s23617 = "23617";
+ public static final String s23618 = "23618";
+ public static final String s23619 = "23619";
+ public static final String s23620 = "23620";
+ public static final String s23621 = "23621";
+ public static final String s23622 = "23622";
+ public static final String s23623 = "23623";
+ public static final String s23624 = "23624";
+ public static final String s23625 = "23625";
+ public static final String s23626 = "23626";
+ public static final String s23627 = "23627";
+ public static final String s23628 = "23628";
+ public static final String s23629 = "23629";
+ public static final String s23630 = "23630";
+ public static final String s23631 = "23631";
+ public static final String s23632 = "23632";
+ public static final String s23633 = "23633";
+ public static final String s23634 = "23634";
+ public static final String s23635 = "23635";
+ public static final String s23636 = "23636";
+ public static final String s23637 = "23637";
+ public static final String s23638 = "23638";
+ public static final String s23639 = "23639";
+ public static final String s23640 = "23640";
+ public static final String s23641 = "23641";
+ public static final String s23642 = "23642";
+ public static final String s23643 = "23643";
+ public static final String s23644 = "23644";
+ public static final String s23645 = "23645";
+ public static final String s23646 = "23646";
+ public static final String s23647 = "23647";
+ public static final String s23648 = "23648";
+ public static final String s23649 = "23649";
+ public static final String s23650 = "23650";
+ public static final String s23651 = "23651";
+ public static final String s23652 = "23652";
+ public static final String s23653 = "23653";
+ public static final String s23654 = "23654";
+ public static final String s23655 = "23655";
+ public static final String s23656 = "23656";
+ public static final String s23657 = "23657";
+ public static final String s23658 = "23658";
+ public static final String s23659 = "23659";
+ public static final String s23660 = "23660";
+ public static final String s23661 = "23661";
+ public static final String s23662 = "23662";
+ public static final String s23663 = "23663";
+ public static final String s23664 = "23664";
+ public static final String s23665 = "23665";
+ public static final String s23666 = "23666";
+ public static final String s23667 = "23667";
+ public static final String s23668 = "23668";
+ public static final String s23669 = "23669";
+ public static final String s23670 = "23670";
+ public static final String s23671 = "23671";
+ public static final String s23672 = "23672";
+ public static final String s23673 = "23673";
+ public static final String s23674 = "23674";
+ public static final String s23675 = "23675";
+ public static final String s23676 = "23676";
+ public static final String s23677 = "23677";
+ public static final String s23678 = "23678";
+ public static final String s23679 = "23679";
+ public static final String s23680 = "23680";
+ public static final String s23681 = "23681";
+ public static final String s23682 = "23682";
+ public static final String s23683 = "23683";
+ public static final String s23684 = "23684";
+ public static final String s23685 = "23685";
+ public static final String s23686 = "23686";
+ public static final String s23687 = "23687";
+ public static final String s23688 = "23688";
+ public static final String s23689 = "23689";
+ public static final String s23690 = "23690";
+ public static final String s23691 = "23691";
+ public static final String s23692 = "23692";
+ public static final String s23693 = "23693";
+ public static final String s23694 = "23694";
+ public static final String s23695 = "23695";
+ public static final String s23696 = "23696";
+ public static final String s23697 = "23697";
+ public static final String s23698 = "23698";
+ public static final String s23699 = "23699";
+ public static final String s23700 = "23700";
+ public static final String s23701 = "23701";
+ public static final String s23702 = "23702";
+ public static final String s23703 = "23703";
+ public static final String s23704 = "23704";
+ public static final String s23705 = "23705";
+ public static final String s23706 = "23706";
+ public static final String s23707 = "23707";
+ public static final String s23708 = "23708";
+ public static final String s23709 = "23709";
+ public static final String s23710 = "23710";
+ public static final String s23711 = "23711";
+ public static final String s23712 = "23712";
+ public static final String s23713 = "23713";
+ public static final String s23714 = "23714";
+ public static final String s23715 = "23715";
+ public static final String s23716 = "23716";
+ public static final String s23717 = "23717";
+ public static final String s23718 = "23718";
+ public static final String s23719 = "23719";
+ public static final String s23720 = "23720";
+ public static final String s23721 = "23721";
+ public static final String s23722 = "23722";
+ public static final String s23723 = "23723";
+ public static final String s23724 = "23724";
+ public static final String s23725 = "23725";
+ public static final String s23726 = "23726";
+ public static final String s23727 = "23727";
+ public static final String s23728 = "23728";
+ public static final String s23729 = "23729";
+ public static final String s23730 = "23730";
+ public static final String s23731 = "23731";
+ public static final String s23732 = "23732";
+ public static final String s23733 = "23733";
+ public static final String s23734 = "23734";
+ public static final String s23735 = "23735";
+ public static final String s23736 = "23736";
+ public static final String s23737 = "23737";
+ public static final String s23738 = "23738";
+ public static final String s23739 = "23739";
+ public static final String s23740 = "23740";
+ public static final String s23741 = "23741";
+ public static final String s23742 = "23742";
+ public static final String s23743 = "23743";
+ public static final String s23744 = "23744";
+ public static final String s23745 = "23745";
+ public static final String s23746 = "23746";
+ public static final String s23747 = "23747";
+ public static final String s23748 = "23748";
+ public static final String s23749 = "23749";
+ public static final String s23750 = "23750";
+ public static final String s23751 = "23751";
+ public static final String s23752 = "23752";
+ public static final String s23753 = "23753";
+ public static final String s23754 = "23754";
+ public static final String s23755 = "23755";
+ public static final String s23756 = "23756";
+ public static final String s23757 = "23757";
+ public static final String s23758 = "23758";
+ public static final String s23759 = "23759";
+ public static final String s23760 = "23760";
+ public static final String s23761 = "23761";
+ public static final String s23762 = "23762";
+ public static final String s23763 = "23763";
+ public static final String s23764 = "23764";
+ public static final String s23765 = "23765";
+ public static final String s23766 = "23766";
+ public static final String s23767 = "23767";
+ public static final String s23768 = "23768";
+ public static final String s23769 = "23769";
+ public static final String s23770 = "23770";
+ public static final String s23771 = "23771";
+ public static final String s23772 = "23772";
+ public static final String s23773 = "23773";
+ public static final String s23774 = "23774";
+ public static final String s23775 = "23775";
+ public static final String s23776 = "23776";
+ public static final String s23777 = "23777";
+ public static final String s23778 = "23778";
+ public static final String s23779 = "23779";
+ public static final String s23780 = "23780";
+ public static final String s23781 = "23781";
+ public static final String s23782 = "23782";
+ public static final String s23783 = "23783";
+ public static final String s23784 = "23784";
+ public static final String s23785 = "23785";
+ public static final String s23786 = "23786";
+ public static final String s23787 = "23787";
+ public static final String s23788 = "23788";
+ public static final String s23789 = "23789";
+ public static final String s23790 = "23790";
+ public static final String s23791 = "23791";
+ public static final String s23792 = "23792";
+ public static final String s23793 = "23793";
+ public static final String s23794 = "23794";
+ public static final String s23795 = "23795";
+ public static final String s23796 = "23796";
+ public static final String s23797 = "23797";
+ public static final String s23798 = "23798";
+ public static final String s23799 = "23799";
+ public static final String s23800 = "23800";
+ public static final String s23801 = "23801";
+ public static final String s23802 = "23802";
+ public static final String s23803 = "23803";
+ public static final String s23804 = "23804";
+ public static final String s23805 = "23805";
+ public static final String s23806 = "23806";
+ public static final String s23807 = "23807";
+ public static final String s23808 = "23808";
+ public static final String s23809 = "23809";
+ public static final String s23810 = "23810";
+ public static final String s23811 = "23811";
+ public static final String s23812 = "23812";
+ public static final String s23813 = "23813";
+ public static final String s23814 = "23814";
+ public static final String s23815 = "23815";
+ public static final String s23816 = "23816";
+ public static final String s23817 = "23817";
+ public static final String s23818 = "23818";
+ public static final String s23819 = "23819";
+ public static final String s23820 = "23820";
+ public static final String s23821 = "23821";
+ public static final String s23822 = "23822";
+ public static final String s23823 = "23823";
+ public static final String s23824 = "23824";
+ public static final String s23825 = "23825";
+ public static final String s23826 = "23826";
+ public static final String s23827 = "23827";
+ public static final String s23828 = "23828";
+ public static final String s23829 = "23829";
+ public static final String s23830 = "23830";
+ public static final String s23831 = "23831";
+ public static final String s23832 = "23832";
+ public static final String s23833 = "23833";
+ public static final String s23834 = "23834";
+ public static final String s23835 = "23835";
+ public static final String s23836 = "23836";
+ public static final String s23837 = "23837";
+ public static final String s23838 = "23838";
+ public static final String s23839 = "23839";
+ public static final String s23840 = "23840";
+ public static final String s23841 = "23841";
+ public static final String s23842 = "23842";
+ public static final String s23843 = "23843";
+ public static final String s23844 = "23844";
+ public static final String s23845 = "23845";
+ public static final String s23846 = "23846";
+ public static final String s23847 = "23847";
+ public static final String s23848 = "23848";
+ public static final String s23849 = "23849";
+ public static final String s23850 = "23850";
+ public static final String s23851 = "23851";
+ public static final String s23852 = "23852";
+ public static final String s23853 = "23853";
+ public static final String s23854 = "23854";
+ public static final String s23855 = "23855";
+ public static final String s23856 = "23856";
+ public static final String s23857 = "23857";
+ public static final String s23858 = "23858";
+ public static final String s23859 = "23859";
+ public static final String s23860 = "23860";
+ public static final String s23861 = "23861";
+ public static final String s23862 = "23862";
+ public static final String s23863 = "23863";
+ public static final String s23864 = "23864";
+ public static final String s23865 = "23865";
+ public static final String s23866 = "23866";
+ public static final String s23867 = "23867";
+ public static final String s23868 = "23868";
+ public static final String s23869 = "23869";
+ public static final String s23870 = "23870";
+ public static final String s23871 = "23871";
+ public static final String s23872 = "23872";
+ public static final String s23873 = "23873";
+ public static final String s23874 = "23874";
+ public static final String s23875 = "23875";
+ public static final String s23876 = "23876";
+ public static final String s23877 = "23877";
+ public static final String s23878 = "23878";
+ public static final String s23879 = "23879";
+ public static final String s23880 = "23880";
+ public static final String s23881 = "23881";
+ public static final String s23882 = "23882";
+ public static final String s23883 = "23883";
+ public static final String s23884 = "23884";
+ public static final String s23885 = "23885";
+ public static final String s23886 = "23886";
+ public static final String s23887 = "23887";
+ public static final String s23888 = "23888";
+ public static final String s23889 = "23889";
+ public static final String s23890 = "23890";
+ public static final String s23891 = "23891";
+ public static final String s23892 = "23892";
+ public static final String s23893 = "23893";
+ public static final String s23894 = "23894";
+ public static final String s23895 = "23895";
+ public static final String s23896 = "23896";
+ public static final String s23897 = "23897";
+ public static final String s23898 = "23898";
+ public static final String s23899 = "23899";
+ public static final String s23900 = "23900";
+ public static final String s23901 = "23901";
+ public static final String s23902 = "23902";
+ public static final String s23903 = "23903";
+ public static final String s23904 = "23904";
+ public static final String s23905 = "23905";
+ public static final String s23906 = "23906";
+ public static final String s23907 = "23907";
+ public static final String s23908 = "23908";
+ public static final String s23909 = "23909";
+ public static final String s23910 = "23910";
+ public static final String s23911 = "23911";
+ public static final String s23912 = "23912";
+ public static final String s23913 = "23913";
+ public static final String s23914 = "23914";
+ public static final String s23915 = "23915";
+ public static final String s23916 = "23916";
+ public static final String s23917 = "23917";
+ public static final String s23918 = "23918";
+ public static final String s23919 = "23919";
+ public static final String s23920 = "23920";
+ public static final String s23921 = "23921";
+ public static final String s23922 = "23922";
+ public static final String s23923 = "23923";
+ public static final String s23924 = "23924";
+ public static final String s23925 = "23925";
+ public static final String s23926 = "23926";
+ public static final String s23927 = "23927";
+ public static final String s23928 = "23928";
+ public static final String s23929 = "23929";
+ public static final String s23930 = "23930";
+ public static final String s23931 = "23931";
+ public static final String s23932 = "23932";
+ public static final String s23933 = "23933";
+ public static final String s23934 = "23934";
+ public static final String s23935 = "23935";
+ public static final String s23936 = "23936";
+ public static final String s23937 = "23937";
+ public static final String s23938 = "23938";
+ public static final String s23939 = "23939";
+ public static final String s23940 = "23940";
+ public static final String s23941 = "23941";
+ public static final String s23942 = "23942";
+ public static final String s23943 = "23943";
+ public static final String s23944 = "23944";
+ public static final String s23945 = "23945";
+ public static final String s23946 = "23946";
+ public static final String s23947 = "23947";
+ public static final String s23948 = "23948";
+ public static final String s23949 = "23949";
+ public static final String s23950 = "23950";
+ public static final String s23951 = "23951";
+ public static final String s23952 = "23952";
+ public static final String s23953 = "23953";
+ public static final String s23954 = "23954";
+ public static final String s23955 = "23955";
+ public static final String s23956 = "23956";
+ public static final String s23957 = "23957";
+ public static final String s23958 = "23958";
+ public static final String s23959 = "23959";
+ public static final String s23960 = "23960";
+ public static final String s23961 = "23961";
+ public static final String s23962 = "23962";
+ public static final String s23963 = "23963";
+ public static final String s23964 = "23964";
+ public static final String s23965 = "23965";
+ public static final String s23966 = "23966";
+ public static final String s23967 = "23967";
+ public static final String s23968 = "23968";
+ public static final String s23969 = "23969";
+ public static final String s23970 = "23970";
+ public static final String s23971 = "23971";
+ public static final String s23972 = "23972";
+ public static final String s23973 = "23973";
+ public static final String s23974 = "23974";
+ public static final String s23975 = "23975";
+ public static final String s23976 = "23976";
+ public static final String s23977 = "23977";
+ public static final String s23978 = "23978";
+ public static final String s23979 = "23979";
+ public static final String s23980 = "23980";
+ public static final String s23981 = "23981";
+ public static final String s23982 = "23982";
+ public static final String s23983 = "23983";
+ public static final String s23984 = "23984";
+ public static final String s23985 = "23985";
+ public static final String s23986 = "23986";
+ public static final String s23987 = "23987";
+ public static final String s23988 = "23988";
+ public static final String s23989 = "23989";
+ public static final String s23990 = "23990";
+ public static final String s23991 = "23991";
+ public static final String s23992 = "23992";
+ public static final String s23993 = "23993";
+ public static final String s23994 = "23994";
+ public static final String s23995 = "23995";
+ public static final String s23996 = "23996";
+ public static final String s23997 = "23997";
+ public static final String s23998 = "23998";
+ public static final String s23999 = "23999";
+ public static final String s24000 = "24000";
+ public static final String s24001 = "24001";
+ public static final String s24002 = "24002";
+ public static final String s24003 = "24003";
+ public static final String s24004 = "24004";
+ public static final String s24005 = "24005";
+ public static final String s24006 = "24006";
+ public static final String s24007 = "24007";
+ public static final String s24008 = "24008";
+ public static final String s24009 = "24009";
+ public static final String s24010 = "24010";
+ public static final String s24011 = "24011";
+ public static final String s24012 = "24012";
+ public static final String s24013 = "24013";
+ public static final String s24014 = "24014";
+ public static final String s24015 = "24015";
+ public static final String s24016 = "24016";
+ public static final String s24017 = "24017";
+ public static final String s24018 = "24018";
+ public static final String s24019 = "24019";
+ public static final String s24020 = "24020";
+ public static final String s24021 = "24021";
+ public static final String s24022 = "24022";
+ public static final String s24023 = "24023";
+ public static final String s24024 = "24024";
+ public static final String s24025 = "24025";
+ public static final String s24026 = "24026";
+ public static final String s24027 = "24027";
+ public static final String s24028 = "24028";
+ public static final String s24029 = "24029";
+ public static final String s24030 = "24030";
+ public static final String s24031 = "24031";
+ public static final String s24032 = "24032";
+ public static final String s24033 = "24033";
+ public static final String s24034 = "24034";
+ public static final String s24035 = "24035";
+ public static final String s24036 = "24036";
+ public static final String s24037 = "24037";
+ public static final String s24038 = "24038";
+ public static final String s24039 = "24039";
+ public static final String s24040 = "24040";
+ public static final String s24041 = "24041";
+ public static final String s24042 = "24042";
+ public static final String s24043 = "24043";
+ public static final String s24044 = "24044";
+ public static final String s24045 = "24045";
+ public static final String s24046 = "24046";
+ public static final String s24047 = "24047";
+ public static final String s24048 = "24048";
+ public static final String s24049 = "24049";
+ public static final String s24050 = "24050";
+ public static final String s24051 = "24051";
+ public static final String s24052 = "24052";
+ public static final String s24053 = "24053";
+ public static final String s24054 = "24054";
+ public static final String s24055 = "24055";
+ public static final String s24056 = "24056";
+ public static final String s24057 = "24057";
+ public static final String s24058 = "24058";
+ public static final String s24059 = "24059";
+ public static final String s24060 = "24060";
+ public static final String s24061 = "24061";
+ public static final String s24062 = "24062";
+ public static final String s24063 = "24063";
+ public static final String s24064 = "24064";
+ public static final String s24065 = "24065";
+ public static final String s24066 = "24066";
+ public static final String s24067 = "24067";
+ public static final String s24068 = "24068";
+ public static final String s24069 = "24069";
+ public static final String s24070 = "24070";
+ public static final String s24071 = "24071";
+ public static final String s24072 = "24072";
+ public static final String s24073 = "24073";
+ public static final String s24074 = "24074";
+ public static final String s24075 = "24075";
+ public static final String s24076 = "24076";
+ public static final String s24077 = "24077";
+ public static final String s24078 = "24078";
+ public static final String s24079 = "24079";
+ public static final String s24080 = "24080";
+ public static final String s24081 = "24081";
+ public static final String s24082 = "24082";
+ public static final String s24083 = "24083";
+ public static final String s24084 = "24084";
+ public static final String s24085 = "24085";
+ public static final String s24086 = "24086";
+ public static final String s24087 = "24087";
+ public static final String s24088 = "24088";
+ public static final String s24089 = "24089";
+ public static final String s24090 = "24090";
+ public static final String s24091 = "24091";
+ public static final String s24092 = "24092";
+ public static final String s24093 = "24093";
+ public static final String s24094 = "24094";
+ public static final String s24095 = "24095";
+ public static final String s24096 = "24096";
+ public static final String s24097 = "24097";
+ public static final String s24098 = "24098";
+ public static final String s24099 = "24099";
+ public static final String s24100 = "24100";
+ public static final String s24101 = "24101";
+ public static final String s24102 = "24102";
+ public static final String s24103 = "24103";
+ public static final String s24104 = "24104";
+ public static final String s24105 = "24105";
+ public static final String s24106 = "24106";
+ public static final String s24107 = "24107";
+ public static final String s24108 = "24108";
+ public static final String s24109 = "24109";
+ public static final String s24110 = "24110";
+ public static final String s24111 = "24111";
+ public static final String s24112 = "24112";
+ public static final String s24113 = "24113";
+ public static final String s24114 = "24114";
+ public static final String s24115 = "24115";
+ public static final String s24116 = "24116";
+ public static final String s24117 = "24117";
+ public static final String s24118 = "24118";
+ public static final String s24119 = "24119";
+ public static final String s24120 = "24120";
+ public static final String s24121 = "24121";
+ public static final String s24122 = "24122";
+ public static final String s24123 = "24123";
+ public static final String s24124 = "24124";
+ public static final String s24125 = "24125";
+ public static final String s24126 = "24126";
+ public static final String s24127 = "24127";
+ public static final String s24128 = "24128";
+ public static final String s24129 = "24129";
+ public static final String s24130 = "24130";
+ public static final String s24131 = "24131";
+ public static final String s24132 = "24132";
+ public static final String s24133 = "24133";
+ public static final String s24134 = "24134";
+ public static final String s24135 = "24135";
+ public static final String s24136 = "24136";
+ public static final String s24137 = "24137";
+ public static final String s24138 = "24138";
+ public static final String s24139 = "24139";
+ public static final String s24140 = "24140";
+ public static final String s24141 = "24141";
+ public static final String s24142 = "24142";
+ public static final String s24143 = "24143";
+ public static final String s24144 = "24144";
+ public static final String s24145 = "24145";
+ public static final String s24146 = "24146";
+ public static final String s24147 = "24147";
+ public static final String s24148 = "24148";
+ public static final String s24149 = "24149";
+ public static final String s24150 = "24150";
+ public static final String s24151 = "24151";
+ public static final String s24152 = "24152";
+ public static final String s24153 = "24153";
+ public static final String s24154 = "24154";
+ public static final String s24155 = "24155";
+ public static final String s24156 = "24156";
+ public static final String s24157 = "24157";
+ public static final String s24158 = "24158";
+ public static final String s24159 = "24159";
+ public static final String s24160 = "24160";
+ public static final String s24161 = "24161";
+ public static final String s24162 = "24162";
+ public static final String s24163 = "24163";
+ public static final String s24164 = "24164";
+ public static final String s24165 = "24165";
+ public static final String s24166 = "24166";
+ public static final String s24167 = "24167";
+ public static final String s24168 = "24168";
+ public static final String s24169 = "24169";
+ public static final String s24170 = "24170";
+ public static final String s24171 = "24171";
+ public static final String s24172 = "24172";
+ public static final String s24173 = "24173";
+ public static final String s24174 = "24174";
+ public static final String s24175 = "24175";
+ public static final String s24176 = "24176";
+ public static final String s24177 = "24177";
+ public static final String s24178 = "24178";
+ public static final String s24179 = "24179";
+ public static final String s24180 = "24180";
+ public static final String s24181 = "24181";
+ public static final String s24182 = "24182";
+ public static final String s24183 = "24183";
+ public static final String s24184 = "24184";
+ public static final String s24185 = "24185";
+ public static final String s24186 = "24186";
+ public static final String s24187 = "24187";
+ public static final String s24188 = "24188";
+ public static final String s24189 = "24189";
+ public static final String s24190 = "24190";
+ public static final String s24191 = "24191";
+ public static final String s24192 = "24192";
+ public static final String s24193 = "24193";
+ public static final String s24194 = "24194";
+ public static final String s24195 = "24195";
+ public static final String s24196 = "24196";
+ public static final String s24197 = "24197";
+ public static final String s24198 = "24198";
+ public static final String s24199 = "24199";
+ public static final String s24200 = "24200";
+ public static final String s24201 = "24201";
+ public static final String s24202 = "24202";
+ public static final String s24203 = "24203";
+ public static final String s24204 = "24204";
+ public static final String s24205 = "24205";
+ public static final String s24206 = "24206";
+ public static final String s24207 = "24207";
+ public static final String s24208 = "24208";
+ public static final String s24209 = "24209";
+ public static final String s24210 = "24210";
+ public static final String s24211 = "24211";
+ public static final String s24212 = "24212";
+ public static final String s24213 = "24213";
+ public static final String s24214 = "24214";
+ public static final String s24215 = "24215";
+ public static final String s24216 = "24216";
+ public static final String s24217 = "24217";
+ public static final String s24218 = "24218";
+ public static final String s24219 = "24219";
+ public static final String s24220 = "24220";
+ public static final String s24221 = "24221";
+ public static final String s24222 = "24222";
+ public static final String s24223 = "24223";
+ public static final String s24224 = "24224";
+ public static final String s24225 = "24225";
+ public static final String s24226 = "24226";
+ public static final String s24227 = "24227";
+ public static final String s24228 = "24228";
+ public static final String s24229 = "24229";
+ public static final String s24230 = "24230";
+ public static final String s24231 = "24231";
+ public static final String s24232 = "24232";
+ public static final String s24233 = "24233";
+ public static final String s24234 = "24234";
+ public static final String s24235 = "24235";
+ public static final String s24236 = "24236";
+ public static final String s24237 = "24237";
+ public static final String s24238 = "24238";
+ public static final String s24239 = "24239";
+ public static final String s24240 = "24240";
+ public static final String s24241 = "24241";
+ public static final String s24242 = "24242";
+ public static final String s24243 = "24243";
+ public static final String s24244 = "24244";
+ public static final String s24245 = "24245";
+ public static final String s24246 = "24246";
+ public static final String s24247 = "24247";
+ public static final String s24248 = "24248";
+ public static final String s24249 = "24249";
+ public static final String s24250 = "24250";
+ public static final String s24251 = "24251";
+ public static final String s24252 = "24252";
+ public static final String s24253 = "24253";
+ public static final String s24254 = "24254";
+ public static final String s24255 = "24255";
+ public static final String s24256 = "24256";
+ public static final String s24257 = "24257";
+ public static final String s24258 = "24258";
+ public static final String s24259 = "24259";
+ public static final String s24260 = "24260";
+ public static final String s24261 = "24261";
+ public static final String s24262 = "24262";
+ public static final String s24263 = "24263";
+ public static final String s24264 = "24264";
+ public static final String s24265 = "24265";
+ public static final String s24266 = "24266";
+ public static final String s24267 = "24267";
+ public static final String s24268 = "24268";
+ public static final String s24269 = "24269";
+ public static final String s24270 = "24270";
+ public static final String s24271 = "24271";
+ public static final String s24272 = "24272";
+ public static final String s24273 = "24273";
+ public static final String s24274 = "24274";
+ public static final String s24275 = "24275";
+ public static final String s24276 = "24276";
+ public static final String s24277 = "24277";
+ public static final String s24278 = "24278";
+ public static final String s24279 = "24279";
+ public static final String s24280 = "24280";
+ public static final String s24281 = "24281";
+ public static final String s24282 = "24282";
+ public static final String s24283 = "24283";
+ public static final String s24284 = "24284";
+ public static final String s24285 = "24285";
+ public static final String s24286 = "24286";
+ public static final String s24287 = "24287";
+ public static final String s24288 = "24288";
+ public static final String s24289 = "24289";
+ public static final String s24290 = "24290";
+ public static final String s24291 = "24291";
+ public static final String s24292 = "24292";
+ public static final String s24293 = "24293";
+ public static final String s24294 = "24294";
+ public static final String s24295 = "24295";
+ public static final String s24296 = "24296";
+ public static final String s24297 = "24297";
+ public static final String s24298 = "24298";
+ public static final String s24299 = "24299";
+ public static final String s24300 = "24300";
+ public static final String s24301 = "24301";
+ public static final String s24302 = "24302";
+ public static final String s24303 = "24303";
+ public static final String s24304 = "24304";
+ public static final String s24305 = "24305";
+ public static final String s24306 = "24306";
+ public static final String s24307 = "24307";
+ public static final String s24308 = "24308";
+ public static final String s24309 = "24309";
+ public static final String s24310 = "24310";
+ public static final String s24311 = "24311";
+ public static final String s24312 = "24312";
+ public static final String s24313 = "24313";
+ public static final String s24314 = "24314";
+ public static final String s24315 = "24315";
+ public static final String s24316 = "24316";
+ public static final String s24317 = "24317";
+ public static final String s24318 = "24318";
+ public static final String s24319 = "24319";
+ public static final String s24320 = "24320";
+ public static final String s24321 = "24321";
+ public static final String s24322 = "24322";
+ public static final String s24323 = "24323";
+ public static final String s24324 = "24324";
+ public static final String s24325 = "24325";
+ public static final String s24326 = "24326";
+ public static final String s24327 = "24327";
+ public static final String s24328 = "24328";
+ public static final String s24329 = "24329";
+ public static final String s24330 = "24330";
+ public static final String s24331 = "24331";
+ public static final String s24332 = "24332";
+ public static final String s24333 = "24333";
+ public static final String s24334 = "24334";
+ public static final String s24335 = "24335";
+ public static final String s24336 = "24336";
+ public static final String s24337 = "24337";
+ public static final String s24338 = "24338";
+ public static final String s24339 = "24339";
+ public static final String s24340 = "24340";
+ public static final String s24341 = "24341";
+ public static final String s24342 = "24342";
+ public static final String s24343 = "24343";
+ public static final String s24344 = "24344";
+ public static final String s24345 = "24345";
+ public static final String s24346 = "24346";
+ public static final String s24347 = "24347";
+ public static final String s24348 = "24348";
+ public static final String s24349 = "24349";
+ public static final String s24350 = "24350";
+ public static final String s24351 = "24351";
+ public static final String s24352 = "24352";
+ public static final String s24353 = "24353";
+ public static final String s24354 = "24354";
+ public static final String s24355 = "24355";
+ public static final String s24356 = "24356";
+ public static final String s24357 = "24357";
+ public static final String s24358 = "24358";
+ public static final String s24359 = "24359";
+ public static final String s24360 = "24360";
+ public static final String s24361 = "24361";
+ public static final String s24362 = "24362";
+ public static final String s24363 = "24363";
+ public static final String s24364 = "24364";
+ public static final String s24365 = "24365";
+ public static final String s24366 = "24366";
+ public static final String s24367 = "24367";
+ public static final String s24368 = "24368";
+ public static final String s24369 = "24369";
+ public static final String s24370 = "24370";
+ public static final String s24371 = "24371";
+ public static final String s24372 = "24372";
+ public static final String s24373 = "24373";
+ public static final String s24374 = "24374";
+ public static final String s24375 = "24375";
+ public static final String s24376 = "24376";
+ public static final String s24377 = "24377";
+ public static final String s24378 = "24378";
+ public static final String s24379 = "24379";
+ public static final String s24380 = "24380";
+ public static final String s24381 = "24381";
+ public static final String s24382 = "24382";
+ public static final String s24383 = "24383";
+ public static final String s24384 = "24384";
+ public static final String s24385 = "24385";
+ public static final String s24386 = "24386";
+ public static final String s24387 = "24387";
+ public static final String s24388 = "24388";
+ public static final String s24389 = "24389";
+ public static final String s24390 = "24390";
+ public static final String s24391 = "24391";
+ public static final String s24392 = "24392";
+ public static final String s24393 = "24393";
+ public static final String s24394 = "24394";
+ public static final String s24395 = "24395";
+ public static final String s24396 = "24396";
+ public static final String s24397 = "24397";
+ public static final String s24398 = "24398";
+ public static final String s24399 = "24399";
+ public static final String s24400 = "24400";
+ public static final String s24401 = "24401";
+ public static final String s24402 = "24402";
+ public static final String s24403 = "24403";
+ public static final String s24404 = "24404";
+ public static final String s24405 = "24405";
+ public static final String s24406 = "24406";
+ public static final String s24407 = "24407";
+ public static final String s24408 = "24408";
+ public static final String s24409 = "24409";
+ public static final String s24410 = "24410";
+ public static final String s24411 = "24411";
+ public static final String s24412 = "24412";
+ public static final String s24413 = "24413";
+ public static final String s24414 = "24414";
+ public static final String s24415 = "24415";
+ public static final String s24416 = "24416";
+ public static final String s24417 = "24417";
+ public static final String s24418 = "24418";
+ public static final String s24419 = "24419";
+ public static final String s24420 = "24420";
+ public static final String s24421 = "24421";
+ public static final String s24422 = "24422";
+ public static final String s24423 = "24423";
+ public static final String s24424 = "24424";
+ public static final String s24425 = "24425";
+ public static final String s24426 = "24426";
+ public static final String s24427 = "24427";
+ public static final String s24428 = "24428";
+ public static final String s24429 = "24429";
+ public static final String s24430 = "24430";
+ public static final String s24431 = "24431";
+ public static final String s24432 = "24432";
+ public static final String s24433 = "24433";
+ public static final String s24434 = "24434";
+ public static final String s24435 = "24435";
+ public static final String s24436 = "24436";
+ public static final String s24437 = "24437";
+ public static final String s24438 = "24438";
+ public static final String s24439 = "24439";
+ public static final String s24440 = "24440";
+ public static final String s24441 = "24441";
+ public static final String s24442 = "24442";
+ public static final String s24443 = "24443";
+ public static final String s24444 = "24444";
+ public static final String s24445 = "24445";
+ public static final String s24446 = "24446";
+ public static final String s24447 = "24447";
+ public static final String s24448 = "24448";
+ public static final String s24449 = "24449";
+ public static final String s24450 = "24450";
+ public static final String s24451 = "24451";
+ public static final String s24452 = "24452";
+ public static final String s24453 = "24453";
+ public static final String s24454 = "24454";
+ public static final String s24455 = "24455";
+ public static final String s24456 = "24456";
+ public static final String s24457 = "24457";
+ public static final String s24458 = "24458";
+ public static final String s24459 = "24459";
+ public static final String s24460 = "24460";
+ public static final String s24461 = "24461";
+ public static final String s24462 = "24462";
+ public static final String s24463 = "24463";
+ public static final String s24464 = "24464";
+ public static final String s24465 = "24465";
+ public static final String s24466 = "24466";
+ public static final String s24467 = "24467";
+ public static final String s24468 = "24468";
+ public static final String s24469 = "24469";
+ public static final String s24470 = "24470";
+ public static final String s24471 = "24471";
+ public static final String s24472 = "24472";
+ public static final String s24473 = "24473";
+ public static final String s24474 = "24474";
+ public static final String s24475 = "24475";
+ public static final String s24476 = "24476";
+ public static final String s24477 = "24477";
+ public static final String s24478 = "24478";
+ public static final String s24479 = "24479";
+ public static final String s24480 = "24480";
+ public static final String s24481 = "24481";
+ public static final String s24482 = "24482";
+ public static final String s24483 = "24483";
+ public static final String s24484 = "24484";
+ public static final String s24485 = "24485";
+ public static final String s24486 = "24486";
+ public static final String s24487 = "24487";
+ public static final String s24488 = "24488";
+ public static final String s24489 = "24489";
+ public static final String s24490 = "24490";
+ public static final String s24491 = "24491";
+ public static final String s24492 = "24492";
+ public static final String s24493 = "24493";
+ public static final String s24494 = "24494";
+ public static final String s24495 = "24495";
+ public static final String s24496 = "24496";
+ public static final String s24497 = "24497";
+ public static final String s24498 = "24498";
+ public static final String s24499 = "24499";
+ public static final String s24500 = "24500";
+ public static final String s24501 = "24501";
+ public static final String s24502 = "24502";
+ public static final String s24503 = "24503";
+ public static final String s24504 = "24504";
+ public static final String s24505 = "24505";
+ public static final String s24506 = "24506";
+ public static final String s24507 = "24507";
+ public static final String s24508 = "24508";
+ public static final String s24509 = "24509";
+ public static final String s24510 = "24510";
+ public static final String s24511 = "24511";
+ public static final String s24512 = "24512";
+ public static final String s24513 = "24513";
+ public static final String s24514 = "24514";
+ public static final String s24515 = "24515";
+ public static final String s24516 = "24516";
+ public static final String s24517 = "24517";
+ public static final String s24518 = "24518";
+ public static final String s24519 = "24519";
+ public static final String s24520 = "24520";
+ public static final String s24521 = "24521";
+ public static final String s24522 = "24522";
+ public static final String s24523 = "24523";
+ public static final String s24524 = "24524";
+ public static final String s24525 = "24525";
+ public static final String s24526 = "24526";
+ public static final String s24527 = "24527";
+ public static final String s24528 = "24528";
+ public static final String s24529 = "24529";
+ public static final String s24530 = "24530";
+ public static final String s24531 = "24531";
+ public static final String s24532 = "24532";
+ public static final String s24533 = "24533";
+ public static final String s24534 = "24534";
+ public static final String s24535 = "24535";
+ public static final String s24536 = "24536";
+ public static final String s24537 = "24537";
+ public static final String s24538 = "24538";
+ public static final String s24539 = "24539";
+ public static final String s24540 = "24540";
+ public static final String s24541 = "24541";
+ public static final String s24542 = "24542";
+ public static final String s24543 = "24543";
+ public static final String s24544 = "24544";
+ public static final String s24545 = "24545";
+ public static final String s24546 = "24546";
+ public static final String s24547 = "24547";
+ public static final String s24548 = "24548";
+ public static final String s24549 = "24549";
+ public static final String s24550 = "24550";
+ public static final String s24551 = "24551";
+ public static final String s24552 = "24552";
+ public static final String s24553 = "24553";
+ public static final String s24554 = "24554";
+ public static final String s24555 = "24555";
+ public static final String s24556 = "24556";
+ public static final String s24557 = "24557";
+ public static final String s24558 = "24558";
+ public static final String s24559 = "24559";
+ public static final String s24560 = "24560";
+ public static final String s24561 = "24561";
+ public static final String s24562 = "24562";
+ public static final String s24563 = "24563";
+ public static final String s24564 = "24564";
+ public static final String s24565 = "24565";
+ public static final String s24566 = "24566";
+ public static final String s24567 = "24567";
+ public static final String s24568 = "24568";
+ public static final String s24569 = "24569";
+ public static final String s24570 = "24570";
+ public static final String s24571 = "24571";
+ public static final String s24572 = "24572";
+ public static final String s24573 = "24573";
+ public static final String s24574 = "24574";
+ public static final String s24575 = "24575";
+ public static final String s24576 = "24576";
+ public static final String s24577 = "24577";
+ public static final String s24578 = "24578";
+ public static final String s24579 = "24579";
+ public static final String s24580 = "24580";
+ public static final String s24581 = "24581";
+ public static final String s24582 = "24582";
+ public static final String s24583 = "24583";
+ public static final String s24584 = "24584";
+ public static final String s24585 = "24585";
+ public static final String s24586 = "24586";
+ public static final String s24587 = "24587";
+ public static final String s24588 = "24588";
+ public static final String s24589 = "24589";
+ public static final String s24590 = "24590";
+ public static final String s24591 = "24591";
+ public static final String s24592 = "24592";
+ public static final String s24593 = "24593";
+ public static final String s24594 = "24594";
+ public static final String s24595 = "24595";
+ public static final String s24596 = "24596";
+ public static final String s24597 = "24597";
+ public static final String s24598 = "24598";
+ public static final String s24599 = "24599";
+ public static final String s24600 = "24600";
+ public static final String s24601 = "24601";
+ public static final String s24602 = "24602";
+ public static final String s24603 = "24603";
+ public static final String s24604 = "24604";
+ public static final String s24605 = "24605";
+ public static final String s24606 = "24606";
+ public static final String s24607 = "24607";
+ public static final String s24608 = "24608";
+ public static final String s24609 = "24609";
+ public static final String s24610 = "24610";
+ public static final String s24611 = "24611";
+ public static final String s24612 = "24612";
+ public static final String s24613 = "24613";
+ public static final String s24614 = "24614";
+ public static final String s24615 = "24615";
+ public static final String s24616 = "24616";
+ public static final String s24617 = "24617";
+ public static final String s24618 = "24618";
+ public static final String s24619 = "24619";
+ public static final String s24620 = "24620";
+ public static final String s24621 = "24621";
+ public static final String s24622 = "24622";
+ public static final String s24623 = "24623";
+ public static final String s24624 = "24624";
+ public static final String s24625 = "24625";
+ public static final String s24626 = "24626";
+ public static final String s24627 = "24627";
+ public static final String s24628 = "24628";
+ public static final String s24629 = "24629";
+ public static final String s24630 = "24630";
+ public static final String s24631 = "24631";
+ public static final String s24632 = "24632";
+ public static final String s24633 = "24633";
+ public static final String s24634 = "24634";
+ public static final String s24635 = "24635";
+ public static final String s24636 = "24636";
+ public static final String s24637 = "24637";
+ public static final String s24638 = "24638";
+ public static final String s24639 = "24639";
+ public static final String s24640 = "24640";
+ public static final String s24641 = "24641";
+ public static final String s24642 = "24642";
+ public static final String s24643 = "24643";
+ public static final String s24644 = "24644";
+ public static final String s24645 = "24645";
+ public static final String s24646 = "24646";
+ public static final String s24647 = "24647";
+ public static final String s24648 = "24648";
+ public static final String s24649 = "24649";
+ public static final String s24650 = "24650";
+ public static final String s24651 = "24651";
+ public static final String s24652 = "24652";
+ public static final String s24653 = "24653";
+ public static final String s24654 = "24654";
+ public static final String s24655 = "24655";
+ public static final String s24656 = "24656";
+ public static final String s24657 = "24657";
+ public static final String s24658 = "24658";
+ public static final String s24659 = "24659";
+ public static final String s24660 = "24660";
+ public static final String s24661 = "24661";
+ public static final String s24662 = "24662";
+ public static final String s24663 = "24663";
+ public static final String s24664 = "24664";
+ public static final String s24665 = "24665";
+ public static final String s24666 = "24666";
+ public static final String s24667 = "24667";
+ public static final String s24668 = "24668";
+ public static final String s24669 = "24669";
+ public static final String s24670 = "24670";
+ public static final String s24671 = "24671";
+ public static final String s24672 = "24672";
+ public static final String s24673 = "24673";
+ public static final String s24674 = "24674";
+ public static final String s24675 = "24675";
+ public static final String s24676 = "24676";
+ public static final String s24677 = "24677";
+ public static final String s24678 = "24678";
+ public static final String s24679 = "24679";
+ public static final String s24680 = "24680";
+ public static final String s24681 = "24681";
+ public static final String s24682 = "24682";
+ public static final String s24683 = "24683";
+ public static final String s24684 = "24684";
+ public static final String s24685 = "24685";
+ public static final String s24686 = "24686";
+ public static final String s24687 = "24687";
+ public static final String s24688 = "24688";
+ public static final String s24689 = "24689";
+ public static final String s24690 = "24690";
+ public static final String s24691 = "24691";
+ public static final String s24692 = "24692";
+ public static final String s24693 = "24693";
+ public static final String s24694 = "24694";
+ public static final String s24695 = "24695";
+ public static final String s24696 = "24696";
+ public static final String s24697 = "24697";
+ public static final String s24698 = "24698";
+ public static final String s24699 = "24699";
+ public static final String s24700 = "24700";
+ public static final String s24701 = "24701";
+ public static final String s24702 = "24702";
+ public static final String s24703 = "24703";
+ public static final String s24704 = "24704";
+ public static final String s24705 = "24705";
+ public static final String s24706 = "24706";
+ public static final String s24707 = "24707";
+ public static final String s24708 = "24708";
+ public static final String s24709 = "24709";
+ public static final String s24710 = "24710";
+ public static final String s24711 = "24711";
+ public static final String s24712 = "24712";
+ public static final String s24713 = "24713";
+ public static final String s24714 = "24714";
+ public static final String s24715 = "24715";
+ public static final String s24716 = "24716";
+ public static final String s24717 = "24717";
+ public static final String s24718 = "24718";
+ public static final String s24719 = "24719";
+ public static final String s24720 = "24720";
+ public static final String s24721 = "24721";
+ public static final String s24722 = "24722";
+ public static final String s24723 = "24723";
+ public static final String s24724 = "24724";
+ public static final String s24725 = "24725";
+ public static final String s24726 = "24726";
+ public static final String s24727 = "24727";
+ public static final String s24728 = "24728";
+ public static final String s24729 = "24729";
+ public static final String s24730 = "24730";
+ public static final String s24731 = "24731";
+ public static final String s24732 = "24732";
+ public static final String s24733 = "24733";
+ public static final String s24734 = "24734";
+ public static final String s24735 = "24735";
+ public static final String s24736 = "24736";
+ public static final String s24737 = "24737";
+ public static final String s24738 = "24738";
+ public static final String s24739 = "24739";
+ public static final String s24740 = "24740";
+ public static final String s24741 = "24741";
+ public static final String s24742 = "24742";
+ public static final String s24743 = "24743";
+ public static final String s24744 = "24744";
+ public static final String s24745 = "24745";
+ public static final String s24746 = "24746";
+ public static final String s24747 = "24747";
+ public static final String s24748 = "24748";
+ public static final String s24749 = "24749";
+ public static final String s24750 = "24750";
+ public static final String s24751 = "24751";
+ public static final String s24752 = "24752";
+ public static final String s24753 = "24753";
+ public static final String s24754 = "24754";
+ public static final String s24755 = "24755";
+ public static final String s24756 = "24756";
+ public static final String s24757 = "24757";
+ public static final String s24758 = "24758";
+ public static final String s24759 = "24759";
+ public static final String s24760 = "24760";
+ public static final String s24761 = "24761";
+ public static final String s24762 = "24762";
+ public static final String s24763 = "24763";
+ public static final String s24764 = "24764";
+ public static final String s24765 = "24765";
+ public static final String s24766 = "24766";
+ public static final String s24767 = "24767";
+ public static final String s24768 = "24768";
+ public static final String s24769 = "24769";
+ public static final String s24770 = "24770";
+ public static final String s24771 = "24771";
+ public static final String s24772 = "24772";
+ public static final String s24773 = "24773";
+ public static final String s24774 = "24774";
+ public static final String s24775 = "24775";
+ public static final String s24776 = "24776";
+ public static final String s24777 = "24777";
+ public static final String s24778 = "24778";
+ public static final String s24779 = "24779";
+ public static final String s24780 = "24780";
+ public static final String s24781 = "24781";
+ public static final String s24782 = "24782";
+ public static final String s24783 = "24783";
+ public static final String s24784 = "24784";
+ public static final String s24785 = "24785";
+ public static final String s24786 = "24786";
+ public static final String s24787 = "24787";
+ public static final String s24788 = "24788";
+ public static final String s24789 = "24789";
+ public static final String s24790 = "24790";
+ public static final String s24791 = "24791";
+ public static final String s24792 = "24792";
+ public static final String s24793 = "24793";
+ public static final String s24794 = "24794";
+ public static final String s24795 = "24795";
+ public static final String s24796 = "24796";
+ public static final String s24797 = "24797";
+ public static final String s24798 = "24798";
+ public static final String s24799 = "24799";
+ public static final String s24800 = "24800";
+ public static final String s24801 = "24801";
+ public static final String s24802 = "24802";
+ public static final String s24803 = "24803";
+ public static final String s24804 = "24804";
+ public static final String s24805 = "24805";
+ public static final String s24806 = "24806";
+ public static final String s24807 = "24807";
+ public static final String s24808 = "24808";
+ public static final String s24809 = "24809";
+ public static final String s24810 = "24810";
+ public static final String s24811 = "24811";
+ public static final String s24812 = "24812";
+ public static final String s24813 = "24813";
+ public static final String s24814 = "24814";
+ public static final String s24815 = "24815";
+ public static final String s24816 = "24816";
+ public static final String s24817 = "24817";
+ public static final String s24818 = "24818";
+ public static final String s24819 = "24819";
+ public static final String s24820 = "24820";
+ public static final String s24821 = "24821";
+ public static final String s24822 = "24822";
+ public static final String s24823 = "24823";
+ public static final String s24824 = "24824";
+ public static final String s24825 = "24825";
+ public static final String s24826 = "24826";
+ public static final String s24827 = "24827";
+ public static final String s24828 = "24828";
+ public static final String s24829 = "24829";
+ public static final String s24830 = "24830";
+ public static final String s24831 = "24831";
+ public static final String s24832 = "24832";
+ public static final String s24833 = "24833";
+ public static final String s24834 = "24834";
+ public static final String s24835 = "24835";
+ public static final String s24836 = "24836";
+ public static final String s24837 = "24837";
+ public static final String s24838 = "24838";
+ public static final String s24839 = "24839";
+ public static final String s24840 = "24840";
+ public static final String s24841 = "24841";
+ public static final String s24842 = "24842";
+ public static final String s24843 = "24843";
+ public static final String s24844 = "24844";
+ public static final String s24845 = "24845";
+ public static final String s24846 = "24846";
+ public static final String s24847 = "24847";
+ public static final String s24848 = "24848";
+ public static final String s24849 = "24849";
+ public static final String s24850 = "24850";
+ public static final String s24851 = "24851";
+ public static final String s24852 = "24852";
+ public static final String s24853 = "24853";
+ public static final String s24854 = "24854";
+ public static final String s24855 = "24855";
+ public static final String s24856 = "24856";
+ public static final String s24857 = "24857";
+ public static final String s24858 = "24858";
+ public static final String s24859 = "24859";
+ public static final String s24860 = "24860";
+ public static final String s24861 = "24861";
+ public static final String s24862 = "24862";
+ public static final String s24863 = "24863";
+ public static final String s24864 = "24864";
+ public static final String s24865 = "24865";
+ public static final String s24866 = "24866";
+ public static final String s24867 = "24867";
+ public static final String s24868 = "24868";
+ public static final String s24869 = "24869";
+ public static final String s24870 = "24870";
+ public static final String s24871 = "24871";
+ public static final String s24872 = "24872";
+ public static final String s24873 = "24873";
+ public static final String s24874 = "24874";
+ public static final String s24875 = "24875";
+ public static final String s24876 = "24876";
+ public static final String s24877 = "24877";
+ public static final String s24878 = "24878";
+ public static final String s24879 = "24879";
+ public static final String s24880 = "24880";
+ public static final String s24881 = "24881";
+ public static final String s24882 = "24882";
+ public static final String s24883 = "24883";
+ public static final String s24884 = "24884";
+ public static final String s24885 = "24885";
+ public static final String s24886 = "24886";
+ public static final String s24887 = "24887";
+ public static final String s24888 = "24888";
+ public static final String s24889 = "24889";
+ public static final String s24890 = "24890";
+ public static final String s24891 = "24891";
+ public static final String s24892 = "24892";
+ public static final String s24893 = "24893";
+ public static final String s24894 = "24894";
+ public static final String s24895 = "24895";
+ public static final String s24896 = "24896";
+ public static final String s24897 = "24897";
+ public static final String s24898 = "24898";
+ public static final String s24899 = "24899";
+ public static final String s24900 = "24900";
+ public static final String s24901 = "24901";
+ public static final String s24902 = "24902";
+ public static final String s24903 = "24903";
+ public static final String s24904 = "24904";
+ public static final String s24905 = "24905";
+ public static final String s24906 = "24906";
+ public static final String s24907 = "24907";
+ public static final String s24908 = "24908";
+ public static final String s24909 = "24909";
+ public static final String s24910 = "24910";
+ public static final String s24911 = "24911";
+ public static final String s24912 = "24912";
+ public static final String s24913 = "24913";
+ public static final String s24914 = "24914";
+ public static final String s24915 = "24915";
+ public static final String s24916 = "24916";
+ public static final String s24917 = "24917";
+ public static final String s24918 = "24918";
+ public static final String s24919 = "24919";
+ public static final String s24920 = "24920";
+ public static final String s24921 = "24921";
+ public static final String s24922 = "24922";
+ public static final String s24923 = "24923";
+ public static final String s24924 = "24924";
+ public static final String s24925 = "24925";
+ public static final String s24926 = "24926";
+ public static final String s24927 = "24927";
+ public static final String s24928 = "24928";
+ public static final String s24929 = "24929";
+ public static final String s24930 = "24930";
+ public static final String s24931 = "24931";
+ public static final String s24932 = "24932";
+ public static final String s24933 = "24933";
+ public static final String s24934 = "24934";
+ public static final String s24935 = "24935";
+ public static final String s24936 = "24936";
+ public static final String s24937 = "24937";
+ public static final String s24938 = "24938";
+ public static final String s24939 = "24939";
+ public static final String s24940 = "24940";
+ public static final String s24941 = "24941";
+ public static final String s24942 = "24942";
+ public static final String s24943 = "24943";
+ public static final String s24944 = "24944";
+ public static final String s24945 = "24945";
+ public static final String s24946 = "24946";
+ public static final String s24947 = "24947";
+ public static final String s24948 = "24948";
+ public static final String s24949 = "24949";
+ public static final String s24950 = "24950";
+ public static final String s24951 = "24951";
+ public static final String s24952 = "24952";
+ public static final String s24953 = "24953";
+ public static final String s24954 = "24954";
+ public static final String s24955 = "24955";
+ public static final String s24956 = "24956";
+ public static final String s24957 = "24957";
+ public static final String s24958 = "24958";
+ public static final String s24959 = "24959";
+ public static final String s24960 = "24960";
+ public static final String s24961 = "24961";
+ public static final String s24962 = "24962";
+ public static final String s24963 = "24963";
+ public static final String s24964 = "24964";
+ public static final String s24965 = "24965";
+ public static final String s24966 = "24966";
+ public static final String s24967 = "24967";
+ public static final String s24968 = "24968";
+ public static final String s24969 = "24969";
+ public static final String s24970 = "24970";
+ public static final String s24971 = "24971";
+ public static final String s24972 = "24972";
+ public static final String s24973 = "24973";
+ public static final String s24974 = "24974";
+ public static final String s24975 = "24975";
+ public static final String s24976 = "24976";
+ public static final String s24977 = "24977";
+ public static final String s24978 = "24978";
+ public static final String s24979 = "24979";
+ public static final String s24980 = "24980";
+ public static final String s24981 = "24981";
+ public static final String s24982 = "24982";
+ public static final String s24983 = "24983";
+ public static final String s24984 = "24984";
+ public static final String s24985 = "24985";
+ public static final String s24986 = "24986";
+ public static final String s24987 = "24987";
+ public static final String s24988 = "24988";
+ public static final String s24989 = "24989";
+ public static final String s24990 = "24990";
+ public static final String s24991 = "24991";
+ public static final String s24992 = "24992";
+ public static final String s24993 = "24993";
+ public static final String s24994 = "24994";
+ public static final String s24995 = "24995";
+ public static final String s24996 = "24996";
+ public static final String s24997 = "24997";
+ public static final String s24998 = "24998";
+ public static final String s24999 = "24999";
+ public static final String s25000 = "25000";
+ public static final String s25001 = "25001";
+ public static final String s25002 = "25002";
+ public static final String s25003 = "25003";
+ public static final String s25004 = "25004";
+ public static final String s25005 = "25005";
+ public static final String s25006 = "25006";
+ public static final String s25007 = "25007";
+ public static final String s25008 = "25008";
+ public static final String s25009 = "25009";
+ public static final String s25010 = "25010";
+ public static final String s25011 = "25011";
+ public static final String s25012 = "25012";
+ public static final String s25013 = "25013";
+ public static final String s25014 = "25014";
+ public static final String s25015 = "25015";
+ public static final String s25016 = "25016";
+ public static final String s25017 = "25017";
+ public static final String s25018 = "25018";
+ public static final String s25019 = "25019";
+ public static final String s25020 = "25020";
+ public static final String s25021 = "25021";
+ public static final String s25022 = "25022";
+ public static final String s25023 = "25023";
+ public static final String s25024 = "25024";
+ public static final String s25025 = "25025";
+ public static final String s25026 = "25026";
+ public static final String s25027 = "25027";
+ public static final String s25028 = "25028";
+ public static final String s25029 = "25029";
+ public static final String s25030 = "25030";
+ public static final String s25031 = "25031";
+ public static final String s25032 = "25032";
+ public static final String s25033 = "25033";
+ public static final String s25034 = "25034";
+ public static final String s25035 = "25035";
+ public static final String s25036 = "25036";
+ public static final String s25037 = "25037";
+ public static final String s25038 = "25038";
+ public static final String s25039 = "25039";
+ public static final String s25040 = "25040";
+ public static final String s25041 = "25041";
+ public static final String s25042 = "25042";
+ public static final String s25043 = "25043";
+ public static final String s25044 = "25044";
+ public static final String s25045 = "25045";
+ public static final String s25046 = "25046";
+ public static final String s25047 = "25047";
+ public static final String s25048 = "25048";
+ public static final String s25049 = "25049";
+ public static final String s25050 = "25050";
+ public static final String s25051 = "25051";
+ public static final String s25052 = "25052";
+ public static final String s25053 = "25053";
+ public static final String s25054 = "25054";
+ public static final String s25055 = "25055";
+ public static final String s25056 = "25056";
+ public static final String s25057 = "25057";
+ public static final String s25058 = "25058";
+ public static final String s25059 = "25059";
+ public static final String s25060 = "25060";
+ public static final String s25061 = "25061";
+ public static final String s25062 = "25062";
+ public static final String s25063 = "25063";
+ public static final String s25064 = "25064";
+ public static final String s25065 = "25065";
+ public static final String s25066 = "25066";
+ public static final String s25067 = "25067";
+ public static final String s25068 = "25068";
+ public static final String s25069 = "25069";
+ public static final String s25070 = "25070";
+ public static final String s25071 = "25071";
+ public static final String s25072 = "25072";
+ public static final String s25073 = "25073";
+ public static final String s25074 = "25074";
+ public static final String s25075 = "25075";
+ public static final String s25076 = "25076";
+ public static final String s25077 = "25077";
+ public static final String s25078 = "25078";
+ public static final String s25079 = "25079";
+ public static final String s25080 = "25080";
+ public static final String s25081 = "25081";
+ public static final String s25082 = "25082";
+ public static final String s25083 = "25083";
+ public static final String s25084 = "25084";
+ public static final String s25085 = "25085";
+ public static final String s25086 = "25086";
+ public static final String s25087 = "25087";
+ public static final String s25088 = "25088";
+ public static final String s25089 = "25089";
+ public static final String s25090 = "25090";
+ public static final String s25091 = "25091";
+ public static final String s25092 = "25092";
+ public static final String s25093 = "25093";
+ public static final String s25094 = "25094";
+ public static final String s25095 = "25095";
+ public static final String s25096 = "25096";
+ public static final String s25097 = "25097";
+ public static final String s25098 = "25098";
+ public static final String s25099 = "25099";
+ public static final String s25100 = "25100";
+ public static final String s25101 = "25101";
+ public static final String s25102 = "25102";
+ public static final String s25103 = "25103";
+ public static final String s25104 = "25104";
+ public static final String s25105 = "25105";
+ public static final String s25106 = "25106";
+ public static final String s25107 = "25107";
+ public static final String s25108 = "25108";
+ public static final String s25109 = "25109";
+ public static final String s25110 = "25110";
+ public static final String s25111 = "25111";
+ public static final String s25112 = "25112";
+ public static final String s25113 = "25113";
+ public static final String s25114 = "25114";
+ public static final String s25115 = "25115";
+ public static final String s25116 = "25116";
+ public static final String s25117 = "25117";
+ public static final String s25118 = "25118";
+ public static final String s25119 = "25119";
+ public static final String s25120 = "25120";
+ public static final String s25121 = "25121";
+ public static final String s25122 = "25122";
+ public static final String s25123 = "25123";
+ public static final String s25124 = "25124";
+ public static final String s25125 = "25125";
+ public static final String s25126 = "25126";
+ public static final String s25127 = "25127";
+ public static final String s25128 = "25128";
+ public static final String s25129 = "25129";
+ public static final String s25130 = "25130";
+ public static final String s25131 = "25131";
+ public static final String s25132 = "25132";
+ public static final String s25133 = "25133";
+ public static final String s25134 = "25134";
+ public static final String s25135 = "25135";
+ public static final String s25136 = "25136";
+ public static final String s25137 = "25137";
+ public static final String s25138 = "25138";
+ public static final String s25139 = "25139";
+ public static final String s25140 = "25140";
+ public static final String s25141 = "25141";
+ public static final String s25142 = "25142";
+ public static final String s25143 = "25143";
+ public static final String s25144 = "25144";
+ public static final String s25145 = "25145";
+ public static final String s25146 = "25146";
+ public static final String s25147 = "25147";
+ public static final String s25148 = "25148";
+ public static final String s25149 = "25149";
+ public static final String s25150 = "25150";
+ public static final String s25151 = "25151";
+ public static final String s25152 = "25152";
+ public static final String s25153 = "25153";
+ public static final String s25154 = "25154";
+ public static final String s25155 = "25155";
+ public static final String s25156 = "25156";
+ public static final String s25157 = "25157";
+ public static final String s25158 = "25158";
+ public static final String s25159 = "25159";
+ public static final String s25160 = "25160";
+ public static final String s25161 = "25161";
+ public static final String s25162 = "25162";
+ public static final String s25163 = "25163";
+ public static final String s25164 = "25164";
+ public static final String s25165 = "25165";
+ public static final String s25166 = "25166";
+ public static final String s25167 = "25167";
+ public static final String s25168 = "25168";
+ public static final String s25169 = "25169";
+ public static final String s25170 = "25170";
+ public static final String s25171 = "25171";
+ public static final String s25172 = "25172";
+ public static final String s25173 = "25173";
+ public static final String s25174 = "25174";
+ public static final String s25175 = "25175";
+ public static final String s25176 = "25176";
+ public static final String s25177 = "25177";
+ public static final String s25178 = "25178";
+ public static final String s25179 = "25179";
+ public static final String s25180 = "25180";
+ public static final String s25181 = "25181";
+ public static final String s25182 = "25182";
+ public static final String s25183 = "25183";
+ public static final String s25184 = "25184";
+ public static final String s25185 = "25185";
+ public static final String s25186 = "25186";
+ public static final String s25187 = "25187";
+ public static final String s25188 = "25188";
+ public static final String s25189 = "25189";
+ public static final String s25190 = "25190";
+ public static final String s25191 = "25191";
+ public static final String s25192 = "25192";
+ public static final String s25193 = "25193";
+ public static final String s25194 = "25194";
+ public static final String s25195 = "25195";
+ public static final String s25196 = "25196";
+ public static final String s25197 = "25197";
+ public static final String s25198 = "25198";
+ public static final String s25199 = "25199";
+ public static final String s25200 = "25200";
+ public static final String s25201 = "25201";
+ public static final String s25202 = "25202";
+ public static final String s25203 = "25203";
+ public static final String s25204 = "25204";
+ public static final String s25205 = "25205";
+ public static final String s25206 = "25206";
+ public static final String s25207 = "25207";
+ public static final String s25208 = "25208";
+ public static final String s25209 = "25209";
+ public static final String s25210 = "25210";
+ public static final String s25211 = "25211";
+ public static final String s25212 = "25212";
+ public static final String s25213 = "25213";
+ public static final String s25214 = "25214";
+ public static final String s25215 = "25215";
+ public static final String s25216 = "25216";
+ public static final String s25217 = "25217";
+ public static final String s25218 = "25218";
+ public static final String s25219 = "25219";
+ public static final String s25220 = "25220";
+ public static final String s25221 = "25221";
+ public static final String s25222 = "25222";
+ public static final String s25223 = "25223";
+ public static final String s25224 = "25224";
+ public static final String s25225 = "25225";
+ public static final String s25226 = "25226";
+ public static final String s25227 = "25227";
+ public static final String s25228 = "25228";
+ public static final String s25229 = "25229";
+ public static final String s25230 = "25230";
+ public static final String s25231 = "25231";
+ public static final String s25232 = "25232";
+ public static final String s25233 = "25233";
+ public static final String s25234 = "25234";
+ public static final String s25235 = "25235";
+ public static final String s25236 = "25236";
+ public static final String s25237 = "25237";
+ public static final String s25238 = "25238";
+ public static final String s25239 = "25239";
+ public static final String s25240 = "25240";
+ public static final String s25241 = "25241";
+ public static final String s25242 = "25242";
+ public static final String s25243 = "25243";
+ public static final String s25244 = "25244";
+ public static final String s25245 = "25245";
+ public static final String s25246 = "25246";
+ public static final String s25247 = "25247";
+ public static final String s25248 = "25248";
+ public static final String s25249 = "25249";
+ public static final String s25250 = "25250";
+ public static final String s25251 = "25251";
+ public static final String s25252 = "25252";
+ public static final String s25253 = "25253";
+ public static final String s25254 = "25254";
+ public static final String s25255 = "25255";
+ public static final String s25256 = "25256";
+ public static final String s25257 = "25257";
+ public static final String s25258 = "25258";
+ public static final String s25259 = "25259";
+ public static final String s25260 = "25260";
+ public static final String s25261 = "25261";
+ public static final String s25262 = "25262";
+ public static final String s25263 = "25263";
+ public static final String s25264 = "25264";
+ public static final String s25265 = "25265";
+ public static final String s25266 = "25266";
+ public static final String s25267 = "25267";
+ public static final String s25268 = "25268";
+ public static final String s25269 = "25269";
+ public static final String s25270 = "25270";
+ public static final String s25271 = "25271";
+ public static final String s25272 = "25272";
+ public static final String s25273 = "25273";
+ public static final String s25274 = "25274";
+ public static final String s25275 = "25275";
+ public static final String s25276 = "25276";
+ public static final String s25277 = "25277";
+ public static final String s25278 = "25278";
+ public static final String s25279 = "25279";
+ public static final String s25280 = "25280";
+ public static final String s25281 = "25281";
+ public static final String s25282 = "25282";
+ public static final String s25283 = "25283";
+ public static final String s25284 = "25284";
+ public static final String s25285 = "25285";
+ public static final String s25286 = "25286";
+ public static final String s25287 = "25287";
+ public static final String s25288 = "25288";
+ public static final String s25289 = "25289";
+ public static final String s25290 = "25290";
+ public static final String s25291 = "25291";
+ public static final String s25292 = "25292";
+ public static final String s25293 = "25293";
+ public static final String s25294 = "25294";
+ public static final String s25295 = "25295";
+ public static final String s25296 = "25296";
+ public static final String s25297 = "25297";
+ public static final String s25298 = "25298";
+ public static final String s25299 = "25299";
+ public static final String s25300 = "25300";
+ public static final String s25301 = "25301";
+ public static final String s25302 = "25302";
+ public static final String s25303 = "25303";
+ public static final String s25304 = "25304";
+ public static final String s25305 = "25305";
+ public static final String s25306 = "25306";
+ public static final String s25307 = "25307";
+ public static final String s25308 = "25308";
+ public static final String s25309 = "25309";
+ public static final String s25310 = "25310";
+ public static final String s25311 = "25311";
+ public static final String s25312 = "25312";
+ public static final String s25313 = "25313";
+ public static final String s25314 = "25314";
+ public static final String s25315 = "25315";
+ public static final String s25316 = "25316";
+ public static final String s25317 = "25317";
+ public static final String s25318 = "25318";
+ public static final String s25319 = "25319";
+ public static final String s25320 = "25320";
+ public static final String s25321 = "25321";
+ public static final String s25322 = "25322";
+ public static final String s25323 = "25323";
+ public static final String s25324 = "25324";
+ public static final String s25325 = "25325";
+ public static final String s25326 = "25326";
+ public static final String s25327 = "25327";
+ public static final String s25328 = "25328";
+ public static final String s25329 = "25329";
+ public static final String s25330 = "25330";
+ public static final String s25331 = "25331";
+ public static final String s25332 = "25332";
+ public static final String s25333 = "25333";
+ public static final String s25334 = "25334";
+ public static final String s25335 = "25335";
+ public static final String s25336 = "25336";
+ public static final String s25337 = "25337";
+ public static final String s25338 = "25338";
+ public static final String s25339 = "25339";
+ public static final String s25340 = "25340";
+ public static final String s25341 = "25341";
+ public static final String s25342 = "25342";
+ public static final String s25343 = "25343";
+ public static final String s25344 = "25344";
+ public static final String s25345 = "25345";
+ public static final String s25346 = "25346";
+ public static final String s25347 = "25347";
+ public static final String s25348 = "25348";
+ public static final String s25349 = "25349";
+ public static final String s25350 = "25350";
+ public static final String s25351 = "25351";
+ public static final String s25352 = "25352";
+ public static final String s25353 = "25353";
+ public static final String s25354 = "25354";
+ public static final String s25355 = "25355";
+ public static final String s25356 = "25356";
+ public static final String s25357 = "25357";
+ public static final String s25358 = "25358";
+ public static final String s25359 = "25359";
+ public static final String s25360 = "25360";
+ public static final String s25361 = "25361";
+ public static final String s25362 = "25362";
+ public static final String s25363 = "25363";
+ public static final String s25364 = "25364";
+ public static final String s25365 = "25365";
+ public static final String s25366 = "25366";
+ public static final String s25367 = "25367";
+ public static final String s25368 = "25368";
+ public static final String s25369 = "25369";
+ public static final String s25370 = "25370";
+ public static final String s25371 = "25371";
+ public static final String s25372 = "25372";
+ public static final String s25373 = "25373";
+ public static final String s25374 = "25374";
+ public static final String s25375 = "25375";
+ public static final String s25376 = "25376";
+ public static final String s25377 = "25377";
+ public static final String s25378 = "25378";
+ public static final String s25379 = "25379";
+ public static final String s25380 = "25380";
+ public static final String s25381 = "25381";
+ public static final String s25382 = "25382";
+ public static final String s25383 = "25383";
+ public static final String s25384 = "25384";
+ public static final String s25385 = "25385";
+ public static final String s25386 = "25386";
+ public static final String s25387 = "25387";
+ public static final String s25388 = "25388";
+ public static final String s25389 = "25389";
+ public static final String s25390 = "25390";
+ public static final String s25391 = "25391";
+ public static final String s25392 = "25392";
+ public static final String s25393 = "25393";
+ public static final String s25394 = "25394";
+ public static final String s25395 = "25395";
+ public static final String s25396 = "25396";
+ public static final String s25397 = "25397";
+ public static final String s25398 = "25398";
+ public static final String s25399 = "25399";
+ public static final String s25400 = "25400";
+ public static final String s25401 = "25401";
+ public static final String s25402 = "25402";
+ public static final String s25403 = "25403";
+ public static final String s25404 = "25404";
+ public static final String s25405 = "25405";
+ public static final String s25406 = "25406";
+ public static final String s25407 = "25407";
+ public static final String s25408 = "25408";
+ public static final String s25409 = "25409";
+ public static final String s25410 = "25410";
+ public static final String s25411 = "25411";
+ public static final String s25412 = "25412";
+ public static final String s25413 = "25413";
+ public static final String s25414 = "25414";
+ public static final String s25415 = "25415";
+ public static final String s25416 = "25416";
+ public static final String s25417 = "25417";
+ public static final String s25418 = "25418";
+ public static final String s25419 = "25419";
+ public static final String s25420 = "25420";
+ public static final String s25421 = "25421";
+ public static final String s25422 = "25422";
+ public static final String s25423 = "25423";
+ public static final String s25424 = "25424";
+ public static final String s25425 = "25425";
+ public static final String s25426 = "25426";
+ public static final String s25427 = "25427";
+ public static final String s25428 = "25428";
+ public static final String s25429 = "25429";
+ public static final String s25430 = "25430";
+ public static final String s25431 = "25431";
+ public static final String s25432 = "25432";
+ public static final String s25433 = "25433";
+ public static final String s25434 = "25434";
+ public static final String s25435 = "25435";
+ public static final String s25436 = "25436";
+ public static final String s25437 = "25437";
+ public static final String s25438 = "25438";
+ public static final String s25439 = "25439";
+ public static final String s25440 = "25440";
+ public static final String s25441 = "25441";
+ public static final String s25442 = "25442";
+ public static final String s25443 = "25443";
+ public static final String s25444 = "25444";
+ public static final String s25445 = "25445";
+ public static final String s25446 = "25446";
+ public static final String s25447 = "25447";
+ public static final String s25448 = "25448";
+ public static final String s25449 = "25449";
+ public static final String s25450 = "25450";
+ public static final String s25451 = "25451";
+ public static final String s25452 = "25452";
+ public static final String s25453 = "25453";
+ public static final String s25454 = "25454";
+ public static final String s25455 = "25455";
+ public static final String s25456 = "25456";
+ public static final String s25457 = "25457";
+ public static final String s25458 = "25458";
+ public static final String s25459 = "25459";
+ public static final String s25460 = "25460";
+ public static final String s25461 = "25461";
+ public static final String s25462 = "25462";
+ public static final String s25463 = "25463";
+ public static final String s25464 = "25464";
+ public static final String s25465 = "25465";
+ public static final String s25466 = "25466";
+ public static final String s25467 = "25467";
+ public static final String s25468 = "25468";
+ public static final String s25469 = "25469";
+ public static final String s25470 = "25470";
+ public static final String s25471 = "25471";
+ public static final String s25472 = "25472";
+ public static final String s25473 = "25473";
+ public static final String s25474 = "25474";
+ public static final String s25475 = "25475";
+ public static final String s25476 = "25476";
+ public static final String s25477 = "25477";
+ public static final String s25478 = "25478";
+ public static final String s25479 = "25479";
+ public static final String s25480 = "25480";
+ public static final String s25481 = "25481";
+ public static final String s25482 = "25482";
+ public static final String s25483 = "25483";
+ public static final String s25484 = "25484";
+ public static final String s25485 = "25485";
+ public static final String s25486 = "25486";
+ public static final String s25487 = "25487";
+ public static final String s25488 = "25488";
+ public static final String s25489 = "25489";
+ public static final String s25490 = "25490";
+ public static final String s25491 = "25491";
+ public static final String s25492 = "25492";
+ public static final String s25493 = "25493";
+ public static final String s25494 = "25494";
+ public static final String s25495 = "25495";
+ public static final String s25496 = "25496";
+ public static final String s25497 = "25497";
+ public static final String s25498 = "25498";
+ public static final String s25499 = "25499";
+ public static final String s25500 = "25500";
+ public static final String s25501 = "25501";
+ public static final String s25502 = "25502";
+ public static final String s25503 = "25503";
+ public static final String s25504 = "25504";
+ public static final String s25505 = "25505";
+ public static final String s25506 = "25506";
+ public static final String s25507 = "25507";
+ public static final String s25508 = "25508";
+ public static final String s25509 = "25509";
+ public static final String s25510 = "25510";
+ public static final String s25511 = "25511";
+ public static final String s25512 = "25512";
+ public static final String s25513 = "25513";
+ public static final String s25514 = "25514";
+ public static final String s25515 = "25515";
+ public static final String s25516 = "25516";
+ public static final String s25517 = "25517";
+ public static final String s25518 = "25518";
+ public static final String s25519 = "25519";
+ public static final String s25520 = "25520";
+ public static final String s25521 = "25521";
+ public static final String s25522 = "25522";
+ public static final String s25523 = "25523";
+ public static final String s25524 = "25524";
+ public static final String s25525 = "25525";
+ public static final String s25526 = "25526";
+ public static final String s25527 = "25527";
+ public static final String s25528 = "25528";
+ public static final String s25529 = "25529";
+ public static final String s25530 = "25530";
+ public static final String s25531 = "25531";
+ public static final String s25532 = "25532";
+ public static final String s25533 = "25533";
+ public static final String s25534 = "25534";
+ public static final String s25535 = "25535";
+ public static final String s25536 = "25536";
+ public static final String s25537 = "25537";
+ public static final String s25538 = "25538";
+ public static final String s25539 = "25539";
+ public static final String s25540 = "25540";
+ public static final String s25541 = "25541";
+ public static final String s25542 = "25542";
+ public static final String s25543 = "25543";
+ public static final String s25544 = "25544";
+ public static final String s25545 = "25545";
+ public static final String s25546 = "25546";
+ public static final String s25547 = "25547";
+ public static final String s25548 = "25548";
+ public static final String s25549 = "25549";
+ public static final String s25550 = "25550";
+ public static final String s25551 = "25551";
+ public static final String s25552 = "25552";
+ public static final String s25553 = "25553";
+ public static final String s25554 = "25554";
+ public static final String s25555 = "25555";
+ public static final String s25556 = "25556";
+ public static final String s25557 = "25557";
+ public static final String s25558 = "25558";
+ public static final String s25559 = "25559";
+ public static final String s25560 = "25560";
+ public static final String s25561 = "25561";
+ public static final String s25562 = "25562";
+ public static final String s25563 = "25563";
+ public static final String s25564 = "25564";
+ public static final String s25565 = "25565";
+ public static final String s25566 = "25566";
+ public static final String s25567 = "25567";
+ public static final String s25568 = "25568";
+ public static final String s25569 = "25569";
+ public static final String s25570 = "25570";
+ public static final String s25571 = "25571";
+ public static final String s25572 = "25572";
+ public static final String s25573 = "25573";
+ public static final String s25574 = "25574";
+ public static final String s25575 = "25575";
+ public static final String s25576 = "25576";
+ public static final String s25577 = "25577";
+ public static final String s25578 = "25578";
+ public static final String s25579 = "25579";
+ public static final String s25580 = "25580";
+ public static final String s25581 = "25581";
+ public static final String s25582 = "25582";
+ public static final String s25583 = "25583";
+ public static final String s25584 = "25584";
+ public static final String s25585 = "25585";
+ public static final String s25586 = "25586";
+ public static final String s25587 = "25587";
+ public static final String s25588 = "25588";
+ public static final String s25589 = "25589";
+ public static final String s25590 = "25590";
+ public static final String s25591 = "25591";
+ public static final String s25592 = "25592";
+ public static final String s25593 = "25593";
+ public static final String s25594 = "25594";
+ public static final String s25595 = "25595";
+ public static final String s25596 = "25596";
+ public static final String s25597 = "25597";
+ public static final String s25598 = "25598";
+ public static final String s25599 = "25599";
+ public static final String s25600 = "25600";
+ public static final String s25601 = "25601";
+ public static final String s25602 = "25602";
+ public static final String s25603 = "25603";
+ public static final String s25604 = "25604";
+ public static final String s25605 = "25605";
+ public static final String s25606 = "25606";
+ public static final String s25607 = "25607";
+ public static final String s25608 = "25608";
+ public static final String s25609 = "25609";
+ public static final String s25610 = "25610";
+ public static final String s25611 = "25611";
+ public static final String s25612 = "25612";
+ public static final String s25613 = "25613";
+ public static final String s25614 = "25614";
+ public static final String s25615 = "25615";
+ public static final String s25616 = "25616";
+ public static final String s25617 = "25617";
+ public static final String s25618 = "25618";
+ public static final String s25619 = "25619";
+ public static final String s25620 = "25620";
+ public static final String s25621 = "25621";
+ public static final String s25622 = "25622";
+ public static final String s25623 = "25623";
+ public static final String s25624 = "25624";
+ public static final String s25625 = "25625";
+ public static final String s25626 = "25626";
+ public static final String s25627 = "25627";
+ public static final String s25628 = "25628";
+ public static final String s25629 = "25629";
+ public static final String s25630 = "25630";
+ public static final String s25631 = "25631";
+ public static final String s25632 = "25632";
+ public static final String s25633 = "25633";
+ public static final String s25634 = "25634";
+ public static final String s25635 = "25635";
+ public static final String s25636 = "25636";
+ public static final String s25637 = "25637";
+ public static final String s25638 = "25638";
+ public static final String s25639 = "25639";
+ public static final String s25640 = "25640";
+ public static final String s25641 = "25641";
+ public static final String s25642 = "25642";
+ public static final String s25643 = "25643";
+ public static final String s25644 = "25644";
+ public static final String s25645 = "25645";
+ public static final String s25646 = "25646";
+ public static final String s25647 = "25647";
+ public static final String s25648 = "25648";
+ public static final String s25649 = "25649";
+ public static final String s25650 = "25650";
+ public static final String s25651 = "25651";
+ public static final String s25652 = "25652";
+ public static final String s25653 = "25653";
+ public static final String s25654 = "25654";
+ public static final String s25655 = "25655";
+ public static final String s25656 = "25656";
+ public static final String s25657 = "25657";
+ public static final String s25658 = "25658";
+ public static final String s25659 = "25659";
+ public static final String s25660 = "25660";
+ public static final String s25661 = "25661";
+ public static final String s25662 = "25662";
+ public static final String s25663 = "25663";
+ public static final String s25664 = "25664";
+ public static final String s25665 = "25665";
+ public static final String s25666 = "25666";
+ public static final String s25667 = "25667";
+ public static final String s25668 = "25668";
+ public static final String s25669 = "25669";
+ public static final String s25670 = "25670";
+ public static final String s25671 = "25671";
+ public static final String s25672 = "25672";
+ public static final String s25673 = "25673";
+ public static final String s25674 = "25674";
+ public static final String s25675 = "25675";
+ public static final String s25676 = "25676";
+ public static final String s25677 = "25677";
+ public static final String s25678 = "25678";
+ public static final String s25679 = "25679";
+ public static final String s25680 = "25680";
+ public static final String s25681 = "25681";
+ public static final String s25682 = "25682";
+ public static final String s25683 = "25683";
+ public static final String s25684 = "25684";
+ public static final String s25685 = "25685";
+ public static final String s25686 = "25686";
+ public static final String s25687 = "25687";
+ public static final String s25688 = "25688";
+ public static final String s25689 = "25689";
+ public static final String s25690 = "25690";
+ public static final String s25691 = "25691";
+ public static final String s25692 = "25692";
+ public static final String s25693 = "25693";
+ public static final String s25694 = "25694";
+ public static final String s25695 = "25695";
+ public static final String s25696 = "25696";
+ public static final String s25697 = "25697";
+ public static final String s25698 = "25698";
+ public static final String s25699 = "25699";
+ public static final String s25700 = "25700";
+ public static final String s25701 = "25701";
+ public static final String s25702 = "25702";
+ public static final String s25703 = "25703";
+ public static final String s25704 = "25704";
+ public static final String s25705 = "25705";
+ public static final String s25706 = "25706";
+ public static final String s25707 = "25707";
+ public static final String s25708 = "25708";
+ public static final String s25709 = "25709";
+ public static final String s25710 = "25710";
+ public static final String s25711 = "25711";
+ public static final String s25712 = "25712";
+ public static final String s25713 = "25713";
+ public static final String s25714 = "25714";
+ public static final String s25715 = "25715";
+ public static final String s25716 = "25716";
+ public static final String s25717 = "25717";
+ public static final String s25718 = "25718";
+ public static final String s25719 = "25719";
+ public static final String s25720 = "25720";
+ public static final String s25721 = "25721";
+ public static final String s25722 = "25722";
+ public static final String s25723 = "25723";
+ public static final String s25724 = "25724";
+ public static final String s25725 = "25725";
+ public static final String s25726 = "25726";
+ public static final String s25727 = "25727";
+ public static final String s25728 = "25728";
+ public static final String s25729 = "25729";
+ public static final String s25730 = "25730";
+ public static final String s25731 = "25731";
+ public static final String s25732 = "25732";
+ public static final String s25733 = "25733";
+ public static final String s25734 = "25734";
+ public static final String s25735 = "25735";
+ public static final String s25736 = "25736";
+ public static final String s25737 = "25737";
+ public static final String s25738 = "25738";
+ public static final String s25739 = "25739";
+ public static final String s25740 = "25740";
+ public static final String s25741 = "25741";
+ public static final String s25742 = "25742";
+ public static final String s25743 = "25743";
+ public static final String s25744 = "25744";
+ public static final String s25745 = "25745";
+ public static final String s25746 = "25746";
+ public static final String s25747 = "25747";
+ public static final String s25748 = "25748";
+ public static final String s25749 = "25749";
+ public static final String s25750 = "25750";
+ public static final String s25751 = "25751";
+ public static final String s25752 = "25752";
+ public static final String s25753 = "25753";
+ public static final String s25754 = "25754";
+ public static final String s25755 = "25755";
+ public static final String s25756 = "25756";
+ public static final String s25757 = "25757";
+ public static final String s25758 = "25758";
+ public static final String s25759 = "25759";
+ public static final String s25760 = "25760";
+ public static final String s25761 = "25761";
+ public static final String s25762 = "25762";
+ public static final String s25763 = "25763";
+ public static final String s25764 = "25764";
+ public static final String s25765 = "25765";
+ public static final String s25766 = "25766";
+ public static final String s25767 = "25767";
+ public static final String s25768 = "25768";
+ public static final String s25769 = "25769";
+ public static final String s25770 = "25770";
+ public static final String s25771 = "25771";
+ public static final String s25772 = "25772";
+ public static final String s25773 = "25773";
+ public static final String s25774 = "25774";
+ public static final String s25775 = "25775";
+ public static final String s25776 = "25776";
+ public static final String s25777 = "25777";
+ public static final String s25778 = "25778";
+ public static final String s25779 = "25779";
+ public static final String s25780 = "25780";
+ public static final String s25781 = "25781";
+ public static final String s25782 = "25782";
+ public static final String s25783 = "25783";
+ public static final String s25784 = "25784";
+ public static final String s25785 = "25785";
+ public static final String s25786 = "25786";
+ public static final String s25787 = "25787";
+ public static final String s25788 = "25788";
+ public static final String s25789 = "25789";
+ public static final String s25790 = "25790";
+ public static final String s25791 = "25791";
+ public static final String s25792 = "25792";
+ public static final String s25793 = "25793";
+ public static final String s25794 = "25794";
+ public static final String s25795 = "25795";
+ public static final String s25796 = "25796";
+ public static final String s25797 = "25797";
+ public static final String s25798 = "25798";
+ public static final String s25799 = "25799";
+ public static final String s25800 = "25800";
+ public static final String s25801 = "25801";
+ public static final String s25802 = "25802";
+ public static final String s25803 = "25803";
+ public static final String s25804 = "25804";
+ public static final String s25805 = "25805";
+ public static final String s25806 = "25806";
+ public static final String s25807 = "25807";
+ public static final String s25808 = "25808";
+ public static final String s25809 = "25809";
+ public static final String s25810 = "25810";
+ public static final String s25811 = "25811";
+ public static final String s25812 = "25812";
+ public static final String s25813 = "25813";
+ public static final String s25814 = "25814";
+ public static final String s25815 = "25815";
+ public static final String s25816 = "25816";
+ public static final String s25817 = "25817";
+ public static final String s25818 = "25818";
+ public static final String s25819 = "25819";
+ public static final String s25820 = "25820";
+ public static final String s25821 = "25821";
+ public static final String s25822 = "25822";
+ public static final String s25823 = "25823";
+ public static final String s25824 = "25824";
+ public static final String s25825 = "25825";
+ public static final String s25826 = "25826";
+ public static final String s25827 = "25827";
+ public static final String s25828 = "25828";
+ public static final String s25829 = "25829";
+ public static final String s25830 = "25830";
+ public static final String s25831 = "25831";
+ public static final String s25832 = "25832";
+ public static final String s25833 = "25833";
+ public static final String s25834 = "25834";
+ public static final String s25835 = "25835";
+ public static final String s25836 = "25836";
+ public static final String s25837 = "25837";
+ public static final String s25838 = "25838";
+ public static final String s25839 = "25839";
+ public static final String s25840 = "25840";
+ public static final String s25841 = "25841";
+ public static final String s25842 = "25842";
+ public static final String s25843 = "25843";
+ public static final String s25844 = "25844";
+ public static final String s25845 = "25845";
+ public static final String s25846 = "25846";
+ public static final String s25847 = "25847";
+ public static final String s25848 = "25848";
+ public static final String s25849 = "25849";
+ public static final String s25850 = "25850";
+ public static final String s25851 = "25851";
+ public static final String s25852 = "25852";
+ public static final String s25853 = "25853";
+ public static final String s25854 = "25854";
+ public static final String s25855 = "25855";
+ public static final String s25856 = "25856";
+ public static final String s25857 = "25857";
+ public static final String s25858 = "25858";
+ public static final String s25859 = "25859";
+ public static final String s25860 = "25860";
+ public static final String s25861 = "25861";
+ public static final String s25862 = "25862";
+ public static final String s25863 = "25863";
+ public static final String s25864 = "25864";
+ public static final String s25865 = "25865";
+ public static final String s25866 = "25866";
+ public static final String s25867 = "25867";
+ public static final String s25868 = "25868";
+ public static final String s25869 = "25869";
+ public static final String s25870 = "25870";
+ public static final String s25871 = "25871";
+ public static final String s25872 = "25872";
+ public static final String s25873 = "25873";
+ public static final String s25874 = "25874";
+ public static final String s25875 = "25875";
+ public static final String s25876 = "25876";
+ public static final String s25877 = "25877";
+ public static final String s25878 = "25878";
+ public static final String s25879 = "25879";
+ public static final String s25880 = "25880";
+ public static final String s25881 = "25881";
+ public static final String s25882 = "25882";
+ public static final String s25883 = "25883";
+ public static final String s25884 = "25884";
+ public static final String s25885 = "25885";
+ public static final String s25886 = "25886";
+ public static final String s25887 = "25887";
+ public static final String s25888 = "25888";
+ public static final String s25889 = "25889";
+ public static final String s25890 = "25890";
+ public static final String s25891 = "25891";
+ public static final String s25892 = "25892";
+ public static final String s25893 = "25893";
+ public static final String s25894 = "25894";
+ public static final String s25895 = "25895";
+ public static final String s25896 = "25896";
+ public static final String s25897 = "25897";
+ public static final String s25898 = "25898";
+ public static final String s25899 = "25899";
+ public static final String s25900 = "25900";
+ public static final String s25901 = "25901";
+ public static final String s25902 = "25902";
+ public static final String s25903 = "25903";
+ public static final String s25904 = "25904";
+ public static final String s25905 = "25905";
+ public static final String s25906 = "25906";
+ public static final String s25907 = "25907";
+ public static final String s25908 = "25908";
+ public static final String s25909 = "25909";
+ public static final String s25910 = "25910";
+ public static final String s25911 = "25911";
+ public static final String s25912 = "25912";
+ public static final String s25913 = "25913";
+ public static final String s25914 = "25914";
+ public static final String s25915 = "25915";
+ public static final String s25916 = "25916";
+ public static final String s25917 = "25917";
+ public static final String s25918 = "25918";
+ public static final String s25919 = "25919";
+ public static final String s25920 = "25920";
+ public static final String s25921 = "25921";
+ public static final String s25922 = "25922";
+ public static final String s25923 = "25923";
+ public static final String s25924 = "25924";
+ public static final String s25925 = "25925";
+ public static final String s25926 = "25926";
+ public static final String s25927 = "25927";
+ public static final String s25928 = "25928";
+ public static final String s25929 = "25929";
+ public static final String s25930 = "25930";
+ public static final String s25931 = "25931";
+ public static final String s25932 = "25932";
+ public static final String s25933 = "25933";
+ public static final String s25934 = "25934";
+ public static final String s25935 = "25935";
+ public static final String s25936 = "25936";
+ public static final String s25937 = "25937";
+ public static final String s25938 = "25938";
+ public static final String s25939 = "25939";
+ public static final String s25940 = "25940";
+ public static final String s25941 = "25941";
+ public static final String s25942 = "25942";
+ public static final String s25943 = "25943";
+ public static final String s25944 = "25944";
+ public static final String s25945 = "25945";
+ public static final String s25946 = "25946";
+ public static final String s25947 = "25947";
+ public static final String s25948 = "25948";
+ public static final String s25949 = "25949";
+ public static final String s25950 = "25950";
+ public static final String s25951 = "25951";
+ public static final String s25952 = "25952";
+ public static final String s25953 = "25953";
+ public static final String s25954 = "25954";
+ public static final String s25955 = "25955";
+ public static final String s25956 = "25956";
+ public static final String s25957 = "25957";
+ public static final String s25958 = "25958";
+ public static final String s25959 = "25959";
+ public static final String s25960 = "25960";
+ public static final String s25961 = "25961";
+ public static final String s25962 = "25962";
+ public static final String s25963 = "25963";
+ public static final String s25964 = "25964";
+ public static final String s25965 = "25965";
+ public static final String s25966 = "25966";
+ public static final String s25967 = "25967";
+ public static final String s25968 = "25968";
+ public static final String s25969 = "25969";
+ public static final String s25970 = "25970";
+ public static final String s25971 = "25971";
+ public static final String s25972 = "25972";
+ public static final String s25973 = "25973";
+ public static final String s25974 = "25974";
+ public static final String s25975 = "25975";
+ public static final String s25976 = "25976";
+ public static final String s25977 = "25977";
+ public static final String s25978 = "25978";
+ public static final String s25979 = "25979";
+ public static final String s25980 = "25980";
+ public static final String s25981 = "25981";
+ public static final String s25982 = "25982";
+ public static final String s25983 = "25983";
+ public static final String s25984 = "25984";
+ public static final String s25985 = "25985";
+ public static final String s25986 = "25986";
+ public static final String s25987 = "25987";
+ public static final String s25988 = "25988";
+ public static final String s25989 = "25989";
+ public static final String s25990 = "25990";
+ public static final String s25991 = "25991";
+ public static final String s25992 = "25992";
+ public static final String s25993 = "25993";
+ public static final String s25994 = "25994";
+ public static final String s25995 = "25995";
+ public static final String s25996 = "25996";
+ public static final String s25997 = "25997";
+ public static final String s25998 = "25998";
+ public static final String s25999 = "25999";
+ public static final String s26000 = "26000";
+ public static final String s26001 = "26001";
+ public static final String s26002 = "26002";
+ public static final String s26003 = "26003";
+ public static final String s26004 = "26004";
+ public static final String s26005 = "26005";
+ public static final String s26006 = "26006";
+ public static final String s26007 = "26007";
+ public static final String s26008 = "26008";
+ public static final String s26009 = "26009";
+ public static final String s26010 = "26010";
+ public static final String s26011 = "26011";
+ public static final String s26012 = "26012";
+ public static final String s26013 = "26013";
+ public static final String s26014 = "26014";
+ public static final String s26015 = "26015";
+ public static final String s26016 = "26016";
+ public static final String s26017 = "26017";
+ public static final String s26018 = "26018";
+ public static final String s26019 = "26019";
+ public static final String s26020 = "26020";
+ public static final String s26021 = "26021";
+ public static final String s26022 = "26022";
+ public static final String s26023 = "26023";
+ public static final String s26024 = "26024";
+ public static final String s26025 = "26025";
+ public static final String s26026 = "26026";
+ public static final String s26027 = "26027";
+ public static final String s26028 = "26028";
+ public static final String s26029 = "26029";
+ public static final String s26030 = "26030";
+ public static final String s26031 = "26031";
+ public static final String s26032 = "26032";
+ public static final String s26033 = "26033";
+ public static final String s26034 = "26034";
+ public static final String s26035 = "26035";
+ public static final String s26036 = "26036";
+ public static final String s26037 = "26037";
+ public static final String s26038 = "26038";
+ public static final String s26039 = "26039";
+ public static final String s26040 = "26040";
+ public static final String s26041 = "26041";
+ public static final String s26042 = "26042";
+ public static final String s26043 = "26043";
+ public static final String s26044 = "26044";
+ public static final String s26045 = "26045";
+ public static final String s26046 = "26046";
+ public static final String s26047 = "26047";
+ public static final String s26048 = "26048";
+ public static final String s26049 = "26049";
+ public static final String s26050 = "26050";
+ public static final String s26051 = "26051";
+ public static final String s26052 = "26052";
+ public static final String s26053 = "26053";
+ public static final String s26054 = "26054";
+ public static final String s26055 = "26055";
+ public static final String s26056 = "26056";
+ public static final String s26057 = "26057";
+ public static final String s26058 = "26058";
+ public static final String s26059 = "26059";
+ public static final String s26060 = "26060";
+ public static final String s26061 = "26061";
+ public static final String s26062 = "26062";
+ public static final String s26063 = "26063";
+ public static final String s26064 = "26064";
+ public static final String s26065 = "26065";
+ public static final String s26066 = "26066";
+ public static final String s26067 = "26067";
+ public static final String s26068 = "26068";
+ public static final String s26069 = "26069";
+ public static final String s26070 = "26070";
+ public static final String s26071 = "26071";
+ public static final String s26072 = "26072";
+ public static final String s26073 = "26073";
+ public static final String s26074 = "26074";
+ public static final String s26075 = "26075";
+ public static final String s26076 = "26076";
+ public static final String s26077 = "26077";
+ public static final String s26078 = "26078";
+ public static final String s26079 = "26079";
+ public static final String s26080 = "26080";
+ public static final String s26081 = "26081";
+ public static final String s26082 = "26082";
+ public static final String s26083 = "26083";
+ public static final String s26084 = "26084";
+ public static final String s26085 = "26085";
+ public static final String s26086 = "26086";
+ public static final String s26087 = "26087";
+ public static final String s26088 = "26088";
+ public static final String s26089 = "26089";
+ public static final String s26090 = "26090";
+ public static final String s26091 = "26091";
+ public static final String s26092 = "26092";
+ public static final String s26093 = "26093";
+ public static final String s26094 = "26094";
+ public static final String s26095 = "26095";
+ public static final String s26096 = "26096";
+ public static final String s26097 = "26097";
+ public static final String s26098 = "26098";
+ public static final String s26099 = "26099";
+ public static final String s26100 = "26100";
+ public static final String s26101 = "26101";
+ public static final String s26102 = "26102";
+ public static final String s26103 = "26103";
+ public static final String s26104 = "26104";
+ public static final String s26105 = "26105";
+ public static final String s26106 = "26106";
+ public static final String s26107 = "26107";
+ public static final String s26108 = "26108";
+ public static final String s26109 = "26109";
+ public static final String s26110 = "26110";
+ public static final String s26111 = "26111";
+ public static final String s26112 = "26112";
+ public static final String s26113 = "26113";
+ public static final String s26114 = "26114";
+ public static final String s26115 = "26115";
+ public static final String s26116 = "26116";
+ public static final String s26117 = "26117";
+ public static final String s26118 = "26118";
+ public static final String s26119 = "26119";
+ public static final String s26120 = "26120";
+ public static final String s26121 = "26121";
+ public static final String s26122 = "26122";
+ public static final String s26123 = "26123";
+ public static final String s26124 = "26124";
+ public static final String s26125 = "26125";
+ public static final String s26126 = "26126";
+ public static final String s26127 = "26127";
+ public static final String s26128 = "26128";
+ public static final String s26129 = "26129";
+ public static final String s26130 = "26130";
+ public static final String s26131 = "26131";
+ public static final String s26132 = "26132";
+ public static final String s26133 = "26133";
+ public static final String s26134 = "26134";
+ public static final String s26135 = "26135";
+ public static final String s26136 = "26136";
+ public static final String s26137 = "26137";
+ public static final String s26138 = "26138";
+ public static final String s26139 = "26139";
+ public static final String s26140 = "26140";
+ public static final String s26141 = "26141";
+ public static final String s26142 = "26142";
+ public static final String s26143 = "26143";
+ public static final String s26144 = "26144";
+ public static final String s26145 = "26145";
+ public static final String s26146 = "26146";
+ public static final String s26147 = "26147";
+ public static final String s26148 = "26148";
+ public static final String s26149 = "26149";
+ public static final String s26150 = "26150";
+ public static final String s26151 = "26151";
+ public static final String s26152 = "26152";
+ public static final String s26153 = "26153";
+ public static final String s26154 = "26154";
+ public static final String s26155 = "26155";
+ public static final String s26156 = "26156";
+ public static final String s26157 = "26157";
+ public static final String s26158 = "26158";
+ public static final String s26159 = "26159";
+ public static final String s26160 = "26160";
+ public static final String s26161 = "26161";
+ public static final String s26162 = "26162";
+ public static final String s26163 = "26163";
+ public static final String s26164 = "26164";
+ public static final String s26165 = "26165";
+ public static final String s26166 = "26166";
+ public static final String s26167 = "26167";
+ public static final String s26168 = "26168";
+ public static final String s26169 = "26169";
+ public static final String s26170 = "26170";
+ public static final String s26171 = "26171";
+ public static final String s26172 = "26172";
+ public static final String s26173 = "26173";
+ public static final String s26174 = "26174";
+ public static final String s26175 = "26175";
+ public static final String s26176 = "26176";
+ public static final String s26177 = "26177";
+ public static final String s26178 = "26178";
+ public static final String s26179 = "26179";
+ public static final String s26180 = "26180";
+ public static final String s26181 = "26181";
+ public static final String s26182 = "26182";
+ public static final String s26183 = "26183";
+ public static final String s26184 = "26184";
+ public static final String s26185 = "26185";
+ public static final String s26186 = "26186";
+ public static final String s26187 = "26187";
+ public static final String s26188 = "26188";
+ public static final String s26189 = "26189";
+ public static final String s26190 = "26190";
+ public static final String s26191 = "26191";
+ public static final String s26192 = "26192";
+ public static final String s26193 = "26193";
+ public static final String s26194 = "26194";
+ public static final String s26195 = "26195";
+ public static final String s26196 = "26196";
+ public static final String s26197 = "26197";
+ public static final String s26198 = "26198";
+ public static final String s26199 = "26199";
+ public static final String s26200 = "26200";
+ public static final String s26201 = "26201";
+ public static final String s26202 = "26202";
+ public static final String s26203 = "26203";
+ public static final String s26204 = "26204";
+ public static final String s26205 = "26205";
+ public static final String s26206 = "26206";
+ public static final String s26207 = "26207";
+ public static final String s26208 = "26208";
+ public static final String s26209 = "26209";
+ public static final String s26210 = "26210";
+ public static final String s26211 = "26211";
+ public static final String s26212 = "26212";
+ public static final String s26213 = "26213";
+ public static final String s26214 = "26214";
+ public static final String s26215 = "26215";
+ public static final String s26216 = "26216";
+ public static final String s26217 = "26217";
+ public static final String s26218 = "26218";
+ public static final String s26219 = "26219";
+ public static final String s26220 = "26220";
+ public static final String s26221 = "26221";
+ public static final String s26222 = "26222";
+ public static final String s26223 = "26223";
+ public static final String s26224 = "26224";
+ public static final String s26225 = "26225";
+ public static final String s26226 = "26226";
+ public static final String s26227 = "26227";
+ public static final String s26228 = "26228";
+ public static final String s26229 = "26229";
+ public static final String s26230 = "26230";
+ public static final String s26231 = "26231";
+ public static final String s26232 = "26232";
+ public static final String s26233 = "26233";
+ public static final String s26234 = "26234";
+ public static final String s26235 = "26235";
+ public static final String s26236 = "26236";
+ public static final String s26237 = "26237";
+ public static final String s26238 = "26238";
+ public static final String s26239 = "26239";
+ public static final String s26240 = "26240";
+ public static final String s26241 = "26241";
+ public static final String s26242 = "26242";
+ public static final String s26243 = "26243";
+ public static final String s26244 = "26244";
+ public static final String s26245 = "26245";
+ public static final String s26246 = "26246";
+ public static final String s26247 = "26247";
+ public static final String s26248 = "26248";
+ public static final String s26249 = "26249";
+ public static final String s26250 = "26250";
+ public static final String s26251 = "26251";
+ public static final String s26252 = "26252";
+ public static final String s26253 = "26253";
+ public static final String s26254 = "26254";
+ public static final String s26255 = "26255";
+ public static final String s26256 = "26256";
+ public static final String s26257 = "26257";
+ public static final String s26258 = "26258";
+ public static final String s26259 = "26259";
+ public static final String s26260 = "26260";
+ public static final String s26261 = "26261";
+ public static final String s26262 = "26262";
+ public static final String s26263 = "26263";
+ public static final String s26264 = "26264";
+ public static final String s26265 = "26265";
+ public static final String s26266 = "26266";
+ public static final String s26267 = "26267";
+ public static final String s26268 = "26268";
+ public static final String s26269 = "26269";
+ public static final String s26270 = "26270";
+ public static final String s26271 = "26271";
+ public static final String s26272 = "26272";
+ public static final String s26273 = "26273";
+ public static final String s26274 = "26274";
+ public static final String s26275 = "26275";
+ public static final String s26276 = "26276";
+ public static final String s26277 = "26277";
+ public static final String s26278 = "26278";
+ public static final String s26279 = "26279";
+ public static final String s26280 = "26280";
+ public static final String s26281 = "26281";
+ public static final String s26282 = "26282";
+ public static final String s26283 = "26283";
+ public static final String s26284 = "26284";
+ public static final String s26285 = "26285";
+ public static final String s26286 = "26286";
+ public static final String s26287 = "26287";
+ public static final String s26288 = "26288";
+ public static final String s26289 = "26289";
+ public static final String s26290 = "26290";
+ public static final String s26291 = "26291";
+ public static final String s26292 = "26292";
+ public static final String s26293 = "26293";
+ public static final String s26294 = "26294";
+ public static final String s26295 = "26295";
+ public static final String s26296 = "26296";
+ public static final String s26297 = "26297";
+ public static final String s26298 = "26298";
+ public static final String s26299 = "26299";
+ public static final String s26300 = "26300";
+ public static final String s26301 = "26301";
+ public static final String s26302 = "26302";
+ public static final String s26303 = "26303";
+ public static final String s26304 = "26304";
+ public static final String s26305 = "26305";
+ public static final String s26306 = "26306";
+ public static final String s26307 = "26307";
+ public static final String s26308 = "26308";
+ public static final String s26309 = "26309";
+ public static final String s26310 = "26310";
+ public static final String s26311 = "26311";
+ public static final String s26312 = "26312";
+ public static final String s26313 = "26313";
+ public static final String s26314 = "26314";
+ public static final String s26315 = "26315";
+ public static final String s26316 = "26316";
+ public static final String s26317 = "26317";
+ public static final String s26318 = "26318";
+ public static final String s26319 = "26319";
+ public static final String s26320 = "26320";
+ public static final String s26321 = "26321";
+ public static final String s26322 = "26322";
+ public static final String s26323 = "26323";
+ public static final String s26324 = "26324";
+ public static final String s26325 = "26325";
+ public static final String s26326 = "26326";
+ public static final String s26327 = "26327";
+ public static final String s26328 = "26328";
+ public static final String s26329 = "26329";
+ public static final String s26330 = "26330";
+ public static final String s26331 = "26331";
+ public static final String s26332 = "26332";
+ public static final String s26333 = "26333";
+ public static final String s26334 = "26334";
+ public static final String s26335 = "26335";
+ public static final String s26336 = "26336";
+ public static final String s26337 = "26337";
+ public static final String s26338 = "26338";
+ public static final String s26339 = "26339";
+ public static final String s26340 = "26340";
+ public static final String s26341 = "26341";
+ public static final String s26342 = "26342";
+ public static final String s26343 = "26343";
+ public static final String s26344 = "26344";
+ public static final String s26345 = "26345";
+ public static final String s26346 = "26346";
+ public static final String s26347 = "26347";
+ public static final String s26348 = "26348";
+ public static final String s26349 = "26349";
+ public static final String s26350 = "26350";
+ public static final String s26351 = "26351";
+ public static final String s26352 = "26352";
+ public static final String s26353 = "26353";
+ public static final String s26354 = "26354";
+ public static final String s26355 = "26355";
+ public static final String s26356 = "26356";
+ public static final String s26357 = "26357";
+ public static final String s26358 = "26358";
+ public static final String s26359 = "26359";
+ public static final String s26360 = "26360";
+ public static final String s26361 = "26361";
+ public static final String s26362 = "26362";
+ public static final String s26363 = "26363";
+ public static final String s26364 = "26364";
+ public static final String s26365 = "26365";
+ public static final String s26366 = "26366";
+ public static final String s26367 = "26367";
+ public static final String s26368 = "26368";
+ public static final String s26369 = "26369";
+ public static final String s26370 = "26370";
+ public static final String s26371 = "26371";
+ public static final String s26372 = "26372";
+ public static final String s26373 = "26373";
+ public static final String s26374 = "26374";
+ public static final String s26375 = "26375";
+ public static final String s26376 = "26376";
+ public static final String s26377 = "26377";
+ public static final String s26378 = "26378";
+ public static final String s26379 = "26379";
+ public static final String s26380 = "26380";
+ public static final String s26381 = "26381";
+ public static final String s26382 = "26382";
+ public static final String s26383 = "26383";
+ public static final String s26384 = "26384";
+ public static final String s26385 = "26385";
+ public static final String s26386 = "26386";
+ public static final String s26387 = "26387";
+ public static final String s26388 = "26388";
+ public static final String s26389 = "26389";
+ public static final String s26390 = "26390";
+ public static final String s26391 = "26391";
+ public static final String s26392 = "26392";
+ public static final String s26393 = "26393";
+ public static final String s26394 = "26394";
+ public static final String s26395 = "26395";
+ public static final String s26396 = "26396";
+ public static final String s26397 = "26397";
+ public static final String s26398 = "26398";
+ public static final String s26399 = "26399";
+ public static final String s26400 = "26400";
+ public static final String s26401 = "26401";
+ public static final String s26402 = "26402";
+ public static final String s26403 = "26403";
+ public static final String s26404 = "26404";
+ public static final String s26405 = "26405";
+ public static final String s26406 = "26406";
+ public static final String s26407 = "26407";
+ public static final String s26408 = "26408";
+ public static final String s26409 = "26409";
+ public static final String s26410 = "26410";
+ public static final String s26411 = "26411";
+ public static final String s26412 = "26412";
+ public static final String s26413 = "26413";
+ public static final String s26414 = "26414";
+ public static final String s26415 = "26415";
+ public static final String s26416 = "26416";
+ public static final String s26417 = "26417";
+ public static final String s26418 = "26418";
+ public static final String s26419 = "26419";
+ public static final String s26420 = "26420";
+ public static final String s26421 = "26421";
+ public static final String s26422 = "26422";
+ public static final String s26423 = "26423";
+ public static final String s26424 = "26424";
+ public static final String s26425 = "26425";
+ public static final String s26426 = "26426";
+ public static final String s26427 = "26427";
+ public static final String s26428 = "26428";
+ public static final String s26429 = "26429";
+ public static final String s26430 = "26430";
+ public static final String s26431 = "26431";
+ public static final String s26432 = "26432";
+ public static final String s26433 = "26433";
+ public static final String s26434 = "26434";
+ public static final String s26435 = "26435";
+ public static final String s26436 = "26436";
+ public static final String s26437 = "26437";
+ public static final String s26438 = "26438";
+ public static final String s26439 = "26439";
+ public static final String s26440 = "26440";
+ public static final String s26441 = "26441";
+ public static final String s26442 = "26442";
+ public static final String s26443 = "26443";
+ public static final String s26444 = "26444";
+ public static final String s26445 = "26445";
+ public static final String s26446 = "26446";
+ public static final String s26447 = "26447";
+ public static final String s26448 = "26448";
+ public static final String s26449 = "26449";
+ public static final String s26450 = "26450";
+ public static final String s26451 = "26451";
+ public static final String s26452 = "26452";
+ public static final String s26453 = "26453";
+ public static final String s26454 = "26454";
+ public static final String s26455 = "26455";
+ public static final String s26456 = "26456";
+ public static final String s26457 = "26457";
+ public static final String s26458 = "26458";
+ public static final String s26459 = "26459";
+ public static final String s26460 = "26460";
+ public static final String s26461 = "26461";
+ public static final String s26462 = "26462";
+ public static final String s26463 = "26463";
+ public static final String s26464 = "26464";
+ public static final String s26465 = "26465";
+ public static final String s26466 = "26466";
+ public static final String s26467 = "26467";
+ public static final String s26468 = "26468";
+ public static final String s26469 = "26469";
+ public static final String s26470 = "26470";
+ public static final String s26471 = "26471";
+ public static final String s26472 = "26472";
+ public static final String s26473 = "26473";
+ public static final String s26474 = "26474";
+ public static final String s26475 = "26475";
+ public static final String s26476 = "26476";
+ public static final String s26477 = "26477";
+ public static final String s26478 = "26478";
+ public static final String s26479 = "26479";
+ public static final String s26480 = "26480";
+ public static final String s26481 = "26481";
+ public static final String s26482 = "26482";
+ public static final String s26483 = "26483";
+ public static final String s26484 = "26484";
+ public static final String s26485 = "26485";
+ public static final String s26486 = "26486";
+ public static final String s26487 = "26487";
+ public static final String s26488 = "26488";
+ public static final String s26489 = "26489";
+ public static final String s26490 = "26490";
+ public static final String s26491 = "26491";
+ public static final String s26492 = "26492";
+ public static final String s26493 = "26493";
+ public static final String s26494 = "26494";
+ public static final String s26495 = "26495";
+ public static final String s26496 = "26496";
+ public static final String s26497 = "26497";
+ public static final String s26498 = "26498";
+ public static final String s26499 = "26499";
+ public static final String s26500 = "26500";
+ public static final String s26501 = "26501";
+ public static final String s26502 = "26502";
+ public static final String s26503 = "26503";
+ public static final String s26504 = "26504";
+ public static final String s26505 = "26505";
+ public static final String s26506 = "26506";
+ public static final String s26507 = "26507";
+ public static final String s26508 = "26508";
+ public static final String s26509 = "26509";
+ public static final String s26510 = "26510";
+ public static final String s26511 = "26511";
+ public static final String s26512 = "26512";
+ public static final String s26513 = "26513";
+ public static final String s26514 = "26514";
+ public static final String s26515 = "26515";
+ public static final String s26516 = "26516";
+ public static final String s26517 = "26517";
+ public static final String s26518 = "26518";
+ public static final String s26519 = "26519";
+ public static final String s26520 = "26520";
+ public static final String s26521 = "26521";
+ public static final String s26522 = "26522";
+ public static final String s26523 = "26523";
+ public static final String s26524 = "26524";
+ public static final String s26525 = "26525";
+ public static final String s26526 = "26526";
+ public static final String s26527 = "26527";
+ public static final String s26528 = "26528";
+ public static final String s26529 = "26529";
+ public static final String s26530 = "26530";
+ public static final String s26531 = "26531";
+ public static final String s26532 = "26532";
+ public static final String s26533 = "26533";
+ public static final String s26534 = "26534";
+ public static final String s26535 = "26535";
+ public static final String s26536 = "26536";
+ public static final String s26537 = "26537";
+ public static final String s26538 = "26538";
+ public static final String s26539 = "26539";
+ public static final String s26540 = "26540";
+ public static final String s26541 = "26541";
+ public static final String s26542 = "26542";
+ public static final String s26543 = "26543";
+ public static final String s26544 = "26544";
+ public static final String s26545 = "26545";
+ public static final String s26546 = "26546";
+ public static final String s26547 = "26547";
+ public static final String s26548 = "26548";
+ public static final String s26549 = "26549";
+ public static final String s26550 = "26550";
+ public static final String s26551 = "26551";
+ public static final String s26552 = "26552";
+ public static final String s26553 = "26553";
+ public static final String s26554 = "26554";
+ public static final String s26555 = "26555";
+ public static final String s26556 = "26556";
+ public static final String s26557 = "26557";
+ public static final String s26558 = "26558";
+ public static final String s26559 = "26559";
+ public static final String s26560 = "26560";
+ public static final String s26561 = "26561";
+ public static final String s26562 = "26562";
+ public static final String s26563 = "26563";
+ public static final String s26564 = "26564";
+ public static final String s26565 = "26565";
+ public static final String s26566 = "26566";
+ public static final String s26567 = "26567";
+ public static final String s26568 = "26568";
+ public static final String s26569 = "26569";
+ public static final String s26570 = "26570";
+ public static final String s26571 = "26571";
+ public static final String s26572 = "26572";
+ public static final String s26573 = "26573";
+ public static final String s26574 = "26574";
+ public static final String s26575 = "26575";
+ public static final String s26576 = "26576";
+ public static final String s26577 = "26577";
+ public static final String s26578 = "26578";
+ public static final String s26579 = "26579";
+ public static final String s26580 = "26580";
+ public static final String s26581 = "26581";
+ public static final String s26582 = "26582";
+ public static final String s26583 = "26583";
+ public static final String s26584 = "26584";
+ public static final String s26585 = "26585";
+ public static final String s26586 = "26586";
+ public static final String s26587 = "26587";
+ public static final String s26588 = "26588";
+ public static final String s26589 = "26589";
+ public static final String s26590 = "26590";
+ public static final String s26591 = "26591";
+ public static final String s26592 = "26592";
+ public static final String s26593 = "26593";
+ public static final String s26594 = "26594";
+ public static final String s26595 = "26595";
+ public static final String s26596 = "26596";
+ public static final String s26597 = "26597";
+ public static final String s26598 = "26598";
+ public static final String s26599 = "26599";
+ public static final String s26600 = "26600";
+ public static final String s26601 = "26601";
+ public static final String s26602 = "26602";
+ public static final String s26603 = "26603";
+ public static final String s26604 = "26604";
+ public static final String s26605 = "26605";
+ public static final String s26606 = "26606";
+ public static final String s26607 = "26607";
+ public static final String s26608 = "26608";
+ public static final String s26609 = "26609";
+ public static final String s26610 = "26610";
+ public static final String s26611 = "26611";
+ public static final String s26612 = "26612";
+ public static final String s26613 = "26613";
+ public static final String s26614 = "26614";
+ public static final String s26615 = "26615";
+ public static final String s26616 = "26616";
+ public static final String s26617 = "26617";
+ public static final String s26618 = "26618";
+ public static final String s26619 = "26619";
+ public static final String s26620 = "26620";
+ public static final String s26621 = "26621";
+ public static final String s26622 = "26622";
+ public static final String s26623 = "26623";
+ public static final String s26624 = "26624";
+ public static final String s26625 = "26625";
+ public static final String s26626 = "26626";
+ public static final String s26627 = "26627";
+ public static final String s26628 = "26628";
+ public static final String s26629 = "26629";
+ public static final String s26630 = "26630";
+ public static final String s26631 = "26631";
+ public static final String s26632 = "26632";
+ public static final String s26633 = "26633";
+ public static final String s26634 = "26634";
+ public static final String s26635 = "26635";
+ public static final String s26636 = "26636";
+ public static final String s26637 = "26637";
+ public static final String s26638 = "26638";
+ public static final String s26639 = "26639";
+ public static final String s26640 = "26640";
+ public static final String s26641 = "26641";
+ public static final String s26642 = "26642";
+ public static final String s26643 = "26643";
+ public static final String s26644 = "26644";
+ public static final String s26645 = "26645";
+ public static final String s26646 = "26646";
+ public static final String s26647 = "26647";
+ public static final String s26648 = "26648";
+ public static final String s26649 = "26649";
+ public static final String s26650 = "26650";
+ public static final String s26651 = "26651";
+ public static final String s26652 = "26652";
+ public static final String s26653 = "26653";
+ public static final String s26654 = "26654";
+ public static final String s26655 = "26655";
+ public static final String s26656 = "26656";
+ public static final String s26657 = "26657";
+ public static final String s26658 = "26658";
+ public static final String s26659 = "26659";
+ public static final String s26660 = "26660";
+ public static final String s26661 = "26661";
+ public static final String s26662 = "26662";
+ public static final String s26663 = "26663";
+ public static final String s26664 = "26664";
+ public static final String s26665 = "26665";
+ public static final String s26666 = "26666";
+ public static final String s26667 = "26667";
+ public static final String s26668 = "26668";
+ public static final String s26669 = "26669";
+ public static final String s26670 = "26670";
+ public static final String s26671 = "26671";
+ public static final String s26672 = "26672";
+ public static final String s26673 = "26673";
+ public static final String s26674 = "26674";
+ public static final String s26675 = "26675";
+ public static final String s26676 = "26676";
+ public static final String s26677 = "26677";
+ public static final String s26678 = "26678";
+ public static final String s26679 = "26679";
+ public static final String s26680 = "26680";
+ public static final String s26681 = "26681";
+ public static final String s26682 = "26682";
+ public static final String s26683 = "26683";
+ public static final String s26684 = "26684";
+ public static final String s26685 = "26685";
+ public static final String s26686 = "26686";
+ public static final String s26687 = "26687";
+ public static final String s26688 = "26688";
+ public static final String s26689 = "26689";
+ public static final String s26690 = "26690";
+ public static final String s26691 = "26691";
+ public static final String s26692 = "26692";
+ public static final String s26693 = "26693";
+ public static final String s26694 = "26694";
+ public static final String s26695 = "26695";
+ public static final String s26696 = "26696";
+ public static final String s26697 = "26697";
+ public static final String s26698 = "26698";
+ public static final String s26699 = "26699";
+ public static final String s26700 = "26700";
+ public static final String s26701 = "26701";
+ public static final String s26702 = "26702";
+ public static final String s26703 = "26703";
+ public static final String s26704 = "26704";
+ public static final String s26705 = "26705";
+ public static final String s26706 = "26706";
+ public static final String s26707 = "26707";
+ public static final String s26708 = "26708";
+ public static final String s26709 = "26709";
+ public static final String s26710 = "26710";
+ public static final String s26711 = "26711";
+ public static final String s26712 = "26712";
+ public static final String s26713 = "26713";
+ public static final String s26714 = "26714";
+ public static final String s26715 = "26715";
+ public static final String s26716 = "26716";
+ public static final String s26717 = "26717";
+ public static final String s26718 = "26718";
+ public static final String s26719 = "26719";
+ public static final String s26720 = "26720";
+ public static final String s26721 = "26721";
+ public static final String s26722 = "26722";
+ public static final String s26723 = "26723";
+ public static final String s26724 = "26724";
+ public static final String s26725 = "26725";
+ public static final String s26726 = "26726";
+ public static final String s26727 = "26727";
+ public static final String s26728 = "26728";
+ public static final String s26729 = "26729";
+ public static final String s26730 = "26730";
+ public static final String s26731 = "26731";
+ public static final String s26732 = "26732";
+ public static final String s26733 = "26733";
+ public static final String s26734 = "26734";
+ public static final String s26735 = "26735";
+ public static final String s26736 = "26736";
+ public static final String s26737 = "26737";
+ public static final String s26738 = "26738";
+ public static final String s26739 = "26739";
+ public static final String s26740 = "26740";
+ public static final String s26741 = "26741";
+ public static final String s26742 = "26742";
+ public static final String s26743 = "26743";
+ public static final String s26744 = "26744";
+ public static final String s26745 = "26745";
+ public static final String s26746 = "26746";
+ public static final String s26747 = "26747";
+ public static final String s26748 = "26748";
+ public static final String s26749 = "26749";
+ public static final String s26750 = "26750";
+ public static final String s26751 = "26751";
+ public static final String s26752 = "26752";
+ public static final String s26753 = "26753";
+ public static final String s26754 = "26754";
+ public static final String s26755 = "26755";
+ public static final String s26756 = "26756";
+ public static final String s26757 = "26757";
+ public static final String s26758 = "26758";
+ public static final String s26759 = "26759";
+ public static final String s26760 = "26760";
+ public static final String s26761 = "26761";
+ public static final String s26762 = "26762";
+ public static final String s26763 = "26763";
+ public static final String s26764 = "26764";
+ public static final String s26765 = "26765";
+ public static final String s26766 = "26766";
+ public static final String s26767 = "26767";
+ public static final String s26768 = "26768";
+ public static final String s26769 = "26769";
+ public static final String s26770 = "26770";
+ public static final String s26771 = "26771";
+ public static final String s26772 = "26772";
+ public static final String s26773 = "26773";
+ public static final String s26774 = "26774";
+ public static final String s26775 = "26775";
+ public static final String s26776 = "26776";
+ public static final String s26777 = "26777";
+ public static final String s26778 = "26778";
+ public static final String s26779 = "26779";
+ public static final String s26780 = "26780";
+ public static final String s26781 = "26781";
+ public static final String s26782 = "26782";
+ public static final String s26783 = "26783";
+ public static final String s26784 = "26784";
+ public static final String s26785 = "26785";
+ public static final String s26786 = "26786";
+ public static final String s26787 = "26787";
+ public static final String s26788 = "26788";
+ public static final String s26789 = "26789";
+ public static final String s26790 = "26790";
+ public static final String s26791 = "26791";
+ public static final String s26792 = "26792";
+ public static final String s26793 = "26793";
+ public static final String s26794 = "26794";
+ public static final String s26795 = "26795";
+ public static final String s26796 = "26796";
+ public static final String s26797 = "26797";
+ public static final String s26798 = "26798";
+ public static final String s26799 = "26799";
+ public static final String s26800 = "26800";
+ public static final String s26801 = "26801";
+ public static final String s26802 = "26802";
+ public static final String s26803 = "26803";
+ public static final String s26804 = "26804";
+ public static final String s26805 = "26805";
+ public static final String s26806 = "26806";
+ public static final String s26807 = "26807";
+ public static final String s26808 = "26808";
+ public static final String s26809 = "26809";
+ public static final String s26810 = "26810";
+ public static final String s26811 = "26811";
+ public static final String s26812 = "26812";
+ public static final String s26813 = "26813";
+ public static final String s26814 = "26814";
+ public static final String s26815 = "26815";
+ public static final String s26816 = "26816";
+ public static final String s26817 = "26817";
+ public static final String s26818 = "26818";
+ public static final String s26819 = "26819";
+ public static final String s26820 = "26820";
+ public static final String s26821 = "26821";
+ public static final String s26822 = "26822";
+ public static final String s26823 = "26823";
+ public static final String s26824 = "26824";
+ public static final String s26825 = "26825";
+ public static final String s26826 = "26826";
+ public static final String s26827 = "26827";
+ public static final String s26828 = "26828";
+ public static final String s26829 = "26829";
+ public static final String s26830 = "26830";
+ public static final String s26831 = "26831";
+ public static final String s26832 = "26832";
+ public static final String s26833 = "26833";
+ public static final String s26834 = "26834";
+ public static final String s26835 = "26835";
+ public static final String s26836 = "26836";
+ public static final String s26837 = "26837";
+ public static final String s26838 = "26838";
+ public static final String s26839 = "26839";
+ public static final String s26840 = "26840";
+ public static final String s26841 = "26841";
+ public static final String s26842 = "26842";
+ public static final String s26843 = "26843";
+ public static final String s26844 = "26844";
+ public static final String s26845 = "26845";
+ public static final String s26846 = "26846";
+ public static final String s26847 = "26847";
+ public static final String s26848 = "26848";
+ public static final String s26849 = "26849";
+ public static final String s26850 = "26850";
+ public static final String s26851 = "26851";
+ public static final String s26852 = "26852";
+ public static final String s26853 = "26853";
+ public static final String s26854 = "26854";
+ public static final String s26855 = "26855";
+ public static final String s26856 = "26856";
+ public static final String s26857 = "26857";
+ public static final String s26858 = "26858";
+ public static final String s26859 = "26859";
+ public static final String s26860 = "26860";
+ public static final String s26861 = "26861";
+ public static final String s26862 = "26862";
+ public static final String s26863 = "26863";
+ public static final String s26864 = "26864";
+ public static final String s26865 = "26865";
+ public static final String s26866 = "26866";
+ public static final String s26867 = "26867";
+ public static final String s26868 = "26868";
+ public static final String s26869 = "26869";
+ public static final String s26870 = "26870";
+ public static final String s26871 = "26871";
+ public static final String s26872 = "26872";
+ public static final String s26873 = "26873";
+ public static final String s26874 = "26874";
+ public static final String s26875 = "26875";
+ public static final String s26876 = "26876";
+ public static final String s26877 = "26877";
+ public static final String s26878 = "26878";
+ public static final String s26879 = "26879";
+ public static final String s26880 = "26880";
+ public static final String s26881 = "26881";
+ public static final String s26882 = "26882";
+ public static final String s26883 = "26883";
+ public static final String s26884 = "26884";
+ public static final String s26885 = "26885";
+ public static final String s26886 = "26886";
+ public static final String s26887 = "26887";
+ public static final String s26888 = "26888";
+ public static final String s26889 = "26889";
+ public static final String s26890 = "26890";
+ public static final String s26891 = "26891";
+ public static final String s26892 = "26892";
+ public static final String s26893 = "26893";
+ public static final String s26894 = "26894";
+ public static final String s26895 = "26895";
+ public static final String s26896 = "26896";
+ public static final String s26897 = "26897";
+ public static final String s26898 = "26898";
+ public static final String s26899 = "26899";
+ public static final String s26900 = "26900";
+ public static final String s26901 = "26901";
+ public static final String s26902 = "26902";
+ public static final String s26903 = "26903";
+ public static final String s26904 = "26904";
+ public static final String s26905 = "26905";
+ public static final String s26906 = "26906";
+ public static final String s26907 = "26907";
+ public static final String s26908 = "26908";
+ public static final String s26909 = "26909";
+ public static final String s26910 = "26910";
+ public static final String s26911 = "26911";
+ public static final String s26912 = "26912";
+ public static final String s26913 = "26913";
+ public static final String s26914 = "26914";
+ public static final String s26915 = "26915";
+ public static final String s26916 = "26916";
+ public static final String s26917 = "26917";
+ public static final String s26918 = "26918";
+ public static final String s26919 = "26919";
+ public static final String s26920 = "26920";
+ public static final String s26921 = "26921";
+ public static final String s26922 = "26922";
+ public static final String s26923 = "26923";
+ public static final String s26924 = "26924";
+ public static final String s26925 = "26925";
+ public static final String s26926 = "26926";
+ public static final String s26927 = "26927";
+ public static final String s26928 = "26928";
+ public static final String s26929 = "26929";
+ public static final String s26930 = "26930";
+ public static final String s26931 = "26931";
+ public static final String s26932 = "26932";
+ public static final String s26933 = "26933";
+ public static final String s26934 = "26934";
+ public static final String s26935 = "26935";
+ public static final String s26936 = "26936";
+ public static final String s26937 = "26937";
+ public static final String s26938 = "26938";
+ public static final String s26939 = "26939";
+ public static final String s26940 = "26940";
+ public static final String s26941 = "26941";
+ public static final String s26942 = "26942";
+ public static final String s26943 = "26943";
+ public static final String s26944 = "26944";
+ public static final String s26945 = "26945";
+ public static final String s26946 = "26946";
+ public static final String s26947 = "26947";
+ public static final String s26948 = "26948";
+ public static final String s26949 = "26949";
+ public static final String s26950 = "26950";
+ public static final String s26951 = "26951";
+ public static final String s26952 = "26952";
+ public static final String s26953 = "26953";
+ public static final String s26954 = "26954";
+ public static final String s26955 = "26955";
+ public static final String s26956 = "26956";
+ public static final String s26957 = "26957";
+ public static final String s26958 = "26958";
+ public static final String s26959 = "26959";
+ public static final String s26960 = "26960";
+ public static final String s26961 = "26961";
+ public static final String s26962 = "26962";
+ public static final String s26963 = "26963";
+ public static final String s26964 = "26964";
+ public static final String s26965 = "26965";
+ public static final String s26966 = "26966";
+ public static final String s26967 = "26967";
+ public static final String s26968 = "26968";
+ public static final String s26969 = "26969";
+ public static final String s26970 = "26970";
+ public static final String s26971 = "26971";
+ public static final String s26972 = "26972";
+ public static final String s26973 = "26973";
+ public static final String s26974 = "26974";
+ public static final String s26975 = "26975";
+ public static final String s26976 = "26976";
+ public static final String s26977 = "26977";
+ public static final String s26978 = "26978";
+ public static final String s26979 = "26979";
+ public static final String s26980 = "26980";
+ public static final String s26981 = "26981";
+ public static final String s26982 = "26982";
+ public static final String s26983 = "26983";
+ public static final String s26984 = "26984";
+ public static final String s26985 = "26985";
+ public static final String s26986 = "26986";
+ public static final String s26987 = "26987";
+ public static final String s26988 = "26988";
+ public static final String s26989 = "26989";
+ public static final String s26990 = "26990";
+ public static final String s26991 = "26991";
+ public static final String s26992 = "26992";
+ public static final String s26993 = "26993";
+ public static final String s26994 = "26994";
+ public static final String s26995 = "26995";
+ public static final String s26996 = "26996";
+ public static final String s26997 = "26997";
+ public static final String s26998 = "26998";
+ public static final String s26999 = "26999";
+ public static final String s27000 = "27000";
+ public static final String s27001 = "27001";
+ public static final String s27002 = "27002";
+ public static final String s27003 = "27003";
+ public static final String s27004 = "27004";
+ public static final String s27005 = "27005";
+ public static final String s27006 = "27006";
+ public static final String s27007 = "27007";
+ public static final String s27008 = "27008";
+ public static final String s27009 = "27009";
+ public static final String s27010 = "27010";
+ public static final String s27011 = "27011";
+ public static final String s27012 = "27012";
+ public static final String s27013 = "27013";
+ public static final String s27014 = "27014";
+ public static final String s27015 = "27015";
+ public static final String s27016 = "27016";
+ public static final String s27017 = "27017";
+ public static final String s27018 = "27018";
+ public static final String s27019 = "27019";
+ public static final String s27020 = "27020";
+ public static final String s27021 = "27021";
+ public static final String s27022 = "27022";
+ public static final String s27023 = "27023";
+ public static final String s27024 = "27024";
+ public static final String s27025 = "27025";
+ public static final String s27026 = "27026";
+ public static final String s27027 = "27027";
+ public static final String s27028 = "27028";
+ public static final String s27029 = "27029";
+ public static final String s27030 = "27030";
+ public static final String s27031 = "27031";
+ public static final String s27032 = "27032";
+ public static final String s27033 = "27033";
+ public static final String s27034 = "27034";
+ public static final String s27035 = "27035";
+ public static final String s27036 = "27036";
+ public static final String s27037 = "27037";
+ public static final String s27038 = "27038";
+ public static final String s27039 = "27039";
+ public static final String s27040 = "27040";
+ public static final String s27041 = "27041";
+ public static final String s27042 = "27042";
+ public static final String s27043 = "27043";
+ public static final String s27044 = "27044";
+ public static final String s27045 = "27045";
+ public static final String s27046 = "27046";
+ public static final String s27047 = "27047";
+ public static final String s27048 = "27048";
+ public static final String s27049 = "27049";
+ public static final String s27050 = "27050";
+ public static final String s27051 = "27051";
+ public static final String s27052 = "27052";
+ public static final String s27053 = "27053";
+ public static final String s27054 = "27054";
+ public static final String s27055 = "27055";
+ public static final String s27056 = "27056";
+ public static final String s27057 = "27057";
+ public static final String s27058 = "27058";
+ public static final String s27059 = "27059";
+ public static final String s27060 = "27060";
+ public static final String s27061 = "27061";
+ public static final String s27062 = "27062";
+ public static final String s27063 = "27063";
+ public static final String s27064 = "27064";
+ public static final String s27065 = "27065";
+ public static final String s27066 = "27066";
+ public static final String s27067 = "27067";
+ public static final String s27068 = "27068";
+ public static final String s27069 = "27069";
+ public static final String s27070 = "27070";
+ public static final String s27071 = "27071";
+ public static final String s27072 = "27072";
+ public static final String s27073 = "27073";
+ public static final String s27074 = "27074";
+ public static final String s27075 = "27075";
+ public static final String s27076 = "27076";
+ public static final String s27077 = "27077";
+ public static final String s27078 = "27078";
+ public static final String s27079 = "27079";
+ public static final String s27080 = "27080";
+ public static final String s27081 = "27081";
+ public static final String s27082 = "27082";
+ public static final String s27083 = "27083";
+ public static final String s27084 = "27084";
+ public static final String s27085 = "27085";
+ public static final String s27086 = "27086";
+ public static final String s27087 = "27087";
+ public static final String s27088 = "27088";
+ public static final String s27089 = "27089";
+ public static final String s27090 = "27090";
+ public static final String s27091 = "27091";
+ public static final String s27092 = "27092";
+ public static final String s27093 = "27093";
+ public static final String s27094 = "27094";
+ public static final String s27095 = "27095";
+ public static final String s27096 = "27096";
+ public static final String s27097 = "27097";
+ public static final String s27098 = "27098";
+ public static final String s27099 = "27099";
+ public static final String s27100 = "27100";
+ public static final String s27101 = "27101";
+ public static final String s27102 = "27102";
+ public static final String s27103 = "27103";
+ public static final String s27104 = "27104";
+ public static final String s27105 = "27105";
+ public static final String s27106 = "27106";
+ public static final String s27107 = "27107";
+ public static final String s27108 = "27108";
+ public static final String s27109 = "27109";
+ public static final String s27110 = "27110";
+ public static final String s27111 = "27111";
+ public static final String s27112 = "27112";
+ public static final String s27113 = "27113";
+ public static final String s27114 = "27114";
+ public static final String s27115 = "27115";
+ public static final String s27116 = "27116";
+ public static final String s27117 = "27117";
+ public static final String s27118 = "27118";
+ public static final String s27119 = "27119";
+ public static final String s27120 = "27120";
+ public static final String s27121 = "27121";
+ public static final String s27122 = "27122";
+ public static final String s27123 = "27123";
+ public static final String s27124 = "27124";
+ public static final String s27125 = "27125";
+ public static final String s27126 = "27126";
+ public static final String s27127 = "27127";
+ public static final String s27128 = "27128";
+ public static final String s27129 = "27129";
+ public static final String s27130 = "27130";
+ public static final String s27131 = "27131";
+ public static final String s27132 = "27132";
+ public static final String s27133 = "27133";
+ public static final String s27134 = "27134";
+ public static final String s27135 = "27135";
+ public static final String s27136 = "27136";
+ public static final String s27137 = "27137";
+ public static final String s27138 = "27138";
+ public static final String s27139 = "27139";
+ public static final String s27140 = "27140";
+ public static final String s27141 = "27141";
+ public static final String s27142 = "27142";
+ public static final String s27143 = "27143";
+ public static final String s27144 = "27144";
+ public static final String s27145 = "27145";
+ public static final String s27146 = "27146";
+ public static final String s27147 = "27147";
+ public static final String s27148 = "27148";
+ public static final String s27149 = "27149";
+ public static final String s27150 = "27150";
+ public static final String s27151 = "27151";
+ public static final String s27152 = "27152";
+ public static final String s27153 = "27153";
+ public static final String s27154 = "27154";
+ public static final String s27155 = "27155";
+ public static final String s27156 = "27156";
+ public static final String s27157 = "27157";
+ public static final String s27158 = "27158";
+ public static final String s27159 = "27159";
+ public static final String s27160 = "27160";
+ public static final String s27161 = "27161";
+ public static final String s27162 = "27162";
+ public static final String s27163 = "27163";
+ public static final String s27164 = "27164";
+ public static final String s27165 = "27165";
+ public static final String s27166 = "27166";
+ public static final String s27167 = "27167";
+ public static final String s27168 = "27168";
+ public static final String s27169 = "27169";
+ public static final String s27170 = "27170";
+ public static final String s27171 = "27171";
+ public static final String s27172 = "27172";
+ public static final String s27173 = "27173";
+ public static final String s27174 = "27174";
+ public static final String s27175 = "27175";
+ public static final String s27176 = "27176";
+ public static final String s27177 = "27177";
+ public static final String s27178 = "27178";
+ public static final String s27179 = "27179";
+ public static final String s27180 = "27180";
+ public static final String s27181 = "27181";
+ public static final String s27182 = "27182";
+ public static final String s27183 = "27183";
+ public static final String s27184 = "27184";
+ public static final String s27185 = "27185";
+ public static final String s27186 = "27186";
+ public static final String s27187 = "27187";
+ public static final String s27188 = "27188";
+ public static final String s27189 = "27189";
+ public static final String s27190 = "27190";
+ public static final String s27191 = "27191";
+ public static final String s27192 = "27192";
+ public static final String s27193 = "27193";
+ public static final String s27194 = "27194";
+ public static final String s27195 = "27195";
+ public static final String s27196 = "27196";
+ public static final String s27197 = "27197";
+ public static final String s27198 = "27198";
+ public static final String s27199 = "27199";
+ public static final String s27200 = "27200";
+ public static final String s27201 = "27201";
+ public static final String s27202 = "27202";
+ public static final String s27203 = "27203";
+ public static final String s27204 = "27204";
+ public static final String s27205 = "27205";
+ public static final String s27206 = "27206";
+ public static final String s27207 = "27207";
+ public static final String s27208 = "27208";
+ public static final String s27209 = "27209";
+ public static final String s27210 = "27210";
+ public static final String s27211 = "27211";
+ public static final String s27212 = "27212";
+ public static final String s27213 = "27213";
+ public static final String s27214 = "27214";
+ public static final String s27215 = "27215";
+ public static final String s27216 = "27216";
+ public static final String s27217 = "27217";
+ public static final String s27218 = "27218";
+ public static final String s27219 = "27219";
+ public static final String s27220 = "27220";
+ public static final String s27221 = "27221";
+ public static final String s27222 = "27222";
+ public static final String s27223 = "27223";
+ public static final String s27224 = "27224";
+ public static final String s27225 = "27225";
+ public static final String s27226 = "27226";
+ public static final String s27227 = "27227";
+ public static final String s27228 = "27228";
+ public static final String s27229 = "27229";
+ public static final String s27230 = "27230";
+ public static final String s27231 = "27231";
+ public static final String s27232 = "27232";
+ public static final String s27233 = "27233";
+ public static final String s27234 = "27234";
+ public static final String s27235 = "27235";
+ public static final String s27236 = "27236";
+ public static final String s27237 = "27237";
+ public static final String s27238 = "27238";
+ public static final String s27239 = "27239";
+ public static final String s27240 = "27240";
+ public static final String s27241 = "27241";
+ public static final String s27242 = "27242";
+ public static final String s27243 = "27243";
+ public static final String s27244 = "27244";
+ public static final String s27245 = "27245";
+ public static final String s27246 = "27246";
+ public static final String s27247 = "27247";
+ public static final String s27248 = "27248";
+ public static final String s27249 = "27249";
+ public static final String s27250 = "27250";
+ public static final String s27251 = "27251";
+ public static final String s27252 = "27252";
+ public static final String s27253 = "27253";
+ public static final String s27254 = "27254";
+ public static final String s27255 = "27255";
+ public static final String s27256 = "27256";
+ public static final String s27257 = "27257";
+ public static final String s27258 = "27258";
+ public static final String s27259 = "27259";
+ public static final String s27260 = "27260";
+ public static final String s27261 = "27261";
+ public static final String s27262 = "27262";
+ public static final String s27263 = "27263";
+ public static final String s27264 = "27264";
+ public static final String s27265 = "27265";
+ public static final String s27266 = "27266";
+ public static final String s27267 = "27267";
+ public static final String s27268 = "27268";
+ public static final String s27269 = "27269";
+ public static final String s27270 = "27270";
+ public static final String s27271 = "27271";
+ public static final String s27272 = "27272";
+ public static final String s27273 = "27273";
+ public static final String s27274 = "27274";
+ public static final String s27275 = "27275";
+ public static final String s27276 = "27276";
+ public static final String s27277 = "27277";
+ public static final String s27278 = "27278";
+ public static final String s27279 = "27279";
+ public static final String s27280 = "27280";
+ public static final String s27281 = "27281";
+ public static final String s27282 = "27282";
+ public static final String s27283 = "27283";
+ public static final String s27284 = "27284";
+ public static final String s27285 = "27285";
+ public static final String s27286 = "27286";
+ public static final String s27287 = "27287";
+ public static final String s27288 = "27288";
+ public static final String s27289 = "27289";
+ public static final String s27290 = "27290";
+ public static final String s27291 = "27291";
+ public static final String s27292 = "27292";
+ public static final String s27293 = "27293";
+ public static final String s27294 = "27294";
+ public static final String s27295 = "27295";
+ public static final String s27296 = "27296";
+ public static final String s27297 = "27297";
+ public static final String s27298 = "27298";
+ public static final String s27299 = "27299";
+ public static final String s27300 = "27300";
+ public static final String s27301 = "27301";
+ public static final String s27302 = "27302";
+ public static final String s27303 = "27303";
+ public static final String s27304 = "27304";
+ public static final String s27305 = "27305";
+ public static final String s27306 = "27306";
+ public static final String s27307 = "27307";
+ public static final String s27308 = "27308";
+ public static final String s27309 = "27309";
+ public static final String s27310 = "27310";
+ public static final String s27311 = "27311";
+ public static final String s27312 = "27312";
+ public static final String s27313 = "27313";
+ public static final String s27314 = "27314";
+ public static final String s27315 = "27315";
+ public static final String s27316 = "27316";
+ public static final String s27317 = "27317";
+ public static final String s27318 = "27318";
+ public static final String s27319 = "27319";
+ public static final String s27320 = "27320";
+ public static final String s27321 = "27321";
+ public static final String s27322 = "27322";
+ public static final String s27323 = "27323";
+ public static final String s27324 = "27324";
+ public static final String s27325 = "27325";
+ public static final String s27326 = "27326";
+ public static final String s27327 = "27327";
+ public static final String s27328 = "27328";
+ public static final String s27329 = "27329";
+ public static final String s27330 = "27330";
+ public static final String s27331 = "27331";
+ public static final String s27332 = "27332";
+ public static final String s27333 = "27333";
+ public static final String s27334 = "27334";
+ public static final String s27335 = "27335";
+ public static final String s27336 = "27336";
+ public static final String s27337 = "27337";
+ public static final String s27338 = "27338";
+ public static final String s27339 = "27339";
+ public static final String s27340 = "27340";
+ public static final String s27341 = "27341";
+ public static final String s27342 = "27342";
+ public static final String s27343 = "27343";
+ public static final String s27344 = "27344";
+ public static final String s27345 = "27345";
+ public static final String s27346 = "27346";
+ public static final String s27347 = "27347";
+ public static final String s27348 = "27348";
+ public static final String s27349 = "27349";
+ public static final String s27350 = "27350";
+ public static final String s27351 = "27351";
+ public static final String s27352 = "27352";
+ public static final String s27353 = "27353";
+ public static final String s27354 = "27354";
+ public static final String s27355 = "27355";
+ public static final String s27356 = "27356";
+ public static final String s27357 = "27357";
+ public static final String s27358 = "27358";
+ public static final String s27359 = "27359";
+ public static final String s27360 = "27360";
+ public static final String s27361 = "27361";
+ public static final String s27362 = "27362";
+ public static final String s27363 = "27363";
+ public static final String s27364 = "27364";
+ public static final String s27365 = "27365";
+ public static final String s27366 = "27366";
+ public static final String s27367 = "27367";
+ public static final String s27368 = "27368";
+ public static final String s27369 = "27369";
+ public static final String s27370 = "27370";
+ public static final String s27371 = "27371";
+ public static final String s27372 = "27372";
+ public static final String s27373 = "27373";
+ public static final String s27374 = "27374";
+ public static final String s27375 = "27375";
+ public static final String s27376 = "27376";
+ public static final String s27377 = "27377";
+ public static final String s27378 = "27378";
+ public static final String s27379 = "27379";
+ public static final String s27380 = "27380";
+ public static final String s27381 = "27381";
+ public static final String s27382 = "27382";
+ public static final String s27383 = "27383";
+ public static final String s27384 = "27384";
+ public static final String s27385 = "27385";
+ public static final String s27386 = "27386";
+ public static final String s27387 = "27387";
+ public static final String s27388 = "27388";
+ public static final String s27389 = "27389";
+ public static final String s27390 = "27390";
+ public static final String s27391 = "27391";
+ public static final String s27392 = "27392";
+ public static final String s27393 = "27393";
+ public static final String s27394 = "27394";
+ public static final String s27395 = "27395";
+ public static final String s27396 = "27396";
+ public static final String s27397 = "27397";
+ public static final String s27398 = "27398";
+ public static final String s27399 = "27399";
+ public static final String s27400 = "27400";
+ public static final String s27401 = "27401";
+ public static final String s27402 = "27402";
+ public static final String s27403 = "27403";
+ public static final String s27404 = "27404";
+ public static final String s27405 = "27405";
+ public static final String s27406 = "27406";
+ public static final String s27407 = "27407";
+ public static final String s27408 = "27408";
+ public static final String s27409 = "27409";
+ public static final String s27410 = "27410";
+ public static final String s27411 = "27411";
+ public static final String s27412 = "27412";
+ public static final String s27413 = "27413";
+ public static final String s27414 = "27414";
+ public static final String s27415 = "27415";
+ public static final String s27416 = "27416";
+ public static final String s27417 = "27417";
+ public static final String s27418 = "27418";
+ public static final String s27419 = "27419";
+ public static final String s27420 = "27420";
+ public static final String s27421 = "27421";
+ public static final String s27422 = "27422";
+ public static final String s27423 = "27423";
+ public static final String s27424 = "27424";
+ public static final String s27425 = "27425";
+ public static final String s27426 = "27426";
+ public static final String s27427 = "27427";
+ public static final String s27428 = "27428";
+ public static final String s27429 = "27429";
+ public static final String s27430 = "27430";
+ public static final String s27431 = "27431";
+ public static final String s27432 = "27432";
+ public static final String s27433 = "27433";
+ public static final String s27434 = "27434";
+ public static final String s27435 = "27435";
+ public static final String s27436 = "27436";
+ public static final String s27437 = "27437";
+ public static final String s27438 = "27438";
+ public static final String s27439 = "27439";
+ public static final String s27440 = "27440";
+ public static final String s27441 = "27441";
+ public static final String s27442 = "27442";
+ public static final String s27443 = "27443";
+ public static final String s27444 = "27444";
+ public static final String s27445 = "27445";
+ public static final String s27446 = "27446";
+ public static final String s27447 = "27447";
+ public static final String s27448 = "27448";
+ public static final String s27449 = "27449";
+ public static final String s27450 = "27450";
+ public static final String s27451 = "27451";
+ public static final String s27452 = "27452";
+ public static final String s27453 = "27453";
+ public static final String s27454 = "27454";
+ public static final String s27455 = "27455";
+ public static final String s27456 = "27456";
+ public static final String s27457 = "27457";
+ public static final String s27458 = "27458";
+ public static final String s27459 = "27459";
+ public static final String s27460 = "27460";
+ public static final String s27461 = "27461";
+ public static final String s27462 = "27462";
+ public static final String s27463 = "27463";
+ public static final String s27464 = "27464";
+ public static final String s27465 = "27465";
+ public static final String s27466 = "27466";
+ public static final String s27467 = "27467";
+ public static final String s27468 = "27468";
+ public static final String s27469 = "27469";
+ public static final String s27470 = "27470";
+ public static final String s27471 = "27471";
+ public static final String s27472 = "27472";
+ public static final String s27473 = "27473";
+ public static final String s27474 = "27474";
+ public static final String s27475 = "27475";
+ public static final String s27476 = "27476";
+ public static final String s27477 = "27477";
+ public static final String s27478 = "27478";
+ public static final String s27479 = "27479";
+ public static final String s27480 = "27480";
+ public static final String s27481 = "27481";
+ public static final String s27482 = "27482";
+ public static final String s27483 = "27483";
+ public static final String s27484 = "27484";
+ public static final String s27485 = "27485";
+ public static final String s27486 = "27486";
+ public static final String s27487 = "27487";
+ public static final String s27488 = "27488";
+ public static final String s27489 = "27489";
+ public static final String s27490 = "27490";
+ public static final String s27491 = "27491";
+ public static final String s27492 = "27492";
+ public static final String s27493 = "27493";
+ public static final String s27494 = "27494";
+ public static final String s27495 = "27495";
+ public static final String s27496 = "27496";
+ public static final String s27497 = "27497";
+ public static final String s27498 = "27498";
+ public static final String s27499 = "27499";
+ public static final String s27500 = "27500";
+ public static final String s27501 = "27501";
+ public static final String s27502 = "27502";
+ public static final String s27503 = "27503";
+ public static final String s27504 = "27504";
+ public static final String s27505 = "27505";
+ public static final String s27506 = "27506";
+ public static final String s27507 = "27507";
+ public static final String s27508 = "27508";
+ public static final String s27509 = "27509";
+ public static final String s27510 = "27510";
+ public static final String s27511 = "27511";
+ public static final String s27512 = "27512";
+ public static final String s27513 = "27513";
+ public static final String s27514 = "27514";
+ public static final String s27515 = "27515";
+ public static final String s27516 = "27516";
+ public static final String s27517 = "27517";
+ public static final String s27518 = "27518";
+ public static final String s27519 = "27519";
+ public static final String s27520 = "27520";
+ public static final String s27521 = "27521";
+ public static final String s27522 = "27522";
+ public static final String s27523 = "27523";
+ public static final String s27524 = "27524";
+ public static final String s27525 = "27525";
+ public static final String s27526 = "27526";
+ public static final String s27527 = "27527";
+ public static final String s27528 = "27528";
+ public static final String s27529 = "27529";
+ public static final String s27530 = "27530";
+ public static final String s27531 = "27531";
+ public static final String s27532 = "27532";
+ public static final String s27533 = "27533";
+ public static final String s27534 = "27534";
+ public static final String s27535 = "27535";
+ public static final String s27536 = "27536";
+ public static final String s27537 = "27537";
+ public static final String s27538 = "27538";
+ public static final String s27539 = "27539";
+ public static final String s27540 = "27540";
+ public static final String s27541 = "27541";
+ public static final String s27542 = "27542";
+ public static final String s27543 = "27543";
+ public static final String s27544 = "27544";
+ public static final String s27545 = "27545";
+ public static final String s27546 = "27546";
+ public static final String s27547 = "27547";
+ public static final String s27548 = "27548";
+ public static final String s27549 = "27549";
+ public static final String s27550 = "27550";
+ public static final String s27551 = "27551";
+ public static final String s27552 = "27552";
+ public static final String s27553 = "27553";
+ public static final String s27554 = "27554";
+ public static final String s27555 = "27555";
+ public static final String s27556 = "27556";
+ public static final String s27557 = "27557";
+ public static final String s27558 = "27558";
+ public static final String s27559 = "27559";
+ public static final String s27560 = "27560";
+ public static final String s27561 = "27561";
+ public static final String s27562 = "27562";
+ public static final String s27563 = "27563";
+ public static final String s27564 = "27564";
+ public static final String s27565 = "27565";
+ public static final String s27566 = "27566";
+ public static final String s27567 = "27567";
+ public static final String s27568 = "27568";
+ public static final String s27569 = "27569";
+ public static final String s27570 = "27570";
+ public static final String s27571 = "27571";
+ public static final String s27572 = "27572";
+ public static final String s27573 = "27573";
+ public static final String s27574 = "27574";
+ public static final String s27575 = "27575";
+ public static final String s27576 = "27576";
+ public static final String s27577 = "27577";
+ public static final String s27578 = "27578";
+ public static final String s27579 = "27579";
+ public static final String s27580 = "27580";
+ public static final String s27581 = "27581";
+ public static final String s27582 = "27582";
+ public static final String s27583 = "27583";
+ public static final String s27584 = "27584";
+ public static final String s27585 = "27585";
+ public static final String s27586 = "27586";
+ public static final String s27587 = "27587";
+ public static final String s27588 = "27588";
+ public static final String s27589 = "27589";
+ public static final String s27590 = "27590";
+ public static final String s27591 = "27591";
+ public static final String s27592 = "27592";
+ public static final String s27593 = "27593";
+ public static final String s27594 = "27594";
+ public static final String s27595 = "27595";
+ public static final String s27596 = "27596";
+ public static final String s27597 = "27597";
+ public static final String s27598 = "27598";
+ public static final String s27599 = "27599";
+ public static final String s27600 = "27600";
+ public static final String s27601 = "27601";
+ public static final String s27602 = "27602";
+ public static final String s27603 = "27603";
+ public static final String s27604 = "27604";
+ public static final String s27605 = "27605";
+ public static final String s27606 = "27606";
+ public static final String s27607 = "27607";
+ public static final String s27608 = "27608";
+ public static final String s27609 = "27609";
+ public static final String s27610 = "27610";
+ public static final String s27611 = "27611";
+ public static final String s27612 = "27612";
+ public static final String s27613 = "27613";
+ public static final String s27614 = "27614";
+ public static final String s27615 = "27615";
+ public static final String s27616 = "27616";
+ public static final String s27617 = "27617";
+ public static final String s27618 = "27618";
+ public static final String s27619 = "27619";
+ public static final String s27620 = "27620";
+ public static final String s27621 = "27621";
+ public static final String s27622 = "27622";
+ public static final String s27623 = "27623";
+ public static final String s27624 = "27624";
+ public static final String s27625 = "27625";
+ public static final String s27626 = "27626";
+ public static final String s27627 = "27627";
+ public static final String s27628 = "27628";
+ public static final String s27629 = "27629";
+ public static final String s27630 = "27630";
+ public static final String s27631 = "27631";
+ public static final String s27632 = "27632";
+ public static final String s27633 = "27633";
+ public static final String s27634 = "27634";
+ public static final String s27635 = "27635";
+ public static final String s27636 = "27636";
+ public static final String s27637 = "27637";
+ public static final String s27638 = "27638";
+ public static final String s27639 = "27639";
+ public static final String s27640 = "27640";
+ public static final String s27641 = "27641";
+ public static final String s27642 = "27642";
+ public static final String s27643 = "27643";
+ public static final String s27644 = "27644";
+ public static final String s27645 = "27645";
+ public static final String s27646 = "27646";
+ public static final String s27647 = "27647";
+ public static final String s27648 = "27648";
+ public static final String s27649 = "27649";
+ public static final String s27650 = "27650";
+ public static final String s27651 = "27651";
+ public static final String s27652 = "27652";
+ public static final String s27653 = "27653";
+ public static final String s27654 = "27654";
+ public static final String s27655 = "27655";
+ public static final String s27656 = "27656";
+ public static final String s27657 = "27657";
+ public static final String s27658 = "27658";
+ public static final String s27659 = "27659";
+ public static final String s27660 = "27660";
+ public static final String s27661 = "27661";
+ public static final String s27662 = "27662";
+ public static final String s27663 = "27663";
+ public static final String s27664 = "27664";
+ public static final String s27665 = "27665";
+ public static final String s27666 = "27666";
+ public static final String s27667 = "27667";
+ public static final String s27668 = "27668";
+ public static final String s27669 = "27669";
+ public static final String s27670 = "27670";
+ public static final String s27671 = "27671";
+ public static final String s27672 = "27672";
+ public static final String s27673 = "27673";
+ public static final String s27674 = "27674";
+ public static final String s27675 = "27675";
+ public static final String s27676 = "27676";
+ public static final String s27677 = "27677";
+ public static final String s27678 = "27678";
+ public static final String s27679 = "27679";
+ public static final String s27680 = "27680";
+ public static final String s27681 = "27681";
+ public static final String s27682 = "27682";
+ public static final String s27683 = "27683";
+ public static final String s27684 = "27684";
+ public static final String s27685 = "27685";
+ public static final String s27686 = "27686";
+ public static final String s27687 = "27687";
+ public static final String s27688 = "27688";
+ public static final String s27689 = "27689";
+ public static final String s27690 = "27690";
+ public static final String s27691 = "27691";
+ public static final String s27692 = "27692";
+ public static final String s27693 = "27693";
+ public static final String s27694 = "27694";
+ public static final String s27695 = "27695";
+ public static final String s27696 = "27696";
+ public static final String s27697 = "27697";
+ public static final String s27698 = "27698";
+ public static final String s27699 = "27699";
+ public static final String s27700 = "27700";
+ public static final String s27701 = "27701";
+ public static final String s27702 = "27702";
+ public static final String s27703 = "27703";
+ public static final String s27704 = "27704";
+ public static final String s27705 = "27705";
+ public static final String s27706 = "27706";
+ public static final String s27707 = "27707";
+ public static final String s27708 = "27708";
+ public static final String s27709 = "27709";
+ public static final String s27710 = "27710";
+ public static final String s27711 = "27711";
+ public static final String s27712 = "27712";
+ public static final String s27713 = "27713";
+ public static final String s27714 = "27714";
+ public static final String s27715 = "27715";
+ public static final String s27716 = "27716";
+ public static final String s27717 = "27717";
+ public static final String s27718 = "27718";
+ public static final String s27719 = "27719";
+ public static final String s27720 = "27720";
+ public static final String s27721 = "27721";
+ public static final String s27722 = "27722";
+ public static final String s27723 = "27723";
+ public static final String s27724 = "27724";
+ public static final String s27725 = "27725";
+ public static final String s27726 = "27726";
+ public static final String s27727 = "27727";
+ public static final String s27728 = "27728";
+ public static final String s27729 = "27729";
+ public static final String s27730 = "27730";
+ public static final String s27731 = "27731";
+ public static final String s27732 = "27732";
+ public static final String s27733 = "27733";
+ public static final String s27734 = "27734";
+ public static final String s27735 = "27735";
+ public static final String s27736 = "27736";
+ public static final String s27737 = "27737";
+ public static final String s27738 = "27738";
+ public static final String s27739 = "27739";
+ public static final String s27740 = "27740";
+ public static final String s27741 = "27741";
+ public static final String s27742 = "27742";
+ public static final String s27743 = "27743";
+ public static final String s27744 = "27744";
+ public static final String s27745 = "27745";
+ public static final String s27746 = "27746";
+ public static final String s27747 = "27747";
+ public static final String s27748 = "27748";
+ public static final String s27749 = "27749";
+ public static final String s27750 = "27750";
+ public static final String s27751 = "27751";
+ public static final String s27752 = "27752";
+ public static final String s27753 = "27753";
+ public static final String s27754 = "27754";
+ public static final String s27755 = "27755";
+ public static final String s27756 = "27756";
+ public static final String s27757 = "27757";
+ public static final String s27758 = "27758";
+ public static final String s27759 = "27759";
+ public static final String s27760 = "27760";
+ public static final String s27761 = "27761";
+ public static final String s27762 = "27762";
+ public static final String s27763 = "27763";
+ public static final String s27764 = "27764";
+ public static final String s27765 = "27765";
+ public static final String s27766 = "27766";
+ public static final String s27767 = "27767";
+ public static final String s27768 = "27768";
+ public static final String s27769 = "27769";
+ public static final String s27770 = "27770";
+ public static final String s27771 = "27771";
+ public static final String s27772 = "27772";
+ public static final String s27773 = "27773";
+ public static final String s27774 = "27774";
+ public static final String s27775 = "27775";
+ public static final String s27776 = "27776";
+ public static final String s27777 = "27777";
+ public static final String s27778 = "27778";
+ public static final String s27779 = "27779";
+ public static final String s27780 = "27780";
+ public static final String s27781 = "27781";
+ public static final String s27782 = "27782";
+ public static final String s27783 = "27783";
+ public static final String s27784 = "27784";
+ public static final String s27785 = "27785";
+ public static final String s27786 = "27786";
+ public static final String s27787 = "27787";
+ public static final String s27788 = "27788";
+ public static final String s27789 = "27789";
+ public static final String s27790 = "27790";
+ public static final String s27791 = "27791";
+ public static final String s27792 = "27792";
+ public static final String s27793 = "27793";
+ public static final String s27794 = "27794";
+ public static final String s27795 = "27795";
+ public static final String s27796 = "27796";
+ public static final String s27797 = "27797";
+ public static final String s27798 = "27798";
+ public static final String s27799 = "27799";
+ public static final String s27800 = "27800";
+ public static final String s27801 = "27801";
+ public static final String s27802 = "27802";
+ public static final String s27803 = "27803";
+ public static final String s27804 = "27804";
+ public static final String s27805 = "27805";
+ public static final String s27806 = "27806";
+ public static final String s27807 = "27807";
+ public static final String s27808 = "27808";
+ public static final String s27809 = "27809";
+ public static final String s27810 = "27810";
+ public static final String s27811 = "27811";
+ public static final String s27812 = "27812";
+ public static final String s27813 = "27813";
+ public static final String s27814 = "27814";
+ public static final String s27815 = "27815";
+ public static final String s27816 = "27816";
+ public static final String s27817 = "27817";
+ public static final String s27818 = "27818";
+ public static final String s27819 = "27819";
+ public static final String s27820 = "27820";
+ public static final String s27821 = "27821";
+ public static final String s27822 = "27822";
+ public static final String s27823 = "27823";
+ public static final String s27824 = "27824";
+ public static final String s27825 = "27825";
+ public static final String s27826 = "27826";
+ public static final String s27827 = "27827";
+ public static final String s27828 = "27828";
+ public static final String s27829 = "27829";
+ public static final String s27830 = "27830";
+ public static final String s27831 = "27831";
+ public static final String s27832 = "27832";
+ public static final String s27833 = "27833";
+ public static final String s27834 = "27834";
+ public static final String s27835 = "27835";
+ public static final String s27836 = "27836";
+ public static final String s27837 = "27837";
+ public static final String s27838 = "27838";
+ public static final String s27839 = "27839";
+ public static final String s27840 = "27840";
+ public static final String s27841 = "27841";
+ public static final String s27842 = "27842";
+ public static final String s27843 = "27843";
+ public static final String s27844 = "27844";
+ public static final String s27845 = "27845";
+ public static final String s27846 = "27846";
+ public static final String s27847 = "27847";
+ public static final String s27848 = "27848";
+ public static final String s27849 = "27849";
+ public static final String s27850 = "27850";
+ public static final String s27851 = "27851";
+ public static final String s27852 = "27852";
+ public static final String s27853 = "27853";
+ public static final String s27854 = "27854";
+ public static final String s27855 = "27855";
+ public static final String s27856 = "27856";
+ public static final String s27857 = "27857";
+ public static final String s27858 = "27858";
+ public static final String s27859 = "27859";
+ public static final String s27860 = "27860";
+ public static final String s27861 = "27861";
+ public static final String s27862 = "27862";
+ public static final String s27863 = "27863";
+ public static final String s27864 = "27864";
+ public static final String s27865 = "27865";
+ public static final String s27866 = "27866";
+ public static final String s27867 = "27867";
+ public static final String s27868 = "27868";
+ public static final String s27869 = "27869";
+ public static final String s27870 = "27870";
+ public static final String s27871 = "27871";
+ public static final String s27872 = "27872";
+ public static final String s27873 = "27873";
+ public static final String s27874 = "27874";
+ public static final String s27875 = "27875";
+ public static final String s27876 = "27876";
+ public static final String s27877 = "27877";
+ public static final String s27878 = "27878";
+ public static final String s27879 = "27879";
+ public static final String s27880 = "27880";
+ public static final String s27881 = "27881";
+ public static final String s27882 = "27882";
+ public static final String s27883 = "27883";
+ public static final String s27884 = "27884";
+ public static final String s27885 = "27885";
+ public static final String s27886 = "27886";
+ public static final String s27887 = "27887";
+ public static final String s27888 = "27888";
+ public static final String s27889 = "27889";
+ public static final String s27890 = "27890";
+ public static final String s27891 = "27891";
+ public static final String s27892 = "27892";
+ public static final String s27893 = "27893";
+ public static final String s27894 = "27894";
+ public static final String s27895 = "27895";
+ public static final String s27896 = "27896";
+ public static final String s27897 = "27897";
+ public static final String s27898 = "27898";
+ public static final String s27899 = "27899";
+ public static final String s27900 = "27900";
+ public static final String s27901 = "27901";
+ public static final String s27902 = "27902";
+ public static final String s27903 = "27903";
+ public static final String s27904 = "27904";
+ public static final String s27905 = "27905";
+ public static final String s27906 = "27906";
+ public static final String s27907 = "27907";
+ public static final String s27908 = "27908";
+ public static final String s27909 = "27909";
+ public static final String s27910 = "27910";
+ public static final String s27911 = "27911";
+ public static final String s27912 = "27912";
+ public static final String s27913 = "27913";
+ public static final String s27914 = "27914";
+ public static final String s27915 = "27915";
+ public static final String s27916 = "27916";
+ public static final String s27917 = "27917";
+ public static final String s27918 = "27918";
+ public static final String s27919 = "27919";
+ public static final String s27920 = "27920";
+ public static final String s27921 = "27921";
+ public static final String s27922 = "27922";
+ public static final String s27923 = "27923";
+ public static final String s27924 = "27924";
+ public static final String s27925 = "27925";
+ public static final String s27926 = "27926";
+ public static final String s27927 = "27927";
+ public static final String s27928 = "27928";
+ public static final String s27929 = "27929";
+ public static final String s27930 = "27930";
+ public static final String s27931 = "27931";
+ public static final String s27932 = "27932";
+ public static final String s27933 = "27933";
+ public static final String s27934 = "27934";
+ public static final String s27935 = "27935";
+ public static final String s27936 = "27936";
+ public static final String s27937 = "27937";
+ public static final String s27938 = "27938";
+ public static final String s27939 = "27939";
+ public static final String s27940 = "27940";
+ public static final String s27941 = "27941";
+ public static final String s27942 = "27942";
+ public static final String s27943 = "27943";
+ public static final String s27944 = "27944";
+ public static final String s27945 = "27945";
+ public static final String s27946 = "27946";
+ public static final String s27947 = "27947";
+ public static final String s27948 = "27948";
+ public static final String s27949 = "27949";
+ public static final String s27950 = "27950";
+ public static final String s27951 = "27951";
+ public static final String s27952 = "27952";
+ public static final String s27953 = "27953";
+ public static final String s27954 = "27954";
+ public static final String s27955 = "27955";
+ public static final String s27956 = "27956";
+ public static final String s27957 = "27957";
+ public static final String s27958 = "27958";
+ public static final String s27959 = "27959";
+ public static final String s27960 = "27960";
+ public static final String s27961 = "27961";
+ public static final String s27962 = "27962";
+ public static final String s27963 = "27963";
+ public static final String s27964 = "27964";
+ public static final String s27965 = "27965";
+ public static final String s27966 = "27966";
+ public static final String s27967 = "27967";
+ public static final String s27968 = "27968";
+ public static final String s27969 = "27969";
+ public static final String s27970 = "27970";
+ public static final String s27971 = "27971";
+ public static final String s27972 = "27972";
+ public static final String s27973 = "27973";
+ public static final String s27974 = "27974";
+ public static final String s27975 = "27975";
+ public static final String s27976 = "27976";
+ public static final String s27977 = "27977";
+ public static final String s27978 = "27978";
+ public static final String s27979 = "27979";
+ public static final String s27980 = "27980";
+ public static final String s27981 = "27981";
+ public static final String s27982 = "27982";
+ public static final String s27983 = "27983";
+ public static final String s27984 = "27984";
+ public static final String s27985 = "27985";
+ public static final String s27986 = "27986";
+ public static final String s27987 = "27987";
+ public static final String s27988 = "27988";
+ public static final String s27989 = "27989";
+ public static final String s27990 = "27990";
+ public static final String s27991 = "27991";
+ public static final String s27992 = "27992";
+ public static final String s27993 = "27993";
+ public static final String s27994 = "27994";
+ public static final String s27995 = "27995";
+ public static final String s27996 = "27996";
+ public static final String s27997 = "27997";
+ public static final String s27998 = "27998";
+ public static final String s27999 = "27999";
+ public static final String s28000 = "28000";
+ public static final String s28001 = "28001";
+ public static final String s28002 = "28002";
+ public static final String s28003 = "28003";
+ public static final String s28004 = "28004";
+ public static final String s28005 = "28005";
+ public static final String s28006 = "28006";
+ public static final String s28007 = "28007";
+ public static final String s28008 = "28008";
+ public static final String s28009 = "28009";
+ public static final String s28010 = "28010";
+ public static final String s28011 = "28011";
+ public static final String s28012 = "28012";
+ public static final String s28013 = "28013";
+ public static final String s28014 = "28014";
+ public static final String s28015 = "28015";
+ public static final String s28016 = "28016";
+ public static final String s28017 = "28017";
+ public static final String s28018 = "28018";
+ public static final String s28019 = "28019";
+ public static final String s28020 = "28020";
+ public static final String s28021 = "28021";
+ public static final String s28022 = "28022";
+ public static final String s28023 = "28023";
+ public static final String s28024 = "28024";
+ public static final String s28025 = "28025";
+ public static final String s28026 = "28026";
+ public static final String s28027 = "28027";
+ public static final String s28028 = "28028";
+ public static final String s28029 = "28029";
+ public static final String s28030 = "28030";
+ public static final String s28031 = "28031";
+ public static final String s28032 = "28032";
+ public static final String s28033 = "28033";
+ public static final String s28034 = "28034";
+ public static final String s28035 = "28035";
+ public static final String s28036 = "28036";
+ public static final String s28037 = "28037";
+ public static final String s28038 = "28038";
+ public static final String s28039 = "28039";
+ public static final String s28040 = "28040";
+ public static final String s28041 = "28041";
+ public static final String s28042 = "28042";
+ public static final String s28043 = "28043";
+ public static final String s28044 = "28044";
+ public static final String s28045 = "28045";
+ public static final String s28046 = "28046";
+ public static final String s28047 = "28047";
+ public static final String s28048 = "28048";
+ public static final String s28049 = "28049";
+ public static final String s28050 = "28050";
+ public static final String s28051 = "28051";
+ public static final String s28052 = "28052";
+ public static final String s28053 = "28053";
+ public static final String s28054 = "28054";
+ public static final String s28055 = "28055";
+ public static final String s28056 = "28056";
+ public static final String s28057 = "28057";
+ public static final String s28058 = "28058";
+ public static final String s28059 = "28059";
+ public static final String s28060 = "28060";
+ public static final String s28061 = "28061";
+ public static final String s28062 = "28062";
+ public static final String s28063 = "28063";
+ public static final String s28064 = "28064";
+ public static final String s28065 = "28065";
+ public static final String s28066 = "28066";
+ public static final String s28067 = "28067";
+ public static final String s28068 = "28068";
+ public static final String s28069 = "28069";
+ public static final String s28070 = "28070";
+ public static final String s28071 = "28071";
+ public static final String s28072 = "28072";
+ public static final String s28073 = "28073";
+ public static final String s28074 = "28074";
+ public static final String s28075 = "28075";
+ public static final String s28076 = "28076";
+ public static final String s28077 = "28077";
+ public static final String s28078 = "28078";
+ public static final String s28079 = "28079";
+ public static final String s28080 = "28080";
+ public static final String s28081 = "28081";
+ public static final String s28082 = "28082";
+ public static final String s28083 = "28083";
+ public static final String s28084 = "28084";
+ public static final String s28085 = "28085";
+ public static final String s28086 = "28086";
+ public static final String s28087 = "28087";
+ public static final String s28088 = "28088";
+ public static final String s28089 = "28089";
+ public static final String s28090 = "28090";
+ public static final String s28091 = "28091";
+ public static final String s28092 = "28092";
+ public static final String s28093 = "28093";
+ public static final String s28094 = "28094";
+ public static final String s28095 = "28095";
+ public static final String s28096 = "28096";
+ public static final String s28097 = "28097";
+ public static final String s28098 = "28098";
+ public static final String s28099 = "28099";
+ public static final String s28100 = "28100";
+ public static final String s28101 = "28101";
+ public static final String s28102 = "28102";
+ public static final String s28103 = "28103";
+ public static final String s28104 = "28104";
+ public static final String s28105 = "28105";
+ public static final String s28106 = "28106";
+ public static final String s28107 = "28107";
+ public static final String s28108 = "28108";
+ public static final String s28109 = "28109";
+ public static final String s28110 = "28110";
+ public static final String s28111 = "28111";
+ public static final String s28112 = "28112";
+ public static final String s28113 = "28113";
+ public static final String s28114 = "28114";
+ public static final String s28115 = "28115";
+ public static final String s28116 = "28116";
+ public static final String s28117 = "28117";
+ public static final String s28118 = "28118";
+ public static final String s28119 = "28119";
+ public static final String s28120 = "28120";
+ public static final String s28121 = "28121";
+ public static final String s28122 = "28122";
+ public static final String s28123 = "28123";
+ public static final String s28124 = "28124";
+ public static final String s28125 = "28125";
+ public static final String s28126 = "28126";
+ public static final String s28127 = "28127";
+ public static final String s28128 = "28128";
+ public static final String s28129 = "28129";
+ public static final String s28130 = "28130";
+ public static final String s28131 = "28131";
+ public static final String s28132 = "28132";
+ public static final String s28133 = "28133";
+ public static final String s28134 = "28134";
+ public static final String s28135 = "28135";
+ public static final String s28136 = "28136";
+ public static final String s28137 = "28137";
+ public static final String s28138 = "28138";
+ public static final String s28139 = "28139";
+ public static final String s28140 = "28140";
+ public static final String s28141 = "28141";
+ public static final String s28142 = "28142";
+ public static final String s28143 = "28143";
+ public static final String s28144 = "28144";
+ public static final String s28145 = "28145";
+ public static final String s28146 = "28146";
+ public static final String s28147 = "28147";
+ public static final String s28148 = "28148";
+ public static final String s28149 = "28149";
+ public static final String s28150 = "28150";
+ public static final String s28151 = "28151";
+ public static final String s28152 = "28152";
+ public static final String s28153 = "28153";
+ public static final String s28154 = "28154";
+ public static final String s28155 = "28155";
+ public static final String s28156 = "28156";
+ public static final String s28157 = "28157";
+ public static final String s28158 = "28158";
+ public static final String s28159 = "28159";
+ public static final String s28160 = "28160";
+ public static final String s28161 = "28161";
+ public static final String s28162 = "28162";
+ public static final String s28163 = "28163";
+ public static final String s28164 = "28164";
+ public static final String s28165 = "28165";
+ public static final String s28166 = "28166";
+ public static final String s28167 = "28167";
+ public static final String s28168 = "28168";
+ public static final String s28169 = "28169";
+ public static final String s28170 = "28170";
+ public static final String s28171 = "28171";
+ public static final String s28172 = "28172";
+ public static final String s28173 = "28173";
+ public static final String s28174 = "28174";
+ public static final String s28175 = "28175";
+ public static final String s28176 = "28176";
+ public static final String s28177 = "28177";
+ public static final String s28178 = "28178";
+ public static final String s28179 = "28179";
+ public static final String s28180 = "28180";
+ public static final String s28181 = "28181";
+ public static final String s28182 = "28182";
+ public static final String s28183 = "28183";
+ public static final String s28184 = "28184";
+ public static final String s28185 = "28185";
+ public static final String s28186 = "28186";
+ public static final String s28187 = "28187";
+ public static final String s28188 = "28188";
+ public static final String s28189 = "28189";
+ public static final String s28190 = "28190";
+ public static final String s28191 = "28191";
+ public static final String s28192 = "28192";
+ public static final String s28193 = "28193";
+ public static final String s28194 = "28194";
+ public static final String s28195 = "28195";
+ public static final String s28196 = "28196";
+ public static final String s28197 = "28197";
+ public static final String s28198 = "28198";
+ public static final String s28199 = "28199";
+ public static final String s28200 = "28200";
+ public static final String s28201 = "28201";
+ public static final String s28202 = "28202";
+ public static final String s28203 = "28203";
+ public static final String s28204 = "28204";
+ public static final String s28205 = "28205";
+ public static final String s28206 = "28206";
+ public static final String s28207 = "28207";
+ public static final String s28208 = "28208";
+ public static final String s28209 = "28209";
+ public static final String s28210 = "28210";
+ public static final String s28211 = "28211";
+ public static final String s28212 = "28212";
+ public static final String s28213 = "28213";
+ public static final String s28214 = "28214";
+ public static final String s28215 = "28215";
+ public static final String s28216 = "28216";
+ public static final String s28217 = "28217";
+ public static final String s28218 = "28218";
+ public static final String s28219 = "28219";
+ public static final String s28220 = "28220";
+ public static final String s28221 = "28221";
+ public static final String s28222 = "28222";
+ public static final String s28223 = "28223";
+ public static final String s28224 = "28224";
+ public static final String s28225 = "28225";
+ public static final String s28226 = "28226";
+ public static final String s28227 = "28227";
+ public static final String s28228 = "28228";
+ public static final String s28229 = "28229";
+ public static final String s28230 = "28230";
+ public static final String s28231 = "28231";
+ public static final String s28232 = "28232";
+ public static final String s28233 = "28233";
+ public static final String s28234 = "28234";
+ public static final String s28235 = "28235";
+ public static final String s28236 = "28236";
+ public static final String s28237 = "28237";
+ public static final String s28238 = "28238";
+ public static final String s28239 = "28239";
+ public static final String s28240 = "28240";
+ public static final String s28241 = "28241";
+ public static final String s28242 = "28242";
+ public static final String s28243 = "28243";
+ public static final String s28244 = "28244";
+ public static final String s28245 = "28245";
+ public static final String s28246 = "28246";
+ public static final String s28247 = "28247";
+ public static final String s28248 = "28248";
+ public static final String s28249 = "28249";
+ public static final String s28250 = "28250";
+ public static final String s28251 = "28251";
+ public static final String s28252 = "28252";
+ public static final String s28253 = "28253";
+ public static final String s28254 = "28254";
+ public static final String s28255 = "28255";
+ public static final String s28256 = "28256";
+ public static final String s28257 = "28257";
+ public static final String s28258 = "28258";
+ public static final String s28259 = "28259";
+ public static final String s28260 = "28260";
+ public static final String s28261 = "28261";
+ public static final String s28262 = "28262";
+ public static final String s28263 = "28263";
+ public static final String s28264 = "28264";
+ public static final String s28265 = "28265";
+ public static final String s28266 = "28266";
+ public static final String s28267 = "28267";
+ public static final String s28268 = "28268";
+ public static final String s28269 = "28269";
+ public static final String s28270 = "28270";
+ public static final String s28271 = "28271";
+ public static final String s28272 = "28272";
+ public static final String s28273 = "28273";
+ public static final String s28274 = "28274";
+ public static final String s28275 = "28275";
+ public static final String s28276 = "28276";
+ public static final String s28277 = "28277";
+ public static final String s28278 = "28278";
+ public static final String s28279 = "28279";
+ public static final String s28280 = "28280";
+ public static final String s28281 = "28281";
+ public static final String s28282 = "28282";
+ public static final String s28283 = "28283";
+ public static final String s28284 = "28284";
+ public static final String s28285 = "28285";
+ public static final String s28286 = "28286";
+ public static final String s28287 = "28287";
+ public static final String s28288 = "28288";
+ public static final String s28289 = "28289";
+ public static final String s28290 = "28290";
+ public static final String s28291 = "28291";
+ public static final String s28292 = "28292";
+ public static final String s28293 = "28293";
+ public static final String s28294 = "28294";
+ public static final String s28295 = "28295";
+ public static final String s28296 = "28296";
+ public static final String s28297 = "28297";
+ public static final String s28298 = "28298";
+ public static final String s28299 = "28299";
+ public static final String s28300 = "28300";
+ public static final String s28301 = "28301";
+ public static final String s28302 = "28302";
+ public static final String s28303 = "28303";
+ public static final String s28304 = "28304";
+ public static final String s28305 = "28305";
+ public static final String s28306 = "28306";
+ public static final String s28307 = "28307";
+ public static final String s28308 = "28308";
+ public static final String s28309 = "28309";
+ public static final String s28310 = "28310";
+ public static final String s28311 = "28311";
+ public static final String s28312 = "28312";
+ public static final String s28313 = "28313";
+ public static final String s28314 = "28314";
+ public static final String s28315 = "28315";
+ public static final String s28316 = "28316";
+ public static final String s28317 = "28317";
+ public static final String s28318 = "28318";
+ public static final String s28319 = "28319";
+ public static final String s28320 = "28320";
+ public static final String s28321 = "28321";
+ public static final String s28322 = "28322";
+ public static final String s28323 = "28323";
+ public static final String s28324 = "28324";
+ public static final String s28325 = "28325";
+ public static final String s28326 = "28326";
+ public static final String s28327 = "28327";
+ public static final String s28328 = "28328";
+ public static final String s28329 = "28329";
+ public static final String s28330 = "28330";
+ public static final String s28331 = "28331";
+ public static final String s28332 = "28332";
+ public static final String s28333 = "28333";
+ public static final String s28334 = "28334";
+ public static final String s28335 = "28335";
+ public static final String s28336 = "28336";
+ public static final String s28337 = "28337";
+ public static final String s28338 = "28338";
+ public static final String s28339 = "28339";
+ public static final String s28340 = "28340";
+ public static final String s28341 = "28341";
+ public static final String s28342 = "28342";
+ public static final String s28343 = "28343";
+ public static final String s28344 = "28344";
+ public static final String s28345 = "28345";
+ public static final String s28346 = "28346";
+ public static final String s28347 = "28347";
+ public static final String s28348 = "28348";
+ public static final String s28349 = "28349";
+ public static final String s28350 = "28350";
+ public static final String s28351 = "28351";
+ public static final String s28352 = "28352";
+ public static final String s28353 = "28353";
+ public static final String s28354 = "28354";
+ public static final String s28355 = "28355";
+ public static final String s28356 = "28356";
+ public static final String s28357 = "28357";
+ public static final String s28358 = "28358";
+ public static final String s28359 = "28359";
+ public static final String s28360 = "28360";
+ public static final String s28361 = "28361";
+ public static final String s28362 = "28362";
+ public static final String s28363 = "28363";
+ public static final String s28364 = "28364";
+ public static final String s28365 = "28365";
+ public static final String s28366 = "28366";
+ public static final String s28367 = "28367";
+ public static final String s28368 = "28368";
+ public static final String s28369 = "28369";
+ public static final String s28370 = "28370";
+ public static final String s28371 = "28371";
+ public static final String s28372 = "28372";
+ public static final String s28373 = "28373";
+ public static final String s28374 = "28374";
+ public static final String s28375 = "28375";
+ public static final String s28376 = "28376";
+ public static final String s28377 = "28377";
+ public static final String s28378 = "28378";
+ public static final String s28379 = "28379";
+ public static final String s28380 = "28380";
+ public static final String s28381 = "28381";
+ public static final String s28382 = "28382";
+ public static final String s28383 = "28383";
+ public static final String s28384 = "28384";
+ public static final String s28385 = "28385";
+ public static final String s28386 = "28386";
+ public static final String s28387 = "28387";
+ public static final String s28388 = "28388";
+ public static final String s28389 = "28389";
+ public static final String s28390 = "28390";
+ public static final String s28391 = "28391";
+ public static final String s28392 = "28392";
+ public static final String s28393 = "28393";
+ public static final String s28394 = "28394";
+ public static final String s28395 = "28395";
+ public static final String s28396 = "28396";
+ public static final String s28397 = "28397";
+ public static final String s28398 = "28398";
+ public static final String s28399 = "28399";
+ public static final String s28400 = "28400";
+ public static final String s28401 = "28401";
+ public static final String s28402 = "28402";
+ public static final String s28403 = "28403";
+ public static final String s28404 = "28404";
+ public static final String s28405 = "28405";
+ public static final String s28406 = "28406";
+ public static final String s28407 = "28407";
+ public static final String s28408 = "28408";
+ public static final String s28409 = "28409";
+ public static final String s28410 = "28410";
+ public static final String s28411 = "28411";
+ public static final String s28412 = "28412";
+ public static final String s28413 = "28413";
+ public static final String s28414 = "28414";
+ public static final String s28415 = "28415";
+ public static final String s28416 = "28416";
+ public static final String s28417 = "28417";
+ public static final String s28418 = "28418";
+ public static final String s28419 = "28419";
+ public static final String s28420 = "28420";
+ public static final String s28421 = "28421";
+ public static final String s28422 = "28422";
+ public static final String s28423 = "28423";
+ public static final String s28424 = "28424";
+ public static final String s28425 = "28425";
+ public static final String s28426 = "28426";
+ public static final String s28427 = "28427";
+ public static final String s28428 = "28428";
+ public static final String s28429 = "28429";
+ public static final String s28430 = "28430";
+ public static final String s28431 = "28431";
+ public static final String s28432 = "28432";
+ public static final String s28433 = "28433";
+ public static final String s28434 = "28434";
+ public static final String s28435 = "28435";
+ public static final String s28436 = "28436";
+ public static final String s28437 = "28437";
+ public static final String s28438 = "28438";
+ public static final String s28439 = "28439";
+ public static final String s28440 = "28440";
+ public static final String s28441 = "28441";
+ public static final String s28442 = "28442";
+ public static final String s28443 = "28443";
+ public static final String s28444 = "28444";
+ public static final String s28445 = "28445";
+ public static final String s28446 = "28446";
+ public static final String s28447 = "28447";
+ public static final String s28448 = "28448";
+ public static final String s28449 = "28449";
+ public static final String s28450 = "28450";
+ public static final String s28451 = "28451";
+ public static final String s28452 = "28452";
+ public static final String s28453 = "28453";
+ public static final String s28454 = "28454";
+ public static final String s28455 = "28455";
+ public static final String s28456 = "28456";
+ public static final String s28457 = "28457";
+ public static final String s28458 = "28458";
+ public static final String s28459 = "28459";
+ public static final String s28460 = "28460";
+ public static final String s28461 = "28461";
+ public static final String s28462 = "28462";
+ public static final String s28463 = "28463";
+ public static final String s28464 = "28464";
+ public static final String s28465 = "28465";
+ public static final String s28466 = "28466";
+ public static final String s28467 = "28467";
+ public static final String s28468 = "28468";
+ public static final String s28469 = "28469";
+ public static final String s28470 = "28470";
+ public static final String s28471 = "28471";
+ public static final String s28472 = "28472";
+ public static final String s28473 = "28473";
+ public static final String s28474 = "28474";
+ public static final String s28475 = "28475";
+ public static final String s28476 = "28476";
+ public static final String s28477 = "28477";
+ public static final String s28478 = "28478";
+ public static final String s28479 = "28479";
+ public static final String s28480 = "28480";
+ public static final String s28481 = "28481";
+ public static final String s28482 = "28482";
+ public static final String s28483 = "28483";
+ public static final String s28484 = "28484";
+ public static final String s28485 = "28485";
+ public static final String s28486 = "28486";
+ public static final String s28487 = "28487";
+ public static final String s28488 = "28488";
+ public static final String s28489 = "28489";
+ public static final String s28490 = "28490";
+ public static final String s28491 = "28491";
+ public static final String s28492 = "28492";
+ public static final String s28493 = "28493";
+ public static final String s28494 = "28494";
+ public static final String s28495 = "28495";
+ public static final String s28496 = "28496";
+ public static final String s28497 = "28497";
+ public static final String s28498 = "28498";
+ public static final String s28499 = "28499";
+ public static final String s28500 = "28500";
+ public static final String s28501 = "28501";
+ public static final String s28502 = "28502";
+ public static final String s28503 = "28503";
+ public static final String s28504 = "28504";
+ public static final String s28505 = "28505";
+ public static final String s28506 = "28506";
+ public static final String s28507 = "28507";
+ public static final String s28508 = "28508";
+ public static final String s28509 = "28509";
+ public static final String s28510 = "28510";
+ public static final String s28511 = "28511";
+ public static final String s28512 = "28512";
+ public static final String s28513 = "28513";
+ public static final String s28514 = "28514";
+ public static final String s28515 = "28515";
+ public static final String s28516 = "28516";
+ public static final String s28517 = "28517";
+ public static final String s28518 = "28518";
+ public static final String s28519 = "28519";
+ public static final String s28520 = "28520";
+ public static final String s28521 = "28521";
+ public static final String s28522 = "28522";
+ public static final String s28523 = "28523";
+ public static final String s28524 = "28524";
+ public static final String s28525 = "28525";
+ public static final String s28526 = "28526";
+ public static final String s28527 = "28527";
+ public static final String s28528 = "28528";
+ public static final String s28529 = "28529";
+ public static final String s28530 = "28530";
+ public static final String s28531 = "28531";
+ public static final String s28532 = "28532";
+ public static final String s28533 = "28533";
+ public static final String s28534 = "28534";
+ public static final String s28535 = "28535";
+ public static final String s28536 = "28536";
+ public static final String s28537 = "28537";
+ public static final String s28538 = "28538";
+ public static final String s28539 = "28539";
+ public static final String s28540 = "28540";
+ public static final String s28541 = "28541";
+ public static final String s28542 = "28542";
+ public static final String s28543 = "28543";
+ public static final String s28544 = "28544";
+ public static final String s28545 = "28545";
+ public static final String s28546 = "28546";
+ public static final String s28547 = "28547";
+ public static final String s28548 = "28548";
+ public static final String s28549 = "28549";
+ public static final String s28550 = "28550";
+ public static final String s28551 = "28551";
+ public static final String s28552 = "28552";
+ public static final String s28553 = "28553";
+ public static final String s28554 = "28554";
+ public static final String s28555 = "28555";
+ public static final String s28556 = "28556";
+ public static final String s28557 = "28557";
+ public static final String s28558 = "28558";
+ public static final String s28559 = "28559";
+ public static final String s28560 = "28560";
+ public static final String s28561 = "28561";
+ public static final String s28562 = "28562";
+ public static final String s28563 = "28563";
+ public static final String s28564 = "28564";
+ public static final String s28565 = "28565";
+ public static final String s28566 = "28566";
+ public static final String s28567 = "28567";
+ public static final String s28568 = "28568";
+ public static final String s28569 = "28569";
+ public static final String s28570 = "28570";
+ public static final String s28571 = "28571";
+ public static final String s28572 = "28572";
+ public static final String s28573 = "28573";
+ public static final String s28574 = "28574";
+ public static final String s28575 = "28575";
+ public static final String s28576 = "28576";
+ public static final String s28577 = "28577";
+ public static final String s28578 = "28578";
+ public static final String s28579 = "28579";
+ public static final String s28580 = "28580";
+ public static final String s28581 = "28581";
+ public static final String s28582 = "28582";
+ public static final String s28583 = "28583";
+ public static final String s28584 = "28584";
+ public static final String s28585 = "28585";
+ public static final String s28586 = "28586";
+ public static final String s28587 = "28587";
+ public static final String s28588 = "28588";
+ public static final String s28589 = "28589";
+ public static final String s28590 = "28590";
+ public static final String s28591 = "28591";
+ public static final String s28592 = "28592";
+ public static final String s28593 = "28593";
+ public static final String s28594 = "28594";
+ public static final String s28595 = "28595";
+ public static final String s28596 = "28596";
+ public static final String s28597 = "28597";
+ public static final String s28598 = "28598";
+ public static final String s28599 = "28599";
+ public static final String s28600 = "28600";
+ public static final String s28601 = "28601";
+ public static final String s28602 = "28602";
+ public static final String s28603 = "28603";
+ public static final String s28604 = "28604";
+ public static final String s28605 = "28605";
+ public static final String s28606 = "28606";
+ public static final String s28607 = "28607";
+ public static final String s28608 = "28608";
+ public static final String s28609 = "28609";
+ public static final String s28610 = "28610";
+ public static final String s28611 = "28611";
+ public static final String s28612 = "28612";
+ public static final String s28613 = "28613";
+ public static final String s28614 = "28614";
+ public static final String s28615 = "28615";
+ public static final String s28616 = "28616";
+ public static final String s28617 = "28617";
+ public static final String s28618 = "28618";
+ public static final String s28619 = "28619";
+ public static final String s28620 = "28620";
+ public static final String s28621 = "28621";
+ public static final String s28622 = "28622";
+ public static final String s28623 = "28623";
+ public static final String s28624 = "28624";
+ public static final String s28625 = "28625";
+ public static final String s28626 = "28626";
+ public static final String s28627 = "28627";
+ public static final String s28628 = "28628";
+ public static final String s28629 = "28629";
+ public static final String s28630 = "28630";
+ public static final String s28631 = "28631";
+ public static final String s28632 = "28632";
+ public static final String s28633 = "28633";
+ public static final String s28634 = "28634";
+ public static final String s28635 = "28635";
+ public static final String s28636 = "28636";
+ public static final String s28637 = "28637";
+ public static final String s28638 = "28638";
+ public static final String s28639 = "28639";
+ public static final String s28640 = "28640";
+ public static final String s28641 = "28641";
+ public static final String s28642 = "28642";
+ public static final String s28643 = "28643";
+ public static final String s28644 = "28644";
+ public static final String s28645 = "28645";
+ public static final String s28646 = "28646";
+ public static final String s28647 = "28647";
+ public static final String s28648 = "28648";
+ public static final String s28649 = "28649";
+ public static final String s28650 = "28650";
+ public static final String s28651 = "28651";
+ public static final String s28652 = "28652";
+ public static final String s28653 = "28653";
+ public static final String s28654 = "28654";
+ public static final String s28655 = "28655";
+ public static final String s28656 = "28656";
+ public static final String s28657 = "28657";
+ public static final String s28658 = "28658";
+ public static final String s28659 = "28659";
+ public static final String s28660 = "28660";
+ public static final String s28661 = "28661";
+ public static final String s28662 = "28662";
+ public static final String s28663 = "28663";
+ public static final String s28664 = "28664";
+ public static final String s28665 = "28665";
+ public static final String s28666 = "28666";
+ public static final String s28667 = "28667";
+ public static final String s28668 = "28668";
+ public static final String s28669 = "28669";
+ public static final String s28670 = "28670";
+ public static final String s28671 = "28671";
+ public static final String s28672 = "28672";
+ public static final String s28673 = "28673";
+ public static final String s28674 = "28674";
+ public static final String s28675 = "28675";
+ public static final String s28676 = "28676";
+ public static final String s28677 = "28677";
+ public static final String s28678 = "28678";
+ public static final String s28679 = "28679";
+ public static final String s28680 = "28680";
+ public static final String s28681 = "28681";
+ public static final String s28682 = "28682";
+ public static final String s28683 = "28683";
+ public static final String s28684 = "28684";
+ public static final String s28685 = "28685";
+ public static final String s28686 = "28686";
+ public static final String s28687 = "28687";
+ public static final String s28688 = "28688";
+ public static final String s28689 = "28689";
+ public static final String s28690 = "28690";
+ public static final String s28691 = "28691";
+ public static final String s28692 = "28692";
+ public static final String s28693 = "28693";
+ public static final String s28694 = "28694";
+ public static final String s28695 = "28695";
+ public static final String s28696 = "28696";
+ public static final String s28697 = "28697";
+ public static final String s28698 = "28698";
+ public static final String s28699 = "28699";
+ public static final String s28700 = "28700";
+ public static final String s28701 = "28701";
+ public static final String s28702 = "28702";
+ public static final String s28703 = "28703";
+ public static final String s28704 = "28704";
+ public static final String s28705 = "28705";
+ public static final String s28706 = "28706";
+ public static final String s28707 = "28707";
+ public static final String s28708 = "28708";
+ public static final String s28709 = "28709";
+ public static final String s28710 = "28710";
+ public static final String s28711 = "28711";
+ public static final String s28712 = "28712";
+ public static final String s28713 = "28713";
+ public static final String s28714 = "28714";
+ public static final String s28715 = "28715";
+ public static final String s28716 = "28716";
+ public static final String s28717 = "28717";
+ public static final String s28718 = "28718";
+ public static final String s28719 = "28719";
+ public static final String s28720 = "28720";
+ public static final String s28721 = "28721";
+ public static final String s28722 = "28722";
+ public static final String s28723 = "28723";
+ public static final String s28724 = "28724";
+ public static final String s28725 = "28725";
+ public static final String s28726 = "28726";
+ public static final String s28727 = "28727";
+ public static final String s28728 = "28728";
+ public static final String s28729 = "28729";
+ public static final String s28730 = "28730";
+ public static final String s28731 = "28731";
+ public static final String s28732 = "28732";
+ public static final String s28733 = "28733";
+ public static final String s28734 = "28734";
+ public static final String s28735 = "28735";
+ public static final String s28736 = "28736";
+ public static final String s28737 = "28737";
+ public static final String s28738 = "28738";
+ public static final String s28739 = "28739";
+ public static final String s28740 = "28740";
+ public static final String s28741 = "28741";
+ public static final String s28742 = "28742";
+ public static final String s28743 = "28743";
+ public static final String s28744 = "28744";
+ public static final String s28745 = "28745";
+ public static final String s28746 = "28746";
+ public static final String s28747 = "28747";
+ public static final String s28748 = "28748";
+ public static final String s28749 = "28749";
+ public static final String s28750 = "28750";
+ public static final String s28751 = "28751";
+ public static final String s28752 = "28752";
+ public static final String s28753 = "28753";
+ public static final String s28754 = "28754";
+ public static final String s28755 = "28755";
+ public static final String s28756 = "28756";
+ public static final String s28757 = "28757";
+ public static final String s28758 = "28758";
+ public static final String s28759 = "28759";
+ public static final String s28760 = "28760";
+ public static final String s28761 = "28761";
+ public static final String s28762 = "28762";
+ public static final String s28763 = "28763";
+ public static final String s28764 = "28764";
+ public static final String s28765 = "28765";
+ public static final String s28766 = "28766";
+ public static final String s28767 = "28767";
+ public static final String s28768 = "28768";
+ public static final String s28769 = "28769";
+ public static final String s28770 = "28770";
+ public static final String s28771 = "28771";
+ public static final String s28772 = "28772";
+ public static final String s28773 = "28773";
+ public static final String s28774 = "28774";
+ public static final String s28775 = "28775";
+ public static final String s28776 = "28776";
+ public static final String s28777 = "28777";
+ public static final String s28778 = "28778";
+ public static final String s28779 = "28779";
+ public static final String s28780 = "28780";
+ public static final String s28781 = "28781";
+ public static final String s28782 = "28782";
+ public static final String s28783 = "28783";
+ public static final String s28784 = "28784";
+ public static final String s28785 = "28785";
+ public static final String s28786 = "28786";
+ public static final String s28787 = "28787";
+ public static final String s28788 = "28788";
+ public static final String s28789 = "28789";
+ public static final String s28790 = "28790";
+ public static final String s28791 = "28791";
+ public static final String s28792 = "28792";
+ public static final String s28793 = "28793";
+ public static final String s28794 = "28794";
+ public static final String s28795 = "28795";
+ public static final String s28796 = "28796";
+ public static final String s28797 = "28797";
+ public static final String s28798 = "28798";
+ public static final String s28799 = "28799";
+ public static final String s28800 = "28800";
+ public static final String s28801 = "28801";
+ public static final String s28802 = "28802";
+ public static final String s28803 = "28803";
+ public static final String s28804 = "28804";
+ public static final String s28805 = "28805";
+ public static final String s28806 = "28806";
+ public static final String s28807 = "28807";
+ public static final String s28808 = "28808";
+ public static final String s28809 = "28809";
+ public static final String s28810 = "28810";
+ public static final String s28811 = "28811";
+ public static final String s28812 = "28812";
+ public static final String s28813 = "28813";
+ public static final String s28814 = "28814";
+ public static final String s28815 = "28815";
+ public static final String s28816 = "28816";
+ public static final String s28817 = "28817";
+ public static final String s28818 = "28818";
+ public static final String s28819 = "28819";
+ public static final String s28820 = "28820";
+ public static final String s28821 = "28821";
+ public static final String s28822 = "28822";
+ public static final String s28823 = "28823";
+ public static final String s28824 = "28824";
+ public static final String s28825 = "28825";
+ public static final String s28826 = "28826";
+ public static final String s28827 = "28827";
+ public static final String s28828 = "28828";
+ public static final String s28829 = "28829";
+ public static final String s28830 = "28830";
+ public static final String s28831 = "28831";
+ public static final String s28832 = "28832";
+ public static final String s28833 = "28833";
+ public static final String s28834 = "28834";
+ public static final String s28835 = "28835";
+ public static final String s28836 = "28836";
+ public static final String s28837 = "28837";
+ public static final String s28838 = "28838";
+ public static final String s28839 = "28839";
+ public static final String s28840 = "28840";
+ public static final String s28841 = "28841";
+ public static final String s28842 = "28842";
+ public static final String s28843 = "28843";
+ public static final String s28844 = "28844";
+ public static final String s28845 = "28845";
+ public static final String s28846 = "28846";
+ public static final String s28847 = "28847";
+ public static final String s28848 = "28848";
+ public static final String s28849 = "28849";
+ public static final String s28850 = "28850";
+ public static final String s28851 = "28851";
+ public static final String s28852 = "28852";
+ public static final String s28853 = "28853";
+ public static final String s28854 = "28854";
+ public static final String s28855 = "28855";
+ public static final String s28856 = "28856";
+ public static final String s28857 = "28857";
+ public static final String s28858 = "28858";
+ public static final String s28859 = "28859";
+ public static final String s28860 = "28860";
+ public static final String s28861 = "28861";
+ public static final String s28862 = "28862";
+ public static final String s28863 = "28863";
+ public static final String s28864 = "28864";
+ public static final String s28865 = "28865";
+ public static final String s28866 = "28866";
+ public static final String s28867 = "28867";
+ public static final String s28868 = "28868";
+ public static final String s28869 = "28869";
+ public static final String s28870 = "28870";
+ public static final String s28871 = "28871";
+ public static final String s28872 = "28872";
+ public static final String s28873 = "28873";
+ public static final String s28874 = "28874";
+ public static final String s28875 = "28875";
+ public static final String s28876 = "28876";
+ public static final String s28877 = "28877";
+ public static final String s28878 = "28878";
+ public static final String s28879 = "28879";
+ public static final String s28880 = "28880";
+ public static final String s28881 = "28881";
+ public static final String s28882 = "28882";
+ public static final String s28883 = "28883";
+ public static final String s28884 = "28884";
+ public static final String s28885 = "28885";
+ public static final String s28886 = "28886";
+ public static final String s28887 = "28887";
+ public static final String s28888 = "28888";
+ public static final String s28889 = "28889";
+ public static final String s28890 = "28890";
+ public static final String s28891 = "28891";
+ public static final String s28892 = "28892";
+ public static final String s28893 = "28893";
+ public static final String s28894 = "28894";
+ public static final String s28895 = "28895";
+ public static final String s28896 = "28896";
+ public static final String s28897 = "28897";
+ public static final String s28898 = "28898";
+ public static final String s28899 = "28899";
+ public static final String s28900 = "28900";
+ public static final String s28901 = "28901";
+ public static final String s28902 = "28902";
+ public static final String s28903 = "28903";
+ public static final String s28904 = "28904";
+ public static final String s28905 = "28905";
+ public static final String s28906 = "28906";
+ public static final String s28907 = "28907";
+ public static final String s28908 = "28908";
+ public static final String s28909 = "28909";
+ public static final String s28910 = "28910";
+ public static final String s28911 = "28911";
+ public static final String s28912 = "28912";
+ public static final String s28913 = "28913";
+ public static final String s28914 = "28914";
+ public static final String s28915 = "28915";
+ public static final String s28916 = "28916";
+ public static final String s28917 = "28917";
+ public static final String s28918 = "28918";
+ public static final String s28919 = "28919";
+ public static final String s28920 = "28920";
+ public static final String s28921 = "28921";
+ public static final String s28922 = "28922";
+ public static final String s28923 = "28923";
+ public static final String s28924 = "28924";
+ public static final String s28925 = "28925";
+ public static final String s28926 = "28926";
+ public static final String s28927 = "28927";
+ public static final String s28928 = "28928";
+ public static final String s28929 = "28929";
+ public static final String s28930 = "28930";
+ public static final String s28931 = "28931";
+ public static final String s28932 = "28932";
+ public static final String s28933 = "28933";
+ public static final String s28934 = "28934";
+ public static final String s28935 = "28935";
+ public static final String s28936 = "28936";
+ public static final String s28937 = "28937";
+ public static final String s28938 = "28938";
+ public static final String s28939 = "28939";
+ public static final String s28940 = "28940";
+ public static final String s28941 = "28941";
+ public static final String s28942 = "28942";
+ public static final String s28943 = "28943";
+ public static final String s28944 = "28944";
+ public static final String s28945 = "28945";
+ public static final String s28946 = "28946";
+ public static final String s28947 = "28947";
+ public static final String s28948 = "28948";
+ public static final String s28949 = "28949";
+ public static final String s28950 = "28950";
+ public static final String s28951 = "28951";
+ public static final String s28952 = "28952";
+ public static final String s28953 = "28953";
+ public static final String s28954 = "28954";
+ public static final String s28955 = "28955";
+ public static final String s28956 = "28956";
+ public static final String s28957 = "28957";
+ public static final String s28958 = "28958";
+ public static final String s28959 = "28959";
+ public static final String s28960 = "28960";
+ public static final String s28961 = "28961";
+ public static final String s28962 = "28962";
+ public static final String s28963 = "28963";
+ public static final String s28964 = "28964";
+ public static final String s28965 = "28965";
+ public static final String s28966 = "28966";
+ public static final String s28967 = "28967";
+ public static final String s28968 = "28968";
+ public static final String s28969 = "28969";
+ public static final String s28970 = "28970";
+ public static final String s28971 = "28971";
+ public static final String s28972 = "28972";
+ public static final String s28973 = "28973";
+ public static final String s28974 = "28974";
+ public static final String s28975 = "28975";
+ public static final String s28976 = "28976";
+ public static final String s28977 = "28977";
+ public static final String s28978 = "28978";
+ public static final String s28979 = "28979";
+ public static final String s28980 = "28980";
+ public static final String s28981 = "28981";
+ public static final String s28982 = "28982";
+ public static final String s28983 = "28983";
+ public static final String s28984 = "28984";
+ public static final String s28985 = "28985";
+ public static final String s28986 = "28986";
+ public static final String s28987 = "28987";
+ public static final String s28988 = "28988";
+ public static final String s28989 = "28989";
+ public static final String s28990 = "28990";
+ public static final String s28991 = "28991";
+ public static final String s28992 = "28992";
+ public static final String s28993 = "28993";
+ public static final String s28994 = "28994";
+ public static final String s28995 = "28995";
+ public static final String s28996 = "28996";
+ public static final String s28997 = "28997";
+ public static final String s28998 = "28998";
+ public static final String s28999 = "28999";
+ public static final String s29000 = "29000";
+ public static final String s29001 = "29001";
+ public static final String s29002 = "29002";
+ public static final String s29003 = "29003";
+ public static final String s29004 = "29004";
+ public static final String s29005 = "29005";
+ public static final String s29006 = "29006";
+ public static final String s29007 = "29007";
+ public static final String s29008 = "29008";
+ public static final String s29009 = "29009";
+ public static final String s29010 = "29010";
+ public static final String s29011 = "29011";
+ public static final String s29012 = "29012";
+ public static final String s29013 = "29013";
+ public static final String s29014 = "29014";
+ public static final String s29015 = "29015";
+ public static final String s29016 = "29016";
+ public static final String s29017 = "29017";
+ public static final String s29018 = "29018";
+ public static final String s29019 = "29019";
+ public static final String s29020 = "29020";
+ public static final String s29021 = "29021";
+ public static final String s29022 = "29022";
+ public static final String s29023 = "29023";
+ public static final String s29024 = "29024";
+ public static final String s29025 = "29025";
+ public static final String s29026 = "29026";
+ public static final String s29027 = "29027";
+ public static final String s29028 = "29028";
+ public static final String s29029 = "29029";
+ public static final String s29030 = "29030";
+ public static final String s29031 = "29031";
+ public static final String s29032 = "29032";
+ public static final String s29033 = "29033";
+ public static final String s29034 = "29034";
+ public static final String s29035 = "29035";
+ public static final String s29036 = "29036";
+ public static final String s29037 = "29037";
+ public static final String s29038 = "29038";
+ public static final String s29039 = "29039";
+ public static final String s29040 = "29040";
+ public static final String s29041 = "29041";
+ public static final String s29042 = "29042";
+ public static final String s29043 = "29043";
+ public static final String s29044 = "29044";
+ public static final String s29045 = "29045";
+ public static final String s29046 = "29046";
+ public static final String s29047 = "29047";
+ public static final String s29048 = "29048";
+ public static final String s29049 = "29049";
+ public static final String s29050 = "29050";
+ public static final String s29051 = "29051";
+ public static final String s29052 = "29052";
+ public static final String s29053 = "29053";
+ public static final String s29054 = "29054";
+ public static final String s29055 = "29055";
+ public static final String s29056 = "29056";
+ public static final String s29057 = "29057";
+ public static final String s29058 = "29058";
+ public static final String s29059 = "29059";
+ public static final String s29060 = "29060";
+ public static final String s29061 = "29061";
+ public static final String s29062 = "29062";
+ public static final String s29063 = "29063";
+ public static final String s29064 = "29064";
+ public static final String s29065 = "29065";
+ public static final String s29066 = "29066";
+ public static final String s29067 = "29067";
+ public static final String s29068 = "29068";
+ public static final String s29069 = "29069";
+ public static final String s29070 = "29070";
+ public static final String s29071 = "29071";
+ public static final String s29072 = "29072";
+ public static final String s29073 = "29073";
+ public static final String s29074 = "29074";
+ public static final String s29075 = "29075";
+ public static final String s29076 = "29076";
+ public static final String s29077 = "29077";
+ public static final String s29078 = "29078";
+ public static final String s29079 = "29079";
+ public static final String s29080 = "29080";
+ public static final String s29081 = "29081";
+ public static final String s29082 = "29082";
+ public static final String s29083 = "29083";
+ public static final String s29084 = "29084";
+ public static final String s29085 = "29085";
+ public static final String s29086 = "29086";
+ public static final String s29087 = "29087";
+ public static final String s29088 = "29088";
+ public static final String s29089 = "29089";
+ public static final String s29090 = "29090";
+ public static final String s29091 = "29091";
+ public static final String s29092 = "29092";
+ public static final String s29093 = "29093";
+ public static final String s29094 = "29094";
+ public static final String s29095 = "29095";
+ public static final String s29096 = "29096";
+ public static final String s29097 = "29097";
+ public static final String s29098 = "29098";
+ public static final String s29099 = "29099";
+ public static final String s29100 = "29100";
+ public static final String s29101 = "29101";
+ public static final String s29102 = "29102";
+ public static final String s29103 = "29103";
+ public static final String s29104 = "29104";
+ public static final String s29105 = "29105";
+ public static final String s29106 = "29106";
+ public static final String s29107 = "29107";
+ public static final String s29108 = "29108";
+ public static final String s29109 = "29109";
+ public static final String s29110 = "29110";
+ public static final String s29111 = "29111";
+ public static final String s29112 = "29112";
+ public static final String s29113 = "29113";
+ public static final String s29114 = "29114";
+ public static final String s29115 = "29115";
+ public static final String s29116 = "29116";
+ public static final String s29117 = "29117";
+ public static final String s29118 = "29118";
+ public static final String s29119 = "29119";
+ public static final String s29120 = "29120";
+ public static final String s29121 = "29121";
+ public static final String s29122 = "29122";
+ public static final String s29123 = "29123";
+ public static final String s29124 = "29124";
+ public static final String s29125 = "29125";
+ public static final String s29126 = "29126";
+ public static final String s29127 = "29127";
+ public static final String s29128 = "29128";
+ public static final String s29129 = "29129";
+ public static final String s29130 = "29130";
+ public static final String s29131 = "29131";
+ public static final String s29132 = "29132";
+ public static final String s29133 = "29133";
+ public static final String s29134 = "29134";
+ public static final String s29135 = "29135";
+ public static final String s29136 = "29136";
+ public static final String s29137 = "29137";
+ public static final String s29138 = "29138";
+ public static final String s29139 = "29139";
+ public static final String s29140 = "29140";
+ public static final String s29141 = "29141";
+ public static final String s29142 = "29142";
+ public static final String s29143 = "29143";
+ public static final String s29144 = "29144";
+ public static final String s29145 = "29145";
+ public static final String s29146 = "29146";
+ public static final String s29147 = "29147";
+ public static final String s29148 = "29148";
+ public static final String s29149 = "29149";
+ public static final String s29150 = "29150";
+ public static final String s29151 = "29151";
+ public static final String s29152 = "29152";
+ public static final String s29153 = "29153";
+ public static final String s29154 = "29154";
+ public static final String s29155 = "29155";
+ public static final String s29156 = "29156";
+ public static final String s29157 = "29157";
+ public static final String s29158 = "29158";
+ public static final String s29159 = "29159";
+ public static final String s29160 = "29160";
+ public static final String s29161 = "29161";
+ public static final String s29162 = "29162";
+ public static final String s29163 = "29163";
+ public static final String s29164 = "29164";
+ public static final String s29165 = "29165";
+ public static final String s29166 = "29166";
+ public static final String s29167 = "29167";
+ public static final String s29168 = "29168";
+ public static final String s29169 = "29169";
+ public static final String s29170 = "29170";
+ public static final String s29171 = "29171";
+ public static final String s29172 = "29172";
+ public static final String s29173 = "29173";
+ public static final String s29174 = "29174";
+ public static final String s29175 = "29175";
+ public static final String s29176 = "29176";
+ public static final String s29177 = "29177";
+ public static final String s29178 = "29178";
+ public static final String s29179 = "29179";
+ public static final String s29180 = "29180";
+ public static final String s29181 = "29181";
+ public static final String s29182 = "29182";
+ public static final String s29183 = "29183";
+ public static final String s29184 = "29184";
+ public static final String s29185 = "29185";
+ public static final String s29186 = "29186";
+ public static final String s29187 = "29187";
+ public static final String s29188 = "29188";
+ public static final String s29189 = "29189";
+ public static final String s29190 = "29190";
+ public static final String s29191 = "29191";
+ public static final String s29192 = "29192";
+ public static final String s29193 = "29193";
+ public static final String s29194 = "29194";
+ public static final String s29195 = "29195";
+ public static final String s29196 = "29196";
+ public static final String s29197 = "29197";
+ public static final String s29198 = "29198";
+ public static final String s29199 = "29199";
+ public static final String s29200 = "29200";
+ public static final String s29201 = "29201";
+ public static final String s29202 = "29202";
+ public static final String s29203 = "29203";
+ public static final String s29204 = "29204";
+ public static final String s29205 = "29205";
+ public static final String s29206 = "29206";
+ public static final String s29207 = "29207";
+ public static final String s29208 = "29208";
+ public static final String s29209 = "29209";
+ public static final String s29210 = "29210";
+ public static final String s29211 = "29211";
+ public static final String s29212 = "29212";
+ public static final String s29213 = "29213";
+ public static final String s29214 = "29214";
+ public static final String s29215 = "29215";
+ public static final String s29216 = "29216";
+ public static final String s29217 = "29217";
+ public static final String s29218 = "29218";
+ public static final String s29219 = "29219";
+ public static final String s29220 = "29220";
+ public static final String s29221 = "29221";
+ public static final String s29222 = "29222";
+ public static final String s29223 = "29223";
+ public static final String s29224 = "29224";
+ public static final String s29225 = "29225";
+ public static final String s29226 = "29226";
+ public static final String s29227 = "29227";
+ public static final String s29228 = "29228";
+ public static final String s29229 = "29229";
+ public static final String s29230 = "29230";
+ public static final String s29231 = "29231";
+ public static final String s29232 = "29232";
+ public static final String s29233 = "29233";
+ public static final String s29234 = "29234";
+ public static final String s29235 = "29235";
+ public static final String s29236 = "29236";
+ public static final String s29237 = "29237";
+ public static final String s29238 = "29238";
+ public static final String s29239 = "29239";
+ public static final String s29240 = "29240";
+ public static final String s29241 = "29241";
+ public static final String s29242 = "29242";
+ public static final String s29243 = "29243";
+ public static final String s29244 = "29244";
+ public static final String s29245 = "29245";
+ public static final String s29246 = "29246";
+ public static final String s29247 = "29247";
+ public static final String s29248 = "29248";
+ public static final String s29249 = "29249";
+ public static final String s29250 = "29250";
+ public static final String s29251 = "29251";
+ public static final String s29252 = "29252";
+ public static final String s29253 = "29253";
+ public static final String s29254 = "29254";
+ public static final String s29255 = "29255";
+ public static final String s29256 = "29256";
+ public static final String s29257 = "29257";
+ public static final String s29258 = "29258";
+ public static final String s29259 = "29259";
+ public static final String s29260 = "29260";
+ public static final String s29261 = "29261";
+ public static final String s29262 = "29262";
+ public static final String s29263 = "29263";
+ public static final String s29264 = "29264";
+ public static final String s29265 = "29265";
+ public static final String s29266 = "29266";
+ public static final String s29267 = "29267";
+ public static final String s29268 = "29268";
+ public static final String s29269 = "29269";
+ public static final String s29270 = "29270";
+ public static final String s29271 = "29271";
+ public static final String s29272 = "29272";
+ public static final String s29273 = "29273";
+ public static final String s29274 = "29274";
+ public static final String s29275 = "29275";
+ public static final String s29276 = "29276";
+ public static final String s29277 = "29277";
+ public static final String s29278 = "29278";
+ public static final String s29279 = "29279";
+ public static final String s29280 = "29280";
+ public static final String s29281 = "29281";
+ public static final String s29282 = "29282";
+ public static final String s29283 = "29283";
+ public static final String s29284 = "29284";
+ public static final String s29285 = "29285";
+ public static final String s29286 = "29286";
+ public static final String s29287 = "29287";
+ public static final String s29288 = "29288";
+ public static final String s29289 = "29289";
+ public static final String s29290 = "29290";
+ public static final String s29291 = "29291";
+ public static final String s29292 = "29292";
+ public static final String s29293 = "29293";
+ public static final String s29294 = "29294";
+ public static final String s29295 = "29295";
+ public static final String s29296 = "29296";
+ public static final String s29297 = "29297";
+ public static final String s29298 = "29298";
+ public static final String s29299 = "29299";
+ public static final String s29300 = "29300";
+ public static final String s29301 = "29301";
+ public static final String s29302 = "29302";
+ public static final String s29303 = "29303";
+ public static final String s29304 = "29304";
+ public static final String s29305 = "29305";
+ public static final String s29306 = "29306";
+ public static final String s29307 = "29307";
+ public static final String s29308 = "29308";
+ public static final String s29309 = "29309";
+ public static final String s29310 = "29310";
+ public static final String s29311 = "29311";
+ public static final String s29312 = "29312";
+ public static final String s29313 = "29313";
+ public static final String s29314 = "29314";
+ public static final String s29315 = "29315";
+ public static final String s29316 = "29316";
+ public static final String s29317 = "29317";
+ public static final String s29318 = "29318";
+ public static final String s29319 = "29319";
+ public static final String s29320 = "29320";
+ public static final String s29321 = "29321";
+ public static final String s29322 = "29322";
+ public static final String s29323 = "29323";
+ public static final String s29324 = "29324";
+ public static final String s29325 = "29325";
+ public static final String s29326 = "29326";
+ public static final String s29327 = "29327";
+ public static final String s29328 = "29328";
+ public static final String s29329 = "29329";
+ public static final String s29330 = "29330";
+ public static final String s29331 = "29331";
+ public static final String s29332 = "29332";
+ public static final String s29333 = "29333";
+ public static final String s29334 = "29334";
+ public static final String s29335 = "29335";
+ public static final String s29336 = "29336";
+ public static final String s29337 = "29337";
+ public static final String s29338 = "29338";
+ public static final String s29339 = "29339";
+ public static final String s29340 = "29340";
+ public static final String s29341 = "29341";
+ public static final String s29342 = "29342";
+ public static final String s29343 = "29343";
+ public static final String s29344 = "29344";
+ public static final String s29345 = "29345";
+ public static final String s29346 = "29346";
+ public static final String s29347 = "29347";
+ public static final String s29348 = "29348";
+ public static final String s29349 = "29349";
+ public static final String s29350 = "29350";
+ public static final String s29351 = "29351";
+ public static final String s29352 = "29352";
+ public static final String s29353 = "29353";
+ public static final String s29354 = "29354";
+ public static final String s29355 = "29355";
+ public static final String s29356 = "29356";
+ public static final String s29357 = "29357";
+ public static final String s29358 = "29358";
+ public static final String s29359 = "29359";
+ public static final String s29360 = "29360";
+ public static final String s29361 = "29361";
+ public static final String s29362 = "29362";
+ public static final String s29363 = "29363";
+ public static final String s29364 = "29364";
+ public static final String s29365 = "29365";
+ public static final String s29366 = "29366";
+ public static final String s29367 = "29367";
+ public static final String s29368 = "29368";
+ public static final String s29369 = "29369";
+ public static final String s29370 = "29370";
+ public static final String s29371 = "29371";
+ public static final String s29372 = "29372";
+ public static final String s29373 = "29373";
+ public static final String s29374 = "29374";
+ public static final String s29375 = "29375";
+ public static final String s29376 = "29376";
+ public static final String s29377 = "29377";
+ public static final String s29378 = "29378";
+ public static final String s29379 = "29379";
+ public static final String s29380 = "29380";
+ public static final String s29381 = "29381";
+ public static final String s29382 = "29382";
+ public static final String s29383 = "29383";
+ public static final String s29384 = "29384";
+ public static final String s29385 = "29385";
+ public static final String s29386 = "29386";
+ public static final String s29387 = "29387";
+ public static final String s29388 = "29388";
+ public static final String s29389 = "29389";
+ public static final String s29390 = "29390";
+ public static final String s29391 = "29391";
+ public static final String s29392 = "29392";
+ public static final String s29393 = "29393";
+ public static final String s29394 = "29394";
+ public static final String s29395 = "29395";
+ public static final String s29396 = "29396";
+ public static final String s29397 = "29397";
+ public static final String s29398 = "29398";
+ public static final String s29399 = "29399";
+ public static final String s29400 = "29400";
+ public static final String s29401 = "29401";
+ public static final String s29402 = "29402";
+ public static final String s29403 = "29403";
+ public static final String s29404 = "29404";
+ public static final String s29405 = "29405";
+ public static final String s29406 = "29406";
+ public static final String s29407 = "29407";
+ public static final String s29408 = "29408";
+ public static final String s29409 = "29409";
+ public static final String s29410 = "29410";
+ public static final String s29411 = "29411";
+ public static final String s29412 = "29412";
+ public static final String s29413 = "29413";
+ public static final String s29414 = "29414";
+ public static final String s29415 = "29415";
+ public static final String s29416 = "29416";
+ public static final String s29417 = "29417";
+ public static final String s29418 = "29418";
+ public static final String s29419 = "29419";
+ public static final String s29420 = "29420";
+ public static final String s29421 = "29421";
+ public static final String s29422 = "29422";
+ public static final String s29423 = "29423";
+ public static final String s29424 = "29424";
+ public static final String s29425 = "29425";
+ public static final String s29426 = "29426";
+ public static final String s29427 = "29427";
+ public static final String s29428 = "29428";
+ public static final String s29429 = "29429";
+ public static final String s29430 = "29430";
+ public static final String s29431 = "29431";
+ public static final String s29432 = "29432";
+ public static final String s29433 = "29433";
+ public static final String s29434 = "29434";
+ public static final String s29435 = "29435";
+ public static final String s29436 = "29436";
+ public static final String s29437 = "29437";
+ public static final String s29438 = "29438";
+ public static final String s29439 = "29439";
+ public static final String s29440 = "29440";
+ public static final String s29441 = "29441";
+ public static final String s29442 = "29442";
+ public static final String s29443 = "29443";
+ public static final String s29444 = "29444";
+ public static final String s29445 = "29445";
+ public static final String s29446 = "29446";
+ public static final String s29447 = "29447";
+ public static final String s29448 = "29448";
+ public static final String s29449 = "29449";
+ public static final String s29450 = "29450";
+ public static final String s29451 = "29451";
+ public static final String s29452 = "29452";
+ public static final String s29453 = "29453";
+ public static final String s29454 = "29454";
+ public static final String s29455 = "29455";
+ public static final String s29456 = "29456";
+ public static final String s29457 = "29457";
+ public static final String s29458 = "29458";
+ public static final String s29459 = "29459";
+ public static final String s29460 = "29460";
+ public static final String s29461 = "29461";
+ public static final String s29462 = "29462";
+ public static final String s29463 = "29463";
+ public static final String s29464 = "29464";
+ public static final String s29465 = "29465";
+ public static final String s29466 = "29466";
+ public static final String s29467 = "29467";
+ public static final String s29468 = "29468";
+ public static final String s29469 = "29469";
+ public static final String s29470 = "29470";
+ public static final String s29471 = "29471";
+ public static final String s29472 = "29472";
+ public static final String s29473 = "29473";
+ public static final String s29474 = "29474";
+ public static final String s29475 = "29475";
+ public static final String s29476 = "29476";
+ public static final String s29477 = "29477";
+ public static final String s29478 = "29478";
+ public static final String s29479 = "29479";
+ public static final String s29480 = "29480";
+ public static final String s29481 = "29481";
+ public static final String s29482 = "29482";
+ public static final String s29483 = "29483";
+ public static final String s29484 = "29484";
+ public static final String s29485 = "29485";
+ public static final String s29486 = "29486";
+ public static final String s29487 = "29487";
+ public static final String s29488 = "29488";
+ public static final String s29489 = "29489";
+ public static final String s29490 = "29490";
+ public static final String s29491 = "29491";
+ public static final String s29492 = "29492";
+ public static final String s29493 = "29493";
+ public static final String s29494 = "29494";
+ public static final String s29495 = "29495";
+ public static final String s29496 = "29496";
+ public static final String s29497 = "29497";
+ public static final String s29498 = "29498";
+ public static final String s29499 = "29499";
+ public static final String s29500 = "29500";
+ public static final String s29501 = "29501";
+ public static final String s29502 = "29502";
+ public static final String s29503 = "29503";
+ public static final String s29504 = "29504";
+ public static final String s29505 = "29505";
+ public static final String s29506 = "29506";
+ public static final String s29507 = "29507";
+ public static final String s29508 = "29508";
+ public static final String s29509 = "29509";
+ public static final String s29510 = "29510";
+ public static final String s29511 = "29511";
+ public static final String s29512 = "29512";
+ public static final String s29513 = "29513";
+ public static final String s29514 = "29514";
+ public static final String s29515 = "29515";
+ public static final String s29516 = "29516";
+ public static final String s29517 = "29517";
+ public static final String s29518 = "29518";
+ public static final String s29519 = "29519";
+ public static final String s29520 = "29520";
+ public static final String s29521 = "29521";
+ public static final String s29522 = "29522";
+ public static final String s29523 = "29523";
+ public static final String s29524 = "29524";
+ public static final String s29525 = "29525";
+ public static final String s29526 = "29526";
+ public static final String s29527 = "29527";
+ public static final String s29528 = "29528";
+ public static final String s29529 = "29529";
+ public static final String s29530 = "29530";
+ public static final String s29531 = "29531";
+ public static final String s29532 = "29532";
+ public static final String s29533 = "29533";
+ public static final String s29534 = "29534";
+ public static final String s29535 = "29535";
+ public static final String s29536 = "29536";
+ public static final String s29537 = "29537";
+ public static final String s29538 = "29538";
+ public static final String s29539 = "29539";
+ public static final String s29540 = "29540";
+ public static final String s29541 = "29541";
+ public static final String s29542 = "29542";
+ public static final String s29543 = "29543";
+ public static final String s29544 = "29544";
+ public static final String s29545 = "29545";
+ public static final String s29546 = "29546";
+ public static final String s29547 = "29547";
+ public static final String s29548 = "29548";
+ public static final String s29549 = "29549";
+ public static final String s29550 = "29550";
+ public static final String s29551 = "29551";
+ public static final String s29552 = "29552";
+ public static final String s29553 = "29553";
+ public static final String s29554 = "29554";
+ public static final String s29555 = "29555";
+ public static final String s29556 = "29556";
+ public static final String s29557 = "29557";
+ public static final String s29558 = "29558";
+ public static final String s29559 = "29559";
+ public static final String s29560 = "29560";
+ public static final String s29561 = "29561";
+ public static final String s29562 = "29562";
+ public static final String s29563 = "29563";
+ public static final String s29564 = "29564";
+ public static final String s29565 = "29565";
+ public static final String s29566 = "29566";
+ public static final String s29567 = "29567";
+ public static final String s29568 = "29568";
+ public static final String s29569 = "29569";
+ public static final String s29570 = "29570";
+ public static final String s29571 = "29571";
+ public static final String s29572 = "29572";
+ public static final String s29573 = "29573";
+ public static final String s29574 = "29574";
+ public static final String s29575 = "29575";
+ public static final String s29576 = "29576";
+ public static final String s29577 = "29577";
+ public static final String s29578 = "29578";
+ public static final String s29579 = "29579";
+ public static final String s29580 = "29580";
+ public static final String s29581 = "29581";
+ public static final String s29582 = "29582";
+ public static final String s29583 = "29583";
+ public static final String s29584 = "29584";
+ public static final String s29585 = "29585";
+ public static final String s29586 = "29586";
+ public static final String s29587 = "29587";
+ public static final String s29588 = "29588";
+ public static final String s29589 = "29589";
+ public static final String s29590 = "29590";
+ public static final String s29591 = "29591";
+ public static final String s29592 = "29592";
+ public static final String s29593 = "29593";
+ public static final String s29594 = "29594";
+ public static final String s29595 = "29595";
+ public static final String s29596 = "29596";
+ public static final String s29597 = "29597";
+ public static final String s29598 = "29598";
+ public static final String s29599 = "29599";
+ public static final String s29600 = "29600";
+ public static final String s29601 = "29601";
+ public static final String s29602 = "29602";
+ public static final String s29603 = "29603";
+ public static final String s29604 = "29604";
+ public static final String s29605 = "29605";
+ public static final String s29606 = "29606";
+ public static final String s29607 = "29607";
+ public static final String s29608 = "29608";
+ public static final String s29609 = "29609";
+ public static final String s29610 = "29610";
+ public static final String s29611 = "29611";
+ public static final String s29612 = "29612";
+ public static final String s29613 = "29613";
+ public static final String s29614 = "29614";
+ public static final String s29615 = "29615";
+ public static final String s29616 = "29616";
+ public static final String s29617 = "29617";
+ public static final String s29618 = "29618";
+ public static final String s29619 = "29619";
+ public static final String s29620 = "29620";
+ public static final String s29621 = "29621";
+ public static final String s29622 = "29622";
+ public static final String s29623 = "29623";
+ public static final String s29624 = "29624";
+ public static final String s29625 = "29625";
+ public static final String s29626 = "29626";
+ public static final String s29627 = "29627";
+ public static final String s29628 = "29628";
+ public static final String s29629 = "29629";
+ public static final String s29630 = "29630";
+ public static final String s29631 = "29631";
+ public static final String s29632 = "29632";
+ public static final String s29633 = "29633";
+ public static final String s29634 = "29634";
+ public static final String s29635 = "29635";
+ public static final String s29636 = "29636";
+ public static final String s29637 = "29637";
+ public static final String s29638 = "29638";
+ public static final String s29639 = "29639";
+ public static final String s29640 = "29640";
+ public static final String s29641 = "29641";
+ public static final String s29642 = "29642";
+ public static final String s29643 = "29643";
+ public static final String s29644 = "29644";
+ public static final String s29645 = "29645";
+ public static final String s29646 = "29646";
+ public static final String s29647 = "29647";
+ public static final String s29648 = "29648";
+ public static final String s29649 = "29649";
+ public static final String s29650 = "29650";
+ public static final String s29651 = "29651";
+ public static final String s29652 = "29652";
+ public static final String s29653 = "29653";
+ public static final String s29654 = "29654";
+ public static final String s29655 = "29655";
+ public static final String s29656 = "29656";
+ public static final String s29657 = "29657";
+ public static final String s29658 = "29658";
+ public static final String s29659 = "29659";
+ public static final String s29660 = "29660";
+ public static final String s29661 = "29661";
+ public static final String s29662 = "29662";
+ public static final String s29663 = "29663";
+ public static final String s29664 = "29664";
+ public static final String s29665 = "29665";
+ public static final String s29666 = "29666";
+ public static final String s29667 = "29667";
+ public static final String s29668 = "29668";
+ public static final String s29669 = "29669";
+ public static final String s29670 = "29670";
+ public static final String s29671 = "29671";
+ public static final String s29672 = "29672";
+ public static final String s29673 = "29673";
+ public static final String s29674 = "29674";
+ public static final String s29675 = "29675";
+ public static final String s29676 = "29676";
+ public static final String s29677 = "29677";
+ public static final String s29678 = "29678";
+ public static final String s29679 = "29679";
+ public static final String s29680 = "29680";
+ public static final String s29681 = "29681";
+ public static final String s29682 = "29682";
+ public static final String s29683 = "29683";
+ public static final String s29684 = "29684";
+ public static final String s29685 = "29685";
+ public static final String s29686 = "29686";
+ public static final String s29687 = "29687";
+ public static final String s29688 = "29688";
+ public static final String s29689 = "29689";
+ public static final String s29690 = "29690";
+ public static final String s29691 = "29691";
+ public static final String s29692 = "29692";
+ public static final String s29693 = "29693";
+ public static final String s29694 = "29694";
+ public static final String s29695 = "29695";
+ public static final String s29696 = "29696";
+ public static final String s29697 = "29697";
+ public static final String s29698 = "29698";
+ public static final String s29699 = "29699";
+ public static final String s29700 = "29700";
+ public static final String s29701 = "29701";
+ public static final String s29702 = "29702";
+ public static final String s29703 = "29703";
+ public static final String s29704 = "29704";
+ public static final String s29705 = "29705";
+ public static final String s29706 = "29706";
+ public static final String s29707 = "29707";
+ public static final String s29708 = "29708";
+ public static final String s29709 = "29709";
+ public static final String s29710 = "29710";
+ public static final String s29711 = "29711";
+ public static final String s29712 = "29712";
+ public static final String s29713 = "29713";
+ public static final String s29714 = "29714";
+ public static final String s29715 = "29715";
+ public static final String s29716 = "29716";
+ public static final String s29717 = "29717";
+ public static final String s29718 = "29718";
+ public static final String s29719 = "29719";
+ public static final String s29720 = "29720";
+ public static final String s29721 = "29721";
+ public static final String s29722 = "29722";
+ public static final String s29723 = "29723";
+ public static final String s29724 = "29724";
+ public static final String s29725 = "29725";
+ public static final String s29726 = "29726";
+ public static final String s29727 = "29727";
+ public static final String s29728 = "29728";
+ public static final String s29729 = "29729";
+ public static final String s29730 = "29730";
+ public static final String s29731 = "29731";
+ public static final String s29732 = "29732";
+ public static final String s29733 = "29733";
+ public static final String s29734 = "29734";
+ public static final String s29735 = "29735";
+ public static final String s29736 = "29736";
+ public static final String s29737 = "29737";
+ public static final String s29738 = "29738";
+ public static final String s29739 = "29739";
+ public static final String s29740 = "29740";
+ public static final String s29741 = "29741";
+ public static final String s29742 = "29742";
+ public static final String s29743 = "29743";
+ public static final String s29744 = "29744";
+ public static final String s29745 = "29745";
+ public static final String s29746 = "29746";
+ public static final String s29747 = "29747";
+ public static final String s29748 = "29748";
+ public static final String s29749 = "29749";
+ public static final String s29750 = "29750";
+ public static final String s29751 = "29751";
+ public static final String s29752 = "29752";
+ public static final String s29753 = "29753";
+ public static final String s29754 = "29754";
+ public static final String s29755 = "29755";
+ public static final String s29756 = "29756";
+ public static final String s29757 = "29757";
+ public static final String s29758 = "29758";
+ public static final String s29759 = "29759";
+ public static final String s29760 = "29760";
+ public static final String s29761 = "29761";
+ public static final String s29762 = "29762";
+ public static final String s29763 = "29763";
+ public static final String s29764 = "29764";
+ public static final String s29765 = "29765";
+ public static final String s29766 = "29766";
+ public static final String s29767 = "29767";
+ public static final String s29768 = "29768";
+ public static final String s29769 = "29769";
+ public static final String s29770 = "29770";
+ public static final String s29771 = "29771";
+ public static final String s29772 = "29772";
+ public static final String s29773 = "29773";
+ public static final String s29774 = "29774";
+ public static final String s29775 = "29775";
+ public static final String s29776 = "29776";
+ public static final String s29777 = "29777";
+ public static final String s29778 = "29778";
+ public static final String s29779 = "29779";
+ public static final String s29780 = "29780";
+ public static final String s29781 = "29781";
+ public static final String s29782 = "29782";
+ public static final String s29783 = "29783";
+ public static final String s29784 = "29784";
+ public static final String s29785 = "29785";
+ public static final String s29786 = "29786";
+ public static final String s29787 = "29787";
+ public static final String s29788 = "29788";
+ public static final String s29789 = "29789";
+ public static final String s29790 = "29790";
+ public static final String s29791 = "29791";
+ public static final String s29792 = "29792";
+ public static final String s29793 = "29793";
+ public static final String s29794 = "29794";
+ public static final String s29795 = "29795";
+ public static final String s29796 = "29796";
+ public static final String s29797 = "29797";
+ public static final String s29798 = "29798";
+ public static final String s29799 = "29799";
+ public static final String s29800 = "29800";
+ public static final String s29801 = "29801";
+ public static final String s29802 = "29802";
+ public static final String s29803 = "29803";
+ public static final String s29804 = "29804";
+ public static final String s29805 = "29805";
+ public static final String s29806 = "29806";
+ public static final String s29807 = "29807";
+ public static final String s29808 = "29808";
+ public static final String s29809 = "29809";
+ public static final String s29810 = "29810";
+ public static final String s29811 = "29811";
+ public static final String s29812 = "29812";
+ public static final String s29813 = "29813";
+ public static final String s29814 = "29814";
+ public static final String s29815 = "29815";
+ public static final String s29816 = "29816";
+ public static final String s29817 = "29817";
+ public static final String s29818 = "29818";
+ public static final String s29819 = "29819";
+ public static final String s29820 = "29820";
+ public static final String s29821 = "29821";
+ public static final String s29822 = "29822";
+ public static final String s29823 = "29823";
+ public static final String s29824 = "29824";
+ public static final String s29825 = "29825";
+ public static final String s29826 = "29826";
+ public static final String s29827 = "29827";
+ public static final String s29828 = "29828";
+ public static final String s29829 = "29829";
+ public static final String s29830 = "29830";
+ public static final String s29831 = "29831";
+ public static final String s29832 = "29832";
+ public static final String s29833 = "29833";
+ public static final String s29834 = "29834";
+ public static final String s29835 = "29835";
+ public static final String s29836 = "29836";
+ public static final String s29837 = "29837";
+ public static final String s29838 = "29838";
+ public static final String s29839 = "29839";
+ public static final String s29840 = "29840";
+ public static final String s29841 = "29841";
+ public static final String s29842 = "29842";
+ public static final String s29843 = "29843";
+ public static final String s29844 = "29844";
+ public static final String s29845 = "29845";
+ public static final String s29846 = "29846";
+ public static final String s29847 = "29847";
+ public static final String s29848 = "29848";
+ public static final String s29849 = "29849";
+ public static final String s29850 = "29850";
+ public static final String s29851 = "29851";
+ public static final String s29852 = "29852";
+ public static final String s29853 = "29853";
+ public static final String s29854 = "29854";
+ public static final String s29855 = "29855";
+ public static final String s29856 = "29856";
+ public static final String s29857 = "29857";
+ public static final String s29858 = "29858";
+ public static final String s29859 = "29859";
+ public static final String s29860 = "29860";
+ public static final String s29861 = "29861";
+ public static final String s29862 = "29862";
+ public static final String s29863 = "29863";
+ public static final String s29864 = "29864";
+ public static final String s29865 = "29865";
+ public static final String s29866 = "29866";
+ public static final String s29867 = "29867";
+ public static final String s29868 = "29868";
+ public static final String s29869 = "29869";
+ public static final String s29870 = "29870";
+ public static final String s29871 = "29871";
+ public static final String s29872 = "29872";
+ public static final String s29873 = "29873";
+ public static final String s29874 = "29874";
+ public static final String s29875 = "29875";
+ public static final String s29876 = "29876";
+ public static final String s29877 = "29877";
+ public static final String s29878 = "29878";
+ public static final String s29879 = "29879";
+ public static final String s29880 = "29880";
+ public static final String s29881 = "29881";
+ public static final String s29882 = "29882";
+ public static final String s29883 = "29883";
+ public static final String s29884 = "29884";
+ public static final String s29885 = "29885";
+ public static final String s29886 = "29886";
+ public static final String s29887 = "29887";
+ public static final String s29888 = "29888";
+ public static final String s29889 = "29889";
+ public static final String s29890 = "29890";
+ public static final String s29891 = "29891";
+ public static final String s29892 = "29892";
+ public static final String s29893 = "29893";
+ public static final String s29894 = "29894";
+ public static final String s29895 = "29895";
+ public static final String s29896 = "29896";
+ public static final String s29897 = "29897";
+ public static final String s29898 = "29898";
+ public static final String s29899 = "29899";
+ public static final String s29900 = "29900";
+ public static final String s29901 = "29901";
+ public static final String s29902 = "29902";
+ public static final String s29903 = "29903";
+ public static final String s29904 = "29904";
+ public static final String s29905 = "29905";
+ public static final String s29906 = "29906";
+ public static final String s29907 = "29907";
+ public static final String s29908 = "29908";
+ public static final String s29909 = "29909";
+ public static final String s29910 = "29910";
+ public static final String s29911 = "29911";
+ public static final String s29912 = "29912";
+ public static final String s29913 = "29913";
+ public static final String s29914 = "29914";
+ public static final String s29915 = "29915";
+ public static final String s29916 = "29916";
+ public static final String s29917 = "29917";
+ public static final String s29918 = "29918";
+ public static final String s29919 = "29919";
+ public static final String s29920 = "29920";
+ public static final String s29921 = "29921";
+ public static final String s29922 = "29922";
+ public static final String s29923 = "29923";
+ public static final String s29924 = "29924";
+ public static final String s29925 = "29925";
+ public static final String s29926 = "29926";
+ public static final String s29927 = "29927";
+ public static final String s29928 = "29928";
+ public static final String s29929 = "29929";
+ public static final String s29930 = "29930";
+ public static final String s29931 = "29931";
+ public static final String s29932 = "29932";
+ public static final String s29933 = "29933";
+ public static final String s29934 = "29934";
+ public static final String s29935 = "29935";
+ public static final String s29936 = "29936";
+ public static final String s29937 = "29937";
+ public static final String s29938 = "29938";
+ public static final String s29939 = "29939";
+ public static final String s29940 = "29940";
+ public static final String s29941 = "29941";
+ public static final String s29942 = "29942";
+ public static final String s29943 = "29943";
+ public static final String s29944 = "29944";
+ public static final String s29945 = "29945";
+ public static final String s29946 = "29946";
+ public static final String s29947 = "29947";
+ public static final String s29948 = "29948";
+ public static final String s29949 = "29949";
+ public static final String s29950 = "29950";
+ public static final String s29951 = "29951";
+ public static final String s29952 = "29952";
+ public static final String s29953 = "29953";
+ public static final String s29954 = "29954";
+ public static final String s29955 = "29955";
+ public static final String s29956 = "29956";
+ public static final String s29957 = "29957";
+ public static final String s29958 = "29958";
+ public static final String s29959 = "29959";
+ public static final String s29960 = "29960";
+ public static final String s29961 = "29961";
+ public static final String s29962 = "29962";
+ public static final String s29963 = "29963";
+ public static final String s29964 = "29964";
+ public static final String s29965 = "29965";
+ public static final String s29966 = "29966";
+ public static final String s29967 = "29967";
+ public static final String s29968 = "29968";
+ public static final String s29969 = "29969";
+ public static final String s29970 = "29970";
+ public static final String s29971 = "29971";
+ public static final String s29972 = "29972";
+ public static final String s29973 = "29973";
+ public static final String s29974 = "29974";
+ public static final String s29975 = "29975";
+ public static final String s29976 = "29976";
+ public static final String s29977 = "29977";
+ public static final String s29978 = "29978";
+ public static final String s29979 = "29979";
+ public static final String s29980 = "29980";
+ public static final String s29981 = "29981";
+ public static final String s29982 = "29982";
+ public static final String s29983 = "29983";
+ public static final String s29984 = "29984";
+ public static final String s29985 = "29985";
+ public static final String s29986 = "29986";
+ public static final String s29987 = "29987";
+ public static final String s29988 = "29988";
+ public static final String s29989 = "29989";
+ public static final String s29990 = "29990";
+ public static final String s29991 = "29991";
+ public static final String s29992 = "29992";
+ public static final String s29993 = "29993";
+ public static final String s29994 = "29994";
+ public static final String s29995 = "29995";
+ public static final String s29996 = "29996";
+ public static final String s29997 = "29997";
+ public static final String s29998 = "29998";
+ public static final String s29999 = "29999";
+ public static final String s30000 = "30000";
+ public static final String s30001 = "30001";
+ public static final String s30002 = "30002";
+ public static final String s30003 = "30003";
+ public static final String s30004 = "30004";
+ public static final String s30005 = "30005";
+ public static final String s30006 = "30006";
+ public static final String s30007 = "30007";
+ public static final String s30008 = "30008";
+ public static final String s30009 = "30009";
+ public static final String s30010 = "30010";
+ public static final String s30011 = "30011";
+ public static final String s30012 = "30012";
+ public static final String s30013 = "30013";
+ public static final String s30014 = "30014";
+ public static final String s30015 = "30015";
+ public static final String s30016 = "30016";
+ public static final String s30017 = "30017";
+ public static final String s30018 = "30018";
+ public static final String s30019 = "30019";
+ public static final String s30020 = "30020";
+ public static final String s30021 = "30021";
+ public static final String s30022 = "30022";
+ public static final String s30023 = "30023";
+ public static final String s30024 = "30024";
+ public static final String s30025 = "30025";
+ public static final String s30026 = "30026";
+ public static final String s30027 = "30027";
+ public static final String s30028 = "30028";
+ public static final String s30029 = "30029";
+ public static final String s30030 = "30030";
+ public static final String s30031 = "30031";
+ public static final String s30032 = "30032";
+ public static final String s30033 = "30033";
+ public static final String s30034 = "30034";
+ public static final String s30035 = "30035";
+ public static final String s30036 = "30036";
+ public static final String s30037 = "30037";
+ public static final String s30038 = "30038";
+ public static final String s30039 = "30039";
+ public static final String s30040 = "30040";
+ public static final String s30041 = "30041";
+ public static final String s30042 = "30042";
+ public static final String s30043 = "30043";
+ public static final String s30044 = "30044";
+ public static final String s30045 = "30045";
+ public static final String s30046 = "30046";
+ public static final String s30047 = "30047";
+ public static final String s30048 = "30048";
+ public static final String s30049 = "30049";
+ public static final String s30050 = "30050";
+ public static final String s30051 = "30051";
+ public static final String s30052 = "30052";
+ public static final String s30053 = "30053";
+ public static final String s30054 = "30054";
+ public static final String s30055 = "30055";
+ public static final String s30056 = "30056";
+ public static final String s30057 = "30057";
+ public static final String s30058 = "30058";
+ public static final String s30059 = "30059";
+ public static final String s30060 = "30060";
+ public static final String s30061 = "30061";
+ public static final String s30062 = "30062";
+ public static final String s30063 = "30063";
+ public static final String s30064 = "30064";
+ public static final String s30065 = "30065";
+ public static final String s30066 = "30066";
+ public static final String s30067 = "30067";
+ public static final String s30068 = "30068";
+ public static final String s30069 = "30069";
+ public static final String s30070 = "30070";
+ public static final String s30071 = "30071";
+ public static final String s30072 = "30072";
+ public static final String s30073 = "30073";
+ public static final String s30074 = "30074";
+ public static final String s30075 = "30075";
+ public static final String s30076 = "30076";
+ public static final String s30077 = "30077";
+ public static final String s30078 = "30078";
+ public static final String s30079 = "30079";
+ public static final String s30080 = "30080";
+ public static final String s30081 = "30081";
+ public static final String s30082 = "30082";
+ public static final String s30083 = "30083";
+ public static final String s30084 = "30084";
+ public static final String s30085 = "30085";
+ public static final String s30086 = "30086";
+ public static final String s30087 = "30087";
+ public static final String s30088 = "30088";
+ public static final String s30089 = "30089";
+ public static final String s30090 = "30090";
+ public static final String s30091 = "30091";
+ public static final String s30092 = "30092";
+ public static final String s30093 = "30093";
+ public static final String s30094 = "30094";
+ public static final String s30095 = "30095";
+ public static final String s30096 = "30096";
+ public static final String s30097 = "30097";
+ public static final String s30098 = "30098";
+ public static final String s30099 = "30099";
+ public static final String s30100 = "30100";
+ public static final String s30101 = "30101";
+ public static final String s30102 = "30102";
+ public static final String s30103 = "30103";
+ public static final String s30104 = "30104";
+ public static final String s30105 = "30105";
+ public static final String s30106 = "30106";
+ public static final String s30107 = "30107";
+ public static final String s30108 = "30108";
+ public static final String s30109 = "30109";
+ public static final String s30110 = "30110";
+ public static final String s30111 = "30111";
+ public static final String s30112 = "30112";
+ public static final String s30113 = "30113";
+ public static final String s30114 = "30114";
+ public static final String s30115 = "30115";
+ public static final String s30116 = "30116";
+ public static final String s30117 = "30117";
+ public static final String s30118 = "30118";
+ public static final String s30119 = "30119";
+ public static final String s30120 = "30120";
+ public static final String s30121 = "30121";
+ public static final String s30122 = "30122";
+ public static final String s30123 = "30123";
+ public static final String s30124 = "30124";
+ public static final String s30125 = "30125";
+ public static final String s30126 = "30126";
+ public static final String s30127 = "30127";
+ public static final String s30128 = "30128";
+ public static final String s30129 = "30129";
+ public static final String s30130 = "30130";
+ public static final String s30131 = "30131";
+ public static final String s30132 = "30132";
+ public static final String s30133 = "30133";
+ public static final String s30134 = "30134";
+ public static final String s30135 = "30135";
+ public static final String s30136 = "30136";
+ public static final String s30137 = "30137";
+ public static final String s30138 = "30138";
+ public static final String s30139 = "30139";
+ public static final String s30140 = "30140";
+ public static final String s30141 = "30141";
+ public static final String s30142 = "30142";
+ public static final String s30143 = "30143";
+ public static final String s30144 = "30144";
+ public static final String s30145 = "30145";
+ public static final String s30146 = "30146";
+ public static final String s30147 = "30147";
+ public static final String s30148 = "30148";
+ public static final String s30149 = "30149";
+ public static final String s30150 = "30150";
+ public static final String s30151 = "30151";
+ public static final String s30152 = "30152";
+ public static final String s30153 = "30153";
+ public static final String s30154 = "30154";
+ public static final String s30155 = "30155";
+ public static final String s30156 = "30156";
+ public static final String s30157 = "30157";
+ public static final String s30158 = "30158";
+ public static final String s30159 = "30159";
+ public static final String s30160 = "30160";
+ public static final String s30161 = "30161";
+ public static final String s30162 = "30162";
+ public static final String s30163 = "30163";
+ public static final String s30164 = "30164";
+ public static final String s30165 = "30165";
+ public static final String s30166 = "30166";
+ public static final String s30167 = "30167";
+ public static final String s30168 = "30168";
+ public static final String s30169 = "30169";
+ public static final String s30170 = "30170";
+ public static final String s30171 = "30171";
+ public static final String s30172 = "30172";
+ public static final String s30173 = "30173";
+ public static final String s30174 = "30174";
+ public static final String s30175 = "30175";
+ public static final String s30176 = "30176";
+ public static final String s30177 = "30177";
+ public static final String s30178 = "30178";
+ public static final String s30179 = "30179";
+ public static final String s30180 = "30180";
+ public static final String s30181 = "30181";
+ public static final String s30182 = "30182";
+ public static final String s30183 = "30183";
+ public static final String s30184 = "30184";
+ public static final String s30185 = "30185";
+ public static final String s30186 = "30186";
+ public static final String s30187 = "30187";
+ public static final String s30188 = "30188";
+ public static final String s30189 = "30189";
+ public static final String s30190 = "30190";
+ public static final String s30191 = "30191";
+ public static final String s30192 = "30192";
+ public static final String s30193 = "30193";
+ public static final String s30194 = "30194";
+ public static final String s30195 = "30195";
+ public static final String s30196 = "30196";
+ public static final String s30197 = "30197";
+ public static final String s30198 = "30198";
+ public static final String s30199 = "30199";
+ public static final String s30200 = "30200";
+ public static final String s30201 = "30201";
+ public static final String s30202 = "30202";
+ public static final String s30203 = "30203";
+ public static final String s30204 = "30204";
+ public static final String s30205 = "30205";
+ public static final String s30206 = "30206";
+ public static final String s30207 = "30207";
+ public static final String s30208 = "30208";
+ public static final String s30209 = "30209";
+ public static final String s30210 = "30210";
+ public static final String s30211 = "30211";
+ public static final String s30212 = "30212";
+ public static final String s30213 = "30213";
+ public static final String s30214 = "30214";
+ public static final String s30215 = "30215";
+ public static final String s30216 = "30216";
+ public static final String s30217 = "30217";
+ public static final String s30218 = "30218";
+ public static final String s30219 = "30219";
+ public static final String s30220 = "30220";
+ public static final String s30221 = "30221";
+ public static final String s30222 = "30222";
+ public static final String s30223 = "30223";
+ public static final String s30224 = "30224";
+ public static final String s30225 = "30225";
+ public static final String s30226 = "30226";
+ public static final String s30227 = "30227";
+ public static final String s30228 = "30228";
+ public static final String s30229 = "30229";
+ public static final String s30230 = "30230";
+ public static final String s30231 = "30231";
+ public static final String s30232 = "30232";
+ public static final String s30233 = "30233";
+ public static final String s30234 = "30234";
+ public static final String s30235 = "30235";
+ public static final String s30236 = "30236";
+ public static final String s30237 = "30237";
+ public static final String s30238 = "30238";
+ public static final String s30239 = "30239";
+ public static final String s30240 = "30240";
+ public static final String s30241 = "30241";
+ public static final String s30242 = "30242";
+ public static final String s30243 = "30243";
+ public static final String s30244 = "30244";
+ public static final String s30245 = "30245";
+ public static final String s30246 = "30246";
+ public static final String s30247 = "30247";
+ public static final String s30248 = "30248";
+ public static final String s30249 = "30249";
+ public static final String s30250 = "30250";
+ public static final String s30251 = "30251";
+ public static final String s30252 = "30252";
+ public static final String s30253 = "30253";
+ public static final String s30254 = "30254";
+ public static final String s30255 = "30255";
+ public static final String s30256 = "30256";
+ public static final String s30257 = "30257";
+ public static final String s30258 = "30258";
+ public static final String s30259 = "30259";
+ public static final String s30260 = "30260";
+ public static final String s30261 = "30261";
+ public static final String s30262 = "30262";
+ public static final String s30263 = "30263";
+ public static final String s30264 = "30264";
+ public static final String s30265 = "30265";
+ public static final String s30266 = "30266";
+ public static final String s30267 = "30267";
+ public static final String s30268 = "30268";
+ public static final String s30269 = "30269";
+ public static final String s30270 = "30270";
+ public static final String s30271 = "30271";
+ public static final String s30272 = "30272";
+ public static final String s30273 = "30273";
+ public static final String s30274 = "30274";
+ public static final String s30275 = "30275";
+ public static final String s30276 = "30276";
+ public static final String s30277 = "30277";
+ public static final String s30278 = "30278";
+ public static final String s30279 = "30279";
+ public static final String s30280 = "30280";
+ public static final String s30281 = "30281";
+ public static final String s30282 = "30282";
+ public static final String s30283 = "30283";
+ public static final String s30284 = "30284";
+ public static final String s30285 = "30285";
+ public static final String s30286 = "30286";
+ public static final String s30287 = "30287";
+ public static final String s30288 = "30288";
+ public static final String s30289 = "30289";
+ public static final String s30290 = "30290";
+ public static final String s30291 = "30291";
+ public static final String s30292 = "30292";
+ public static final String s30293 = "30293";
+ public static final String s30294 = "30294";
+ public static final String s30295 = "30295";
+ public static final String s30296 = "30296";
+ public static final String s30297 = "30297";
+ public static final String s30298 = "30298";
+ public static final String s30299 = "30299";
+ public static final String s30300 = "30300";
+ public static final String s30301 = "30301";
+ public static final String s30302 = "30302";
+ public static final String s30303 = "30303";
+ public static final String s30304 = "30304";
+ public static final String s30305 = "30305";
+ public static final String s30306 = "30306";
+ public static final String s30307 = "30307";
+ public static final String s30308 = "30308";
+ public static final String s30309 = "30309";
+ public static final String s30310 = "30310";
+ public static final String s30311 = "30311";
+ public static final String s30312 = "30312";
+ public static final String s30313 = "30313";
+ public static final String s30314 = "30314";
+ public static final String s30315 = "30315";
+ public static final String s30316 = "30316";
+ public static final String s30317 = "30317";
+ public static final String s30318 = "30318";
+ public static final String s30319 = "30319";
+ public static final String s30320 = "30320";
+ public static final String s30321 = "30321";
+ public static final String s30322 = "30322";
+ public static final String s30323 = "30323";
+ public static final String s30324 = "30324";
+ public static final String s30325 = "30325";
+ public static final String s30326 = "30326";
+ public static final String s30327 = "30327";
+ public static final String s30328 = "30328";
+ public static final String s30329 = "30329";
+ public static final String s30330 = "30330";
+ public static final String s30331 = "30331";
+ public static final String s30332 = "30332";
+ public static final String s30333 = "30333";
+ public static final String s30334 = "30334";
+ public static final String s30335 = "30335";
+ public static final String s30336 = "30336";
+ public static final String s30337 = "30337";
+ public static final String s30338 = "30338";
+ public static final String s30339 = "30339";
+ public static final String s30340 = "30340";
+ public static final String s30341 = "30341";
+ public static final String s30342 = "30342";
+ public static final String s30343 = "30343";
+ public static final String s30344 = "30344";
+ public static final String s30345 = "30345";
+ public static final String s30346 = "30346";
+ public static final String s30347 = "30347";
+ public static final String s30348 = "30348";
+ public static final String s30349 = "30349";
+ public static final String s30350 = "30350";
+ public static final String s30351 = "30351";
+ public static final String s30352 = "30352";
+ public static final String s30353 = "30353";
+ public static final String s30354 = "30354";
+ public static final String s30355 = "30355";
+ public static final String s30356 = "30356";
+ public static final String s30357 = "30357";
+ public static final String s30358 = "30358";
+ public static final String s30359 = "30359";
+ public static final String s30360 = "30360";
+ public static final String s30361 = "30361";
+ public static final String s30362 = "30362";
+ public static final String s30363 = "30363";
+ public static final String s30364 = "30364";
+ public static final String s30365 = "30365";
+ public static final String s30366 = "30366";
+ public static final String s30367 = "30367";
+ public static final String s30368 = "30368";
+ public static final String s30369 = "30369";
+ public static final String s30370 = "30370";
+ public static final String s30371 = "30371";
+ public static final String s30372 = "30372";
+ public static final String s30373 = "30373";
+ public static final String s30374 = "30374";
+ public static final String s30375 = "30375";
+ public static final String s30376 = "30376";
+ public static final String s30377 = "30377";
+ public static final String s30378 = "30378";
+ public static final String s30379 = "30379";
+ public static final String s30380 = "30380";
+ public static final String s30381 = "30381";
+ public static final String s30382 = "30382";
+ public static final String s30383 = "30383";
+ public static final String s30384 = "30384";
+ public static final String s30385 = "30385";
+ public static final String s30386 = "30386";
+ public static final String s30387 = "30387";
+ public static final String s30388 = "30388";
+ public static final String s30389 = "30389";
+ public static final String s30390 = "30390";
+ public static final String s30391 = "30391";
+ public static final String s30392 = "30392";
+ public static final String s30393 = "30393";
+ public static final String s30394 = "30394";
+ public static final String s30395 = "30395";
+ public static final String s30396 = "30396";
+ public static final String s30397 = "30397";
+ public static final String s30398 = "30398";
+ public static final String s30399 = "30399";
+ public static final String s30400 = "30400";
+ public static final String s30401 = "30401";
+ public static final String s30402 = "30402";
+ public static final String s30403 = "30403";
+ public static final String s30404 = "30404";
+ public static final String s30405 = "30405";
+ public static final String s30406 = "30406";
+ public static final String s30407 = "30407";
+ public static final String s30408 = "30408";
+ public static final String s30409 = "30409";
+ public static final String s30410 = "30410";
+ public static final String s30411 = "30411";
+ public static final String s30412 = "30412";
+ public static final String s30413 = "30413";
+ public static final String s30414 = "30414";
+ public static final String s30415 = "30415";
+ public static final String s30416 = "30416";
+ public static final String s30417 = "30417";
+ public static final String s30418 = "30418";
+ public static final String s30419 = "30419";
+ public static final String s30420 = "30420";
+ public static final String s30421 = "30421";
+ public static final String s30422 = "30422";
+ public static final String s30423 = "30423";
+ public static final String s30424 = "30424";
+ public static final String s30425 = "30425";
+ public static final String s30426 = "30426";
+ public static final String s30427 = "30427";
+ public static final String s30428 = "30428";
+ public static final String s30429 = "30429";
+ public static final String s30430 = "30430";
+ public static final String s30431 = "30431";
+ public static final String s30432 = "30432";
+ public static final String s30433 = "30433";
+ public static final String s30434 = "30434";
+ public static final String s30435 = "30435";
+ public static final String s30436 = "30436";
+ public static final String s30437 = "30437";
+ public static final String s30438 = "30438";
+ public static final String s30439 = "30439";
+ public static final String s30440 = "30440";
+ public static final String s30441 = "30441";
+ public static final String s30442 = "30442";
+ public static final String s30443 = "30443";
+ public static final String s30444 = "30444";
+ public static final String s30445 = "30445";
+ public static final String s30446 = "30446";
+ public static final String s30447 = "30447";
+ public static final String s30448 = "30448";
+ public static final String s30449 = "30449";
+ public static final String s30450 = "30450";
+ public static final String s30451 = "30451";
+ public static final String s30452 = "30452";
+ public static final String s30453 = "30453";
+ public static final String s30454 = "30454";
+ public static final String s30455 = "30455";
+ public static final String s30456 = "30456";
+ public static final String s30457 = "30457";
+ public static final String s30458 = "30458";
+ public static final String s30459 = "30459";
+ public static final String s30460 = "30460";
+ public static final String s30461 = "30461";
+ public static final String s30462 = "30462";
+ public static final String s30463 = "30463";
+ public static final String s30464 = "30464";
+ public static final String s30465 = "30465";
+ public static final String s30466 = "30466";
+ public static final String s30467 = "30467";
+ public static final String s30468 = "30468";
+ public static final String s30469 = "30469";
+ public static final String s30470 = "30470";
+ public static final String s30471 = "30471";
+ public static final String s30472 = "30472";
+ public static final String s30473 = "30473";
+ public static final String s30474 = "30474";
+ public static final String s30475 = "30475";
+ public static final String s30476 = "30476";
+ public static final String s30477 = "30477";
+ public static final String s30478 = "30478";
+ public static final String s30479 = "30479";
+ public static final String s30480 = "30480";
+ public static final String s30481 = "30481";
+ public static final String s30482 = "30482";
+ public static final String s30483 = "30483";
+ public static final String s30484 = "30484";
+ public static final String s30485 = "30485";
+ public static final String s30486 = "30486";
+ public static final String s30487 = "30487";
+ public static final String s30488 = "30488";
+ public static final String s30489 = "30489";
+ public static final String s30490 = "30490";
+ public static final String s30491 = "30491";
+ public static final String s30492 = "30492";
+ public static final String s30493 = "30493";
+ public static final String s30494 = "30494";
+ public static final String s30495 = "30495";
+ public static final String s30496 = "30496";
+ public static final String s30497 = "30497";
+ public static final String s30498 = "30498";
+ public static final String s30499 = "30499";
+ public static final String s30500 = "30500";
+ public static final String s30501 = "30501";
+ public static final String s30502 = "30502";
+ public static final String s30503 = "30503";
+ public static final String s30504 = "30504";
+ public static final String s30505 = "30505";
+ public static final String s30506 = "30506";
+ public static final String s30507 = "30507";
+ public static final String s30508 = "30508";
+ public static final String s30509 = "30509";
+ public static final String s30510 = "30510";
+ public static final String s30511 = "30511";
+ public static final String s30512 = "30512";
+ public static final String s30513 = "30513";
+ public static final String s30514 = "30514";
+ public static final String s30515 = "30515";
+ public static final String s30516 = "30516";
+ public static final String s30517 = "30517";
+ public static final String s30518 = "30518";
+ public static final String s30519 = "30519";
+ public static final String s30520 = "30520";
+ public static final String s30521 = "30521";
+ public static final String s30522 = "30522";
+ public static final String s30523 = "30523";
+ public static final String s30524 = "30524";
+ public static final String s30525 = "30525";
+ public static final String s30526 = "30526";
+ public static final String s30527 = "30527";
+ public static final String s30528 = "30528";
+ public static final String s30529 = "30529";
+ public static final String s30530 = "30530";
+ public static final String s30531 = "30531";
+ public static final String s30532 = "30532";
+ public static final String s30533 = "30533";
+ public static final String s30534 = "30534";
+ public static final String s30535 = "30535";
+ public static final String s30536 = "30536";
+ public static final String s30537 = "30537";
+ public static final String s30538 = "30538";
+ public static final String s30539 = "30539";
+ public static final String s30540 = "30540";
+ public static final String s30541 = "30541";
+ public static final String s30542 = "30542";
+ public static final String s30543 = "30543";
+ public static final String s30544 = "30544";
+ public static final String s30545 = "30545";
+ public static final String s30546 = "30546";
+ public static final String s30547 = "30547";
+ public static final String s30548 = "30548";
+ public static final String s30549 = "30549";
+ public static final String s30550 = "30550";
+ public static final String s30551 = "30551";
+ public static final String s30552 = "30552";
+ public static final String s30553 = "30553";
+ public static final String s30554 = "30554";
+ public static final String s30555 = "30555";
+ public static final String s30556 = "30556";
+ public static final String s30557 = "30557";
+ public static final String s30558 = "30558";
+ public static final String s30559 = "30559";
+ public static final String s30560 = "30560";
+ public static final String s30561 = "30561";
+ public static final String s30562 = "30562";
+ public static final String s30563 = "30563";
+ public static final String s30564 = "30564";
+ public static final String s30565 = "30565";
+ public static final String s30566 = "30566";
+ public static final String s30567 = "30567";
+ public static final String s30568 = "30568";
+ public static final String s30569 = "30569";
+ public static final String s30570 = "30570";
+ public static final String s30571 = "30571";
+ public static final String s30572 = "30572";
+ public static final String s30573 = "30573";
+ public static final String s30574 = "30574";
+ public static final String s30575 = "30575";
+ public static final String s30576 = "30576";
+ public static final String s30577 = "30577";
+ public static final String s30578 = "30578";
+ public static final String s30579 = "30579";
+ public static final String s30580 = "30580";
+ public static final String s30581 = "30581";
+ public static final String s30582 = "30582";
+ public static final String s30583 = "30583";
+ public static final String s30584 = "30584";
+ public static final String s30585 = "30585";
+ public static final String s30586 = "30586";
+ public static final String s30587 = "30587";
+ public static final String s30588 = "30588";
+ public static final String s30589 = "30589";
+ public static final String s30590 = "30590";
+ public static final String s30591 = "30591";
+ public static final String s30592 = "30592";
+ public static final String s30593 = "30593";
+ public static final String s30594 = "30594";
+ public static final String s30595 = "30595";
+ public static final String s30596 = "30596";
+ public static final String s30597 = "30597";
+ public static final String s30598 = "30598";
+ public static final String s30599 = "30599";
+ public static final String s30600 = "30600";
+ public static final String s30601 = "30601";
+ public static final String s30602 = "30602";
+ public static final String s30603 = "30603";
+ public static final String s30604 = "30604";
+ public static final String s30605 = "30605";
+ public static final String s30606 = "30606";
+ public static final String s30607 = "30607";
+ public static final String s30608 = "30608";
+ public static final String s30609 = "30609";
+ public static final String s30610 = "30610";
+ public static final String s30611 = "30611";
+ public static final String s30612 = "30612";
+ public static final String s30613 = "30613";
+ public static final String s30614 = "30614";
+ public static final String s30615 = "30615";
+ public static final String s30616 = "30616";
+ public static final String s30617 = "30617";
+ public static final String s30618 = "30618";
+ public static final String s30619 = "30619";
+ public static final String s30620 = "30620";
+ public static final String s30621 = "30621";
+ public static final String s30622 = "30622";
+ public static final String s30623 = "30623";
+ public static final String s30624 = "30624";
+ public static final String s30625 = "30625";
+ public static final String s30626 = "30626";
+ public static final String s30627 = "30627";
+ public static final String s30628 = "30628";
+ public static final String s30629 = "30629";
+ public static final String s30630 = "30630";
+ public static final String s30631 = "30631";
+ public static final String s30632 = "30632";
+ public static final String s30633 = "30633";
+ public static final String s30634 = "30634";
+ public static final String s30635 = "30635";
+ public static final String s30636 = "30636";
+ public static final String s30637 = "30637";
+ public static final String s30638 = "30638";
+ public static final String s30639 = "30639";
+ public static final String s30640 = "30640";
+ public static final String s30641 = "30641";
+ public static final String s30642 = "30642";
+ public static final String s30643 = "30643";
+ public static final String s30644 = "30644";
+ public static final String s30645 = "30645";
+ public static final String s30646 = "30646";
+ public static final String s30647 = "30647";
+ public static final String s30648 = "30648";
+ public static final String s30649 = "30649";
+ public static final String s30650 = "30650";
+ public static final String s30651 = "30651";
+ public static final String s30652 = "30652";
+ public static final String s30653 = "30653";
+ public static final String s30654 = "30654";
+ public static final String s30655 = "30655";
+ public static final String s30656 = "30656";
+ public static final String s30657 = "30657";
+ public static final String s30658 = "30658";
+ public static final String s30659 = "30659";
+ public static final String s30660 = "30660";
+ public static final String s30661 = "30661";
+ public static final String s30662 = "30662";
+ public static final String s30663 = "30663";
+ public static final String s30664 = "30664";
+ public static final String s30665 = "30665";
+ public static final String s30666 = "30666";
+ public static final String s30667 = "30667";
+ public static final String s30668 = "30668";
+ public static final String s30669 = "30669";
+ public static final String s30670 = "30670";
+ public static final String s30671 = "30671";
+ public static final String s30672 = "30672";
+ public static final String s30673 = "30673";
+ public static final String s30674 = "30674";
+ public static final String s30675 = "30675";
+ public static final String s30676 = "30676";
+ public static final String s30677 = "30677";
+ public static final String s30678 = "30678";
+ public static final String s30679 = "30679";
+ public static final String s30680 = "30680";
+ public static final String s30681 = "30681";
+ public static final String s30682 = "30682";
+ public static final String s30683 = "30683";
+ public static final String s30684 = "30684";
+ public static final String s30685 = "30685";
+ public static final String s30686 = "30686";
+ public static final String s30687 = "30687";
+ public static final String s30688 = "30688";
+ public static final String s30689 = "30689";
+ public static final String s30690 = "30690";
+ public static final String s30691 = "30691";
+ public static final String s30692 = "30692";
+ public static final String s30693 = "30693";
+ public static final String s30694 = "30694";
+ public static final String s30695 = "30695";
+ public static final String s30696 = "30696";
+ public static final String s30697 = "30697";
+ public static final String s30698 = "30698";
+ public static final String s30699 = "30699";
+ public static final String s30700 = "30700";
+ public static final String s30701 = "30701";
+ public static final String s30702 = "30702";
+ public static final String s30703 = "30703";
+ public static final String s30704 = "30704";
+ public static final String s30705 = "30705";
+ public static final String s30706 = "30706";
+ public static final String s30707 = "30707";
+ public static final String s30708 = "30708";
+ public static final String s30709 = "30709";
+ public static final String s30710 = "30710";
+ public static final String s30711 = "30711";
+ public static final String s30712 = "30712";
+ public static final String s30713 = "30713";
+ public static final String s30714 = "30714";
+ public static final String s30715 = "30715";
+ public static final String s30716 = "30716";
+ public static final String s30717 = "30717";
+ public static final String s30718 = "30718";
+ public static final String s30719 = "30719";
+ public static final String s30720 = "30720";
+ public static final String s30721 = "30721";
+ public static final String s30722 = "30722";
+ public static final String s30723 = "30723";
+ public static final String s30724 = "30724";
+ public static final String s30725 = "30725";
+ public static final String s30726 = "30726";
+ public static final String s30727 = "30727";
+ public static final String s30728 = "30728";
+ public static final String s30729 = "30729";
+ public static final String s30730 = "30730";
+ public static final String s30731 = "30731";
+ public static final String s30732 = "30732";
+ public static final String s30733 = "30733";
+ public static final String s30734 = "30734";
+ public static final String s30735 = "30735";
+ public static final String s30736 = "30736";
+ public static final String s30737 = "30737";
+ public static final String s30738 = "30738";
+ public static final String s30739 = "30739";
+ public static final String s30740 = "30740";
+ public static final String s30741 = "30741";
+ public static final String s30742 = "30742";
+ public static final String s30743 = "30743";
+ public static final String s30744 = "30744";
+ public static final String s30745 = "30745";
+ public static final String s30746 = "30746";
+ public static final String s30747 = "30747";
+ public static final String s30748 = "30748";
+ public static final String s30749 = "30749";
+ public static final String s30750 = "30750";
+ public static final String s30751 = "30751";
+ public static final String s30752 = "30752";
+ public static final String s30753 = "30753";
+ public static final String s30754 = "30754";
+ public static final String s30755 = "30755";
+ public static final String s30756 = "30756";
+ public static final String s30757 = "30757";
+ public static final String s30758 = "30758";
+ public static final String s30759 = "30759";
+ public static final String s30760 = "30760";
+ public static final String s30761 = "30761";
+ public static final String s30762 = "30762";
+ public static final String s30763 = "30763";
+ public static final String s30764 = "30764";
+ public static final String s30765 = "30765";
+ public static final String s30766 = "30766";
+ public static final String s30767 = "30767";
+ public static final String s30768 = "30768";
+ public static final String s30769 = "30769";
+ public static final String s30770 = "30770";
+ public static final String s30771 = "30771";
+ public static final String s30772 = "30772";
+ public static final String s30773 = "30773";
+ public static final String s30774 = "30774";
+ public static final String s30775 = "30775";
+ public static final String s30776 = "30776";
+ public static final String s30777 = "30777";
+ public static final String s30778 = "30778";
+ public static final String s30779 = "30779";
+ public static final String s30780 = "30780";
+ public static final String s30781 = "30781";
+ public static final String s30782 = "30782";
+ public static final String s30783 = "30783";
+ public static final String s30784 = "30784";
+ public static final String s30785 = "30785";
+ public static final String s30786 = "30786";
+ public static final String s30787 = "30787";
+ public static final String s30788 = "30788";
+ public static final String s30789 = "30789";
+ public static final String s30790 = "30790";
+ public static final String s30791 = "30791";
+ public static final String s30792 = "30792";
+ public static final String s30793 = "30793";
+ public static final String s30794 = "30794";
+ public static final String s30795 = "30795";
+ public static final String s30796 = "30796";
+ public static final String s30797 = "30797";
+ public static final String s30798 = "30798";
+ public static final String s30799 = "30799";
+ public static final String s30800 = "30800";
+ public static final String s30801 = "30801";
+ public static final String s30802 = "30802";
+ public static final String s30803 = "30803";
+ public static final String s30804 = "30804";
+ public static final String s30805 = "30805";
+ public static final String s30806 = "30806";
+ public static final String s30807 = "30807";
+ public static final String s30808 = "30808";
+ public static final String s30809 = "30809";
+ public static final String s30810 = "30810";
+ public static final String s30811 = "30811";
+ public static final String s30812 = "30812";
+ public static final String s30813 = "30813";
+ public static final String s30814 = "30814";
+ public static final String s30815 = "30815";
+ public static final String s30816 = "30816";
+ public static final String s30817 = "30817";
+ public static final String s30818 = "30818";
+ public static final String s30819 = "30819";
+ public static final String s30820 = "30820";
+ public static final String s30821 = "30821";
+ public static final String s30822 = "30822";
+ public static final String s30823 = "30823";
+ public static final String s30824 = "30824";
+ public static final String s30825 = "30825";
+ public static final String s30826 = "30826";
+ public static final String s30827 = "30827";
+ public static final String s30828 = "30828";
+ public static final String s30829 = "30829";
+ public static final String s30830 = "30830";
+ public static final String s30831 = "30831";
+ public static final String s30832 = "30832";
+ public static final String s30833 = "30833";
+ public static final String s30834 = "30834";
+ public static final String s30835 = "30835";
+ public static final String s30836 = "30836";
+ public static final String s30837 = "30837";
+ public static final String s30838 = "30838";
+ public static final String s30839 = "30839";
+ public static final String s30840 = "30840";
+ public static final String s30841 = "30841";
+ public static final String s30842 = "30842";
+ public static final String s30843 = "30843";
+ public static final String s30844 = "30844";
+ public static final String s30845 = "30845";
+ public static final String s30846 = "30846";
+ public static final String s30847 = "30847";
+ public static final String s30848 = "30848";
+ public static final String s30849 = "30849";
+ public static final String s30850 = "30850";
+ public static final String s30851 = "30851";
+ public static final String s30852 = "30852";
+ public static final String s30853 = "30853";
+ public static final String s30854 = "30854";
+ public static final String s30855 = "30855";
+ public static final String s30856 = "30856";
+ public static final String s30857 = "30857";
+ public static final String s30858 = "30858";
+ public static final String s30859 = "30859";
+ public static final String s30860 = "30860";
+ public static final String s30861 = "30861";
+ public static final String s30862 = "30862";
+ public static final String s30863 = "30863";
+ public static final String s30864 = "30864";
+ public static final String s30865 = "30865";
+ public static final String s30866 = "30866";
+ public static final String s30867 = "30867";
+ public static final String s30868 = "30868";
+ public static final String s30869 = "30869";
+ public static final String s30870 = "30870";
+ public static final String s30871 = "30871";
+ public static final String s30872 = "30872";
+ public static final String s30873 = "30873";
+ public static final String s30874 = "30874";
+ public static final String s30875 = "30875";
+ public static final String s30876 = "30876";
+ public static final String s30877 = "30877";
+ public static final String s30878 = "30878";
+ public static final String s30879 = "30879";
+ public static final String s30880 = "30880";
+ public static final String s30881 = "30881";
+ public static final String s30882 = "30882";
+ public static final String s30883 = "30883";
+ public static final String s30884 = "30884";
+ public static final String s30885 = "30885";
+ public static final String s30886 = "30886";
+ public static final String s30887 = "30887";
+ public static final String s30888 = "30888";
+ public static final String s30889 = "30889";
+ public static final String s30890 = "30890";
+ public static final String s30891 = "30891";
+ public static final String s30892 = "30892";
+ public static final String s30893 = "30893";
+ public static final String s30894 = "30894";
+ public static final String s30895 = "30895";
+ public static final String s30896 = "30896";
+ public static final String s30897 = "30897";
+ public static final String s30898 = "30898";
+ public static final String s30899 = "30899";
+ public static final String s30900 = "30900";
+ public static final String s30901 = "30901";
+ public static final String s30902 = "30902";
+ public static final String s30903 = "30903";
+ public static final String s30904 = "30904";
+ public static final String s30905 = "30905";
+ public static final String s30906 = "30906";
+ public static final String s30907 = "30907";
+ public static final String s30908 = "30908";
+ public static final String s30909 = "30909";
+ public static final String s30910 = "30910";
+ public static final String s30911 = "30911";
+ public static final String s30912 = "30912";
+ public static final String s30913 = "30913";
+ public static final String s30914 = "30914";
+ public static final String s30915 = "30915";
+ public static final String s30916 = "30916";
+ public static final String s30917 = "30917";
+ public static final String s30918 = "30918";
+ public static final String s30919 = "30919";
+ public static final String s30920 = "30920";
+ public static final String s30921 = "30921";
+ public static final String s30922 = "30922";
+ public static final String s30923 = "30923";
+ public static final String s30924 = "30924";
+ public static final String s30925 = "30925";
+ public static final String s30926 = "30926";
+ public static final String s30927 = "30927";
+ public static final String s30928 = "30928";
+ public static final String s30929 = "30929";
+ public static final String s30930 = "30930";
+ public static final String s30931 = "30931";
+ public static final String s30932 = "30932";
+ public static final String s30933 = "30933";
+ public static final String s30934 = "30934";
+ public static final String s30935 = "30935";
+ public static final String s30936 = "30936";
+ public static final String s30937 = "30937";
+ public static final String s30938 = "30938";
+ public static final String s30939 = "30939";
+ public static final String s30940 = "30940";
+ public static final String s30941 = "30941";
+ public static final String s30942 = "30942";
+ public static final String s30943 = "30943";
+ public static final String s30944 = "30944";
+ public static final String s30945 = "30945";
+ public static final String s30946 = "30946";
+ public static final String s30947 = "30947";
+ public static final String s30948 = "30948";
+ public static final String s30949 = "30949";
+ public static final String s30950 = "30950";
+ public static final String s30951 = "30951";
+ public static final String s30952 = "30952";
+ public static final String s30953 = "30953";
+ public static final String s30954 = "30954";
+ public static final String s30955 = "30955";
+ public static final String s30956 = "30956";
+ public static final String s30957 = "30957";
+ public static final String s30958 = "30958";
+ public static final String s30959 = "30959";
+ public static final String s30960 = "30960";
+ public static final String s30961 = "30961";
+ public static final String s30962 = "30962";
+ public static final String s30963 = "30963";
+ public static final String s30964 = "30964";
+ public static final String s30965 = "30965";
+ public static final String s30966 = "30966";
+ public static final String s30967 = "30967";
+ public static final String s30968 = "30968";
+ public static final String s30969 = "30969";
+ public static final String s30970 = "30970";
+ public static final String s30971 = "30971";
+ public static final String s30972 = "30972";
+ public static final String s30973 = "30973";
+ public static final String s30974 = "30974";
+ public static final String s30975 = "30975";
+ public static final String s30976 = "30976";
+ public static final String s30977 = "30977";
+ public static final String s30978 = "30978";
+ public static final String s30979 = "30979";
+ public static final String s30980 = "30980";
+ public static final String s30981 = "30981";
+ public static final String s30982 = "30982";
+ public static final String s30983 = "30983";
+ public static final String s30984 = "30984";
+ public static final String s30985 = "30985";
+ public static final String s30986 = "30986";
+ public static final String s30987 = "30987";
+ public static final String s30988 = "30988";
+ public static final String s30989 = "30989";
+ public static final String s30990 = "30990";
+ public static final String s30991 = "30991";
+ public static final String s30992 = "30992";
+ public static final String s30993 = "30993";
+ public static final String s30994 = "30994";
+ public static final String s30995 = "30995";
+ public static final String s30996 = "30996";
+ public static final String s30997 = "30997";
+ public static final String s30998 = "30998";
+ public static final String s30999 = "30999";
+ public static final String s31000 = "31000";
+ public static final String s31001 = "31001";
+ public static final String s31002 = "31002";
+ public static final String s31003 = "31003";
+ public static final String s31004 = "31004";
+ public static final String s31005 = "31005";
+ public static final String s31006 = "31006";
+ public static final String s31007 = "31007";
+ public static final String s31008 = "31008";
+ public static final String s31009 = "31009";
+ public static final String s31010 = "31010";
+ public static final String s31011 = "31011";
+ public static final String s31012 = "31012";
+ public static final String s31013 = "31013";
+ public static final String s31014 = "31014";
+ public static final String s31015 = "31015";
+ public static final String s31016 = "31016";
+ public static final String s31017 = "31017";
+ public static final String s31018 = "31018";
+ public static final String s31019 = "31019";
+ public static final String s31020 = "31020";
+ public static final String s31021 = "31021";
+ public static final String s31022 = "31022";
+ public static final String s31023 = "31023";
+ public static final String s31024 = "31024";
+ public static final String s31025 = "31025";
+ public static final String s31026 = "31026";
+ public static final String s31027 = "31027";
+ public static final String s31028 = "31028";
+ public static final String s31029 = "31029";
+ public static final String s31030 = "31030";
+ public static final String s31031 = "31031";
+ public static final String s31032 = "31032";
+ public static final String s31033 = "31033";
+ public static final String s31034 = "31034";
+ public static final String s31035 = "31035";
+ public static final String s31036 = "31036";
+ public static final String s31037 = "31037";
+ public static final String s31038 = "31038";
+ public static final String s31039 = "31039";
+ public static final String s31040 = "31040";
+ public static final String s31041 = "31041";
+ public static final String s31042 = "31042";
+ public static final String s31043 = "31043";
+ public static final String s31044 = "31044";
+ public static final String s31045 = "31045";
+ public static final String s31046 = "31046";
+ public static final String s31047 = "31047";
+ public static final String s31048 = "31048";
+ public static final String s31049 = "31049";
+ public static final String s31050 = "31050";
+ public static final String s31051 = "31051";
+ public static final String s31052 = "31052";
+ public static final String s31053 = "31053";
+ public static final String s31054 = "31054";
+ public static final String s31055 = "31055";
+ public static final String s31056 = "31056";
+ public static final String s31057 = "31057";
+ public static final String s31058 = "31058";
+ public static final String s31059 = "31059";
+ public static final String s31060 = "31060";
+ public static final String s31061 = "31061";
+ public static final String s31062 = "31062";
+ public static final String s31063 = "31063";
+ public static final String s31064 = "31064";
+ public static final String s31065 = "31065";
+ public static final String s31066 = "31066";
+ public static final String s31067 = "31067";
+ public static final String s31068 = "31068";
+ public static final String s31069 = "31069";
+ public static final String s31070 = "31070";
+ public static final String s31071 = "31071";
+ public static final String s31072 = "31072";
+ public static final String s31073 = "31073";
+ public static final String s31074 = "31074";
+ public static final String s31075 = "31075";
+ public static final String s31076 = "31076";
+ public static final String s31077 = "31077";
+ public static final String s31078 = "31078";
+ public static final String s31079 = "31079";
+ public static final String s31080 = "31080";
+ public static final String s31081 = "31081";
+ public static final String s31082 = "31082";
+ public static final String s31083 = "31083";
+ public static final String s31084 = "31084";
+ public static final String s31085 = "31085";
+ public static final String s31086 = "31086";
+ public static final String s31087 = "31087";
+ public static final String s31088 = "31088";
+ public static final String s31089 = "31089";
+ public static final String s31090 = "31090";
+ public static final String s31091 = "31091";
+ public static final String s31092 = "31092";
+ public static final String s31093 = "31093";
+ public static final String s31094 = "31094";
+ public static final String s31095 = "31095";
+ public static final String s31096 = "31096";
+ public static final String s31097 = "31097";
+ public static final String s31098 = "31098";
+ public static final String s31099 = "31099";
+ public static final String s31100 = "31100";
+ public static final String s31101 = "31101";
+ public static final String s31102 = "31102";
+ public static final String s31103 = "31103";
+ public static final String s31104 = "31104";
+ public static final String s31105 = "31105";
+ public static final String s31106 = "31106";
+ public static final String s31107 = "31107";
+ public static final String s31108 = "31108";
+ public static final String s31109 = "31109";
+ public static final String s31110 = "31110";
+ public static final String s31111 = "31111";
+ public static final String s31112 = "31112";
+ public static final String s31113 = "31113";
+ public static final String s31114 = "31114";
+ public static final String s31115 = "31115";
+ public static final String s31116 = "31116";
+ public static final String s31117 = "31117";
+ public static final String s31118 = "31118";
+ public static final String s31119 = "31119";
+ public static final String s31120 = "31120";
+ public static final String s31121 = "31121";
+ public static final String s31122 = "31122";
+ public static final String s31123 = "31123";
+ public static final String s31124 = "31124";
+ public static final String s31125 = "31125";
+ public static final String s31126 = "31126";
+ public static final String s31127 = "31127";
+ public static final String s31128 = "31128";
+ public static final String s31129 = "31129";
+ public static final String s31130 = "31130";
+ public static final String s31131 = "31131";
+ public static final String s31132 = "31132";
+ public static final String s31133 = "31133";
+ public static final String s31134 = "31134";
+ public static final String s31135 = "31135";
+ public static final String s31136 = "31136";
+ public static final String s31137 = "31137";
+ public static final String s31138 = "31138";
+ public static final String s31139 = "31139";
+ public static final String s31140 = "31140";
+ public static final String s31141 = "31141";
+ public static final String s31142 = "31142";
+ public static final String s31143 = "31143";
+ public static final String s31144 = "31144";
+ public static final String s31145 = "31145";
+ public static final String s31146 = "31146";
+ public static final String s31147 = "31147";
+ public static final String s31148 = "31148";
+ public static final String s31149 = "31149";
+ public static final String s31150 = "31150";
+ public static final String s31151 = "31151";
+ public static final String s31152 = "31152";
+ public static final String s31153 = "31153";
+ public static final String s31154 = "31154";
+ public static final String s31155 = "31155";
+ public static final String s31156 = "31156";
+ public static final String s31157 = "31157";
+ public static final String s31158 = "31158";
+ public static final String s31159 = "31159";
+ public static final String s31160 = "31160";
+ public static final String s31161 = "31161";
+ public static final String s31162 = "31162";
+ public static final String s31163 = "31163";
+ public static final String s31164 = "31164";
+ public static final String s31165 = "31165";
+ public static final String s31166 = "31166";
+ public static final String s31167 = "31167";
+ public static final String s31168 = "31168";
+ public static final String s31169 = "31169";
+ public static final String s31170 = "31170";
+ public static final String s31171 = "31171";
+ public static final String s31172 = "31172";
+ public static final String s31173 = "31173";
+ public static final String s31174 = "31174";
+ public static final String s31175 = "31175";
+ public static final String s31176 = "31176";
+ public static final String s31177 = "31177";
+ public static final String s31178 = "31178";
+ public static final String s31179 = "31179";
+ public static final String s31180 = "31180";
+ public static final String s31181 = "31181";
+ public static final String s31182 = "31182";
+ public static final String s31183 = "31183";
+ public static final String s31184 = "31184";
+ public static final String s31185 = "31185";
+ public static final String s31186 = "31186";
+ public static final String s31187 = "31187";
+ public static final String s31188 = "31188";
+ public static final String s31189 = "31189";
+ public static final String s31190 = "31190";
+ public static final String s31191 = "31191";
+ public static final String s31192 = "31192";
+ public static final String s31193 = "31193";
+ public static final String s31194 = "31194";
+ public static final String s31195 = "31195";
+ public static final String s31196 = "31196";
+ public static final String s31197 = "31197";
+ public static final String s31198 = "31198";
+ public static final String s31199 = "31199";
+ public static final String s31200 = "31200";
+ public static final String s31201 = "31201";
+ public static final String s31202 = "31202";
+ public static final String s31203 = "31203";
+ public static final String s31204 = "31204";
+ public static final String s31205 = "31205";
+ public static final String s31206 = "31206";
+ public static final String s31207 = "31207";
+ public static final String s31208 = "31208";
+ public static final String s31209 = "31209";
+ public static final String s31210 = "31210";
+ public static final String s31211 = "31211";
+ public static final String s31212 = "31212";
+ public static final String s31213 = "31213";
+ public static final String s31214 = "31214";
+ public static final String s31215 = "31215";
+ public static final String s31216 = "31216";
+ public static final String s31217 = "31217";
+ public static final String s31218 = "31218";
+ public static final String s31219 = "31219";
+ public static final String s31220 = "31220";
+ public static final String s31221 = "31221";
+ public static final String s31222 = "31222";
+ public static final String s31223 = "31223";
+ public static final String s31224 = "31224";
+ public static final String s31225 = "31225";
+ public static final String s31226 = "31226";
+ public static final String s31227 = "31227";
+ public static final String s31228 = "31228";
+ public static final String s31229 = "31229";
+ public static final String s31230 = "31230";
+ public static final String s31231 = "31231";
+ public static final String s31232 = "31232";
+ public static final String s31233 = "31233";
+ public static final String s31234 = "31234";
+ public static final String s31235 = "31235";
+ public static final String s31236 = "31236";
+ public static final String s31237 = "31237";
+ public static final String s31238 = "31238";
+ public static final String s31239 = "31239";
+ public static final String s31240 = "31240";
+ public static final String s31241 = "31241";
+ public static final String s31242 = "31242";
+ public static final String s31243 = "31243";
+ public static final String s31244 = "31244";
+ public static final String s31245 = "31245";
+ public static final String s31246 = "31246";
+ public static final String s31247 = "31247";
+ public static final String s31248 = "31248";
+ public static final String s31249 = "31249";
+ public static final String s31250 = "31250";
+ public static final String s31251 = "31251";
+ public static final String s31252 = "31252";
+ public static final String s31253 = "31253";
+ public static final String s31254 = "31254";
+ public static final String s31255 = "31255";
+ public static final String s31256 = "31256";
+ public static final String s31257 = "31257";
+ public static final String s31258 = "31258";
+ public static final String s31259 = "31259";
+ public static final String s31260 = "31260";
+ public static final String s31261 = "31261";
+ public static final String s31262 = "31262";
+ public static final String s31263 = "31263";
+ public static final String s31264 = "31264";
+ public static final String s31265 = "31265";
+ public static final String s31266 = "31266";
+ public static final String s31267 = "31267";
+ public static final String s31268 = "31268";
+ public static final String s31269 = "31269";
+ public static final String s31270 = "31270";
+ public static final String s31271 = "31271";
+ public static final String s31272 = "31272";
+ public static final String s31273 = "31273";
+ public static final String s31274 = "31274";
+ public static final String s31275 = "31275";
+ public static final String s31276 = "31276";
+ public static final String s31277 = "31277";
+ public static final String s31278 = "31278";
+ public static final String s31279 = "31279";
+ public static final String s31280 = "31280";
+ public static final String s31281 = "31281";
+ public static final String s31282 = "31282";
+ public static final String s31283 = "31283";
+ public static final String s31284 = "31284";
+ public static final String s31285 = "31285";
+ public static final String s31286 = "31286";
+ public static final String s31287 = "31287";
+ public static final String s31288 = "31288";
+ public static final String s31289 = "31289";
+ public static final String s31290 = "31290";
+ public static final String s31291 = "31291";
+ public static final String s31292 = "31292";
+ public static final String s31293 = "31293";
+ public static final String s31294 = "31294";
+ public static final String s31295 = "31295";
+ public static final String s31296 = "31296";
+ public static final String s31297 = "31297";
+ public static final String s31298 = "31298";
+ public static final String s31299 = "31299";
+ public static final String s31300 = "31300";
+ public static final String s31301 = "31301";
+ public static final String s31302 = "31302";
+ public static final String s31303 = "31303";
+ public static final String s31304 = "31304";
+ public static final String s31305 = "31305";
+ public static final String s31306 = "31306";
+ public static final String s31307 = "31307";
+ public static final String s31308 = "31308";
+ public static final String s31309 = "31309";
+ public static final String s31310 = "31310";
+ public static final String s31311 = "31311";
+ public static final String s31312 = "31312";
+ public static final String s31313 = "31313";
+ public static final String s31314 = "31314";
+ public static final String s31315 = "31315";
+ public static final String s31316 = "31316";
+ public static final String s31317 = "31317";
+ public static final String s31318 = "31318";
+ public static final String s31319 = "31319";
+ public static final String s31320 = "31320";
+ public static final String s31321 = "31321";
+ public static final String s31322 = "31322";
+ public static final String s31323 = "31323";
+ public static final String s31324 = "31324";
+ public static final String s31325 = "31325";
+ public static final String s31326 = "31326";
+ public static final String s31327 = "31327";
+ public static final String s31328 = "31328";
+ public static final String s31329 = "31329";
+ public static final String s31330 = "31330";
+ public static final String s31331 = "31331";
+ public static final String s31332 = "31332";
+ public static final String s31333 = "31333";
+ public static final String s31334 = "31334";
+ public static final String s31335 = "31335";
+ public static final String s31336 = "31336";
+ public static final String s31337 = "31337";
+ public static final String s31338 = "31338";
+ public static final String s31339 = "31339";
+ public static final String s31340 = "31340";
+ public static final String s31341 = "31341";
+ public static final String s31342 = "31342";
+ public static final String s31343 = "31343";
+ public static final String s31344 = "31344";
+ public static final String s31345 = "31345";
+ public static final String s31346 = "31346";
+ public static final String s31347 = "31347";
+ public static final String s31348 = "31348";
+ public static final String s31349 = "31349";
+ public static final String s31350 = "31350";
+ public static final String s31351 = "31351";
+ public static final String s31352 = "31352";
+ public static final String s31353 = "31353";
+ public static final String s31354 = "31354";
+ public static final String s31355 = "31355";
+ public static final String s31356 = "31356";
+ public static final String s31357 = "31357";
+ public static final String s31358 = "31358";
+ public static final String s31359 = "31359";
+ public static final String s31360 = "31360";
+ public static final String s31361 = "31361";
+ public static final String s31362 = "31362";
+ public static final String s31363 = "31363";
+ public static final String s31364 = "31364";
+ public static final String s31365 = "31365";
+ public static final String s31366 = "31366";
+ public static final String s31367 = "31367";
+ public static final String s31368 = "31368";
+ public static final String s31369 = "31369";
+ public static final String s31370 = "31370";
+ public static final String s31371 = "31371";
+ public static final String s31372 = "31372";
+ public static final String s31373 = "31373";
+ public static final String s31374 = "31374";
+ public static final String s31375 = "31375";
+ public static final String s31376 = "31376";
+ public static final String s31377 = "31377";
+ public static final String s31378 = "31378";
+ public static final String s31379 = "31379";
+ public static final String s31380 = "31380";
+ public static final String s31381 = "31381";
+ public static final String s31382 = "31382";
+ public static final String s31383 = "31383";
+ public static final String s31384 = "31384";
+ public static final String s31385 = "31385";
+ public static final String s31386 = "31386";
+ public static final String s31387 = "31387";
+ public static final String s31388 = "31388";
+ public static final String s31389 = "31389";
+ public static final String s31390 = "31390";
+ public static final String s31391 = "31391";
+ public static final String s31392 = "31392";
+ public static final String s31393 = "31393";
+ public static final String s31394 = "31394";
+ public static final String s31395 = "31395";
+ public static final String s31396 = "31396";
+ public static final String s31397 = "31397";
+ public static final String s31398 = "31398";
+ public static final String s31399 = "31399";
+ public static final String s31400 = "31400";
+ public static final String s31401 = "31401";
+ public static final String s31402 = "31402";
+ public static final String s31403 = "31403";
+ public static final String s31404 = "31404";
+ public static final String s31405 = "31405";
+ public static final String s31406 = "31406";
+ public static final String s31407 = "31407";
+ public static final String s31408 = "31408";
+ public static final String s31409 = "31409";
+ public static final String s31410 = "31410";
+ public static final String s31411 = "31411";
+ public static final String s31412 = "31412";
+ public static final String s31413 = "31413";
+ public static final String s31414 = "31414";
+ public static final String s31415 = "31415";
+ public static final String s31416 = "31416";
+ public static final String s31417 = "31417";
+ public static final String s31418 = "31418";
+ public static final String s31419 = "31419";
+ public static final String s31420 = "31420";
+ public static final String s31421 = "31421";
+ public static final String s31422 = "31422";
+ public static final String s31423 = "31423";
+ public static final String s31424 = "31424";
+ public static final String s31425 = "31425";
+ public static final String s31426 = "31426";
+ public static final String s31427 = "31427";
+ public static final String s31428 = "31428";
+ public static final String s31429 = "31429";
+ public static final String s31430 = "31430";
+ public static final String s31431 = "31431";
+ public static final String s31432 = "31432";
+ public static final String s31433 = "31433";
+ public static final String s31434 = "31434";
+ public static final String s31435 = "31435";
+ public static final String s31436 = "31436";
+ public static final String s31437 = "31437";
+ public static final String s31438 = "31438";
+ public static final String s31439 = "31439";
+ public static final String s31440 = "31440";
+ public static final String s31441 = "31441";
+ public static final String s31442 = "31442";
+ public static final String s31443 = "31443";
+ public static final String s31444 = "31444";
+ public static final String s31445 = "31445";
+ public static final String s31446 = "31446";
+ public static final String s31447 = "31447";
+ public static final String s31448 = "31448";
+ public static final String s31449 = "31449";
+ public static final String s31450 = "31450";
+ public static final String s31451 = "31451";
+ public static final String s31452 = "31452";
+ public static final String s31453 = "31453";
+ public static final String s31454 = "31454";
+ public static final String s31455 = "31455";
+ public static final String s31456 = "31456";
+ public static final String s31457 = "31457";
+ public static final String s31458 = "31458";
+ public static final String s31459 = "31459";
+ public static final String s31460 = "31460";
+ public static final String s31461 = "31461";
+ public static final String s31462 = "31462";
+ public static final String s31463 = "31463";
+ public static final String s31464 = "31464";
+ public static final String s31465 = "31465";
+ public static final String s31466 = "31466";
+ public static final String s31467 = "31467";
+ public static final String s31468 = "31468";
+ public static final String s31469 = "31469";
+ public static final String s31470 = "31470";
+ public static final String s31471 = "31471";
+ public static final String s31472 = "31472";
+ public static final String s31473 = "31473";
+ public static final String s31474 = "31474";
+ public static final String s31475 = "31475";
+ public static final String s31476 = "31476";
+ public static final String s31477 = "31477";
+ public static final String s31478 = "31478";
+ public static final String s31479 = "31479";
+ public static final String s31480 = "31480";
+ public static final String s31481 = "31481";
+ public static final String s31482 = "31482";
+ public static final String s31483 = "31483";
+ public static final String s31484 = "31484";
+ public static final String s31485 = "31485";
+ public static final String s31486 = "31486";
+ public static final String s31487 = "31487";
+ public static final String s31488 = "31488";
+ public static final String s31489 = "31489";
+ public static final String s31490 = "31490";
+ public static final String s31491 = "31491";
+ public static final String s31492 = "31492";
+ public static final String s31493 = "31493";
+ public static final String s31494 = "31494";
+ public static final String s31495 = "31495";
+ public static final String s31496 = "31496";
+ public static final String s31497 = "31497";
+ public static final String s31498 = "31498";
+ public static final String s31499 = "31499";
+ public static final String s31500 = "31500";
+ public static final String s31501 = "31501";
+ public static final String s31502 = "31502";
+ public static final String s31503 = "31503";
+ public static final String s31504 = "31504";
+ public static final String s31505 = "31505";
+ public static final String s31506 = "31506";
+ public static final String s31507 = "31507";
+ public static final String s31508 = "31508";
+ public static final String s31509 = "31509";
+ public static final String s31510 = "31510";
+ public static final String s31511 = "31511";
+ public static final String s31512 = "31512";
+ public static final String s31513 = "31513";
+ public static final String s31514 = "31514";
+ public static final String s31515 = "31515";
+ public static final String s31516 = "31516";
+ public static final String s31517 = "31517";
+ public static final String s31518 = "31518";
+ public static final String s31519 = "31519";
+ public static final String s31520 = "31520";
+ public static final String s31521 = "31521";
+ public static final String s31522 = "31522";
+ public static final String s31523 = "31523";
+ public static final String s31524 = "31524";
+ public static final String s31525 = "31525";
+ public static final String s31526 = "31526";
+ public static final String s31527 = "31527";
+ public static final String s31528 = "31528";
+ public static final String s31529 = "31529";
+ public static final String s31530 = "31530";
+ public static final String s31531 = "31531";
+ public static final String s31532 = "31532";
+ public static final String s31533 = "31533";
+ public static final String s31534 = "31534";
+ public static final String s31535 = "31535";
+ public static final String s31536 = "31536";
+ public static final String s31537 = "31537";
+ public static final String s31538 = "31538";
+ public static final String s31539 = "31539";
+ public static final String s31540 = "31540";
+ public static final String s31541 = "31541";
+ public static final String s31542 = "31542";
+ public static final String s31543 = "31543";
+ public static final String s31544 = "31544";
+ public static final String s31545 = "31545";
+ public static final String s31546 = "31546";
+ public static final String s31547 = "31547";
+ public static final String s31548 = "31548";
+ public static final String s31549 = "31549";
+ public static final String s31550 = "31550";
+ public static final String s31551 = "31551";
+ public static final String s31552 = "31552";
+ public static final String s31553 = "31553";
+ public static final String s31554 = "31554";
+ public static final String s31555 = "31555";
+ public static final String s31556 = "31556";
+ public static final String s31557 = "31557";
+ public static final String s31558 = "31558";
+ public static final String s31559 = "31559";
+ public static final String s31560 = "31560";
+ public static final String s31561 = "31561";
+ public static final String s31562 = "31562";
+ public static final String s31563 = "31563";
+ public static final String s31564 = "31564";
+ public static final String s31565 = "31565";
+ public static final String s31566 = "31566";
+ public static final String s31567 = "31567";
+ public static final String s31568 = "31568";
+ public static final String s31569 = "31569";
+ public static final String s31570 = "31570";
+ public static final String s31571 = "31571";
+ public static final String s31572 = "31572";
+ public static final String s31573 = "31573";
+ public static final String s31574 = "31574";
+ public static final String s31575 = "31575";
+ public static final String s31576 = "31576";
+ public static final String s31577 = "31577";
+ public static final String s31578 = "31578";
+ public static final String s31579 = "31579";
+ public static final String s31580 = "31580";
+ public static final String s31581 = "31581";
+ public static final String s31582 = "31582";
+ public static final String s31583 = "31583";
+ public static final String s31584 = "31584";
+ public static final String s31585 = "31585";
+ public static final String s31586 = "31586";
+ public static final String s31587 = "31587";
+ public static final String s31588 = "31588";
+ public static final String s31589 = "31589";
+ public static final String s31590 = "31590";
+ public static final String s31591 = "31591";
+ public static final String s31592 = "31592";
+ public static final String s31593 = "31593";
+ public static final String s31594 = "31594";
+ public static final String s31595 = "31595";
+ public static final String s31596 = "31596";
+ public static final String s31597 = "31597";
+ public static final String s31598 = "31598";
+ public static final String s31599 = "31599";
+ public static final String s31600 = "31600";
+ public static final String s31601 = "31601";
+ public static final String s31602 = "31602";
+ public static final String s31603 = "31603";
+ public static final String s31604 = "31604";
+ public static final String s31605 = "31605";
+ public static final String s31606 = "31606";
+ public static final String s31607 = "31607";
+ public static final String s31608 = "31608";
+ public static final String s31609 = "31609";
+ public static final String s31610 = "31610";
+ public static final String s31611 = "31611";
+ public static final String s31612 = "31612";
+ public static final String s31613 = "31613";
+ public static final String s31614 = "31614";
+ public static final String s31615 = "31615";
+ public static final String s31616 = "31616";
+ public static final String s31617 = "31617";
+ public static final String s31618 = "31618";
+ public static final String s31619 = "31619";
+ public static final String s31620 = "31620";
+ public static final String s31621 = "31621";
+ public static final String s31622 = "31622";
+ public static final String s31623 = "31623";
+ public static final String s31624 = "31624";
+ public static final String s31625 = "31625";
+ public static final String s31626 = "31626";
+ public static final String s31627 = "31627";
+ public static final String s31628 = "31628";
+ public static final String s31629 = "31629";
+ public static final String s31630 = "31630";
+ public static final String s31631 = "31631";
+ public static final String s31632 = "31632";
+ public static final String s31633 = "31633";
+ public static final String s31634 = "31634";
+ public static final String s31635 = "31635";
+ public static final String s31636 = "31636";
+ public static final String s31637 = "31637";
+ public static final String s31638 = "31638";
+ public static final String s31639 = "31639";
+ public static final String s31640 = "31640";
+ public static final String s31641 = "31641";
+ public static final String s31642 = "31642";
+ public static final String s31643 = "31643";
+ public static final String s31644 = "31644";
+ public static final String s31645 = "31645";
+ public static final String s31646 = "31646";
+ public static final String s31647 = "31647";
+ public static final String s31648 = "31648";
+ public static final String s31649 = "31649";
+ public static final String s31650 = "31650";
+ public static final String s31651 = "31651";
+ public static final String s31652 = "31652";
+ public static final String s31653 = "31653";
+ public static final String s31654 = "31654";
+ public static final String s31655 = "31655";
+ public static final String s31656 = "31656";
+ public static final String s31657 = "31657";
+ public static final String s31658 = "31658";
+ public static final String s31659 = "31659";
+ public static final String s31660 = "31660";
+ public static final String s31661 = "31661";
+ public static final String s31662 = "31662";
+ public static final String s31663 = "31663";
+ public static final String s31664 = "31664";
+ public static final String s31665 = "31665";
+ public static final String s31666 = "31666";
+ public static final String s31667 = "31667";
+ public static final String s31668 = "31668";
+ public static final String s31669 = "31669";
+ public static final String s31670 = "31670";
+ public static final String s31671 = "31671";
+ public static final String s31672 = "31672";
+ public static final String s31673 = "31673";
+ public static final String s31674 = "31674";
+ public static final String s31675 = "31675";
+ public static final String s31676 = "31676";
+ public static final String s31677 = "31677";
+ public static final String s31678 = "31678";
+ public static final String s31679 = "31679";
+ public static final String s31680 = "31680";
+ public static final String s31681 = "31681";
+ public static final String s31682 = "31682";
+ public static final String s31683 = "31683";
+ public static final String s31684 = "31684";
+ public static final String s31685 = "31685";
+ public static final String s31686 = "31686";
+ public static final String s31687 = "31687";
+ public static final String s31688 = "31688";
+ public static final String s31689 = "31689";
+ public static final String s31690 = "31690";
+ public static final String s31691 = "31691";
+ public static final String s31692 = "31692";
+ public static final String s31693 = "31693";
+ public static final String s31694 = "31694";
+ public static final String s31695 = "31695";
+ public static final String s31696 = "31696";
+ public static final String s31697 = "31697";
+ public static final String s31698 = "31698";
+ public static final String s31699 = "31699";
+ public static final String s31700 = "31700";
+ public static final String s31701 = "31701";
+ public static final String s31702 = "31702";
+ public static final String s31703 = "31703";
+ public static final String s31704 = "31704";
+ public static final String s31705 = "31705";
+ public static final String s31706 = "31706";
+ public static final String s31707 = "31707";
+ public static final String s31708 = "31708";
+ public static final String s31709 = "31709";
+ public static final String s31710 = "31710";
+ public static final String s31711 = "31711";
+ public static final String s31712 = "31712";
+ public static final String s31713 = "31713";
+ public static final String s31714 = "31714";
+ public static final String s31715 = "31715";
+ public static final String s31716 = "31716";
+ public static final String s31717 = "31717";
+ public static final String s31718 = "31718";
+ public static final String s31719 = "31719";
+ public static final String s31720 = "31720";
+ public static final String s31721 = "31721";
+ public static final String s31722 = "31722";
+ public static final String s31723 = "31723";
+ public static final String s31724 = "31724";
+ public static final String s31725 = "31725";
+ public static final String s31726 = "31726";
+ public static final String s31727 = "31727";
+ public static final String s31728 = "31728";
+ public static final String s31729 = "31729";
+ public static final String s31730 = "31730";
+ public static final String s31731 = "31731";
+ public static final String s31732 = "31732";
+ public static final String s31733 = "31733";
+ public static final String s31734 = "31734";
+ public static final String s31735 = "31735";
+ public static final String s31736 = "31736";
+ public static final String s31737 = "31737";
+ public static final String s31738 = "31738";
+ public static final String s31739 = "31739";
+ public static final String s31740 = "31740";
+ public static final String s31741 = "31741";
+ public static final String s31742 = "31742";
+ public static final String s31743 = "31743";
+ public static final String s31744 = "31744";
+ public static final String s31745 = "31745";
+ public static final String s31746 = "31746";
+ public static final String s31747 = "31747";
+ public static final String s31748 = "31748";
+ public static final String s31749 = "31749";
+ public static final String s31750 = "31750";
+ public static final String s31751 = "31751";
+ public static final String s31752 = "31752";
+ public static final String s31753 = "31753";
+ public static final String s31754 = "31754";
+ public static final String s31755 = "31755";
+ public static final String s31756 = "31756";
+ public static final String s31757 = "31757";
+ public static final String s31758 = "31758";
+ public static final String s31759 = "31759";
+ public static final String s31760 = "31760";
+ public static final String s31761 = "31761";
+ public static final String s31762 = "31762";
+ public static final String s31763 = "31763";
+ public static final String s31764 = "31764";
+ public static final String s31765 = "31765";
+ public static final String s31766 = "31766";
+ public static final String s31767 = "31767";
+ public static final String s31768 = "31768";
+ public static final String s31769 = "31769";
+ public static final String s31770 = "31770";
+ public static final String s31771 = "31771";
+ public static final String s31772 = "31772";
+ public static final String s31773 = "31773";
+ public static final String s31774 = "31774";
+ public static final String s31775 = "31775";
+ public static final String s31776 = "31776";
+ public static final String s31777 = "31777";
+ public static final String s31778 = "31778";
+ public static final String s31779 = "31779";
+ public static final String s31780 = "31780";
+ public static final String s31781 = "31781";
+ public static final String s31782 = "31782";
+ public static final String s31783 = "31783";
+ public static final String s31784 = "31784";
+ public static final String s31785 = "31785";
+ public static final String s31786 = "31786";
+ public static final String s31787 = "31787";
+ public static final String s31788 = "31788";
+ public static final String s31789 = "31789";
+ public static final String s31790 = "31790";
+ public static final String s31791 = "31791";
+ public static final String s31792 = "31792";
+ public static final String s31793 = "31793";
+ public static final String s31794 = "31794";
+ public static final String s31795 = "31795";
+ public static final String s31796 = "31796";
+ public static final String s31797 = "31797";
+ public static final String s31798 = "31798";
+ public static final String s31799 = "31799";
+ public static final String s31800 = "31800";
+ public static final String s31801 = "31801";
+ public static final String s31802 = "31802";
+ public static final String s31803 = "31803";
+ public static final String s31804 = "31804";
+ public static final String s31805 = "31805";
+ public static final String s31806 = "31806";
+ public static final String s31807 = "31807";
+ public static final String s31808 = "31808";
+ public static final String s31809 = "31809";
+ public static final String s31810 = "31810";
+ public static final String s31811 = "31811";
+ public static final String s31812 = "31812";
+ public static final String s31813 = "31813";
+ public static final String s31814 = "31814";
+ public static final String s31815 = "31815";
+ public static final String s31816 = "31816";
+ public static final String s31817 = "31817";
+ public static final String s31818 = "31818";
+ public static final String s31819 = "31819";
+ public static final String s31820 = "31820";
+ public static final String s31821 = "31821";
+ public static final String s31822 = "31822";
+ public static final String s31823 = "31823";
+ public static final String s31824 = "31824";
+ public static final String s31825 = "31825";
+ public static final String s31826 = "31826";
+ public static final String s31827 = "31827";
+ public static final String s31828 = "31828";
+ public static final String s31829 = "31829";
+ public static final String s31830 = "31830";
+ public static final String s31831 = "31831";
+ public static final String s31832 = "31832";
+ public static final String s31833 = "31833";
+ public static final String s31834 = "31834";
+ public static final String s31835 = "31835";
+ public static final String s31836 = "31836";
+ public static final String s31837 = "31837";
+ public static final String s31838 = "31838";
+ public static final String s31839 = "31839";
+ public static final String s31840 = "31840";
+ public static final String s31841 = "31841";
+ public static final String s31842 = "31842";
+ public static final String s31843 = "31843";
+ public static final String s31844 = "31844";
+ public static final String s31845 = "31845";
+ public static final String s31846 = "31846";
+ public static final String s31847 = "31847";
+ public static final String s31848 = "31848";
+ public static final String s31849 = "31849";
+ public static final String s31850 = "31850";
+ public static final String s31851 = "31851";
+ public static final String s31852 = "31852";
+ public static final String s31853 = "31853";
+ public static final String s31854 = "31854";
+ public static final String s31855 = "31855";
+ public static final String s31856 = "31856";
+ public static final String s31857 = "31857";
+ public static final String s31858 = "31858";
+ public static final String s31859 = "31859";
+ public static final String s31860 = "31860";
+ public static final String s31861 = "31861";
+ public static final String s31862 = "31862";
+ public static final String s31863 = "31863";
+ public static final String s31864 = "31864";
+ public static final String s31865 = "31865";
+ public static final String s31866 = "31866";
+ public static final String s31867 = "31867";
+ public static final String s31868 = "31868";
+ public static final String s31869 = "31869";
+ public static final String s31870 = "31870";
+ public static final String s31871 = "31871";
+ public static final String s31872 = "31872";
+ public static final String s31873 = "31873";
+ public static final String s31874 = "31874";
+ public static final String s31875 = "31875";
+ public static final String s31876 = "31876";
+ public static final String s31877 = "31877";
+ public static final String s31878 = "31878";
+ public static final String s31879 = "31879";
+ public static final String s31880 = "31880";
+ public static final String s31881 = "31881";
+ public static final String s31882 = "31882";
+ public static final String s31883 = "31883";
+ public static final String s31884 = "31884";
+ public static final String s31885 = "31885";
+ public static final String s31886 = "31886";
+ public static final String s31887 = "31887";
+ public static final String s31888 = "31888";
+ public static final String s31889 = "31889";
+ public static final String s31890 = "31890";
+ public static final String s31891 = "31891";
+ public static final String s31892 = "31892";
+ public static final String s31893 = "31893";
+ public static final String s31894 = "31894";
+ public static final String s31895 = "31895";
+ public static final String s31896 = "31896";
+ public static final String s31897 = "31897";
+ public static final String s31898 = "31898";
+ public static final String s31899 = "31899";
+ public static final String s31900 = "31900";
+ public static final String s31901 = "31901";
+ public static final String s31902 = "31902";
+ public static final String s31903 = "31903";
+ public static final String s31904 = "31904";
+ public static final String s31905 = "31905";
+ public static final String s31906 = "31906";
+ public static final String s31907 = "31907";
+ public static final String s31908 = "31908";
+ public static final String s31909 = "31909";
+ public static final String s31910 = "31910";
+ public static final String s31911 = "31911";
+ public static final String s31912 = "31912";
+ public static final String s31913 = "31913";
+ public static final String s31914 = "31914";
+ public static final String s31915 = "31915";
+ public static final String s31916 = "31916";
+ public static final String s31917 = "31917";
+ public static final String s31918 = "31918";
+ public static final String s31919 = "31919";
+ public static final String s31920 = "31920";
+ public static final String s31921 = "31921";
+ public static final String s31922 = "31922";
+ public static final String s31923 = "31923";
+ public static final String s31924 = "31924";
+ public static final String s31925 = "31925";
+ public static final String s31926 = "31926";
+ public static final String s31927 = "31927";
+ public static final String s31928 = "31928";
+ public static final String s31929 = "31929";
+ public static final String s31930 = "31930";
+ public static final String s31931 = "31931";
+ public static final String s31932 = "31932";
+ public static final String s31933 = "31933";
+ public static final String s31934 = "31934";
+ public static final String s31935 = "31935";
+ public static final String s31936 = "31936";
+ public static final String s31937 = "31937";
+ public static final String s31938 = "31938";
+ public static final String s31939 = "31939";
+ public static final String s31940 = "31940";
+ public static final String s31941 = "31941";
+ public static final String s31942 = "31942";
+ public static final String s31943 = "31943";
+ public static final String s31944 = "31944";
+ public static final String s31945 = "31945";
+ public static final String s31946 = "31946";
+ public static final String s31947 = "31947";
+ public static final String s31948 = "31948";
+ public static final String s31949 = "31949";
+ public static final String s31950 = "31950";
+ public static final String s31951 = "31951";
+ public static final String s31952 = "31952";
+ public static final String s31953 = "31953";
+ public static final String s31954 = "31954";
+ public static final String s31955 = "31955";
+ public static final String s31956 = "31956";
+ public static final String s31957 = "31957";
+ public static final String s31958 = "31958";
+ public static final String s31959 = "31959";
+ public static final String s31960 = "31960";
+ public static final String s31961 = "31961";
+ public static final String s31962 = "31962";
+ public static final String s31963 = "31963";
+ public static final String s31964 = "31964";
+ public static final String s31965 = "31965";
+ public static final String s31966 = "31966";
+ public static final String s31967 = "31967";
+ public static final String s31968 = "31968";
+ public static final String s31969 = "31969";
+ public static final String s31970 = "31970";
+ public static final String s31971 = "31971";
+ public static final String s31972 = "31972";
+ public static final String s31973 = "31973";
+ public static final String s31974 = "31974";
+ public static final String s31975 = "31975";
+ public static final String s31976 = "31976";
+ public static final String s31977 = "31977";
+ public static final String s31978 = "31978";
+ public static final String s31979 = "31979";
+ public static final String s31980 = "31980";
+ public static final String s31981 = "31981";
+ public static final String s31982 = "31982";
+ public static final String s31983 = "31983";
+ public static final String s31984 = "31984";
+ public static final String s31985 = "31985";
+ public static final String s31986 = "31986";
+ public static final String s31987 = "31987";
+ public static final String s31988 = "31988";
+ public static final String s31989 = "31989";
+ public static final String s31990 = "31990";
+ public static final String s31991 = "31991";
+ public static final String s31992 = "31992";
+ public static final String s31993 = "31993";
+ public static final String s31994 = "31994";
+ public static final String s31995 = "31995";
+ public static final String s31996 = "31996";
+ public static final String s31997 = "31997";
+ public static final String s31998 = "31998";
+ public static final String s31999 = "31999";
+ public static final String s32000 = "32000";
+ public static final String s32001 = "32001";
+ public static final String s32002 = "32002";
+ public static final String s32003 = "32003";
+ public static final String s32004 = "32004";
+ public static final String s32005 = "32005";
+ public static final String s32006 = "32006";
+ public static final String s32007 = "32007";
+ public static final String s32008 = "32008";
+ public static final String s32009 = "32009";
+ public static final String s32010 = "32010";
+ public static final String s32011 = "32011";
+ public static final String s32012 = "32012";
+ public static final String s32013 = "32013";
+ public static final String s32014 = "32014";
+ public static final String s32015 = "32015";
+ public static final String s32016 = "32016";
+ public static final String s32017 = "32017";
+ public static final String s32018 = "32018";
+ public static final String s32019 = "32019";
+ public static final String s32020 = "32020";
+ public static final String s32021 = "32021";
+ public static final String s32022 = "32022";
+ public static final String s32023 = "32023";
+ public static final String s32024 = "32024";
+ public static final String s32025 = "32025";
+ public static final String s32026 = "32026";
+ public static final String s32027 = "32027";
+ public static final String s32028 = "32028";
+ public static final String s32029 = "32029";
+ public static final String s32030 = "32030";
+ public static final String s32031 = "32031";
+ public static final String s32032 = "32032";
+ public static final String s32033 = "32033";
+ public static final String s32034 = "32034";
+ public static final String s32035 = "32035";
+ public static final String s32036 = "32036";
+ public static final String s32037 = "32037";
+ public static final String s32038 = "32038";
+ public static final String s32039 = "32039";
+ public static final String s32040 = "32040";
+ public static final String s32041 = "32041";
+ public static final String s32042 = "32042";
+ public static final String s32043 = "32043";
+ public static final String s32044 = "32044";
+ public static final String s32045 = "32045";
+ public static final String s32046 = "32046";
+ public static final String s32047 = "32047";
+ public static final String s32048 = "32048";
+ public static final String s32049 = "32049";
+ public static final String s32050 = "32050";
+ public static final String s32051 = "32051";
+ public static final String s32052 = "32052";
+ public static final String s32053 = "32053";
+ public static final String s32054 = "32054";
+ public static final String s32055 = "32055";
+ public static final String s32056 = "32056";
+ public static final String s32057 = "32057";
+ public static final String s32058 = "32058";
+ public static final String s32059 = "32059";
+ public static final String s32060 = "32060";
+ public static final String s32061 = "32061";
+ public static final String s32062 = "32062";
+ public static final String s32063 = "32063";
+ public static final String s32064 = "32064";
+ public static final String s32065 = "32065";
+ public static final String s32066 = "32066";
+ public static final String s32067 = "32067";
+ public static final String s32068 = "32068";
+ public static final String s32069 = "32069";
+ public static final String s32070 = "32070";
+ public static final String s32071 = "32071";
+ public static final String s32072 = "32072";
+ public static final String s32073 = "32073";
+ public static final String s32074 = "32074";
+ public static final String s32075 = "32075";
+ public static final String s32076 = "32076";
+ public static final String s32077 = "32077";
+ public static final String s32078 = "32078";
+ public static final String s32079 = "32079";
+ public static final String s32080 = "32080";
+ public static final String s32081 = "32081";
+ public static final String s32082 = "32082";
+ public static final String s32083 = "32083";
+ public static final String s32084 = "32084";
+ public static final String s32085 = "32085";
+ public static final String s32086 = "32086";
+ public static final String s32087 = "32087";
+ public static final String s32088 = "32088";
+ public static final String s32089 = "32089";
+ public static final String s32090 = "32090";
+ public static final String s32091 = "32091";
+ public static final String s32092 = "32092";
+ public static final String s32093 = "32093";
+ public static final String s32094 = "32094";
+ public static final String s32095 = "32095";
+ public static final String s32096 = "32096";
+ public static final String s32097 = "32097";
+ public static final String s32098 = "32098";
+ public static final String s32099 = "32099";
+ public static final String s32100 = "32100";
+ public static final String s32101 = "32101";
+ public static final String s32102 = "32102";
+ public static final String s32103 = "32103";
+ public static final String s32104 = "32104";
+ public static final String s32105 = "32105";
+ public static final String s32106 = "32106";
+ public static final String s32107 = "32107";
+ public static final String s32108 = "32108";
+ public static final String s32109 = "32109";
+ public static final String s32110 = "32110";
+ public static final String s32111 = "32111";
+ public static final String s32112 = "32112";
+ public static final String s32113 = "32113";
+ public static final String s32114 = "32114";
+ public static final String s32115 = "32115";
+ public static final String s32116 = "32116";
+ public static final String s32117 = "32117";
+ public static final String s32118 = "32118";
+ public static final String s32119 = "32119";
+ public static final String s32120 = "32120";
+ public static final String s32121 = "32121";
+ public static final String s32122 = "32122";
+ public static final String s32123 = "32123";
+ public static final String s32124 = "32124";
+ public static final String s32125 = "32125";
+ public static final String s32126 = "32126";
+ public static final String s32127 = "32127";
+ public static final String s32128 = "32128";
+ public static final String s32129 = "32129";
+ public static final String s32130 = "32130";
+ public static final String s32131 = "32131";
+ public static final String s32132 = "32132";
+ public static final String s32133 = "32133";
+ public static final String s32134 = "32134";
+ public static final String s32135 = "32135";
+ public static final String s32136 = "32136";
+ public static final String s32137 = "32137";
+ public static final String s32138 = "32138";
+ public static final String s32139 = "32139";
+ public static final String s32140 = "32140";
+ public static final String s32141 = "32141";
+ public static final String s32142 = "32142";
+ public static final String s32143 = "32143";
+ public static final String s32144 = "32144";
+ public static final String s32145 = "32145";
+ public static final String s32146 = "32146";
+ public static final String s32147 = "32147";
+ public static final String s32148 = "32148";
+ public static final String s32149 = "32149";
+ public static final String s32150 = "32150";
+ public static final String s32151 = "32151";
+ public static final String s32152 = "32152";
+ public static final String s32153 = "32153";
+ public static final String s32154 = "32154";
+ public static final String s32155 = "32155";
+ public static final String s32156 = "32156";
+ public static final String s32157 = "32157";
+ public static final String s32158 = "32158";
+ public static final String s32159 = "32159";
+ public static final String s32160 = "32160";
+ public static final String s32161 = "32161";
+ public static final String s32162 = "32162";
+ public static final String s32163 = "32163";
+ public static final String s32164 = "32164";
+ public static final String s32165 = "32165";
+ public static final String s32166 = "32166";
+ public static final String s32167 = "32167";
+ public static final String s32168 = "32168";
+ public static final String s32169 = "32169";
+ public static final String s32170 = "32170";
+ public static final String s32171 = "32171";
+ public static final String s32172 = "32172";
+ public static final String s32173 = "32173";
+ public static final String s32174 = "32174";
+ public static final String s32175 = "32175";
+ public static final String s32176 = "32176";
+ public static final String s32177 = "32177";
+ public static final String s32178 = "32178";
+ public static final String s32179 = "32179";
+ public static final String s32180 = "32180";
+ public static final String s32181 = "32181";
+ public static final String s32182 = "32182";
+ public static final String s32183 = "32183";
+ public static final String s32184 = "32184";
+ public static final String s32185 = "32185";
+ public static final String s32186 = "32186";
+ public static final String s32187 = "32187";
+ public static final String s32188 = "32188";
+ public static final String s32189 = "32189";
+ public static final String s32190 = "32190";
+ public static final String s32191 = "32191";
+ public static final String s32192 = "32192";
+ public static final String s32193 = "32193";
+ public static final String s32194 = "32194";
+ public static final String s32195 = "32195";
+ public static final String s32196 = "32196";
+ public static final String s32197 = "32197";
+ public static final String s32198 = "32198";
+ public static final String s32199 = "32199";
+ public static final String s32200 = "32200";
+ public static final String s32201 = "32201";
+ public static final String s32202 = "32202";
+ public static final String s32203 = "32203";
+ public static final String s32204 = "32204";
+ public static final String s32205 = "32205";
+ public static final String s32206 = "32206";
+ public static final String s32207 = "32207";
+ public static final String s32208 = "32208";
+ public static final String s32209 = "32209";
+ public static final String s32210 = "32210";
+ public static final String s32211 = "32211";
+ public static final String s32212 = "32212";
+ public static final String s32213 = "32213";
+ public static final String s32214 = "32214";
+ public static final String s32215 = "32215";
+ public static final String s32216 = "32216";
+ public static final String s32217 = "32217";
+ public static final String s32218 = "32218";
+ public static final String s32219 = "32219";
+ public static final String s32220 = "32220";
+ public static final String s32221 = "32221";
+ public static final String s32222 = "32222";
+ public static final String s32223 = "32223";
+ public static final String s32224 = "32224";
+ public static final String s32225 = "32225";
+ public static final String s32226 = "32226";
+ public static final String s32227 = "32227";
+ public static final String s32228 = "32228";
+ public static final String s32229 = "32229";
+ public static final String s32230 = "32230";
+ public static final String s32231 = "32231";
+ public static final String s32232 = "32232";
+ public static final String s32233 = "32233";
+ public static final String s32234 = "32234";
+ public static final String s32235 = "32235";
+ public static final String s32236 = "32236";
+ public static final String s32237 = "32237";
+ public static final String s32238 = "32238";
+ public static final String s32239 = "32239";
+ public static final String s32240 = "32240";
+ public static final String s32241 = "32241";
+ public static final String s32242 = "32242";
+ public static final String s32243 = "32243";
+ public static final String s32244 = "32244";
+ public static final String s32245 = "32245";
+ public static final String s32246 = "32246";
+ public static final String s32247 = "32247";
+ public static final String s32248 = "32248";
+ public static final String s32249 = "32249";
+ public static final String s32250 = "32250";
+ public static final String s32251 = "32251";
+ public static final String s32252 = "32252";
+ public static final String s32253 = "32253";
+ public static final String s32254 = "32254";
+ public static final String s32255 = "32255";
+ public static final String s32256 = "32256";
+ public static final String s32257 = "32257";
+ public static final String s32258 = "32258";
+ public static final String s32259 = "32259";
+ public static final String s32260 = "32260";
+ public static final String s32261 = "32261";
+ public static final String s32262 = "32262";
+ public static final String s32263 = "32263";
+ public static final String s32264 = "32264";
+ public static final String s32265 = "32265";
+ public static final String s32266 = "32266";
+ public static final String s32267 = "32267";
+ public static final String s32268 = "32268";
+ public static final String s32269 = "32269";
+ public static final String s32270 = "32270";
+ public static final String s32271 = "32271";
+ public static final String s32272 = "32272";
+ public static final String s32273 = "32273";
+ public static final String s32274 = "32274";
+ public static final String s32275 = "32275";
+ public static final String s32276 = "32276";
+ public static final String s32277 = "32277";
+ public static final String s32278 = "32278";
+ public static final String s32279 = "32279";
+ public static final String s32280 = "32280";
+ public static final String s32281 = "32281";
+ public static final String s32282 = "32282";
+ public static final String s32283 = "32283";
+ public static final String s32284 = "32284";
+ public static final String s32285 = "32285";
+ public static final String s32286 = "32286";
+ public static final String s32287 = "32287";
+ public static final String s32288 = "32288";
+ public static final String s32289 = "32289";
+ public static final String s32290 = "32290";
+ public static final String s32291 = "32291";
+ public static final String s32292 = "32292";
+ public static final String s32293 = "32293";
+ public static final String s32294 = "32294";
+ public static final String s32295 = "32295";
+ public static final String s32296 = "32296";
+ public static final String s32297 = "32297";
+ public static final String s32298 = "32298";
+ public static final String s32299 = "32299";
+ public static final String s32300 = "32300";
+ public static final String s32301 = "32301";
+ public static final String s32302 = "32302";
+ public static final String s32303 = "32303";
+ public static final String s32304 = "32304";
+ public static final String s32305 = "32305";
+ public static final String s32306 = "32306";
+ public static final String s32307 = "32307";
+ public static final String s32308 = "32308";
+ public static final String s32309 = "32309";
+ public static final String s32310 = "32310";
+ public static final String s32311 = "32311";
+ public static final String s32312 = "32312";
+ public static final String s32313 = "32313";
+ public static final String s32314 = "32314";
+ public static final String s32315 = "32315";
+ public static final String s32316 = "32316";
+ public static final String s32317 = "32317";
+ public static final String s32318 = "32318";
+ public static final String s32319 = "32319";
+ public static final String s32320 = "32320";
+ public static final String s32321 = "32321";
+ public static final String s32322 = "32322";
+ public static final String s32323 = "32323";
+ public static final String s32324 = "32324";
+ public static final String s32325 = "32325";
+ public static final String s32326 = "32326";
+ public static final String s32327 = "32327";
+ public static final String s32328 = "32328";
+ public static final String s32329 = "32329";
+ public static final String s32330 = "32330";
+ public static final String s32331 = "32331";
+ public static final String s32332 = "32332";
+ public static final String s32333 = "32333";
+ public static final String s32334 = "32334";
+ public static final String s32335 = "32335";
+ public static final String s32336 = "32336";
+ public static final String s32337 = "32337";
+ public static final String s32338 = "32338";
+ public static final String s32339 = "32339";
+ public static final String s32340 = "32340";
+ public static final String s32341 = "32341";
+ public static final String s32342 = "32342";
+ public static final String s32343 = "32343";
+ public static final String s32344 = "32344";
+ public static final String s32345 = "32345";
+ public static final String s32346 = "32346";
+ public static final String s32347 = "32347";
+ public static final String s32348 = "32348";
+ public static final String s32349 = "32349";
+ public static final String s32350 = "32350";
+ public static final String s32351 = "32351";
+ public static final String s32352 = "32352";
+ public static final String s32353 = "32353";
+ public static final String s32354 = "32354";
+ public static final String s32355 = "32355";
+ public static final String s32356 = "32356";
+ public static final String s32357 = "32357";
+ public static final String s32358 = "32358";
+ public static final String s32359 = "32359";
+ public static final String s32360 = "32360";
+ public static final String s32361 = "32361";
+ public static final String s32362 = "32362";
+ public static final String s32363 = "32363";
+ public static final String s32364 = "32364";
+ public static final String s32365 = "32365";
+ public static final String s32366 = "32366";
+ public static final String s32367 = "32367";
+ public static final String s32368 = "32368";
+ public static final String s32369 = "32369";
+ public static final String s32370 = "32370";
+ public static final String s32371 = "32371";
+ public static final String s32372 = "32372";
+ public static final String s32373 = "32373";
+ public static final String s32374 = "32374";
+ public static final String s32375 = "32375";
+ public static final String s32376 = "32376";
+ public static final String s32377 = "32377";
+ public static final String s32378 = "32378";
+ public static final String s32379 = "32379";
+ public static final String s32380 = "32380";
+ public static final String s32381 = "32381";
+ public static final String s32382 = "32382";
+ public static final String s32383 = "32383";
+ public static final String s32384 = "32384";
+ public static final String s32385 = "32385";
+ public static final String s32386 = "32386";
+ public static final String s32387 = "32387";
+ public static final String s32388 = "32388";
+ public static final String s32389 = "32389";
+ public static final String s32390 = "32390";
+ public static final String s32391 = "32391";
+ public static final String s32392 = "32392";
+ public static final String s32393 = "32393";
+ public static final String s32394 = "32394";
+ public static final String s32395 = "32395";
+ public static final String s32396 = "32396";
+ public static final String s32397 = "32397";
+ public static final String s32398 = "32398";
+ public static final String s32399 = "32399";
+ public static final String s32400 = "32400";
+ public static final String s32401 = "32401";
+ public static final String s32402 = "32402";
+ public static final String s32403 = "32403";
+ public static final String s32404 = "32404";
+ public static final String s32405 = "32405";
+ public static final String s32406 = "32406";
+ public static final String s32407 = "32407";
+ public static final String s32408 = "32408";
+ public static final String s32409 = "32409";
+ public static final String s32410 = "32410";
+ public static final String s32411 = "32411";
+ public static final String s32412 = "32412";
+ public static final String s32413 = "32413";
+ public static final String s32414 = "32414";
+ public static final String s32415 = "32415";
+ public static final String s32416 = "32416";
+ public static final String s32417 = "32417";
+ public static final String s32418 = "32418";
+ public static final String s32419 = "32419";
+ public static final String s32420 = "32420";
+ public static final String s32421 = "32421";
+ public static final String s32422 = "32422";
+ public static final String s32423 = "32423";
+ public static final String s32424 = "32424";
+ public static final String s32425 = "32425";
+ public static final String s32426 = "32426";
+ public static final String s32427 = "32427";
+ public static final String s32428 = "32428";
+ public static final String s32429 = "32429";
+ public static final String s32430 = "32430";
+ public static final String s32431 = "32431";
+ public static final String s32432 = "32432";
+ public static final String s32433 = "32433";
+ public static final String s32434 = "32434";
+ public static final String s32435 = "32435";
+ public static final String s32436 = "32436";
+ public static final String s32437 = "32437";
+ public static final String s32438 = "32438";
+ public static final String s32439 = "32439";
+ public static final String s32440 = "32440";
+ public static final String s32441 = "32441";
+ public static final String s32442 = "32442";
+ public static final String s32443 = "32443";
+ public static final String s32444 = "32444";
+ public static final String s32445 = "32445";
+ public static final String s32446 = "32446";
+ public static final String s32447 = "32447";
+ public static final String s32448 = "32448";
+ public static final String s32449 = "32449";
+ public static final String s32450 = "32450";
+ public static final String s32451 = "32451";
+ public static final String s32452 = "32452";
+ public static final String s32453 = "32453";
+ public static final String s32454 = "32454";
+ public static final String s32455 = "32455";
+ public static final String s32456 = "32456";
+ public static final String s32457 = "32457";
+ public static final String s32458 = "32458";
+ public static final String s32459 = "32459";
+ public static final String s32460 = "32460";
+ public static final String s32461 = "32461";
+ public static final String s32462 = "32462";
+ public static final String s32463 = "32463";
+ public static final String s32464 = "32464";
+ public static final String s32465 = "32465";
+ public static final String s32466 = "32466";
+ public static final String s32467 = "32467";
+ public static final String s32468 = "32468";
+ public static final String s32469 = "32469";
+ public static final String s32470 = "32470";
+ public static final String s32471 = "32471";
+ public static final String s32472 = "32472";
+ public static final String s32473 = "32473";
+ public static final String s32474 = "32474";
+ public static final String s32475 = "32475";
+ public static final String s32476 = "32476";
+ public static final String s32477 = "32477";
+ public static final String s32478 = "32478";
+ public static final String s32479 = "32479";
+ public static final String s32480 = "32480";
+ public static final String s32481 = "32481";
+ public static final String s32482 = "32482";
+ public static final String s32483 = "32483";
+ public static final String s32484 = "32484";
+ public static final String s32485 = "32485";
+ public static final String s32486 = "32486";
+ public static final String s32487 = "32487";
+ public static final String s32488 = "32488";
+ public static final String s32489 = "32489";
+ public static final String s32490 = "32490";
+ public static final String s32491 = "32491";
+ public static final String s32492 = "32492";
+ public static final String s32493 = "32493";
+ public static final String s32494 = "32494";
+ public static final String s32495 = "32495";
+ public static final String s32496 = "32496";
+ public static final String s32497 = "32497";
+ public static final String s32498 = "32498";
+ public static final String s32499 = "32499";
+ public static final String s32500 = "32500";
+ public static final String s32501 = "32501";
+ public static final String s32502 = "32502";
+ public static final String s32503 = "32503";
+ public static final String s32504 = "32504";
+ public static final String s32505 = "32505";
+ public static final String s32506 = "32506";
+ public static final String s32507 = "32507";
+ public static final String s32508 = "32508";
+ public static final String s32509 = "32509";
+ public static final String s32510 = "32510";
+ public static final String s32511 = "32511";
+ public static final String s32512 = "32512";
+ public static final String s32513 = "32513";
+ public static final String s32514 = "32514";
+ public static final String s32515 = "32515";
+ public static final String s32516 = "32516";
+ public static final String s32517 = "32517";
+ public static final String s32518 = "32518";
+ public static final String s32519 = "32519";
+ public static final String s32520 = "32520";
+ public static final String s32521 = "32521";
+ public static final String s32522 = "32522";
+ public static final String s32523 = "32523";
+ public static final String s32524 = "32524";
+ public static final String s32525 = "32525";
+ public static final String s32526 = "32526";
+ public static final String s32527 = "32527";
+ public static final String s32528 = "32528";
+ public static final String s32529 = "32529";
+ public static final String s32530 = "32530";
+ public static final String s32531 = "32531";
+ public static final String s32532 = "32532";
+ public static final String s32533 = "32533";
+ public static final String s32534 = "32534";
+ public static final String s32535 = "32535";
+ public static final String s32536 = "32536";
+ public static final String s32537 = "32537";
+ public static final String s32538 = "32538";
+ public static final String s32539 = "32539";
+ public static final String s32540 = "32540";
+ public static final String s32541 = "32541";
+ public static final String s32542 = "32542";
+ public static final String s32543 = "32543";
+ public static final String s32544 = "32544";
+ public static final String s32545 = "32545";
+ public static final String s32546 = "32546";
+ public static final String s32547 = "32547";
+ public static final String s32548 = "32548";
+ public static final String s32549 = "32549";
+ public static final String s32550 = "32550";
+ public static final String s32551 = "32551";
+ public static final String s32552 = "32552";
+ public static final String s32553 = "32553";
+ public static final String s32554 = "32554";
+ public static final String s32555 = "32555";
+ public static final String s32556 = "32556";
+ public static final String s32557 = "32557";
+ public static final String s32558 = "32558";
+ public static final String s32559 = "32559";
+ public static final String s32560 = "32560";
+ public static final String s32561 = "32561";
+ public static final String s32562 = "32562";
+ public static final String s32563 = "32563";
+ public static final String s32564 = "32564";
+ public static final String s32565 = "32565";
+ public static final String s32566 = "32566";
+ public static final String s32567 = "32567";
+ public static final String s32568 = "32568";
+ public static final String s32569 = "32569";
+ public static final String s32570 = "32570";
+ public static final String s32571 = "32571";
+ public static final String s32572 = "32572";
+ public static final String s32573 = "32573";
+ public static final String s32574 = "32574";
+ public static final String s32575 = "32575";
+ public static final String s32576 = "32576";
+ public static final String s32577 = "32577";
+ public static final String s32578 = "32578";
+ public static final String s32579 = "32579";
+ public static final String s32580 = "32580";
+ public static final String s32581 = "32581";
+ public static final String s32582 = "32582";
+ public static final String s32583 = "32583";
+ public static final String s32584 = "32584";
+ public static final String s32585 = "32585";
+ public static final String s32586 = "32586";
+ public static final String s32587 = "32587";
+ public static final String s32588 = "32588";
+ public static final String s32589 = "32589";
+ public static final String s32590 = "32590";
+ public static final String s32591 = "32591";
+ public static final String s32592 = "32592";
+ public static final String s32593 = "32593";
+ public static final String s32594 = "32594";
+ public static final String s32595 = "32595";
+ public static final String s32596 = "32596";
+ public static final String s32597 = "32597";
+ public static final String s32598 = "32598";
+ public static final String s32599 = "32599";
+ public static final String s32600 = "32600";
+ public static final String s32601 = "32601";
+ public static final String s32602 = "32602";
+ public static final String s32603 = "32603";
+ public static final String s32604 = "32604";
+ public static final String s32605 = "32605";
+ public static final String s32606 = "32606";
+ public static final String s32607 = "32607";
+ public static final String s32608 = "32608";
+ public static final String s32609 = "32609";
+ public static final String s32610 = "32610";
+ public static final String s32611 = "32611";
+ public static final String s32612 = "32612";
+ public static final String s32613 = "32613";
+ public static final String s32614 = "32614";
+ public static final String s32615 = "32615";
+ public static final String s32616 = "32616";
+ public static final String s32617 = "32617";
+ public static final String s32618 = "32618";
+ public static final String s32619 = "32619";
+ public static final String s32620 = "32620";
+ public static final String s32621 = "32621";
+ public static final String s32622 = "32622";
+ public static final String s32623 = "32623";
+ public static final String s32624 = "32624";
+ public static final String s32625 = "32625";
+ public static final String s32626 = "32626";
+ public static final String s32627 = "32627";
+ public static final String s32628 = "32628";
+ public static final String s32629 = "32629";
+ public static final String s32630 = "32630";
+ public static final String s32631 = "32631";
+ public static final String s32632 = "32632";
+ public static final String s32633 = "32633";
+ public static final String s32634 = "32634";
+ public static final String s32635 = "32635";
+ public static final String s32636 = "32636";
+ public static final String s32637 = "32637";
+ public static final String s32638 = "32638";
+ public static final String s32639 = "32639";
+ public static final String s32640 = "32640";
+ public static final String s32641 = "32641";
+ public static final String s32642 = "32642";
+ public static final String s32643 = "32643";
+ public static final String s32644 = "32644";
+ public static final String s32645 = "32645";
+ public static final String s32646 = "32646";
+ public static final String s32647 = "32647";
+ public static final String s32648 = "32648";
+ public static final String s32649 = "32649";
+ public static final String s32650 = "32650";
+ public static final String s32651 = "32651";
+ public static final String s32652 = "32652";
+ public static final String s32653 = "32653";
+ public static final String s32654 = "32654";
+ public static final String s32655 = "32655";
+ public static final String s32656 = "32656";
+ public static final String s32657 = "32657";
+ public static final String s32658 = "32658";
+ public static final String s32659 = "32659";
+ public static final String s32660 = "32660";
+ public static final String s32661 = "32661";
+ public static final String s32662 = "32662";
+ public static final String s32663 = "32663";
+ public static final String s32664 = "32664";
+ public static final String s32665 = "32665";
+ public static final String s32666 = "32666";
+ public static final String s32667 = "32667";
+ public static final String s32668 = "32668";
+ public static final String s32669 = "32669";
+ public static final String s32670 = "32670";
+ public static final String s32671 = "32671";
+ public static final String s32672 = "32672";
+ public static final String s32673 = "32673";
+ public static final String s32674 = "32674";
+ public static final String s32675 = "32675";
+ public static final String s32676 = "32676";
+ public static final String s32677 = "32677";
+ public static final String s32678 = "32678";
+ public static final String s32679 = "32679";
+ public static final String s32680 = "32680";
+ public static final String s32681 = "32681";
+ public static final String s32682 = "32682";
+ public static final String s32683 = "32683";
+ public static final String s32684 = "32684";
+ public static final String s32685 = "32685";
+ public static final String s32686 = "32686";
+ public static final String s32687 = "32687";
+ public static final String s32688 = "32688";
+ public static final String s32689 = "32689";
+ public static final String s32690 = "32690";
+ public static final String s32691 = "32691";
+ public static final String s32692 = "32692";
+ public static final String s32693 = "32693";
+ public static final String s32694 = "32694";
+ public static final String s32695 = "32695";
+ public static final String s32696 = "32696";
+ public static final String s32697 = "32697";
+ public static final String s32698 = "32698";
+ public static final String s32699 = "32699";
+ public static final String s32700 = "32700";
+ public static final String s32701 = "32701";
+ public static final String s32702 = "32702";
+ public static final String s32703 = "32703";
+ public static final String s32704 = "32704";
+ public static final String s32705 = "32705";
+ public static final String s32706 = "32706";
+ public static final String s32707 = "32707";
+ public static final String s32708 = "32708";
+ public static final String s32709 = "32709";
+ public static final String s32710 = "32710";
+ public static final String s32711 = "32711";
+ public static final String s32712 = "32712";
+ public static final String s32713 = "32713";
+ public static final String s32714 = "32714";
+ public static final String s32715 = "32715";
+ public static final String s32716 = "32716";
+ public static final String s32717 = "32717";
+ public static final String s32718 = "32718";
+ public static final String s32719 = "32719";
+ public static final String s32720 = "32720";
+ public static final String s32721 = "32721";
+ public static final String s32722 = "32722";
+ public static final String s32723 = "32723";
+ public static final String s32724 = "32724";
+ public static final String s32725 = "32725";
+ public static final String s32726 = "32726";
+ public static final String s32727 = "32727";
+ public static final String s32728 = "32728";
+ public static final String s32729 = "32729";
+ public static final String s32730 = "32730";
+ public static final String s32731 = "32731";
+ public static final String s32732 = "32732";
+ public static final String s32733 = "32733";
+ public static final String s32734 = "32734";
+ public static final String s32735 = "32735";
+ public static final String s32736 = "32736";
+ public static final String s32737 = "32737";
+ public static final String s32738 = "32738";
+ public static final String s32739 = "32739";
+ public static final String s32740 = "32740";
+ public static final String s32741 = "32741";
+ public static final String s32742 = "32742";
+ public static final String s32743 = "32743";
+ public static final String s32744 = "32744";
+ public static final String s32745 = "32745";
+ public static final String s32746 = "32746";
+ public static final String s32747 = "32747";
+ public static final String s32748 = "32748";
+ public static final String s32749 = "32749";
+ public static final String s32750 = "32750";
+ public static final String s32751 = "32751";
+ public static final String s32752 = "32752";
+ public static final String s32753 = "32753";
+ public static final String s32754 = "32754";
+ public static final String s32755 = "32755";
+ public static final String s32756 = "32756";
+ public static final String s32757 = "32757";
+ public static final String s32758 = "32758";
+ public static final String s32759 = "32759";
+ public static final String s32760 = "32760";
+ public static final String s32761 = "32761";
+ public static final String s32762 = "32762";
+ public static final String s32763 = "32763";
+ public static final String s32764 = "32764";
+ public static final String s32765 = "32765";
+ public static final String s32766 = "32766";
+ public static final String s32767 = "32767";
+}
diff --git a/src/test/examples/loadconst/LoadConst.java b/src/test/examples/loadconst/LoadConst.java
new file mode 100644
index 0000000..7ba7f30
--- /dev/null
+++ b/src/test/examples/loadconst/LoadConst.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package loadconst;
+
+public class LoadConst {
+
+ public static void main(String[] args) {
+ // ldc int
+ System.out.print(0x7fffffff);
+ // ldc float
+ System.out.print(1234.5);
+ // ldc string
+ System.out.println("foobar");
+ // ldc const class
+ System.out.println(LoadConst.class);
+ }
+}
diff --git a/src/test/examples/memberrebinding/ClassAtBottomOfChain.java b/src/test/examples/memberrebinding/ClassAtBottomOfChain.java
new file mode 100644
index 0000000..38e5d50
--- /dev/null
+++ b/src/test/examples/memberrebinding/ClassAtBottomOfChain.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding;
+
+public class ClassAtBottomOfChain extends EssentiallyEmptyClass {
+
+ @Override
+ public void superCallsProperlyPropagate() {
+ System.out.println("Try invoke on super, aka ClassInMiddleOfChain");
+ super.superCallsProperlyPropagate();
+ }
+
+ public void methodThatCallsSuperCallsProperlyPropagateTwo() {
+ // Invoke the method on the superclass even though this class does not override it.
+ super.superCallsProperlyPropagateTwo();
+ }
+
+ // Method with same name but different signature to test lookup.
+ public void methodThatShadowsPrivate(int ignore) {
+
+ }
+}
diff --git a/src/test/examples/memberrebinding/ClassExtendsLibraryClass.java b/src/test/examples/memberrebinding/ClassExtendsLibraryClass.java
new file mode 100644
index 0000000..80e5a30
--- /dev/null
+++ b/src/test/examples/memberrebinding/ClassExtendsLibraryClass.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class ClassExtendsLibraryClass extends ArrayList<String> {
+
+ public void methodThatAddsHelloWorld() {
+ // call add on this type, so it gets rebound.
+ add("hello");
+ // call it on list to make the methodid available.
+ addOnArrayList(this, "world");
+ }
+
+ private static <T> void addOnArrayList(ArrayList<T> list, T item) {
+ list.add(item);
+ }
+
+ public void methodThatAddsHelloWorldUsingAddAll() {
+ // call this only on this type, so that it cannot be rebound to the interface.
+ String[] words = new String[]{"hello", "world"};
+ addAll(Arrays.asList(words));
+ }
+
+}
diff --git a/src/test/examples/memberrebinding/ClassExtendsOtherLibraryClass.java b/src/test/examples/memberrebinding/ClassExtendsOtherLibraryClass.java
new file mode 100644
index 0000000..ab138fc
--- /dev/null
+++ b/src/test/examples/memberrebinding/ClassExtendsOtherLibraryClass.java
@@ -0,0 +1,20 @@
+// 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 memberrebinding;
+
+import memberrebindinglib.AnIndependentInterface;
+
+public class ClassExtendsOtherLibraryClass extends
+ SuperClassOfClassExtendsOtherLibraryClass implements AnIndependentInterface {
+
+ @Override
+ public boolean aMethodThatReturnsTrue() {
+ return true;
+ }
+
+ @Override
+ public boolean aMethodThatReturnsFalse() {
+ return false;
+ }
+}
diff --git a/src/test/examples/memberrebinding/ClassInMiddleOfChain.java b/src/test/examples/memberrebinding/ClassInMiddleOfChain.java
new file mode 100644
index 0000000..8cf34b2
--- /dev/null
+++ b/src/test/examples/memberrebinding/ClassInMiddleOfChain.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding;
+
+public class ClassInMiddleOfChain extends SuperClassOfAll {
+
+ @Override
+ public void superCallsProperlyPropagate() {
+ // Do not! call super here to break the super-chain.
+ System.out.println("Invoked superCallsProperlyPropagate on ClassInMiddleOfChain.");
+ }
+
+ @Override
+ public void superCallsProperlyPropagateTwo() {
+ // Do not! call super here to break the super-chain.
+ System.out.println("Invoked superCallsProperlyPropagateTwo on ClassInMiddleOfChain.");
+ }
+
+ public void methodThatShadowsPrivate() {
+ System.out.println("methodThatShadowsPrivate on ClassInMiddleOfChain");
+ }
+}
diff --git a/src/test/examples/memberrebinding/EssentiallyEmptyClass.java b/src/test/examples/memberrebinding/EssentiallyEmptyClass.java
new file mode 100644
index 0000000..43403b3
--- /dev/null
+++ b/src/test/examples/memberrebinding/EssentiallyEmptyClass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding;
+
+public class EssentiallyEmptyClass extends ClassInMiddleOfChain {
+
+}
diff --git a/src/test/examples/memberrebinding/SuperClassOfAll.java b/src/test/examples/memberrebinding/SuperClassOfAll.java
new file mode 100644
index 0000000..f986584
--- /dev/null
+++ b/src/test/examples/memberrebinding/SuperClassOfAll.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'memberrebinging.dex' is what is run.
+
+package memberrebinding;
+
+public class SuperClassOfAll {
+
+ public int superField;
+
+ public void superCallsProperlyPropagate() {
+ System.out.println("Invoked superCallsProperlyPropagate on SuperClassOfAll.");
+ }
+
+ public void superCallsProperlyPropagateTwo() {
+ System.out.println("Invoked superCallsProperlyPropagateTwo on SuperClassOfAll.");
+ }
+
+ private void methodThatShadowsPrivate() {
+ System.out.println("methodThatShadowsPrivate on SuperClassOfAll");
+ }
+
+ public void ensureAllCalled() {
+ methodThatShadowsPrivate();
+ }
+}
\ No newline at end of file
diff --git a/src/test/examples/memberrebinding/SuperClassOfClassExtendsOtherLibraryClass.java b/src/test/examples/memberrebinding/SuperClassOfClassExtendsOtherLibraryClass.java
new file mode 100644
index 0000000..03f1bdc
--- /dev/null
+++ b/src/test/examples/memberrebinding/SuperClassOfClassExtendsOtherLibraryClass.java
@@ -0,0 +1,14 @@
+// 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 memberrebinding;
+
+import memberrebindinglib.ImplementedInProgramClass;
+import memberrebindinglib.SubClass;
+
+public abstract class SuperClassOfClassExtendsOtherLibraryClass extends SubClass implements
+ ImplementedInProgramClass {
+ public int aMethodThatReturnsFour() {
+ return 4;
+ }
+}
diff --git a/src/test/examples/memberrebinding/Test.java b/src/test/examples/memberrebinding/Test.java
new file mode 100644
index 0000000..ba5e2f0
--- /dev/null
+++ b/src/test/examples/memberrebinding/Test.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding;
+
+import memberrebinding.subpackage.PublicClass;
+import memberrebindinglib.AnIndependentInterface;
+
+public class Test {
+
+ public static void main(String[] args) {
+ ClassAtBottomOfChain bottomInstance = new ClassAtBottomOfChain();
+ bottomInstance.superCallsProperlyPropagate();
+ bottomInstance.methodThatCallsSuperCallsProperlyPropagateTwo();
+ bottomInstance.methodThatShadowsPrivate();
+ bottomInstance.ensureAllCalled();
+ System.out.println(bottomInstance.superField);
+ ClassExtendsLibraryClass classExtendsLibraryClass = new ClassExtendsLibraryClass();
+ classExtendsLibraryClass.methodThatAddsHelloWorld();
+ classExtendsLibraryClass.methodThatAddsHelloWorldUsingAddAll();
+ System.out.println(classExtendsLibraryClass.get(0));
+ System.out.println(classExtendsLibraryClass.get(1));
+ System.out.println(classExtendsLibraryClass.get(2));
+ PublicClass instance = new PublicClass();
+ instance.aMethod();
+ ClassExtendsOtherLibraryClass classExtendsOther = new ClassExtendsOtherLibraryClass();
+ System.out.println(classExtendsOther.aMethodThatReturnsOne());
+ System.out.println(classExtendsOther.aMethodThatReturnsTwo());
+ System.out.println(classExtendsOther.aMethodThatReturnsThree());
+ System.out.println(classExtendsOther.aMethodThatReturnsFour());
+ AnIndependentInterface iface = classExtendsOther;
+ System.out.println(iface.aMethodThatReturnsTwo());
+ SuperClassOfClassExtendsOtherLibraryClass superClass = classExtendsOther;
+ System.out.println(superClass.aMethodThatReturnsTrue());
+ System.out.println(superClass.aMethodThatReturnsFalse());
+ }
+}
diff --git a/src/test/examples/memberrebinding/package.map b/src/test/examples/memberrebinding/package.map
new file mode 100644
index 0000000..e9bfd68
--- /dev/null
+++ b/src/test/examples/memberrebinding/package.map
@@ -0,0 +1,4 @@
+#
+# package.map file used by java/com/android/tools/r8/dex/ExtraFileTest.java
+#
+memberrebinding.subpackage:1
diff --git a/src/test/examples/memberrebinding/proguard.map b/src/test/examples/memberrebinding/proguard.map
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/test/examples/memberrebinding/proguard.map
diff --git a/src/test/examples/memberrebinding/subpackage/PackagePrivateClass.java b/src/test/examples/memberrebinding/subpackage/PackagePrivateClass.java
new file mode 100644
index 0000000..d749fb1
--- /dev/null
+++ b/src/test/examples/memberrebinding/subpackage/PackagePrivateClass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding.subpackage;
+
+abstract class PackagePrivateClass {
+
+ public final void aMethod() {
+ System.out.println("Hello, I am aMethod");
+ }
+}
diff --git a/src/test/examples/memberrebinding/subpackage/PublicClass.java b/src/test/examples/memberrebinding/subpackage/PublicClass.java
new file mode 100644
index 0000000..df3a92d
--- /dev/null
+++ b/src/test/examples/memberrebinding/subpackage/PublicClass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding.subpackage;
+
+public class PublicClass extends PackagePrivateClass {
+ // Intentionally left empty.
+}
diff --git a/src/test/examples/memberrebinding2/ClassAtBottomOfChain.java b/src/test/examples/memberrebinding2/ClassAtBottomOfChain.java
new file mode 100644
index 0000000..22ea072
--- /dev/null
+++ b/src/test/examples/memberrebinding2/ClassAtBottomOfChain.java
@@ -0,0 +1,11 @@
+// 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 memberrebinding2;
+
+public class ClassAtBottomOfChain extends EssentiallyEmptyClass {
+
+ public static int staticBottomField;
+
+ public int bottomField;
+}
diff --git a/src/test/examples/memberrebinding2/ClassExtendsLibraryClass.java b/src/test/examples/memberrebinding2/ClassExtendsLibraryClass.java
new file mode 100644
index 0000000..9094360
--- /dev/null
+++ b/src/test/examples/memberrebinding2/ClassExtendsLibraryClass.java
@@ -0,0 +1,21 @@
+// 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 memberrebinding2;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class ClassExtendsLibraryClass extends ArrayList<String> {
+
+ private static <T> void addOnArrayList(ArrayList<T> list, T item) {
+ list.add(item);
+ }
+
+ public void methodThatAddsHelloWorldUsingAddAll() {
+ // call this only on this type, so that it cannot be rebound to the interface.
+ String[] words = new String[]{"hello", "world"};
+ addAll(Arrays.asList(words));
+ }
+
+}
diff --git a/src/test/examples/memberrebinding2/ClassInMiddleOfChain.java b/src/test/examples/memberrebinding2/ClassInMiddleOfChain.java
new file mode 100644
index 0000000..209abf7
--- /dev/null
+++ b/src/test/examples/memberrebinding2/ClassInMiddleOfChain.java
@@ -0,0 +1,11 @@
+// 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 memberrebinding2;
+
+public class ClassInMiddleOfChain extends SuperClassOfAll {
+
+ public static int staticMiddleField;
+
+ public int middleField;
+}
diff --git a/src/test/examples/memberrebinding2/EssentiallyEmptyClass.java b/src/test/examples/memberrebinding2/EssentiallyEmptyClass.java
new file mode 100644
index 0000000..4da56b5
--- /dev/null
+++ b/src/test/examples/memberrebinding2/EssentiallyEmptyClass.java
@@ -0,0 +1,8 @@
+// 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 memberrebinding2;
+
+public class EssentiallyEmptyClass extends ClassInMiddleOfChain {
+
+}
diff --git a/src/test/examples/memberrebinding2/SuperClassOfAll.java b/src/test/examples/memberrebinding2/SuperClassOfAll.java
new file mode 100644
index 0000000..fa4156f
--- /dev/null
+++ b/src/test/examples/memberrebinding2/SuperClassOfAll.java
@@ -0,0 +1,15 @@
+// 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.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'memberrebinging.dex' is what is run.
+
+package memberrebinding2;
+
+public class SuperClassOfAll {
+
+ public static int staticSuperField;
+
+ public int superField;
+}
\ No newline at end of file
diff --git a/src/test/examples/memberrebinding2/Test.java b/src/test/examples/memberrebinding2/Test.java
new file mode 100644
index 0000000..d4b005e
--- /dev/null
+++ b/src/test/examples/memberrebinding2/Test.java
@@ -0,0 +1,38 @@
+// 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 memberrebinding2;
+
+import memberrebinding2.subpackage.PublicClass;
+
+public class Test {
+
+ public static void main(String[] args) {
+ ClassAtBottomOfChain bottomInstance = new ClassAtBottomOfChain();
+ PublicClass instance = new PublicClass();
+
+ int x = 0;
+
+ bottomInstance.bottomField = 1;
+ bottomInstance.middleField = 2;
+ bottomInstance.superField = 3;
+ instance.field = 4;
+
+ bottomInstance.staticBottomField = 5;
+ bottomInstance.staticMiddleField = 6;
+ bottomInstance.staticSuperField = 7;
+ instance.staticField = 8;
+
+ x += bottomInstance.bottomField;
+ x += bottomInstance.middleField;
+ x += bottomInstance.superField;
+ x += instance.field;
+
+ x += bottomInstance.staticBottomField;
+ x += bottomInstance.staticMiddleField;
+ x += bottomInstance.staticSuperField;
+ x += instance.staticField;
+
+ System.out.println(x);
+ }
+}
diff --git a/src/test/examples/memberrebinding2/subpackage/PackagePrivateClass.java b/src/test/examples/memberrebinding2/subpackage/PackagePrivateClass.java
new file mode 100644
index 0000000..733776c
--- /dev/null
+++ b/src/test/examples/memberrebinding2/subpackage/PackagePrivateClass.java
@@ -0,0 +1,11 @@
+// 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 memberrebinding2.subpackage;
+
+abstract class PackagePrivateClass {
+
+ public static int staticField;
+
+ public int field;
+}
diff --git a/src/test/examples/memberrebinding2/subpackage/PublicClass.java b/src/test/examples/memberrebinding2/subpackage/PublicClass.java
new file mode 100644
index 0000000..070bdce
--- /dev/null
+++ b/src/test/examples/memberrebinding2/subpackage/PublicClass.java
@@ -0,0 +1,8 @@
+// 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 memberrebinding2.subpackage;
+
+public class PublicClass extends PackagePrivateClass {
+ // Intentionally left empty.
+}
diff --git a/src/test/examples/memberrebinding3/ClassAtBottomOfChain.java b/src/test/examples/memberrebinding3/ClassAtBottomOfChain.java
new file mode 100644
index 0000000..8607f81
--- /dev/null
+++ b/src/test/examples/memberrebinding3/ClassAtBottomOfChain.java
@@ -0,0 +1,11 @@
+// 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 memberrebinding3;
+
+public class ClassAtBottomOfChain extends EssentiallyEmptyClass {
+
+ void bottomMethod() {
+
+ }
+}
diff --git a/src/test/examples/memberrebinding3/ClassInMiddleOfChain.java b/src/test/examples/memberrebinding3/ClassInMiddleOfChain.java
new file mode 100644
index 0000000..6e763c0
--- /dev/null
+++ b/src/test/examples/memberrebinding3/ClassInMiddleOfChain.java
@@ -0,0 +1,11 @@
+// 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 memberrebinding3;
+
+public class ClassInMiddleOfChain extends SuperClassOfAll {
+
+ void middleMethod() {
+
+ }
+}
diff --git a/src/test/examples/memberrebinding3/EssentiallyEmptyClass.java b/src/test/examples/memberrebinding3/EssentiallyEmptyClass.java
new file mode 100644
index 0000000..810fe0a
--- /dev/null
+++ b/src/test/examples/memberrebinding3/EssentiallyEmptyClass.java
@@ -0,0 +1,8 @@
+// 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 memberrebinding3;
+
+public class EssentiallyEmptyClass extends ClassInMiddleOfChain {
+
+}
diff --git a/src/test/examples/memberrebinding3/SuperClassOfAll.java b/src/test/examples/memberrebinding3/SuperClassOfAll.java
new file mode 100644
index 0000000..b34d6c7
--- /dev/null
+++ b/src/test/examples/memberrebinding3/SuperClassOfAll.java
@@ -0,0 +1,15 @@
+// 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.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'memberrebinging.dex' is what is run.
+
+package memberrebinding3;
+
+public class SuperClassOfAll {
+
+ void topMethod() {
+
+ }
+}
\ No newline at end of file
diff --git a/src/test/examples/memberrebinding3/Test.java b/src/test/examples/memberrebinding3/Test.java
new file mode 100644
index 0000000..d37f263
--- /dev/null
+++ b/src/test/examples/memberrebinding3/Test.java
@@ -0,0 +1,29 @@
+// 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 memberrebinding3;
+
+public class Test extends ClassAtBottomOfChain {
+
+ void bottomMethod() {
+
+ }
+
+ void middleMethod() {
+
+ }
+
+ void topMethod() {
+
+ }
+
+ private void test() {
+ super.bottomMethod();
+ super.middleMethod();
+ super.topMethod();
+ }
+
+ public static void main(String[] args) {
+ new Test().test();
+ }
+}
diff --git a/src/test/examples/memberrebindinglib/AnIndependentInterface.java b/src/test/examples/memberrebindinglib/AnIndependentInterface.java
new file mode 100644
index 0000000..01f018a
--- /dev/null
+++ b/src/test/examples/memberrebindinglib/AnIndependentInterface.java
@@ -0,0 +1,8 @@
+// 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 memberrebindinglib;
+
+public interface AnIndependentInterface {
+ public int aMethodThatReturnsTwo();
+}
diff --git a/src/test/examples/memberrebindinglib/ImplementedInLibrary.java b/src/test/examples/memberrebindinglib/ImplementedInLibrary.java
new file mode 100644
index 0000000..786cc9e
--- /dev/null
+++ b/src/test/examples/memberrebindinglib/ImplementedInLibrary.java
@@ -0,0 +1,8 @@
+// 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 memberrebindinglib;
+
+public interface ImplementedInLibrary {
+ boolean aMethodThatReturnsTrue();
+}
diff --git a/src/test/examples/memberrebindinglib/ImplementedInProgramClass.java b/src/test/examples/memberrebindinglib/ImplementedInProgramClass.java
new file mode 100644
index 0000000..3c6ec33
--- /dev/null
+++ b/src/test/examples/memberrebindinglib/ImplementedInProgramClass.java
@@ -0,0 +1,8 @@
+// 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 memberrebindinglib;
+
+public interface ImplementedInProgramClass {
+ boolean aMethodThatReturnsFalse();
+}
diff --git a/src/test/examples/memberrebindinglib/SubClass.java b/src/test/examples/memberrebindinglib/SubClass.java
new file mode 100644
index 0000000..175053f
--- /dev/null
+++ b/src/test/examples/memberrebindinglib/SubClass.java
@@ -0,0 +1,18 @@
+// 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 memberrebindinglib;
+
+public abstract class SubClass extends SuperClass implements ImplementedInLibrary {
+ public int aMethodThatReturnsOne() {
+ return 1;
+ }
+
+ public int aMethodThatReturnsTwo() {
+ return 2;
+ }
+
+ public int aMethodThatReturnsFour() {
+ return 4;
+ }
+}
diff --git a/src/test/examples/memberrebindinglib/SuperClass.java b/src/test/examples/memberrebindinglib/SuperClass.java
new file mode 100644
index 0000000..c5818b4
--- /dev/null
+++ b/src/test/examples/memberrebindinglib/SuperClass.java
@@ -0,0 +1,14 @@
+// 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 memberrebindinglib;
+
+public class SuperClass {
+ public int aMethodThatReturnsTwo() {
+ return 2;
+ }
+
+ public int aMethodThatReturnsThree() {
+ return 3;
+ }
+}
diff --git a/src/test/examples/minification/ClassD.java b/src/test/examples/minification/ClassD.java
new file mode 100644
index 0000000..9804afc
--- /dev/null
+++ b/src/test/examples/minification/ClassD.java
@@ -0,0 +1,19 @@
+// 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 minification;
+
+public class ClassD implements InterfaceD {
+
+ @Override
+ public int anotherFunctionFromIntToInt(int arg) {
+ System.out.println("ClassD:anotherFunctionFromIntToInt");
+ return arg;
+ }
+
+ @Override
+ public int functionFromIntToInt(int arg) {
+ System.out.println("ClassD:functionFromIntToInt");
+ return arg;
+ }
+}
diff --git a/src/test/examples/minification/EmptyInterface.java b/src/test/examples/minification/EmptyInterface.java
new file mode 100644
index 0000000..1fda851
--- /dev/null
+++ b/src/test/examples/minification/EmptyInterface.java
@@ -0,0 +1,8 @@
+// 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 minification;
+
+public interface EmptyInterface {
+
+}
diff --git a/src/test/examples/minification/InterfaceA.java b/src/test/examples/minification/InterfaceA.java
new file mode 100644
index 0000000..922ea3d
--- /dev/null
+++ b/src/test/examples/minification/InterfaceA.java
@@ -0,0 +1,17 @@
+// 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 minification;
+
+public interface InterfaceA {
+
+ /**
+ * This method is also defined in {@link InterfaceB} and {@link InterfaceD}.
+ */
+ int functionFromIntToInt(int arg);
+
+ /**
+ * This signature is only defined here.
+ */
+ int uniqueLittleMethodInA();
+}
diff --git a/src/test/examples/minification/InterfaceB.java b/src/test/examples/minification/InterfaceB.java
new file mode 100644
index 0000000..ceddaa7
--- /dev/null
+++ b/src/test/examples/minification/InterfaceB.java
@@ -0,0 +1,17 @@
+// 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 minification;
+
+public interface InterfaceB extends EmptyInterface {
+
+ /**
+ * This method is also defined in {@link InterfaceA} and {@link InterfaceD};
+ */
+ int functionFromIntToInt(int arg);
+
+ /**
+ * This signature is only defined here.
+ */
+ int uniqueLittleMethodInB();
+}
diff --git a/src/test/examples/minification/InterfaceC.java b/src/test/examples/minification/InterfaceC.java
new file mode 100644
index 0000000..e6c4917
--- /dev/null
+++ b/src/test/examples/minification/InterfaceC.java
@@ -0,0 +1,12 @@
+// 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 minification;
+
+public interface InterfaceC extends InterfaceB {
+
+ /**
+ * This signature is only defined here.
+ */
+ int uniqueLittleMethodInC();
+}
diff --git a/src/test/examples/minification/InterfaceD.java b/src/test/examples/minification/InterfaceD.java
new file mode 100644
index 0000000..ec60f06
--- /dev/null
+++ b/src/test/examples/minification/InterfaceD.java
@@ -0,0 +1,17 @@
+// 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 minification;
+
+public interface InterfaceD {
+
+ /**
+ * This method is only defined here.
+ */
+ int anotherFunctionFromIntToInt(int arg);
+
+ /**
+ * This method is also defined in {@link InterfaceA} and {@link InterfaceB};
+ */
+ int functionFromIntToInt(int arg);
+}
diff --git a/src/test/examples/minification/Minification.java b/src/test/examples/minification/Minification.java
new file mode 100644
index 0000000..b5f2f6e
--- /dev/null
+++ b/src/test/examples/minification/Minification.java
@@ -0,0 +1,39 @@
+// 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 minification;
+
+public class Minification {
+
+ public static void main(String[] args) {
+ SubClassA subClassA = new SubClassA();
+ exerciseA(subClassA);
+ SubSubClassAB subSubClassAB = new SubSubClassAB();
+ exerciseA(subSubClassAB);
+ exerciseB(subSubClassAB);
+ SubClassB subClassB = new SubClassB();
+ exerciseB(subClassB);
+ SubClassC subClassC = new SubClassC();
+ exerciseB(subClassC);
+ exerciseC(subClassC);
+ ClassD classD = new ClassD();
+ exerciseD(classD);
+ }
+
+ private static void exerciseA(InterfaceA thing) {
+ thing.functionFromIntToInt(thing.uniqueLittleMethodInA());
+ }
+
+ private static void exerciseB(InterfaceB thing) {
+ thing.functionFromIntToInt(thing.uniqueLittleMethodInB());
+ }
+
+ private static void exerciseC(InterfaceC thing) {
+ thing.functionFromIntToInt(thing.uniqueLittleMethodInC());
+ }
+
+ private static void exerciseD(InterfaceD thing) {
+ thing.anotherFunctionFromIntToInt(42);
+ thing.functionFromIntToInt(42);
+ }
+}
diff --git a/src/test/examples/minification/SubClassA.java b/src/test/examples/minification/SubClassA.java
new file mode 100644
index 0000000..b856f04
--- /dev/null
+++ b/src/test/examples/minification/SubClassA.java
@@ -0,0 +1,19 @@
+// 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 minification;
+
+public class SubClassA extends SuperClassA {
+
+ @Override
+ public int functionFromIntToInt(int arg) {
+ System.out.println("SubClassA:functionFromIntToInt(A)");
+ return arg;
+ }
+
+ @Override
+ public int uniqueLittleMethodInA() {
+ System.out.println("SubClassA:uniqueLittleMethodInA");
+ return 0;
+ }
+}
diff --git a/src/test/examples/minification/SubClassB.java b/src/test/examples/minification/SubClassB.java
new file mode 100644
index 0000000..663c87c
--- /dev/null
+++ b/src/test/examples/minification/SubClassB.java
@@ -0,0 +1,19 @@
+// 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 minification;
+
+public class SubClassB implements InterfaceB {
+
+ @Override
+ public int functionFromIntToInt(int arg) {
+ System.out.println("SubClassB.functionFromIntToInt");
+ return arg;
+ }
+
+ @Override
+ public int uniqueLittleMethodInB() {
+ System.out.println("SubClassB.uniqueLittleMethodInB");
+ return 0;
+ }
+}
diff --git a/src/test/examples/minification/SubClassC.java b/src/test/examples/minification/SubClassC.java
new file mode 100644
index 0000000..bdffc0b
--- /dev/null
+++ b/src/test/examples/minification/SubClassC.java
@@ -0,0 +1,25 @@
+// 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 minification;
+
+public class SubClassC extends SuperClassC implements InterfaceC {
+
+ @Override
+ public int functionFromIntToInt(int arg) {
+ System.out.println("SubClassC:functionFromIntToInt");
+ return arg;
+ }
+
+ @Override
+ public int uniqueLittleMethodInB() {
+ System.out.println("SubClassC:uniqueLittleMethodInB");
+ return 0;
+ }
+
+ @Override
+ public int uniqueLittleMethodInC() {
+ System.out.println("SubClassC:uniqueLittleMethodInC");
+ return 0;
+ }
+}
diff --git a/src/test/examples/minification/SubSubClassAB.java b/src/test/examples/minification/SubSubClassAB.java
new file mode 100644
index 0000000..4e7fa41
--- /dev/null
+++ b/src/test/examples/minification/SubSubClassAB.java
@@ -0,0 +1,19 @@
+// 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 minification;
+
+public class SubSubClassAB extends SubClassA implements InterfaceB {
+
+ @Override
+ public int functionFromIntToInt(int arg) {
+ System.out.println("SubSubClassAB:functionFromIntToInt");
+ return super.functionFromIntToInt(arg);
+ }
+
+ @Override
+ public int uniqueLittleMethodInB() {
+ System.out.println("SubSubClassAB:uniqueLittleMethodInB");
+ return 0;
+ }
+}
diff --git a/src/test/examples/minification/SuperClassA.java b/src/test/examples/minification/SuperClassA.java
new file mode 100644
index 0000000..99dfe22
--- /dev/null
+++ b/src/test/examples/minification/SuperClassA.java
@@ -0,0 +1,8 @@
+// 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 minification;
+
+public abstract class SuperClassA implements InterfaceA {
+
+}
diff --git a/src/test/examples/minification/SuperClassB.java b/src/test/examples/minification/SuperClassB.java
new file mode 100644
index 0000000..7dd134f
--- /dev/null
+++ b/src/test/examples/minification/SuperClassB.java
@@ -0,0 +1,8 @@
+// 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 minification;
+
+public class SuperClassB {
+
+}
diff --git a/src/test/examples/minification/SuperClassC.java b/src/test/examples/minification/SuperClassC.java
new file mode 100644
index 0000000..156e863
--- /dev/null
+++ b/src/test/examples/minification/SuperClassC.java
@@ -0,0 +1,8 @@
+// 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 minification;
+
+public class SuperClassC {
+
+}
diff --git a/src/test/examples/minification/keep-rules.txt b/src/test/examples/minification/keep-rules.txt
new file mode 100644
index 0000000..efd06c5
--- /dev/null
+++ b/src/test/examples/minification/keep-rules.txt
@@ -0,0 +1,12 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class minification.Minification {
+ public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/newarray/NewArray.java b/src/test/examples/newarray/NewArray.java
new file mode 100644
index 0000000..b652324
--- /dev/null
+++ b/src/test/examples/newarray/NewArray.java
@@ -0,0 +1,168 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package newarray;
+
+class NewArray {
+
+ static class A {
+ int v0;
+ int v1;
+ int v2;
+ int v3;
+ int v4;
+ int v5;
+ int v6;
+ }
+
+ public static void printArray(int[] array) {
+ for (int i : array) System.out.println(i);
+ }
+
+ public static void test() {
+ int x0[] = new int[]{};
+ int x1[] = new int[]{0};
+ int x2[] = new int[]{0, 1};
+ int x3[] = new int[]{0, 1, 2};
+ int x4[] = new int[]{0, 1, 2, 3};
+ int x5[] = new int[]{0, 1, 2, 3, 4};
+ int x6[] = new int[]{0, 1, 2, 3, 4, 5};
+ int x7[] = new int[]{0, 1, 2, 3, 4, 5, 6};
+ int x8[] = new int[]{0, 1, 2, 3, 4, 5, 6, 7};
+ int x9[] = new int[]{0, 1, 0, 3, 4, 0, 6, 7};
+ printArray(x0);
+ printArray(x1);
+ printArray(x2);
+ printArray(x3);
+ printArray(x4);
+ printArray(x5);
+ printArray(x6);
+ printArray(x7);
+ printArray(x8);
+ printArray(x9);
+ }
+
+ public static void testIntArgs(int v0, int v1, int v2, int v3, int v4, int v5) {
+ int x0[] = new int[]{};
+ int x1[] = new int[]{v0};
+ int x2[] = new int[]{v0, v1};
+ int x3[] = new int[]{v0, v1, v2};
+ int x4[] = new int[]{v0, v1, v2, v3};
+ int x5[] = new int[]{v0, v1, v2, v3, v4};
+ int x6[] = new int[]{v0, v1, v2, v3, v4, v5};
+ int x7[] = new int[]{v0, v1, v2, v3, v4, v5, v0, v1, v0, v4, v0};
+ printArray(x0);
+ printArray(x1);
+ printArray(x2);
+ printArray(x3);
+ printArray(x4);
+ printArray(x5);
+ printArray(x6);
+ printArray(x7);
+ }
+
+ public static void testObjectArg(A a) {
+ int x0[] = new int[]{};
+ int x1[] = new int[]{a.v0};
+ int x2[] = new int[]{a.v0, a.v1};
+ int x3[] = new int[]{a.v0, a.v1, a.v2};
+ int x4[] = new int[]{a.v0, a.v1, a.v2, a.v3};
+ int x5[] = new int[]{a.v0, a.v1, a.v2, a.v3, a.v4};
+ int x6[] = new int[]{a.v0, a.v1, a.v2, a.v3, a.v4, a.v5};
+ int x7[] = new int[]{a.v0, a.v1, a.v2, a.v3, a.v4, a.v5, a.v6};
+ int x8[] = new int[]{a.v0, a.v1, a.v2, a.v0, a.v3, a.v4, a.v5, a.v6};
+ printArray(x0);
+ printArray(x1);
+ printArray(x2);
+ printArray(x3);
+ printArray(x4);
+ printArray(x5);
+ printArray(x6);
+ printArray(x7);
+ printArray(x8);
+ }
+
+ public static void newMultiDimensionalArrays(int n) {
+ int[][] i2 = new int[n][n];
+ int[][][] i3 = new int[n][n][n];
+ int[][][][] i4 = new int[n][n][n][n];
+ int[][][][][] i5 = new int[n][n][n][n][n];
+ int[][][][][][] i6 = new int[n][n][n][n][n][n];
+ System.out.println(i2.length);
+ System.out.println(i3.length);
+ System.out.println(i4.length);
+ System.out.println(i5.length);
+ System.out.println(i6.length);
+ }
+
+ public static void newMultiDimensionalArrays2(int n1, int n2, int n3, int n4, int n5, int n6) {
+ int[][] i2 = new int[n1][n2];
+ System.out.println(i2.length);
+ int[][][] i3 = new int[n1][n2][n3];
+ System.out.println(i3.length);
+ int[][][][] i4 = new int[n1][n2][n3][n4];
+ System.out.println(i4.length);
+ int[][][][][] i5 = new int[n1][n2][n3][n4][n5];
+ System.out.println(i5.length);
+ int[][][][][][] i6 = new int[n1][n2][n3][n4][n5][n6];
+ System.out.println(i6.length);
+ int[][][][][][] i7 = new int[n1][n2][n1][n4][n5][n1];
+ System.out.println(i7.length);
+ }
+
+ public static void newMultiDimensionalArrays3(int n) {
+ int[][][] i3 = new int[n][n][];
+ int[][][][] i4 = new int[n][n][][];
+ int[][][][][][][] i7 = new int[n][n][n][n][n][n][];
+ int[][][][][][][][] i8 = new int[n][n][n][n][n][n][][];
+ System.out.println(i3.length);
+ System.out.println(i4.length);
+ System.out.println(i7.length);
+ System.out.println(i8.length);
+ }
+
+ public static void newMultiDimensionalArrays4() {
+ boolean[][] a1 = new boolean[1][2];
+ byte[][] a2 = new byte[3][4];
+ char[][] a3 = new char[5][6];
+ short[][] a4 = new short[7][8];
+ long[][] a5 = new long[9][10];
+ float[][] a6 = new float[11][12];
+ double[][] a7 = new double[13][14];
+ A[][] a8 = new A[15][16];
+ System.out.println(a1[0].length);
+ System.out.println(a2[0].length);
+ System.out.println(a3[0].length);
+ System.out.println(a4[0].length);
+ System.out.println(a5[0].length);
+ System.out.println(a6[0].length);
+ System.out.println(a7[0].length);
+ System.out.println(a8[0].length);
+ System.out.println(a1[0][0]);
+ System.out.println(a2[0][0]);
+ System.out.println(a3[0][0]);
+ System.out.println(a4[0][0]);
+ System.out.println(a5[0][0]);
+ System.out.println(a6[0][0]);
+ System.out.println(a7[0][0]);
+ System.out.println(a8[0][0]);
+ }
+
+ public static void main(String[] args) {
+ test();
+ testIntArgs(0, 1, 2, 3, 4, 5);
+ A a = new A();
+ a.v0 = 0;
+ a.v1 = 1;
+ a.v2 = 2;
+ a.v3 = 3;
+ a.v4 = 4;
+ a.v5 = 5;
+ a.v6 = 6;
+ testObjectArg(a);
+ newMultiDimensionalArrays(6);
+ newMultiDimensionalArrays2(1, 2, 3, 4, 5, 6);
+ newMultiDimensionalArrays3(8);
+ newMultiDimensionalArrays4();
+ }
+}
\ No newline at end of file
diff --git a/src/test/examples/regalloc/RegAlloc.java b/src/test/examples/regalloc/RegAlloc.java
new file mode 100644
index 0000000..68ad054
--- /dev/null
+++ b/src/test/examples/regalloc/RegAlloc.java
@@ -0,0 +1,391 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'regalloc.dex' is what is run.
+
+package regalloc;
+
+// Various test cases that are challenging for the register allocator.
+public class RegAlloc {
+
+ public static class BoxedInteger {
+ public int i;
+ public BoxedInteger(int i) {
+ this.i = i;
+ }
+ }
+
+ // Takes as many arguments as are allowed by the Java programming language (255) and
+ // does computations on them.
+ public static void binaryOpUsingHighRegistersArguments(
+ int d000, int d001, int d002, int d003, int d004, int d005,
+ int d006, int d007, int d008, int d009, int d010, int d011,
+ int d012, int d013, int d014, int d015, int d016, int d017,
+ int d018, int d019, int d020, int d021, int d022, int d023,
+ int d024, int d025, int d026, int d027, int d028, int d029,
+ int d030, int d031, int d032, int d033, int d034, int d035,
+ int d036, int d037, int d038, int d039, int d040, int d041,
+ int d042, int d043, int d044, int d045, int d046, int d047,
+ int d048, int d049, int d050, int d051, int d052, int d053,
+ int d054, int d055, int d056, int d057, int d058, int d059,
+ int d060, int d061, int d062, int d063, int d064, int d065,
+ int d066, int d067, int d068, int d069, int d070, int d071,
+ int d072, int d073, int d074, int d075, int d076, int d077,
+ int d078, int d079, int d080, int d081, int d082, int d083,
+ int d084, int d085, int d086, int d087, int d088, int d089,
+ int d090, int d091, int d092, int d093, int d094, int d095,
+ int d096, int d097, int d098, int d099, int d100, int d101,
+ int d102, int d103, int d104, int d105, int d106, int d107,
+ int d108, int d109, int d110, int d111, int d112, int d113,
+ int d114, int d115, int d116, int d117, int d118, int d119,
+ int d120, int d121, int d122, int d123, int d124, int d125,
+ int d126, int d127, int d128, int d129, int d130, int d131,
+ int d132, int d133, int d134, int d135, int d136, int d137,
+ int d138, int d139, int d140, int d141, int d142, int d143,
+ int d144, int d145, int d146, int d147, int d148, int d149,
+ int d150, int d151, int d152, int d153, int d154, int d155,
+ int d156, int d157, int d158, int d159, int d160, int d161,
+ int d162, int d163, int d164, int d165, int d166, int d167,
+ int d168, int d169, int d170, int d171, int d172, int d173,
+ int d174, int d175, int d176, int d177, int d178, int d179,
+ int d180, int d181, int d182, int d183, int d184, int d185,
+ int d186, int d187, int d188, int d189, int d190, int d191,
+ int d192, int d193, int d194, int d195, int d196, int d197,
+ int d198, int d199, int d200, int d201, int d202, int d203,
+ int d204, int d205, int d206, int d207, int d208, int d209,
+ int d210, int d211, int d212, int d213, int d214, int d215,
+ int d216, int d217, int d218, int d219, int d220, int d221,
+ int d222, int d223, int d224, int d225, int d226, int d227,
+ int d228, int d229, int d230, int d231, int d232, int d233,
+ int d234, int d235, int d236, int d237, int d238, int d239,
+ int d240, int d241, int d242, int d243, int d244, int d245,
+ int d246, int d247, int d248, int d249, int d250, int d251,
+ int d252, int d253, int d254) {
+ d254 += d253;
+ d253 += 4;
+ d016 += 17000;
+ System.out.println("binaryOpUsingHighRegistersArguments: " + d016 + " " + d253 + " " + d254);
+ }
+
+ // Takes as many arguments as are allowed by the Java programming language (255) and
+ // does computations on them.
+ public static void binaryDoubleOpUsingHighRegistersArguments(
+ double d000, double d001, double d002, double d003, double d004, double d005,
+ double d006, double d007, double d008, double d009, double d010, double d011,
+ double d012, double d013, double d014, double d015, double d016, double d017,
+ double d018, double d019, double d020, double d021, double d022, double d023,
+ double d024, double d025, double d026, double d027, double d028, double d029,
+ double d030, double d031, double d032, double d033, double d034, double d035,
+ double d036, double d037, double d038, double d039, double d040, double d041,
+ double d042, double d043, double d044, double d045, double d046, double d047,
+ double d048, double d049, double d050, double d051, double d052, double d053,
+ double d054, double d055, double d056, double d057, double d058, double d059,
+ double d060, double d061, double d062, double d063, double d064, double d065,
+ double d066, double d067, double d068, double d069, double d070, double d071,
+ double d072, double d073, double d074, double d075, double d076, double d077,
+ double d078, double d079, double d080, double d081, double d082, double d083,
+ double d084, double d085, double d086, double d087, double d088, double d089,
+ double d090, double d091, double d092, double d093, double d094, double d095,
+ double d096, double d097, double d098, double d099, double d100, double d101,
+ double d102, double d103, double d104, double d105, double d106, double d107,
+ double d108, double d109, double d110, double d111, double d112, double d113,
+ double d114, double d115, double d116, double d117, double d118, double d119,
+ double d120, double d121, double d122, double d123, double d124, double d125,
+ double d126) {
+ d126 += d125;
+ System.out.println("binaryDoubleOpUsingHighRegistersArguments: " + d126 + " " + d125);
+ }
+
+ public static <T> T identity(T i) {
+ return i;
+ }
+
+ // Uses a lot of locals that are alive across an addition of two of them. Trivial
+ // register allocation will assign too high registers for the additions.
+ public static void binaryOpUsingHighRegistersLocals() {
+ // Go through identity function in an attempt to make sure that the addition
+ // at the end of this method is not constant folded away.
+ int i000 = identity(0); int i001 = identity(1); int i002 = identity(2);
+ int i003 = identity(3); int i004 = identity(4); int i005 = identity(5);
+ int i006 = identity(6); int i007 = identity(7); int i008 = identity(8);
+ int i009 = identity(9); int i010 = identity(10); int i011 = identity(11);
+ int i012 = identity(12); int i013 = identity(13); int i014 = identity(14);
+ int i015 = identity(15); int i016 = identity(16); int i017 = identity(17);
+ int i018 = identity(18); int i019 = identity(19); int i020 = identity(20);
+ int i021 = identity(21); int i022 = identity(22); int i023 = identity(23);
+ int i024 = identity(24); int i025 = identity(25); int i026 = identity(26);
+ int i027 = identity(27); int i028 = identity(28); int i029 = identity(29);
+ int i030 = identity(30); int i031 = identity(31); int i032 = identity(32);
+ int i033 = identity(33); int i034 = identity(34); int i035 = identity(35);
+ int i036 = identity(36); int i037 = identity(37); int i038 = identity(38);
+ int i039 = identity(39); int i040 = identity(40); int i041 = identity(41);
+ int i042 = identity(42); int i043 = identity(43); int i044 = identity(44);
+ int i045 = identity(45); int i046 = identity(46); int i047 = identity(47);
+ int i048 = identity(48); int i049 = identity(49); int i050 = identity(50);
+ int i051 = identity(51); int i052 = identity(52); int i053 = identity(53);
+ int i054 = identity(54); int i055 = identity(55); int i056 = identity(56);
+ int i057 = identity(57); int i058 = identity(58); int i059 = identity(59);
+ int i060 = identity(60); int i061 = identity(61); int i062 = identity(62);
+ int i063 = identity(63); int i064 = identity(64); int i065 = identity(65);
+ int i066 = identity(66); int i067 = identity(67); int i068 = identity(68);
+ int i069 = identity(69); int i070 = identity(70); int i071 = identity(71);
+ int i072 = identity(72); int i073 = identity(73); int i074 = identity(74);
+ int i075 = identity(75); int i076 = identity(76); int i077 = identity(77);
+ int i078 = identity(78); int i079 = identity(79); int i080 = identity(80);
+ int i081 = identity(81); int i082 = identity(82); int i083 = identity(83);
+ int i084 = identity(84); int i085 = identity(85); int i086 = identity(86);
+ int i087 = identity(87); int i088 = identity(88); int i089 = identity(89);
+ int i090 = identity(90); int i091 = identity(91); int i092 = identity(92);
+ int i093 = identity(93); int i094 = identity(94); int i095 = identity(95);
+ int i096 = identity(96); int i097 = identity(97); int i098 = identity(98);
+ int i099 = identity(99); int i100 = identity(100); int i101 = identity(101);
+ int i102 = identity(102); int i103 = identity(103); int i104 = identity(104);
+ int i105 = identity(105); int i106 = identity(106); int i107 = identity(107);
+ int i108 = identity(108); int i109 = identity(109); int i110 = identity(110);
+ int i111 = identity(111); int i112 = identity(112); int i113 = identity(113);
+ int i114 = identity(114); int i115 = identity(115); int i116 = identity(116);
+ int i117 = identity(117); int i118 = identity(118); int i119 = identity(119);
+ int i120 = identity(120); int i121 = identity(121); int i122 = identity(122);
+ int i123 = identity(123); int i124 = identity(124); int i125 = identity(125);
+ int i126 = identity(126); int i127 = identity(127); int i128 = identity(128);
+ int i129 = identity(129); int i130 = identity(130); int i131 = identity(131);
+ int i132 = identity(132); int i133 = identity(133); int i134 = identity(134);
+ int i135 = identity(135); int i136 = identity(136); int i137 = identity(137);
+ int i138 = identity(138); int i139 = identity(139); int i140 = identity(140);
+ int i141 = identity(141); int i142 = identity(142); int i143 = identity(143);
+ int i144 = identity(144); int i145 = identity(145); int i146 = identity(146);
+ int i147 = identity(147); int i148 = identity(148); int i149 = identity(149);
+ int i150 = identity(150); int i151 = identity(151); int i152 = identity(152);
+ int i153 = identity(153); int i154 = identity(154); int i155 = identity(155);
+ int i156 = identity(156); int i157 = identity(157); int i158 = identity(158);
+ int i159 = identity(159); int i160 = identity(160); int i161 = identity(161);
+ int i162 = identity(162); int i163 = identity(163); int i164 = identity(164);
+ int i165 = identity(165); int i166 = identity(166); int i167 = identity(167);
+ int i168 = identity(168); int i169 = identity(169); int i170 = identity(170);
+ int i171 = identity(171); int i172 = identity(172); int i173 = identity(173);
+ int i174 = identity(174); int i175 = identity(175); int i176 = identity(176);
+ int i177 = identity(177); int i178 = identity(178); int i179 = identity(179);
+ int i180 = identity(180); int i181 = identity(181); int i182 = identity(182);
+ int i183 = identity(183); int i184 = identity(184); int i185 = identity(185);
+ int i186 = identity(186); int i187 = identity(187); int i188 = identity(188);
+ int i189 = identity(189); int i190 = identity(190); int i191 = identity(191);
+ int i192 = identity(192); int i193 = identity(193); int i194 = identity(194);
+ int i195 = identity(195); int i196 = identity(196); int i197 = identity(197);
+ int i198 = identity(198); int i199 = identity(199); int i200 = identity(200);
+ int i201 = identity(201); int i202 = identity(202); int i203 = identity(203);
+ int i204 = identity(204); int i205 = identity(205); int i206 = identity(206);
+ int i207 = identity(207); int i208 = identity(208); int i209 = identity(209);
+ int i210 = identity(210); int i211 = identity(211); int i212 = identity(212);
+ int i213 = identity(213); int i214 = identity(214); int i215 = identity(215);
+ int i216 = identity(216); int i217 = identity(217); int i218 = identity(218);
+ int i219 = identity(219); int i220 = identity(220); int i221 = identity(221);
+ int i222 = identity(222); int i223 = identity(223); int i224 = identity(224);
+ int i225 = identity(225); int i226 = identity(226); int i227 = identity(227);
+ int i228 = identity(228); int i229 = identity(229); int i230 = identity(230);
+ int i231 = identity(231); int i232 = identity(232); int i233 = identity(233);
+ int i234 = identity(234); int i235 = identity(235); int i236 = identity(236);
+ int i237 = identity(237); int i238 = identity(238); int i239 = identity(239);
+ int i240 = identity(240); int i241 = identity(241); int i242 = identity(242);
+ int i243 = identity(243); int i244 = identity(244); int i245 = identity(245);
+ int i246 = identity(246); int i247 = identity(247); int i248 = identity(248);
+ int i249 = identity(249); int i250 = identity(250); int i251 = identity(251);
+ int i252 = identity(252); int i253 = identity(253); int i254 = identity(254);
+ int i255 = identity(255); int i256 = identity(256); int i257 = identity(257);
+ int i258 = identity(258); int i259 = identity(259);
+
+ int i = i259 + i259;
+ System.out.println("binaryOpUsingHighRegistersLocals " + i);
+
+ i = new BoxedInteger(42).i;
+ System.out.println("instance get many registers" + i);
+
+ int j = i000 + i001 + i002 + i003 + i004 + i005 + i006 + i007 + i008 + i009 + i010 + i011 +
+ i012 + i013 + i014 + i015 + i016 + i017 + i018 + i019 + i020 + i021 + i022 + i023 +
+ i024 + i025 + i026 + i027 + i028 + i029 + i030 + i031 + i032 + i033 + i034 + i035 +
+ i036 + i037 + i038 + i039 + i040 + i041 + i042 + i043 + i044 + i045 + i046 + i047 +
+ i048 + i049 + i050 + i051 + i052 + i053 + i054 + i055 + i056 + i057 + i058 + i059 +
+ i060 + i061 + i062 + i063 + i064 + i065 + i066 + i067 + i068 + i069 + i070 + i071 +
+ i072 + i073 + i074 + i075 + i076 + i077 + i078 + i079 + i080 + i081 + i082 + i083 +
+ i084 + i085 + i086 + i087 + i088 + i089 + i090 + i091 + i092 + i093 + i094 + i095 +
+ i096 + i097 + i098 + i099 + i100 + i101 + i102 + i103 + i104 + i105 + i106 + i107 +
+ i108 + i109 + i110 + i111 + i112 + i113 + i114 + i115 + i116 + i117 + i118 + i119 +
+ i120 + i121 + i122 + i123 + i124 + i125 + i126 + i127 + i128 + i129 + i130 + i131 +
+ i132 + i133 + i134 + i135 + i136 + i137 + i138 + i139 + i140 + i141 + i142 + i143 +
+ i144 + i145 + i146 + i147 + i148 + i149 + i150 + i151 + i152 + i153 + i154 + i155 +
+ i156 + i157 + i158 + i159 + i160 + i161 + i162 + i163 + i164 + i165 + i166 + i167 +
+ i168 + i169 + i170 + i171 + i172 + i173 + i174 + i175 + i176 + i177 + i178 + i179 +
+ i180 + i181 + i182 + i183 + i184 + i185 + i186 + i187 + i188 + i189 + i190 + i191 +
+ i192 + i193 + i194 + i195 + i196 + i197 + i198 + i199 + i200 + i201 + i202 + i203 +
+ i204 + i205 + i206 + i207 + i208 + i209 + i210 + i211 + i212 + i213 + i214 + i215 +
+ i216 + i217 + i218 + i219 + i220 + i221 + i222 + i223 + i224 + i225 + i226 + i227 +
+ i228 + i229 + i230 + i231 + i232 + i233 + i234 + i235 + i236 + i237 + i238 + i239 +
+ i240 + i241 + i242 + i243 + i244 + i245 + i246 + i247 + i248 + i249 + i250 + i251 +
+ i252 + i253 + i254 + i255 + i256 + i257 + i258 + i259;
+ System.out.println("sum: " + j);
+ }
+
+ // Uses a lot of locals that are alive across an addition of two of them. Trivial
+ // register allocation will assign too high registers for the additions.
+ public static void binaryDoubleOpUsingHighRegistersLocals() {
+ // Go through identity function in an attempt to make sure that the addition
+ // at the end of this method is not constant folded away.
+ double i000 = identity(0.0); double i001 = identity(1.0); double i002 = identity(2.0);
+ double i003 = identity(3.0); double i004 = identity(4.0); double i005 = identity(5.0);
+ double i006 = identity(6.0); double i007 = identity(7.0); double i008 = identity(8.0);
+ double i009 = identity(9.0); double i010 = identity(10.0); double i011 = identity(11.0);
+ double i012 = identity(12.0); double i013 = identity(13.0); double i014 = identity(14.0);
+ double i015 = identity(15.0); double i016 = identity(16.0); double i017 = identity(17.0);
+ double i018 = identity(18.0); double i019 = identity(19.0); double i020 = identity(20.0);
+ double i021 = identity(21.0); double i022 = identity(22.0); double i023 = identity(23.0);
+ double i024 = identity(24.0); double i025 = identity(25.0); double i026 = identity(26.0);
+ double i027 = identity(27.0); double i028 = identity(28.0); double i029 = identity(29.0);
+ double i030 = identity(30.0); double i031 = identity(31.0); double i032 = identity(32.0);
+ double i033 = identity(33.0); double i034 = identity(34.0); double i035 = identity(35.0);
+ double i036 = identity(36.0); double i037 = identity(37.0); double i038 = identity(38.0);
+ double i039 = identity(39.0); double i040 = identity(40.0); double i041 = identity(41.0);
+ double i042 = identity(42.0); double i043 = identity(43.0); double i044 = identity(44.0);
+ double i045 = identity(45.0); double i046 = identity(46.0); double i047 = identity(47.0);
+ double i048 = identity(48.0); double i049 = identity(49.0); double i050 = identity(50.0);
+ double i051 = identity(51.0); double i052 = identity(52.0); double i053 = identity(53.0);
+ double i054 = identity(54.0); double i055 = identity(55.0); double i056 = identity(56.0);
+ double i057 = identity(57.0); double i058 = identity(58.0); double i059 = identity(59.0);
+ double i060 = identity(60.0); double i061 = identity(61.0); double i062 = identity(62.0);
+ double i063 = identity(63.0); double i064 = identity(64.0); double i065 = identity(65.0);
+ double i066 = identity(66.0); double i067 = identity(67.0); double i068 = identity(68.0);
+ double i069 = identity(69.0); double i070 = identity(70.0); double i071 = identity(71.0);
+ double i072 = identity(72.0); double i073 = identity(73.0); double i074 = identity(74.0);
+ double i075 = identity(75.0); double i076 = identity(76.0); double i077 = identity(77.0);
+ double i078 = identity(78.0); double i079 = identity(79.0); double i080 = identity(80.0);
+ double i081 = identity(81.0); double i082 = identity(82.0); double i083 = identity(83.0);
+ double i084 = identity(84.0); double i085 = identity(85.0); double i086 = identity(86.0);
+ double i087 = identity(87.0); double i088 = identity(88.0); double i089 = identity(89.0);
+ double i090 = identity(90.0); double i091 = identity(91.0); double i092 = identity(92.0);
+ double i093 = identity(93.0); double i094 = identity(94.0); double i095 = identity(95.0);
+ double i096 = identity(96.0); double i097 = identity(97.0); double i098 = identity(98.0);
+ double i099 = identity(99.0); double i100 = identity(100.0); double i101 = identity(101.0);
+ double i102 = identity(102.0); double i103 = identity(103.0); double i104 = identity(104.0);
+ double i105 = identity(105.0); double i106 = identity(106.0); double i107 = identity(107.0);
+ double i108 = identity(108.0); double i109 = identity(109.0); double i110 = identity(110.0);
+ double i111 = identity(111.0); double i112 = identity(112.0); double i113 = identity(113.0);
+ double i114 = identity(114.0); double i115 = identity(115.0); double i116 = identity(116.0);
+ double i117 = identity(117.0); double i118 = identity(118.0); double i119 = identity(119.0);
+ double i120 = identity(120.0); double i121 = identity(121.0); double i122 = identity(122.0);
+ double i123 = identity(123.0); double i124 = identity(124.0); double i125 = identity(125.0);
+ double i126 = identity(126.0); double i127 = identity(127.0); double i128 = identity(128.0);
+ double i129 = identity(129.0); double i130 = identity(130.0); double i131 = identity(131.0);
+ double i132 = identity(132.0); double i133 = identity(133.0); double i134 = identity(134.0);
+ double i135 = identity(135.0); double i136 = identity(136.0); double i137 = identity(137.0);
+ double i138 = identity(138.0); double i139 = identity(139.0); double i140 = identity(140.0);
+ double i141 = identity(141.0); double i142 = identity(142.0); double i143 = identity(143.0);
+ double i144 = identity(144.0); double i145 = identity(145.0); double i146 = identity(146.0);
+ double i147 = identity(147.0); double i148 = identity(148.0); double i149 = identity(149.0);
+ double i150 = identity(150.0); double i151 = identity(151.0); double i152 = identity(152.0);
+ double i153 = identity(153.0); double i154 = identity(154.0); double i155 = identity(155.0);
+ double i156 = identity(156.0); double i157 = identity(157.0); double i158 = identity(158.0);
+ double i159 = identity(159.0); double i160 = identity(160.0); double i161 = identity(161.0);
+ double i162 = identity(162.0); double i163 = identity(163.0); double i164 = identity(164.0);
+ double i165 = identity(165.0); double i166 = identity(166.0); double i167 = identity(167.0);
+ double i168 = identity(168.0); double i169 = identity(169.0); double i170 = identity(170.0);
+ double i171 = identity(171.0); double i172 = identity(172.0); double i173 = identity(173.0);
+ double i174 = identity(174.0); double i175 = identity(175.0); double i176 = identity(176.0);
+ double i177 = identity(177.0); double i178 = identity(178.0); double i179 = identity(179.0);
+ double i180 = identity(180.0); double i181 = identity(181.0); double i182 = identity(182.0);
+ double i183 = identity(183.0); double i184 = identity(184.0); double i185 = identity(185.0);
+ double i186 = identity(186.0); double i187 = identity(187.0); double i188 = identity(188.0);
+ double i189 = identity(189.0); double i190 = identity(190.0); double i191 = identity(191.0);
+ double i192 = identity(192.0); double i193 = identity(193.0); double i194 = identity(194.0);
+ double i195 = identity(195.0); double i196 = identity(196.0); double i197 = identity(197.0);
+ double i198 = identity(198.0); double i199 = identity(199.0); double i200 = identity(200.0);
+ double i201 = identity(201.0); double i202 = identity(202.0); double i203 = identity(203.0);
+ double i204 = identity(204.0); double i205 = identity(205.0); double i206 = identity(206.0);
+ double i207 = identity(207.0); double i208 = identity(208.0); double i209 = identity(209.0);
+ double i210 = identity(210.0); double i211 = identity(211.0); double i212 = identity(212.0);
+ double i213 = identity(213.0); double i214 = identity(214.0); double i215 = identity(215.0);
+ double i216 = identity(216.0); double i217 = identity(217.0); double i218 = identity(218.0);
+ double i219 = identity(219.0); double i220 = identity(220.0); double i221 = identity(221.0);
+ double i222 = identity(222.0); double i223 = identity(223.0); double i224 = identity(224.0);
+ double i225 = identity(225.0); double i226 = identity(226.0); double i227 = identity(227.0);
+ double i228 = identity(228.0); double i229 = identity(229.0); double i230 = identity(230.0);
+ double i231 = identity(231.0); double i232 = identity(232.0); double i233 = identity(233.0);
+ double i234 = identity(234.0); double i235 = identity(235.0); double i236 = identity(236.0);
+ double i237 = identity(237.0); double i238 = identity(238.0); double i239 = identity(239.0);
+ double i240 = identity(240.0); double i241 = identity(241.0); double i242 = identity(242.0);
+ double i243 = identity(243.0); double i244 = identity(244.0); double i245 = identity(245.0);
+ double i246 = identity(246.0); double i247 = identity(247.0); double i248 = identity(248.0);
+ double i249 = identity(249.0); double i250 = identity(250.0); double i251 = identity(251.0);
+ double i252 = identity(252.0); double i253 = identity(253.0); double i254 = identity(254.0);
+ double i255 = identity(255.0); double i256 = identity(256.0); double i257 = identity(257.0);
+ double i258 = identity(258.0); double i259 = identity(259.0);
+
+ double i = i259 + i259;
+ System.out.println("binaryOpUsingHighRegistersLocals " + i);
+
+ double j = i000 + i001 + i002 + i003 + i004 + i005 + i006 + i007 + i008 + i009 + i010 + i011 +
+ i012 + i013 + i014 + i015 + i016 + i017 + i018 + i019 + i020 + i021 + i022 + i023 +
+ i024 + i025 + i026 + i027 + i028 + i029 + i030 + i031 + i032 + i033 + i034 + i035 +
+ i036 + i037 + i038 + i039 + i040 + i041 + i042 + i043 + i044 + i045 + i046 + i047 +
+ i048 + i049 + i050 + i051 + i052 + i053 + i054 + i055 + i056 + i057 + i058 + i059 +
+ i060 + i061 + i062 + i063 + i064 + i065 + i066 + i067 + i068 + i069 + i070 + i071 +
+ i072 + i073 + i074 + i075 + i076 + i077 + i078 + i079 + i080 + i081 + i082 + i083 +
+ i084 + i085 + i086 + i087 + i088 + i089 + i090 + i091 + i092 + i093 + i094 + i095 +
+ i096 + i097 + i098 + i099 + i100 + i101 + i102 + i103 + i104 + i105 + i106 + i107 +
+ i108 + i109 + i110 + i111 + i112 + i113 + i114 + i115 + i116 + i117 + i118 + i119 +
+ i120 + i121 + i122 + i123 + i124 + i125 + i126 + i127 + i128 + i129 + i130 + i131 +
+ i132 + i133 + i134 + i135 + i136 + i137 + i138 + i139 + i140 + i141 + i142 + i143 +
+ i144 + i145 + i146 + i147 + i148 + i149 + i150 + i151 + i152 + i153 + i154 + i155 +
+ i156 + i157 + i158 + i159 + i160 + i161 + i162 + i163 + i164 + i165 + i166 + i167 +
+ i168 + i169 + i170 + i171 + i172 + i173 + i174 + i175 + i176 + i177 + i178 + i179 +
+ i180 + i181 + i182 + i183 + i184 + i185 + i186 + i187 + i188 + i189 + i190 + i191 +
+ i192 + i193 + i194 + i195 + i196 + i197 + i198 + i199 + i200 + i201 + i202 + i203 +
+ i204 + i205 + i206 + i207 + i208 + i209 + i210 + i211 + i212 + i213 + i214 + i215 +
+ i216 + i217 + i218 + i219 + i220 + i221 + i222 + i223 + i224 + i225 + i226 + i227 +
+ i228 + i229 + i230 + i231 + i232 + i233 + i234 + i235 + i236 + i237 + i238 + i239 +
+ i240 + i241 + i242 + i243 + i244 + i245 + i246 + i247 + i248 + i249 + i250 + i251 +
+ i252 + i253 + i254 + i255 + i256 + i257 + i258 + i259;
+ System.out.println("sum: " + j);
+ }
+
+ public static void main(String[] args) {
+ binaryOpUsingHighRegistersArguments(
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
+ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
+ 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
+ 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203,
+ 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215,
+ 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227,
+ 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
+ 252, 253, 254);
+ binaryDoubleOpUsingHighRegistersArguments(
+ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,
+ 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0,
+ 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0,
+ 30.0, 31.0, 32.0, 33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0,
+ 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 48.0, 49.0,
+ 50.0, 51.0, 52.0, 53.0, 54.0, 55.0, 56.0, 57.0, 58.0, 59.0,
+ 60.0, 61.0, 62.0, 63.0, 64.0, 65.0, 66.0, 67.0, 68.0, 69.0,
+ 70.0, 71.0, 72.0, 73.0, 74.0, 75.0, 76.0, 77.0, 78.0, 79.0,
+ 80.0, 81.0, 82.0, 83.0, 84.0, 85.0, 86.0, 87.0, 88.0, 89.0,
+ 90.0, 91.0, 92.0, 93.0, 94.0, 95.0, 96.0, 97.0, 98.0, 99.0,
+ 100.0, 101.0, 102.0, 103.0, 104.0, 105.0, 106.0, 107.0, 108.0, 109.0,
+ 110.0, 111.0, 112.0, 113.0, 114.0, 115.0, 116.0, 117.0, 118.0, 119.0,
+ 120.0, 121.0, 122.0, 123.0, 124.0, 125.0, 126.0);
+ binaryOpUsingHighRegistersLocals();
+ binaryDoubleOpUsingHighRegistersLocals();
+ }
+}
diff --git a/src/test/examples/regress/Regress.java b/src/test/examples/regress/Regress.java
new file mode 100644
index 0000000..4b931b6
--- /dev/null
+++ b/src/test/examples/regress/Regress.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'arithmetic.dex' is what is run.
+
+package regress;
+
+public class Regress {
+
+ static void b33336471_int_float() {
+ int i = 0;
+ float f0 = 0.0f;
+ float f1 = 1.0f;
+ for (int j = i; j < 2; j++) {
+ System.out.println("LOOP");
+ }
+ float f[] = new float[1];
+ if (f[0] != f0) {
+ System.out.println("FLOAT COMPARISON FAILED");
+ }
+ if (f[0] == f1) {
+ System.out.println("FLOAT COMPARISON FAILED");
+ }
+ }
+
+ static void b33336471_long_double() {
+ long i = 0;
+ double d0 = 0.0f;
+ double d1 = 1.0f;
+ for (long j = i; j < 2; j++) {
+ System.out.println("LOOP");
+ }
+ double d[] = new double[1];
+ if (d[0] != d0) {
+ System.out.println("DOUBLE COMPARISON FAILED");
+ }
+ if (d[0] == d1) {
+ System.out.println("DOUBLE COMPARISON FAILED");
+ }
+ }
+
+ public static void main(String[] args) {
+ b33336471_int_float();
+ b33336471_long_double();
+ }
+}
diff --git a/src/test/examples/regress2/Regress2.java b/src/test/examples/regress2/Regress2.java
new file mode 100644
index 0000000..151a520
--- /dev/null
+++ b/src/test/examples/regress2/Regress2.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package regress2;
+
+public class Regress2 {
+
+ static class X {
+
+ void add() {
+ }
+ }
+
+ static private boolean test() {
+ X x = null;
+ X y = null;
+
+ int a = 5;
+ System.out.println("START");
+ while (a-- > 0) {
+ System.out.println("LOOP");
+ int b = 0;
+ switch (b) {
+ case 1:
+ X current = new X();
+ if (x == null) {
+ x = current;
+ } else {
+ x = null;
+ }
+ y.add();
+ break;
+ case 2:
+ if (x != null) {
+ x = null;
+ }
+ y.add();
+ break;
+ }
+ }
+ System.out.println("END");
+ return true;
+ }
+
+ public static void main(String[] args) {
+ test();
+ }
+}
diff --git a/src/test/examples/regress_37658666/Regress.java b/src/test/examples/regress_37658666/Regress.java
new file mode 100644
index 0000000..4f6019d
--- /dev/null
+++ b/src/test/examples/regress_37658666/Regress.java
@@ -0,0 +1,26 @@
+// 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 regress_37658666;
+
+class Float {
+ int cmp(float a, float b) {
+ if (a > b)
+ return 1;
+ if (a == b)
+ return 0;
+ return -1;
+ }
+}
+
+public class Regress {
+
+ public static boolean check(int i, int j) {
+ return i == j;
+ }
+
+ public static void main(String[] args) {
+ Float f = new Float();
+ System.out.println(check(0, f.cmp(+0f, -0f)));
+ }
+}
\ No newline at end of file
diff --git a/src/test/examples/regress_37726195/Regress.java b/src/test/examples/regress_37726195/Regress.java
new file mode 100644
index 0000000..ad9781a
--- /dev/null
+++ b/src/test/examples/regress_37726195/Regress.java
@@ -0,0 +1,42 @@
+// 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 regress_37726195;
+
+public class Regress {
+
+ // Regression test for issue where aput instructions for different primitive array types
+ // were joined. The art verifier doesn't allow that.
+ public static void set(Object array, int index, byte value) {
+ if (array instanceof float[]) {
+ float[] floats = (float[]) array;
+ floats[index] = value;
+ } else if (array instanceof int[]) {
+ int[] ints = (int[]) array;
+ ints[index] = value;
+ }
+ }
+
+ // Regression test for issue where aget instructions for different primitive array types
+ // were joined. The art verifier doesn't allow that.
+ public static void get(Object array, int index) {
+ if (array instanceof float[]) {
+ float[] floats = (float[]) array;
+ float f = floats[index];
+ } else if (array instanceof int[]) {
+ int[] ints = (int[]) array;
+ int i = ints[index];
+ }
+ }
+
+ public static void main(String[] args) {
+ int[] ints = { 0 };
+ float[] floats = { 0.0f };
+ set(ints, 0, (byte) 4);
+ System.out.println(ints[0]);
+ set(floats, 0, (byte) 4);
+ System.out.println(floats[0]);
+ get(ints, 0);
+ get(floats, 0);
+ }
+}
diff --git a/src/test/examples/regress_37875803/Regress.java b/src/test/examples/regress_37875803/Regress.java
new file mode 100644
index 0000000..9360046
--- /dev/null
+++ b/src/test/examples/regress_37875803/Regress.java
@@ -0,0 +1,70 @@
+// 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 regress_37875803;
+
+public class Regress {
+
+ private static int digitSub(int codePoint) {
+ int result = -888;
+ if ('a' <= codePoint && codePoint <= 'z') {
+ result = 10 + (codePoint - 'a');
+ } else if ('A' <= codePoint && codePoint <= 'Z') {
+ result = 10 + (codePoint - 'A');
+ }
+ if (result < 0) {
+ throw new RuntimeException("codePoint = " + codePoint + " result = " + result);
+ }
+ return result;
+ }
+
+ private static int digitSubLeft(int codePoint) {
+ int result = -888;
+ if ('a' <= codePoint && codePoint <= 'z') {
+ result = 10 + ('a' - codePoint);
+ } else if ('A' <= codePoint && codePoint <= 'Z') {
+ result = 10 + ('A' - codePoint);
+ }
+ if (result < 0) {
+ throw new RuntimeException("codePoint = " + codePoint + " result = " + result);
+ }
+ return result;
+ }
+
+ private static int digitAdd(int codePoint) {
+ int result = -888;
+ if ('a' <= codePoint && codePoint <= 'z') {
+ result = 10 + (codePoint + 'a');
+ } else if ('A' <= codePoint && codePoint <= 'Z') {
+ result = 10 + (codePoint + 'A');
+ }
+ if (result < 0) {
+ throw new RuntimeException("codePoint = " + codePoint + " result = " + result);
+ }
+ return result;
+ }
+
+ private static int digitOr(int codePoint) {
+ int result = -888;
+ if ('a' <= codePoint && codePoint <= 'z') {
+ result = 10 + (codePoint | 'a');
+ } else if ('A' <= codePoint && codePoint <= 'Z') {
+ result = 10 + (codePoint | 'A');
+ }
+ if (result < 0) {
+ throw new RuntimeException("codePoint = " + codePoint + " result = " + result);
+ }
+ return result;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(digitSub((int) 'a'));
+ System.out.println(digitSub((int) 'A'));
+ System.out.println(digitSubLeft((int) 'a'));
+ System.out.println(digitSubLeft((int) 'A'));
+ System.out.println(digitAdd((int) 'a'));
+ System.out.println(digitAdd((int) 'A'));
+ System.out.println(digitOr((int) 'a'));
+ System.out.println(digitOr((int) 'A'));
+ }
+}
diff --git a/src/test/examples/regress_37955340/Regress.java b/src/test/examples/regress_37955340/Regress.java
new file mode 100644
index 0000000..a8b5e0b
--- /dev/null
+++ b/src/test/examples/regress_37955340/Regress.java
@@ -0,0 +1,26 @@
+// 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 regress_37955340;
+
+public class Regress {
+
+ static class A {
+
+ int x(int a) {
+ return a;
+ }
+ }
+
+ static class B extends A {
+
+ int x(int a) {
+ return 2;
+ }
+ }
+
+ public static void main(String[] args) {
+ A a = new B();
+ System.out.println(a.x(1));
+ }
+}
diff --git a/src/test/examples/returns/Returns.java b/src/test/examples/returns/Returns.java
new file mode 100644
index 0000000..c496ce5
--- /dev/null
+++ b/src/test/examples/returns/Returns.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package returns;
+
+public class Returns {
+ public static void main(String[] args) {
+ doubleJunk(); // Ignored to get pop2
+ longJunk();
+ longFloat();
+ }
+
+ private static double doubleJunk() {
+ return 42.42;
+ }
+
+ private static long longJunk() {
+ long billion5 = 5000000000L;
+ return billion5;
+ }
+
+ private static float longFloat() {
+ float foobar = 1.0F;
+ return foobar;
+ }
+}
diff --git a/src/test/examples/rewrite/LongCompare.java b/src/test/examples/rewrite/LongCompare.java
new file mode 100644
index 0000000..7c96c10
--- /dev/null
+++ b/src/test/examples/rewrite/LongCompare.java
@@ -0,0 +1,40 @@
+// 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 rewrite;
+
+public class LongCompare {
+
+ public static int simpleCompare(long l1, long l2) {
+ try {
+ return Long.compare(l1, l2);
+ } catch (Throwable t) {
+ System.out.println(t);
+ }
+ return 2;
+ }
+
+ public static long getValue1() {
+ return 123456789L;
+ }
+
+ public static long getValue2() {
+ return 0;
+ }
+
+ public static boolean complexCompare(long l1, long l2) {
+ return Long.compare(getValue1(), l1) == 0 && Long.compare(l2, getValue2()) > 0;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(simpleCompare(123456789L, 987654321L));
+ System.out.println(simpleCompare(Long.MAX_VALUE, 0L));
+ System.out.println(simpleCompare(Long.MIN_VALUE, 0L));
+ System.out.println(simpleCompare(Long.MAX_VALUE, Long.MAX_VALUE));
+
+ System.out.println(complexCompare(123456789L, 1));
+ System.out.println(complexCompare(123456789L, -1));
+ System.out.println(complexCompare(1234567890L, 1));
+ System.out.println(complexCompare(1234567890L, -1));
+ }
+}
diff --git a/src/test/examples/rewrite/RequireNonNull.java b/src/test/examples/rewrite/RequireNonNull.java
new file mode 100644
index 0000000..d8868bf
--- /dev/null
+++ b/src/test/examples/rewrite/RequireNonNull.java
@@ -0,0 +1,39 @@
+// 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 rewrite;
+
+import java.util.Objects;
+
+public class RequireNonNull {
+
+ public static void main(String[] args) {
+ RequireNonNull o = new RequireNonNull();
+ System.out.println(o.nonnullRemove().toString());
+ System.out.println(o.nonnullRemove(o).toString());
+ o.nonnullWithPhiInstruction(true, o);
+ o.nonnullWithPhiInstruction(false, o);
+ }
+
+ private Object nonnullRemove() {
+ return Objects.requireNonNull(this);
+ }
+
+ private Object nonnullRemove(Object o) {
+ Objects.requireNonNull(o);
+ return o;
+ }
+
+ private void nonnullWithPhiInstruction(boolean b, Object input) {
+ Object o = null;
+ if (b) {
+ o = Objects.requireNonNull(input);
+ }
+ System.out.println(o);
+ }
+
+ @Override
+ public String toString() {
+ return "toString";
+ }
+}
diff --git a/src/test/examples/shaking1/Shaking.java b/src/test/examples/shaking1/Shaking.java
new file mode 100644
index 0000000..8c39ab8
--- /dev/null
+++ b/src/test/examples/shaking1/Shaking.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking1;
+
+public class Shaking {
+ public static void main(String[] args) {
+ System.out.println(new Used("world").method());
+ }
+}
diff --git a/src/test/examples/shaking1/Unused.java b/src/test/examples/shaking1/Unused.java
new file mode 100644
index 0000000..fd6fe40
--- /dev/null
+++ b/src/test/examples/shaking1/Unused.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking1;
+
+public class Unused {
+
+ public static int aStaticFieldThatIsNotUsedButKept = 42;
+
+ public static void unusedStaticMethod() {
+ System.out.println("unusedStaticMethod");
+ }
+
+ public void unusedInstanceMethod() {
+ System.out.println("unusedInstanceMethod");
+ }
+
+ public String aMethodThatIsNotUsedButKept() {
+ return "Unused string";
+ }
+}
diff --git a/src/test/examples/shaking1/Used.java b/src/test/examples/shaking1/Used.java
new file mode 100644
index 0000000..23450ab
--- /dev/null
+++ b/src/test/examples/shaking1/Used.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking1;
+
+public class Used {
+
+ public static int aStaticFieldThatIsNotUsedButKept = 42;
+
+ private final String name;
+
+ public Used(String name) {
+ this.name = name;
+ }
+
+ public String method() {
+ return "Hello, " + name;
+ }
+
+ public String aMethodThatIsNotUsedButKept() {
+ return "Unused string";
+ }
+}
diff --git a/src/test/examples/shaking1/keep-rules-dont-shrink.txt b/src/test/examples/shaking1/keep-rules-dont-shrink.txt
new file mode 100644
index 0000000..6edaafc
--- /dev/null
+++ b/src/test/examples/shaking1/keep-rules-dont-shrink.txt
@@ -0,0 +1,7 @@
+# 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.
+
+-dontshrink
+
+@keep-rules.txt
\ No newline at end of file
diff --git a/src/test/examples/shaking1/keep-rules-repackaging.txt b/src/test/examples/shaking1/keep-rules-repackaging.txt
new file mode 100644
index 0000000..b5c2194
--- /dev/null
+++ b/src/test/examples/shaking1/keep-rules-repackaging.txt
@@ -0,0 +1,21 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking1.Shaking {
+ public static void main(...);
+}
+
+# Keep two members if class is live
+-keepclassmembers class * {
+ public static int aStaticFieldThatIsNotUsedButKept;
+ public java.lang.String aMethodThatIsNotUsedButKept();
+}
+
+# allow access modification to enable minification
+-allowaccessmodification
+
+# change package of renamed classes
+-repackageclasses 'repackaged'
diff --git a/src/test/examples/shaking1/keep-rules.txt b/src/test/examples/shaking1/keep-rules.txt
new file mode 100644
index 0000000..66cf1c6
--- /dev/null
+++ b/src/test/examples/shaking1/keep-rules.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking1.Shaking {
+ public static void main(...);
+}
+
+# Keep two members if class is live
+-keepclassmembers class * {
+ public static int aStaticFieldThatIsNotUsedButKept;
+ public java.lang.String aMethodThatIsNotUsedButKept();
+}
+
+# allow access modification to enable minification
+-allowaccessmodification
diff --git a/src/test/examples/shaking1/print-mapping.ref b/src/test/examples/shaking1/print-mapping.ref
new file mode 100644
index 0000000..8382304
--- /dev/null
+++ b/src/test/examples/shaking1/print-mapping.ref
@@ -0,0 +1,4 @@
+shaking1.Shaking -> shaking1.Shaking:
+shaking1.Used -> shaking1.a:
+ java.lang.String name -> a
+ java.lang.String method() -> a
diff --git a/src/test/examples/shaking10/HasAFooImpl.java b/src/test/examples/shaking10/HasAFooImpl.java
new file mode 100644
index 0000000..d975b26
--- /dev/null
+++ b/src/test/examples/shaking10/HasAFooImpl.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking10;
+
+import shakinglib.HasAFoo;
+import shakinglib.HasAGetter;
+
+public class HasAFooImpl implements HasAFoo {
+
+ @Override
+ public HasAGetter foo() {
+ return new ReturnsOne();
+ }
+
+ public int bar() {
+ return 0;
+ }
+
+ private static class ReturnsOne implements HasAGetter {
+
+ @Override
+ public int getIt() {
+ return 1;
+ }
+ }
+}
diff --git a/src/test/examples/shaking10/Shaking.java b/src/test/examples/shaking10/Shaking.java
new file mode 100644
index 0000000..0737ec8
--- /dev/null
+++ b/src/test/examples/shaking10/Shaking.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking10;
+
+import shakinglib.IndirectMethodInvoker;
+
+public class Shaking {
+
+ public static void main(String[] args) {
+ HasAFooImpl impl = new HasAFooImpl();
+ IndirectMethodInvoker invoker = new IndirectMethodInvoker();
+ System.out.println(invoker.invokeFoo(impl));
+ }
+}
diff --git a/src/test/examples/shaking10/keep-rules.txt b/src/test/examples/shaking10/keep-rules.txt
new file mode 100644
index 0000000..0895849
--- /dev/null
+++ b/src/test/examples/shaking10/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking10.Shaking {
+ public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking11/Shaking.java b/src/test/examples/shaking11/Shaking.java
new file mode 100644
index 0000000..6dd396f
--- /dev/null
+++ b/src/test/examples/shaking11/Shaking.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking11;
+
+public class Shaking {
+
+ public static void main(String[] args) {
+ }
+}
diff --git a/src/test/examples/shaking11/Subclass.java b/src/test/examples/shaking11/Subclass.java
new file mode 100644
index 0000000..e6b2973
--- /dev/null
+++ b/src/test/examples/shaking11/Subclass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking11;
+
+public class Subclass extends SuperClass {
+ // Intentionally empty.
+}
diff --git a/src/test/examples/shaking11/SubclassWithMethod.java b/src/test/examples/shaking11/SubclassWithMethod.java
new file mode 100644
index 0000000..073d352
--- /dev/null
+++ b/src/test/examples/shaking11/SubclassWithMethod.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking11;
+
+public class SubclassWithMethod extends SuperClass {
+
+ public void aMethod() {
+ // Intentionally empty.
+ }
+}
diff --git a/src/test/examples/shaking11/SuperClass.java b/src/test/examples/shaking11/SuperClass.java
new file mode 100644
index 0000000..1c0fc8f
--- /dev/null
+++ b/src/test/examples/shaking11/SuperClass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking11;
+
+public class SuperClass {
+
+ public void aMethod() {
+ // Intentionally empty.
+ }
+}
diff --git a/src/test/examples/shaking11/keep-rules-keep-method.txt b/src/test/examples/shaking11/keep-rules-keep-method.txt
new file mode 100644
index 0000000..8b65519
--- /dev/null
+++ b/src/test/examples/shaking11/keep-rules-keep-method.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking11.Shaking {
+ public static void main(...);
+}
+
+-keep class shaking11.SuperClass {}
+
+-keep class shaking11.Sub* {
+ public void aMethod();
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking11/keep-rules.txt b/src/test/examples/shaking11/keep-rules.txt
new file mode 100644
index 0000000..0a58c01
--- /dev/null
+++ b/src/test/examples/shaking11/keep-rules.txt
@@ -0,0 +1,16 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking11.Shaking {
+ public static void main(...);
+}
+
+-keepclasseswithmembers class * {
+ public void aMethod();
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking12/AnimalClass.java b/src/test/examples/shaking12/AnimalClass.java
new file mode 100644
index 0000000..87557b3
--- /dev/null
+++ b/src/test/examples/shaking12/AnimalClass.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking12;
+
+public class AnimalClass extends Named {
+
+ @Override
+ public String getName() {
+ return "AnimalClass";
+ }
+}
diff --git a/src/test/examples/shaking12/MetaphorClass.java b/src/test/examples/shaking12/MetaphorClass.java
new file mode 100644
index 0000000..3192368
--- /dev/null
+++ b/src/test/examples/shaking12/MetaphorClass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking12;
+
+public class MetaphorClass extends Named {
+
+ public String getName() {
+ return "MetaphorClass";
+ }
+}
diff --git a/src/test/examples/shaking12/Named.java b/src/test/examples/shaking12/Named.java
new file mode 100644
index 0000000..b829050
--- /dev/null
+++ b/src/test/examples/shaking12/Named.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking12;
+
+public abstract class Named {
+
+ public abstract String getName();
+}
diff --git a/src/test/examples/shaking12/PeopleClass.java b/src/test/examples/shaking12/PeopleClass.java
new file mode 100644
index 0000000..24c1f3e
--- /dev/null
+++ b/src/test/examples/shaking12/PeopleClass.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking12;
+
+public class PeopleClass extends Named {
+
+ @Override
+ public String getName() {
+ return "PeopleClass";
+ }
+}
diff --git a/src/test/examples/shaking12/Shaking.java b/src/test/examples/shaking12/Shaking.java
new file mode 100644
index 0000000..3d1c40e
--- /dev/null
+++ b/src/test/examples/shaking12/Shaking.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking12;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class Shaking {
+
+ static private Named createInstance(Class<? extends Named> aClass) {
+ try {
+ return aClass.newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ return new Named() {
+ @Override
+ public String getName() {
+ return "Unknown";
+ }
+ };
+ }
+
+ public static void main(String[] args) {
+ List<Class<? extends Named>> classes = new ArrayList<>(3);
+ classes.add(MetaphorClass.class);
+ classes.add(PeopleClass.class);
+ classes.add(ThingClass.class);
+ Iterator<Class<? extends Named>> iterator = classes.iterator();
+ iterator.next();
+ while (iterator.hasNext()) {
+ Named item = createInstance(iterator.next());
+ if (item instanceof AnimalClass) {
+ System.out.println("An animal!");
+ }
+ System.out.println(createInstance(iterator.next()).getName());
+ }
+ }
+}
diff --git a/src/test/examples/shaking12/ThingClass.java b/src/test/examples/shaking12/ThingClass.java
new file mode 100644
index 0000000..554b87b
--- /dev/null
+++ b/src/test/examples/shaking12/ThingClass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking12;
+
+public class ThingClass extends Named {
+
+ public String getName() {
+ return "ThingClass";
+ }
+}
diff --git a/src/test/examples/shaking12/keep-rules.txt b/src/test/examples/shaking12/keep-rules.txt
new file mode 100644
index 0000000..6a5e277
--- /dev/null
+++ b/src/test/examples/shaking12/keep-rules.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking12.Shaking {
+ public static void main(...);
+}
+
+# Keep the constructors that are used via newInstance.
+-keep public class shaking12.ThingClass,shaking12.PeopleClass {
+ <init>(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
\ No newline at end of file
diff --git a/src/test/examples/shaking13/AClassWithFields.java b/src/test/examples/shaking13/AClassWithFields.java
new file mode 100644
index 0000000..6a4052b
--- /dev/null
+++ b/src/test/examples/shaking13/AClassWithFields.java
@@ -0,0 +1,10 @@
+// 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 shaking13;
+
+public class AClassWithFields {
+ public static int staticIntField;
+ public int intField;
+
+}
diff --git a/src/test/examples/shaking13/Shaking.java b/src/test/examples/shaking13/Shaking.java
new file mode 100644
index 0000000..36e615f
--- /dev/null
+++ b/src/test/examples/shaking13/Shaking.java
@@ -0,0 +1,23 @@
+// 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 shaking13;
+
+import shakinglib.LibraryClass;
+
+public class Shaking {
+
+ private static void fieldTest() {
+ AClassWithFields instance = new AClassWithFields();
+ instance.intField = 1;
+ AClassWithFields.staticIntField = 2;
+
+ LibraryClass.staticIntField = 3;
+ LibraryClass libraryInstance = new LibraryClass();
+ libraryInstance.intField = 4;
+ }
+
+ public static void main(String[] args) {
+ fieldTest();
+ }
+}
diff --git a/src/test/examples/shaking13/keep-rules.txt b/src/test/examples/shaking13/keep-rules.txt
new file mode 100644
index 0000000..531e320
--- /dev/null
+++ b/src/test/examples/shaking13/keep-rules.txt
@@ -0,0 +1,12 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking13.Shaking {
+ public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
\ No newline at end of file
diff --git a/src/test/examples/shaking14/Shaking.java b/src/test/examples/shaking14/Shaking.java
new file mode 100644
index 0000000..b12bd70
--- /dev/null
+++ b/src/test/examples/shaking14/Shaking.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking14;
+
+public class Shaking {
+
+ public static void main(String[] args) {
+ SubSubClass instance = new SubSubClass();
+ System.out.println(Subclass.aMethod(99));
+ System.out.println(instance.anotherMethod(100));
+ }
+}
diff --git a/src/test/examples/shaking14/SubSubClass.java b/src/test/examples/shaking14/SubSubClass.java
new file mode 100644
index 0000000..c06ebb2
--- /dev/null
+++ b/src/test/examples/shaking14/SubSubClass.java
@@ -0,0 +1,8 @@
+// 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 shaking14;
+
+public class SubSubClass extends Subclass {
+ // Intentionally left empty.
+}
diff --git a/src/test/examples/shaking14/Subclass.java b/src/test/examples/shaking14/Subclass.java
new file mode 100644
index 0000000..31dd650
--- /dev/null
+++ b/src/test/examples/shaking14/Subclass.java
@@ -0,0 +1,14 @@
+// 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 shaking14;
+
+public class Subclass extends Superclass {
+ static int aMethod(int value) {
+ return value + 42;
+ }
+
+ static double anotherMethod(double value) {
+ return value + 42;
+ }
+}
diff --git a/src/test/examples/shaking14/Superclass.java b/src/test/examples/shaking14/Superclass.java
new file mode 100644
index 0000000..9dafff3
--- /dev/null
+++ b/src/test/examples/shaking14/Superclass.java
@@ -0,0 +1,14 @@
+// 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 shaking14;
+
+public class Superclass {
+ static int aMethod(int value) {
+ return value;
+ }
+
+ static double anotherMethod(double value) {
+ return value;
+ }
+}
diff --git a/src/test/examples/shaking14/keep-rules.txt b/src/test/examples/shaking14/keep-rules.txt
new file mode 100644
index 0000000..94682a3
--- /dev/null
+++ b/src/test/examples/shaking14/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking14.Shaking {
+ public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking15/Shaking.java b/src/test/examples/shaking15/Shaking.java
new file mode 100644
index 0000000..4d81efd
--- /dev/null
+++ b/src/test/examples/shaking15/Shaking.java
@@ -0,0 +1,12 @@
+// 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 shaking15;
+
+public class Shaking {
+
+ public static void main(String[] args) {
+ Subclass thing = new Subclass();
+ thing.callingSuper();
+ }
+}
diff --git a/src/test/examples/shaking15/Subclass.java b/src/test/examples/shaking15/Subclass.java
new file mode 100644
index 0000000..16b9f1a
--- /dev/null
+++ b/src/test/examples/shaking15/Subclass.java
@@ -0,0 +1,20 @@
+// 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 shaking15;
+
+public class Subclass extends Superclass {
+ public int a,b,c;
+
+ @Override
+ public void aMethod() {
+ System.out.println("Called aMethod in Subclass");
+ }
+
+ @Override
+ public void callingSuper() {
+ System.out.println("Called callingSuper in Subclass");
+ super.callingSuper();
+ super.aMethod();
+ }
+}
diff --git a/src/test/examples/shaking15/Superclass.java b/src/test/examples/shaking15/Superclass.java
new file mode 100644
index 0000000..5e46622
--- /dev/null
+++ b/src/test/examples/shaking15/Superclass.java
@@ -0,0 +1,15 @@
+// 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 shaking15;
+
+public class Superclass {
+
+ public void aMethod() {
+ System.out.println("Called aMethod in Superclass");
+ }
+
+ public void callingSuper() {
+ System.out.println("Called callingSuper in Superclass");
+ }
+}
diff --git a/src/test/examples/shaking15/dictionary b/src/test/examples/shaking15/dictionary
new file mode 100644
index 0000000..b7c9f2f
--- /dev/null
+++ b/src/test/examples/shaking15/dictionary
@@ -0,0 +1,14 @@
+# this is a comment
+abc# this is another comment
+def # this is again another comment after an ignored character
+ghi
+jkl
+mno pqr
+stu!vw$[
+
+/*-+&~"'
+xyz_
+
+ ea
+1abc
+!
\ No newline at end of file
diff --git a/src/test/examples/shaking15/keep-rules.txt b/src/test/examples/shaking15/keep-rules.txt
new file mode 100644
index 0000000..70948bb
--- /dev/null
+++ b/src/test/examples/shaking15/keep-rules.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking15.Shaking {
+ public static void main(...);
+}
+-dontshrink
+
+# allow access modification to enable minification
+-allowaccessmodification
+
+# use dictionaries
+-obfuscationdictionary dictionary
+-classobfuscationdictionary dictionary
+-packageobfuscationdictionary dictionary
diff --git a/src/test/examples/shaking15/sub/A.java b/src/test/examples/shaking15/sub/A.java
new file mode 100644
index 0000000..25e4d7a
--- /dev/null
+++ b/src/test/examples/shaking15/sub/A.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+package shaking15.sub;
+
+public class A {
+
+}
diff --git a/src/test/examples/shaking16/Shaking.java b/src/test/examples/shaking16/Shaking.java
new file mode 100644
index 0000000..7de3ebc
--- /dev/null
+++ b/src/test/examples/shaking16/Shaking.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking16;
+
+public class Shaking {
+
+ public static void main(String[] args) {
+ a thing = new a(3, 5);
+ thing.a();
+ thing.b();
+ }
+}
diff --git a/src/test/examples/shaking16/a.java b/src/test/examples/shaking16/a.java
new file mode 100644
index 0000000..3a6a4ec
--- /dev/null
+++ b/src/test/examples/shaking16/a.java
@@ -0,0 +1,28 @@
+// 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 shaking16;
+
+public class a extends b {
+
+ public int a;
+ public int b;
+
+ public a(int a, int b) {
+ super(a -1, b + 1);
+ this.a = a;
+ this.b = b;
+ }
+
+ @Override
+ public void a() {
+ System.out.println("Called a in Subclass " + a + ", " + b);
+ }
+
+ @Override
+ public void b() {
+ System.out.println("Called b in Subclass");
+ super.b();
+ super.a();
+ }
+}
diff --git a/src/test/examples/shaking16/b.java b/src/test/examples/shaking16/b.java
new file mode 100644
index 0000000..7ded44f
--- /dev/null
+++ b/src/test/examples/shaking16/b.java
@@ -0,0 +1,23 @@
+// 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 shaking16;
+
+public class b {
+
+ public int a;
+ public int b;
+
+ public b(int a, int b) {
+ this.a = a;
+ this.b = b;
+ }
+
+ public void a() {
+ System.out.println("Called a in Superclass" + a + ", " + b);
+ }
+
+ public void b() {
+ System.out.println("Called b in Superclass");
+ }
+}
diff --git a/src/test/examples/shaking16/keep-rules-1.txt b/src/test/examples/shaking16/keep-rules-1.txt
new file mode 100644
index 0000000..8043cb2
--- /dev/null
+++ b/src/test/examples/shaking16/keep-rules-1.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking16.Shaking {
+ public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking16/keep-rules-2.txt b/src/test/examples/shaking16/keep-rules-2.txt
new file mode 100644
index 0000000..f11c3ad
--- /dev/null
+++ b/src/test/examples/shaking16/keep-rules-2.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking16.Shaking {
+ public static void main(...);
+}
+
+-keep public class shaking16.a {
+ int a;
+ void a();
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking2/Interface.java b/src/test/examples/shaking2/Interface.java
new file mode 100644
index 0000000..3862b05
--- /dev/null
+++ b/src/test/examples/shaking2/Interface.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public interface Interface extends SuperInterface1, SuperInterface2 {
+ public void unusedInterfaceMethod();
+ public void interfaceMethod();
+ public void interfaceMethod4();
+ public void interfaceMethod5(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8);
+}
diff --git a/src/test/examples/shaking2/Shaking.java b/src/test/examples/shaking2/Shaking.java
new file mode 100644
index 0000000..f99fd8c
--- /dev/null
+++ b/src/test/examples/shaking2/Shaking.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public class Shaking {
+ public static void callInterfaceMethod(Interface object) {
+ object.interfaceMethod4();
+ object.interfaceMethod5(1, 2, 3, 4, 5, 6, 7, 8);
+ }
+
+ public static void callAsSuperClassAndInterface(SuperClass object) {
+ object.interfaceMethod();
+ object.interfaceMethod2();
+ object.interfaceMethod3();
+ object.virtualMethod();
+ object.virtualMethod2(1, 2, 3, 4, 5, 6, 7, 8);
+ object.accessFields();
+ callInterfaceMethod(object);
+ }
+
+ public static void accessStaticFields() {
+ System.out.println("StaticFields::used: " + StaticFields.used);
+ System.out.println("StaitcFields::read" +
+ " " + StaticFields.readInt +
+ " " + StaticFields.readBoolean+
+ " " + StaticFields.readByte +
+ " " + StaticFields.readChar +
+ " " + StaticFields.readObject +
+ " " + StaticFields.readShort +
+ " " + StaticFields.readDouble);
+ StaticFields.writeInt = 1;
+ StaticFields.writeBoolean = true;
+ StaticFields.writeByte = 2;
+ StaticFields.writeChar = 3;
+ StaticFields.writeObject = new Object();
+ StaticFields.writeShort = 3;
+ StaticFields.writeDouble = 3.3;
+ }
+
+ public static void main(String[] args) {
+ accessStaticFields();
+ SuperClass.staticMethod();
+ SuperClass.staticMethod2(1, 2, 3, 4, 5, 6, 7, 8);
+ SubClass1 instance1 = new SubClass1(1);
+ callAsSuperClassAndInterface(instance1);
+ instance1.virtualMethod3();
+ instance1.virtualMethod4(1, 2, 3, 4, 5, 6, 7, 8);
+ callAsSuperClassAndInterface(new SubClass1(1, 2, 3, 4, 5, 6, 7, 8));
+ SubClass2 instance2 = new SubClass2(2);
+ callAsSuperClassAndInterface(instance2);
+ instance2.virtualMethod3();
+ instance2.virtualMethod4(1, 2, 3, 4, 5, 6, 7, 8);
+ }
+}
diff --git a/src/test/examples/shaking2/StaticFields.java b/src/test/examples/shaking2/StaticFields.java
new file mode 100644
index 0000000..80bf6ba
--- /dev/null
+++ b/src/test/examples/shaking2/StaticFields.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public class StaticFields {
+ public static int used = 42;
+ // Unused but initialized by <clinit>.
+ public static int unused = -42;
+ // Not even used by <clinit>.
+ public static int completelyUnused;
+
+ public static int readInt;
+ public static int writeInt;
+ public static boolean readBoolean;
+ public static boolean writeBoolean;
+ public static byte readByte;
+ public static byte writeByte;
+ public static char readChar;
+ public static char writeChar;
+ public static Object readObject;
+ public static Object writeObject;
+ public static short readShort;
+ public static short writeShort;
+ public static double readDouble;
+ public static double writeDouble;
+}
\ No newline at end of file
diff --git a/src/test/examples/shaking2/SubClass1.java b/src/test/examples/shaking2/SubClass1.java
new file mode 100644
index 0000000..722c6e0
--- /dev/null
+++ b/src/test/examples/shaking2/SubClass1.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public class SubClass1 extends SuperClass {
+ private int used;
+ private int alsoUsed;
+ private boolean usedBoolean;
+ private byte usedByte;
+ private char usedChar;
+ private Object usedObject;
+ private short usedShort;
+ private double usedDouble;
+
+ private int unused;
+
+ public SubClass1(int used) {
+ this.used = used;
+ }
+
+ public SubClass1(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+ this.used = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8;
+ }
+
+ @Override
+ public void virtualMethod() {
+ System.out.println("SubClass1::virtualMethod");
+ }
+
+ @Override
+ public void virtualMethod2(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+ System.out.println("SubClass1::virtualMethod2 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+ }
+
+ @Override
+ public void virtualMethod3() {
+ super.virtualMethod3();
+ System.out.println("SubClass1::virtualMethod3");
+ }
+
+ @Override
+ public void virtualMethod4(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+ super.virtualMethod4(a1, a2, a3, a4, a5, a6, a7, a8);
+ System.out.println("SubClass1::virtualMethod4 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+ }
+
+ @Override
+ public void unusedVirtualMethod() {
+ System.out.println("SubClass1::unusedVirtualMethod");
+ }
+
+ @Override
+ public void interfaceMethod() {
+ System.out.println("SubClass1::interfaceMethod");
+ }
+
+ @Override
+ public void interfaceMethod2() {
+ System.out.println("SubClass1::interfaceMethod2");
+ }
+
+ @Override
+ public void interfaceMethod3() {
+ System.out.println("SubClass1::interfaceMethod3");
+ }
+
+ @Override
+ public void interfaceMethod4() {
+ System.out.println("SubClass1::interfaceMethod4");
+ }
+
+ @Override
+ public void interfaceMethod5(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+ System.out.println("SubClass1::interfaceMethod5 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+ }
+
+ @Override
+ public void accessFields() {
+ super.accessFields();
+ System.out.println("SubClass1::fields: " + used + " " + alsoUsed + " " + usedBoolean +
+ " " + usedByte + " " + usedChar + " " + usedObject + " " + usedShort + " " + usedDouble);
+ used = 1;
+ alsoUsed = 2;
+ usedBoolean = true;
+ usedByte = 3;
+ usedChar = '?';
+ usedObject = new Object();
+ usedShort = 4;
+ usedDouble = 42.42;
+ }
+}
diff --git a/src/test/examples/shaking2/SubClass2.java b/src/test/examples/shaking2/SubClass2.java
new file mode 100644
index 0000000..6a0dcb2
--- /dev/null
+++ b/src/test/examples/shaking2/SubClass2.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public class SubClass2 extends SuperClass {
+ private int used;
+ private int unused;
+ private int unusedToo;
+
+ public SubClass2(int used) {
+ this.used = used;
+ }
+
+ @Override
+ public void virtualMethod() {
+ System.out.println("SubClass2::virtualMethod");
+ }
+
+ @Override
+ public void virtualMethod2(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+ System.out.println("SubClass2::virtualMethod2 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+ }
+
+ @Override
+ public void virtualMethod3() {
+ super.virtualMethod3();
+ System.out.println("SubClass2::virtualMethod3");
+ }
+
+ @Override
+ public void virtualMethod4(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+ super.virtualMethod4(a1, a2, a3, a4, a5, a6, a7, a8);
+ System.out.println("SubClass2::virtualMethod4 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+ }
+
+ @Override
+ public void interfaceMethod() {
+ System.out.println("SubClass2::interfaceMethod");
+ }
+
+ @Override
+ public void interfaceMethod2() {
+ System.out.println("SubClass2::interfaceMethod2");
+ }
+
+ @Override
+ public void interfaceMethod3() {
+ System.out.println("SubClass2::interfaceMethod3");
+ }
+
+ @Override
+ public void interfaceMethod4() {
+ System.out.println("SubClass2::interfaceMethod4");
+ }
+
+ @Override
+ public void interfaceMethod5(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+ System.out.println("SubClass1::interfaceMethod5 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+ }
+}
diff --git a/src/test/examples/shaking2/SuperClass.java b/src/test/examples/shaking2/SuperClass.java
new file mode 100644
index 0000000..f382671
--- /dev/null
+++ b/src/test/examples/shaking2/SuperClass.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public abstract class SuperClass implements Interface {
+ public int used;
+
+ public static void staticMethod() {
+ System.out.println("SuperClass::staticMethod");
+ }
+
+ public static void staticMethod2(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+ System.out.println("SuperClass::staticMethod2: " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+ }
+
+ public static void unusedStaticMethod() {
+ System.out.println("SuperClass::unusedStaticMethod");
+ }
+
+ public void virtualMethod() {
+ System.out.println("SuperClass::virtualMethod");
+ }
+
+ public void virtualMethod2(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+ System.out.println("SuperClass::virtualMethod2 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+ }
+
+ public void virtualMethod3() {
+ System.out.println("SuperClass::virtualMethod3");
+ }
+
+ public void virtualMethod4(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+ System.out.println("SuperClass::virtualMethod4 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+ }
+
+ public void unusedVirtualMethod() {
+ System.out.println("SuperClass::unusedVirtualMethod");
+ }
+
+ public void unusedInterfaceMethod() { System.out.println("SuperClass::unusedInterfaceMethod"); }
+
+ public void accessFields() {
+ System.out.println("SuperClass::fields: " + used);
+ }
+}
diff --git a/src/test/examples/shaking2/SuperInterface1.java b/src/test/examples/shaking2/SuperInterface1.java
new file mode 100644
index 0000000..7ec5d4e
--- /dev/null
+++ b/src/test/examples/shaking2/SuperInterface1.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public interface SuperInterface1 {
+ public void interfaceMethod2();
+}
diff --git a/src/test/examples/shaking2/SuperInterface2.java b/src/test/examples/shaking2/SuperInterface2.java
new file mode 100644
index 0000000..f9a1ff7
--- /dev/null
+++ b/src/test/examples/shaking2/SuperInterface2.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public interface SuperInterface2 extends SuperInterface1 {
+ public void interfaceMethod3();
+}
diff --git a/src/test/examples/shaking2/UnusedSubclass.java b/src/test/examples/shaking2/UnusedSubclass.java
new file mode 100644
index 0000000..400ec64
--- /dev/null
+++ b/src/test/examples/shaking2/UnusedSubclass.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public class UnusedSubclass extends SuperClass {
+ private int used;
+ private int unused;
+
+ public UnusedSubclass(int used) {
+ this.used = used;
+ }
+
+ @Override
+ public void virtualMethod() {
+ System.out.println("UnusedSubclass::virtualMethod");
+ }
+
+ @Override
+ public void interfaceMethod() {
+ System.out.println("UnusedSubclass::interfaceMethod");
+ }
+
+ @Override
+ public void interfaceMethod2() {
+ System.out.println("UnusedSubclass::interfaceMethod2");
+ }
+
+ @Override
+ public void interfaceMethod3() {
+ System.out.println("UnusedSubclass::interfaceMethod3");
+ }
+
+ @Override
+ public void interfaceMethod4() {
+ System.out.println("UnusedSubclass::interfaceMethod4");
+ }
+
+ @Override
+ public void interfaceMethod5(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+ System.out.println("SubClass1::interfaceMethod5 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+ }
+}
diff --git a/src/test/examples/shaking2/keep-rules-dont-shrink.txt b/src/test/examples/shaking2/keep-rules-dont-shrink.txt
new file mode 100644
index 0000000..6edaafc
--- /dev/null
+++ b/src/test/examples/shaking2/keep-rules-dont-shrink.txt
@@ -0,0 +1,7 @@
+# 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.
+
+-dontshrink
+
+@keep-rules.txt
\ No newline at end of file
diff --git a/src/test/examples/shaking2/keep-rules.txt b/src/test/examples/shaking2/keep-rules.txt
new file mode 100644
index 0000000..e05f735
--- /dev/null
+++ b/src/test/examples/shaking2/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking2.Shaking {
+ public static void main(java.lang.String[]);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking3/A.java b/src/test/examples/shaking3/A.java
new file mode 100644
index 0000000..45413a6
--- /dev/null
+++ b/src/test/examples/shaking3/A.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+@shaking3.UsedByReflection
+class A implements Comparable<A>, AnInterfaceWithATag {
+
+ @shaking3.RandomTag
+ public A() {
+ // Intentionally left empty.
+ }
+
+ @Override
+ public String toString() {
+ return "A";
+ }
+
+ public void unused() { }
+
+ @Override
+ public int compareTo(A other) {
+ if (other == this) {
+ return 0;
+ }
+ return 1;
+ }
+}
diff --git a/src/test/examples/shaking3/AnAbstractClass.java b/src/test/examples/shaking3/AnAbstractClass.java
new file mode 100644
index 0000000..e38501e
--- /dev/null
+++ b/src/test/examples/shaking3/AnAbstractClass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+abstract public class AnAbstractClass {
+
+ public void withAMethod() {
+
+ }
+}
diff --git a/src/test/examples/shaking3/AnInterfaceWithATag.java b/src/test/examples/shaking3/AnInterfaceWithATag.java
new file mode 100644
index 0000000..4072be1
--- /dev/null
+++ b/src/test/examples/shaking3/AnInterfaceWithATag.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+@SubtypeUsedByReflection
+public interface AnInterfaceWithATag {
+
+}
diff --git a/src/test/examples/shaking3/B.java b/src/test/examples/shaking3/B.java
new file mode 100644
index 0000000..011f8a6
--- /dev/null
+++ b/src/test/examples/shaking3/B.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+@RandomTag
+public class B {
+
+ public AnAbstractClass notVeryInteresting() {
+ return null;
+ }
+
+}
diff --git a/src/test/examples/shaking3/RandomTag.java b/src/test/examples/shaking3/RandomTag.java
new file mode 100644
index 0000000..2a33f91
--- /dev/null
+++ b/src/test/examples/shaking3/RandomTag.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+@interface RandomTag {
+
+ Class type() default void.class;
+}
diff --git a/src/test/examples/shaking3/Shaking.java b/src/test/examples/shaking3/Shaking.java
new file mode 100644
index 0000000..76f6b4a
--- /dev/null
+++ b/src/test/examples/shaking3/Shaking.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+public class Shaking {
+
+ public static void main(String[] args)
+ throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+ Class t = Class.forName("shaking3.A");
+ Object object = t.newInstance();
+ System.out.println(object);
+ }
+}
diff --git a/src/test/examples/shaking3/SubtypeUsedByReflection.java b/src/test/examples/shaking3/SubtypeUsedByReflection.java
new file mode 100644
index 0000000..10db4bc
--- /dev/null
+++ b/src/test/examples/shaking3/SubtypeUsedByReflection.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+public @interface SubtypeUsedByReflection {
+
+}
diff --git a/src/test/examples/shaking3/UsedByReflection.java b/src/test/examples/shaking3/UsedByReflection.java
new file mode 100644
index 0000000..65ec5b0
--- /dev/null
+++ b/src/test/examples/shaking3/UsedByReflection.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+@interface UsedByReflection { }
diff --git a/src/test/examples/shaking3/keep-by-tag-default.txt b/src/test/examples/shaking3/keep-by-tag-default.txt
new file mode 100644
index 0000000..857e4e5
--- /dev/null
+++ b/src/test/examples/shaking3/keep-by-tag-default.txt
@@ -0,0 +1,13 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point.
+-keep public class shaking3.Shaking {
+ public static void main(...);
+}
+
+-keep @shaking3.UsedByReflection class *
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking3/keep-by-tag-on-method.txt b/src/test/examples/shaking3/keep-by-tag-on-method.txt
new file mode 100644
index 0000000..39f2674
--- /dev/null
+++ b/src/test/examples/shaking3/keep-by-tag-on-method.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point.
+-keep public class shaking3.Shaking {
+ public static void main(...);
+}
+
+-keepclasseswithmembers class ** {
+ @shaking3.RandomTag <init>(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking3/keep-by-tag-via-interface.txt b/src/test/examples/shaking3/keep-by-tag-via-interface.txt
new file mode 100644
index 0000000..6202a25
--- /dev/null
+++ b/src/test/examples/shaking3/keep-by-tag-via-interface.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point.
+-keep public class shaking3.Shaking {
+ public static void main(...);
+}
+
+-keep class ** implements @shaking3.SubtypeUsedByReflection ** {
+ <init>(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking3/keep-by-tag-with-pattern.txt b/src/test/examples/shaking3/keep-by-tag-with-pattern.txt
new file mode 100644
index 0000000..d880bf2
--- /dev/null
+++ b/src/test/examples/shaking3/keep-by-tag-with-pattern.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point.
+-keep public class shaking3.Shaking {
+ public static void main(...);
+}
+
+-keep @*.UsedByReflection class ** {
+ <init>(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking3/keep-by-tag.txt b/src/test/examples/shaking3/keep-by-tag.txt
new file mode 100644
index 0000000..08b5862
--- /dev/null
+++ b/src/test/examples/shaking3/keep-by-tag.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point.
+-keep public class shaking3.Shaking {
+ public static void main(...);
+}
+
+-keep @shaking3.UsedByReflection class ** {
+ <init>(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking3/keep-no-abstract-classes.txt b/src/test/examples/shaking3/keep-no-abstract-classes.txt
new file mode 100644
index 0000000..6412247
--- /dev/null
+++ b/src/test/examples/shaking3/keep-no-abstract-classes.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point.
+-keep public class shaking3.Shaking {
+ public static void main(...);
+}
+
+-keep !abstract class ** {
+ <init>(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking4/A.java b/src/test/examples/shaking4/A.java
new file mode 100644
index 0000000..15926dc
--- /dev/null
+++ b/src/test/examples/shaking4/A.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking4;
+
+public class A extends Superclass implements Interface {
+}
diff --git a/src/test/examples/shaking4/Interface.java b/src/test/examples/shaking4/Interface.java
new file mode 100644
index 0000000..4dd904c
--- /dev/null
+++ b/src/test/examples/shaking4/Interface.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking4;
+
+public interface Interface {
+ public void virtualMethod();
+}
diff --git a/src/test/examples/shaking4/Shaking.java b/src/test/examples/shaking4/Shaking.java
new file mode 100644
index 0000000..cc8fa35
--- /dev/null
+++ b/src/test/examples/shaking4/Shaking.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking4;
+
+public class Shaking {
+ public static void main(String[] args) {
+ A a = new A();
+ a.virtualMethod();
+ }
+}
diff --git a/src/test/examples/shaking4/Superclass.java b/src/test/examples/shaking4/Superclass.java
new file mode 100644
index 0000000..31f7537
--- /dev/null
+++ b/src/test/examples/shaking4/Superclass.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking4;
+
+public class Superclass {
+ public void virtualMethod() {
+ System.out.println("virtualMethod");
+ }
+}
diff --git a/src/test/examples/shaking4/keep-rules-dont-shrink.txt b/src/test/examples/shaking4/keep-rules-dont-shrink.txt
new file mode 100644
index 0000000..6edaafc
--- /dev/null
+++ b/src/test/examples/shaking4/keep-rules-dont-shrink.txt
@@ -0,0 +1,7 @@
+# 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.
+
+-dontshrink
+
+@keep-rules.txt
\ No newline at end of file
diff --git a/src/test/examples/shaking4/keep-rules.txt b/src/test/examples/shaking4/keep-rules.txt
new file mode 100644
index 0000000..64953e2
--- /dev/null
+++ b/src/test/examples/shaking4/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking4.Shaking {
+ public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking5/A.java b/src/test/examples/shaking5/A.java
new file mode 100644
index 0000000..5a9a738
--- /dev/null
+++ b/src/test/examples/shaking5/A.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking5;
+
+public class A extends Superclass {
+
+ public void virtualMethod() {
+ System.out.println("virtualMethod");
+ }
+}
diff --git a/src/test/examples/shaking5/Shaking.java b/src/test/examples/shaking5/Shaking.java
new file mode 100644
index 0000000..06c797d
--- /dev/null
+++ b/src/test/examples/shaking5/Shaking.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking5;
+
+public class Shaking {
+
+ public static void main(String[] args) {
+ A a = new A();
+ a.virtualMethod();
+ }
+}
diff --git a/src/test/examples/shaking5/Superclass.java b/src/test/examples/shaking5/Superclass.java
new file mode 100644
index 0000000..6807967
--- /dev/null
+++ b/src/test/examples/shaking5/Superclass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking5;
+
+public class Superclass {
+
+ private void virtualMethod() {
+ System.out.println("private virtualMethod");
+ }
+}
diff --git a/src/test/examples/shaking5/keep-rules.txt b/src/test/examples/shaking5/keep-rules.txt
new file mode 100644
index 0000000..616b03e
--- /dev/null
+++ b/src/test/examples/shaking5/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking5.Shaking {
+ public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking6/Shaking.java b/src/test/examples/shaking6/Shaking.java
new file mode 100644
index 0000000..bc0b827
--- /dev/null
+++ b/src/test/examples/shaking6/Shaking.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking6;
+
+public class Shaking {
+
+ public static void main(String[] args) {
+ }
+}
diff --git a/src/test/examples/shaking6/Subclass.java b/src/test/examples/shaking6/Subclass.java
new file mode 100644
index 0000000..cc193ba
--- /dev/null
+++ b/src/test/examples/shaking6/Subclass.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking6;
+
+public class Subclass extends Superclass {
+
+ public void publicMethod() {
+ // Intentionally empty.
+ }
+
+ private void privateMethod() {
+ // Intentionally empty.
+ }
+
+ // Public method with same name as private method in superclass.
+ public void justAMethod() {
+ // Intentionally empty.
+ }
+
+ public void justAMethod(int ignore) {
+ // Intentionally empty.
+ }
+
+ public void justAMethod(boolean ignore) {
+ // Intentionally empty.
+ }
+
+ public int justAMethod(double ignore) {
+ // Intentionally empty.
+ return 0;
+ }
+
+ final void aFinalMethod() {
+ // Intentionally empty.
+ }
+}
diff --git a/src/test/examples/shaking6/Superclass.java b/src/test/examples/shaking6/Superclass.java
new file mode 100644
index 0000000..b7fefb7
--- /dev/null
+++ b/src/test/examples/shaking6/Superclass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking6;
+
+public class Superclass {
+
+ private void justAMethod() {
+ // Intentionally empty
+ }
+}
diff --git a/src/test/examples/shaking6/keep-justAMethod-OnInt.txt b/src/test/examples/shaking6/keep-justAMethod-OnInt.txt
new file mode 100644
index 0000000..2ff630a
--- /dev/null
+++ b/src/test/examples/shaking6/keep-justAMethod-OnInt.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking6.Shaking {
+ public static void main(...);
+}
+
+# Also keep the int justAMethod
+-keep public class ** {
+ void justAMethod(int);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking6/keep-justAMethod-public.txt b/src/test/examples/shaking6/keep-justAMethod-public.txt
new file mode 100644
index 0000000..8d910f7
--- /dev/null
+++ b/src/test/examples/shaking6/keep-justAMethod-public.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking6.Shaking {
+ public static void main(...);
+}
+
+# Also keep the public justAMethod
+-keep public class ** {
+ public void justAMethod(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking6/keep-non-public.txt b/src/test/examples/shaking6/keep-non-public.txt
new file mode 100644
index 0000000..cc1316a
--- /dev/null
+++ b/src/test/examples/shaking6/keep-non-public.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep everything non-public and main
+-keep public class ** {
+ !public *;
+}
+
+-keep public class shaking6.Shaking {
+ public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking6/keep-public.txt b/src/test/examples/shaking6/keep-public.txt
new file mode 100644
index 0000000..a4f1ea6
--- /dev/null
+++ b/src/test/examples/shaking6/keep-public.txt
@@ -0,0 +1,11 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep everything public
+-keep public class ** {
+ public *;
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking7/Liar.java b/src/test/examples/shaking7/Liar.java
new file mode 100644
index 0000000..bad63a5
--- /dev/null
+++ b/src/test/examples/shaking7/Liar.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking7;
+
+public class Liar extends Superclass {
+
+ public int theDoubleField;
+
+ public double theIntField;
+}
diff --git a/src/test/examples/shaking7/Shaking.java b/src/test/examples/shaking7/Shaking.java
new file mode 100644
index 0000000..8701ad7
--- /dev/null
+++ b/src/test/examples/shaking7/Shaking.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking7;
+
+public class Shaking {
+
+ public static void main(String[] args) {
+ }
+}
diff --git a/src/test/examples/shaking7/Subclass.java b/src/test/examples/shaking7/Subclass.java
new file mode 100644
index 0000000..492acd7
--- /dev/null
+++ b/src/test/examples/shaking7/Subclass.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking7;
+
+public class Subclass extends Superclass {
+
+ public int theIntField = 4;
+
+ public double theDoubleField = 2.0;
+
+ public String toString() {
+ return "Subclass";
+ }
+}
diff --git a/src/test/examples/shaking7/Superclass.java b/src/test/examples/shaking7/Superclass.java
new file mode 100644
index 0000000..a1027c5
--- /dev/null
+++ b/src/test/examples/shaking7/Superclass.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking7;
+
+public class Superclass {
+
+ private int theIntField = 42;
+
+ public final double theDoubleField = 1.0;
+
+ public String toString() {
+ return "Superclass";
+ }
+}
diff --git a/src/test/examples/shaking7/keep-double-fields.txt b/src/test/examples/shaking7/keep-double-fields.txt
new file mode 100644
index 0000000..813bce9
--- /dev/null
+++ b/src/test/examples/shaking7/keep-double-fields.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking7.Shaking {
+ public static void main(...);
+}
+
+# Also keep double fields (named theDoubleField, there is no wildcard on names...)
+-keep public class ** {
+ double theDoubleField;
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking7/keep-public-fields.txt b/src/test/examples/shaking7/keep-public-fields.txt
new file mode 100644
index 0000000..c5e8af3
--- /dev/null
+++ b/src/test/examples/shaking7/keep-public-fields.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking7.Shaking {
+ public static void main(...);
+}
+
+# Also keep public fields
+-keep public class ** {
+ public <fields>;
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking7/keep-public-theDoubleField-fields.txt b/src/test/examples/shaking7/keep-public-theDoubleField-fields.txt
new file mode 100644
index 0000000..941217b
--- /dev/null
+++ b/src/test/examples/shaking7/keep-public-theDoubleField-fields.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking7.Shaking {
+ public static void main(...);
+}
+
+# Also keep all public fields named theDoubleField
+-keep public class ** {
+ public *** theDoubleField;
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking7/keep-public-theIntField-fields.txt b/src/test/examples/shaking7/keep-public-theIntField-fields.txt
new file mode 100644
index 0000000..8b93c59
--- /dev/null
+++ b/src/test/examples/shaking7/keep-public-theIntField-fields.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking7.Shaking {
+ public static void main(...);
+}
+
+# Also keep all public fields named theIntField
+-keep public class ** {
+ public *** theIntField;
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking8/OtherThing.java b/src/test/examples/shaking8/OtherThing.java
new file mode 100644
index 0000000..053501b
--- /dev/null
+++ b/src/test/examples/shaking8/OtherThing.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking8;
+
+public class OtherThing {
+
+ private final int otherField;
+
+ public OtherThing(int otherField) {
+ this.otherField = otherField;
+ }
+
+ @Override
+ public String toString() {
+ return "OtherThing(" + otherField + ")";
+ }
+}
diff --git a/src/test/examples/shaking8/Shaking.java b/src/test/examples/shaking8/Shaking.java
new file mode 100644
index 0000000..2c2d700
--- /dev/null
+++ b/src/test/examples/shaking8/Shaking.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking8;
+
+import java.util.Arrays;
+
+public class Shaking {
+
+ public static void main(String[] args) {
+ Thing[] empty = new Thing[0];
+ OtherThing[] one = {new OtherThing(1)};
+ callCloneOnArray(null);
+ System.out.println(Arrays.toString(empty));
+ System.out.println(Arrays.toString(one));
+ }
+
+ private static void callCloneOnArray(YetAnotherThing[] array) {
+ if (array != null) {
+ array.clone();
+ }
+ }
+}
diff --git a/src/test/examples/shaking8/Thing.java b/src/test/examples/shaking8/Thing.java
new file mode 100644
index 0000000..9b8f013
--- /dev/null
+++ b/src/test/examples/shaking8/Thing.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking8;
+
+public class Thing {
+
+ private final int aField;
+
+ public Thing(int aField) {
+ this.aField = aField;
+ }
+
+ @Override
+ public String toString() {
+ return "Thing(" + aField + ")";
+ }
+}
diff --git a/src/test/examples/shaking8/YetAnotherThing.java b/src/test/examples/shaking8/YetAnotherThing.java
new file mode 100644
index 0000000..ad3df45
--- /dev/null
+++ b/src/test/examples/shaking8/YetAnotherThing.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking8;
+
+public class YetAnotherThing {
+
+ private final int anInt;
+
+ public YetAnotherThing(int anInt) {
+ this.anInt = anInt;
+ }
+
+ @Override
+ public String toString() {
+ return "YetAnotherThing(" + anInt + ")";
+ }
+}
diff --git a/src/test/examples/shaking8/keep-rules.txt b/src/test/examples/shaking8/keep-rules.txt
new file mode 100644
index 0000000..a97f896
--- /dev/null
+++ b/src/test/examples/shaking8/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking8.Shaking {
+ public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking9/Shaking.java b/src/test/examples/shaking9/Shaking.java
new file mode 100644
index 0000000..578dda2
--- /dev/null
+++ b/src/test/examples/shaking9/Shaking.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking9;
+
+public class Shaking {
+
+ public static void main(String[] args) {
+ Subclass thing = new Subclass();
+ thing.callingSuper();
+ }
+}
diff --git a/src/test/examples/shaking9/Subclass.java b/src/test/examples/shaking9/Subclass.java
new file mode 100644
index 0000000..7c1e809
--- /dev/null
+++ b/src/test/examples/shaking9/Subclass.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking9;
+
+public class Subclass extends Superclass {
+
+ @Override
+ public void aMethod() {
+ System.out.println("Called aMethod in Subclass");
+ }
+
+ @Override
+ public void callingSuper() {
+ System.out.println("Called callingSuper in Subclass");
+ super.callingSuper();
+ super.aMethod();
+ }
+}
diff --git a/src/test/examples/shaking9/Superclass.java b/src/test/examples/shaking9/Superclass.java
new file mode 100644
index 0000000..90041ee
--- /dev/null
+++ b/src/test/examples/shaking9/Superclass.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking9;
+
+public class Superclass {
+
+ public void aMethod() {
+ System.out.println("Called aMethod in Superclass");
+ }
+
+ public void callingSuper() {
+ System.out.println("Called callingSuper in Superclass");
+ }
+}
diff --git a/src/test/examples/shaking9/keep-rules.txt b/src/test/examples/shaking9/keep-rules.txt
new file mode 100644
index 0000000..e8b0919
--- /dev/null
+++ b/src/test/examples/shaking9/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking9.Shaking {
+ public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shakinglib/HasAFoo.java b/src/test/examples/shakinglib/HasAFoo.java
new file mode 100644
index 0000000..077c86f
--- /dev/null
+++ b/src/test/examples/shakinglib/HasAFoo.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shakinglib;
+
+public interface HasAFoo {
+
+ HasAGetter foo();
+}
diff --git a/src/test/examples/shakinglib/HasAGetter.java b/src/test/examples/shakinglib/HasAGetter.java
new file mode 100644
index 0000000..9195b76
--- /dev/null
+++ b/src/test/examples/shakinglib/HasAGetter.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shakinglib;
+
+public interface HasAGetter {
+
+ int getIt();
+}
diff --git a/src/test/examples/shakinglib/IndirectMethodInvoker.java b/src/test/examples/shakinglib/IndirectMethodInvoker.java
new file mode 100644
index 0000000..1ec7415
--- /dev/null
+++ b/src/test/examples/shakinglib/IndirectMethodInvoker.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shakinglib;
+
+public class IndirectMethodInvoker {
+
+ public int invokeFoo(HasAFoo thing) {
+ return thing.foo().getIt();
+ }
+}
diff --git a/src/test/examples/shakinglib/LibraryClass.java b/src/test/examples/shakinglib/LibraryClass.java
new file mode 100644
index 0000000..f50bdf4
--- /dev/null
+++ b/src/test/examples/shakinglib/LibraryClass.java
@@ -0,0 +1,10 @@
+// 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 shakinglib;
+
+public class LibraryClass {
+ public static int staticIntField;
+
+ public int intField;
+}
diff --git a/src/test/examples/staticfield/StaticField.java b/src/test/examples/staticfield/StaticField.java
new file mode 100644
index 0000000..f4d5854
--- /dev/null
+++ b/src/test/examples/staticfield/StaticField.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package staticfield;
+
+public class StaticField {
+
+ // Final static initialized fields, out of order, in dex these must be sorted by field idx.
+ public static final String fieldB = "B";
+ public static final String fieldC = "C";
+ public static final String fieldA = "A";
+
+ public static StaticField field = null;
+
+ private int x;
+
+ public StaticField(int x) {
+ this.x = x;
+ }
+
+ @Override
+ public String toString() {
+ return "" + x;
+ }
+
+ public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
+ StaticField value = new StaticField(101010);
+ StaticField.field = value;
+ System.out.println(StaticField.field);
+ System.out.println(value.field);
+
+ System.out.print(StaticField.fieldA);
+ System.out.print(StaticField.fieldB);
+ System.out.println(StaticField.fieldC);
+
+ // Check that we can access the same static final value via the class object.
+ System.out.print(StaticField.class.getField("fieldA").get(value));
+ System.out.print(StaticField.class.getField("fieldB").get(value));
+ System.out.println(StaticField.class.getField("fieldC").get(value));
+ }
+}
diff --git a/src/test/examples/stringbuilding/StringBuilding.java b/src/test/examples/stringbuilding/StringBuilding.java
new file mode 100644
index 0000000..18152fb
--- /dev/null
+++ b/src/test/examples/stringbuilding/StringBuilding.java
@@ -0,0 +1,66 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'stringbuilding.dex' is what is run.
+
+package stringbuilding;
+
+class StringBuilding {
+
+ static class X {
+
+ public String toString() {
+ return "an X";
+ }
+ }
+
+ public static void main(String[] args) {
+ StringBuilder builder = new StringBuilder();
+ buildWithStatements(builder);
+ buildWithExpressions(builder);
+ System.out.print(builder);
+ System.out.println(buildWithConcat());
+ System.out.print(buildWithAllAppendSignatures());
+ }
+
+ private static void buildWithStatements(StringBuilder builder) {
+ builder.append("a");
+ builder.append(2);
+ builder.append("c");
+ builder.append("-");
+ }
+
+ private static void buildWithExpressions(StringBuilder builder) {
+ builder.append("x").append('y').append("z").append("-");
+ }
+
+ private static String buildWithConcat() {
+ return "a" + "b" + "c" + someValue() + "x" + "y" + "z";
+ }
+
+ private static String buildWithAllAppendSignatures() {
+ CharSequence seq = "1234";
+ StringBuilder builder = new StringBuilder();
+ builder
+ .append(true)
+ .append('A')
+ .append(new char[]{'B', 'C'})
+ .append(new char[]{'C', 'D', 'E', 'F'}, 1, 2)
+ .append(seq)
+ .append(seq, 1, 3)
+ .append(2.2)
+ .append(1.1f)
+ .append(0)
+ .append(1L)
+ .append(new X())
+ .append("string")
+ .append(new StringBuilder("builder"));
+ return builder.toString();
+ }
+
+ private static int someValue() {
+ return 7;
+ }
+}
\ No newline at end of file
diff --git a/src/test/examples/switches/Switches.java b/src/test/examples/switches/Switches.java
new file mode 100644
index 0000000..588e541
--- /dev/null
+++ b/src/test/examples/switches/Switches.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'switches.dex' is what is run.
+
+package switches;
+
+class Switches {
+
+ public static void packedSwitch(int value) {
+ System.out.print("packedSwitch cases: ");
+ switch (value) {
+ case 0:
+ System.out.print("0 ");
+ case 1:
+ case 2:
+ System.out.print("1 2 ");
+ break;
+ case 3:
+ System.out.print("3 ");
+ break;
+ }
+ System.out.println("after switch " + value);
+ }
+
+ public static void sparseSwitch(int value) {
+ switch (value) {
+ case 0:
+ System.out.println("0 ");
+ case 100:
+ System.out.println("100 ");
+ break;
+ case 200:
+ System.out.println("200 ");
+ break;
+ }
+ System.out.println("after switch " + value);
+ }
+
+ public static void switchWithLocals(int value) {
+ switch (value) {
+ case 0: {
+ int i = 42;
+ System.out.println(" " + i + value);
+ break;
+ }
+ case 2: {
+ double d = 1.0;
+ System.out.println(" " + d + value);
+ break;
+ }
+ }
+ }
+
+ public static void maybePackedSwitch(int value) {
+ switch (value) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ System.out.print("0-21 ");
+ break;
+ case 60:
+ System.out.print("60 ");
+ break;
+ }
+ System.out.println("after switch " + value);
+ }
+
+ public static void main(String[] args) {
+ packedSwitch(0);
+ packedSwitch(1);
+ packedSwitch(2);
+ packedSwitch(-1); // No such case, use fallthrough.
+ sparseSwitch(0);
+ sparseSwitch(100);
+ sparseSwitch(200);
+ sparseSwitch(-1); // No such case, use fallthrough.
+ switchWithLocals(0);
+ switchWithLocals(2);
+ maybePackedSwitch(1);
+ maybePackedSwitch(10);
+ maybePackedSwitch(40); // Fallthrough.
+ maybePackedSwitch(60);
+ }
+}
diff --git a/src/test/examples/sync/Sync.java b/src/test/examples/sync/Sync.java
new file mode 100644
index 0000000..7459d0e
--- /dev/null
+++ b/src/test/examples/sync/Sync.java
@@ -0,0 +1,157 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package sync;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+public class Sync {
+
+ public static final int THREADS = 10;
+ public static final int ITERATIONS = 10;
+
+ private static final int INITIAL_SHARED_STATE = -1;
+ private static final long SLEEP = 10;
+
+ // Shared mutable state that is tested to be consistent
+ private static int sharedState = INITIAL_SHARED_STATE;
+ private static boolean shouldThrow = false;
+
+ // Copy of interface java.util.function.Consumer to make this test work without a Java 8 runtime
+ // library
+ public interface Consumer<T> {
+
+ void accept(T t);
+ }
+
+ public static void couldThrow(int index) {
+ if (shouldThrow) throw new RuntimeException();
+ // Copy shared state and trash it (we set our index).
+ int local = sharedState;
+ sharedState = index;
+ try {
+ Thread.sleep(SLEEP);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ // Restore the shared state if it is still valid.
+ if (sharedState == index) {
+ sharedState = local;
+ }
+ }
+
+ public static synchronized void staticSynchronized(int index) {
+ System.out.println("static");
+ couldThrow(index);
+ System.out.println("end");
+ }
+
+ public synchronized void instanceSynchronized(int index) {
+ System.out.println("instance");
+ couldThrow(index);
+ System.out.println("end");
+ }
+
+ public void manualSynchronized(int index) {
+ System.out.println("manual");
+ synchronized (this) {
+ couldThrow(index);
+ }
+ System.out.println("manual");
+ }
+
+ public synchronized void tryCatchSynchronized(int index) {
+ System.out.println("trycatch");
+ try {
+ couldThrow(index);
+ try {
+ couldThrow(index);
+ } finally {
+ System.out.println("end");
+ return;
+ }
+ } catch (RuntimeException e) {
+ System.out.println("caught & end");
+ return;
+ } catch (Throwable e) {
+ System.out.println("caught other");
+ }
+ System.out.println("end");
+ }
+
+ public static synchronized void throwStaticSynchronized() {
+ throw new RuntimeException();
+ }
+
+ public synchronized void throwInstanceSynchronized() {
+ throw new RuntimeException();
+ }
+
+ public static void run(ExecutorService service, final Consumer<Integer> fn)
+ throws ExecutionException, InterruptedException {
+ Future[] results = new Future[ITERATIONS];
+ for (int i = 0; i < ITERATIONS; ++i) {
+ final int index = i;
+ results[i] = service.submit(new Runnable() {
+ @Override
+ public void run() {
+ fn.accept(index);
+ }
+ });
+ }
+ for (Future result : results) {
+ result.get();
+ }
+ if (sharedState != INITIAL_SHARED_STATE) {
+ throw new RuntimeException("Synchronization error!");
+ }
+ }
+
+ public static void main(String[] args) throws ExecutionException, InterruptedException {
+ shouldThrow = args.length > 100;
+ ExecutorService service = Executors.newFixedThreadPool(THREADS);
+ run(service, new Consumer<Integer>() {
+ @Override
+ public void accept(Integer index) {
+ Sync.staticSynchronized(index);
+ }
+ });
+ final Sync sync = new Sync();
+ run(service, new Consumer<Integer>() {
+ @Override
+ public void accept(Integer index) {
+ sync.instanceSynchronized(index);
+ }
+ });
+ run(service, new Consumer<Integer>() {
+ @Override
+ public void accept(Integer index) {
+ sync.manualSynchronized(index);
+ }
+ });
+ run(service, new Consumer<Integer>() {
+ @Override
+ public void accept(Integer index) {
+ sync.tryCatchSynchronized(index);
+ }
+ });
+ service.shutdown();
+ service.awaitTermination(5, TimeUnit.SECONDS);
+ try {
+ Sync.throwStaticSynchronized();
+ throw new Error("expected throw");
+ } catch (RuntimeException e) {
+ System.out.println("caught throw");
+ }
+ try {
+ sync.throwInstanceSynchronized();
+ throw new Error("expected throw");
+ } catch (RuntimeException e) {
+ System.out.println("caught throw");
+ }
+ }
+}
diff --git a/src/test/examples/throwing/Overloaded.java b/src/test/examples/throwing/Overloaded.java
new file mode 100644
index 0000000..44b5435
--- /dev/null
+++ b/src/test/examples/throwing/Overloaded.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package throwing;
+
+import java.util.List;
+
+public abstract class Overloaded {
+
+ public int aMethod(int x) {
+ return 0;
+ }
+
+ public int conflictingMethod(int x) {
+ return 0;
+ }
+
+ public abstract int bMethod(double x);
+
+ public int conflictingMethod(double x) {
+ return 0;
+ }
+
+ public int cMethod(boolean x) {
+ return 0;
+ }
+
+ public int conflictingMethod(boolean x) {
+ return 0;
+ }
+
+ public int anotherConflict(boolean x) {
+ return 0;
+ }
+
+ public int unique(List x) {
+ return 0;
+ }
+}
diff --git a/src/test/examples/throwing/RenamedClass.java b/src/test/examples/throwing/RenamedClass.java
new file mode 100644
index 0000000..a3fd537
--- /dev/null
+++ b/src/test/examples/throwing/RenamedClass.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'throwing.dex' is what is run.
+
+package throwing;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This class' logic is completely bogus. The only purpose is to be recursive to avoid inlining
+ * and terminate.
+ */
+public class RenamedClass {
+ public List list = new ArrayList();
+
+ public List getList() {
+ if (list == null) { // always false
+ setList(getList());
+ }
+ return list;
+ }
+
+ public void setList(List list) {
+ if (list == null) {
+ setList(new LinkedList());
+ } else {
+ this.list = list;
+ }
+ }
+
+ // Another method with the same signature as getList
+ public void swap(List list) {
+ List before = getList();
+ setList(list);
+ if (before == null) { // always false
+ swap(list);
+ }
+ }
+
+ static RenamedClass create() {
+ RenamedClass theClass = new RenamedClass();
+ theClass.setList(new LinkedList());
+ return theClass;
+ }
+
+ void takeThingsForASpin(int value) {
+ if (value == 42) {
+ swap(new LinkedList<>());
+ setList(getList());
+ } else {
+ takeThingsForASpin(42);
+ }
+ }
+}
diff --git a/src/test/examples/throwing/Throwing.java b/src/test/examples/throwing/Throwing.java
new file mode 100644
index 0000000..574556f
--- /dev/null
+++ b/src/test/examples/throwing/Throwing.java
@@ -0,0 +1,227 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'throwing.dex' is what is run.
+
+package throwing;
+
+import java.util.Collections;
+import java.util.List;
+
+class Throwing {
+
+ static int[] used = new int[10];
+
+ public static void main(String[] args) {
+ try {
+ used[0] = throwAtFistLine(42);
+ } catch (Exception e) {
+ printFrameHead(e);
+ }
+ try {
+ used[1] = throwInMiddle(42);
+ } catch (Exception e) {
+ printFrameHead(e);
+ }
+ try {
+ used[2] = throwAfterMultiInline(42);
+ } catch (Exception e) {
+ printFrameHead(e);
+ }
+ try {
+ int value = magicNumber(42);
+ // This throws after an inline, on the top-level.
+ used[6] = value * 10;
+ used[7] = anotherInlinedFunction(value);
+ //
+ // Some space to increase line numbers...
+ //
+ used[8] = value / (value & 0x0);
+ } catch (Exception e) {
+ printFrameHead(e);
+ }
+
+ Nested nested = new Nested();
+
+ try {
+ used[3] = nested.justThrow(42);
+ } catch (Exception e) {
+ printFrameHead(e);
+ }
+
+ nested.doSomethingUseless();
+
+ used[0] += Nested.callAMethod(nested, 11);
+ used[0] += Nested.callAMethod(nested, 42);
+
+ RenamedClass aInstance = RenamedClass.create();
+ aInstance.takeThingsForASpin(42);
+
+ System.out.print(used[0]);
+
+ try {
+ throwInAFunctionThatIsNotInlinedAndCalledTwice();
+ } catch (Exception e) {
+ printFrameHead(e);
+ }
+
+ try {
+ throwInAFunctionThatIsNotInlinedAndCalledTwice();
+ } catch (Exception e) {
+ printFrameHead(e);
+ }
+
+ try {
+ aFunctionThatCallsAnInlinedMethodThatThrows(Collections.emptyList());
+ } catch (Exception e) {
+ printFrameHead(e);
+ }
+
+ try {
+ anotherFunctionThatCallsAnInlinedMethodThatThrows("string");
+ } catch (Exception e) {
+ printFrameHead(e);
+ }
+
+ try {
+ aFunctionsThatThrowsBeforeAnInlinedMethod(magicNumber(42));
+ } catch (Exception e) {
+ printFrameHead(e);
+ }
+ }
+
+ public static int magicNumber(int value) {
+ if (value < 0) {
+ return magicNumber(value++);
+ }
+ return value;
+ }
+
+ public static void printFrameHead(Exception e) {
+ for (StackTraceElement element : e.getStackTrace()) {
+ System.out.println("FRAME: " + element);
+ }
+ }
+
+ // This throws in the first line of the method.
+ public static int throwAtFistLine(int value) {
+ int aValue = value * 2 / (value & 0x0);
+ return aValue;
+ }
+
+ // This throws a little further down.
+ public static int throwInMiddle(int value) {
+ used[2] = value * 10;
+ used[3] = value >> 3;
+ used[4] = value / (value & 0x0);
+ used[5] = value * 20;
+ return value >> 5;
+ }
+
+ // This throws after another inlined function.
+ public static int throwAfterMultiInline(int value) {
+ used[6] = value * 10;
+ used[7] = anotherInlinedFunction(value);
+ //
+ // Some space to increase line numbers...
+ //
+ used[8] = value / (value & 0x0);
+ return value >> 5;
+ }
+
+ public static int throwInAFunctionThatIsNotInlinedAndCalledTwice() {
+ for (int i = 0; i < 10; i++) {
+ used[9] += i;
+ System.out.println("Increment by one!");
+ }
+ System.out.println("Incremented by 10.");
+ used[9] = used[9] / (used[9] & 0x0);
+ return used[9];
+ }
+
+ // Small method that throws and can be inlined.
+ private static int anotherThrowingMethodToInline(int value) {
+ used[4] = value / (value & 0x0);
+ return value >> 5;
+ }
+
+ // It is important that this function uses an argument type that is otherwise unused, so it gets
+ // the same minified name.
+ public static int aFunctionThatCallsAnInlinedMethodThatThrows(List aList) {
+ used[9] = aList.size();
+ for (int i = 0; i < 10; i++) {
+ used[9] += i;
+ System.out.println("Increment by one!");
+ }
+ System.out.println("Incremented by 10.");
+ used[9] = anotherThrowingMethodToInline(used[9]);
+ return used[9];
+ }
+
+ // Small method that throws and can be inlined.
+ private static int yetAnotherThrowingMethodToInline(int value) {
+ used[5] = value / (value & 0x0);
+ return value >> 5;
+ }
+
+ // It is important that this function uses an argument type that is otherwise unused, so it gets
+ // the same minified name.
+ public static int anotherFunctionThatCallsAnInlinedMethodThatThrows(String aString) {
+ used[0] = aString.length();
+ for (int i = 0; i < 10; i++) {
+ used[8] += i;
+ System.out.println("Increment by one!");
+ }
+ System.out.println("Incremented by 10.");
+ used[8] = yetAnotherThrowingMethodToInline(used[8]);
+ return used[8];
+ }
+
+ public static int aFunctionsThatThrowsBeforeAnInlinedMethod(int value) {
+ used[1] = value / (value & 0x0);
+ anotherInlinedFunction(used[1]);
+ return used[1];
+ }
+
+ // This will be inlined above but does not throw
+ public static int anotherInlinedFunction(int value) {
+ return value / (value & 0xff);
+ }
+
+ /**
+ * A nested class with different kind of methods to have inlining from a nested class and also
+ * renamings of a nested class in the mapping file.
+ *
+ * <p>Some methods are recursive to avoid inlining.
+ */
+ static class Nested {
+
+ int justThrow(int value) {
+ return used[8] = value / (value & 0x0);
+ }
+
+ // This will also be inlined. Not used in test but for generating interesting mapping files.
+ void doSomethingUseless() {
+ Throwing.used[9] = 11;
+ }
+
+ static int callAMethod(Nested on, int value) {
+ if (value > 20) {
+ return callAMethod(on, value - 1);
+ } else {
+ return on.aMethod(value);
+ }
+ }
+
+ int aMethod(int value) {
+ if (value > 10) {
+ return aMethod(value - 1);
+ } else {
+ return value;
+ }
+ }
+ }
+
+}
diff --git a/src/test/examples/throwing/proguard.cfg b/src/test/examples/throwing/proguard.cfg
new file mode 100644
index 0000000..6de724a
--- /dev/null
+++ b/src/test/examples/throwing/proguard.cfg
@@ -0,0 +1,14 @@
+# 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.
+
+-keep class throwing.Throwing {
+ public static void main(java.lang.String[]);
+}
+
+-keep,allowobfuscation class throwing.Overloaded {
+ *;
+}
+
+-keepattributes SourceFile,LineNumberTable
+
diff --git a/src/test/examples/trivial/Trivial.java b/src/test/examples/trivial/Trivial.java
new file mode 100644
index 0000000..b6b8299
--- /dev/null
+++ b/src/test/examples/trivial/Trivial.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package trivial;
+
+public class Trivial {
+ public static void main(String[] args) {
+ }
+}
diff --git a/src/test/examples/trycatch/TryCatch.java b/src/test/examples/trycatch/TryCatch.java
new file mode 100644
index 0000000..509e7c9
--- /dev/null
+++ b/src/test/examples/trycatch/TryCatch.java
@@ -0,0 +1,161 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'trycatch.dex' is what is run.
+
+package trycatch;
+
+class TryCatch {
+
+
+ private static class Thrower {
+
+ private boolean shouldThrow;
+
+ public Thrower(boolean shouldThrow) {
+ this.shouldThrow = shouldThrow;
+ }
+
+ public void maybeThrow() {
+ if (shouldThrow) {
+ throw new RuntimeException();
+ }
+ }
+ }
+
+ private static void throwOrCatch(Thrower thrower) {
+ String firstJunk = "junk 1";
+ String result = "Did not throw";
+ String secondJunk = "junk 2";
+ try {
+ thrower.maybeThrow();
+ } catch (Throwable e) {
+ String tmp = secondJunk;
+ secondJunk = firstJunk;
+ result = "Did throw";
+ firstJunk = tmp;
+ }
+ System.out.println(result);
+ System.out.println(firstJunk);
+ System.out.println(secondJunk);
+ }
+
+ private static void throwOnPositive(int i) {
+ if (i > 0) {
+ throw new RuntimeException();
+ }
+ }
+
+ private static int loopWhileThrow(int i) {
+ while (true) {
+ int result = 100;
+ try {
+ throwOnPositive(i--);
+ return result;
+ } catch (Throwable e) {
+ }
+ }
+ }
+
+ private static void foo() {
+ try {
+ throw new RuntimeException();
+ } catch (IllegalStateException e) {
+ System.out.println("Error!");
+ }
+ }
+
+ private static String tryCatchPhi(int i) {
+ String result = "one";
+ String otherResult = "two";
+ try {
+ throwOnPositive(i++);
+ result = otherResult;
+ throwOnPositive(i);
+ } catch (Throwable e) {
+ return result;
+ }
+ return "three";
+ }
+
+ private static void emptyMethod(int x) {
+ }
+
+ private static int regressRemoveCatchHandlers(int a, int b) {
+ try {
+ if (a == b) {
+ emptyMethod(a);
+ return 0;
+ } else {
+ emptyMethod(a);
+ return a / b;
+ }
+ } catch (IllegalStateException | ArithmeticException e) {
+ System.out.println("Error!");
+ }
+ return -1;
+ }
+
+ static final int NUM_OF_EVENTS_PER_FILE = 8192;
+
+ public static Object regressMultiNewArray() {
+ try {
+ int[][] args = new int[3][NUM_OF_EVENTS_PER_FILE];
+ return args;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ // Create a class hierarchy which introduces a bridge method (B::x returning Object) by
+ // overriding a method (A::x) with one with a narrower type.
+ public static class A {
+
+ public Object x() {
+ return null;
+ }
+ }
+
+ public static class B extends A {
+
+ // This bridge method will be generated.
+ // public Object x() {
+ // return result of call "String x()"
+ //}
+
+ public String x() {
+ return null;
+ }
+ }
+
+ static B b = new B();
+
+ public static Object regressBridgeMethod() {
+ String s;
+ try {
+ s = b.x();
+ } catch (ClassCastException e) {
+ return null;
+ }
+ return s;
+ }
+
+ public static void main(String[] args) {
+ loopWhileThrow(-100);
+ throwOrCatch(new Thrower(false));
+ throwOrCatch(new Thrower(true));
+ System.out.println(tryCatchPhi(-1));
+ System.out.println(tryCatchPhi(0));
+ System.out.println(tryCatchPhi(1));
+ try {
+ foo();
+ } catch (RuntimeException e) {
+ System.out.print("Success!");
+ }
+ regressRemoveCatchHandlers(1, 0);
+ regressMultiNewArray();
+ regressBridgeMethod();
+ }
+}
\ No newline at end of file
diff --git a/src/test/examples/trycatchmany/TryCatchMany.java b/src/test/examples/trycatchmany/TryCatchMany.java
new file mode 100644
index 0000000..eb07f27
--- /dev/null
+++ b/src/test/examples/trycatchmany/TryCatchMany.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'trycatchmany.dex' is what is run.
+
+package trycatchmany;
+
+class TryCatchMany {
+ public static void main(String[] args) {
+ try {
+ foo();
+ try {
+ bar();
+ } catch (RuntimeException e) {
+ System.out.println("Another error");
+ }
+ foo();
+ } catch (IllegalStateException e) {
+ System.out.print("Error again");
+ } catch (RuntimeException e) {
+ System.out.print("Success");
+ } finally {
+ System.out.print("!");
+ }
+ }
+
+ private static void foo() {
+ try {
+ throw new RuntimeException();
+ } catch (IllegalStateException e) {
+ System.out.println("Error");
+ }
+ }
+
+ private static void bar() {
+ throw new IllegalStateException();
+ }
+}
diff --git a/src/test/examplesAndroidN/interfacemethods/C2.java b/src/test/examplesAndroidN/interfacemethods/C2.java
new file mode 100644
index 0000000..da0d112
--- /dev/null
+++ b/src/test/examplesAndroidN/interfacemethods/C2.java
@@ -0,0 +1,8 @@
+// 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 interfacemethods;
+
+public class C2 implements I2 {
+}
diff --git a/src/test/examplesAndroidN/interfacemethods/DefaultMethods.java b/src/test/examplesAndroidN/interfacemethods/DefaultMethods.java
new file mode 100644
index 0000000..49e792c
--- /dev/null
+++ b/src/test/examplesAndroidN/interfacemethods/DefaultMethods.java
@@ -0,0 +1,12 @@
+// 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 interfacemethods;
+
+public class DefaultMethods {
+
+ public static void main(String[] args) {
+ new C2().d1();
+ }
+}
diff --git a/src/test/examplesAndroidN/interfacemethods/I1.java b/src/test/examplesAndroidN/interfacemethods/I1.java
new file mode 100644
index 0000000..52b00ae
--- /dev/null
+++ b/src/test/examplesAndroidN/interfacemethods/I1.java
@@ -0,0 +1,12 @@
+// 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 interfacemethods;
+
+public interface I1 {
+
+ static void s1() {
+ System.out.println("s1");
+ }
+}
diff --git a/src/test/examplesAndroidN/interfacemethods/I2.java b/src/test/examplesAndroidN/interfacemethods/I2.java
new file mode 100644
index 0000000..6973c21
--- /dev/null
+++ b/src/test/examplesAndroidN/interfacemethods/I2.java
@@ -0,0 +1,12 @@
+// 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 interfacemethods;
+
+public interface I2 {
+
+ default void d1() {
+ System.out.println("d1");
+ }
+}
diff --git a/src/test/examplesAndroidN/interfacemethods/StaticInterfaceMethods.java b/src/test/examplesAndroidN/interfacemethods/StaticInterfaceMethods.java
new file mode 100644
index 0000000..32a651e
--- /dev/null
+++ b/src/test/examplesAndroidN/interfacemethods/StaticInterfaceMethods.java
@@ -0,0 +1,12 @@
+// 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 interfacemethods;
+
+public class StaticInterfaceMethods {
+
+ public static void main(String[] args) {
+ I1.s1();
+ }
+}
diff --git a/src/test/examplesAndroidO/invokecustom/InvokeCustom.java b/src/test/examplesAndroidO/invokecustom/InvokeCustom.java
new file mode 100644
index 0000000..0d4d5c5
--- /dev/null
+++ b/src/test/examplesAndroidO/invokecustom/InvokeCustom.java
@@ -0,0 +1,177 @@
+// 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 invokecustom;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+interface J {
+
+ default void targetMethodTest8() {
+ System.out.println("targetMethodTest8 from J");
+ }
+
+ default void targetMethodTest7() {
+ System.out.println("targetMethodTest7 from J");
+ }
+
+ default void targetMethodTest6() {
+ System.out.println("targetMethodTest6 from J");
+ }
+}
+
+interface I extends J {
+ void targetMethodTest8();
+
+ default void targetMethodTest6() {
+ System.out.println("targetMethodTest6 from I");
+ }
+
+ default void targetMethodTest9() {
+ System.out.println("targetMethodTest9 from I");
+ }
+
+ default void targetMethodTest10() {
+ System.out.println("targetMethodTest10 from I");
+ }
+}
+
+abstract class Super {
+ public void targetMethodTest5() {
+ System.out.println("targetMethodTest5 from Super");
+ }
+
+ abstract void targetMethodTest10();
+}
+
+public class InvokeCustom extends Super implements I {
+
+ private static String staticField1 = "StaticField1";
+
+ private String instanceField1 = "instanceField1";
+
+ private static void targetMethodTest1() {
+ System.out.println("Hello World!");
+ }
+
+ private static void targetMethodTest2(boolean z, byte b, char c, short s, int i, float f, long l,
+ double d, String str) {
+ System.out.println(z);
+ System.out.println(b);
+ System.out.println(c);
+ System.out.println(s);
+ System.out.println(i);
+ System.out.println(f);
+ System.out.println(l);
+ System.out.println(d);
+ System.out.println(str);
+ }
+
+ private static void targetMethodTest3() {
+ }
+
+ public static CallSite bsmLookupStatic(MethodHandles.Lookup caller, String name, MethodType type)
+ throws NoSuchMethodException, IllegalAccessException {
+ final MethodHandles.Lookup lookup = MethodHandles.lookup();
+ final MethodHandle targetMH = lookup.findStatic(lookup.lookupClass(), name, type);
+ return new ConstantCallSite(targetMH.asType(type));
+ }
+
+ public static CallSite bsmLookupStaticWithExtraArgs(
+ MethodHandles.Lookup caller, String name, MethodType type, int i, long l, float f, double d)
+ throws NoSuchMethodException, IllegalAccessException {
+ System.out.println(i);
+ System.out.println(l);
+ System.out.println(f);
+ System.out.println(d);
+ final MethodHandles.Lookup lookup = MethodHandles.lookup();
+ final MethodHandle targetMH = lookup.findStatic(lookup.lookupClass(), name, type);
+ return new ConstantCallSite(targetMH.asType(type));
+ }
+
+ @Override
+ public void targetMethodTest5() {
+ System.out.println("targetMethodTest5 from InvokeCustom");
+ }
+
+ private static void targetMethodTest4() {
+ System.out.println("targetMethodTest4");
+ }
+
+ public static CallSite bsmCreateCallSite(
+ MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+ throws Throwable {
+ // Using mh to create the call site fails when run on Art. See b/36957105 for details.
+ final MethodHandle targetMH = MethodHandles.lookup().findSpecial(Super.class,
+ "targetMethodTest5", MethodType.methodType(void.class), InvokeCustom.class);
+ return new ConstantCallSite(targetMH);
+ }
+
+ public static CallSite bsmCreateCallCallingtargetMethodTest6(
+ MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+ throws Throwable {
+ // Using mh to create the call site fails when run on Art. See b/36957105 for details.
+ final MethodHandle targetMH =
+ MethodHandles.lookup().findVirtual(
+ I.class, "targetMethodTest6", MethodType.methodType(void.class));
+ return new ConstantCallSite(targetMH);
+ }
+
+ public static CallSite bsmCreateCallCallingtargetMethodTest7(
+ MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+ throws Throwable {
+ // Using mh to create the call site fails when run on Art. See b/36957105 for details.
+ final MethodHandle targetMH =
+ MethodHandles.lookup().findVirtual(
+ J.class, "targetMethodTest7", MethodType.methodType(void.class));
+ return new ConstantCallSite(targetMH);
+ }
+
+ public void targetMethodTest8() {
+ System.out.println("targetMethodTest8 from InvokeCustom");
+ }
+
+ public static CallSite bsmCreateCallCallingtargetMethodTest8(
+ MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+ throws Throwable {
+ // Using mh to create the call site fails when run on Art. See b/36957105 for details.
+ final MethodHandle targetMH =
+ MethodHandles.lookup().findVirtual(
+ J.class, "targetMethodTest8", MethodType.methodType(void.class));
+ return new ConstantCallSite(targetMH);
+ }
+
+ public static CallSite bsmCreateCallCallingtargetMethodTest9(
+ MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+ throws Throwable {
+ // Using mh to create the call site fails when run on Art. See b/36957105 for details.
+ final MethodHandle targetMH =
+ MethodHandles.lookup().findVirtual(
+ InvokeCustom.class, "targetMethodTest9", MethodType.methodType(void.class));
+ return new ConstantCallSite(targetMH);
+ }
+
+ public void targetMethodTest10() {
+ System.out.println("targetMethodTest10 from InvokeCustom");
+ }
+
+ public static CallSite bsmCreateCallCallingtargetMethodTest10(
+ MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+ throws Throwable {
+ // Using mh to create the call site fails when run on Art. See b/36957105 for details.
+ final MethodHandle targetMH =
+ MethodHandles.lookup().findVirtual(
+ InvokeCustom.class, "targetMethodTest10", MethodType.methodType(void.class));
+ return new ConstantCallSite(targetMH);
+ }
+
+ public static CallSite bsmCreateCallCallingtargetMethod(
+ MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+ throws Throwable {
+ return new ConstantCallSite(mh);
+ }
+}
diff --git a/src/test/examplesAndroidO/invokecustom/TestGenerator.java b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
new file mode 100644
index 0000000..883106f
--- /dev/null
+++ b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
@@ -0,0 +1,422 @@
+// 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 invokecustom;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class TestGenerator {
+
+ private final Path classNamePath;
+
+ public static void main(String[] args) throws IOException {
+ assert args.length == 1;
+ TestGenerator testGenerator = new TestGenerator(Paths.get(args[0],
+ TestGenerator.class.getPackage().getName(), InvokeCustom.class.getSimpleName() + ".class"));
+ testGenerator.generateTests();
+ }
+
+ public TestGenerator(Path classNamePath) {
+ this.classNamePath = classNamePath;
+ }
+
+ private void generateTests() throws IOException {
+ ClassReader cr = new ClassReader(new FileInputStream(classNamePath.toFile()));
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+ cr.accept(
+ new ClassVisitor(Opcodes.ASM5, cw) {
+ @Override
+ public void visitEnd() {
+ generateMethodTest1(cw);
+ generateMethodTest2(cw);
+ generateMethodTest3(cw);
+ generateMethodTest4(cw);
+ generateMethodTest5(cw);
+ generateMethodTest6(cw);
+ generateMethodTest7(cw);
+ generateMethodTest8(cw);
+ generateMethodTest9(cw);
+ generateMethodTest10(cw);
+ generateMethodTest11(cw);
+ generateMethodTest12(cw);
+ generateMethodTest13(cw);
+ generateMethodMain(cw);
+ super.visitEnd();
+ }
+ }, 0);
+ new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray());
+ }
+
+ /* generate main method that only call all test methods. */
+ private void generateMethodMain(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(
+ Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test1", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test2", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test3", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test4", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test5", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test6", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test7", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test8", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test9", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test10", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test11", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test12", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test13", "()V", false);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method without extra args and no arg
+ * to the target method.
+ */
+ private void generateMethodTest1(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test1", "()V",
+ null, null);
+ MethodType mt =
+ MethodType.methodType(
+ CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
+ Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+ mv.visitInvokeDynamicInsn("targetMethodTest1", "()V", bootstrap);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method without extra args and
+ * args to the target method.
+ */
+ private void generateMethodTest2(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test2", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(
+ CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
+ Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+ mv.visitLdcInsn(new Boolean(true));
+ mv.visitLdcInsn(new Byte((byte) 127));
+ mv.visitLdcInsn(new Character('c'));
+ mv.visitLdcInsn(new Short((short) 1024));
+ mv.visitLdcInsn(new Integer(123456));
+ mv.visitLdcInsn(new Float(1.2f));
+ mv.visitLdcInsn(new Long(123456789));
+ mv.visitLdcInsn(new Double(3.5123456789));
+ mv.visitLdcInsn("String");
+ mv.visitInvokeDynamicInsn("targetMethodTest2", "(ZBCSIFJDLjava/lang/String;)V", bootstrap);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with extra args and no arg
+ * to the target method.
+ */
+ private void generateMethodTest3(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test3", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(
+ CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class,
+ long.class, float.class, double.class);
+ Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmLookupStaticWithExtraArgs", mt.toMethodDescriptorString(), false);
+ mv.visitInvokeDynamicInsn("targetMethodTest3", "()V", bootstrap, new Integer(1),
+ new Long(123456789), new Float(123.456), new Double(123456.789123));
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+ * MethodHandle of kind invokespecial.
+ */
+ private void generateMethodTest4(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test4", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class, MethodHandle.class);
+ Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
+ mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+ mv.visitInvokeDynamicInsn("targetMethodTest5", "(Linvokecustom/InvokeCustom;)V", bootstrap,
+ new Handle( Opcodes.H_INVOKESPECIAL, Type.getInternalName(Super.class),
+ "targetMethodTest5", "()V", false));
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+ * MethodHandle of kind invoke interface. The target method is a default method into an interface
+ * that shadows another default method from a super interface.
+ */
+ private void generateMethodTest5(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test5", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class, MethodHandle.class);
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmCreateCallCallingtargetMethodTest6", mt.toMethodDescriptorString(), false);
+ mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+ mv.visitInvokeDynamicInsn("targetMethodTest6", "(Linvokecustom/I;)V", bootstrap,
+ new Handle(Opcodes.H_INVOKEINTERFACE, Type.getInternalName(I.class),
+ "targetMethodTest6", "()V", true));
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+ * MethodHandle of kind invoke interface. The target method is a default method into an interface
+ * that is at the end of a chain of interfaces.
+ */
+ private void generateMethodTest6(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test6", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class, MethodHandle.class);
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmCreateCallCallingtargetMethodTest7", mt.toMethodDescriptorString(), false);
+ mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+ mv.visitInvokeDynamicInsn("targetMethodTest7", "(Linvokecustom/J;)V", bootstrap,
+ new Handle(Opcodes.H_INVOKEINTERFACE, Type.getInternalName(J.class),
+ "targetMethodTest7", "()V", true));
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+ * MethodHandle of kind invoke interface. The target method is a method into an interface
+ * that is shadowed by another definition into a sub interfaces.
+ */
+ private void generateMethodTest7(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test7", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class, MethodHandle.class);
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmCreateCallCallingtargetMethodTest8", mt.toMethodDescriptorString(), false);
+ mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+ mv.visitInvokeDynamicInsn("targetMethodTest8", "(Linvokecustom/J;)V", bootstrap,
+ new Handle(Opcodes.H_INVOKEINTERFACE, Type.getInternalName(J.class),
+ "targetMethodTest8", "()V", true));
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+ * MethodHandle of kind invoke virtual. The target method is a method into an interface that is
+ * not shadowed by an implementation into a classes implementing the interface.
+ */
+ private void generateMethodTest8(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test8", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class, MethodHandle.class);
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmCreateCallCallingtargetMethodTest9", mt.toMethodDescriptorString(), false);
+ mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+ mv.visitInvokeDynamicInsn("targetMethodTest9", "(Linvokecustom/InvokeCustom;)V", bootstrap,
+ new Handle(Opcodes.H_INVOKEVIRTUAL, Type.getInternalName(InvokeCustom.class),
+ "targetMethodTest9", "()V", false));
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+ * MethodHandle of kind invoke virtual. The target method is a method into a class implementing
+ * an abstract method and that shadows a default method from an interface.
+ */
+ private void generateMethodTest9(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test9", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class, MethodHandle.class);
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmCreateCallCallingtargetMethodTest10", mt.toMethodDescriptorString(), false);
+ mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+ mv.visitInvokeDynamicInsn("targetMethodTest10", "(Linvokecustom/InvokeCustom;)V", bootstrap,
+ new Handle(Opcodes.H_INVOKEVIRTUAL, Type.getInternalName(InvokeCustom.class),
+ "targetMethodTest10", "()V", false));
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+ * MethodHandle of kind get static. The method handle read a static field from a class.
+ */
+ private void generateMethodTest10(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test10", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class, MethodHandle.class);
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmCreateCallCallingtargetMethod", mt.toMethodDescriptorString(), false);
+ mv.visitFieldInsn(Opcodes.GETSTATIC,
+ "java/lang/System",
+ "out",
+ "Ljava/io/PrintStream;");
+ mv.visitInvokeDynamicInsn("staticField1", "()Ljava/lang/String;", bootstrap,
+ new Handle(Opcodes.H_GETSTATIC, Type.getInternalName(InvokeCustom.class),
+ "staticField1", "Ljava/lang/String;", false));
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+ "java/io/PrintStream",
+ "println",
+ "(Ljava/lang/String;)V", false);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+ * MethodHandle of kind put static. The method handle write a static field in a class and then
+ * print its value.
+ */
+ private void generateMethodTest11(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test11", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class, MethodHandle.class);
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmCreateCallCallingtargetMethod", mt.toMethodDescriptorString(), false);
+ mv.visitLdcInsn("Write static field");
+ mv.visitInvokeDynamicInsn("staticField1", "(Ljava/lang/String;)V", bootstrap,
+ new Handle(Opcodes.H_PUTSTATIC, Type.getInternalName(InvokeCustom.class),
+ "staticField1", "Ljava/lang/String;", false));
+ mv.visitFieldInsn(Opcodes.GETSTATIC,
+ "java/lang/System",
+ "out",
+ "Ljava/io/PrintStream;");
+ mv.visitFieldInsn(Opcodes.GETSTATIC,
+ Type.getInternalName(InvokeCustom.class),
+ "staticField1",
+ "Ljava/lang/String;");
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+ "java/io/PrintStream",
+ "println",
+ "(Ljava/lang/String;)V", false);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+ * MethodHandle of kind get instance. The method handle read an instance field from a class.
+ */
+ private void generateMethodTest12(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test12", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class, MethodHandle.class);
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmCreateCallCallingtargetMethod", mt.toMethodDescriptorString(), false);
+ mv.visitFieldInsn(Opcodes.GETSTATIC,
+ "java/lang/System",
+ "out",
+ "Ljava/io/PrintStream;");
+ mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+ mv.visitInvokeDynamicInsn("instanceField1", "(Linvokecustom/InvokeCustom;)Ljava/lang/String;",
+ bootstrap, new Handle(Opcodes.H_GETFIELD, Type.getInternalName(InvokeCustom.class),
+ "instanceField1", "Ljava/lang/String;", false));
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+ "java/io/PrintStream",
+ "println",
+ "(Ljava/lang/String;)V", false);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+ * MethodHandle of kind put instance. The method handle write an instance field in a class and
+ * then print its value.
+ */
+ private void generateMethodTest13(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test13", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class, MethodHandle.class);
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmCreateCallCallingtargetMethod", mt.toMethodDescriptorString(), false);
+ mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+ mv.visitVarInsn(Opcodes.ASTORE, 0);
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ mv.visitLdcInsn("Write instance field");
+ mv.visitInvokeDynamicInsn("instanceField1", "(Linvokecustom/InvokeCustom;Ljava/lang/String;)V",
+ bootstrap, new Handle(Opcodes.H_PUTFIELD, Type.getInternalName(InvokeCustom.class),
+ "instanceField1", "Ljava/lang/String;", false));
+ mv.visitFieldInsn(Opcodes.GETSTATIC,
+ "java/lang/System",
+ "out",
+ "Ljava/io/PrintStream;");
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ mv.visitFieldInsn(Opcodes.GETFIELD,
+ Type.getInternalName(InvokeCustom.class),
+ "instanceField1",
+ "Ljava/lang/String;");
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+ "java/io/PrintStream",
+ "println",
+ "(Ljava/lang/String;)V", false);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+}
diff --git a/src/test/examplesAndroidO/invokecustom/keep-rules.txt b/src/test/examplesAndroidO/invokecustom/keep-rules.txt
new file mode 100644
index 0000000..52fa2a7
--- /dev/null
+++ b/src/test/examplesAndroidO/invokecustom/keep-rules.txt
@@ -0,0 +1,17 @@
+# 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.
+
+# Keep the application entry point and the target methods of invoke-custom because these methods
+# can not be known at compile time. Get rid of everything that is not reachable from there.
+-keep public class invokecustom.InvokeCustom {
+ public static void main(...);
+}
+
+-keepclassmembers class * {
+ *** targetMethodTest*(...);
+}
+
+# allow access modification to enable minification
+-allowaccessmodification
+
diff --git a/src/test/examplesAndroidO/invokepolymorphic/InvokePolymorphic.java b/src/test/examplesAndroidO/invokepolymorphic/InvokePolymorphic.java
new file mode 100644
index 0000000..90cb064
--- /dev/null
+++ b/src/test/examplesAndroidO/invokepolymorphic/InvokePolymorphic.java
@@ -0,0 +1,104 @@
+// 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 invokepolymorphic;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+class Data {
+}
+
+public class InvokePolymorphic {
+
+ public String buildString(Integer i1, int i2, String s) {
+ return (i1 == null ? "N" : "!N") + "-" + i2 + "-" + s;
+ }
+
+ public void testInvokePolymorphic() {
+ MethodType mt = MethodType.methodType(String.class, Integer.class, int.class, String.class);
+ MethodHandles.Lookup lk = MethodHandles.lookup();
+
+ try {
+ MethodHandle mh = lk.findVirtual(getClass(), "buildString", mt);
+ System.out.println(mh.invoke(this, null, 1, "string"));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ public String buildString(
+ byte b, char c, short s, float f, double d, long l, Integer i1, int i2, String str) {
+ return b + "-" + c + "-" + s + "-" + f + "-" + d + "-" + l + "-" + (i1 == null ? "N" : "!N")
+ + "-" + i2 + "-" + str;
+ }
+
+ public void testInvokePolymorphicRange() {
+ MethodType mt = MethodType.methodType(String.class, byte.class, char.class, short.class,
+ float.class, double.class, long.class, Integer.class, int.class, String.class);
+ MethodHandles.Lookup lk = MethodHandles.lookup();
+
+ try {
+ MethodHandle mh = lk.findVirtual(getClass(), "buildString", mt);
+ System.out.println(
+ mh.invoke(this, (byte) 2, 'a', (short) 0xFFFF, 1.1f, 2.24d, 12345678L, null,
+ 1, "string"));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ public static void testWithAllTypes(
+ boolean z, char a, short b, int c, long d, float e, double f, String g, Object h) {
+ System.out.println(z);
+ System.out.println(a);
+ System.out.println(b);
+ System.out.println(c);
+ System.out.println(d);
+ System.out.println(e);
+ System.out.println(f);
+ System.out.println(g);
+ System.out.println(h);
+ }
+
+ public void testInvokePolymorphicWithAllTypes() {
+ try {
+ MethodHandle mth =
+ MethodHandles.lookup()
+ .findStatic(
+ InvokePolymorphic.class,
+ "testWithAllTypes",
+ MethodType.methodType(
+ void.class, boolean.class, char.class, short.class, int.class, long.class,
+ float.class, double.class, String.class, Object.class));
+ mth.invokeExact(false,'h', (short) 56, 72, Integer.MAX_VALUE + 42l,
+ 0.56f, 100.0d, "hello", (Object) "goodbye");
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ public MethodHandle testInvokePolymorphicWithConstructor() {
+ MethodHandle mh = null;
+ MethodType mt = MethodType.methodType(void.class);
+ MethodHandles.Lookup lk = MethodHandles.lookup();
+
+ try {
+ mh = lk.findConstructor(Data.class, mt);
+ System.out.println(mh.invoke().getClass() == Data.class);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+
+ return mh;
+ }
+
+ public static void main(String[] args) {
+ InvokePolymorphic invokePolymorphic = new InvokePolymorphic();
+ invokePolymorphic.testInvokePolymorphic();
+ invokePolymorphic.testInvokePolymorphicRange();
+ invokePolymorphic.testInvokePolymorphicWithAllTypes();
+ invokePolymorphic.testInvokePolymorphicWithConstructor();
+ }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java b/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java
new file mode 100644
index 0000000..4b7ebd9
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java
@@ -0,0 +1,529 @@
+// 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 lambdadesugaring;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import lambdadesugaring.legacy.Legacy;
+import lambdadesugaring.other.OtherRefs;
+
+public class LambdaDesugaring {
+ interface I {
+ String foo();
+ }
+
+ interface V {
+ void foo();
+ }
+
+ interface VT<T> {
+ void foo(T t);
+ }
+
+ interface P1<X> {
+ X foo(int i);
+ }
+
+ interface I2 extends I {
+ }
+
+ interface I3 {
+ String foo();
+ }
+
+ interface M1 {
+ }
+
+ interface M2 {
+ }
+
+ interface J {
+ String foo(String a, int b, boolean c);
+ }
+
+ interface G {
+ A foo();
+ }
+
+ interface H<T extends A> {
+ T foo(T o);
+ }
+
+ interface K {
+ Object foo(String a, String b, String c);
+ }
+
+ interface ObjectProvider {
+ Object act();
+ }
+
+ interface S2Z {
+ boolean foo(String a);
+ }
+
+ interface SS2Z {
+ boolean foo(String a, String b);
+ }
+
+ interface ArrayTransformerA<T> {
+ T[] transform(T[] a);
+ }
+
+ @SuppressWarnings("unchecked")
+ interface ArrayTransformerB<T> {
+ T[] transform(T... a);
+ }
+
+ static <T> void print(T[] a) {
+ StringBuilder builder = new StringBuilder("{");
+ String sep = "";
+ for (T s : a) {
+ builder.append(sep).append(s.toString());
+ sep = ", ";
+ }
+ builder.append("}");
+ System.out.println(builder.toString());
+ }
+
+ <T> T[] reorder(T[] a) {
+ int size = a.length;
+ for (int x = 0; x < size / 2; x++) {
+ T t = a[x];
+ a[x] = a[size - 1 - x];
+ a[size - 1 - x] = t;
+ }
+ return a;
+ }
+
+ static void atA(ArrayTransformerA<Integer> f) {
+ print(f.transform(new Integer[] { 1, 2, 3 }));
+ }
+
+ static void atB(ArrayTransformerB<String> f) {
+ print(f.transform("A", "B", "C"));
+ }
+
+ public static String staticUnused() {
+ return "ReleaseTests::staticUnused";
+ }
+
+ public static void testUnusedLambdas() {
+ System.out.print("Before unused ... ");
+ Object o = (I) LambdaDesugaring::staticUnused;
+ System.out.println("after unused.");
+ }
+
+ class A {
+ final String toString;
+
+ A(String toString) {
+ this.toString = toString;
+ }
+
+ @Override
+ public String toString() {
+ return toString;
+ }
+ }
+
+ class B extends A {
+ B(String toString) {
+ super(toString);
+ }
+ }
+
+ class C extends B {
+ C(String toString) {
+ super(toString);
+ }
+ }
+
+ class D extends C {
+ D(String toString) {
+ super(toString);
+ }
+ }
+
+ public static class Refs {
+ public static String f(I i) {
+ return i.foo();
+ }
+
+ public static void v(V v) {
+ v.foo();
+ }
+
+ public static void vt(VT<String> v) {
+ v.foo(null);
+ }
+
+ public static String p1(P1 p) {
+ return p.foo(123).getClass().getCanonicalName();
+ }
+
+ public static String pSS2Z(SS2Z p) {
+ return "" + p.foo("123", "321");
+ }
+
+ public static String pS2Z(S2Z p) {
+ return "" + p.foo("123");
+ }
+
+ public static String p3(K k) {
+ return k.foo("A", "B", "C").toString();
+ }
+
+ public static String g(ObjectProvider op) {
+ return op.act().toString();
+ }
+
+ static class A extends OtherRefs {
+ String fooInternal() {
+ return "Refs::A::fooInternal()";
+ }
+
+ protected String fooProtected() {
+ return "Refs::A::fooProtected()";
+ }
+
+ protected String fooProtectedOverridden() {
+ return "Refs::A::fooProtectedOverridden()";
+ }
+
+ protected static String staticProtected() {
+ return "Refs::A::staticProtected()";
+ }
+
+ static String staticInternal() {
+ return "Refs::A::staticInternal()";
+ }
+ }
+
+ public static class B extends A {
+ public void test() {
+ System.out.println(f(new A()::fooInternal));
+ System.out.println(f(this::fooInternal));
+ System.out.println(f(this::fooProtected));
+ System.out.println(f(this::fooProtectedOverridden));
+ System.out.println(f(this::fooPublic));
+ System.out.println(f(this::fooInternal));
+
+ System.out.println(f(super::fooProtectedOverridden));
+ System.out.println(f(this::fooOtherProtected));
+ System.out.println(f(this::fooOtherPublic));
+
+ System.out.println(g(this::fooPrivate));
+ System.out.println(g(new Integer(123)::toString));
+ System.out.println(g(System::lineSeparator));
+
+ System.out.println(f(A::staticInternal));
+ System.out.println(f(A::staticProtected));
+ System.out.println(f(B::staticPrivate));
+ System.out.println(f(OtherRefs::staticOtherPublic));
+ System.out.println(f(OtherRefs::staticOtherProtected));
+
+ System.out.println(g(StringBuilder::new));
+ System.out.println(g(OtherRefs.PublicInit::new));
+ System.out.println(ProtectedInit.testProtected());
+ System.out.println(g(ProtectedInit::new));
+ System.out.println(g(InternalInit::new));
+ System.out.println(PrivateInit.testPrivate());
+ System.out.println(g(PrivateInit::new));
+
+ System.out.println(p1(D[]::new));
+ System.out.println(p1(Integer::new));
+ System.out.println(p1(B::staticArray));
+
+ System.out.println(pSS2Z(String::equalsIgnoreCase));
+ System.out.println(pS2Z("123321"::contains));
+ System.out.println(pS2Z(String::isEmpty));
+
+ System.out.println(p3(B::fooConcat));
+
+ v(D::new); // Discarding the return value
+ vt((new ArrayList<String>())::add);
+
+ I3 i3 = this::fooPrivate;
+ System.out.println(f(i3::foo));
+ }
+
+ private static String staticPrivate() {
+ return "Refs::B::staticPrivate()";
+ }
+
+ private String fooPrivate() {
+ return "Refs::B::fooPrivate()";
+ }
+
+ String fooInternal() {
+ return "Refs::B::fooInternal()";
+ }
+
+ public static StringBuilder fooConcat(Object... objs) {
+ StringBuilder builder = new StringBuilder("Refs::B::fooConcat(");
+ String sep = "";
+ for (Object obj : objs) {
+ builder.append(sep).append(obj.toString());
+ sep = ", ";
+ }
+ return builder.append(")");
+ }
+
+ @Override
+ protected String fooProtectedOverridden() {
+ return "Refs::B::fooProtectedOverridden()";
+ }
+
+ public String fooPublic() {
+ return "Refs::B::fooPublic()";
+ }
+
+ static int[] staticArray(int size) {
+ return new int[size];
+ }
+ }
+
+ static class D {
+ D() {
+ System.out.println("Refs::D::init()");
+ }
+ }
+
+ public static class ProtectedInit extends OtherRefs.PublicInit {
+ protected ProtectedInit() {
+ }
+
+ static String testProtected() {
+ return g(ProtectedInit::new);
+ }
+
+ @Override
+ public String toString() {
+ return "OtherRefs::ProtectedInit::init()";
+ }
+ }
+
+ static class InternalInit extends ProtectedInit {
+ InternalInit() {
+ }
+
+ @Override
+ public String toString() {
+ return "Refs::InternalInit::init()";
+ }
+ }
+
+ static class PrivateInit extends InternalInit {
+ private PrivateInit() {
+ }
+
+ static String testPrivate() {
+ return g(PrivateInit::new);
+ }
+
+ @Override
+ public String toString() {
+ return "Refs::PrivateInit::init()";
+ }
+ }
+ }
+
+ public void testLambdasSimple() {
+ System.out.println(f(() -> "testLambdasSimple#1"));
+ System.out.println(
+ g((a, b, c) -> "{" + a + ":" + b + ":" + c + "}",
+ "testLambdasSimple#2", 123, true));
+ }
+
+ public void testLambdasSimpleWithCaptures() {
+ String s = "<stirng>";
+ long l = 1234567890123456789L;
+ char c = '#';
+
+ System.out.println(
+ g((x, y, z) -> "{" + s + ":" + l + ":" + c + ":" + x + ":" + y + ":" + z + "}",
+ "param1", 2, false));
+
+ I i1 = () -> "i1";
+ I i2 = () -> i1.foo() + ":i2";
+ I i3 = () -> i2.foo() + ":i3";
+ System.out.println(f(() -> "{" + i3.foo() + ":anonymous}"));
+ }
+
+ public void testInstructionPatchingWithCatchHandlers() {
+ try {
+ int a = 1, b = 0;
+ System.out.println(f(() -> "testInstructionPatchingWithCatchHandlers:1"));
+ System.out.println(f(() -> ("does not matter " + (a / b))));
+ } catch (IndexOutOfBoundsException | ArithmeticException e) {
+ System.out.println("testInstructionPatchingWithCatchHandlers:Divide By Zero");
+ } catch (RuntimeException re) {
+ throw re;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ int changes = -1;
+ try {
+ if (f(() -> "").isEmpty()) {
+ changes = 32;
+ System.out.println(f(() -> "testInstructionPatchingWithCatchHandlers:lambda"));
+ throw new RuntimeException();
+ } else {
+ changes = 42;
+ throw new RuntimeException();
+ }
+ } catch (Throwable t) {
+ System.out.println("testInstructionPatchingWithCatchHandlers:changes=" + changes);
+ }
+ }
+
+ public void testInstanceLambdaMethods() {
+ Integer i = 12345;
+ System.out.println(h(() -> new A("{testInstanceLambdaMethods:" + i + "}")));
+ }
+
+ @SuppressWarnings("unchecked")
+ private void testEnforcedSignatureHelper() {
+ H h = ((H<B>) x -> new B("{testEnforcedSignature:" + x + "}"));
+ System.out.println(h.foo(new A("A")).toString());
+ }
+
+ public void testEnforcedSignature() {
+ String capture = "capture";
+ System.out.println(i(x -> new B("{testEnforcedSignature:" + x + "}")));
+ System.out.println(i(x -> new B("{testEnforcedSignature:" + capture + "}")));
+
+ try {
+ testEnforcedSignatureHelper();
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+
+ atA(t -> new LambdaDesugaring().reorder(t));
+ atB(t -> new LambdaDesugaring().reorder(t));
+ }
+
+ public void testMultipleInterfaces() {
+ System.out.println(j((I2 & M1 & I3 & M2) () -> "{testMultipleInterfaces:1}"));
+
+ Object o = (I2 & M1 & I3 & M2) () -> "{testMultipleInterfaces:2}";
+ M1 m1 = (M1) o;
+ M2 m2 = (M2) m1;
+ I i = (I) m2;
+ System.out.println(((I3) i).foo());
+
+ o = (I2 & Serializable & M2) () -> "{testMultipleInterfaces:3}";
+ m2 = (M2) o;
+ Serializable s = (Serializable) m2;
+ System.out.println(((I) s).foo());
+ }
+
+ public void testBridges() {
+ k((Legacy.BH) (x -> x), "{testBridges:1}");
+ k((Legacy.BK<Legacy.D> & Serializable) (x -> x), new Legacy.D("{testBridges:2}"));
+ // k((Legacy.BL) (x -> x), new Legacy.B("{testBridges:3}")); crashes javac
+ k((Legacy.BM) (x -> x), new Legacy.C("{testBridges:4}"));
+ }
+
+ public String f(I i) {
+ return i.foo();
+ }
+
+ String g(J j, String a, int b, boolean c) {
+ return j.foo(a, b, c);
+ }
+
+ String h(G g) {
+ return g.foo().toString();
+ }
+
+ String i(H<B> h) {
+ return h.foo(new B("i(H<B>)")).toString();
+ }
+
+ <T extends I2 & M1 & M2 & I3> String j(T l) {
+ return ((I3) ((M2) ((M1) (((I2) l))))).foo();
+ }
+
+ static <T> void k(Legacy.BI<T> i, T v) {
+ System.out.println(i.foo(v).toString());
+ }
+
+ static I statelessLambda() {
+ return InstanceAndClassChecks::staticProvider;
+ }
+
+ static I statefulLambda() {
+ return InstanceAndClassChecks.INSTANCE::instanceProvider;
+ }
+
+ static class InstanceAndClassChecks {
+ static final InstanceAndClassChecks INSTANCE = new InstanceAndClassChecks();
+
+ static void test() {
+ assertSameInstance(
+ InstanceAndClassChecks::staticProvider,
+ InstanceAndClassChecks::staticProvider,
+ "Instances must be same");
+ assertSameInstance(
+ InstanceAndClassChecks::staticProvider,
+ statelessLambda(),
+ "Instances must be same");
+
+ assertDifferentInstance(
+ INSTANCE::instanceProvider,
+ INSTANCE::instanceProvider,
+ "Instances must be different");
+ assertDifferentInstance(
+ INSTANCE::instanceProvider,
+ statefulLambda(), "Instances must be different");
+ }
+
+ public static String staticProvider() {
+ return "staticProvider";
+ }
+
+ public String instanceProvider() {
+ return "instanceProvider";
+ }
+
+ static void assertSameInstance(I a, I b, String msg) {
+ if (a != b) {
+ throw new AssertionError(msg);
+ }
+ }
+
+ static void assertDifferentInstance(I a, I b, String msg) {
+ if (a == b) {
+ throw new AssertionError(msg);
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ LambdaDesugaring tests = new LambdaDesugaring();
+ tests.testLambdasSimple();
+ LambdaDesugaring.testUnusedLambdas();
+ tests.testLambdasSimpleWithCaptures();
+ tests.testInstructionPatchingWithCatchHandlers();
+ tests.testInstanceLambdaMethods();
+ tests.testEnforcedSignature();
+ tests.testMultipleInterfaces();
+ tests.testBridges();
+ new Refs.B().test();
+ if (isAndroid()) {
+ InstanceAndClassChecks.test();
+ }
+ }
+
+ static boolean isAndroid() {
+ try {
+ Class.forName("dalvik.system.VMRuntime");
+ return true;
+ } catch (Exception ignored) {
+ }
+ return false;
+ }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaring/ValueAdjustments.java b/src/test/examplesAndroidO/lambdadesugaring/ValueAdjustments.java
new file mode 100644
index 0000000..b9d2752
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaring/ValueAdjustments.java
@@ -0,0 +1,330 @@
+// 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 lambdadesugaring;
+
+public class ValueAdjustments {
+ interface B2i {
+ int foo(Byte i);
+ }
+
+ interface BnUnB {
+ Object foo(boolean z, Boolean Z, byte b, Byte B, char c, Character C, short s, Short S,
+ int i, Integer I, long l, Long L, float f, Float F, double d, Double D);
+ }
+
+ interface iz {
+ boolean f();
+ }
+
+ interface iZ {
+ Boolean f();
+ }
+
+ interface ib {
+ byte f();
+ }
+
+ interface iO {
+ Object f();
+ }
+
+ interface iN {
+ Number f();
+ }
+
+ interface iB {
+ Byte f();
+ }
+
+ interface ic {
+ char f();
+ }
+
+ interface iC {
+ Character f();
+ }
+
+ interface is {
+ short f();
+ }
+
+ interface iS {
+ Short f();
+ }
+
+ interface ii {
+ int f();
+ }
+
+ interface iI {
+ Integer f();
+ }
+
+ interface ij {
+ long f();
+ }
+
+ interface iJ {
+ Long f();
+ }
+
+ interface if_ {
+ float f();
+ }
+
+ interface iF {
+ Float f();
+ }
+
+ interface id {
+ double f();
+ }
+
+ interface iD {
+ Double f();
+ }
+
+ private static void checkObject(StringBuffer builder) {
+ builder
+ .append(((iO) ValueAdjustments::z).f()).append(' ')
+ .append(((iO) ValueAdjustments::Z).f()).append(' ')
+ .append(((iO) ValueAdjustments::b).f()).append(' ')
+ .append(((iO) ValueAdjustments::B).f()).append(' ')
+ .append(((iO) ValueAdjustments::c).f()).append(' ')
+ .append(((iO) ValueAdjustments::C).f()).append(' ')
+ .append(((iO) ValueAdjustments::s).f()).append(' ')
+ .append(((iO) ValueAdjustments::S).f()).append(' ')
+ .append(((iO) ValueAdjustments::i).f()).append(' ')
+ .append(((iO) ValueAdjustments::I).f()).append(' ')
+ .append(((iO) ValueAdjustments::j).f()).append(' ')
+ .append(((iO) ValueAdjustments::J).f()).append(' ')
+ .append(((iO) ValueAdjustments::f).f()).append(' ')
+ .append(((iO) ValueAdjustments::F).f()).append(' ')
+ .append(((iO) ValueAdjustments::d).f()).append(' ')
+ .append(((iO) ValueAdjustments::D).f()).append('\n');
+ }
+
+ private static void checkNumber(StringBuffer builder) {
+ builder
+ .append(((iN) ValueAdjustments::b).f()).append(' ')
+ .append(((iN) ValueAdjustments::B).f()).append(' ')
+ .append(((iN) ValueAdjustments::s).f()).append(' ')
+ .append(((iN) ValueAdjustments::S).f()).append(' ')
+ .append(((iN) ValueAdjustments::i).f()).append(' ')
+ .append(((iN) ValueAdjustments::I).f()).append(' ')
+ .append(((iN) ValueAdjustments::j).f()).append(' ')
+ .append(((iN) ValueAdjustments::J).f()).append(' ')
+ .append(((iN) ValueAdjustments::f).f()).append(' ')
+ .append(((iN) ValueAdjustments::F).f()).append(' ')
+ .append(((iN) ValueAdjustments::d).f()).append(' ')
+ .append(((iN) ValueAdjustments::D).f()).append('\n');
+ }
+
+ private static void checkBoxes(StringBuffer builder) {
+ builder
+ .append(((iZ) ValueAdjustments::z).f()).append(' ')
+ .append(((iB) ValueAdjustments::b).f()).append(' ')
+ .append(((iC) ValueAdjustments::c).f()).append(' ')
+ .append(((iS) ValueAdjustments::s).f()).append(' ')
+ .append(((iI) ValueAdjustments::i).f()).append(' ')
+ .append(((iJ) ValueAdjustments::j).f()).append(' ')
+ .append(((iF) ValueAdjustments::f).f()).append(' ')
+ .append(((iD) ValueAdjustments::d).f()).append('\n');
+ }
+
+ private static void checkDouble(StringBuffer builder) {
+ builder
+ .append(((id) ValueAdjustments::b).f()).append(' ')
+ .append(((id) ValueAdjustments::B).f()).append(' ')
+ .append(((id) ValueAdjustments::s).f()).append(' ')
+ .append(((id) ValueAdjustments::S).f()).append(' ')
+ .append(((id) ValueAdjustments::c).f()).append(' ')
+ .append(((id) ValueAdjustments::C).f()).append(' ')
+ .append(((id) ValueAdjustments::i).f()).append(' ')
+ .append(((id) ValueAdjustments::I).f()).append(' ')
+ .append(((id) ValueAdjustments::j).f()).append(' ')
+ .append(((id) ValueAdjustments::J).f()).append(' ')
+ .append(((id) ValueAdjustments::f).f()).append(' ')
+ .append(((id) ValueAdjustments::F).f()).append(' ')
+ .append(((id) ValueAdjustments::d).f()).append(' ')
+ .append(((id) ValueAdjustments::D).f()).append('\n');
+ }
+
+ private static void checkFloat(StringBuffer builder) {
+ builder
+ .append(((if_) ValueAdjustments::b).f()).append(' ')
+ .append(((if_) ValueAdjustments::B).f()).append(' ')
+ .append(((if_) ValueAdjustments::s).f()).append(' ')
+ .append(((if_) ValueAdjustments::S).f()).append(' ')
+ .append(((if_) ValueAdjustments::c).f()).append(' ')
+ .append(((if_) ValueAdjustments::C).f()).append(' ')
+ .append(((if_) ValueAdjustments::i).f()).append(' ')
+ .append(((if_) ValueAdjustments::I).f()).append(' ')
+ .append(((if_) ValueAdjustments::j).f()).append(' ')
+ .append(((if_) ValueAdjustments::J).f()).append(' ')
+ .append(((if_) ValueAdjustments::f).f()).append(' ')
+ .append(((if_) ValueAdjustments::F).f()).append('\n');
+ }
+
+ private static void checkLong(StringBuffer builder) {
+ builder
+ .append(((ij) ValueAdjustments::b).f()).append(' ')
+ .append(((ij) ValueAdjustments::B).f()).append(' ')
+ .append(((ij) ValueAdjustments::s).f()).append(' ')
+ .append(((ij) ValueAdjustments::S).f()).append(' ')
+ .append(((ij) ValueAdjustments::c).f()).append(' ')
+ .append(((ij) ValueAdjustments::C).f()).append(' ')
+ .append(((ij) ValueAdjustments::i).f()).append(' ')
+ .append(((ij) ValueAdjustments::I).f()).append(' ')
+ .append(((ij) ValueAdjustments::j).f()).append(' ')
+ .append(((ij) ValueAdjustments::J).f()).append('\n');
+ }
+
+ private static void checkInt(StringBuffer builder) {
+ builder
+ .append(((ii) ValueAdjustments::b).f()).append(' ')
+ .append(((ii) ValueAdjustments::B).f()).append(' ')
+ .append(((ii) ValueAdjustments::s).f()).append(' ')
+ .append(((ii) ValueAdjustments::S).f()).append(' ')
+ .append(((ii) ValueAdjustments::c).f()).append(' ')
+ .append(((ii) ValueAdjustments::C).f()).append(' ')
+ .append(((ii) ValueAdjustments::i).f()).append(' ')
+ .append(((ii) ValueAdjustments::I).f()).append('\n');
+ }
+
+ private static void checkShort(StringBuffer builder) {
+ builder
+ .append(((is) ValueAdjustments::b).f()).append(' ')
+ .append(((is) ValueAdjustments::B).f()).append(' ')
+ .append(((is) ValueAdjustments::s).f()).append(' ')
+ .append(((is) ValueAdjustments::S).f()).append('\n');
+ }
+
+ private static void checkChar(StringBuffer builder) {
+ builder
+ .append(((ic) ValueAdjustments::c).f()).append(' ')
+ .append(((ic) ValueAdjustments::C).f()).append('\n');
+ }
+
+ private static void checkByte(StringBuffer builder) {
+ builder
+ .append(((ib) ValueAdjustments::b).f()).append(' ')
+ .append(((ib) ValueAdjustments::B).f()).append('\n');
+ }
+
+ private static void checkBoolean(StringBuffer builder) {
+ builder
+ .append(((iz) ValueAdjustments::z).f()).append(' ')
+ .append(((iz) ValueAdjustments::Z).f()).append('\n');
+ }
+
+ private static void checkMisc(StringBuffer builder) {
+ builder
+ .append(((BnUnB) ValueAdjustments::boxingAndUnboxing).foo(true, false, (byte) 1, (byte) 2,
+ (char) 33, (char) 44, (short) 5, (short) 6, 7, 8, 9, 10L, 11, 12f, 13, 14d))
+ .append('\n')
+ .append(((BnUnB) ValueAdjustments::boxingAndUnboxingW).foo(true, false, (byte) 1, (byte) 2,
+ (char) 33, (char) 44, (short) 5, (short) 6, 7, 8, 9, 10L, 11, 12f, 13, 14d))
+ .append('\n')
+ .append(((B2i) (Integer::new)).foo(Byte.valueOf((byte) 44))).append('\n');
+ }
+
+ static String boxingAndUnboxing(Boolean Z, boolean z, Byte B, byte b, Character C, char c,
+ Short S, short s, Integer I, int i, Long L, long l, Float F, float f, Double D, double d) {
+ return "" + Z + ":" + z + ":" + B + ":" + b + ":" + C + ":" + c + ":" + S + ":" + s
+ + ":" + I + ":" + i + ":" + L + ":" + l + ":" + F + ":" + f + ":" + D + ":" + d;
+ }
+
+ static String boxingAndUnboxingW(boolean Z, boolean z, double B, double b,
+ double C, double c, double S, double s, double I, double i, double L, double l,
+ double F, double f, double D, double d) {
+ return "" + Z + ":" + z + ":" + B + ":" + b + ":" + C + ":" + c + ":" + S + ":" + s
+ + ":" + I + ":" + i + ":" + L + ":" + l + ":" + F + ":" + f + ":" + D + ":" + d;
+ }
+
+ static boolean z() {
+ return true;
+ }
+
+ static byte b() {
+ return 8;
+ }
+
+ static char c() {
+ return 'c';
+ }
+
+ static short s() {
+ return 16;
+ }
+
+ static int i() {
+ return 32;
+ }
+
+ static long j() {
+ return 64;
+ }
+
+ static float f() {
+ return 0.32f;
+ }
+
+ static double d() {
+ return 0.64;
+ }
+
+ static Boolean Z() {
+ return false;
+ }
+
+ static Byte B() {
+ return -8;
+ }
+
+ static Character C() {
+ return 'C';
+ }
+
+ static Short S() {
+ return -16;
+ }
+
+ static Integer I() {
+ return -32;
+ }
+
+ static Long J() {
+ return -64L;
+ }
+
+ static Float F() {
+ return -0.32f;
+ }
+
+ static Double D() {
+ return -0.64;
+ }
+
+ public static void main(String[] args) {
+ StringBuffer builder = new StringBuffer();
+
+ checkBoolean(builder);
+ checkByte(builder);
+ checkChar(builder);
+ checkShort(builder);
+ checkInt(builder);
+ checkLong(builder);
+ checkFloat(builder);
+ checkDouble(builder);
+
+ checkBoxes(builder);
+ checkNumber(builder);
+ checkObject(builder);
+
+ checkMisc(builder);
+
+ System.out.println(builder.toString());
+ }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaring/legacy/Legacy.java b/src/test/examplesAndroidO/lambdadesugaring/legacy/Legacy.java
new file mode 100644
index 0000000..aabd8d8
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaring/legacy/Legacy.java
@@ -0,0 +1,56 @@
+// 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 lambdadesugaring.legacy;
+
+public class Legacy {
+ public static class A {
+ private final String toString;
+
+ public A(String toString) {
+ this.toString = toString;
+ }
+
+ @Override
+ public String toString() {
+ return toString;
+ }
+ }
+
+ public static class B extends A {
+ public B(String toString) {
+ super(toString);
+ }
+ }
+
+ public static class C extends B {
+ public C(String toString) {
+ super(toString);
+ }
+ }
+
+ public static class D extends C {
+ public D(String toString) {
+ super(toString);
+ }
+ }
+
+ public interface BI<T> {
+ T foo(T o1);
+ }
+
+ public interface BH extends BI<String> {
+ }
+
+ public interface BK<T extends A> extends BI<T> {
+ T foo(T o1);
+ }
+
+ public interface BL<T extends B> extends BK<T> {
+ T foo(T o1);
+ }
+
+ public interface BM extends BK<C> {
+ C foo(C o1);
+ }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaring/other/OtherRefs.java b/src/test/examplesAndroidO/lambdadesugaring/other/OtherRefs.java
new file mode 100644
index 0000000..e783be3
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaring/other/OtherRefs.java
@@ -0,0 +1,32 @@
+// 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 lambdadesugaring.other;
+
+public class OtherRefs {
+ public static class PublicInit {
+ public PublicInit() {
+ }
+
+ @Override
+ public String toString() {
+ return "OtherRefs::PublicInit::init()";
+ }
+ }
+
+ protected String fooOtherProtected() {
+ return "OtherRefs::fooOtherProtected()";
+ }
+
+ public String fooOtherPublic() {
+ return "OtherRefs::fooOtherPublic()";
+ }
+
+ protected static String staticOtherProtected() {
+ return "OtherRefs::staticOtherProtected()";
+ }
+
+ public static String staticOtherPublic() {
+ return "OtherRefs::staticOtherPublic()";
+ }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
new file mode 100644
index 0000000..d2e738d
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
@@ -0,0 +1,405 @@
+// 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 lambdadesugaringnplus;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import lambdadesugaringnplus.other.ClassWithDefaultPackagePrivate;
+import lambdadesugaringnplus.other.InterfaceWithDefaultPackagePrivate;
+
+public class LambdasWithStaticAndDefaultMethods {
+ interface I {
+ String iRegular();
+
+ static String iStatic() {
+ return "I::iStatic()";
+ }
+
+ default String iDefault() {
+ return "I::iDefault()";
+ }
+
+ default String iDefaultOverridden() {
+ return "I::iDefaultOverridden()";
+ }
+
+ default II stateless() {
+ return () -> "I::stateless()";
+ }
+
+ default II stateful() {
+ return () -> "I::captureThis(" + stateless().iRegular() + ")";
+ }
+ }
+
+ static class C implements I {
+ @Override
+ public String iRegular() {
+ return "C::iRegular()";
+ }
+
+ @Override
+ public String iDefaultOverridden() {
+ return "C::iDefaultOverridden()";
+ }
+ }
+
+ interface II extends I {
+ static String iStatic() {
+ II ii = I::iStatic;
+ return "II::iStatic(" + ((I) I::iStatic).iRegular() +
+ "|" + ((II) ii::iDefaultOverridden).iRegular() +
+ "|" + ((II) String::new).iRegular() +
+ "|" + ((II) ii::iRegular).iRegular() + ")";
+ }
+
+ default String iDefaultOverridden() {
+ return "II::iDefault(" + ((I) this::iDefault).iRegular() +
+ "|" + ((II) "One-Two-Three"::intern).iRegular() +
+ "|" + ((II) this::iDefault).iRegular() + ")";
+ }
+ }
+
+ interface P {
+ String get();
+ }
+
+ static void p(P p) {
+ System.out.println(p.get());
+ }
+
+ interface X<T> {
+ String foo(T t);
+ }
+
+ interface Y<T extends I> extends X<T> {
+ String foo(T t);
+ }
+
+ interface Z extends Y<II> {
+ String foo(II t);
+ }
+
+ interface G<T> {
+ T foo(T t);
+ }
+
+ interface B38257361_I1<T extends Number> {
+ default T copy(T t) {
+ return t;
+ }
+ }
+
+ interface B38257361_I2 extends B38257361_I1<Integer> {
+ @Override
+ default Integer copy(Integer t) {
+ return B38257361_I1.super.copy(t);
+ }
+ }
+
+ static class B38257361_C implements B38257361_I2 {
+ }
+
+ static class B38257361 {
+ private B38257361_C c = new B38257361_C();
+
+ public Integer test(Integer i) {
+ return c.copy(i);
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public Number test(Number n) {
+ return ((B38257361_I1) c).copy(n);
+ }
+
+ public static void test() {
+ B38257361 l = new B38257361();
+ Integer i = new Integer(1);
+ if (i.equals(l.test(i))) {
+ System.out.println("Check 1: OK");
+ } else {
+ System.out.println("Check 1: NOT OK");
+ }
+ if (i.equals(l.test((Number) i))) {
+ System.out.println("Check 2: OK");
+ } else {
+ System.out.println("Check 2: NOT OK");
+ }
+ try {
+ Double d = new Double(1);
+ if (d.equals(l.test((Number) d))) {
+ System.out.println("Check 3: NOT OK, classCastException expected");
+ } else {
+ System.out.println("Check 3: NOT OK, classCastException expected");
+ }
+ System.out.println("Error, ClassCastException is expected");
+ } catch (ClassCastException e) {
+ // Class cast into the bridge method is expected
+ System.out.println("OK, ClassCastException is expected");
+ }
+ }
+ }
+
+ interface B38257037_I1 {
+ default Number getNumber() {
+ return new Integer(1);
+ }
+ }
+
+ interface B38257037_I2 extends B38257037_I1 {
+ @Override
+ default Double getNumber() {
+ return new Double(2.3);
+ }
+ }
+
+ static class B38257037_C implements B38257037_I2 {
+ }
+
+ /**
+ * Check that bridges are generated.
+ */
+ static class B38257037 {
+ private B38257037_C c = new B38257037_C();
+
+ public Double test1() {
+ return c.getNumber();
+ }
+
+ public Number test2() {
+ return ((B38257037_I1) c).getNumber();
+ }
+
+ public static void test() {
+ B38257037 l = new B38257037();
+ if (l.test1() == 2.3) {
+ System.out.println("Check 1: OK");
+ } else {
+ System.out.println("Check 1: NOT OK");
+ }
+ if (l.test2().equals(new Double(2.3))) {
+ System.out.println("Check 2: OK");
+ } else {
+ System.out.println("Check 2: NOT OK");
+ }
+ }
+ }
+
+ interface B38306708_I {
+ class $CC{
+ static void print() {
+ System.out.println("$CC");
+ }
+ }
+
+ default String m() {
+ return "ITop.m()";
+ }
+ }
+
+ static class B38306708 {
+ public static void test() {
+ B38306708_I.$CC.print();
+ }
+ }
+
+ interface B38308515_I {
+ default String m() {
+ return "m instance";
+ }
+
+ static String m(B38308515_I i) {
+ return "m static";
+ }
+ }
+
+ static class B38308515_C implements B38308515_I {
+ }
+
+ static class B38308515 {
+ static void test() {
+ B38308515_C c = new B38308515_C();
+ System.out.println(c.m());
+ System.out.println(B38308515_I.m(c));
+ }
+ }
+
+ static class B38302860 {
+
+ @SomeAnnotation(1)
+ private interface AnnotatedInterface {
+
+ @SomeAnnotation(2)
+ void annotatedAbstractMethod();
+
+ @SomeAnnotation(3)
+ default void annotatedDefaultMethod() {
+ }
+
+ @SomeAnnotation(4)
+ static void annotatedStaticMethod() {
+ }
+ }
+
+ @Retention(value = RetentionPolicy.RUNTIME)
+ private @interface SomeAnnotation {
+ int value();
+ }
+
+ private static boolean checkAnnotationValue(Annotation[] annotations, int value) {
+ if (annotations.length != 1) {
+ return false;
+ }
+ return annotations[0] instanceof SomeAnnotation
+ && ((SomeAnnotation) annotations[0]).value() == value;
+ }
+
+ @SuppressWarnings("unchecked")
+ static void test() throws Exception {
+ if (checkAnnotationValue(AnnotatedInterface.class.getAnnotations(), 1)) {
+ System.out.println("Check 1: OK");
+ } else {
+ System.out.println("Check 1: NOT OK");
+ }
+
+ if (checkAnnotationValue(
+ AnnotatedInterface.class.getMethod("annotatedAbstractMethod").getAnnotations(), 2)) {
+ System.out.println("Check 2: OK");
+ } else {
+ System.out.println("Check 2: NOT OK");
+ }
+
+ if (checkAnnotationValue(
+ AnnotatedInterface.class.getMethod("annotatedDefaultMethod").getAnnotations(), 3)) {
+ System.out.println("Check 3: OK");
+ } else {
+ System.out.println("Check 3: NOT OK");
+ }
+
+ if (checkAnnotationValue(
+ getCompanionClassOrInterface().getMethod("annotatedStaticMethod").getAnnotations(), 4)) {
+ System.out.println("Check 4: OK");
+ } else {
+ System.out.println("Check 4: NOT OK");
+ }
+ }
+
+ private static Class getCompanionClassOrInterface() {
+ try {
+ return Class.forName("lambdadesugaringnplus."
+ + "LambdasWithStaticAndDefaultMethods$B38302860$AnnotatedInterface-CC");
+ } catch (Exception e) {
+ return AnnotatedInterface.class;
+ }
+ }
+ }
+
+ static void z(Z p) {
+ System.out.println(p.foo(null));
+ }
+
+ static void g(G<String[]> g) {
+ StringBuilder builder = new StringBuilder("{");
+ String sep = "";
+ for (String s : g.foo(new String[] { "Arg0", "Arg1", "Arg2" })) {
+ builder.append(sep).append(s);
+ sep = ", ";
+ }
+ builder.append("}");
+ System.out.println(builder.toString());
+ }
+
+ interface SuperChain {
+ default String iMain() {
+ return "SuperChain::iMain()";
+ }
+ }
+
+ interface SuperChainDerived extends SuperChain {
+ default String iMain() {
+ return "SuperChainDerived::iMain(" + SuperChain.super.iMain() + ")";
+ }
+ }
+
+ interface OtherSuperChain {
+ default String iMain() {
+ return "OtherSuperChain::iMain()";
+ }
+ }
+
+ static class ClassWithSuperChain implements SuperChainDerived, OtherSuperChain {
+ public String iMain() {
+ return "ClassWithSuperChain::iMain(" + SuperChainDerived.super.iMain() + ")" + iMainImpl();
+ }
+
+ public String iMainImpl() {
+ return "ClassWithSuperChain::iMain(" + SuperChainDerived.super.iMain() +
+ " + " + OtherSuperChain.super.iMain() + ")";
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ C c = new C();
+ I i = c;
+
+ c.iRegular();
+ c.iDefault();
+ c.iDefaultOverridden();
+ I.iStatic();
+ i.iRegular();
+ i.iDefault();
+ i.iDefaultOverridden();
+
+ p(i.stateless()::iRegular);
+ p(i.stateful()::iRegular);
+
+ g(a -> a);
+ g(a -> {
+ int size = a.length;
+ for (int x = 0; x < size / 2; x++) {
+ String t = a[x];
+ a[x] = a[size - 1 - x];
+ a[size - 1 - x] = t;
+ }
+ return a;
+ });
+
+ p(c::iRegular);
+ p(c::iDefault);
+ p(c::iDefaultOverridden);
+ p(I::iStatic);
+ p(i::iRegular);
+ p(i::iDefault);
+ p(i::iDefaultOverridden);
+
+ II ii = i::iRegular;
+ p(II::iStatic);
+ p(ii::iRegular);
+ p(ii::iDefault);
+ p(ii::iDefaultOverridden);
+
+ z(s -> "From Interface With Bridges");
+
+ System.out.println(new ClassWithSuperChain().iMain());
+
+ ClassWithDefaultPackagePrivate c2 = new ClassWithDefaultPackagePrivate();
+ InterfaceWithDefaultPackagePrivate i2 = c2;
+
+ c2.defaultFoo();
+ i2.defaultFoo();
+ InterfaceWithDefaultPackagePrivate.staticFoo();
+
+ p(c2::defaultFoo);
+ p(i2::defaultFoo);
+ p(InterfaceWithDefaultPackagePrivate::staticFoo);
+ p(c2.lambda()::foo);
+
+ B38257361.test();
+ B38257037.test();
+ B38306708.test();
+ B38308515.test();
+ B38302860.test();
+ }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/other/ClassWithDefaultPackagePrivate.java b/src/test/examplesAndroidO/lambdadesugaringnplus/other/ClassWithDefaultPackagePrivate.java
new file mode 100644
index 0000000..102b13b
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/other/ClassWithDefaultPackagePrivate.java
@@ -0,0 +1,15 @@
+// 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 lambdadesugaringnplus.other;
+
+public class ClassWithDefaultPackagePrivate implements InterfaceWithDefaultPackagePrivate {
+ public InterfaceWithDefaultPackagePrivate lambda() {
+ return () -> ("lambda(" + InterfaceWithDefaultPackagePrivate.super.defaultFoo() + ")");
+ }
+
+ @Override
+ public String foo() {
+ throw new RuntimeException("Don't call me!");
+ }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/other/InterfaceWithDefaultPackagePrivate.java b/src/test/examplesAndroidO/lambdadesugaringnplus/other/InterfaceWithDefaultPackagePrivate.java
new file mode 100644
index 0000000..536bc29
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/other/InterfaceWithDefaultPackagePrivate.java
@@ -0,0 +1,23 @@
+// 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 lambdadesugaringnplus.other;
+
+class PackagePrivate {
+ @Override
+ public String toString() {
+ return "PackagePrivate::toString()";
+ }
+}
+
+public interface InterfaceWithDefaultPackagePrivate {
+ String foo();
+
+ default String defaultFoo() {
+ return "defaultFoo: " + new PackagePrivate().toString();
+ }
+
+ static String staticFoo() {
+ return "staticFoo: " + new PackagePrivate().toString();
+ }
+}
diff --git a/src/test/examplesAndroidO/paramnames/ParameterNames.java b/src/test/examplesAndroidO/paramnames/ParameterNames.java
new file mode 100644
index 0000000..e9d9334
--- /dev/null
+++ b/src/test/examplesAndroidO/paramnames/ParameterNames.java
@@ -0,0 +1,56 @@
+// 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 paramnames;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+public class ParameterNames {
+
+ private static final int MODIFIER_NONE = 0;
+ private static final int MODIFIER_FINAL = 0X10;
+
+ public ParameterNames(int a, final int b) {
+ }
+
+ public static void check(String expected, String checked) {
+ if (!expected.equals(checked)) {
+ throw new RuntimeException("Found '" + checked + "' while expecting '" + expected + "'");
+ }
+ }
+
+ public static void check(int expected, int checked) {
+ if (expected != checked) {
+ throw new RuntimeException("Found '" + checked + "' while expecting '" + expected + "'");
+ }
+ }
+
+ public static void myMethod(int a, final int b) throws NoSuchMethodException {
+ Class<ParameterNames> clazz = ParameterNames.class;
+ Method myMethod = clazz.getDeclaredMethod("myMethod", int.class, int.class);
+ Parameter[] parameters = myMethod.getParameters();
+ check(2, parameters.length);
+ check("a", parameters[0].getName());
+ check("b", parameters[1].getName());
+ check(MODIFIER_NONE, parameters[0].getModifiers());
+ check(MODIFIER_FINAL, parameters[1].getModifiers());
+ }
+
+ public static void myConstructor() throws NoSuchMethodException {
+ Class<ParameterNames> clazz = ParameterNames.class;
+ Constructor<?> myConstructor = clazz.getDeclaredConstructor(int.class, int.class);
+ Parameter[] parameters = myConstructor.getParameters();
+ check(2, parameters.length);
+ check("a", parameters[0].getName());
+ check("b", parameters[1].getName());
+ check(MODIFIER_NONE, parameters[0].getModifiers());
+ check(MODIFIER_FINAL, parameters[1].getModifiers());
+ }
+
+ public static void main(String[] args) throws NoSuchMethodException {
+ myMethod(0, 1);
+ myConstructor();
+ }
+}
diff --git a/src/test/examplesAndroidO/repeat_annotations/NumberAnnotation.java b/src/test/examplesAndroidO/repeat_annotations/NumberAnnotation.java
new file mode 100644
index 0000000..7e304b9
--- /dev/null
+++ b/src/test/examplesAndroidO/repeat_annotations/NumberAnnotation.java
@@ -0,0 +1,14 @@
+// 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 repeat_annotations;
+
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Repeatable(NumberAnnotations.class)
+public @interface NumberAnnotation {
+ int number() default 32;
+}
diff --git a/src/test/examplesAndroidO/repeat_annotations/NumberAnnotations.java b/src/test/examplesAndroidO/repeat_annotations/NumberAnnotations.java
new file mode 100644
index 0000000..a70587a
--- /dev/null
+++ b/src/test/examplesAndroidO/repeat_annotations/NumberAnnotations.java
@@ -0,0 +1,12 @@
+// 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 repeat_annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NumberAnnotations {
+ NumberAnnotation[] value();
+}
diff --git a/src/test/examplesAndroidO/repeat_annotations/RepeatAnnotations.java b/src/test/examplesAndroidO/repeat_annotations/RepeatAnnotations.java
new file mode 100644
index 0000000..d55de94
--- /dev/null
+++ b/src/test/examplesAndroidO/repeat_annotations/RepeatAnnotations.java
@@ -0,0 +1,22 @@
+// 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 repeat_annotations;
+
+// Simple test of Java 8 repeated annotations.
+public class RepeatAnnotations {
+
+ @NumberAnnotation(number = 1)
+ @NumberAnnotation(number = 2)
+ @NumberAnnotation(number = 3)
+ class Inner {
+ }
+
+ public static void main(String[] args) {
+ NumberAnnotations annotations = Inner.class.getAnnotation(NumberAnnotations.class);
+ System.out.println(annotations.value().length);
+ for (NumberAnnotation annotation : annotations.value()) {
+ System.out.println("Number annotation value: " + annotation.number());
+ }
+ }
+}
diff --git a/src/test/examplesAndroidO/repeat_annotations/RepeatAnnotationsNewApi.java b/src/test/examplesAndroidO/repeat_annotations/RepeatAnnotationsNewApi.java
new file mode 100644
index 0000000..6d23706
--- /dev/null
+++ b/src/test/examplesAndroidO/repeat_annotations/RepeatAnnotationsNewApi.java
@@ -0,0 +1,23 @@
+// 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 repeat_annotations;
+
+// Simple test of Java 8 repeated annotations using the new getAnnotationsByType
+// API to access them.
+public class RepeatAnnotationsNewApi {
+
+ @NumberAnnotation(number = 1)
+ @NumberAnnotation(number = 2)
+ @NumberAnnotation(number = 3)
+ class Inner {
+ }
+
+ public static void main(String[] args) {
+ NumberAnnotation[] annotations = Inner.class.getAnnotationsByType(NumberAnnotation.class);
+ System.out.println(annotations.length);
+ for (NumberAnnotation annotation : annotations) {
+ System.out.println("Number annotation value: " + annotation.number());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
new file mode 100644
index 0000000..978f9de
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -0,0 +1,99 @@
+// 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;
+
+import com.android.tools.r8.errors.CompilationError;
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.UnaryOperator;
+
+public class D8IncrementalRunExamplesAndroidOTest
+ extends RunExamplesAndroidOTest<D8Command.Builder> {
+
+ class D8IncrementalTestRunner extends TestRunner {
+
+ D8IncrementalTestRunner(String testName, String packageName, String mainClass) {
+ super(testName, packageName, mainClass);
+ }
+
+ @Override
+ TestRunner withMinApiLevel(int minApiLevel) {
+ return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
+ }
+
+ @Override
+ void build(Path testJarFile, Path out) throws Throwable {
+ // Collect classes and compile separately.
+ List<String> classFiles = collectClassFiles(testJarFile);
+ List<String> dexFiles = new ArrayList<>();
+ for (int i = 0; i < classFiles.size(); i++) {
+ Path indexedOut = Paths.get(
+ out.getParent().toString(), out.getFileName() + "." + i + ".zip");
+ compile(testJarFile.toString(), Collections.singletonList(classFiles.get(i)), indexedOut);
+ dexFiles.add(indexedOut.toString());
+ }
+
+ // When compiled add files separately, merge them.
+ compile(null, dexFiles, out);
+ }
+
+ private List<String> collectClassFiles(Path testJarFile) {
+ List<String> result = new ArrayList<>();
+ // Collect Java 8 classes.
+ Path parent = testJarFile.getParent();
+ File packageDir = parent.resolve(Paths.get("classes", packageName)).toFile();
+ collectClassFiles(packageDir, result);
+ // Collect legacy classes.
+ Path legacyPath = Paths.get("..",
+ parent.getFileName().toString() + "Legacy", "classes", packageName);
+ packageDir = parent.resolve(legacyPath).toFile();
+ collectClassFiles(packageDir, result);
+ Collections.sort(result);
+ return result;
+ }
+
+ private void collectClassFiles(File dir, List<String> result) {
+ File[] files = dir.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory()) {
+ collectClassFiles(file, result);
+ } else {
+ result.add(file.getAbsolutePath());
+ }
+ }
+ }
+ }
+
+ private void compile(String classpath, List<String> inputFiles, Path out) throws Throwable {
+ D8Command.Builder builder = D8Command.builder();
+ if (classpath != null) {
+ builder.addClasspathFiles(Paths.get(classpath));
+ }
+ for (String inputFile : inputFiles) {
+ builder.addProgramFiles(Paths.get(inputFile));
+ }
+ for (UnaryOperator<D8Command.Builder> transformation : builderTransformations) {
+ builder = transformation.apply(builder);
+ }
+ builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+ D8Command command = builder.setOutputPath(out).build();
+ try {
+ ToolHelper.runD8(command, this::combinedOptionConsumer);
+ } catch (RuntimeException re) {
+ throw re instanceof CompilationError ? re : re.getCause();
+ }
+ }
+ }
+
+ @Override
+ TestRunner test(String testName, String packageName, String mainClass) {
+ return new D8IncrementalTestRunner(testName, packageName, mainClass);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
new file mode 100644
index 0000000..ea94040
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -0,0 +1,45 @@
+// 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;
+
+import com.android.tools.r8.errors.CompilationError;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.function.UnaryOperator;
+
+public class D8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<D8Command.Builder> {
+
+ class D8TestRunner extends TestRunner {
+
+ D8TestRunner(String testName, String packageName, String mainClass) {
+ super(testName, packageName, mainClass);
+ }
+
+ @Override
+ TestRunner withMinApiLevel(int minApiLevel) {
+ return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
+ }
+
+ @Override
+ void build(Path inputFile, Path out) throws Throwable {
+ D8Command.Builder builder = D8Command.builder();
+ for (UnaryOperator<D8Command.Builder> transformation : builderTransformations) {
+ builder = transformation.apply(builder);
+ }
+ builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+ D8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
+ try {
+ ToolHelper.runD8(command, this::combinedOptionConsumer);
+ } catch (RuntimeException re) {
+ throw re instanceof CompilationError ? re : re.getCause();
+ }
+ }
+ }
+
+ @Override
+ TestRunner test(String testName, String packageName, String mainClass) {
+ return new D8TestRunner(testName, packageName, mainClass);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
new file mode 100644
index 0000000..7f9ecb0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -0,0 +1,4833 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import static com.android.tools.r8.TestCondition.D8_COMPILER;
+import static com.android.tools.r8.TestCondition.R8_COMPILER;
+import static com.android.tools.r8.TestCondition.any;
+import static com.android.tools.r8.TestCondition.match;
+import static com.android.tools.r8.TestCondition.runtimes;
+
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.R8RunArtTestsTest.DexTool;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.Multimap;
+import java.util.Collection;
+
+public class JctfTestSpecifications {
+
+ public enum Outcome {
+ PASSES,
+ FAILS_WITH_ART,
+ TIMEOUTS_WITH_ART,
+ FLAKY_WITH_ART
+ }
+
+ public static final Multimap<String, TestCondition> failuresToTriage =
+ new ImmutableListMultimap.Builder<String, TestCondition>()
+
+ .put("math.BigInteger.nextProbablePrime.BigInteger_nextProbablePrime_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.ArithmeticException
+
+ .put("math.BigInteger.ConstructorLjava_lang_String.BigInteger_Constructor_A02", any())
+ // 1) t03
+ // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
+
+ .put("lang.StringBuffer.insertILjava_lang_Object.StringBuffer_insert_A01",
+ match(runtimes(DexVm.ART_DEFAULT)))
+ // 1) t01
+ // java.lang.StringIndexOutOfBoundsException: length=21; regionStart=0; regionLength=42
+
+ .put("lang.StringBuffer.serialization.StringBuffer_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/StringBuffer/serialization/StringBuffer_serialization_A01.golden.ser
+
+ .put(
+ "lang.CloneNotSupportedException.serialization.CloneNotSupportedException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/CloneNotSupportedException/serialization/CloneNotSupportedException_serialization_A01.golden.0.ser
+
+ .put("lang.NumberFormatException.serialization.NumberFormatException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NumberFormatException/serialization/NumberFormatException_serialization_A01.golden.0.ser
+
+ .put("lang.StrictMath.roundF.StrictMath_round_A01",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+ // 1) t01
+ // java.lang.AssertionError: Wrong result produced for argument: 0.49999997 expected:<1.0> but was:<0.0>
+
+ .put("lang.StrictMath.roundD.StrictMath_round_A01",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+ // 1) t01
+ // java.lang.AssertionError: Wrong result produced for argument: 0.49999999999999994 expected:<1.0> but was:<0.0>
+
+ .put("lang.StrictMath.atan2DD.StrictMath_atan2_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad value returned for arguments: 2.225073858507201E-308 0.9999999999999999 expected:<2.2250738585072014E-308> but was:<2.225073858507201E-308>
+
+ .put("lang.Thread.stop.Thread_stop_A05", any())
+ // 1) t01
+ // java.lang.UnsupportedOperationException
+ // 2) t02
+ // java.lang.UnsupportedOperationException
+
+ .put("lang.Thread.resume.Thread_resume_A02", any())
+ // 1) t01
+ // java.lang.UnsupportedOperationException
+ // 2) t02
+ // java.lang.UnsupportedOperationException
+
+ .put("lang.Thread.suspend.Thread_suspend_A02", any())
+ // 1) t01
+ // java.lang.UnsupportedOperationException
+ // 2) t02
+ // java.lang.UnsupportedOperationException
+ // 3) t03
+ // java.lang.UnsupportedOperationException
+ // 4) t04
+ // java.lang.UnsupportedOperationException
+
+ .put("lang.Thread.stop.Thread_stop_A03", any())
+ // 1) t01
+ // java.lang.UnsupportedOperationException
+ // 2) t02
+ // java.lang.UnsupportedOperationException
+ // 3) t03
+ // java.lang.UnsupportedOperationException
+ // 4) t04
+ // java.lang.UnsupportedOperationException
+ // 5) t05
+ // java.lang.UnsupportedOperationException
+ // 6) t06
+ // java.lang.UnsupportedOperationException
+
+ .put("lang.Thread.interrupt.Thread_interrupt_A03", any())
+ // 1) t01
+ // java.lang.AssertionError: Not implemented
+
+ .put("lang.Thread.stop.Thread_stop_A04", any())
+ // 1) t01
+ // java.lang.UnsupportedOperationException
+
+ .put(
+ "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A01",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1)))
+ // 1) t01
+ // java.lang.OutOfMemoryError: pthread_create (-8589934591GB stack) failed: Resource temporarily unavailable
+
+ .put("lang.Thread.getUncaughtExceptionHandler.Thread_getUncaughtExceptionHandler_A01",
+ any())
+ // 1) t03
+ // java.lang.AssertionError: expected null, but was:<java.lang.ThreadGroup[name=main,maxpri=10]>
+
+ .put("lang.Thread.getStackTrace.Thread_getStackTrace_A02", any())
+ // 1) t01
+ // org.junit.ComparisonFailure: expected:<get[]StackTrace> but was:<get[Thread]StackTrace>
+
+ .put("lang.Thread.enumerate$Ljava_lang_Thread.Thread_enumerate_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: test failed with error:java.lang.SecurityException
+
+ .put("lang.Thread.countStackFrames.Thread_countStackFrames_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalThreadStateException
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalThreadStateException
+ // 3) t03
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalThreadStateException
+ // 4) t04
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalThreadStateException
+
+ .put("lang.Thread.getAllStackTraces.Thread_getAllStackTraces_A01",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+ // 1) t01
+ // java.lang.AssertionError
+
+ .put("lang.Thread.destroy.Thread_destroy_A01", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.NoSuchMethodError> but was<java.lang.UnsupportedOperationException>
+ // Caused by: java.lang.UnsupportedOperationException
+ // 2) t02
+ // java.lang.Exception: Unexpected exception, expected<java.lang.NoSuchMethodError> but was<java.lang.UnsupportedOperationException>
+ // Caused by: java.lang.UnsupportedOperationException
+
+ .put("lang.Thread.isAlive.Thread_isAlive_A01", any())
+ // 1) t01
+ // java.lang.UnsupportedOperationException
+
+ .put("lang.Thread.stopLjava_lang_Throwable.Thread_stop_A04", any())
+ // 1) t01
+ // java.lang.UnsupportedOperationException
+
+ .put("lang.Thread.stopLjava_lang_Throwable.Thread_stop_A03", any())
+ // 1) t01
+ // java.lang.UnsupportedOperationException
+ // 2) t02
+ // java.lang.UnsupportedOperationException
+ // 3) t03
+ // java.lang.UnsupportedOperationException
+ // 4) t05
+ // java.lang.UnsupportedOperationException
+ // 5) t06
+ // java.lang.UnsupportedOperationException
+
+ .put("lang.Thread.stopLjava_lang_Throwable.Thread_stop_A05", any())
+ // 1) t02
+ // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.UnsupportedOperationException>
+ // Caused by: java.lang.UnsupportedOperationException
+
+ .put("lang.Thread.getPriority.Thread_getPriority_A01", any())
+ // 1) t02
+ // java.lang.UnsupportedOperationException
+
+ .put("lang.Thread.getContextClassLoader.Thread_getContextClassLoader_A03",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+ // 1) t01
+ // java.lang.AssertionError: improper ClassLoader expected same:<null> was not:<dalvik.system.PathClassLoader[DexPathList[[dex file "/tmp/junit7794202178392390143/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]]>
+
+ .put("lang.OutOfMemoryError.serialization.OutOfMemoryError_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/OutOfMemoryError/serialization/OutOfMemoryError_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.RuntimePermission.ConstructorLjava_lang_StringLjava_lang_String.RuntimePermission_Constructor_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<a> but was:<null>
+ // 2) t02
+ // java.lang.AssertionError: expected:<2/3/2> but was:<null>
+
+ .put("lang.RuntimePermission.serialization.RuntimePermission_serialization_A01", any())
+ // 1) t01
+ // java.lang.RuntimeException: Failed to detect comparator
+ // 2) t02
+ // java.lang.RuntimeException: Failed to detect comparator
+
+ .put(
+ "lang.RuntimePermission.ConstructorLjava_lang_StringLjava_lang_String.RuntimePermission_Constructor_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.RuntimePermission.ConstructorLjava_lang_StringLjava_lang_String.RuntimePermission_Constructor_A03",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A17", any())
+ // 1) t01
+ // java.lang.AssertionError: Not implemented
+
+ .put(
+ "lang.RuntimePermission.ConstructorLjava_lang_String.RuntimePermission_Constructor_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.RuntimePermission.ConstructorLjava_lang_String.RuntimePermission_Constructor_A03",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+ .put(
+ "lang.RuntimePermission.ConstructorLjava_lang_String.RuntimePermission_Constructor_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<a> but was:<null>
+ // 2) t02
+ // java.lang.AssertionError: expected:<2/3/2> but was:<null>
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A26", any())
+ // 1) t01
+ // java.lang.AssertionError: Not implemented
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A04", any())
+ // 1) t01
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String/Thread_Constructor_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String.Thread_Constructor_A04" on path: DexPathList[[dex file "/tmp/junit4094594533964383293/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ/Thread_Constructor_A05;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A05" on path: DexPathList[[dex file "/tmp/junit4094594533964383293/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t03
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String/Thread_Constructor_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String.Thread_Constructor_A04" on path: DexPathList[[dex file "/tmp/junit4094594533964383293/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t04
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ/Thread_Constructor_A05;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A05" on path: DexPathList[[dex file "/tmp/junit4094594533964383293/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A03", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setContextClassLoaderLjava_lang_ClassLoader/Thread_setContextClassLoader_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setContextClassLoaderLjava_lang_ClassLoader.Thread_setContextClassLoader_A02" on path: DexPathList[[dex file "/tmp/junit8983002984475576815/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setContextClassLoaderLjava_lang_ClassLoader/Thread_setContextClassLoader_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setContextClassLoaderLjava_lang_ClassLoader.Thread_setContextClassLoader_A02" on path: DexPathList[[dex file "/tmp/junit8983002984475576815/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A25", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setDefaultUncaughtExceptionHandler/Thread_setDefaultUncaughtExceptionHandler_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setDefaultUncaughtExceptionHandler.Thread_setDefaultUncaughtExceptionHandler_A02" on path: DexPathList[[dex file "/tmp/junit1629291049961551929/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setDefaultUncaughtExceptionHandler/Thread_setDefaultUncaughtExceptionHandler_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setDefaultUncaughtExceptionHandler.Thread_setDefaultUncaughtExceptionHandler_A02" on path: DexPathList[[dex file "/tmp/junit1629291049961551929/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A06", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/Constructor/SecurityManager_Constructor_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.Constructor.SecurityManager_Constructor_A01" on path: DexPathList[[dex file "/tmp/junit4316252965733666132/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/Constructor/SecurityManager_Constructor_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.Constructor.SecurityManager_Constructor_A01" on path: DexPathList[[dex file "/tmp/junit4316252965733666132/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A21", any())
+ // 1) t02
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkPackageDefinitionLjava_lang_String/SecurityManager_checkPackageDefinition_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkPackageDefinitionLjava_lang_String.SecurityManager_checkPackageDefinition_A01" on path: DexPathList[[dex file "/tmp/junit622469116149972008/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A22", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredClasses/Class_getDeclaredClasses_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredClasses.Class_getDeclaredClasses_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructors/Class_getDeclaredConstructors_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructors.Class_getDeclaredConstructors_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t03
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructor$Ljava_lang_Class/Class_getDeclaredConstructor_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructor$Ljava_lang_Class.Class_getDeclaredConstructor_A03" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t04
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFields/Class_getDeclaredFields_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFields.Class_getDeclaredFields_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 5) t05
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFieldLjava_lang_String/Class_getDeclaredField_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFieldLjava_lang_String.Class_getDeclaredField_A04" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 6) t06
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethods/Class_getDeclaredMethods_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethods.Class_getDeclaredMethods_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 7) t07
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethodLjava_lang_String$Ljava_lang_Class/Class_getDeclaredMethod_A05;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethodLjava_lang_String$Ljava_lang_Class.Class_getDeclaredMethod_A05" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 8) t08
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredClasses/Class_getDeclaredClasses_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredClasses.Class_getDeclaredClasses_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 9) t09
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructors/Class_getDeclaredConstructors_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructors.Class_getDeclaredConstructors_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 10) t10
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructor$Ljava_lang_Class/Class_getDeclaredConstructor_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructor$Ljava_lang_Class.Class_getDeclaredConstructor_A03" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 11) t11
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFields/Class_getDeclaredFields_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFields.Class_getDeclaredFields_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 12) t12
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFieldLjava_lang_String/Class_getDeclaredField_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFieldLjava_lang_String.Class_getDeclaredField_A04" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 13) t13
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethods/Class_getDeclaredMethods_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethods.Class_getDeclaredMethods_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 14) t14
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethodLjava_lang_String$Ljava_lang_Class/Class_getDeclaredMethod_A05;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethodLjava_lang_String$Ljava_lang_Class.Class_getDeclaredMethod_A05" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 15) t15
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/newInstance/Class_newInstance_A07;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.newInstance.Class_newInstance_A07" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 16) t16
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/newInstance/Class_newInstance_A07;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.newInstance.Class_newInstance_A07" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 17) t17
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getClasses/Class_getClasses_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getClasses.Class_getClasses_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 18) t18
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructors/Class_getConstructors_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructors.Class_getConstructors_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 19) t19
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructor$Ljava_lang_Class/Class_getConstructor_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructor$Ljava_lang_Class.Class_getConstructor_A04" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 20) t20
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFields/Class_getFields_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFields.Class_getFields_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 21) t21
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFieldLjava_lang_String/Class_getField_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A04" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 22) t22
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethods/Class_getMethods_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethods.Class_getMethods_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 23) t23
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethodLjava_lang_String$Ljava_lang_Class/Class_getMethod_A05;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A05" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 24) t24
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getClasses/Class_getClasses_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getClasses.Class_getClasses_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 25) t25
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructors/Class_getConstructors_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructors.Class_getConstructors_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 26) t26
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructor$Ljava_lang_Class/Class_getConstructor_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructor$Ljava_lang_Class.Class_getConstructor_A04" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 27) t27
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFields/Class_getFields_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFields.Class_getFields_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 28) t28
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFieldLjava_lang_String/Class_getField_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A04" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 29) t29
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethods/Class_getMethods_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethods.Class_getMethods_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 30) t30
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethodLjava_lang_String$Ljava_lang_Class/Class_getMethod_A05;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A05" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 31) t31
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkMemberAccessLjava_lang_ClassI/SecurityManager_checkMemberAccess_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkMemberAccessLjava_lang_ClassI.SecurityManager_checkMemberAccess_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 32) t32
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkMemberAccessLjava_lang_ClassI/SecurityManager_checkMemberAccess_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkMemberAccessLjava_lang_ClassI.SecurityManager_checkMemberAccess_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A11", any())
+ // 1) t01
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setInLjava_io_InputStream/System_setIn_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setInLjava_io_InputStream.System_setIn_A02" on path: DexPathList[[dex file "/tmp/junit8702631411569316964/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setOutLjava_io_PrintStream/System_setOut_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setOutLjava_io_PrintStream.System_setOut_A02" on path: DexPathList[[dex file "/tmp/junit8702631411569316964/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t03
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setErrLjava_io_PrintStream/System_setErr_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setErrLjava_io_PrintStream.System_setErr_A02" on path: DexPathList[[dex file "/tmp/junit8702631411569316964/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t04
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setInLjava_io_InputStream/System_setIn_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setInLjava_io_InputStream.System_setIn_A02" on path: DexPathList[[dex file "/tmp/junit8702631411569316964/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 5) t05
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setOutLjava_io_PrintStream/System_setOut_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setOutLjava_io_PrintStream.System_setOut_A02" on path: DexPathList[[dex file "/tmp/junit8702631411569316964/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 6) t06
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setErrLjava_io_PrintStream/System_setErr_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setErrLjava_io_PrintStream.System_setErr_A02" on path: DexPathList[[dex file "/tmp/junit8702631411569316964/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A08", any())
+ // 1) t01
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/exitI/Runtime_exit_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A04" on path: DexPathList[[dex file "/tmp/junit161743829053407699/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/haltI/Runtime_halt_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A03" on path: DexPathList[[dex file "/tmp/junit161743829053407699/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t03
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/exitI/Runtime_exit_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A04" on path: DexPathList[[dex file "/tmp/junit161743829053407699/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t04
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/haltI/Runtime_halt_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A03" on path: DexPathList[[dex file "/tmp/junit161743829053407699/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A16", any())
+ // 1) t01
+ // java.lang.AssertionError: Not implemented
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A12", any())
+ // 1) t01
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/interrupt/Thread_interrupt_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.interrupt.Thread_interrupt_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/stop/Thread_stop_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.stop.Thread_stop_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t03
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/suspend/Thread_suspend_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.suspend.Thread_suspend_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t04
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/resume/Thread_resume_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.resume.Thread_resume_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 5) t05
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setDaemonZ/Thread_setDaemon_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setDaemonZ.Thread_setDaemon_A03" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 6) t06
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setPriorityI/Thread_setPriority_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setPriorityI.Thread_setPriority_A02" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 7) t07
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setNameLjava_lang_String/Thread_setName_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setNameLjava_lang_String.Thread_setName_A02" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 8) t08
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setUncaughtExceptionHandler/Thread_setUncaughtExceptionHandler_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setUncaughtExceptionHandler.Thread_setUncaughtExceptionHandler_A02" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 9) t09
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/interrupt/Thread_interrupt_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.interrupt.Thread_interrupt_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 10) t10
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/suspend/Thread_suspend_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.suspend.Thread_suspend_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 11) t11
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/stop/Thread_stop_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.stop.Thread_stop_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 12) t12
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/resume/Thread_resume_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.resume.Thread_resume_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 13) t13
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setDaemonZ/Thread_setDaemon_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setDaemonZ.Thread_setDaemon_A03" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 14) t14
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setPriorityI/Thread_setPriority_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setPriorityI.Thread_setPriority_A02" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 15) t15
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setNameLjava_lang_String/Thread_setName_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setNameLjava_lang_String.Thread_setName_A02" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 16) t16
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setUncaughtExceptionHandler/Thread_setUncaughtExceptionHandler_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setUncaughtExceptionHandler.Thread_setUncaughtExceptionHandler_A02" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 17) t17
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkAccessLjava_lang_Thread/SecurityManager_checkAccess_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkAccessLjava_lang_Thread.SecurityManager_checkAccess_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 18) t18
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkAccessLjava_lang_Thread/SecurityManager_checkAccess_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkAccessLjava_lang_Thread.SecurityManager_checkAccess_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A24", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/getAllStackTraces/Thread_getAllStackTraces_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.getAllStackTraces.Thread_getAllStackTraces_A02" on path: DexPathList[[dex file "/tmp/junit6143640280960303446/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/getStackTrace/Thread_getStackTrace_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.getStackTrace.Thread_getStackTrace_A03" on path: DexPathList[[dex file "/tmp/junit6143640280960303446/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t03
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/getStackTrace/Thread_getStackTrace_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.getStackTrace.Thread_getStackTrace_A03" on path: DexPathList[[dex file "/tmp/junit6143640280960303446/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t04
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/getAllStackTraces/Thread_getAllStackTraces_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.getAllStackTraces.Thread_getAllStackTraces_A02" on path: DexPathList[[dex file "/tmp/junit6143640280960303446/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A23", any())
+ // 1) t01
+ // java.lang.AssertionError: Not implemented
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A18", any())
+ // 1) t01
+ // java.lang.AssertionError: Not implemented
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A19", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/loadLjava_lang_String/Runtime_load_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.loadLjava_lang_String.Runtime_load_A03" on path: DexPathList[[dex file "/tmp/junit3066867775929885441/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/loadLibraryLjava_lang_String/Runtime_loadLibrary_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.loadLibraryLjava_lang_String.Runtime_loadLibrary_A03" on path: DexPathList[[dex file "/tmp/junit3066867775929885441/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t03
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/loadLjava_lang_String/Runtime_load_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.loadLjava_lang_String.Runtime_load_A03" on path: DexPathList[[dex file "/tmp/junit3066867775929885441/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t04
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/loadLibraryLjava_lang_String/Runtime_loadLibrary_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.loadLibraryLjava_lang_String.Runtime_loadLibrary_A03" on path: DexPathList[[dex file "/tmp/junit3066867775929885441/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A07", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/getenv/System_getenv_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.getenv.System_getenv_A04" on path: DexPathList[[dex file "/tmp/junit8429314639688357329/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/getenvLjava_lang_String/System_getenv_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.getenvLjava_lang_String.System_getenv_A03" on path: DexPathList[[dex file "/tmp/junit8429314639688357329/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t03
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ProcessBuilder/environment/ProcessBuilder_environment_A07;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ProcessBuilder.environment.ProcessBuilder_environment_A07" on path: DexPathList[[dex file "/tmp/junit8429314639688357329/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t04
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/getenv/System_getenv_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.getenv.System_getenv_A04" on path: DexPathList[[dex file "/tmp/junit8429314639688357329/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 5) t05
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/getenvLjava_lang_String/System_getenv_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.getenvLjava_lang_String.System_getenv_A03" on path: DexPathList[[dex file "/tmp/junit8429314639688357329/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 6) t06
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ProcessBuilder/environment/ProcessBuilder_environment_A07;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ProcessBuilder.environment.ProcessBuilder_environment_A07" on path: DexPathList[[dex file "/tmp/junit8429314639688357329/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A20", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredClasses/Class_getDeclaredClasses_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredClasses.Class_getDeclaredClasses_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructors/Class_getDeclaredConstructors_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructors.Class_getDeclaredConstructors_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t03
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructor$Ljava_lang_Class/Class_getDeclaredConstructor_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructor$Ljava_lang_Class.Class_getDeclaredConstructor_A03" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t04
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFields/Class_getDeclaredFields_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFields.Class_getDeclaredFields_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 5) t05
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFieldLjava_lang_String/Class_getDeclaredField_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFieldLjava_lang_String.Class_getDeclaredField_A04" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 6) t06
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethods/Class_getDeclaredMethods_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethods.Class_getDeclaredMethods_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 7) t07
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethodLjava_lang_String$Ljava_lang_Class/Class_getDeclaredMethod_A05;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethodLjava_lang_String$Ljava_lang_Class.Class_getDeclaredMethod_A05" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 8) t08
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredClasses/Class_getDeclaredClasses_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredClasses.Class_getDeclaredClasses_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 9) t09
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructors/Class_getDeclaredConstructors_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructors.Class_getDeclaredConstructors_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 10) t10
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructor$Ljava_lang_Class/Class_getDeclaredConstructor_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructor$Ljava_lang_Class.Class_getDeclaredConstructor_A03" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 11) t11
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFields/Class_getDeclaredFields_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFields.Class_getDeclaredFields_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 12) t12
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFieldLjava_lang_String/Class_getDeclaredField_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFieldLjava_lang_String.Class_getDeclaredField_A04" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 13) t13
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethods/Class_getDeclaredMethods_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethods.Class_getDeclaredMethods_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 14) t14
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethodLjava_lang_String$Ljava_lang_Class/Class_getDeclaredMethod_A05;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethodLjava_lang_String$Ljava_lang_Class.Class_getDeclaredMethod_A05" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 15) t15
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/newInstance/Class_newInstance_A07;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.newInstance.Class_newInstance_A07" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 16) t16
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/newInstance/Class_newInstance_A07;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.newInstance.Class_newInstance_A07" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 17) t17
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getClasses/Class_getClasses_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getClasses.Class_getClasses_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 18) t18
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructors/Class_getConstructors_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructors.Class_getConstructors_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 19) t19
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructor$Ljava_lang_Class/Class_getConstructor_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructor$Ljava_lang_Class.Class_getConstructor_A04" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 20) t20
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFields/Class_getFields_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFields.Class_getFields_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 21) t21
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFieldLjava_lang_String/Class_getField_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A04" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 22) t22
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethods/Class_getMethods_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethods.Class_getMethods_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 23) t23
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethodLjava_lang_String$Ljava_lang_Class/Class_getMethod_A05;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A05" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 24) t24
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getClasses/Class_getClasses_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getClasses.Class_getClasses_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 25) t25
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructors/Class_getConstructors_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructors.Class_getConstructors_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 26) t26
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructor$Ljava_lang_Class/Class_getConstructor_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructor$Ljava_lang_Class.Class_getConstructor_A04" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 27) t27
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFields/Class_getFields_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFields.Class_getFields_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 28) t28
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFieldLjava_lang_String/Class_getField_A04;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A04" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 29) t29
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethods/Class_getMethods_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethods.Class_getMethods_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 30) t30
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethodLjava_lang_String$Ljava_lang_Class/Class_getMethod_A05;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A05" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 31) t32
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkPackageAccessLjava_lang_String/SecurityManager_checkPackageAccess_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkPackageAccessLjava_lang_String.SecurityManager_checkPackageAccess_A01" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A15", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getProtectionDomain/Class_getProtectionDomain_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getProtectionDomain.Class_getProtectionDomain_A02" on path: DexPathList[[dex file "/tmp/junit2159138358960857439/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getProtectionDomain/Class_getProtectionDomain_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getProtectionDomain.Class_getProtectionDomain_A02" on path: DexPathList[[dex file "/tmp/junit2159138358960857439/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A05", any())
+ // 1) t01
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setSecurityManagerLjava_lang_SecurityManager/System_setSecurityManager_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setSecurityManagerLjava_lang_SecurityManager.System_setSecurityManager_A01" on path: DexPathList[[dex file "/tmp/junit5991060062859827030/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setSecurityManagerLjava_lang_SecurityManager/System_setSecurityManager_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setSecurityManagerLjava_lang_SecurityManager.System_setSecurityManager_A01" on path: DexPathList[[dex file "/tmp/junit5991060062859827030/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A09", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/addShutdownHookLjava_lang_Thread/Runtime_addShutdownHook_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A01" on path: DexPathList[[dex file "/tmp/junit719694264001364171/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/removeShutdownHookLjava_lang_Thread/Runtime_removeShutdownHook_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A01" on path: DexPathList[[dex file "/tmp/junit719694264001364171/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t03
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/addShutdownHookLjava_lang_Thread/Runtime_addShutdownHook_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A01" on path: DexPathList[[dex file "/tmp/junit719694264001364171/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t04
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/removeShutdownHookLjava_lang_Thread/Runtime_removeShutdownHook_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A01" on path: DexPathList[[dex file "/tmp/junit719694264001364171/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A10", any())
+ // 1) t01
+ // java.lang.AssertionError: Not implemented
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A01", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ClassLoader/Constructor/ClassLoader_Constructor_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ClassLoader.Constructor.ClassLoader_Constructor_A02" on path: DexPathList[[dex file "/tmp/junit6765412840574788386/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ClassLoader/ConstructorLjava_lang_ClassLoader/ClassLoader_Constructor_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ClassLoader.ConstructorLjava_lang_ClassLoader.ClassLoader_Constructor_A02" on path: DexPathList[[dex file "/tmp/junit6765412840574788386/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t03
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ClassLoader/Constructor/ClassLoader_Constructor_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ClassLoader.Constructor.ClassLoader_Constructor_A02" on path: DexPathList[[dex file "/tmp/junit6765412840574788386/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t04
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ClassLoader/ConstructorLjava_lang_ClassLoader/ClassLoader_Constructor_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ClassLoader.ConstructorLjava_lang_ClassLoader.ClassLoader_Constructor_A02" on path: DexPathList[[dex file "/tmp/junit6765412840574788386/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A06",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A14", any())
+ // 1) t01
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/interrupt/ThreadGroup_interrupt_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.interrupt.ThreadGroup_interrupt_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/stop/ThreadGroup_stop_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.stop.ThreadGroup_stop_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t03
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/suspend/ThreadGroup_suspend_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.suspend.ThreadGroup_suspend_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t04
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/resume/ThreadGroup_resume_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.resume.ThreadGroup_resume_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 5) t05
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/setDaemonZ/ThreadGroup_setDaemon_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.setDaemonZ.ThreadGroup_setDaemon_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 6) t06
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/setMaxPriorityI/ThreadGroup_setMaxPriority_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 7) t07
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/getParent/ThreadGroup_getParent_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.getParent.ThreadGroup_getParent_A03" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 8) t08
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/enumerate$ThreadGroup/ThreadGroup_enumerate_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.enumerate$ThreadGroup.ThreadGroup_enumerate_A03" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 9) t09
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/interrupt/ThreadGroup_interrupt_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.interrupt.ThreadGroup_interrupt_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 10) t10
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/suspend/ThreadGroup_suspend_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.suspend.ThreadGroup_suspend_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 11) t11
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/stop/ThreadGroup_stop_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.stop.ThreadGroup_stop_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 12) t12
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/resume/ThreadGroup_resume_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.resume.ThreadGroup_resume_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 13) t13
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/setDaemonZ/ThreadGroup_setDaemon_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.setDaemonZ.ThreadGroup_setDaemon_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 14) t14
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/setMaxPriorityI/ThreadGroup_setMaxPriority_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 15) t15
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/getParent/ThreadGroup_getParent_A02;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.getParent.ThreadGroup_getParent_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 16) t16
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/enumerate$ThreadGroup/ThreadGroup_enumerate_A03;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.enumerate$ThreadGroup.ThreadGroup_enumerate_A03" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 17) t17
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkAccessLjava_lang_ThreadGroup/SecurityManager_checkAccess_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkAccessLjava_lang_ThreadGroup.SecurityManager_checkAccess_A01" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 18) t18
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkAccessLjava_lang_ThreadGroup/SecurityManager_checkAccess_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkAccessLjava_lang_ThreadGroup.SecurityManager_checkAccess_A01" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A05",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A02",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 3) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A04",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A03",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A01",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A07",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.UnsupportedOperationException>
+ // Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
+
+ .put(
+ "lang.ClassLoader.setPackageAssertionStatusLjava_lang_StringZ.ClassLoader_setPackageAssertionStatus_A02",
+ any())
+ // 1) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.setPackageAssertionStatusLjava_lang_StringZ.ClassLoader_setPackageAssertionStatus_A01",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.setPackageAssertionStatusLjava_lang_StringZ.ClassLoader_setPackageAssertionStatus_A03",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.loadClassLjava_lang_StringZ.ClassLoader_loadClass_A03", any())
+ // 1) t03
+ // java.lang.AssertionError: ClassNotFoundException expected for class: java/lang/Object
+
+ .put("lang.ClassLoader.loadClassLjava_lang_StringZ.ClassLoader_loadClass_A01", any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 3) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.loadClassLjava_lang_StringZ.ClassLoader_loadClass_A04", any())
+ // 1) t01(com.google.jctf.test.lib.java.lang.ClassLoader.loadClassLjava_lang_StringZ.ClassLoader_loadClass_A04)
+ // java.lang.AssertionError: NoClassDefFoundError expected for class: com/google/jctf/test/lib/java/lang/ClassLoader/loadClassLjava_lang_StringZ/ClassLoader_loadClass_A04
+
+ .put(
+ "lang.ClassLoader.definePackageLjava_lang_String6Ljava_net_URL.ClassLoader_definePackage_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: java.lang.ClassNotFoundException: com.android.okhttp.HttpHandler
+ // Caused by: java.lang.ClassNotFoundException: com.android.okhttp.HttpHandler
+ // Caused by: java.lang.ClassNotFoundException: com.android.okhttp.HttpHandler
+ // Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available
+
+ .put(
+ "lang.ClassLoader.definePackageLjava_lang_String6Ljava_net_URL.ClassLoader_definePackage_A01",
+ any())
+ // 1) t02
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.ClassLoader.definePackageLjava_lang_String6Ljava_net_URL.ClassLoader_definePackage_A03",
+ any())
+ // 1) t02
+ // java.lang.AssertionError: IllegalArgumentException expected for package name: name
+ // 2) t03
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A05",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.getResourceLjava_lang_String.ClassLoader_getResource_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Resource not found: java/lang/ClassLoader.class
+ // 2) t02
+ // java.lang.AssertionError: Resource not found:
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A01",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A02",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 3) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 4) t04
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A06",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A03",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A07",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.UnsupportedOperationException>
+ // Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A04",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.setSignersLjava_lang_Class$Ljava_lang_Object.ClassLoader_setSigners_A01",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t04
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.ClassLoader.clearAssertionStatus.ClassLoader_clearAssertionStatus_A01", any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 3) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 4) t04
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.Constructor.ClassLoader_Constructor_A02", any())
+ // 1) t02
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+
+ .put(
+ "lang.ClassLoader.getSystemResourceLjava_lang_String.ClassLoader_getSystemResource_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Resource not found: java/lang/ClassLoader.class
+ // 2) t02
+ // java.lang.AssertionError: Resource not found:
+
+ .put("lang.ClassLoader.getResourcesLjava_lang_String.ClassLoader_getResources_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Resource not found: java/lang/ClassLoader.class
+ // 2) t04
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.ClassLoader.resolveClassLjava_lang_Class.ClassLoader_resolveClass_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.ClassLoader.getResourceAsStreamLjava_lang_String.ClassLoader_getResourceAsStream_A01",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.InputStream.close()' on a null object reference
+ // 2) t04
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.ClassLoader.findLoadedClassLjava_lang_String.ClassLoader_findLoadedClass_A01",
+ any())
+ // 1) initializationError
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ClassLoader/findLoadedClassLjava_lang_String/TestLoader;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ClassLoader.findLoadedClassLjava_lang_String.TestLoader" on path: DexPathList[[dex file "/tmp/junit1789265657215742712/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A02",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 3) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.resolveClassLjava_lang_Class.ClassLoader_resolveClass_A01", any())
+ // 1) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.setDefaultAssertionStatusZ.ClassLoader_setDefaultAssertionStatus_A01",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A05",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A01",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A06",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A08",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.UnsupportedOperationException>
+ // Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A03",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 3) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 4) t04
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.getSystemResourcesLjava_lang_String.ClassLoader_getSystemResources_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Resource not found: java/lang/ClassLoader.class
+ // 2) t02
+ // java.lang.AssertionError: Resource not found:
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A07",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A09",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.UnsupportedOperationException>
+ // Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
+
+ .put(
+ "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A04",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.defineClass$BII.ClassLoader_defineClass_A02", any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 3) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.getSystemResourceAsStreamLjava_lang_String.ClassLoader_getSystemResourceAsStream_A01",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.InputStream.close()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.getPackages.ClassLoader_getPackages_A01", any())
+ // 1) t02
+ // java.lang.AssertionError:
+ // Expected: (an array containing <package java.lang, Unknown, version 0.0> and an array containing <package java.security, Unknown, version 0.0> and an array containing <package java.util, Unknown, version 0.0>)
+ // but: an array containing <package java.lang, Unknown, version 0.0> was []
+ // 2) t04
+ // java.lang.AssertionError:
+ // Expected: an array containing <package com.google.jctf.test.lib.java.lang.ClassLoader.getPackages, Unknown, version 0.0>
+ // but: was []
+ // 3) t05
+ // java.lang.AssertionError: java.lang.ClassNotFoundException: com.android.okhttp.HttpHandler
+ // Caused by: java.lang.ClassNotFoundException: com.android.okhttp.HttpHandler
+ // Caused by: java.lang.ClassNotFoundException: com.android.okhttp.HttpHandler
+ // Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available
+
+ .put(
+ "lang.ClassLoader.setClassAssertionStatusLjava_lang_StringZ.ClassLoader_setClassAssertionStatus_A01",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.defineClass$BII.ClassLoader_defineClass_A03", any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.defineClass$BII.ClassLoader_defineClass_A01", any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.defineClass$BII.ClassLoader_defineClass_A04", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.UnsupportedOperationException>
+ // Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
+
+ .put("lang.ClassLoader.getParent.ClassLoader_getParent_A01", any())
+ // 1) t04
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.setClassAssertionStatusLjava_lang_StringZ.ClassLoader_setClassAssertionStatus_A04",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.ClassLoader.setClassAssertionStatusLjava_lang_StringZ.ClassLoader_setClassAssertionStatus_A02",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.ClassLoader.getParent.ClassLoader_getParent_A02", any())
+ // 1) t02
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put("lang.ClassLoader.getSystemClassLoader.ClassLoader_getSystemClassLoader_A02", any())
+ // 1) t02
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+
+ .put("lang.ClassLoader.ConstructorLjava_lang_ClassLoader.ClassLoader_Constructor_A02",
+ any())
+ // 1) t02
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+
+ .put("lang.ClassLoader.findSystemClassLjava_lang_String.ClassLoader_findSystemClass_A04",
+ any())
+ // 1) t01
+ // java.lang.ClassNotFoundException: Invalid name: com/google/jctf/test/lib/java/lang/ClassLoader/findSystemClassLjava_lang_String/ClassLoader_findSystemClass_A04
+
+ .put("lang.ClassLoader.getPackageLjava_lang_String.ClassLoader_getPackage_A01", any())
+ // 1) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Package.getName()' on a null object reference
+ // 2) t04
+ // java.lang.AssertionError: expected:<package com.google.jctf.test.lib.java.lang.ClassLoader.getPackageLjava_lang_String, Unknown, version 0.0> but was:<null>
+ // 3) t05
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.NoClassDefFoundError.serialization.NoClassDefFoundError_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NoClassDefFoundError/serialization/NoClassDefFoundError_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.TypeNotPresentException.serialization.TypeNotPresentException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/TypeNotPresentException/serialization/TypeNotPresentException_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.IndexOutOfBoundsException.serialization.IndexOutOfBoundsException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IndexOutOfBoundsException/serialization/IndexOutOfBoundsException_serialization_A01.golden.0.ser
+
+ .put("lang.Enum.serialization.Enum_serialization_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Enum/serialization/Enum_serialization_A01.golden.0.ser
+
+ .put("lang.Enum.ConstructorLjava_lang_StringI.Enum_Constructor_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+ .put("lang.InternalError.serialization.InternalError_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/InternalError/serialization/InternalError_serialization_A01.golden.0.ser
+
+ .put("lang.Error.serialization.Error_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Error/serialization/Error_serialization_A01.golden.0.ser
+
+ .put("lang.Runtime.loadLjava_lang_String.Runtime_load_A02", any())
+ // 1) t05
+ // java.lang.Exception: Unexpected exception, expected<java.lang.UnsatisfiedLinkError> but was<java.lang.AssertionError>
+ // Caused by: java.lang.AssertionError: Misconfiguration, java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[dex file "/tmp/junit955082866383345627/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]] couldn't find "libstub.so"
+ // 2) t06
+ // java.lang.Exception: Unexpected exception, expected<java.lang.UnsatisfiedLinkError> but was<java.lang.AssertionError>
+ // Caused by: java.lang.AssertionError: Misconfiguration, java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[dex file "/tmp/junit955082866383345627/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]] couldn't find "libstub.so"
+
+ .put("lang.Runtime.loadLjava_lang_String.Runtime_load_A05", any())
+ // 1) t01
+ // java.lang.AssertionError
+ // 2) t02
+ // java.lang.AssertionError
+
+ .put("lang.Runtime.loadLjava_lang_String.Runtime_load_A03", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.AssertionError
+
+ .put("lang.Runtime.loadLjava_lang_String.Runtime_load_A04", any())
+ // 1) t01
+ // java.lang.AssertionError: Misconfiguration, missing property: 'jctf.library.path'
+ // 2) t02
+ // java.lang.AssertionError
+
+ .put("lang.Runtime.exec$Ljava_lang_String.Runtime_exec_A02", any())
+ // 1) t01
+ // org.junit.ComparisonFailure: expected:<[t02]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exec$Ljava_lang_String.Runtime_exec_A02$T01
+ // ]>
+
+ .put("lang.Runtime.exec$Ljava_lang_String.Runtime_exec_A03", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<0> but was:<1>
+
+ .put("lang.Runtime.exec$Ljava_lang_String.Runtime_exec_A01", any())
+ // 1) t01
+ // org.junit.ComparisonFailure: expected:<[com google jctf test lib java lang Runtime]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoArgs
+ // ]>
+
+ .put("lang.Runtime.loadLibraryLjava_lang_String.Runtime_loadLibrary_A04", any())
+ // 1) t01
+ // java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[dex file "/tmp/junit958205418142169834/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]] couldn't find "libstub.so"
+ // 2) t02
+ // java.lang.AssertionError
+
+ .put("lang.Runtime.loadLibraryLjava_lang_String.Runtime_loadLibrary_A05", any())
+ // 1) t01
+ // java.lang.AssertionError
+ // 2) t02
+ // java.lang.AssertionError
+
+ .put("lang.Runtime.loadLibraryLjava_lang_String.Runtime_loadLibrary_A03", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.AssertionError
+
+ .put("lang.Runtime.execLjava_lang_String.Runtime_exec_A02", any())
+ // 1) t01
+ // org.junit.ComparisonFailure: expected:<[t02]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.execLjava_lang_String.Runtime_exec_A02$T01
+ // ]>
+
+ .put("lang.Runtime.execLjava_lang_String.Runtime_exec_A03", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<0> but was:<1>
+
+ .put("lang.Runtime.loadLibraryLjava_lang_String.Runtime_loadLibrary_A02", any())
+ // 1) t03
+ // java.lang.Exception: Unexpected exception, expected<java.lang.UnsatisfiedLinkError> but was<java.lang.AssertionError>
+ // Caused by: java.lang.AssertionError: Misconfiguration, missing property: 'jctf.library.path'
+
+ .put("lang.Runtime.traceMethodCallsZ.Runtime_traceMethodCalls_A01", any())
+ // 1) t01
+ // java.lang.UnsupportedOperationException
+
+ .put("lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A01", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A08", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A08$T01
+ // expected:<0> but was:<1>
+
+ .put("lang.Runtime.execLjava_lang_String.Runtime_exec_A01", any())
+ // 1) t01
+ // org.junit.ComparisonFailure: expected:<[com google jctf test lib java lang Runtime]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoArgs
+ // ]>
+
+ .put("lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A03", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A03$T01
+ // expected:<0> but was:<1>
+
+ .put("lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A07", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A07$T01
+ // expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A07$T02
+ // expected:<0> but was:<1>
+
+ .put("lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T01
+ // expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError:
+ // Expected: a string containing "TERMINATED_BY_EXCEPTION"
+ // but: was "Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T02
+ // "
+ // 3) t03
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T03
+ // expected:<0> but was:<1>
+ // 4) t04
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T04
+ // expected:<0> but was:<1>
+ // 5) t05
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T05
+ // expected:<0> but was:<1>
+ // 6) t06
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T06
+ // expected:<0> but was:<1>
+ // 7) t07
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T07
+ // expected:<0> but was:<1>
+ // 8) t08
+ // java.lang.AssertionError:
+ // Expected: a string containing "java.lang.UnknownError: Main Thread Exit"
+ // but: was "Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T08
+ // "
+
+ .put("lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A06", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A06$T01
+ // expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A06$T02
+ // expected:<0> but was:<1>
+
+ .put("lang.Runtime.execLjava_lang_String$Ljava_lang_String.Runtime_exec_A03", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<0> but was:<1>
+
+ .put("lang.Runtime.execLjava_lang_String$Ljava_lang_String.Runtime_exec_A02", any())
+ // 1) t01
+ // org.junit.ComparisonFailure: expected:<[t01]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoEnv
+ // ]>
+ // 2) t02
+ // org.junit.ComparisonFailure: expected:<[t02]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.execLjava_lang_String$Ljava_lang_String.Runtime_exec_A02$T02
+ // ]>
+
+ .put("lang.Runtime.execLjava_lang_String$Ljava_lang_String.Runtime_exec_A01", any())
+ // 1) t01
+ // org.junit.ComparisonFailure: expected:<[com google jctf test lib java lang Runtime]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoArgs
+ // ]>
+
+ .put("lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A02$T01
+ // expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A02$T02
+ // expected:<0> but was:<1>
+
+ .put("lang.Runtime.exec$Ljava_lang_String$Ljava_lang_String.Runtime_exec_A01", any())
+ // 1) t01
+ // org.junit.ComparisonFailure: expected:<[com google jctf test lib java lang Runtime]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoArgs
+ // ]>
+
+ .put("lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A01",
+ any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.Runtime.exec$Ljava_lang_String$Ljava_lang_String.Runtime_exec_A02", any())
+ // 1) t01
+ // org.junit.ComparisonFailure: expected:<[t01]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoEnv
+ // ]>
+ // 2) t02
+ // org.junit.ComparisonFailure: expected:<[t02]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exec$Ljava_lang_String$Ljava_lang_String.Runtime_exec_A02$T02
+ // ]>
+
+ .put("lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A03",
+ any())
+ // 1) t03
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A03$T03
+ // expected:<0> but was:<1>
+
+ .put(
+ "lang.Runtime.exec$Ljava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: actual=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoArgs
+ // : array lengths differed, expected.length=8 actual.length=9
+
+ .put("lang.Runtime.exec$Ljava_lang_String$Ljava_lang_String.Runtime_exec_A03", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<0> but was:<1>
+
+ .put(
+ "lang.Runtime.exec$Ljava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A02",
+ any())
+ // 1) t01
+ // org.junit.ComparisonFailure: expected:<[t01]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoEnv
+ // ]>
+ // 2) t02
+ // org.junit.ComparisonFailure: expected:<[t02]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exec$Ljava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A02$T02
+ // ]>
+
+ .put("lang.Runtime.haltI.Runtime_halt_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A02$T01
+ // expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A02$T02
+ // expected:<0> but was:<1>
+
+ .put(
+ "lang.Runtime.exec$Ljava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A03",
+ any())
+ // 1) t02
+ // java.lang.AssertionError: expected:<0> but was:<1>
+
+ .put("lang.Runtime.haltI.Runtime_halt_A03", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A03$T01
+ // expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A03$T02
+ // expected:<0> but was:<1>
+
+ .put("lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T01
+ // expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T02
+ // expected:<0> but was:<1>
+ // 3) t03
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T03
+ // expected:<0> but was:<1>
+ // 4) t04
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T04
+ // expected:<0> but was:<1>
+ // 5) t06
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T06
+ // expected:<0> but was:<1>
+ // 6) t07
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T07
+ // expected:<0> but was:<1>
+ // 7) t08
+ // java.lang.AssertionError:
+ // Expected: a string containing "java.lang.UnknownError: Main Thread Exit"
+ // but: was "Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T08
+ // "
+
+ .put("lang.Runtime.haltI.Runtime_halt_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A01$T01
+ // expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A01$T02
+ // expected:<0> but was:<1>
+
+ .put("lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A03", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A03$T03
+ // expected:<0> but was:<1>
+
+ .put("lang.Runtime.execLjava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A03",
+ any())
+ // 1) t02
+ // java.lang.AssertionError: expected:<0> but was:<1>
+
+ .put("lang.Runtime.execLjava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A01",
+ any())
+ // 1) t01
+ // org.junit.ComparisonFailure: expected:<[com google jctf test lib java lang Runtime]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoArgs
+ // ]>
+
+ .put("lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A02$T01
+ // expected:<0> but was:<1>
+
+ .put("lang.Runtime.exitI.Runtime_exit_A03", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A03$T01
+ // expected:<123> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: Process did not block but exited with code 1;
+ // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A03$T01
+ // 3) t03
+ // java.lang.AssertionError: Process did not block but exited with code 1;
+ // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A03$T03
+
+ .put("lang.Runtime.execLjava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A02",
+ any())
+ // 1) t01
+ // org.junit.ComparisonFailure: expected:<[t01]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoEnv
+ // ]>
+ // 2) t02
+ // org.junit.ComparisonFailure: expected:<[t02]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.execLjava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A02$T02
+ // ]>
+
+ .put("lang.Runtime.exitI.Runtime_exit_A04", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A04$T01
+ // expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A04$T02
+ // expected:<0> but was:<1>
+
+ .put("lang.NoSuchMethodException.serialization.NoSuchMethodException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NoSuchMethodException/serialization/NoSuchMethodException_serialization_A01.golden.0.ser
+
+ .put("lang.Runtime.exitI.Runtime_exit_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A01$T01
+ // expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A01$T02
+ // expected:<0> but was:<1>
+ // 3) t03
+ // java.lang.AssertionError: Process did not block but exited with code 1;
+ // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A01$T03
+ // out=
+ // 4) t04
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A01$T04
+ // expected:<-1> but was:<1>
+ // 5) t05
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A01$T05
+ // expected:<0> but was:<1>
+
+ .put("lang.Runtime.exitI.Runtime_exit_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T01
+ // expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: Process did not block but exited with code 1;
+ // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T02
+ // out=
+ // 3) t03
+ // java.lang.AssertionError: Process did not block but exited with code 1;
+ // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T03
+ // out=
+ // 4) t04
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T04
+ // expected:<0> but was:<1>
+ // 5) t05
+ // java.lang.AssertionError: Process did not block but exited with code 1;
+ // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T05
+ // out=
+ // 6) t06
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T06
+ // expected:<0> but was:<1>
+ // 7) t07
+ // java.lang.AssertionError: Process did not block but exited with code 1;
+ // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T07
+ // out=
+
+ .put("lang.InstantiationException.serialization.InstantiationException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/InstantiationException/serialization/InstantiationException_serialization_A01.golden.0.ser
+
+ .put("lang.Exception.serialization.Exception_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Exception/serialization/Exception_serialization_A01.golden.0.ser
+
+ .put("lang.StackOverflowError.serialization.StackOverflowError_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/StackOverflowError/serialization/StackOverflowError_serialization_A01.golden.0.ser
+
+ .put("lang.NoSuchFieldException.serialization.NoSuchFieldException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NoSuchFieldException/serialization/NoSuchFieldException_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.NegativeArraySizeException.serialization.NegativeArraySizeException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NegativeArraySizeException/serialization/NegativeArraySizeException_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.ArrayIndexOutOfBoundsException.serialization.ArrayIndexOutOfBoundsException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ArrayIndexOutOfBoundsException/serialization/ArrayIndexOutOfBoundsException_serialization_A01.golden.0.ser
+
+ .put("lang.VerifyError.serialization.VerifyError_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/VerifyError/serialization/VerifyError_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.IllegalArgumentException.serialization.IllegalArgumentException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IllegalArgumentException/serialization/IllegalArgumentException_serialization_A01.golden.0.ser
+
+ .put("lang.IllegalStateException.serialization.IllegalStateException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IllegalStateException/serialization/IllegalStateException_serialization_A01.golden.0.ser
+
+ .put("lang.Double.serialization.Double_serialization_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Double/serialization/Double_serialization_A01.golden.0.ser
+
+ .put("lang.Double.toStringD.Double_toString_A05", any())
+ // 1) t01
+ // org.junit.ComparisonFailure: expected:<0.001[0]> but was:<0.001[]>
+
+ .put("lang.ArithmeticException.serialization.ArithmeticException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ArithmeticException/serialization/ArithmeticException_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.ExceptionInInitializerError.serialization.ExceptionInInitializerError_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ExceptionInInitializerError/serialization/ExceptionInInitializerError_serialization_A01.golden.0.ser
+
+ .put("lang.ThreadLocal.Class.ThreadLocal_class_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Stale thread-local value was not finalized
+
+ .put("lang.Byte.serialization.Byte_serialization_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Byte/serialization/Byte_serialization_A01.golden.0.ser
+
+ .put("lang.Byte.parseByteLjava_lang_StringI.Byte_parseByte_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Parsed Byte instance from string:+1 radix:10
+
+ .put("lang.Byte.valueOfLjava_lang_StringI.Byte_valueOf_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Parsed Byte instance from string:+1 radix:10
+
+ .put("lang.Byte.valueOfLjava_lang_String.Byte_ValueOf_A02", any())
+ // 1) t02
+ // java.lang.AssertionError: Parsed Byte instance from string:+1
+
+ .put("lang.Byte.decodeLjava_lang_String.Byte_decode_A04", any())
+ // 1) t01
+ // java.lang.AssertionError: Decoded Byte instance from string:+1
+
+ .put("lang.LinkageError.serialization.LinkageError_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/LinkageError/serialization/LinkageError_serialization_A01.golden.0.ser
+
+ .put("lang.ClassCastException.serialization.ClassCastException_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ClassCastException/serialization/ClassCastException_serialization_A01.golden.0.ser
+
+ .put("lang.Byte.ConstructorLjava_lang_String.Byte_Constructor_A02", any())
+ // 1) t02
+ // java.lang.AssertionError: Parsed Byte instance from string:+1
+
+ .put("lang.Byte.parseByteLjava_lang_String.Byte_parseByte_A02", any())
+ // 1) t02
+ // java.lang.AssertionError: Parsed Byte instance from string:+1
+
+ .put("lang.NoSuchFieldError.serialization.NoSuchFieldError_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NoSuchFieldError/serialization/NoSuchFieldError_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.UnsupportedOperationException.serialization.UnsupportedOperationException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/UnsupportedOperationException/serialization/UnsupportedOperationException_serialization_A01.golden.0.ser
+
+ .put("lang.NoSuchMethodError.serialization.NoSuchMethodError_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NoSuchMethodError/serialization/NoSuchMethodError_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.IllegalMonitorStateException.serialization.IllegalMonitorStateException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IllegalMonitorStateException/serialization/IllegalMonitorStateException_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.StringIndexOutOfBoundsException.serialization.StringIndexOutOfBoundsException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/StringIndexOutOfBoundsException/serialization/StringIndexOutOfBoundsException_serialization_A01.golden.0.ser
+
+ .put("lang.SecurityException.serialization.SecurityException_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/SecurityException/serialization/SecurityException_serialization_A01.golden.0.ser
+
+ .put("lang.IllegalAccessError.serialization.IllegalAccessError_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IllegalAccessError/serialization/IllegalAccessError_serialization_A01.golden.0.ser
+
+ .put("lang.ArrayStoreException.serialization.ArrayStoreException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ArrayStoreException/serialization/ArrayStoreException_serialization_A01.golden.0.ser
+
+ .put("lang.UnknownError.serialization.UnknownError_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/UnknownError/serialization/UnknownError_serialization_A01.golden.0.ser
+
+ .put("lang.Boolean.serialization.Boolean_serialization_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Boolean/serialization/Boolean_serialization_A01.golden.0.ser
+
+ .put("lang.Integer.valueOfLjava_lang_StringI.Integer_valueOf_A02", any())
+ // 1) t07
+ // java.lang.AssertionError: NumberFormatException expected for input: +1 and radix: 10
+
+ .put("lang.Integer.serialization.Integer_serialization_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Integer/serialization/Integer_serialization_A01.golden.0.ser
+
+ .put("lang.Integer.parseIntLjava_lang_String.Integer_parseInt_A02", any())
+ // 1) t06
+ // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
+
+ .put("lang.Integer.getIntegerLjava_lang_StringI.Integer_getInteger_A02", any())
+ // 1) t03
+ // java.lang.AssertionError: expected:<6031769> but was:<1>
+
+ .put("lang.Integer.valueOfLjava_lang_String.Integer_valueOf_A02", any())
+ // 1) t07
+ // java.lang.AssertionError: NumberFormatException expected for input: +1
+
+ .put("lang.Integer.decodeLjava_lang_String.Integer_decode_A04", any())
+ // 1) t06
+ // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
+
+ .put("lang.Integer.parseIntLjava_lang_StringI.Integer_parseInt_A02", any())
+ // 1) t06
+ // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
+
+ .put("lang.Integer.getIntegerLjava_lang_StringLjava_lang_Integer.Integer_getInteger_A02",
+ any())
+ // 1) t03
+ // java.lang.AssertionError: expected:<6031769> but was:<1>
+
+ .put("lang.Integer.ConstructorLjava_lang_String.Integer_Constructor_A02", any())
+ // 1) t06
+ // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
+
+ .put("lang.Integer.getIntegerLjava_lang_String.Integer_getInteger_A02", any())
+ // 1) t03
+ // java.lang.AssertionError: expected null, but was:<1>
+
+ .put("lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01", any())
+ // 1) t04
+ // java.lang.AssertionError: reference is not enqueued after 2 sec
+
+ .put("lang.ref.SoftReference.isEnqueued.SoftReference_isEnqueued_A01", any())
+ // 1) t03
+ // java.lang.AssertionError: reference is not enqueued after 2 sec
+
+ .put("lang.ref.SoftReference.get.SoftReference_get_A01", any())
+ // 1) t03
+ // java.lang.AssertionError: expected null, but was:<[I@e2603b4>
+
+ .put("lang.ref.ReferenceQueue.poll.ReferenceQueue_poll_A01", any())
+ // 1) t03
+ // java.lang.AssertionError: reference is not enqueued after 2 sec
+
+ .put("lang.StackTraceElement.serialization.StackTraceElement_serialization_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/StackTraceElement/serialization/StackTraceElement_serialization_A01.golden.0.ser
+
+ .put("lang.ref.WeakReference.get.WeakReference_get_A01", any())
+ // 1) t03
+ // java.lang.AssertionError: expected null, but was:<[I@1b32f32>
+
+ .put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01", any())
+ // 1) t03
+ // java.lang.AssertionError: reference is not enqueued after 2 sec
+
+ .put("lang.StackTraceElement.toString.StackTraceElement_toString_A01",
+ match(runtimes(DexVm.ART_DEFAULT)))
+ // 1) t03
+ // org.junit.ComparisonFailure: expected:<...ethod(Unknown Source[])> but was:<...ethod(Unknown Source[:1])>
+
+ .put("lang.NullPointerException.serialization.NullPointerException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NullPointerException/serialization/NullPointerException_serialization_A01.golden.0.ser
+
+ .put("lang.VirtualMachineError.serialization.VirtualMachineError_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/VirtualMachineError/serialization/VirtualMachineError_serialization_A01.golden.0.ser
+
+ .put("lang.ClassCircularityError.serialization.ClassCircularityError_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ClassCircularityError/serialization/ClassCircularityError_serialization_A01.golden.0.ser
+
+ .put("lang.ThreadDeath.serialization.ThreadDeath_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ThreadDeath/serialization/ThreadDeath_serialization_A01.golden.0.ser
+
+ .put("lang.InstantiationError.serialization.InstantiationError_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/InstantiationError/serialization/InstantiationError_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.IllegalThreadStateException.serialization.IllegalThreadStateException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IllegalThreadStateException/serialization/IllegalThreadStateException_serialization_A01.golden.0.ser
+
+ .put("lang.ProcessBuilder.environment.ProcessBuilder_environment_A05", any())
+ // 1) t01
+ // java.lang.AssertionError: Input Stream should not be empty
+
+ .put("lang.ProcessBuilder.environment.ProcessBuilder_environment_A06", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.ProcessBuilder.start.ProcessBuilder_start_A05", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.ProcessBuilder.start.ProcessBuilder_start_A06", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.ClassFormatError.serialization.ClassFormatError_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ClassFormatError/serialization/ClassFormatError_serialization_A01.golden.0.ser
+
+ .put("lang.Math.cbrtD.Math_cbrt_A01",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1)))
+ // 1) t01
+ // java.lang.AssertionError: cbrt(27.) expected:<3.0> but was:<3.0000000000000004>
+
+ .put("lang.Math.powDD.Math_pow_A08", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<NaN> but was:<1.0>
+ // 2) t02
+ // java.lang.AssertionError: expected:<NaN> but was:<1.0>
+ // 3) t03
+ // java.lang.AssertionError: expected:<NaN> but was:<1.0>
+ // 4) t04
+ // java.lang.AssertionError: expected:<NaN> but was:<1.0>
+
+ .put(
+ "lang.IncompatibleClassChangeError.serialization.IncompatibleClassChangeError_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IncompatibleClassChangeError/serialization/IncompatibleClassChangeError_serialization_A01.golden.0.ser
+
+ .put("lang.Float.serialization.Float_serialization_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Float/serialization/Float_serialization_A01.golden.0.ser
+
+ .put("lang.Float.toStringF.Float_toString_A02", any())
+ // 1) t02
+ // org.junit.ComparisonFailure: expected:<0.001[0]> but was:<0.001[]>
+ // 2) t04
+ // org.junit.ComparisonFailure: expected:<0.001[0]> but was:<0.001[]>
+
+ .put("lang.Short.valueOfLjava_lang_StringI.Short_valueOf_A02", any())
+ // 1) t03
+ // java.lang.AssertionError: Missing NumberFormatException for radix=10
+
+ .put("lang.Short.valueOfLjava_lang_String.Short_valueOf_A02", any())
+ // 1) t03
+ // java.lang.AssertionError: Missing NumberFormatException for arg="+1"
+
+ .put("lang.Short.serialization.Short_serialization_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Short/serialization/Short_serialization_A01.golden.0.ser
+
+ .put("lang.Short.parseShortLjava_lang_String.Short_parseShort_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Parsed Short instance from string:+1
+
+ .put("lang.Short.decodeLjava_lang_String.Short_decode_A04", any())
+ // 1) t01
+ // java.lang.AssertionError: Decoded Short instance from string:+1
+
+ .put("lang.Short.ConstructorLjava_lang_String.Short_Constructor_A02", any())
+ // 1) t02
+ // java.lang.AssertionError: Created Short instance from string:+1
+
+ .put("lang.ClassNotFoundException.serialization.ClassNotFoundException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ClassNotFoundException/serialization/ClassNotFoundException_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.annotation.AnnotationFormatError.serialization.AnnotationFormatError_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/annotation/AnnotationFormatError/serialization/AnnotationFormatError_serialization_A01.golden.0.ser
+
+ .put("lang.Short.parseShortLjava_lang_StringI.Short_parseShort_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Parsed Short instance from string:+1 radix:10
+
+ .put(
+ "lang.annotation.IncompleteAnnotationException.ConstructorLjava_lang_ClassLjava_lang_String.IncompleteAnnotationException_Constructor_A01",
+ match(runtimes(DexVm.ART_DEFAULT)))
+ // 1) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.String.toString()' on a null object reference
+ // 2) t04
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.String.toString()' on a null object reference
+
+ .put("lang.InterruptedException.serialization.InterruptedException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/InterruptedException/serialization/InterruptedException_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.annotation.IncompleteAnnotationException.Class.IncompleteAnnotationException_class_A01",
+ any())
+ // 1) t01
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.IncompleteAnnotationException.Class.IncompleteAnnotationClass
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.IncompleteAnnotationException.Class.IncompleteAnnotationClass" on path: DexPathList[[dex file "/tmp/junit6988968562481945570/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.annotation.Annotation.Class.Annotation_class_A03", any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.annotation.Annotation.serialization.Annotation_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/annotation/Annotation/serialization/Annotation_serialization_A01.golden.0.ser
+
+ .put("lang.annotation.Annotation.annotationType.Annotation_annotationType_A01", any())
+ // 1) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t03
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/annotation/Annotation/annotationType/Mark;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.annotationType.Mark" on path: DexPathList[[dex file "/tmp/junit2356208730386617024/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t04
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/annotation/Annotation/annotationType/Mark;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.annotationType.Mark" on path: DexPathList[[dex file "/tmp/junit2356208730386617024/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t05
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/annotation/Annotation/annotationType/Mark;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.annotationType.Mark" on path: DexPathList[[dex file "/tmp/junit2356208730386617024/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 5) t06
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/annotation/Annotation/annotationType/Mark;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.annotationType.Mark" on path: DexPathList[[dex file "/tmp/junit2356208730386617024/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put(
+ "lang.annotation.IncompleteAnnotationException.serialization.IncompleteAnnotationException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/annotation/IncompleteAnnotationException/serialization/IncompleteAnnotationException_serialization_A01.golden.0.ser
+
+ .put("lang.annotation.Annotation.Class.Annotation_class_A02", any())
+ // 1) t04
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingClassValue
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingClassValue" on path: DexPathList[[dex file "/tmp/junit5702619070125761074/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t05
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingClassArrayValue
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingClassArrayValue" on path: DexPathList[[dex file "/tmp/junit5702619070125761074/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t06
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingEnumValue
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingEnumValue" on path: DexPathList[[dex file "/tmp/junit5702619070125761074/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 4) t07
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingEnumArrayValue
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingEnumArrayValue" on path: DexPathList[[dex file "/tmp/junit5702619070125761074/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 5) t08
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 6) t09
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.annotation.Retention.Retention_class_A01", any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 3) t04
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.annotation.AnnotationTypeMismatchException.Class.AnnotationTypeMismatchException_class_A01",
+ any())
+ // 1) t01
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.AnnotationTypeMismatchException.Class.AnnotationTypeMismatchClass
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.AnnotationTypeMismatchException.Class.AnnotationTypeMismatchClass" on path: DexPathList[[dex file "/tmp/junit7410548264446836680/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.AnnotationTypeMismatchException.Class.AnnotationTypeArrayMismatchClass
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.AnnotationTypeMismatchException.Class.AnnotationTypeArrayMismatchClass" on path: DexPathList[[dex file "/tmp/junit7410548264446836680/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.Long.serialization.Long_serialization_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Long/serialization/Long_serialization_A01.golden.0.ser
+
+ .put("lang.ThreadGroup.resume.ThreadGroup_resume_A01", any())
+ // 1) t01
+ // java.lang.UnsupportedOperationException
+
+ .put("lang.AbstractMethodError.serialization.AbstractMethodError_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/AbstractMethodError/serialization/AbstractMethodError_serialization_A01.golden.0.ser
+
+ .put("lang.RuntimeException.serialization.RuntimeException_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/RuntimeException/serialization/RuntimeException_serialization_A01.golden.0.ser
+
+ .put("lang.ThreadGroup.suspend.ThreadGroup_suspend_A01", any())
+ // 1) t01
+ // java.lang.UnsupportedOperationException
+ // 2) t02
+ // java.lang.UnsupportedOperationException
+
+ .put(
+ "lang.ThreadGroup.ConstructorLjava_lang_ThreadGroupLjava_lang_String.ThreadGroup_Constructor_A03",
+ any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.ThreadGroup.stop.ThreadGroup_stop_A01", any())
+ // 1) t01
+ // java.lang.UnsupportedOperationException
+ // 2) t02
+ // java.lang.UnsupportedOperationException
+
+ .put("lang.ThreadGroup.enumerate$Thread.ThreadGroup_enumerate_A01", any())
+ // 1) t05
+ // java.lang.UnsupportedOperationException
+
+ .put(
+ "lang.ThreadGroup.ConstructorLjava_lang_ThreadGroupLjava_lang_String.ThreadGroup_Constructor_A04",
+ any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.ThreadGroup.parentOfLjava_lang_ThreadGroup.ThreadGroup_parentOf_A01", any())
+ // 1) t05
+ // java.lang.SecurityException
+
+ .put("lang.ThreadGroup.getMaxPriority.ThreadGroup_getMaxPriority_A02", any())
+ // 1) t02
+ // java.lang.AssertionError: expected:<1> but was:<5>
+
+ .put("lang.ThreadGroup.checkAccess.ThreadGroup_checkAccess_A03", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.ThreadGroup.enumerate$ThreadZ.ThreadGroup_enumerate_A01", any())
+ // 1) t06
+ // java.lang.UnsupportedOperationException
+
+ .put(
+ "lang.ThreadGroup.uncaughtExceptionLjava_lang_ThreadLjava_lang_Throwable.ThreadGroup_uncaughtException_A01",
+ any())
+ // 1) t01
+ // java.lang.UnsupportedOperationException
+ // 2) t01
+ // java.lang.IllegalThreadStateException
+
+ .put("lang.ThreadGroup.checkAccess.ThreadGroup_checkAccess_A02", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.ThreadGroup.ConstructorLjava_lang_String.ThreadGroup_Constructor_A04", any())
+ // 1) t01
+ // java.lang.AssertionError: test failed with error:java.lang.SecurityException
+
+ .put("lang.ThreadGroup.activeCount.ThreadGroup_activeCount_A01", any())
+ // 1) t04
+ // java.lang.UnsupportedOperationException
+
+ .put("lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A03", any())
+ // 1) t01
+ // java.lang.AssertionError: Maximum priority should not be changed. expected same:<10> was not:<1>
+
+ .put("lang.ThreadGroup.ConstructorLjava_lang_String.ThreadGroup_Constructor_A03", any())
+ // 1) t01
+ // java.lang.AssertionError: test failed with error:java.lang.SecurityException
+
+ .put("lang.ThreadGroup.getParent.ThreadGroup_getParent_A03", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.Class.getDeclaredConstructors.Class_getDeclaredConstructors_A02", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put("lang.AssertionError.serialization.AssertionError_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/AssertionError/serialization/AssertionError_serialization_A01.golden.0.ser
+
+ .put("lang.Class.getClassLoader.Class_getClassLoader_A01", any())
+ // 1) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t03
+ // java.lang.AssertionError: expected null, but was:<java.lang.BootClassLoader@d17167d>
+
+ .put("lang.Class.getDeclaringClass.Class_getDeclaringClass_A01", any())
+ // 1) t04
+ // java.lang.AssertionError: expected null, but was:<class com.google.jctf.test.lib.java.lang.Class.getDeclaringClass.Class_getDeclaringClass_A01>
+
+ .put("lang.Class.getDeclaredFields.Class_getDeclaredFields_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: array lengths differed, expected.length=0 actual.length=2
+
+ .put("lang.Class.getClassLoader.Class_getClassLoader_A02", any())
+ // 1) t02
+ // java.lang.AssertionError: ClassLoader of int[] expected null, but was:<java.lang.BootClassLoader@34f2660>
+
+ .put("lang.Class.getClassLoader.Class_getClassLoader_A03", any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 3) t03
+ // java.lang.SecurityException
+
+ .put("lang.Class.getDeclaredFields.Class_getDeclaredFields_A02", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put("lang.Class.getResourceLjava_lang_String.Class_getResource_A01", any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getPath()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getPath()' on a null object reference
+ // 3) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getPath()' on a null object reference
+ // 4) t04
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getPath()' on a null object reference
+ // 5) t06
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getPath()' on a null object reference
+ // 6) t07
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.Class.getConstructor$Ljava_lang_Class.Class_getConstructor_A03", any())
+ // 1) t03
+ // java.lang.AssertionError: Vague error message
+
+ .put("lang.Class.forNameLjava_lang_StringZLjava_lang_ClassLoader.Class_forName_A03",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.LinkageError
+
+ .put("lang.Class.forNameLjava_lang_StringZLjava_lang_ClassLoader.Class_forName_A07",
+ any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+
+ .put("lang.Class.forNameLjava_lang_StringZLjava_lang_ClassLoader.Class_forName_A01",
+ any())
+ // 1) t05
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.forNameLjava_lang_StringZLjava_lang_ClassLoader.Class_forName_A01$TestFixture
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t06
+ // java.lang.ClassNotFoundException: [[[Lcom.google.jctf.test.lib.java.lang.Class.forNameLjava_lang_StringZLjava_lang_ClassLoader.Class_forName_A01$TestFixture;
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.Class.getConstructor$Ljava_lang_Class.Class_getConstructor_A04", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put("lang.Class.serialization.Class_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Class/serialization/Class_serialization_A01-Object.golden.ser
+
+ .put("lang.Class.getMethods.Class_getMethods_A02", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put(
+ "lang.Class.getDeclaredMethodLjava_lang_String$Ljava_lang_Class.Class_getDeclaredMethod_A05",
+ any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put("lang.Class.getClasses.Class_getClasses_A02", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put(
+ "lang.Class.getDeclaredMethodLjava_lang_String$Ljava_lang_Class.Class_getDeclaredMethod_A03",
+ any())
+ // 1) t05
+ // java.lang.AssertionError: Vague error message
+
+ .put("lang.Class.getClasses.Class_getClasses_A01",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+ // 1) t03
+ // java.lang.AssertionError: Array lengths expected:<2> but was:<3>
+
+ .put("lang.Class.getProtectionDomain.Class_getProtectionDomain_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: unexpected null
+ // 2) t02
+ // java.lang.AssertionError
+
+ .put("lang.Class.getProtectionDomain.Class_getProtectionDomain_A02", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.Class.getDeclaredMethods.Class_getDeclaredMethods_A01",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+ // 1) t03
+ // java.lang.AssertionError: Array lengths expected:<1> but was:<3>
+
+ .put("lang.Class.getMethods.Class_getMethods_A01",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+ // 1) t03
+ // java.lang.AssertionError: Array lengths expected:<1> but was:<3>
+
+ .put("lang.Class.getGenericInterfaces.Class_getGenericInterfaces_A04", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.BadSignatureClass
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.BadSignatureClass" on path: DexPathList[[dex file "/tmp/junit3946430209802684584/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.Class.getDeclaredFieldLjava_lang_String.Class_getDeclaredField_A04", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put("lang.Class.getDeclaredMethods.Class_getDeclaredMethods_A02", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put("lang.Class.getResourceAsStreamLjava_lang_String.Class_getResourceAsStream_A01",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.InputStream.close()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.InputStream.close()' on a null object reference
+ // 3) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.InputStream.close()' on a null object reference
+ // 4) t04
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.InputStream.close()' on a null object reference
+ // 5) t06
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 6) t07
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.Class.getGenericInterfaces.Class_getGenericInterfaces_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.MalformedSuperinterface
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.MalformedSuperinterface" on path: DexPathList[[dex file "/tmp/junit4997784696243614791/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.MalformedSuperclass
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.MalformedSuperclass" on path: DexPathList[[dex file "/tmp/junit4997784696243614791/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.Class.getAnnotationLjava_lang_Class.Class_getAnnotation_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+ // 2) t03
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+ .put("lang.Class.getGenericInterfaces.Class_getGenericInterfaces_A03", any())
+ // 1) t01
+ // java.lang.AssertionError: Should throw TypeNotPresentException for class: interface com.google.jctf.test.lib.java.lang.Class.getGenericInterfaces.Class_getGenericInterfaces_A03$I01
+
+ .put("lang.Class.getDeclaredClasses.Class_getDeclaredClasses_A02", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put("lang.Class.desiredAssertionStatus.Class_desiredAssertionStatus_A01", any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 3) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 4) t04
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 5) t05
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 6) t06
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 7) t07
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 8) t08
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.Class.getPackage.Class_getPackage_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: expected same:<package java.lang, Unknown, version 0.0> was not:<package java.lang, Unknown, version 0.0>
+ // 2) t03
+ // java.lang.AssertionError: expected null, but was:<package [Ljava.lang, Unknown, version 0.0>
+
+ .put("lang.Class.getFieldLjava_lang_String.Class_getField_A04", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put("lang.Class.getTypeParameters.Class_getTypeParameters_A02", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.BadSignatureClass
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.BadSignatureClass" on path: DexPathList[[dex file "/tmp/junit8470679547599572122/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.Class.getDeclaredAnnotations.Class_getDeclaredAnnotations_A01", any())
+ // 1) t03
+ // java.lang.AssertionError: expected:<0> but was:<1>
+
+ .put("lang.Class.getConstructors.Class_getConstructors_A02", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put("lang.Class.isAnnotationPresentLjava_lang_Class.Class_isAnnotationPresent_A01",
+ any())
+ // 1) t03
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+ // 2) t04
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+ .put("lang.Class.getFields.Class_getFields_A02", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put("lang.Class.getGenericSuperclass.Class_getGenericSuperclass_A03", any())
+ // 1) t01
+ // java.lang.AssertionError: Should throw TypeNotPresentException for class: class com.google.jctf.test.lib.java.lang.Class.getGenericSuperclass.Class_getGenericSuperclass_A03$C01
+
+ .put("lang.Class.getGenericSuperclass.Class_getGenericSuperclass_A04", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.MalformedSuperclass
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.MalformedSuperclass" on path: DexPathList[[dex file "/tmp/junit552939904349045383/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.MalformedSuperinterface
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.MalformedSuperinterface" on path: DexPathList[[dex file "/tmp/junit552939904349045383/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.Class.getSigners.Class_getSigners_A01", any())
+ // 1) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'java.security.CodeSource java.security.ProtectionDomain.getCodeSource()' on a null object reference
+ // 2) t04
+ // java.lang.AssertionError: Unable to configure default providers
+ // 3) t05
+ // java.lang.NoClassDefFoundError: sun.security.jca.Providers
+ // Caused by: java.lang.AssertionError: Unable to configure default providers
+
+ .put("lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A01",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+ // 1) t04
+ // java.lang.AssertionError: expected:<interface com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A01$I1> but was:<interface com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A01$I2>
+
+ .put("lang.Class.getGenericSuperclass.Class_getGenericSuperclass_A01", any())
+ // 1) t03
+ // java.lang.AssertionError: expected same:<class java.lang.reflect.AccessibleObject> was not:<class java.lang.reflect.Executable>
+
+ .put("lang.Class.getGenericSuperclass.Class_getGenericSuperclass_A02", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.BadSignatureClass
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.BadSignatureClass" on path: DexPathList[[dex file "/tmp/junit6101943207514034648/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.Class.newInstance.Class_newInstance_A07", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put(
+ "lang.Class.getDeclaredConstructor$Ljava_lang_Class.Class_getDeclaredConstructor_A02",
+ any())
+ // 1) t03
+ // java.lang.AssertionError: Vague error message
+
+ .put("lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A05", any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put("lang.Class.forNameLjava_lang_String.Class_forName_A01", any())
+ // 1) t05
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t06
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.Class.getDeclaredConstructor$Ljava_lang_Class.Class_getDeclaredConstructor_A03",
+ any())
+ // 1) t01
+ // java.lang.SecurityException
+ // 2) t03
+ // java.lang.SecurityException
+ // 3) t04
+ // java.lang.SecurityException
+
+ .put("lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A03", any())
+ // 1) t03
+ // java.lang.AssertionError: Vague error message
+
+ .put("lang.Class.forNameLjava_lang_String.Class_forName_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.LinkageError
+
+ .put("lang.UnsatisfiedLinkError.serialization.UnsatisfiedLinkError_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/UnsatisfiedLinkError/serialization/UnsatisfiedLinkError_serialization_A01.golden.0.ser
+
+ .put("lang.Class.getAnnotations.Class_getAnnotations_A01", any())
+ // 1) t04
+ // java.lang.AssertionError: expected:<0> but was:<1>
+ // 2) t06
+ // java.lang.AssertionError: Misconfigured test
+
+ .put(
+ "lang.EnumConstantNotPresentException.serialization.EnumConstantNotPresentException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/EnumConstantNotPresentException/serialization/EnumConstantNotPresentException_serialization_A01.golden.0.ser
+
+ .put("lang.String.toLowerCase.String_toLowerCase_A01", any())
+ // 1) t02
+ // org.junit.ComparisonFailure: expected:<i[]> but was:<i[̇]>
+
+ .put("lang.String.splitLjava_lang_StringI.String_split_A01", any())
+ // 1) t06
+ // java.lang.AssertionError: array lengths differed, expected.length=1 actual.length=2
+
+ .put("lang.String.serialization.String_serialization_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/String/serialization/String_serialization_A01.golden.0.ser
+
+ .put("lang.String.regionMatchesZILjava_lang_StringII.String_regionMatches_A01", any())
+ // 1) t07
+ // java.lang.AssertionError
+
+ .put("lang.String.valueOfF.String_valueOf_A01", any())
+ // 1) t09
+ // org.junit.ComparisonFailure: Incorrect double string returned expected:<0.001[0]> but was:<0.001[]>
+
+ .put("lang.String.Constructor$BLjava_nio_charset_Charset.String_Constructor_A01", any())
+ // 1) t02
+ // org.junit.ComparisonFailure: expected:<�[]> but was:<�[�]>
+ // 2) t03
+ // org.junit.ComparisonFailure: expected:<[�]> but was:<[�]>
+ // 3) t04
+ // org.junit.ComparisonFailure: expected:<[�]> but was:<[�]>
+ // 4) t05
+ // org.junit.ComparisonFailure: expected:<[�]> but was:<[�]>
+
+ .put("lang.String.concatLjava_lang_String.String_concat_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: expected not same
+
+ .put("lang.String.matchesLjava_lang_String.String_matches_A01", any())
+ // 1) t15
+ // java.lang.AssertionError: pattern: [^a-d[^m-p]]*abb input: admpabb
+ // 2) t19
+ // java.util.regex.PatternSyntaxException: Syntax error in regexp pattern near index 11
+ // .*(?<=abc)*\.log$
+ // ^
+
+ .put("lang.String.CASE_INSENSITIVE_ORDER.serialization.String_serialization_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/String/CASE_INSENSITIVE_ORDER/serialization/String_serialization_A01.golden.0.ser
+
+ .put("lang.String.getBytesLjava_lang_String.String_getBytes_A14", any())
+ // 1) t07
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+ // 2) t12
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+ // 3) t22
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+ // 4) t32
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+ // 5) t42
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+ // 6) t52
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+ // 7) t62
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+
+ .put("lang.String.splitLjava_lang_String.String_split_A01", any())
+ // 1) t06
+ // java.lang.AssertionError: array lengths differed, expected.length=1 actual.length=2
+
+ .put("lang.String.getBytesII$BI.String_getBytes_A03", any())
+ // 1) t04
+ // java.lang.AssertionError: Should throws IndexOutOfBoundsException: 0
+ // 2) t05
+ // java.lang.AssertionError: Should throws IndexOutOfBoundsException: 0
+
+ .put("lang.String.getBytesII$BI.String_getBytes_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.String.toLowerCaseLjava_util_Locale.String_toLowerCase_A01", any())
+ // 1) t02
+ // org.junit.ComparisonFailure: expected:<i[]> but was:<i[̇]>
+
+ .put("lang.String.Constructor$BIILjava_nio_charset_Charset.String_Constructor_A01", any())
+ // 1) t02
+ // org.junit.ComparisonFailure: expected:<�[]> but was:<�[�]>
+ // 2) t03
+ // org.junit.ComparisonFailure: expected:<[�]> but was:<[�]>
+ // 3) t04
+ // org.junit.ComparisonFailure: expected:<[�]> but was:<[�]>
+ // 4) t05
+ // org.junit.ComparisonFailure: expected:<[�]> but was:<[�]>
+
+ .put("lang.String.getBytesLjava_nio_charset_Charset.String_getBytes_A01", any())
+ // 1) t05
+ // arrays first differed at element [0]; expected:<-40> but was:<-37>
+ // Caused by: java.lang.AssertionError: expected:<-40> but was:<-37>
+
+ .put("lang.String.valueOfD.String_valueOf_A01", any())
+ // 1) t09
+ // org.junit.ComparisonFailure: Incorrect double string returned expected:<0.001[0]> but was:<0.001[]>
+
+ .put("lang.String.getBytesLjava_nio_charset_Charset.String_getBytes_A14", any())
+ // 1) t07
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+ // 2) t12
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+ // 3) t22
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+ // 4) t32
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+ // 5) t42
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+ // 6) t52
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+ // 7) t62
+ // arrays first differed at element [0]; expected:<-2> but was:<-1>
+ // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+
+ .put("lang.Package.isSealed.Package_isSealed_A01", any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.getSpecificationVersion.Package_getSpecificationVersion_A01", any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.getAnnotationLjava_lang_Class.Package_getAnnotation_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+ // 2) t03
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+ // 3) t05
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.isAnnotationPresentLjava_lang_Class.Package_isAnnotationPresent_A02",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+ // 1) testIsAnnotationPresent_Null2
+ // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.getName.Package_getName_A01", any())
+ // 1) t03
+ // java.lang.ClassNotFoundException: com.simpleClass1
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.getImplementationVersion.Package_getImplementationVersion_A01", any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.getDeclaredAnnotations.Package_getDeclaredAnnotations_A01", any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+ // 2) t03
+ // java.lang.AssertionError: expected:<0> but was:<1>
+
+ .put("lang.Package.getSpecificationVendor.Package_getSpecificationVendor_A01", any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.getAnnotationLjava_lang_Class.Package_getAnnotation_A02",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+ // 1) testGetAnnotation_Null2
+ // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.isCompatibleWithLjava_lang_String.Package_isCompatibleWith_A01", any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.toString.Package_toString_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: expect: package com.google.jctf.test.lib.java.lang.Package.toString, Unknown, version 0.0, actual: package com.google.jctf.test.lib.java.lang.Package.toString
+ // 2) t03
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.getAnnotations.Package_getAnnotations_A01", any())
+ // 1) t03
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+ // 2) t04
+ // java.lang.AssertionError: expected:<0> but was:<1>
+ // 3) t06
+ // java.lang.AssertionError: Misconfigured test
+
+ .put("lang.Package.isAnnotationPresentLjava_lang_Class.Package_isAnnotationPresent_A01",
+ any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+ // 2) t03
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+ // 3) t04
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+ .put("lang.Package.getSpecificationTitle.Package_getSpecificationTitle_A01", any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.getImplementationTitle.Package_getImplementationTitle_A01", any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.getPackages.Package_getPackages_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Package getPackages failed to retrieve a packages
+
+ .put("lang.Package.hashCode.Package_hashCode_A01", any())
+ // 1) t03
+ // java.lang.ClassNotFoundException: com.simpleClass1
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.getPackageLjava_lang_String.Package_getPackage_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Package getPackage failed for java.lang expected same:<package java.lang, Unknown, version 0.0> was not:<package java.lang, Unknown, version 0.0>
+
+ .put("lang.Package.getImplementationVendor.Package_getImplementationVendor_A01", any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.Package.isSealedLjava_net_URL.Package_isSealed_A01", any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+ .put("lang.StringBuilder.serialization.StringBuilder_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/StringBuilder/serialization/StringBuilder_serialization_A01.golden.ser
+
+ .put(
+ "lang.SecurityManager.checkReadLjava_io_FileDescriptor.SecurityManager_checkRead_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.SecurityManager.checkAwtEventQueueAccess.SecurityManager_checkAwtEventQueueAccess_A01",
+ any())
+ // 1) t01
+ // java.lang.NoClassDefFoundError: Failed resolution of: Ljava/awt/AWTPermission;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "java.awt.AWTPermission" on path: DexPathList[[dex file "/tmp/junit5851789677967468571/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Ljava/awt/AWTPermission;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "java.awt.AWTPermission" on path: DexPathList[[dex file "/tmp/junit5851789677967468571/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.SecurityManager.checkWriteLjava_lang_String.SecurityManager_checkWrite_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.SecurityManager.inClassLoader.SecurityManager_inClassLoader_A01", any())
+ // 1) t01
+ // java.lang.AssertionError
+
+ .put(
+ "lang.SecurityManager.checkPermissionLjava_security_PermissionLjava_lang_Object.SecurityManager_checkPermission_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.SecurityManager.checkReadLjava_io_FileDescriptor.SecurityManager_checkRead_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@cf3e8f1>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.SecurityManager.inCheck.SecurityManager_inCheck_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: inCheck field must always remain false
+ // 2) t03
+ // java.lang.AssertionError: inCheck field must always remain false
+
+ .put("lang.SecurityManager.currentClassLoader.SecurityManager_currentClassLoader_A02",
+ any())
+ // 1) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.SecurityManager.checkPrintJobAccess.SecurityManager_checkPrintJobAccess_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@a49048c>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.SecurityManager.checkWriteLjava_lang_String.SecurityManager_checkWrite_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.io.FilePermission@9656315>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put(
+ "lang.SecurityManager.checkPackageAccessLjava_lang_String.SecurityManager_checkPackageAccess_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.SecurityManager.checkAcceptLjava_lang_StringI.SecurityManager_checkAccept_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.SecurityManager.checkPermissionLjava_security_PermissionLjava_lang_Object.SecurityManager_checkPermission_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <com.google.jctf.test.lib.java.lang.SecurityManager.checkPermissionLjava_security_PermissionLjava_lang_Object.SecurityManager_checkPermission_A01$1@5d6dd39>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+ // 3) t03
+ // java.lang.AssertionError: SecurityException should be thrown for null context
+
+ .put("lang.SecurityManager.currentClassLoader.SecurityManager_currentClassLoader_A01",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.SecurityManager.checkMulticastLjava_net_InetAddress.SecurityManager_checkMulticast_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.SecurityManager.checkListenI.SecurityManager_checkListen_A01", any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@32bf8d4>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.SecurityManager.getSecurityContext.SecurityManager_getSecurityContext_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<java.security.AccessControlContext@248575d> but was:<null>
+ // 2) t02
+ // java.lang.AssertionError: expected:<java.security.AccessControlContext@259b8d2> but was:<null>
+
+ .put(
+ "lang.SecurityManager.checkPackageAccessLjava_lang_String.SecurityManager_checkPackageAccess_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@3230dd4>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@a1bab7d>
+ // but:
+ // 3) t03
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@bb64a72>
+ // but:
+ // 4) t05
+ // java.lang.AssertionError: SecurityException should be thrown for restricted package: 1234
+
+ .put(
+ "lang.SecurityManager.checkMemberAccessLjava_lang_ClassI.SecurityManager_checkMemberAccess_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@81146f>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put(
+ "lang.SecurityManager.checkMulticastLjava_net_InetAddressB.SecurityManager_checkMulticast_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.SecurityManager.checkAcceptLjava_lang_StringI.SecurityManager_checkAccept_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@eb9d181>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@db9b226>
+ // but:
+ // 3) t03
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put(
+ "lang.SecurityManager.checkMulticastLjava_net_InetAddress.SecurityManager_checkMulticast_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@dd6300a>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@891b07b>
+ // but:
+ // 3) t03
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.SecurityManager.Constructor.SecurityManager_Constructor_A01", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.SecurityManager.getClassContext.SecurityManager_getClassContext_A01", any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to read from null array
+
+ .put(
+ "lang.SecurityManager.checkMemberAccessLjava_lang_ClassI.SecurityManager_checkMemberAccess_A03",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.SecurityManager.checkDeleteLjava_lang_String.SecurityManager_checkDelete_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.io.FilePermission@96408b7>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put(
+ "lang.SecurityManager.checkReadLjava_lang_StringLjava_lang_Object.SecurityManager_checkRead_A03",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.SecurityManager.checkMulticastLjava_net_InetAddressB.SecurityManager_checkMulticast_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@79cc5c9>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@479a4ce>
+ // but:
+ // 3) t03
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.SecurityManager.checkListenI.SecurityManager_checkListen_A02", any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@6b92452>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.SecurityManager.checkAccessLjava_lang_Thread.SecurityManager_checkAccess_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@5d582db>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: SecurityException should be thrown
+
+ .put(
+ "lang.SecurityManager.checkWriteLjava_io_FileDescriptor.SecurityManager_checkWrite_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.SecurityManager.checkDeleteLjava_lang_String.SecurityManager_checkDelete_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.SecurityManager.checkPropertiesAccess.SecurityManager_checkPropertiesAccess_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.util.PropertyPermission@32a9e76>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put(
+ "lang.SecurityManager.checkReadLjava_lang_StringLjava_lang_Object.SecurityManager_checkRead_A02",
+ any())
+ // 1) t03
+ // java.lang.AssertionError: SecurityException should be thrown for null context
+
+ .put(
+ "lang.SecurityManager.checkAccessLjava_lang_ThreadGroup.SecurityManager_checkAccess_A03",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.SecurityManager.checkAccessLjava_lang_Thread.SecurityManager_checkAccess_A03",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.SecurityManager.checkPackageDefinitionLjava_lang_String.SecurityManager_checkPackageDefinition_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.SecurityManager.checkReadLjava_lang_String.SecurityManager_checkRead_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.SecurityManager.checkWriteLjava_io_FileDescriptor.SecurityManager_checkWrite_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@cf13435>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put(
+ "lang.SecurityManager.checkReadLjava_lang_StringLjava_lang_Object.SecurityManager_checkRead_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.io.FilePermission@c0d92be>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.SecurityManager.checkExecLjava_lang_String.SecurityManager_checkExec_A03",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.SecurityManager.checkPackageDefinitionLjava_lang_String.SecurityManager_checkPackageDefinition_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@8bc5330>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@a83ba9>
+ // but:
+ // 3) t03
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@785152e>
+ // but:
+ // 4) t05
+ // java.lang.AssertionError: SecurityException should be thrown for restricted package: 1234
+
+ .put("lang.SecurityManager.checkExecLjava_lang_String.SecurityManager_checkExec_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.io.FilePermission@79b6b6b>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put(
+ "lang.SecurityManager.checkCreateClassLoader.SecurityManager_checkCreateClassLoader_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@6b7c9f4>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.SecurityManager.checkReadLjava_lang_String.SecurityManager_checkRead_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.io.FilePermission@5d4287d>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put(
+ "lang.SecurityManager.checkAccessLjava_lang_ThreadGroup.SecurityManager_checkAccess_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@4f08706>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.SecurityManager.inClassLjava_lang_String.SecurityManager_inClass_A03", any())
+ // 1) t01
+ // java.lang.AssertionError
+
+ .put(
+ "lang.SecurityManager.checkConnectLjava_lang_StringILjava_lang_Object.SecurityManager_checkConnect_A03",
+ any())
+ // 1) t03
+ // java.lang.AssertionError: SecurityException should be thrown for null context
+
+ .put("lang.SecurityManager.checkExecLjava_lang_String.SecurityManager_checkExec_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.io.FilePermission@162012a>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.SecurityManager.checkSetFactory.SecurityManager_checkSetFactory_A01", any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@162012a>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put(
+ "lang.SecurityManager.checkConnectLjava_lang_StringILjava_lang_Object.SecurityManager_checkConnect_A04",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.SecurityManager.checkPermissionLjava_security_Permission.SecurityManager_checkPermission_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <com.google.jctf.test.lib.java.lang.SecurityManager.checkPermissionLjava_security_Permission.SecurityManager_checkPermission_A01$2@f9abe3c>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.SecurityManager.inClassLjava_lang_String.SecurityManager_inClass_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Method from class: com.google.jctf.test.lib.java.lang.SecurityManager.inClassLjava_lang_String.SecurityManager_inClass_A01 must be on execution stack.
+
+ .put("lang.SecurityManager.inClassLjava_lang_String.SecurityManager_inClass_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.SecurityManager.checkPropertyAccessLjava_lang_String.SecurityManager_checkPropertyAccess_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.SecurityManager.checkExitI.SecurityManager_checkExit_A01", any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@c0c3860>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put(
+ "lang.SecurityManager.checkConnectLjava_lang_StringILjava_lang_Object.SecurityManager_checkConnect_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@b2896e9>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@f796f6e>
+ // but:
+ // 3) t03
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put(
+ "lang.SecurityManager.checkConnectLjava_lang_StringILjava_lang_Object.SecurityManager_checkConnect_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@96153fb>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@5296c18>
+ // but:
+ // 3) t03
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.SecurityManager.classLoaderDepth.SecurityManager_classLoaderDepth_A02", any())
+ // 1) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.SecurityManager.classDepthLjava_lang_String.SecurityManager_classDepth_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.SecurityManager.checkPropertyAccessLjava_lang_String.SecurityManager_checkPropertyAccess_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.util.PropertyPermission@79a110d>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put(
+ "lang.SecurityManager.checkPropertyAccessLjava_lang_String.SecurityManager_checkPropertyAccess_A03",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+ .put(
+ "lang.SecurityManager.checkConnectLjava_lang_StringI.SecurityManager_checkConnect_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@4ef2ca8>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@b6163c1>
+ // but:
+ // 3) t03
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.SecurityManager.checkLinkLjava_lang_String.SecurityManager_checkLink_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.SecurityManager.classLoaderDepth.SecurityManager_classLoaderDepth_A01", any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.SecurityManager.checkPermissionLjava_security_Permission.SecurityManager_checkPermission_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.SecurityManager.currentLoadedClass.SecurityManager_currentLoadedClass_A01",
+ any())
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.SecurityManager.checkSecurityAccessLjava_lang_String.SecurityManager_checkSecurityAccess_A03",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+ .put(
+ "lang.SecurityManager.checkConnectLjava_lang_StringI.SecurityManager_checkConnect_A03",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.SecurityManager.checkConnectLjava_lang_StringI.SecurityManager_checkConnect_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@f9963de>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.net.SocketPermission@c7159bf>
+ // but:
+ // 3) t03
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put(
+ "lang.SecurityManager.checkTopLevelWindowLjava_lang_Object.SecurityManager_checkTopLevelWindow_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.SecurityManager.currentLoadedClass.SecurityManager_currentLoadedClass_A02",
+ any())
+ // 1) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.SecurityManager.classDepthLjava_lang_String.SecurityManager_classDepth_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<1> but was:<-1>
+
+ .put(
+ "lang.SecurityManager.checkSecurityAccessLjava_lang_String.SecurityManager_checkSecurityAccess_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.security.SecurityPermission@a439b14>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.Throwable.serialization.Throwable_serialization_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Throwable/serialization/Throwable_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.SecurityManager.checkTopLevelWindowLjava_lang_Object.SecurityManager_checkTopLevelWindow_A01",
+ any())
+ // 1) t01
+ // java.lang.NoClassDefFoundError: Failed resolution of: Ljava/awt/AWTPermission;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "java.awt.AWTPermission" on path: DexPathList[[dex file "/tmp/junit9085263025235375443/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.NoClassDefFoundError: Failed resolution of: Ljava/awt/AWTPermission;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "java.awt.AWTPermission" on path: DexPathList[[dex file "/tmp/junit9085263025235375443/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.SecurityManager.checkLinkLjava_lang_String.SecurityManager_checkLink_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError:
+ // Expected: a collection containing <java.lang.RuntimePermission@87c5826>
+ // but:
+ // 2) t02
+ // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+ .put("lang.Throwable.getStackTrace.Throwable_getStackTrace_A01", any())
+ // 1) t04
+ // org.junit.ComparisonFailure: wrongly omit constructor frame for other exception expected:<[<init>]> but was:<[t04]>
+
+ .put(
+ "lang.SecurityManager.checkSystemClipboardAccess.SecurityManager_checkSystemClipboardAccess_A01",
+ any())
+ // 1) t01
+ // java.lang.NoClassDefFoundError: Failed resolution of: Ljava/awt/AWTPermission;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "java.awt.AWTPermission" on path: DexPathList[[dex file "/tmp/junit529120552959989860/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+ // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Ljava/awt/AWTPermission;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "java.awt.AWTPermission" on path: DexPathList[[dex file "/tmp/junit529120552959989860/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put(
+ "lang.SecurityManager.checkSecurityAccessLjava_lang_String.SecurityManager_checkSecurityAccess_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.reflect.ReflectPermission.Constructor_java_lang_String.ReflectPermission_Constructor_A03",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+ .put(
+ "lang.reflect.MalformedParameterizedTypeException.serialization.MalformedParameterizedTypeException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/reflect/MalformedParameterizedTypeException/serialization/MalformedParameterizedTypeException_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.reflect.ReflectPermission.Constructor_java_lang_StringLjava_lang_String.ReflectPermission_Constructor_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put(
+ "lang.UnsupportedClassVersionError.serialization.UnsupportedClassVersionError_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/UnsupportedClassVersionError/serialization/UnsupportedClassVersionError_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.reflect.ReflectPermission.Constructor_java_lang_String.ReflectPermission_Constructor_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Incorrect permission constructed
+ // 2) t02
+ // java.lang.AssertionError: expected:<a> but was:<null>
+ // 3) t03
+ // java.lang.AssertionError: expected:<2/3/2> but was:<null>
+
+ .put("lang.reflect.ReflectPermission.Class.ReflectPermission_class_A01", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.reflect.Proxy.serialization.Proxy_serialization_A01", any())
+ // 1) t01
+ // java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
+ // Caused by: java.lang.reflect.InvocationTargetException
+ // Caused by: java.lang.NullPointerException
+ // 2) t02
+ // java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
+ // Caused by: java.lang.reflect.InvocationTargetException
+ // Caused by: java.lang.NullPointerException
+
+ .put(
+ "lang.reflect.ReflectPermission.Constructor_java_lang_StringLjava_lang_String.ReflectPermission_Constructor_A03",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+ .put(
+ "lang.reflect.ReflectPermission.Constructor_java_lang_String.ReflectPermission_Constructor_A02",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+ .put("lang.reflect.ReflectPermission.Class.ReflectPermission_class_A02", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put(
+ "lang.reflect.ReflectPermission.Constructor_java_lang_StringLjava_lang_String.ReflectPermission_Constructor_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Incorrect permission constructed
+ // 2) t04
+ // java.lang.AssertionError: expected:<a> but was:<null>
+ // 3) t05
+ // java.lang.AssertionError: expected:<2/3/2> but was:<null>
+
+ .put(
+ "lang.reflect.Proxy.getInvocationHandlerLjava_lang_Object.Proxy_getInvocationHandler_A02",
+ any())
+ // 1) t02
+ // java.lang.Exception: Unexpected exception, expected<java.lang.IllegalArgumentException> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException
+
+ .put("lang.reflect.Proxy.Class.Proxy_class_A01", any())
+ // 1) t04
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t05
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.reflect.Proxy.getProxyClassLjava_lang_ClassLoader$Ljava_lang_Class.Proxy_getProxyClass_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: expected same:<null> was not:<java.lang.BootClassLoader@ecc20b9>
+ // 2) t04
+ // java.lang.AssertionError: expected same:<null> was not:<java.lang.BootClassLoader@ecc20b9>
+
+ .put(
+ "lang.reflect.Proxy.getProxyClassLjava_lang_ClassLoader$Ljava_lang_Class.Proxy_getProxyClass_A03",
+ any())
+ // 1) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t05
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Proxy.h.Proxy_h_A01", match(runtimes(DexVm.ART_DEFAULT)))
+ // 1) t01
+ // java.lang.reflect.InvocationTargetException
+ // Caused by: java.lang.NullPointerException
+
+ .put("lang.reflect.Proxy.serialization.Proxy_serialization_A02", any())
+ // 1) t01
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/reflect/Proxy/serialization/Proxy_serialization_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Proxy.serialization.Proxy_serialization_A01" on path: DexPathList[[dex file "/tmp/junit3110030363172925878/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t02
+ // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/reflect/Proxy/serialization/Proxy_serialization_A01;
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Proxy.serialization.Proxy_serialization_A01" on path: DexPathList[[dex file "/tmp/junit3110030363172925878/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 3) t03
+ // java.lang.AssertionError: Unable to configure default providers
+
+ .put(
+ "lang.reflect.GenericSignatureFormatError.serialization.GenericSignatureFormatError_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/reflect/GenericSignatureFormatError/serialization/GenericSignatureFormatError_serialization_A01.golden.0.ser
+
+ .put(
+ "lang.reflect.Proxy.newProxyInstanceLjava_lang_ClassLoader$Ljava_lang_ClassLjava_lang_reflect_InvocationHandler.Proxy_newProxyInstance_A02",
+ any())
+ // 1) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put(
+ "lang.reflect.Proxy.ConstructorLjava_lang_reflect_InvocationHandler.Proxy_Constructor_A01",
+ match(runtimes(DexVm.ART_DEFAULT)))
+ // 1) t01
+ // java.lang.NullPointerException
+
+ .put(
+ "lang.reflect.Proxy.newProxyInstanceLjava_lang_ClassLoader$Ljava_lang_ClassLjava_lang_reflect_InvocationHandler.Proxy_newProxyInstance_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Bad classloader expected:<null> but was:<java.lang.BootClassLoader@fda9ca7>
+
+ .put("lang.reflect.Modifier.isStrictI.Modifier_isStrict_A01", any())
+ // 1) t05
+ // java.lang.AssertionError
+
+ .put("lang.reflect.Method.getGenericReturnType.Method_getGenericReturnType_A03", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.TypeNotPresentException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.MissingReturnTypeMethod
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.MissingReturnTypeMethod" on path: DexPathList[[dex file "/tmp/junit7196800508165091862/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Method.getGenericReturnType.Method_getGenericReturnType_A02", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod" on path: DexPathList[[dex file "/tmp/junit1047440880764311474/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Method.getAnnotationLjava_lang_Class.Method_getAnnotation_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+ // 2) t03
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+ .put("lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A02",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod" on path: DexPathList[[dex file "/tmp/junit2055893562046815723/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Method.isBridge.Method_isBridge_A01", any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.BridgeTestMethod
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.BridgeTestMethod" on path: DexPathList[[dex file "/tmp/junit1244663784930832031/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Method.isSynthetic.Method_isSynthetic_A01", any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.SyntheticTestMethod
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.SyntheticTestMethod" on path: DexPathList[[dex file "/tmp/junit5876026561576323251/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Method.getGenericReturnType.Method_getGenericReturnType_A04", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.MalformedReturnTypeMethod
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.MalformedReturnTypeMethod" on path: DexPathList[[dex file "/tmp/junit4310478819215974904/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A01",
+ any())
+ // 1) t03
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A01$Third
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A01$Third" on path: DexPathList[[dex file "/tmp/junit8600081041276641493/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t04
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A01$Fourth
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A01$Fourth" on path: DexPathList[[dex file "/tmp/junit8600081041276641493/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Method.invokeLjava_lang_Object$Ljava_lang_Object.Method_invoke_A07",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalAccessException
+
+ .put("lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A04",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.MalformedExceptionTypeMethod
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.MalformedExceptionTypeMethod" on path: DexPathList[[dex file "/tmp/junit1512043528789417983/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Method.getTypeParameters.Method_getTypeParameters_A02", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod" on path: DexPathList[[dex file "/tmp/junit2853449662835100192/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A03",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.TypeNotPresentException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.MissingExceptionTypeMethod
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.MissingExceptionTypeMethod" on path: DexPathList[[dex file "/tmp/junit1347702687417623444/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Method.getDeclaredAnnotations.Method_getDeclaredAnnotations_A01",
+ any())
+ // 1) t03
+ // java.lang.AssertionError: expected:<0> but was:<1>
+
+ .put("lang.reflect.Method.getGenericParameterTypes.Method_getGenericParameterTypes_A04",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.MalformedParameterTypeMethod
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.MalformedParameterTypeMethod" on path: DexPathList[[dex file "/tmp/junit2056931399679564203/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Method.toGenericString.Method_toGenericString_A01", any())
+ // 1) t03
+ // org.junit.ComparisonFailure: expected:<public static final [synchronized ]native void com.goog...> but was:<public static final []native void com.goog...>
+ // 2) t04
+ // org.junit.ComparisonFailure: expected:<..._toGenericString_A01[.com.google.jctf.test.lib.java.lang.reflect.Method.toGenericString.Method_toGenericString_A01]$GenericClass<java.l...> but was:<..._toGenericString_A01[]$GenericClass<java.l...>
+ // 3) t05
+ // org.junit.ComparisonFailure: expected:<..._toGenericString_A01[.com.google.jctf.test.lib.java.lang.reflect.Method.toGenericString.Method_toGenericString_A01]$GenericClass<java.l...> but was:<..._toGenericString_A01[]$GenericClass<java.l...>
+
+ .put("lang.reflect.Method.getGenericParameterTypes.Method_getGenericParameterTypes_A03",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.TypeNotPresentException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.MissingParameterTypeMethod
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.MissingParameterTypeMethod" on path: DexPathList[[dex file "/tmp/junit3534060116722105133/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put(
+ "lang.reflect.InvocationHandler.invokeLjava_lang_ObjectLjava_lang_reflect_Method$Ljava_lang_Object.InvocationHandler_invoke_A02",
+ any())
+ // 1) t04
+ // java.lang.AssertionError: ClassCastException should be thrown
+
+ .put("lang.reflect.Method.getDefaultValue.Method_getDefaultValue_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.TypeNotPresentException
+
+ .put("lang.reflect.Method.toString.Method_toString_A01", any())
+ // 1) t04
+ // org.junit.ComparisonFailure: expected:<public static final [synchronized ]native void com.goog...> but was:<public static final []native void com.goog...>
+
+ .put("lang.reflect.Method.getGenericParameterTypes.Method_getGenericParameterTypes_A02",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod" on path: DexPathList[[dex file "/tmp/junit7973288126499824876/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Field.getFloatLjava_lang_Object.Field_getFloat_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.getDeclaringClass.Field_getDeclaringClass_A01", any())
+ // 1) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.getByteLjava_lang_Object.Field_getByte_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.getCharLjava_lang_Object.Field_getChar_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.getBooleanLjava_lang_Object.Field_getBoolean_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.setByteLjava_lang_ObjectB.Field_setByte_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.setByteLjava_lang_ObjectB.Field_setByte_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Exception is not thrown: field: bytePublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@b1b0f3d
+
+ .put("lang.reflect.Field.setBooleanLjava_lang_ObjectZ.Field_setBoolean_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Exception is not thrown: field: booleanPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@953cc4f
+
+ .put("lang.reflect.Field.setCharLjava_lang_ObjectC.Field_setChar_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.isSynthetic.Field_isSynthetic_A01", any())
+ // 1) t01
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Field.TestSyntheticField
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Field.TestSyntheticField" on path: DexPathList[[dex file "/tmp/junit8256784459468391222/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Field.setBooleanLjava_lang_ObjectZ.Field_setBoolean_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.getType.Field_getType_A01", any())
+ // 1) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.setCharLjava_lang_ObjectC.Field_setChar_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Exception is not thrown: field: charPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@95271f1
+
+ .put("lang.reflect.Field.getDoubleLjava_lang_Object.Field_getDouble_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.setFloatLjava_lang_ObjectF.Field_setFloat_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Exception is not thrown: field: floatPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@3fca927
+
+ .put("lang.reflect.Field.getAnnotationLjava_lang_Class.Field_getAnnotation_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+ // 2) t03
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+ .put("lang.reflect.Field.getIntLjava_lang_Object.Field_getInt_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.setFloatLjava_lang_ObjectF.Field_setFloat_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.getShortLjava_lang_Object.Field_getShort_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.getGenericType.Field_getGenericType_A03", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.TypeNotPresentException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Field.TestMissingTypeField
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Field.TestMissingTypeField" on path: DexPathList[[dex file "/tmp/junit1097443728550054537/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Field.getDeclaredAnnotations.Field_getDeclaredAnnotations_A01", any())
+ // 1) t03
+ // java.lang.AssertionError: expected:<0> but was:<1>
+
+ .put("lang.reflect.Field.getGenericType.Field_getGenericType_A01", any())
+ // 1) t02
+ // org.junit.ComparisonFailure: expected:<...Field.TestOtherField[.com.google.jctf.test.lib.java.lang.reflect.Field.TestOtherField]$SomeClass<?>> but was:<...Field.TestOtherField[]$SomeClass<?>>
+ // 2) t03
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.setIntLjava_lang_ObjectI.Field_setInt_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.getGenericType.Field_getGenericType_A02", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Field.TestBadSignatureField
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Field.TestBadSignatureField" on path: DexPathList[[dex file "/tmp/junit8638189152422058286/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Field.toGenericString.Field_toGenericString_A01", any())
+ // 1) t02
+ // org.junit.ComparisonFailure: expected:<...Field.TestOtherField[.com.google.jctf.test.lib.java.lang.reflect.Field.TestOtherField]$SomeClass<?> com.go...> but was:<...Field.TestOtherField[]$SomeClass<?> com.go...>
+
+ .put("lang.reflect.Field.getGenericType.Field_getGenericType_A04", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Field.TestMalformedTypeField
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Field.TestMalformedTypeField" on path: DexPathList[[dex file "/tmp/junit1860681606366685174/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.Field.setIntLjava_lang_ObjectI.Field_setInt_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Exception is not thrown: field: intPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@94fbd35
+
+ .put("lang.reflect.Field.setDoubleLjava_lang_ObjectD.Field_setDouble_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.setShortLjava_lang_ObjectS.Field_setShort_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Exception is not thrown: field: shortPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@7887a47
+
+ .put("lang.reflect.Field.setLongLjava_lang_ObjectJ.Field_setLong_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.setLongLjava_lang_ObjectJ.Field_setLong_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Exception is not thrown: field: longPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@5c13759
+
+ .put("lang.reflect.Field.setDoubleLjava_lang_ObjectD.Field_setDouble_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Exception is not thrown: field: doublePublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@4dd95e2
+
+ .put("lang.reflect.Field.setShortLjava_lang_ObjectS.Field_setShort_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.getLjava_lang_Object.Field_get_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.getLongLjava_lang_Object.Field_getLong_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.setLjava_lang_ObjectLjava_lang_Object.Field_set_A05", any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+ // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+ // 2) t02
+ // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+ .put("lang.reflect.Field.setLjava_lang_ObjectLjava_lang_Object.Field_set_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Exception is not thrown: field: shortPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@bf7ecde
+
+ .put("lang.reflect.Constructor.newInstance$Ljava_lang_Object.Constructor_newInstance_A06",
+ any())
+ // 1) t05
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+ .put("lang.reflect.Constructor.isSynthetic.Constructor_isSynthetic_A01", any())
+ // 1) t02
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.SyntheticConstructorTestData
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.SyntheticConstructorTestData" on path: DexPathList[[dex file "/tmp/junit1480674965150331230/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put(
+ "lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A03",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.TypeNotPresentException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.MissingExceptionTypeConstructor
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.MissingExceptionTypeConstructor" on path: DexPathList[[dex file "/tmp/junit4009528913069484861/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put(
+ "lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A02",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.BadSignatureConstructor
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.BadSignatureConstructor" on path: DexPathList[[dex file "/tmp/junit1321049867835327167/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put(
+ "lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A01",
+ any())
+ // 1) t03
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A01$Third
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A01$Third" on path: DexPathList[[dex file "/tmp/junit8133550864036959380/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+ // 2) t04
+ // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A01$Fourth
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A01$Fourth" on path: DexPathList[[dex file "/tmp/junit8133550864036959380/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put(
+ "lang.reflect.Constructor.getAnnotationLjava_lang_Class.Constructor_getAnnotation_A01",
+ any())
+ // 1) t02
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+ // 2) t03
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+ .put(
+ "lang.reflect.Constructor.getDeclaredAnnotations.Constructor_getDeclaredAnnotations_A01",
+ any())
+ // 1) t03
+ // java.lang.AssertionError: expected:<0> but was:<1>
+
+ .put(
+ "lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A04",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.MalformedExceptionTypeConstructor
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.MalformedExceptionTypeConstructor" on path: DexPathList[[dex file "/tmp/junit376255323471566097/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put(
+ "lang.reflect.InvocationTargetException.serialization.InvocationTargetException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/reflect/InvocationTargetException/serialization/InvocationTargetException_serialization_A01.golden.0.ser
+
+ .put("lang.reflect.Constructor.toGenericString.Constructor_toGenericString_A01", any())
+ // 1) t04
+ // org.junit.ComparisonFailure: expected:<..._toGenericString_A01[.com.google.jctf.test.lib.java.lang.reflect.Constructor.toGenericString.Constructor_toGenericString_A01]$GenericClass<java.l...> but was:<..._toGenericString_A01[]$GenericClass<java.l...>
+
+ .put("lang.reflect.Constructor.getTypeParameters.Constructor_getTypeParameters_A02",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.BadSignatureConstructor
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.BadSignatureConstructor" on path: DexPathList[[dex file "/tmp/junit7135581864552916266/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put(
+ "lang.reflect.Constructor.getGenericParameterTypes.Constructor_getGenericParameterTypes_A03",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.TypeNotPresentException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.MissingParameterTypeConstructor
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.MissingParameterTypeConstructor" on path: DexPathList[[dex file "/tmp/junit1307676357171999053/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put(
+ "lang.reflect.Constructor.getGenericParameterTypes.Constructor_getGenericParameterTypes_A04",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.MalformedParameterTypeConstructor
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.MalformedParameterTypeConstructor" on path: DexPathList[[dex file "/tmp/junit4591001470670613975/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put(
+ "lang.reflect.Constructor.getGenericParameterTypes.Constructor_getGenericParameterTypes_A02",
+ any())
+ // 1) t01
+ // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+ // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.BadSignatureConstructor
+ // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.BadSignatureConstructor" on path: DexPathList[[dex file "/tmp/junit4070388768283971494/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+ .put("lang.reflect.AccessibleObject.setAccessibleZ.AccessibleObject_setAccessible_A03",
+ any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put(
+ "lang.reflect.UndeclaredThrowableException.serialization.UndeclaredThrowableException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/reflect/UndeclaredThrowableException/serialization/UndeclaredThrowableException_serialization_A01.golden.0.ser
+
+ .put("lang.reflect.AccessibleObject.setAccessibleZ.AccessibleObject_setAccessible_A02",
+ any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put(
+ "lang.reflect.AccessibleObject.setAccessible$Ljava_lang_reflect_AccessibleObjectZ.AccessibleObject_setAccessible_A03",
+ any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put(
+ "lang.reflect.AccessibleObject.isAnnotationPresentLjava_lang_Class.AccessibleObject_isAnnotationPresent_A01",
+ any())
+ // 1) t03
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+ // 2) t04
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+ .put(
+ "lang.reflect.AccessibleObject.setAccessible$Ljava_lang_reflect_AccessibleObjectZ.AccessibleObject_setAccessible_A02",
+ any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.reflect.AccessibleObject.getAnnotations.AccessibleObject_getAnnotations_A01",
+ any())
+ // 1) t04
+ // java.lang.AssertionError: expected:<0> but was:<1>
+ // 2) t06
+ // java.lang.AssertionError: Misconfigured test
+
+ .put(
+ "lang.reflect.AccessibleObject.getDeclaredAnnotations.AccessibleObject_getDeclaredAnnotations_A01",
+ any())
+ // 1) t03
+ // java.lang.AssertionError: expected:<0> but was:<1>
+
+ .put(
+ "lang.reflect.AccessibleObject.getAnnotationLjava_lang_Class.AccessibleObject_getAnnotation_A01",
+ any())
+ // 1) t02
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+ // 2) t03
+ // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+ .put("lang.IllegalAccessException.serialization.IllegalAccessException_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IllegalAccessException/serialization/IllegalAccessException_serialization_A01.golden.0.ser
+
+ .put("lang.Character.getTypeI.Character_getType_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<22> but was:<28>
+
+ .put("lang.Character.isDigitI.Character_isDigit_A01", any())
+ // 1) t02
+ // java.lang.AssertionError
+
+ .put("lang.Character.getTypeC.Character_getType_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<22> but was:<28>
+
+ .put("lang.Character.serialization.Character_serialization_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Character/serialization/Character_serialization_A01.golden.0.ser
+
+ .put("lang.Character.isDigitC.Character_isDigit_A01", any())
+ // 1) t01
+ // java.lang.AssertionError
+
+ .put("lang.Character.digitCI.Character_digit_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<1> but was:<-1>
+
+ .put("lang.Character.digitII.Character_digit_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<1> but was:<-1>
+
+ .put("lang.Character.isLowerCaseC.Character_isLowerCase_A01", any())
+ // 1) t01
+ // java.lang.AssertionError
+
+ .put("lang.Character.getDirectionalityI.Character_getDirectionality_A01", any())
+ // 1) t01
+ // java.lang.AssertionError
+
+ .put("lang.Character.UnicodeBlock.ofC.UnicodeBlock_of_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: expected null, but was:<ARABIC_SUPPLEMENT>
+
+ .put("lang.Character.UnicodeBlock.ofI.UnicodeBlock_of_A01", any())
+ // 1) t02
+ // java.lang.AssertionError: expected null, but was:<ANCIENT_GREEK_NUMBERS>
+
+ .put("lang.Character.isLowerCaseI.Character_isLowerCase_A01", any())
+ // 1) t01
+ // java.lang.AssertionError
+
+ .put("lang.Process.waitFor.Process_waitFor_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: expected:<127> but was:<1>
+
+ .put("lang.System.getProperties.System_getProperties_A01", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.Process.getErrorStream.Process_getErrorStream_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<0> but was:<69>
+
+ .put("lang.Character.getDirectionalityC.Character_getDirectionality_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Char #0
+
+ .put("lang.Process.exitValue.Process_exitValue_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: expected:<127> but was:<1>
+
+ .put("lang.System.loadLjava_lang_String.System_load_A01", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.Process.getInputStream.Process_getInputStream_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<0> but was:<-1>
+
+ .put("lang.System.loadLibraryLjava_lang_String.System_loadLibrary_A01", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put(
+ "lang.System.setSecurityManagerLjava_lang_SecurityManager.System_setSecurityManager_A02",
+ any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.System.runFinalizersOnExitZ.System_runFinalizersOnExit_A01", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.System.getenvLjava_lang_String.System_getenv_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Error: Could not find or load main class com.google.jctf.test.lib.java.lang.System.getenvLjava_lang_String.System_getenv_A01
+ // expected:<0> but was:<1>
+
+ .put("lang.System.getenv.System_getenv_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: Error: Could not find or load main class com.google.jctf.test.lib.java.lang.System.getenv.System_getenv_A01
+ // expected:<0> but was:<1>
+
+ .put("lang.System.getPropertyLjava_lang_StringLjava_lang_String.System_getProperty_A01",
+ any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.System.exitI.System_exit_A01", any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<88> but was:<1>
+
+ .put(
+ "util.concurrent.ArrayBlockingQueue.serialization.ArrayBlockingQueue_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/ArrayBlockingQueue/serialization/ArrayBlockingQueue_serialization_A01.golden.0.ser
+
+ .put("lang.System.arraycopyLjava_lang_ObjectILjava_lang_ObjectII.System_arraycopy_A04",
+ any())
+ // 1) t05
+ // java.lang.ArrayIndexOutOfBoundsException: src.length=3 srcPos=0 dst.length=1 dstPos=1 length=2
+ // 2) t06
+ // java.lang.ArrayIndexOutOfBoundsException: src.length=1 srcPos=0 dst.length=3 dstPos=1 length=2
+ // 3) t07
+ // java.lang.ArrayIndexOutOfBoundsException: src.length=1 srcPos=0 dst.length=1 dstPos=1 length=100
+
+ .put("lang.System.setPropertiesLjava_util_Properties.System_setProperties_A02", any())
+ // 1) t01
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.System.setPropertiesLjava_util_Properties.System_setProperties_A02
+ // expected:<0> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.System.setPropertiesLjava_util_Properties.System_setProperties_A02
+ // expected:<0> but was:<1>
+
+ .put("lang.System.clearPropertyLjava_lang_String.System_clearProperty_A02", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put("lang.System.getPropertyLjava_lang_String.System_getProperty_A01", any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put(
+ "util.concurrent.LinkedBlockingQueue.serialization.LinkedBlockingQueue_serialization_A01",
+ any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/LinkedBlockingQueue/serialization/LinkedBlockingQueue_serialization_A01.golden.0.ser
+
+ .put(
+ "util.concurrent.LinkedBlockingDeque.serialization.LinkedBlockingDeque_serialization_A01",
+ any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/LinkedBlockingDeque/serialization/LinkedBlockingDeque_serialization_A01.golden.0.ser
+
+ .put(
+ "util.concurrent.ConcurrentLinkedQueue.serialization.ConcurrentLinkedQueue_serialization_A01",
+ any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/ConcurrentLinkedQueue/serialization/ConcurrentLinkedQueue_serialization_A01.golden.0.ser
+
+ .put("util.concurrent.SynchronousQueue.serialization.SynchronousQueue_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+ // 2) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/SynchronousQueue/serialization/SynchronousQueue_serialization_A01.golden.0.ser
+
+ .put(
+ "util.concurrent.CopyOnWriteArrayList.serialization.CopyOnWriteArrayList_serialization_A01",
+ any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/CopyOnWriteArrayList/serialization/CopyOnWriteArrayList_serialization_A01.golden.0.ser
+
+ .put("util.concurrent.CopyOnWriteArrayList.subListII.CopyOnWriteArrayList_subList_A01",
+ any())
+ // 1) t03
+ // java.util.ConcurrentModificationException
+
+ .put(
+ "util.concurrent.ScheduledThreadPoolExecutor.getTaskCount.ScheduledThreadPoolExecutor_getTaskCount_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: expected:<1> but was:<2>
+
+ .put(
+ "util.concurrent.ConcurrentHashMap.serialization.ConcurrentHashMap_serialization_A01",
+ any())
+ // 1) t01
+ // java.lang.AssertionError: Unable to configure default providers
+
+ .put("util.concurrent.ConcurrentHashMap.keySet.ConcurrentHashMap_keySet_A01", any())
+ // 1) t01
+ // java.lang.NoSuchMethodError: No virtual method keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; in class Ljava/util/concurrent/ConcurrentHashMap; or its super classes (declaration of 'java.util.concurrent.ConcurrentHashMap' appears in r8/tools/linux/art/framework/core-oj-hostdex.jar)
+ // 2) t02
+ // java.lang.NoSuchMethodError: No virtual method keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; in class Ljava/util/concurrent/ConcurrentHashMap; or its super classes (declaration of 'java.util.concurrent.ConcurrentHashMap' appears in r8/tools/linux/art/framework/core-oj-hostdex.jar)
+
+ .put(
+ "util.concurrent.Executors.privilegedThreadFactory.Executors_privilegedThreadFactory_A01",
+ any())
+ // 1) t02
+ // java.lang.AssertionError: Unexpected exception: java.lang.AssertionError: no AccessControlException
+ // 2) t02
+ // java.lang.AssertionError: java.lang.AssertionError: no AccessControlException
+ // Caused by: java.lang.AssertionError: no AccessControlException
+ // 3) t03
+ // java.lang.AssertionError: Unexpected exception: java.lang.AssertionError: no AccessControlException
+ // 4) t03
+ // java.lang.AssertionError: java.lang.AssertionError: no AccessControlException
+ // Caused by: java.lang.AssertionError: no AccessControlException
+
+ .put(
+ "util.concurrent.Executors.privilegedCallableLjava_util_concurrent_Callable.Executors_privilegedCallable_A01",
+ any())
+ // 1) t01
+ // java.lang.SecurityException
+
+ .put(
+ "util.concurrent.CopyOnWriteArraySet.serialization.CopyOnWriteArraySet_serialization_A01",
+ any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/CopyOnWriteArraySet/serialization/CopyOnWriteArraySet_serialization_A01.golden.0.ser
+
+ .put(
+ "util.concurrent.Executors.privilegedCallableUsingCurrentClassLoaderLjava_util_concurrent_Callable.Executors_privilegedCallableUsingCurrentClassLoader_A01",
+ any())
+ // 1) t02
+ // java.lang.SecurityException
+
+ .put(
+ "util.concurrent.PriorityBlockingQueue.ConstructorLjava_util_Collection.PriorityBlockingQueue_Constructor_A01",
+ any())
+ // 1) t03
+ // java.lang.AssertionError: expected same:<com.google.jctf.test.lib.java.util.concurrent.PriorityBlockingQueue.MyReverseComparator@735f1e1> was not:<null>
+
+ .put(
+ "util.concurrent.PriorityBlockingQueue.serialization.PriorityBlockingQueue_serialization_A01",
+ any())
+ // 1) t02
+ // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/PriorityBlockingQueue/serialization/PriorityBlockingQueue_serialization_A01.golden.0.ser
+
+ .put("lang.ThreadGroup.destroy.ThreadGroup_destroy_A01", match(R8_COMPILER))
+ // 1) t05
+ // java.lang.AssertionError: Destroyed thread group was not finalized
+
+ .put("lang.ThreadGroup.destroy.ThreadGroup_destroy_A01",
+ match(D8_COMPILER, runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t02
+ // java.lang.IllegalThreadStateException: Thread group still contains threads: Test group
+ // 2) t04
+ // java.lang.IllegalThreadStateException: Thread group still contains threads: Depth = 2, N = 0
+ // 3) t05
+ // java.lang.AssertionError: Destroyed thread group was not finalized
+
+ .put("lang.Thread.start.Thread_start_A01",
+ match(runtimes(DexVm.ART_7_0_0)))
+ // 1) t01(com.google.jctf.test.lib.java.lang.Thread.start.Thread_start_A01)
+ // java.lang.AssertionError: no IllegalThreadStateException 1
+
+ .put("lang.String.getBytesLjava_lang_String.String_getBytes_A02",
+ match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01(com.google.jctf.test.lib.java.lang.String.getBytesLjava_lang_String.String_getBytes_A02)
+ // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.io.UnsupportedEncodingException>
+
+ .put(
+ "util.concurrent.CopyOnWriteArrayList.lastIndexOfLjava_lang_ObjectI.CopyOnWriteArrayList_lastIndexOf_A02",
+ match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // java.lang.AssertionError: Expected exception: java.lang.IndexOutOfBoundsException
+
+ .put(
+ "util.concurrent.CopyOnWriteArrayList.lastIndexOfLjava_lang_ObjectI.CopyOnWriteArrayList_lastIndexOf_A01",
+ match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01(com.google.jctf.test.lib.java.util.concurrent.CopyOnWriteArrayList.lastIndexOfLjava_lang_ObjectI.CopyOnWriteArrayList_lastIndexOf_A01)
+ // java.lang.AssertionError: expected:<3> but was:<1>
+ // 2) t02(com.google.jctf.test.lib.java.util.concurrent.CopyOnWriteArrayList.lastIndexOfLjava_lang_ObjectI.CopyOnWriteArrayList_lastIndexOf_A01)
+ // java.lang.ArrayIndexOutOfBoundsException: length=3; index=2147483647
+
+ .put("lang.StringBuffer.getCharsII$CI.StringBuffer_getChars_A03",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t03
+ // java.lang.NullPointerException: dst == null
+
+ .put("lang.StringBuffer.appendF.StringBuffer_append_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t02
+ // java.lang.AssertionError: Buffer is invalid length after append expected:<26> but was:<25>
+
+ .put("lang.StringBuffer.insertI$CII.StringBuffer_insert_A02",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.NullPointerException: Attempt to get length of null array
+
+ .put("lang.StrictMath.scalbDI.StrictMath_scalb_A03",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.AssertionError: Wrong result provided for argument: -1.7976931348623157E308 scaleFactor: 2147483647 expected:<-Infinity> but was:<-0.0>
+
+ .put("lang.StrictMath.scalbDI.StrictMath_scalb_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t03
+ // java.lang.AssertionError: Wrong result provided for argument: -2.2250738585072014E-308 scaleFactor: -2147483647 expected:<-0.0> but was:<-Infinity>
+ // 2) t04
+ // java.lang.AssertionError: Wrong result provided for argument: 1.7976931348623157E308 scaleFactor: -2046 expected:<2.2250738585072014E-308> but was:<2.225073858507201E-308>
+
+ .put("lang.StrictMath.scalbFI.StrictMath_scalb_A03",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.AssertionError: Wrong result provided for argument: -3.4028235E38 scaleFactor: 2147483647 expected:<-Infinity> but was:<-0.0>
+
+ .put("lang.StrictMath.scalbFI.StrictMath_scalb_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t02
+ // java.lang.AssertionError: Wrong result provided for argument: 3.4028235E38 scaleFactor: -254 expected:<1.1754943508222875E-38> but was:<1.1754942106924411E-38>
+ // 2) t03
+ // java.lang.AssertionError: Wrong result provided for argument: -1.1754944E-38 scaleFactor: -2147483647 expected:<-0.0> but was:<-Infinity>
+
+ .put(
+ "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A07",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t02
+ // java.lang.AssertionError: wrong daemonism expected:<true> but was:<false>
+
+ .put(
+ "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String.Thread_Constructor_A07",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t02
+ // java.lang.AssertionError: wrong daemonism expected:<true> but was:<false>
+
+ .put("lang.Thread.toString.Thread_toString_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t02
+ // java.lang.AssertionError
+
+ .put("lang.Thread.start.Thread_start_A02",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.IllegalThreadStateException: Thread group still contains threads: start
+
+ .put("lang.Thread.setPriorityI.Thread_setPriority_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t02
+ // java.lang.AssertionError: expected:<5> but was:<10>
+
+ .put("lang.ClassLoader.ConstructorLjava_lang_ClassLoader.ClassLoader_Constructor_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.NullPointerException: parentLoader == null && !nullAllowed
+ // 2) t03
+ // java.lang.NullPointerException: parentLoader == null && !nullAllowed
+
+ .put("lang.Enum.compareToLjava_lang_Enum.Enum_compareTo_A03",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.ClassCastException
+
+ .put("lang.Enum.hashCode.Enum_hashCode_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t02
+ // java.lang.AssertionError
+
+ .put("lang.StackTraceElement.hashCode.StackTraceElement_hashCode_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t02
+ // java.lang.AssertionError
+
+ .put("lang.ProcessBuilder.environment.ProcessBuilder_environment_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.AssertionError: should throw ClassCastException.
+
+ .put("lang.ProcessBuilder.environment.ProcessBuilder_environment_A03",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.AssertionError: should throw ClassCastException.
+
+ .put("lang.Float.toStringF.Float_toString_A04",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t02
+ // org.junit.ComparisonFailure: Invalid string produced for bits: 4efffffa expected:<2.147482[88]E9> but was:<2.147482[9]E9>
+
+ .put("lang.Float.toStringF.Float_toString_A03",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t02
+ // org.junit.ComparisonFailure: expected:<-1.175494[35]E-38> but was:<-1.175494[4]E-38>
+
+ .put("lang.ThreadGroup.getMaxPriority.ThreadGroup_getMaxPriority_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.AssertionError: New value should be the same as we set expected:<2> but was:<1>
+
+ .put(
+ "lang.ThreadGroup.uncaughtExceptionLjava_lang_ThreadLjava_lang_Throwable.ThreadGroup_uncaughtException_A02",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t05
+ // java.lang.AssertionError: Non-informative exception info: java.lang.RuntimeException
+
+ .put("lang.ThreadGroup.list.ThreadGroup_list_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t04
+ // java.lang.IllegalThreadStateException: Thread group still contains threads: Test group(list)
+
+ .put("lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.IllegalThreadStateException: Thread group still contains threads: Test root(setMaxPriority)
+
+ .put("lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A04",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.AssertionError: New value should be the same as we set expected:<2> but was:<1>
+ // 2) t02
+ // java.lang.AssertionError: expected:<4> but was:<1>
+ // 3) t03
+ // java.lang.AssertionError: expected:<7> but was:<1>
+
+ .put("lang.ThreadGroup.toString.ThreadGroup_toString_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // org.junit.ComparisonFailure: toString does not follow the RI expected:<... group(toString),max[pri]=10]> but was:<... group(toString),max[Priority]=10]>
+
+ .put("lang.Class.getFieldLjava_lang_String.Class_getField_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t04
+ // java.lang.AssertionError: expected:<interface com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A01$I1> but was:<class com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A01$S1>
+
+ .put("lang.String.replaceCC.String_replace_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t04
+ // java.lang.AssertionError: expected same:<aaaaaa> was not:<aaaaaa>
+
+ .put("lang.Package.isCompatibleWithLjava_lang_String.Package_isCompatibleWith_A02",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.AssertionError: NumberFormatException isn't thrown for desired . and current 1.0
+ // 2) t03
+ // java.lang.AssertionError: NumberFormatException isn't thrown for desired 1.0 and current .
+
+ .put("lang.StringBuilder.appendF.StringBuilder_append_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.AssertionError: Invalid length of created builder expected:<14> but was:<13>
+
+ .put("lang.StringBuilder.insertIF.StringBuilder_insert_A01",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.AssertionError: Invalid length of created builder expected:<14> but was:<13>
+
+ .put(
+ "lang.reflect.AccessibleObject.setAccessible$Ljava_lang_reflect_AccessibleObjectZ.AccessibleObject_setAccessible_A04",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.AssertionError: SecurityException expected.
+ // 2) t02
+ // java.lang.AssertionError: SecurityException expected.
+ // 3) t03
+ // java.lang.AssertionError: SecurityException expected.
+ // 4) t04
+ // java.lang.AssertionError: SecurityException expected.
+
+ .put("lang.Character.UnicodeBlock.forName_java_lang_String.UnicodeBlock_forName_A03",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+ .put("lang.System.loadLjava_lang_String.System_load_A02",
+ match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // 1) t03
+ // java.lang.AssertionError: Expected exception: java.lang.UnsatisfiedLinkError
+
+ .put("lang.StrictMath.nextAfterFD.StrictMath_nextAfter_A01",
+ match(R8_COMPILER, runtimes(DexVm.ART_5_1_1)))
+ // 1) t01
+ // java.lang.AssertionError: Wrong value returned for start: Infinity direction: NaN expected:<Infinity> but was:<NaN>
+ // 2) t02
+ // java.lang.AssertionError: Wrong value returned for start: -0.0 direction: NaN expected:<-1.4E-45> but was:<NaN>
+
+ .put("lang.Math.hypotDD.Math_hypot_A04", match(runtimes(DexVm.ART_5_1_1)))
+ // 1) t04
+ // java.lang.AssertionError
+
+ .build(); // end of failuresToTriage
+
+ public static final Multimap<String, TestCondition> flakyWithArt =
+ new ImmutableListMultimap.Builder<String, TestCondition>()
+
+ .put("lang.Object.notifyAll.Object_notifyAll_A03", any())
+ // AssertionError: Unexpected art failure: '+ invoke_with=
+ // AssertionError: expected:<BLOCKED> but was:<WAITING>
+
+ .put("lang.Object.notify.Object_notify_A03", any())
+ // AssertionError: Unexpected art failure: '+ invoke_with=
+ // AssertionError: expected:<BLOCKED> but was:<WAITING>
+
+ .put(
+ "util.concurrent.ConcurrentSkipListSet.addLjava_lang_Object.ConcurrentSkipListSet_add_A01",
+ any())
+ // AssertionError: Unexpected art failure: '+ invoke_with=
+ // AssertionError: Expected exception: java.lang.ClassCastException
+
+ .put("util.concurrent.SynchronousQueue.ConstructorZ", any())
+ // Only on bots:
+ // 1) t02
+ // java.lang.AssertionError: java.lang.AssertionError: expected:<5> but was:<4>
+
+ .put("lang.Thread.interrupt.Thread_interrupt_A04",
+ match(D8_COMPILER, runtimes(DexVm.ART_6_0_1)))
+ // Been running fine for a while then this happened on a bot:
+ // 1) t01
+ // java.lang.AssertionError: expected:<BLOCKED> but was:<RUNNABLE>
+
+ .put("util.concurrent.SynchronousQueue.ConstructorZ.SynchronousQueue_Constructor_A01",
+ any())
+ // Failed on bot only:
+ // 1) t02
+ // java.lang.AssertionError: java.lang.AssertionError: expected:<7> but was:<6>
+
+ .put("lang.Thread.getState.Thread_getState_A01", any())
+ // Failed on bot only (R8):
+ // 1) t02
+ // java.lang.AssertionError: java.lang.AssertionError: expected:<7> but was:<6>
+
+ .build(); // end of flakyWithArt
+
+ public static final Multimap<String, TestCondition> timeoutsWithArt =
+ new ImmutableListMultimap.Builder<String, TestCondition>()
+ .put("lang.Thread.interrupt.Thread_interrupt_A01", any())
+ .put("lang.Thread.resume.Thread_resume_A01", any())
+ .put("lang.Thread.stop.Thread_stop_A01", any())
+ .put("lang.Thread.suspend.Thread_suspend_A01", any())
+ .put(
+ "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A04",
+ any())
+ .put(
+ "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A03",
+ any())
+ .put(
+ "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A05",
+ any())
+ .put("lang.Thread.setNameLjava_lang_String.Thread_setName_A02", any())
+ .put("lang.Thread.stop.Thread_stop_A02", any())
+ .put(
+ "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String.Thread_Constructor_A02",
+ any())
+ .put(
+ "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String.Thread_Constructor_A03",
+ any())
+ .put("lang.Thread.getStackTrace.Thread_getStackTrace_A03", any())
+ .put(
+ "lang.Thread.setDefaultUncaughtExceptionHandler.Thread_setDefaultUncaughtExceptionHandler_A02",
+ any())
+ .put("lang.Thread.checkAccess.Thread_checkAccess_A01", any())
+ .put(
+ "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String.Thread_Constructor_A04",
+ any())
+ .put("lang.Thread.setUncaughtExceptionHandler.Thread_setUncaughtExceptionHandler_A02",
+ any())
+ .put("lang.Thread.stopLjava_lang_Throwable.Thread_stop_A01", any())
+ .put("lang.Thread.getAllStackTraces.Thread_getAllStackTraces_A02", any())
+ .put(
+ "lang.Thread.setContextClassLoaderLjava_lang_ClassLoader.Thread_setContextClassLoader_A02",
+ any())
+ .put("lang.Thread.setPriorityI.Thread_setPriority_A02", any())
+ .put("lang.Thread.stopLjava_lang_Throwable.Thread_stop_A02", any())
+ .put("lang.Runtime.execLjava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A04",
+ any())
+ .put("lang.Thread.getContextClassLoader.Thread_getContextClassLoader_A02", any())
+ .put("lang.ThreadGroup.suspend.ThreadGroup_suspend_A02", any())
+ .put("lang.Thread.setDaemonZ.Thread_setDaemon_A03", any())
+ .put("lang.ProcessBuilder.environment.ProcessBuilder_environment_A07", any())
+ .put(
+ "lang.Runtime.exec$Ljava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A04",
+ any())
+ .put("lang.Runtime.execLjava_lang_String$Ljava_lang_String.Runtime_exec_A04", any())
+ .put("lang.Runtime.exec$Ljava_lang_String.Runtime_exec_A04", any())
+ .put("lang.Runtime.execLjava_lang_String.Runtime_exec_A04", any())
+ .put("lang.System.clearPropertyLjava_lang_String.System_clearProperty_A03", any())
+ .put("lang.System.getSecurityManager.System_getSecurityManager_A01", any())
+ .put("lang.System.setInLjava_io_InputStream.System_setIn_A02", any())
+ .put("lang.System.setOutLjava_io_PrintStream.System_setOut_A02", any())
+ .put("lang.ThreadGroup.destroy.ThreadGroup_destroy_A04", any())
+ .put("lang.ThreadGroup.enumerate$ThreadGroupZ.ThreadGroup_enumerate_A03", any())
+ .put("lang.ThreadGroup.enumerate$Thread.ThreadGroup_enumerate_A03", any())
+ .put("lang.ThreadGroup.enumerate$ThreadZ.ThreadGroup_enumerate_A03", any())
+ .put("lang.ThreadGroup.interrupt.ThreadGroup_interrupt_A02", any())
+ .put("lang.ThreadGroup.resume.ThreadGroup_resume_A02", any())
+ .put("lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A02", any())
+ .put("lang.Runtime.exec$Ljava_lang_String$Ljava_lang_String.Runtime_exec_A04", any())
+ .put("lang.System.getenvLjava_lang_String.System_getenv_A03", any())
+ .put("lang.System.setPropertyLjava_lang_StringLjava_lang_String.System_setProperty_A02",
+ any())
+ .put("lang.ThreadGroup.enumerate$ThreadGroup.ThreadGroup_enumerate_A03", any())
+ .put("lang.ThreadGroup.getParent.ThreadGroup_getParent_A02", any())
+ .put("lang.ThreadGroup.setDaemonZ.ThreadGroup_setDaemon_A02", any())
+ .put("lang.ThreadGroup.stop.ThreadGroup_stop_A02", any())
+ .put("lang.Class.getSuperclass.Class_getSuperclass_A01", any())
+ .put("lang.System.getenv.System_getenv_A03", any())
+ .put("lang.System.inheritedChannel.System_inheritedChannel_A01", any())
+ .put(
+ "util.concurrent.ArrayBlockingQueue.containsLjava_lang_Object.ArrayBlockingQueue_contains_A01",
+ any())
+ .put("lang.System.arraycopyLjava_lang_ObjectILjava_lang_ObjectII.System_arraycopy_A03",
+ any())
+ .put("lang.System.setErrLjava_io_PrintStream.System_setErr_A02", any())
+ .put(
+ "util.concurrent.ArrayBlockingQueue.containsLjava_lang_Object.ArrayBlockingQueue_contains_A01",
+ any())
+ .put(
+ "lang.System.setSecurityManagerLjava_lang_SecurityManager.System_setSecurityManager_A01",
+ any())
+ .put(
+ "util.concurrent.ArrayBlockingQueue.containsLjava_lang_Object.ArrayBlockingQueue_contains_A01",
+ any())
+ .put(
+ "util.concurrent.ArrayBlockingQueue.containsLjava_lang_Object.ArrayBlockingQueue_contains_A01",
+ any())
+ .put("lang.System.setPropertiesLjava_util_Properties.System_setProperties_A01", any())
+ .put(
+ "util.concurrent.CopyOnWriteArrayList.ConstructorLjava_util_Collection.CopyOnWriteArrayList_Constructor_A02",
+ any())
+ .put("util.concurrent.CyclicBarrier.reset.CyclicBarrier_reset_A03", any())
+ .put("lang.System.clearPropertyLjava_lang_String.System_clearProperty_A01", any())
+ .put("lang.System.getenv.System_getenv_A04", any())
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A02", any())
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A13", any())
+ .build(); // end of timeoutsWithArt
+
+ private static final boolean testMatch(Multimap<String, TestCondition> testConditions,
+ String name,
+ CompilerUnderTest compilerUnderTest, DexVm dexVm) {
+ Collection<TestCondition> entries = testConditions.get(name);
+ for (TestCondition entry : entries) {
+ if (entry.test(DexTool.NONE, compilerUnderTest, dexVm)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static final Outcome getExpectedOutcome(String name,
+ CompilerUnderTest compilerUnderTest,
+ DexVm dexVm) {
+
+ Outcome outcome = null;
+
+ if (testMatch(failuresToTriage, name, compilerUnderTest, dexVm)) {
+ outcome = Outcome.FAILS_WITH_ART;
+ }
+ if (testMatch(timeoutsWithArt, name, compilerUnderTest, dexVm)) {
+ assert outcome == null;
+ outcome = Outcome.TIMEOUTS_WITH_ART;
+ }
+ if (testMatch(flakyWithArt, name, compilerUnderTest, dexVm)) {
+ assert outcome == null;
+ outcome = Outcome.FLAKY_WITH_ART;
+ }
+ return outcome == null ? Outcome.PASSES : outcome;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java b/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java
new file mode 100644
index 0000000..8fca2ed
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.DexFileReader;
+import com.android.tools.r8.dex.Segment;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+
+public class R8CodeCanonicalizationTest {
+
+ private static final String SOURCE_DEX = "invokeempty/classes.dex";
+
+ private int readNumberOfCodes(Path file) throws IOException {
+ Segment[] segments = DexFileReader.parseMapFrom(file);
+ for (Segment segment : segments) {
+ if (segment.getType() == Constants.TYPE_CODE_ITEM) {
+ return segment.getSize();
+ }
+ }
+ return 0;
+ }
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void testNumberOfCodeItemsUnchanged()
+ throws IOException, ExecutionException, ProguardRuleParserException, CompilationException {
+ int numberOfCodes = readNumberOfCodes(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + SOURCE_DEX));
+ ToolHelper.runR8(ToolHelper.EXAMPLES_BUILD_DIR + SOURCE_DEX, temp.getRoot().getCanonicalPath());
+ int newNumberOfCodes = readNumberOfCodes(
+ Paths.get(temp.getRoot().getCanonicalPath(), "classes.dex"));
+ Assert.assertEquals("Number of codeitems does not change.", numberOfCodes, newNumberOfCodes);
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
new file mode 100644
index 0000000..b026dcd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -0,0 +1,1368 @@
+// 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.ArtErrorParser;
+import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.JarBuilder;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.OffOrAuto;
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.ObjectArrays;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+import org.junit.ComparisonFailure;
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * This test class is not invoked directly. Instead, the gradle script generates one subclass per
+ * actual art test. This allows us to run these in parallel.
+ */
+public abstract class R8RunArtTestsTest {
+
+ private static final boolean DEX_COMPARE_WITH_DEX_REFERENCE_ON_FAILURE = true;
+ private static String[] D8_EXTRA_ARGS = {"--debug"};
+
+ private final String name;
+ private final DexTool toolchain;
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ public enum DexTool {
+ JACK,
+ DX,
+ NONE // Working directly on .class files.
+ }
+
+ public enum CompilerUnderTest {
+ D8,
+ R8
+ }
+
+ private static final String ART_TESTS_DIR = "tests/art";
+ private static final String ART_TESTS_NATIVE_LIBRARY_DIR = "tests/art/lib64";
+
+ // Input jar for jctf tests.
+ private static final String JCTF_COMMON_JAR = "build/libs/jctfCommon.jar";
+
+ // Parent dir for on-the-fly compiled jctf dex output.
+ private static final String JCTF_TESTS_PREFIX = "build/classes/jctfTests";
+ private static final String JCTF_TESTS_LIB_PREFIX =
+ JCTF_TESTS_PREFIX + "/com/google/jctf/test/lib";
+ private static final String JUNIT_TEST_RUNNER = "org.junit.runner.JUnitCore";
+ private static final String JUNIT_JAR = "third_party/gradle/gradle/lib/plugins/junit-4.12.jar";
+ private static final String HAMCREST_JAR =
+ "third_party/gradle/gradle/lib/plugins/hamcrest-core-1.3.jar";
+
+ // Test that required to set min-sdk-version to a specific value.
+ private static Map<String, Integer> needMinSdkVersion =
+ new ImmutableMap.Builder<String, Integer>()
+ // Android O
+ .put("953-invoke-polymorphic-compiler", Constants.ANDROID_O_API)
+ .put("957-methodhandle-transforms", Constants.ANDROID_O_API)
+ .put("958-methodhandle-stackframe", Constants.ANDROID_O_API)
+ .put("959-invoke-polymorphic-accessors", Constants.ANDROID_O_API)
+ // Test intentionally asserts presence of bridge default methods desugar removes.
+ .put("044-proxy", Constants.ANDROID_N_API)
+ // Test intentionally asserts absence of default interface method in a class.
+ .put("048-reflect-v8", Constants.ANDROID_N_API)
+ // Interface initializer is not triggered after desugaring.
+ .put("962-iface-static", Constants.ANDROID_N_API)
+ // Interface initializer is not triggered after desugaring.
+ .put("964-default-iface-init-gen", Constants.ANDROID_N_API)
+ // AbstractMethodError (for method not implemented in class) instead of
+ // IncompatibleClassChangeError (for conflict of default interface methods).
+ .put("968-default-partial-compile-gen", Constants.ANDROID_N_API)
+ // NoClassDefFoundError (for companion class) instead of NoSuchMethodError.
+ .put("970-iface-super-resolution-gen", Constants.ANDROID_N_API)
+ // NoClassDefFoundError (for companion class) instead of AbstractMethodError.
+ .put("971-iface-super", Constants.ANDROID_N_API)
+ // Test for miranda methods is not relevant for desugaring scenario.
+ .put("972-default-imt-collision", Constants.ANDROID_N_API)
+ // Uses default interface methods.
+ .put("972-iface-super-multidex", Constants.ANDROID_N_API)
+ // java.util.Objects is missing and test has default methods.
+ .put("973-default-multidex", Constants.ANDROID_N_API)
+ // a.klass.that.does.not.Exist is missing and test has default methods.
+ .put("974-verify-interface-super", Constants.ANDROID_N_API)
+ // Desugaring of interface private methods is not yet supported.
+ .put("975-iface-private", Constants.ANDROID_N_API)
+ .build();
+
+ // Tests requiring interface method desugaring because they explicitly or
+ // implicitly define static or default interface methods and are enabled
+ // on pre-N Android.
+ private static List<String> enableInterfaceMethodDesugaring =
+ ImmutableList.of(
+ "563-checker-invoke-super",
+ "604-hot-static-interface",
+ "961-default-iface-resolution-gen",
+ "963-default-range-smali",
+ "965-default-verify",
+ "967-default-ame",
+ "969-iface-super",
+ "978-virtual-interface"
+ );
+
+ // Tests that timeout when run with Art.
+ private static final Multimap<String, TestCondition> timeoutOrSkipRunWithArt =
+ new ImmutableListMultimap.Builder<String, TestCondition>()
+ // Loops on art - timeout.
+ .put("109-suspend-check", TestCondition.match(TestCondition.runtimes(DexVm.ART_5_1_1)))
+ // Flaky loops on art.
+ .put("129-ThreadGetId", TestCondition.match(TestCondition.runtimes(DexVm.ART_5_1_1)))
+ // Takes ages to run on art 5.1.1 and behaves the same as on 6.0.1. Running this
+ // tests on 5.1.1 makes our buildbot cycles time too long.
+ .put("800-smali", TestCondition.match(TestCondition.runtimes(DexVm.ART_5_1_1)))
+ .build();
+
+ // Tests that are flaky with the Art version we currently use.
+ // TODO(zerny): Amend flaky tests with an expected flaky result to track issues.
+ private static List<String> flakyRunWithArt = ImmutableList.of(
+ // Can crash but mostly passes
+ // Crashes:
+ // check_reference_map_visitor.h:44] At Main.f
+ // Check failed: size() >= sizeof(T) (size()=0, sizeof(T)=1)
+ // If art is passed -Xrelocate instead of -Xnorelocate the test passes.
+ "004-ReferenceMap",
+ // Also marked flaky in the art repo, sometimes hangs, sometimes fails with a segfault:
+ // line 105: 23283 Segmentation fault
+ "004-ThreadStress",
+ // When it fails:
+ // stack_walk_jni.cc:57] Check failed: 0xdU == GetDexPc() (0xdU=13, GetDexPc()=23)
+ // The R8/D8 code does not produce code with the same instructions.
+ "004-StackWalk",
+ // Nothing ensures the weak-ref is not cleared between makeRef and printWeakReference on lines
+ // Main.java:78-79. An expected flaky result contains: "but was:<wimp: [null]".
+ "036-finalizer",
+ // Can lead to art-master crash: concurrent_copying.cc:2135] Check failed:
+ // to_ref != nullptr Fall-back non-moving space allocation failed
+ "080-oom-fragmentation",
+ // Seen crash: currently no more information
+ "144-static-field-sigquit",
+ // Opens a lot of file descriptors and depending on the state of the machine this
+ // can crash art or not. Skip the run on art.
+ "151-OpenFileLimit",
+ // Can cause a segfault in the art vm 7.0.0
+ // tools/linux/art-7.0.0/bin/art: line 105: 14395 Segmentation fault
+ "607-daemon-stress",
+ // Marked as flaky in the Art repository.
+ "149-suspend-all-stress"
+ );
+
+ // Tests that are never compiled or run.
+ private static List<String> skipAltogether = ImmutableList.of(
+ // This test contains an invalid type hierarchy, which we cannot currently handle.
+ "065-mismatched-implements",
+ // This test contains invalid dex code that reads uninitialized registers after an
+ // an instruction that would in any case throw (implicit via aget null 0).
+ "706-jit-skip-compilation",
+ // This test uses a user defined class loader to validate correct execution order
+ // between loadclass event and the execution of a static method.
+ // This does not work when performing inlining across classes.
+ "496-checker-inlining-class-loader"
+ );
+
+ // Tests that may produce different output on consecutive runs or when processed or not.
+ private static List<String> outputMayDiffer = ImmutableList.of(
+ // One some versions of art, this will print the address of the boot classloader, which
+ // may change between runs.
+ "506-verify-aput",
+ // Art does fail during validation of dex files if it encounters an ill-typed virtual-invoke
+ // but allows interface-invokes to pass. As we rewrite one kind into the other, we remove
+ // a verification error and thus change output.
+ "135-MirandaDispatch",
+ // This test constructs a conflict between static and instance field and expects to throw
+ // an IllegalClassChangeException. However, with R8 or D8, the two accesses are rebound to
+ // their actual target, this resolving the conflict.
+ "073-mismatched-field"
+ );
+
+ // Tests that make use of Java 8 features.
+ private static List<String> usesJava8 = ImmutableList.of(
+ "914-hello-obsolescence",
+ "915-obsolete-2",
+ "916-obsolete-jit",
+ "919-obsolete-fields",
+ "926-multi-obsolescence",
+ "936-search-onload",
+ "940-recursive-obsolete",
+ "941-recurive-obsolete-jit",
+ "942-private-recursive",
+ "943-private-recursive-jit",
+ "946-obsolete-throw",
+ "951-threaded-obsolete",
+ "952-invoke-custom"
+ );
+
+ // Tests that make use of agents/native code. Our test setup does handle flags/linking of these.
+ private static List<String> usesNativeAgentCode = ImmutableList.of(
+ "497-inlining-and-class-loader",
+ "626-const-class-linking",
+ "642-fp-callees",
+ "909-attach-agent",
+ "917-fields-transformation",
+ "918-fields",
+ "920-objects",
+ "921-hello-failure",
+ "922-properties",
+ "923-monitors",
+ "924-threads",
+ "925-threadgroups",
+ "927-timers",
+ "928-jni-table",
+ "929-search",
+ "930-hello-retransform",
+ "931-agent-thread",
+ "932-transform-saves",
+ "933-misc-events",
+ "934-load-transform",
+ "935-non-retransformable",
+ "936-search-onload",
+ "937-hello-retransform-package",
+ "938-load-transform-bcp",
+ "939-hello-transformation-bcp",
+ "944-transform-classloaders",
+ "945-obsolete-native",
+ "947-reflect-method",
+ "948-change-annotations",
+ "949-in-memory-transform",
+ "950-redefine-intrinsic",
+ "980-redefine-object"
+ );
+
+ // Tests with custom run.
+ private static List<String> customRun = ImmutableList.of(
+ "000-nop",
+ "018-stack-overflow",
+ "055-enum-performance",
+ "071-dexfile-map-clean",
+ "091-override-package-private-method",
+ "103-string-append",
+ "115-native-bridge",
+ "116-nodex2oat",
+ "117-nopatchoat",
+ "118-noimage-dex2oat",
+ "119-noimage-patchoat",
+ "126-miranda-multidex",
+ "127-checker-secondarydex",
+ "131-structural-change",
+ "133-static-invoke-super",
+ "134-nodex2oat-nofallback",
+ "137-cfi",
+ "138-duplicate-classes-check2",
+ "146-bad-interface",
+ "147-stripped-dex-fallback",
+ "304-method-tracing",
+ "529-checker-unresolved",
+ "555-checker-regression-x86const",
+ "569-checker-pattern-replacement",
+ "570-checker-osr",
+ "574-irreducible-and-constant-area",
+ "577-profile-foreign-dex",
+ "595-profile-saving",
+ "597-deopt-new-string",
+ "608-checker-unresolved-lse",
+ "613-inlining-dex-cache",
+ "636-wrong-static-access",
+ "900-hello-plugin",
+ "901-hello-ti-agent",
+ "902-hello-transformation",
+ "910-methods",
+ "911-get-stack-trace",
+ "912-classes",
+ "913-heaps"
+ );
+
+ // Tests with C++ code (JNI)
+ private static List<String> useJNI = ImmutableList.of(
+ "004-JniTest",
+ "004-NativeAllocations",
+ "004-ReferenceMap",
+ "004-SignalTest",
+ "004-StackWalk",
+ "004-ThreadStress",
+ "004-UnsafeTest",
+ "044-proxy",
+ "051-thread",
+ "115-native-bridge",
+ "117-nopatchoat",
+ "136-daemon-jni-shutdown",
+ "137-cfi",
+ "139-register-natives",
+ "141-class-unload",
+ "148-multithread-gc-annotations",
+ "149-suspend-all-stress",
+ "154-gc-loop",
+ "155-java-set-resolved-type",
+ "157-void-class",
+ "158-app-image-class-table",
+ "454-get-vreg",
+ "457-regs",
+ "461-get-reference-vreg",
+ "466-get-live-vreg",
+ "497-inlining-and-class-loader",
+ "543-env-long-ref",
+ "566-polymorphic-inlining",
+ "570-checker-osr",
+ "595-profile-saving",
+ "596-app-images",
+ "597-deopt-new-string",
+ "616-cha",
+ "616-cha-abstract",
+ "616-cha-regression-proxy-method",
+ "616-cha-native",
+ "626-const-class-linking",
+ "626-set-resolved-string",
+ "629-vdex-speed",
+ "900-hello-plugin",
+ "901-hello-ti-agent",
+ "1337-gc-coverage"
+ );
+
+ private static List<String> expectedToFailRunWithArtNonDefault = ImmutableList.of(
+ // Fails due to missing symbol, jni tests, fails on non-R8/D8 run.
+ "004-JniTest",
+ "004-SignalTest",
+ "004-ThreadStress",
+ "004-UnsafeTest",
+ "044-proxy",
+ "051-thread",
+ "136-daemon-jni-shutdown",
+ "139-register-natives",
+ "148-multithread-gc-annotations",
+ "149-suspend-all-stress",
+ "154-gc-loop",
+ "155-java-set-resolved-type",
+ "156-register-dex-file-multi-loader",
+ "157-void-class",
+ "158-app-image-class-table",
+ "466-get-live-vreg",
+ "497-inlining-and-class-loader",
+ "566-polymorphic-inlining",
+ "596-app-images",
+ "616-cha",
+ "616-cha-abstract",
+ "616-cha-regression-proxy-method",
+ "616-cha-native",
+ "626-set-resolved-string",
+ "629-vdex-speed",
+ "1337-gc-coverage",
+
+ // Addition of checks for super-class-initialization cause this to abort on non-ToT art.
+ "008-exceptions",
+
+ // Fails due to non-matching Exception messages.
+ "201-built-in-except-detail-messages",
+
+ // Fails on non-R8/D8 run
+ "031-class-attributes",
+ "617-clinit-oome"
+ );
+
+ private static Map<DexVm, List<String>> expectedToFailRunWithArtVersion = ImmutableMap.of(
+ DexVm.ART_7_0_0, ImmutableList.of(
+ // Generally fails on non-R8/D8 running.
+ "412-new-array",
+ "610-arraycopy",
+ "625-checker-licm-regressions",
+ // Crashes the VM, cause is unclear.
+ "080-oom-throw"
+ ),
+ DexVm.ART_6_0_1, ImmutableList.of(
+ // Generally fails on non-R8/D8 running.
+ "004-checker-UnsafeTest18",
+ "005-annotations",
+ "008-exceptions",
+ "061-out-of-memory",
+ "082-inline-execute",
+ "099-vmdebug",
+ "412-new-array",
+ "530-checker-lse2",
+ "534-checker-bce-deoptimization",
+ "550-new-instance-clinit",
+ "580-checker-round",
+ "594-invoke-super",
+ "625-checker-licm-regressions",
+ "626-const-class-linking",
+ // Crashes the VM, cause is unclear.
+ "080-oom-throw"
+ ),
+ DexVm.ART_5_1_1, ImmutableList.of(
+ // Generally fails on non R8/D8 running.
+ "004-checker-UnsafeTest18",
+ "004-NativeAllocations",
+ "005-annotations",
+ "008-exceptions",
+ "082-inline-execute",
+ "099-vmdebug",
+ "143-string-value",
+ "530-checker-lse2",
+ "536-checker-intrinsic-optimization",
+ "552-invoke-non-existent-super",
+ "580-checker-round",
+ "580-checker-string-fact-intrinsics",
+ "594-invoke-super",
+ "605-new-string-from-bytes",
+ "626-const-class-linking"
+ ),
+ DexVm.ART_4_4_4, ImmutableList.of()
+
+ );
+
+ // Tests where the R8/D8 output runs in Art but the original does not.
+ private static Multimap<String, TestCondition> failingRunWithArtOriginalOnly =
+ new ImmutableListMultimap.Builder<String, TestCondition>().build();
+
+ // Tests where the output of R8 fails when run with Art.
+ private static final Multimap<String, TestCondition> failingRunWithArt =
+ new ImmutableListMultimap.Builder<String, TestCondition>()
+ // This test relies on specific field access patterns, which we rewrite.
+ .put("064-field-access", TestCondition.match(TestCondition.R8_COMPILER))
+ // The growth limit test fails after processing by R8 because R8 will eliminate an
+ // "unneeded" const store. The following reflective call to the VM's GC will then see the
+ // large array as still live and the subsequent allocations will fail to reach the desired
+ // size before an out-of-memory error occurs. See:
+ // tests/art/{dx,jack}/104-growth-limit/src/Main.java:40
+ .put("104-growth-limit", TestCondition.match(TestCondition.R8_COMPILER))
+ .put(
+ "461-get-reference-vreg",
+ TestCondition.match(
+ TestCondition.D8_COMPILER,
+ TestCondition.runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // Unsatisfiable link error:
+ // libarttest.so: undefined symbol: _ZN3art6Thread18RunEmptyCheckpointEv
+ .put(
+ "543-env-long-ref",
+ TestCondition.match(
+ TestCondition.D8_COMPILER,
+ TestCondition.runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ // Regression test for an issue that is not fixed on version 5.1.1. Throws an Exception
+ // instance instead of the expected NullPointerException. This bug is only tickled when
+ // running the R8 generated code when starting from jar or from dex code generated with
+ // dx. However, the code that R8 generates is valid and there is nothing we can do for
+ // this one.
+ .put(
+ "551-implicit-null-checks",
+ TestCondition.match(
+ TestCondition.tools(DexTool.NONE, DexTool.DX),
+ TestCondition.R8_COMPILER,
+ TestCondition.runtimes(DexVm.ART_5_1_1)))
+ // Contains a method (B.<init>) which pass too few arguments to invoke. Also, contains an
+ // iput on a static field.
+ .put(
+ "600-verifier-fails",
+ TestCondition.match(
+ TestCondition.D8_COMPILER,
+ TestCondition.runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ .build();
+
+ // Tests where the output of R8/D8 runs in Art but produces different output than the expected.txt
+ // checked into the Art repo.
+ private static final Multimap<String, TestCondition> failingRunWithArtOutput =
+ new ImmutableListMultimap.Builder<String, TestCondition>()
+ // This one is expected to have different output. It counts instances, but the list that
+ // keeps the instances alive is dead and could be garbage collected. The compiler reuses
+ // the register for the list and therefore there are no live instances.
+ .put("099-vmdebug", TestCondition.any())
+ // This test relies on output on stderr, which we currently do not collect.
+ .put("143-string-value", TestCondition.any())
+ // This one is expected to have different output. It counts instances, but the list that
+ // keeps the instances alive is dead and could be garbage collected. The compiler reuses
+ // the register for the list and therefore there are no live instances.
+ .put("099-vmdebug", TestCondition.any())
+ // This test relies on output on stderr, which we currently do not collect.
+ .put("143-string-value", TestCondition.any())
+ .put(
+ "800-smali",
+ TestCondition.match(
+ TestCondition.D8_COMPILER,
+ TestCondition.runtimes(DexVm.ART_5_1_1, DexVm.ART_6_0_1)))
+ // Triggers regression test in 6.0.1 when using R8/D8 in debug mode.
+ .put(
+ "474-fp-sub-neg",
+ TestCondition.match(
+ TestCondition.tools(DexTool.NONE, DexTool.JACK),
+ TestCondition.D8_COMPILER,
+ TestCondition.runtimes(DexVm.ART_6_0_1)))
+ .build();
+
+ private static final TestCondition beforeAndroidN =
+ TestCondition
+ .match(TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1));
+ private static final TestCondition beforeAndroidO =
+ TestCondition.match(
+ TestCondition.runtimes(
+ DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1, DexVm.ART_7_0_0));
+
+ // TODO(ager): Could we test that these fail in the way that we expect?
+ private static final Multimap<String, TestCondition> expectedToFailRunWithArt =
+ new ImmutableListMultimap.Builder<String, TestCondition>()
+ // Contains bad finalizer that times out.
+ .put("030-bad-finalizer", TestCondition.any())
+ // Contains a direct method call with a null receiver.
+ .put("034-call-null", TestCondition.any())
+ // Testing uncaught exceptions.
+ .put("054-uncaught", TestCondition.any())
+ // Contains a null pointer exception.
+ .put("038-inner-null", TestCondition.any())
+ // Null pointer exception.
+ .put("071-dexfile", TestCondition.any())
+ // Array index out of bounds exception.
+ .put("088-monitor-verification", TestCondition.any())
+ // Attempts to run hprof.
+ .put("130-hprof", TestCondition.any())
+ // Null pointer exception. Test doesn't exist for dx.
+ .put("138-duplicate-classes-check", TestCondition.any())
+ // Array index out of bounds exception.
+ .put("150-loadlibrary", TestCondition.any())
+ // Uses dex file version 37 and therefore only runs on Android N and above.
+ .put("370-dex-v37",
+ TestCondition.match(TestCondition.tools(DexTool.JACK, DexTool.DX),
+ TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1)))
+ // Array index out of bounds exception.
+ .put("449-checker-bce", TestCondition.any())
+ // Fails: get_vreg_jni.cc:46] Check failed: value == 42u (value=314630384, 42u=42)
+ // The R8/D8 code does not produce values in the same registers as the tests expects in
+ // Main.testPairVReg where Main.doNativeCall is called (v1 vs v0).
+ .put("454-get-vreg", TestCondition.any())
+ // Fails: regs_jni.cc:42] Check failed: GetVReg(m, 0, kIntVReg, &value)
+ // The R8/D8 code does not put values in the same registers as the tests expects.
+ .put("457-regs", TestCondition.any())
+ // Class not found.
+ .put("529-checker-unresolved", TestCondition.any())
+ // Fails: env_long_ref.cc:44] Check failed: GetVReg(m, 1, kReferenceVReg, &value)
+ // The R8/D8 code does not produce values in the same registers as the tests expects in
+ // the stack frame for TestCase.testCase checked by the native Main.lookForMyRegisters
+ // (v1 vs v0).
+ .put("543-env-long-ref", TestCondition.match(TestCondition.R8_COMPILER))
+ // Array index out of bounds exception.
+ .put("555-UnsafeGetLong-regression", TestCondition.any())
+ // Array index out of bounds exception.
+ .put("563-checker-fakestring", TestCondition.any())
+ // Array index out of bounds exception.
+ .put("575-checker-string-init-alias", TestCondition.any())
+ // Runtime exception.
+ .put("577-profile-foreign-dex", TestCondition.any())
+ // Array index out of bounds exception.
+ .put("602-deoptimizeable", TestCondition.any())
+ // Array index out of bounds exception.
+ .put("604-hot-static-interface", TestCondition.any())
+ // Array index out of bounds exception.
+ .put("612-jit-dex-cache", TestCondition.any())
+ // Fails: get_reference_vreg_jni.cc:43] Check failed: GetVReg(m, 1, kReferenceVReg,
+ // &value)
+ // The R8 code does not put values in the same registers as the tests expects in
+ // Main.testThisWithInstanceCall checked by the native Main.doNativeCallRef (v0 vs. v1 and
+ // only 1 register instead fof 2).
+ .put("461-get-reference-vreg", TestCondition.match(TestCondition.R8_COMPILER))
+ // This test uses register r1 in method that is declared to only use 1 register (r0). This
+ // is in dex code which D8 does not convert. Therefore the error is a verification error
+ // at runtime and that is expected.
+ .put("142-classloader2", TestCondition.match(TestCondition.D8_COMPILER))
+ // Invoke-polymorphic is supported by D8 and R8, but it can only run on our newest version
+ // of art.
+ .put("953-invoke-polymorphic-compiler", beforeAndroidO)
+ .put("957-methodhandle-transforms", beforeAndroidO)
+ .put("958-methodhandle-stackframe", beforeAndroidO)
+ .put("959-invoke-polymorphic-accessors", beforeAndroidO)
+ .put("044-proxy", beforeAndroidN) // --min-sdk = 24
+ .put("048-reflect-v8", beforeAndroidN) // --min-sdk = 24
+ .put("962-iface-static", beforeAndroidN) // --min-sdk = 24
+ .put("964-default-iface-init-gen", beforeAndroidN) // --min-sdk = 24
+ .put("968-default-partial-compile-gen", beforeAndroidN) // --min-sdk = 24
+ .put("970-iface-super-resolution-gen", beforeAndroidN) // --min-sdk = 24
+ .put("971-iface-super", beforeAndroidN) // --min-sdk = 24
+ .put("972-default-imt-collision", beforeAndroidN) // --min-sdk = 24
+ .put("973-default-multidex", beforeAndroidN) // --min-sdk = 24
+ .put("974-verify-interface-super", beforeAndroidN) // --min-sdk = 24
+ .put("975-iface-private", beforeAndroidN) // --min-sdk = 24
+ // Uses dex file version 37 and therefore only runs on Android N and above.
+ .put("972-iface-super-multidex",
+ TestCondition.match(TestCondition.tools(DexTool.JACK, DexTool.DX),
+ TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1)))
+ // Uses dex file version 37 and therefore only runs on Android N and above.
+ .put("978-virtual-interface",
+ TestCondition.match(TestCondition.tools(DexTool.JACK, DexTool.DX),
+ TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1)))
+ .build();
+
+ // Tests where code generation fails.
+ private static final Multimap<String, TestCondition> failingWithCompiler =
+ new ImmutableListMultimap.Builder<String, TestCondition>()
+ // Contains two methods with the same name and signature but different code.
+ .put("097-duplicate-method", TestCondition.any())
+ // Contains a method (B.<init>) which pass too few arguments to invoke. Also, contains an
+ // iput on a static field.
+ .put("600-verifier-fails", TestCondition.match(TestCondition.R8_COMPILER))
+ // Contains a method that falls off the end without a return.
+ .put("606-erroneous-class", TestCondition
+ .match(TestCondition.tools(DexTool.DX, DexTool.JACK), TestCondition.R8_COMPILER))
+ .put("064-field-access", TestCondition
+ .match(TestCondition.tools(DexTool.NONE), TestCondition.D8_COMPILER))
+ .build();
+
+ // Tests that are invalid dex files and on which R8/D8 fails and that is OK.
+ private static final Multimap<String, TestCondition> expectedToFailWithCompiler =
+ new ImmutableListMultimap.Builder<String, TestCondition>()
+ // When starting from the Jar frontend we see the A$B class both from the Java source
+ // code and from the smali dex code. We reject that because there are then two definitions
+ // of the same class in the application. When running from the final dex files there is
+ // only on A$B class because of a custom build script that merges them.
+ .put("121-modifiers", TestCondition.match(TestCondition.tools(DexTool.NONE)))
+ // This test uses register r1 in method that is declared to only use 1 register (r0).
+ .put("142-classloader2", TestCondition.match(TestCondition.R8_COMPILER))
+ // This test uses an uninitialized register.
+ .put("471-uninitialized-locals", TestCondition.match(TestCondition.R8_COMPILER))
+ // This test is starting from invalid dex code. It splits up a double value and uses
+ // the first register of a double with the second register of another double.
+ .put("800-smali", TestCondition.match(TestCondition.R8_COMPILER))
+ // Contains a loop in the class hierarchy.
+ .put("804-class-extends-itself", TestCondition.any())
+ // It is not possible to compute target of method call due to ambiguous methods, thus fail
+ // to generate one dex from several dex inputs that represent an invalid program.
+ .put("004-JniTest", TestCondition.match(TestCondition.R8_COMPILER))
+ .put("960-default-smali", TestCondition.match(TestCondition.R8_COMPILER))
+ .put("966-default-conflict", TestCondition.match(TestCondition.R8_COMPILER))
+ .put("972-iface-super-multidex", TestCondition.match(TestCondition.R8_COMPILER))
+ .build();
+
+ // Tests that does not have valid input for us to be compatible with jack/dx running.
+ private static List<String> noInputJar = ImmutableList.of(
+ "064-field-access", // Missing classes2 dir (has src2)
+ "097-duplicate-method", // No input class files.
+ "606-erroneous-class", // Based on multiple smali files.
+ "630-safecast-array", // No input class files.
+ "801-VoidCheckCast", // No input class files.
+ "804-class-extends-itself", // No input class files.
+ "972-iface-super-multidex", // Based on multiple smali files
+ "973-default-multidex", // Based on multiple smali files.
+ "974-verify-interface-super", // No input class files.
+ "975-iface-private", // No input class files.
+ "976-conflict-no-methods", // No input class files
+ "978-virtual-interface" // No input class files
+ );
+
+ // Some JCTF test cases require test classes from other tests. These are listed here.
+ private static final Map<String, List<String>> jctfTestsExternalClassFiles =
+ new ImmutableMap.Builder<String, List<String>>()
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A13",
+ new ImmutableList.Builder<String>()
+ .add("lang/Thread/stop/Thread_stop_A02.class")
+ .add("lang/Thread/stopLjava_lang_Throwable/Thread_stop_A02.class")
+ .build())
+ .put("lang.RuntimePermission.Class.RuntimePermission_class_A02",
+ new ImmutableList.Builder<String>()
+ .add("lang/Class/getClassLoader/Class_getClassLoader_A03.class")
+ .add("lang/ClassLoader/getParent/ClassLoader_getParent_A02.class")
+ .add("lang/Thread/getContextClassLoader/Thread_getContextClassLoader_A02.class")
+ .add("lang/Runtime/exitI/Runtime_exit_A02.class")
+ .build())
+ .put("lang.Runtime.exitI.Runtime_exit_A03",
+ new ImmutableList.Builder<String>()
+ .add("lang/Runtime/exitI/Runtime_exit_A02.class")
+ .build())
+ .build();
+
+ private static List<String> failuresToTriage = ImmutableList.of();
+
+ private static class TestSpecification {
+
+ // Name of the Art test
+ private final String name;
+ // Directory of the test files (containing prebuild dex/jar files and expected output).
+ private final File directory;
+ // Compiler that these expectations are for dx or Jack, or none if running on class files.
+ private final DexTool dexTool;
+ // Native library to use for running this test - if any.
+ private final String nativeLibrary;
+ // Skip running this test with Art - most likely due to timeout.
+ private final boolean skipArt;
+ // Skip running this test altogether. For example, there might be no input in this configuration
+ // (e.g. no class files).
+ private final boolean skipTest;
+ // Fails compilation - most likely throws an exception.
+ private final boolean failsWithX8;
+ // Expected to fail compilation with a CompilationError.
+ private final boolean expectedToFailWithX8;
+ // Fails running the output in Art with an assertion error. Typically due to verification
+ // errors.
+ private final boolean failsWithArt;
+ // Runs in art but fails the run because it produces different results.
+ private final boolean failsWithArtOutput;
+ // Original fails in art but the R8/D8 version can run in art.
+ private final boolean failsWithArtOriginalOnly;
+ // Test might produce different outputs.
+ private final boolean outputMayDiffer;
+
+ TestSpecification(String name, DexTool dexTool,
+ File directory, boolean skipArt, boolean skipTest, boolean failsWithX8,
+ boolean failsWithArt, boolean failsWithArtOutput, boolean failsWithArtOriginalOnly,
+ String nativeLibrary, boolean expectedToFailWithX8, boolean outputMayDiffer) {
+ this.name = name;
+ this.dexTool = dexTool;
+ this.nativeLibrary = nativeLibrary;
+ this.directory = directory;
+ this.skipArt = skipArt;
+ this.skipTest = skipTest;
+ this.failsWithX8 = failsWithX8;
+ this.failsWithArt = failsWithArt;
+ this.failsWithArtOutput = failsWithArtOutput;
+ this.failsWithArtOriginalOnly = failsWithArtOriginalOnly;
+ this.expectedToFailWithX8 = expectedToFailWithX8;
+ this.outputMayDiffer = outputMayDiffer;
+ }
+
+ TestSpecification(String name, DexTool dexTool, File directory, boolean skipArt,
+ boolean failsWithArt) {
+ this(name, dexTool, directory, skipArt,
+ false, false, failsWithArt, false, false, null, false, false);
+ }
+
+ public File resolveFile(String name) {
+ return directory.toPath().resolve(name).toFile();
+ }
+
+ public String toString() {
+ return name + " (" + dexTool + ")";
+ }
+ }
+
+ private static class SpecificationKey {
+
+ private final String name;
+ private final DexTool toolchain;
+
+ private SpecificationKey(String name, DexTool toolchain) {
+ assert name != null;
+ this.name = name;
+ this.toolchain = toolchain;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof SpecificationKey)) {
+ return false;
+ }
+
+ SpecificationKey that = (SpecificationKey) o;
+ return name.equals(that.name) && toolchain == that.toolchain;
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * name.hashCode() + toolchain.hashCode();
+ }
+ }
+
+ private static Map<SpecificationKey, TestSpecification> AVAILABLE_TESTS_MAP_R8 =
+ data(CompilerUnderTest.R8);
+
+ private static Map<SpecificationKey, TestSpecification> AVAILABLE_TESTS_MAP_D8 =
+ data(CompilerUnderTest.D8);
+
+
+ private static Set<String> collectTestsMatchingConditions(DexTool dexTool,
+ CompilerUnderTest compilerUnderTest, DexVm dexVm,
+ Multimap<String, TestCondition> testConditionsMap) {
+ Set<String> set = Sets.newHashSet();
+ for (Map.Entry<String, TestCondition> kv : testConditionsMap.entries()) {
+ if (kv.getValue().test(dexTool, compilerUnderTest, dexVm)) {
+ set.add(kv.getKey());
+ }
+ }
+ return set;
+ }
+
+
+ private static Map<SpecificationKey, TestSpecification> data(
+ CompilerUnderTest compilerUnderTest) {
+ File artTestDir = new File(ART_TESTS_DIR);
+ if (!artTestDir.exists()) {
+ // Don't run any tests if the directory does not exist.
+ return Collections.emptyMap();
+ }
+ Map<SpecificationKey, TestSpecification> data = new HashMap<>();
+
+ // Collect tests where running Art is skipped (we still run R8/D8 on these).
+ Set<String> skipArt = Sets.newHashSet(flakyRunWithArt);
+ skipArt.addAll(customRun);
+
+ Set<String> skipTest = Sets.newHashSet(skipAltogether);
+ skipTest.addAll(usesJava8);
+ skipTest.addAll(usesNativeAgentCode);
+ skipTest.addAll(failuresToTriage);
+
+ // Collect the tests requiring the native library.
+ Set<String> useNativeLibrary = Sets.newHashSet(useJNI);
+
+ DexVm dexVm = ToolHelper.getDexVm();
+ for (DexTool dexTool : DexTool.values()) {
+ // Collect the tests failing code generation.
+ Set<String> failsWithCompiler = collectTestsMatchingConditions(dexTool, compilerUnderTest,
+ dexVm, failingWithCompiler);
+
+ // Collect tests that has no input:
+ if (dexTool == DexTool.NONE) {
+ skipTest.addAll(noInputJar);
+ }
+
+ // Collect the test that we should skip in this configuration.
+ skipArt.addAll(collectTestsMatchingConditions(dexTool, compilerUnderTest,
+ dexVm, timeoutOrSkipRunWithArt));
+
+ // Collect the tests failing to run in Art (we still run R8/D8 on these).
+ Set<String> failsWithArt = collectTestsMatchingConditions(dexTool, compilerUnderTest, dexVm,
+ failingRunWithArt);
+ {
+ Set<String> tmpSet = collectTestsMatchingConditions(dexTool, compilerUnderTest, dexVm,
+ expectedToFailRunWithArt);
+ failsWithArt.addAll(tmpSet);
+ }
+
+ if (!ToolHelper.isDefaultDexVm()) {
+ // Generally failing when not TOT art.
+ failsWithArt.addAll(expectedToFailRunWithArtNonDefault);
+ // Version specific failures
+ failsWithArt.addAll(expectedToFailRunWithArtVersion.get(ToolHelper.getDexVm()));
+ }
+
+ // Collect the tests failing with output differences in Art.
+ Set<String> failsRunWithArtOutput = collectTestsMatchingConditions(dexTool, compilerUnderTest,
+ dexVm, failingRunWithArtOutput);
+ Set<String> expectedToFailWithCompilerSet = collectTestsMatchingConditions(dexTool,
+ compilerUnderTest, dexVm, expectedToFailWithCompiler);
+
+ // Collect the tests where the original works in Art and the R8/D8 generated output does not.
+ Set<String> failsRunWithArtOriginalOnly = collectTestsMatchingConditions(dexTool,
+ compilerUnderTest, dexVm, failingRunWithArtOriginalOnly);
+
+ File compilerTestDir = artTestDir.toPath().resolve(dexToolDirectory(dexTool)).toFile();
+ File[] testDirs = compilerTestDir.listFiles();
+ assert testDirs != null;
+ for (File testDir : testDirs) {
+ String name = testDir.getName();
+ // All the native code for all Art tests is currently linked into the
+ // libarttest.so file.
+ data.put(
+ new SpecificationKey(name, dexTool),
+ new TestSpecification(name, dexTool, testDir, skipArt.contains(name),
+ skipTest.contains(name),
+ failsWithCompiler.contains(name),
+ failsWithArt.contains(name),
+ failsRunWithArtOutput.contains(name),
+ failsRunWithArtOriginalOnly.contains(name),
+ useNativeLibrary.contains(name) ? "arttest" : null,
+ expectedToFailWithCompilerSet.contains(name),
+ outputMayDiffer.contains(name)));
+ }
+ }
+ return data;
+ }
+
+ private static String dexToolDirectory(DexTool tool) {
+ // DexTool.NONE uses class files in the dx directory.
+ return tool == DexTool.JACK ? "jack" : "dx";
+ }
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ public R8RunArtTestsTest(String name, DexTool toolchain) {
+ this.name = name;
+ this.toolchain = toolchain;
+ }
+
+ private ArtCommandBuilder buildArtCommand(
+ File dexFile, TestSpecification specification, DexVm artVersion) {
+ ArtCommandBuilder builder = new ArtCommandBuilder(artVersion);
+ builder.appendClasspath(dexFile.toString());
+ // All Art tests have the main class Main.
+ builder.setMainClass("Main");
+ if (specification.nativeLibrary != null) {
+ // All the native libraries for all Art tests is in the same directory.
+ File artTestNativeLibraryDir = new File(ART_TESTS_NATIVE_LIBRARY_DIR);
+ builder.appendArtSystemProperty(
+ "java.library.path",
+ artTestNativeLibraryDir.getAbsolutePath());
+ builder.appendProgramArgument(specification.nativeLibrary);
+ }
+ return builder;
+ }
+
+ protected void runArtTest() throws Throwable {
+ // Use the default dex VM specified.
+ runArtTest(ToolHelper.getDexVm(), CompilerUnderTest.R8);
+ }
+
+ protected void runArtTest(CompilerUnderTest compilerUnderTest) throws Throwable {
+ // Use the default dex VM specified.
+ runArtTest(ToolHelper.getDexVm(), compilerUnderTest);
+ }
+
+ private void executeCompilerUnderTest(
+ CompilerUnderTest compilerUnderTest, Collection<String> fileNames, String resultPath)
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+ executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, null);
+ }
+
+ private void executeCompilerUnderTest(
+ CompilerUnderTest compilerUnderTest,
+ Collection<String> fileNames,
+ String resultPath,
+ String keepRulesFile)
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+ switch (compilerUnderTest) {
+ case D8: {
+ assert keepRulesFile == null : "Keep-rules file specified for D8.";
+ D8Command.Builder builder =
+ D8Command.builder()
+ .setMode(CompilationMode.DEBUG)
+ .addProgramFiles(ListUtils.map(fileNames, Paths::get));
+ Integer minSdkVersion = needMinSdkVersion.get(name);
+ if (minSdkVersion != null) {
+ builder.setMinApiLevel(minSdkVersion);
+ }
+ D8Output output = D8.run(builder.build());
+ output.write(Paths.get(resultPath));
+ break;
+ }
+ case R8: {
+ R8Command.Builder builder =
+ R8Command.builder()
+ .setMode(CompilationMode.RELEASE)
+ .setOutputPath(Paths.get(resultPath))
+ .addProgramFiles(ListUtils.map(fileNames, Paths::get))
+ .setIgnoreMissingClasses(true);
+ Integer minSdkVersion = needMinSdkVersion.get(name);
+ if (minSdkVersion != null) {
+ builder.setMinApiLevel(minSdkVersion);
+ }
+ if (keepRulesFile != null) {
+ builder.addProguardConfigurationFiles(Paths.get(keepRulesFile));
+ }
+ // Add internal flags for testing purposes.
+ ToolHelper.runR8(builder.build(), options -> {
+ if (enableInterfaceMethodDesugaring.contains(name)) {
+ options.interfaceMethodDesugaring = OffOrAuto.Auto;
+ }
+ });
+ break;
+ }
+ default:
+ assert false : compilerUnderTest;
+ }
+ }
+
+ private static R8Command.Builder setDefaultArgs(R8Command.Builder builder) {
+ return builder.setMinification(false);
+ }
+
+ private static final boolean isAuxClassFile(String fileName, String auxClassFileBase) {
+ return fileName.endsWith(".class")
+ && (fileName.startsWith(auxClassFileBase + "$")
+ || fileName.startsWith(auxClassFileBase + "_"));
+ }
+
+
+ private ArrayList<File> getJctfTestAuxClassFiles(File classFile) {
+ // Collect additional files from the same directory with file names like
+ // <dir>/<filename_wo_ext>$*.class and <dir>/<filename_wo_ext>_*.class
+ String classFileString = classFile.toString();
+ assert classFileString.endsWith(".class");
+
+ String auxClassFileBase =
+ new File(
+ classFileString.substring(0, classFileString.length() - ".class".length()))
+ .getName();
+
+ ArrayList<File> auxClassFiles = new ArrayList<>();
+
+ File[] files = classFile.getParentFile()
+ .listFiles(
+ (File file) -> isAuxClassFile(file.getName(), auxClassFileBase));
+ if (files != null) {
+ auxClassFiles.addAll(Arrays.asList(files));
+ }
+
+ if (auxClassFileBase.matches(".*[A-Z]\\d\\d")) {
+ // Also collect all the files in this directory that doesn't match this pattern
+ // They will be helper classes defined in one of the test class files but we don't know in
+ // which one, so we just add them to all tests.
+ final int SUFFIX_LENGTH_TO_STRIP = 3; // one letter (usually 'A' and two digits)
+ String testClassFilePattern =
+ auxClassFileBase.substring(0, auxClassFileBase.length() - SUFFIX_LENGTH_TO_STRIP)
+ + "[A-Z]\\d\\d.*\\.class";
+ files = classFile.getParentFile()
+ .listFiles(
+ (File file) -> file.getName().matches(".*\\.class") && !file.getName()
+ .matches(testClassFilePattern));
+ if (files != null) {
+ auxClassFiles.addAll(Arrays.asList(files));
+ }
+ }
+
+ return auxClassFiles;
+ }
+
+ protected void runJctfTest(CompilerUnderTest compilerUnderTest, String classFilePath,
+ String fullClassName)
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+
+ DexVm dexVm = ToolHelper.getDexVm();
+
+ File resultDir = temp.getRoot();
+
+ JctfTestSpecifications.Outcome expectedOutcome = JctfTestSpecifications
+ .getExpectedOutcome(name, compilerUnderTest, dexVm);
+ TestSpecification specification = new TestSpecification(name, DexTool.NONE, resultDir,
+ expectedOutcome == JctfTestSpecifications.Outcome.TIMEOUTS_WITH_ART
+ || expectedOutcome == JctfTestSpecifications.Outcome.FLAKY_WITH_ART,
+ expectedOutcome == JctfTestSpecifications.Outcome.FAILS_WITH_ART);
+
+ if (specification.skipTest) {
+ return;
+ }
+
+ File classFile = new File(JCTF_TESTS_PREFIX + "/" + classFilePath);
+ if (!classFile.exists()) {
+ throw new FileNotFoundException(
+ "Class file for Jctf test not found: \"" + classFile.toString() + "\".");
+ }
+
+ ArrayList<File> classFiles = new ArrayList<>();
+ classFiles.add(classFile);
+
+ // some tests need files from other tests
+ int langIndex = fullClassName.indexOf(".java.");
+ assert langIndex >= 0;
+ List<String> externalClassFiles = jctfTestsExternalClassFiles
+ .get(fullClassName.substring(langIndex + ".java.".length()));
+
+ if (externalClassFiles != null) {
+ for (String s : externalClassFiles) {
+ classFiles.add(new File(JCTF_TESTS_LIB_PREFIX + "/java/" + s));
+ }
+ }
+
+ ArrayList<File> allClassFiles = new ArrayList<>();
+
+ for (File f : classFiles) {
+ allClassFiles.add(f);
+ allClassFiles.addAll(getJctfTestAuxClassFiles(f));
+ }
+
+ File jctfCommonFile = new File(JCTF_COMMON_JAR);
+ if (!jctfCommonFile.exists()) {
+ throw new FileNotFoundException(
+ "Jar file of Jctf tests common code not found: \"" + jctfCommonFile.toString() + "\".");
+ }
+
+ File junitFile = new File(JUNIT_JAR);
+ if (!junitFile.exists()) {
+ throw new FileNotFoundException(
+ "Junit Jar not found: \"" + junitFile.toString() + "\".");
+ }
+
+ File hamcrestFile = new File(HAMCREST_JAR);
+ if (!hamcrestFile.exists()) {
+ throw new FileNotFoundException(
+ "Hamcrest Jar not found: \"" + hamcrestFile.toString() + "\".");
+ }
+
+ // allClassFiles may contain duplicated files, that's why the HashSet
+ Set<String> fileNames = new HashSet<>();
+
+ fileNames.addAll(Arrays.asList(
+ jctfCommonFile.getCanonicalPath(),
+ junitFile.getCanonicalPath(),
+ hamcrestFile.getCanonicalPath()
+ ));
+
+ for (File f : allClassFiles) {
+ fileNames.add(f.getCanonicalPath());
+ }
+ executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getCanonicalPath());
+
+ if (!ToolHelper.artSupported()) {
+ return;
+ }
+
+ File processedFile;
+
+ // Collect the generated dex files.
+ File[] outputFiles =
+ resultDir.listFiles((File file) -> file.getName().endsWith(".dex"));
+ if (outputFiles.length == 1) {
+ // Just run Art on classes.dex.
+ processedFile = outputFiles[0];
+ } else {
+ // Run Art on JAR file with multiple dex files.
+ processedFile
+ = temp.getRoot().toPath().resolve(specification.name + ".jar").toFile();
+ JarBuilder.buildJar(outputFiles, processedFile);
+ }
+
+ boolean compileOnly = System.getProperty("jctf_compile_only", "0").equals("1");
+ if (compileOnly || specification.skipArt) {
+ // verify dex code instead of running it
+ Path oatFile = temp.getRoot().toPath().resolve("all.oat");
+ ToolHelper.runDex2Oat(processedFile.toPath(), oatFile);
+ return;
+ }
+
+ ArtCommandBuilder builder = buildArtCommand(processedFile, specification, dexVm);
+ builder.appendArtOption("-Ximage:/system/non/existent/jdwp/image.art");
+ for (String s : ToolHelper.getArtBootLibs()) {
+ builder.appendBootClassPath(new File(s).getCanonicalPath());
+ }
+ builder.setMainClass(JUNIT_TEST_RUNNER);
+ builder.appendProgramArgument(fullClassName);
+
+ if (specification.failsWithArt) {
+ thrown.expect(AssertionError.class);
+ }
+
+ try {
+ ToolHelper.runArt(builder);
+ } catch (AssertionError e) {
+ addDexInformationToVerificationError(fileNames, processedFile,
+ specification.resolveFile("classes.dex"), e);
+ throw e;
+ }
+ if (specification.failsWithArt) {
+ System.err.println("Should have failed run with art");
+ return;
+ }
+ }
+
+ protected void runArtTest(DexVm version, CompilerUnderTest compilerUnderTest)
+ throws Throwable {
+ Map<SpecificationKey, TestSpecification> specificationMap = null;
+
+ if (compilerUnderTest == CompilerUnderTest.R8) {
+ specificationMap = AVAILABLE_TESTS_MAP_R8;
+ } else {
+ assert (compilerUnderTest == CompilerUnderTest.D8);
+ specificationMap = AVAILABLE_TESTS_MAP_D8;
+ }
+
+ TestSpecification specification = specificationMap.get(new SpecificationKey(name, toolchain));
+
+ if (specification == null) {
+ throw new RuntimeException("Test " + name + " has no specification for toolchain"
+ + toolchain + ".");
+ }
+
+ if (specification.skipTest) {
+ return;
+ }
+
+ File[] inputFiles;
+ if (toolchain == DexTool.NONE) {
+ File classes = new File(specification.directory, "classes");
+ inputFiles =
+ Files.fileTreeTraverser().breadthFirstTraversal(classes).filter(
+ (File f) -> !f.isDirectory()).toArray(File.class);
+ File smali = new File(specification.directory, "smali");
+ if (smali.exists()) {
+ File smaliDex = new File(smali, "out.dex");
+ assert smaliDex.exists();
+ inputFiles = ObjectArrays.concat(inputFiles, smaliDex);
+ }
+ File classes2 = new File(specification.directory, "classes2");
+ if (classes2.exists()) {
+ inputFiles = ObjectArrays.concat(inputFiles,
+ Files.fileTreeTraverser().breadthFirstTraversal(classes2).filter(
+ (File f) -> !f.isDirectory()).toArray(File.class), File.class);
+ }
+ } else {
+ inputFiles =
+ specification.directory.listFiles((File file) -> file.getName().endsWith(".dex"));
+ }
+ List<String> fileNames = new ArrayList<>();
+ for (File file : inputFiles) {
+ fileNames.add(file.getCanonicalPath());
+ }
+ File resultDir = temp.getRoot();
+
+ if (specification.expectedToFailWithX8) {
+ thrown.expect(CompilationError.class);
+ try {
+ executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getCanonicalPath());
+ } catch (CompilationException e) {
+ throw new CompilationError(e.getMessage(), e);
+ } catch (ExecutionException e) {
+ throw e.getCause();
+ }
+ System.err.println("Should have failed R8/D8 compilation with a CompilationError.");
+ return;
+ } else if (specification.failsWithX8) {
+ thrown.expect(Throwable.class);
+ executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getCanonicalPath());
+ System.err.println("Should have failed R8/D8 compilation with an exception.");
+ return;
+ } else {
+ executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getCanonicalPath());
+ }
+
+ if (!specification.skipArt && ToolHelper.artSupported()) {
+ File originalFile;
+ File processedFile;
+
+ // Collect the generated dex files.
+ File[] outputFiles =
+ resultDir.listFiles((File file) -> file.getName().endsWith(".dex"));
+ if (outputFiles.length == 1) {
+ // Just run Art on classes.dex.
+ processedFile = outputFiles[0];
+ } else {
+ // Run Art on JAR file with multiple dex files.
+ processedFile
+ = temp.getRoot().toPath().resolve(specification.name + ".jar").toFile();
+ JarBuilder.buildJar(outputFiles, processedFile);
+ }
+
+ File expectedFile = specification.resolveFile("expected.txt");
+ String expected = Files.toString(expectedFile, Charsets.UTF_8);
+ if (specification.failsWithArt) {
+ thrown.expect(AssertionError.class);
+ }
+
+ ArtCommandBuilder builder = buildArtCommand(processedFile, specification, version);
+ String output;
+ try {
+ output = ToolHelper.runArt(builder);
+ } catch (AssertionError e) {
+ addDexInformationToVerificationError(fileNames, processedFile,
+ specification.resolveFile("classes.dex"), e);
+ throw e;
+ }
+ if (specification.failsWithArt) {
+ System.err.println("Should have failed run with art");
+ return;
+ }
+
+ File checkCommand = specification.resolveFile("check");
+ if (checkCommand.exists()) {
+ // Run the Art test custom check command.
+ File actualFile = temp.newFile();
+ Files.asByteSink(actualFile).write(output.getBytes(Charsets.UTF_8));
+ ProcessBuilder processBuilder = new ProcessBuilder();
+ processBuilder.command(
+ specification.resolveFile("check").toString(), expectedFile.toString(),
+ actualFile.toString());
+ ProcessResult result = ToolHelper.runProcess(processBuilder);
+ if (result.exitCode != 0 && !specification.failsWithArtOutput) {
+ System.err.println("ERROR: check script failed. Building comparison of dex files");
+ failWithDexDiff(specification.resolveFile("classes.dex"), processedFile);
+ }
+ } else {
+ if (!expected.equals(output)) {
+ // The expected.txt in the Android repository might not match what our version of Art
+ // produces.
+ originalFile = specification.resolveFile(specification.name + ".jar");
+ if (specification.failsWithArtOriginalOnly) {
+ thrown.expect(AssertionError.class);
+ }
+ builder = buildArtCommand(originalFile, specification, version);
+ expected = ToolHelper.runArt(builder);
+ if (specification.failsWithArtOriginalOnly) {
+ System.err.println("Original file should have failed run with art");
+ return;
+ }
+ }
+ if (specification.failsWithArtOutput) {
+ thrown.expect(ComparisonFailure.class);
+ }
+ if (!specification.outputMayDiffer) {
+ assertEquals(expected, output);
+ }
+ }
+ }
+ }
+
+ private void failWithDexDiff(File originalFile, File processedFile)
+ throws IOException, ExecutionException {
+ DexInspector inspectOriginal =
+ new DexInspector(originalFile.toPath().toAbsolutePath());
+ DexInspector inspectProcessed =
+ new DexInspector(processedFile.toPath().toAbsolutePath());
+ StringBuilder builderOriginal = new StringBuilder();
+ StringBuilder builderProcessed = new StringBuilder();
+ inspectOriginal.forAllClasses((clazz) -> builderOriginal.append(clazz.dumpMethods()));
+ inspectProcessed.forAllClasses((clazz) -> builderProcessed.append(clazz.dumpMethods()));
+ assertEquals(builderOriginal.toString(), builderProcessed.toString());
+ fail();
+ }
+
+ private void addDexInformationToVerificationError(
+ Collection<String> inputFiles, File processedFile, File referenceFile,
+ AssertionError verificationError) {
+ List<ComparisonFailure> errors;
+ try {
+ // Parse all the verification errors.
+ DexInspector processed = new DexInspector(processedFile.toPath());
+ DexInspector original = DEX_COMPARE_WITH_DEX_REFERENCE_ON_FAILURE
+ ? new DexInspector(referenceFile.toPath())
+ : new DexInspector(inputFiles.stream().map(Paths::get).collect(Collectors.toList()));
+ List<ArtErrorInfo> errorInfo = ArtErrorParser.parse(verificationError.getMessage());
+ errors = ListUtils.map(errorInfo, (error) ->
+ new ComparisonFailure(
+ error.getMessage(),
+ "ORIGINAL\n" + error.dump(original, false) + "\nEND ORIGINAL",
+ "PROCESSED\n" + error.dump(processed, true) + "\nEND PROCESSED"));
+ } catch (Throwable e) {
+ System.err.println("Failed to add extra dex information to the verification error:");
+ e.printStackTrace();
+ throw verificationError;
+ }
+
+ // If we failed to annotate anything, rethrow the original exception.
+ if (errors.isEmpty()) {
+ throw verificationError;
+ }
+
+ // Otherwise, we print each error and throw the last one, since Intellij only supports nice
+ // comparison-diff if thrown and we can only throw one :-(
+ System.err.println(verificationError.getMessage());
+ for (ComparisonFailure error : errors.subList(0, errors.size() - 1)) {
+ System.err.println(error.toString());
+ }
+ throw errors.get(errors.size() - 1);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java
new file mode 100644
index 0000000..1335970
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java
@@ -0,0 +1,153 @@
+// 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;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OffOrAuto;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public class R8RunExamplesAndroidNTest {
+
+ private static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR;
+
+ private static Map<DexVm, List<String>> failsOn =
+ ImmutableMap.of(
+ DexVm.ART_4_4_4,
+ ImmutableList.of(
+ // Dex version not supported
+ "staticinterfacemethods", "defaultmethods"),
+ DexVm.ART_5_1_1,
+ ImmutableList.of(
+ // Dex version not supported
+ "staticinterfacemethods", "defaultmethods"),
+ DexVm.ART_6_0_1,
+ ImmutableList.of(
+ // Dex version not supported
+ "staticinterfacemethods", "defaultmethods"),
+ DexVm.ART_7_0_0,
+ ImmutableList.of(),
+ DexVm.ART_DEFAULT,
+ ImmutableList.of());
+
+ @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void staticInterfaceMethods() throws Throwable {
+ doTest(
+ "staticinterfacemethods",
+ "interfacemethods",
+ "StaticInterfaceMethods",
+ Constants.ANDROID_N_API,
+ options -> options.interfaceMethodDesugaring = OffOrAuto.Auto);
+ }
+
+ @Test
+ public void staticInterfaceMethodsErrorDueToMinSdk() throws Throwable {
+ thrown.expect(CompilationError.class);
+ doTest(
+ "staticinterfacemethods-error-due-to-min-sdk",
+ "interfacemethods",
+ "StaticInterfaceMethods");
+ }
+
+ @Test
+ public void defaultMethods() throws Throwable {
+ doTest(
+ "defaultmethods",
+ "interfacemethods",
+ "DefaultMethods",
+ Constants.ANDROID_N_API,
+ options -> options.interfaceMethodDesugaring = OffOrAuto.Auto);
+ }
+
+ @Test
+ public void defaultMethodsErrorDueToMinSdk() throws Throwable {
+ thrown.expect(CompilationError.class);
+ doTest("defaultmethods-error-due-to-min-sdk", "interfacemethods", "DefaultMethods");
+ }
+
+ private void doTest(String testName, String packageName, String className) throws Throwable {
+ doTest(testName, packageName, className, R8Command.builder(), options -> {});
+ }
+
+ private void doTest(
+ String testName,
+ String packageName,
+ String className,
+ int minSdk,
+ Consumer<InternalOptions> optionsConsumer)
+ throws Throwable {
+ doTest(
+ testName,
+ packageName,
+ className,
+ R8Command.builder().setMinApiLevel(minSdk),
+ optionsConsumer);
+ }
+
+ public void doTest(
+ String testName,
+ String packageName,
+ String className,
+ R8Command.Builder builder,
+ Consumer<InternalOptions> optionsConsumer)
+ throws Throwable {
+ String mainClass = packageName + "." + className;
+ Path inputFile = Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
+ Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
+
+ try {
+ ToolHelper.runR8(
+ builder.addProgramFiles(inputFile).setOutputPath(out).build(), optionsConsumer);
+ } catch (ExecutionException e) {
+ throw e.getCause();
+ }
+
+ if (!ToolHelper.artSupported()) {
+ return;
+ }
+
+ boolean expectedToFail = false;
+ if (failsOn.containsKey(ToolHelper.getDexVm())
+ && failsOn.get(ToolHelper.getDexVm()).contains(testName)) {
+ expectedToFail = true;
+ thrown.expect(Throwable.class);
+ }
+ String output = ToolHelper.runArtNoVerificationErrors(out.toString(), mainClass);
+ if (!expectedToFail) {
+ ProcessResult javaResult =
+ ToolHelper.runJava(ImmutableList.of(inputFile.toString()), mainClass);
+ assertEquals("JVM run failed", javaResult.exitCode, 0);
+ assertTrue(
+ "JVM output does not match art output.\n\tjvm: "
+ + javaResult.stdout
+ + "\n\tart: "
+ + output,
+ output.equals(javaResult.stdout));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
new file mode 100644
index 0000000..6c45f40
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -0,0 +1,84 @@
+// 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;
+
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.dex.Constants;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.function.UnaryOperator;
+import org.junit.Test;
+
+public class R8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<R8Command.Builder> {
+
+ private static Map<DexVm, List<String>> alsoFailsOn =
+ ImmutableMap.of(
+ DexVm.ART_4_4_4, ImmutableList.of(
+ "invokecustom-with-shrinking"
+ ),
+ DexVm.ART_5_1_1, ImmutableList.of(
+ "invokecustom-with-shrinking"
+ ),
+ DexVm.ART_6_0_1, ImmutableList.of(
+ "invokecustom-with-shrinking"
+ ),
+ DexVm.ART_7_0_0, ImmutableList.of(
+ "invokecustom-with-shrinking"
+ ),
+ DexVm.ART_DEFAULT, ImmutableList.of(
+ )
+ );
+
+ @Test
+ public void invokeCustomWithShrinking() throws Throwable {
+ test("invokecustom-with-shrinking", "invokecustom", "InvokeCustom")
+ .withMinApiLevel(Constants.ANDROID_O_API)
+ .withBuilderTransformation(builder ->
+ builder.addProguardConfigurationFiles(
+ Paths.get(ToolHelper.EXAMPLES_ANDROID_O_DIR, "invokecustom/keep-rules.txt")))
+ .run();
+ }
+
+ class R8TestRunner extends TestRunner {
+
+ R8TestRunner(String testName, String packageName, String mainClass) {
+ super(testName, packageName, mainClass);
+ }
+
+ @Override
+ TestRunner withMinApiLevel(int minApiLevel) {
+ return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
+ }
+
+ @Override
+ void build(Path inputFile, Path out) throws Throwable {
+ try {
+ R8Command.Builder builder = R8Command.builder();
+ for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) {
+ builder = transformation.apply(builder);
+ }
+ R8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
+ ToolHelper.runR8(command, this::combinedOptionConsumer);
+ } catch (ExecutionException e) {
+ throw e.getCause();
+ }
+ }
+ }
+
+ @Override
+ TestRunner test(String testName, String packageName, String mainClass) {
+ return new R8TestRunner(testName, packageName, mainClass);
+ }
+
+ @Override
+ boolean expectedToFail(String name) {
+ return super.expectedToFail(name) || failsOn(alsoFailsOn, name);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
new file mode 100644
index 0000000..f72694b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -0,0 +1,255 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.JarBuilder;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class R8RunExamplesTest {
+
+ private static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_BUILD_DIR;
+ private static final String JAR_EXTENSION = ".jar";
+ private static final String DEX_EXTENSION = ".dex";
+ private static final String DEFAULT_DEX_FILENAME = "classes.dex";
+
+ private static final String[] failsWithJar = {};
+
+ // For local testing on a specific Art version(s) change this set. e.g. to
+ // ImmutableSet.of("default") or pass the option -Ddex_vm=<version> to the Java VM.
+ private static Set<DexVm> artVersions = ToolHelper.getArtVersions();
+
+ // A set of class names for examples that might produce bigger output and thus are excluded from
+ // size testing.
+ private static Set<String> mayBeBigger = ImmutableSet.of(
+ // Contains a reference to an extra type due to member rebinding.
+ "throwing.Throwing"
+ );
+
+ @Parameters(name = "{0}{1}")
+ public static Collection<String[]> data() {
+ String[][] tests = {
+ {"arithmetic.Arithmetic", null},
+ {"arrayaccess.ArrayAccess", "37=37"},
+ {"barray.BArray", "bits 42 and bool true"},
+ {"bridge.BridgeMethod", null},
+ {"cse.CommonSubexpressionElimination", "1\n1\n2 2\n2\n3\n3\n4 4\n4\nA\nB\n"},
+ {"constants.Constants", null},
+ {"controlflow.ControlFlow", null},
+ {"conversions.Conversions", null},
+ {"floating_point_annotations.FloatingPointValuedAnnotationTest", null},
+ {"filledarray.FilledArray", null},
+ {"hello.Hello", "Hello, world"},
+ {"ifstatements.IfStatements", null},
+ {"instancevariable.InstanceVariable", "144=144"},
+ {"instanceofstring.InstanceofString", "is-string:true"},
+ {"invoke.Invoke", null},
+ {"jumbostring.JumboString", null},
+ {"loadconst.LoadConst", null},
+ {"newarray.NewArray", null},
+ {"regalloc.RegAlloc", null},
+ {"returns.Returns", null},
+ {"staticfield.StaticField", "101010\n101010\nABC\nABC\n"},
+ {"stringbuilding.StringBuilding",
+ "a2c-xyz-abc7xyz\ntrueABCDE1234232.21.101an Xstringbuilder"},
+ {"switches.Switches", null},
+ {"sync.Sync", null},
+ {"throwing.Throwing", "Throwing"},
+ {"trivial.Trivial", null},
+ {"trycatch.TryCatch", "Success!"},
+ {"trycatchmany.TryCatchMany", "Success!"},
+ {"invokeempty.InvokeEmpty", "AB"},
+ {"regress.Regress", null},
+ {"regress2.Regress2", "START\nLOOP\nLOOP\nLOOP\nLOOP\nLOOP\nEND"},
+ {"regress_37726195.Regress", null},
+ {"regress_37658666.Regress", null},
+ {"regress_37875803.Regress", null},
+ {"regress_37955340.Regress", null},
+ {"memberrebinding2.Test", Integer.toString((8 * 9) / 2)},
+ {"memberrebinding3.Test", null},
+ {"minification.Minification", null},
+ {"enclosingmethod.Main", null},
+ };
+
+ List<String[]> fullTestList = new ArrayList<>(tests.length * 2);
+ for (String[] test : tests) {
+ String qualified = test[0];
+ String pkg = qualified.substring(0, qualified.lastIndexOf('.'));
+ fullTestList.add(new String[]{pkg, DEX_EXTENSION, qualified, test[1]});
+ fullTestList.add(new String[]{pkg, JAR_EXTENSION, qualified, test[1]});
+ }
+ return fullTestList;
+ }
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+ private final String name;
+
+ private final String mainClass;
+ private final String expectedOutput;
+ private final String fileType;
+ private static Map<DexVm, List<String>> failsOn =
+ ImmutableMap.of(
+ DexVm.ART_4_4_4, ImmutableList.of(
+ "vmdebug.dex",
+ "vmdebug.jar",
+ "memberrebinding2.dex", // b/38187737
+ "memberrebinding2.jar" // b/38187737
+ ),
+ DexVm.ART_5_1_1, ImmutableList.of(
+ "vmdebug.dex",
+ "vmdebug.jar",
+ "memberrebinding2.dex", // b/38187737
+ "memberrebinding2.jar" // b/38187737
+ ),
+ DexVm.ART_6_0_1, ImmutableList.of(
+ "vmdebug.dex",
+ "vmdebug.jar",
+ "memberrebinding2.dex", // b/38187737
+ "memberrebinding2.jar" // b/38187737
+ ),
+ DexVm.ART_7_0_0, ImmutableList.of(
+ "memberrebinding2.dex", // b/38187737
+ "memberrebinding2.jar" // b/38187737
+ ),
+ DexVm.ART_DEFAULT, ImmutableList.of(
+ "memberrebinding2.dex", // b/38187737
+ "memberrebinding2.jar" // b/38187737
+ )
+ );
+
+ public R8RunExamplesTest(String name, String fileType, String mainClass, String expectedOutput) {
+ this.name = name;
+ this.fileType = fileType;
+ this.mainClass = mainClass;
+ this.expectedOutput = expectedOutput;
+ }
+
+ private Path getInputFile() {
+ if (fileType == JAR_EXTENSION) {
+ return Paths.get(EXAMPLE_DIR, name + JAR_EXTENSION);
+ } else {
+ assert fileType == DEX_EXTENSION;
+ return getOriginalDexFile();
+ }
+ }
+
+ private Path getOriginalDexFile() {
+ return Paths.get(EXAMPLE_DIR, name, DEFAULT_DEX_FILENAME);
+ }
+
+ private Path getGeneratedDexFile() throws IOException {
+ return Paths.get(temp.getRoot().getCanonicalPath(), DEFAULT_DEX_FILENAME);
+ }
+
+ private String getTestName() {
+ return this.name + this.fileType;
+ }
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Before
+ public void generateR8Version()
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+ if (fileType == JAR_EXTENSION && Arrays.asList(failsWithJar).contains(name)) {
+ thrown.expect(Throwable.class);
+ }
+ String out = temp.getRoot().getCanonicalPath();
+ ToolHelper.runR8(getInputFile().toString(), out);
+ }
+
+ @Test
+ public void processedFileIsSmaller() throws IOException {
+ if (mayBeBigger.contains(mainClass)) {
+ return;
+ }
+ long original = Files.size(getOriginalDexFile());
+ long generated = Files.size(getGeneratedDexFile());
+
+ if (generated > original) {
+ DexInspector inspectOriginal = null;
+ DexInspector inspectGenerated = null;
+ try {
+ inspectOriginal = new DexInspector(getOriginalDexFile());
+ inspectGenerated = new DexInspector(getGeneratedDexFile());
+ } catch (Throwable e) {
+ System.err.println("Failed to parse dex files for post-failure processing");
+ e.printStackTrace();
+ }
+ if (inspectGenerated != null && inspectOriginal != null) {
+ assertEquals("Generated file is larger than original: " + generated + " vs. " + original,
+ inspectOriginal.clazz(mainClass).dumpMethods(),
+ inspectGenerated.clazz(mainClass).dumpMethods());
+ }
+ }
+ assertTrue("Generated file is larger than original: " + generated + " vs. " + original,
+ generated <= original);
+ }
+
+ @Test
+ public void outputIsIdentical() throws IOException, InterruptedException, ExecutionException {
+ if (!ToolHelper.artSupported()) {
+ return;
+ }
+
+ String original = getOriginalDexFile().toString();
+
+ File generated;
+ // Collect the generated dex files.
+ File[] outputFiles =
+ temp.getRoot().listFiles((File file) -> file.getName().endsWith(".dex"));
+ if (outputFiles.length == 1) {
+ // Just run Art on classes.dex.
+ generated = outputFiles[0];
+ } else {
+ // Run Art on JAR file with multiple dex files.
+ generated = temp.getRoot().toPath().resolve(name + ".jar").toFile();
+ JarBuilder.buildJar(outputFiles, generated);
+ }
+ // TODO(ager): Once we have a bot running using dalvik (version 4.4.4) we should remove
+ // this explicit loop to get rid of repeated testing on the buildbots.
+ for (DexVm version : artVersions) {
+ boolean expectedToFail = false;
+ if (failsOn.containsKey(version) && failsOn.get(version).contains(getTestName())) {
+ expectedToFail = true;
+ thrown.expect(Throwable.class);
+ }
+ String output =
+ ToolHelper.checkArtOutputIdentical(original, generated.toString(), mainClass, version);
+ if (expectedOutput != null && !expectedToFail) {
+ assertTrue("'" + output + "' lacks '" + expectedOutput + "'",
+ output.contains(expectedOutput));
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
new file mode 100644
index 0000000..3fe2173
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
@@ -0,0 +1,130 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class R8RunSmaliTestsTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private static final String SMALI_DIR = ToolHelper.SMALI_BUILD_DIR;
+
+ // Tests where the original smali code fails on Art, but runs after R8 processing.
+ private static Map<DexVm, List<String>> originalFailingOnArtVersions = ImmutableMap.of(
+ DexVm.ART_5_1_1, ImmutableList.of(
+ "sparse-switch",
+ "regression/33846227"
+ )
+ );
+
+ // Tests where the original smali code runs on Art, but fails after R8 processing
+ private static Map<String, List<String>> failingOnArtVersions = ImmutableMap.of(
+ // This list is currently empty!
+ );
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Parameters(name = "{0}")
+ public static Collection<String[]> data() {
+ return Arrays.asList(new String[][]{
+ {"arithmetic", "-1\n3\n2\n3\n3.0\n1\n0\n-131580\n-131580\n2\n4\n-2\n"},
+ {"controlflow", "2\n1\n2\n1\n2\n1\n2\n1\n2\n1\n2\n1\n2\n"},
+ {"fibonacci", "55\n55\n55\n55\n"},
+ {"fill-array-data", "[1, 2, 3][4, 5, 6]"},
+ {"filled-new-array", "[1, 2, 3][4, 5, 6][1, 2, 3, 4, 5, 6][6, 5, 4, 3, 2, 1]"},
+ {"packed-switch", "12345"},
+ {"sparse-switch", "12345"},
+ {"unreachable-code-1", "777"},
+ {"multiple-returns", "TFtf\n1\n4611686018427387904\ntrue\nfalse\n"},
+ {"try-catch", ""},
+ {"phi-removal-regression", "returnBoolean\n"},
+ {"overlapping-long-registers", "-9151314442816847872\n-9151314442816319488\n"},
+ {"type-confusion-regression", "java.lang.RuntimeException: Test.<init>()\n"},
+ {"type-confusion-regression2",
+ "java.lang.NullPointerException: Attempt to read from null array\n"},
+ {"type-confusion-regression3",
+ "java.lang.NullPointerException: Attempt to read from field 'byte[] Test.a'" +
+ " on a null object reference\n"},
+ {"type-confusion-regression4", ""},
+ {"type-confusion-regression5", "java.lang.RuntimeException: getId()I\n"},
+ {"chain-of-loops", "java.lang.RuntimeException: f(II)\n"},
+ {"new-instance-and-init", "Test(0)\nTest(0)\nTest(0)\n"},
+ {"bad-codegen",
+ "java.lang.NullPointerException: Attempt to read from field " +
+ "'Test Test.a' on a null object reference\n"},
+ {"merge-blocks-regression", "java.lang.NullPointerException: Attempt to invoke virtual"
+ +" method 'Test Test.bW_()' on a null object reference\n"},
+ {"self-is-catch-block", "100\n-1\n"},
+ {"infinite-loop", ""},
+ {"regression/33336471",
+ "START\n0\n2\nLOOP\n1\n2\nLOOP\n2\n2\nDONE\n" +
+ "START\n0\n2\nLOOP\n1\n2\nLOOP\n2\n2\nDONE\n"},
+ {"regression/33846227", ""},
+ });
+ }
+
+ private String directoryName;
+ private String dexFileName;
+ private String expectedOutput;
+
+ public R8RunSmaliTestsTest(String name, String expectedOutput) {
+ this.directoryName = name;
+ this.dexFileName = name.substring(name.lastIndexOf('/') + 1) + ".dex";
+ this.expectedOutput = expectedOutput;
+ }
+
+ @Test
+ public void SmaliTest()
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+ File originalDexFile = Paths.get(SMALI_DIR, directoryName, dexFileName).toFile();
+ String outputPath = temp.getRoot().getCanonicalPath();
+ ToolHelper.runR8(originalDexFile.getCanonicalPath(), outputPath);
+
+ if (!ToolHelper.artSupported()) {
+ return;
+ }
+
+ String mainClass = "Test";
+ String generated = outputPath + "/classes.dex";
+ String output;
+
+ // If the original smali code fails on the target VM, only run the code produced by R8.
+ if (originalFailingOnArtVersions.containsKey(ToolHelper.getDexVm())
+ && originalFailingOnArtVersions.get(ToolHelper.getDexVm()).contains(directoryName)) {
+ output = ToolHelper.runArtNoVerificationErrors(generated, mainClass);
+ } else {
+ if (failingOnArtVersions.containsKey(ToolHelper.getDexVm())
+ && failingOnArtVersions.get(ToolHelper.getDexVm()).contains(directoryName)) {
+ thrown.expect(Throwable.class);
+ }
+ output =
+ ToolHelper.checkArtOutputIdentical(originalDexFile.toString(), generated, mainClass, null);
+ }
+ assertEquals(expectedOutput, output);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
new file mode 100644
index 0000000..77eb5eb0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.junit.Test;
+
+public class R8UnreachableCodeTest {
+
+ private static final Path SMALI_DIR = Paths.get(ToolHelper.SMALI_BUILD_DIR);
+
+ @Test
+ public void UnreachableCode() throws IOException, ExecutionException {
+ String name = "unreachable-code-1";
+ AndroidApp input = AndroidApp.fromProgramFiles(SMALI_DIR.resolve(name).resolve(name + ".dex"));
+ ExecutorService executorService = Executors.newSingleThreadExecutor();
+ Timing timing = new Timing("R8UnreachableCodeTest");
+ DexApplication application =
+ new ApplicationReader(input, new InternalOptions(), timing).read(executorService);
+ IRConverter converter =
+ new IRConverter(
+ application, new AppInfoWithSubtyping(application), new InternalOptions(), null);
+ converter.optimize();
+ DexProgramClass clazz = application.classes().iterator().next();
+ assertEquals(4, clazz.directMethods().length);
+ for (DexEncodedMethod method : clazz.directMethods()) {
+ if (!method.method.name.toString().equals("main")) {
+ assertEquals(2, method.getCode().asDexCode().instructions.length);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
new file mode 100644
index 0000000..3f63319
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -0,0 +1,242 @@
+// 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;
+
+import static com.android.tools.r8.dex.Constants.ANDROID_K_API;
+import static com.android.tools.r8.dex.Constants.ANDROID_O_API;
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OffOrAuto;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.UnaryOperator;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public abstract class RunExamplesAndroidOTest<B> {
+ static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR;
+
+ abstract class TestRunner {
+ final String testName;
+ final String packageName;
+ final String mainClass;
+
+ final List<Consumer<InternalOptions>> optionConsumers = new ArrayList<>();
+ final List<UnaryOperator<B>> builderTransformations = new ArrayList<>();
+
+ TestRunner(String testName, String packageName, String mainClass) {
+ this.testName = testName;
+ this.packageName = packageName;
+ this.mainClass = mainClass;
+ }
+
+ TestRunner withOptionConsumer(Consumer<InternalOptions> consumer) {
+ optionConsumers.add(consumer);
+ return this;
+ }
+
+ TestRunner withInterfaceMethodDesugaring(OffOrAuto behavior) {
+ return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior);
+ }
+
+ TestRunner withBuilderTransformation(UnaryOperator<B> builderTransformation) {
+ builderTransformations.add(builderTransformation);
+ return this;
+ }
+
+ void combinedOptionConsumer(InternalOptions options) {
+ for (Consumer<InternalOptions> consumer : optionConsumers) {
+ consumer.accept(options);
+ }
+ }
+
+ void run() throws Throwable {
+ if (compilationErrorExpected(testName)) {
+ thrown.expect(CompilationError.class);
+ }
+
+ String qualifiedMainClass = packageName + "." + mainClass;
+ Path inputFile = Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
+ Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
+
+ build(inputFile, out);
+
+ if (!ToolHelper.artSupported()) {
+ return;
+ }
+
+ boolean expectedToFail = expectedToFail(testName);
+ if (expectedToFail) {
+ thrown.expect(Throwable.class);
+ }
+
+ String output = ToolHelper.runArtNoVerificationErrors(out.toString(), qualifiedMainClass);
+ if (!expectedToFail) {
+ ToolHelper.ProcessResult javaResult =
+ ToolHelper.runJava(ImmutableList.of(inputFile.toString()), qualifiedMainClass);
+ assertEquals("JVM run failed", javaResult.exitCode, 0);
+ assertTrue(
+ "JVM output does not match art output.\n\tjvm: "
+ + javaResult.stdout
+ + "\n\tart: "
+ + output,
+ output.equals(javaResult.stdout));
+ }
+ }
+
+ abstract TestRunner withMinApiLevel(int minApiLevel);
+
+ abstract void build(Path inputFile, Path out) throws Throwable;
+ }
+
+ private static List<String> compilationErrorExpected =
+ ImmutableList.of(
+ "invokepolymorphic-error-due-to-min-sdk", "invokecustom-error-due-to-min-sdk");
+
+ private static Map<DexVm, List<String>> failsOn =
+ ImmutableMap.of(
+ DexVm.ART_4_4_4, ImmutableList.of(
+ // API not supported
+ "paramnames",
+ "repeat_annotations_new_api",
+ // Dex version not supported
+ "invokepolymorphic",
+ "invokecustom"
+ ),
+ DexVm.ART_5_1_1, ImmutableList.of(
+ // API not supported
+ "paramnames",
+ "repeat_annotations_new_api",
+ // Dex version not supported
+ "invokepolymorphic",
+ "invokecustom"
+ ),
+ DexVm.ART_6_0_1, ImmutableList.of(
+ // API not supported
+ "paramnames",
+ "repeat_annotations_new_api",
+ // Dex version not supported
+ "invokepolymorphic",
+ "invokecustom"
+ ),
+ DexVm.ART_7_0_0, ImmutableList.of(
+ // API not supported
+ "paramnames",
+ // Dex version not supported
+ "invokepolymorphic",
+ "invokecustom"
+ ),
+ DexVm.ART_DEFAULT, ImmutableList.of(
+ )
+ );
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ boolean failsOn(Map<ToolHelper.DexVm, List<String>> failsOn, String name) {
+ return failsOn.containsKey(ToolHelper.getDexVm())
+ && failsOn.get(ToolHelper.getDexVm()).contains(name);
+ }
+
+ boolean expectedToFail(String name) {
+ return failsOn(failsOn, name);
+ }
+
+ boolean compilationErrorExpected(String testName) {
+ return compilationErrorExpected.contains(testName);
+ }
+
+ @Test
+ public void invokeCustom() throws Throwable {
+ test("invokecustom", "invokecustom", "InvokeCustom")
+ .withMinApiLevel(ANDROID_O_API)
+ .run();
+ }
+
+ @Test
+ public void invokeCustomErrorDueToMinSdk() throws Throwable {
+ test("invokecustom-error-due-to-min-sdk", "invokecustom", "InvokeCustom")
+ .withMinApiLevel(25)
+ .run();
+ }
+
+ @Test
+ public void invokePolymorphic() throws Throwable {
+ test("invokepolymorphic", "invokepolymorphic", "InvokePolymorphic")
+ .withMinApiLevel(ANDROID_O_API)
+ .run();
+ }
+
+ @Test
+ public void invokePolymorphicErrorDueToMinSdk() throws Throwable {
+ test("invokepolymorphic-error-due-to-min-sdk", "invokepolymorphic", "InvokePolymorphic")
+ .withMinApiLevel(25)
+ .run();
+ }
+
+ @Test
+ public void lambdaDesugaring() throws Throwable {
+ test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
+ .withMinApiLevel(ANDROID_K_API)
+ .run();
+ }
+
+ @Test
+ public void lambdaDesugaringNPlus() throws Throwable {
+ test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
+ .withMinApiLevel(ANDROID_K_API)
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .run();
+ }
+
+ @Test
+ public void lambdaDesugaringValueAdjustments() throws Throwable {
+ test("lambdadesugaring-value-adjustments", "lambdadesugaring", "ValueAdjustments")
+ .withMinApiLevel(ANDROID_K_API)
+ .run();
+ }
+
+ @Test
+ public void paramNames() throws Throwable {
+ test("paramnames", "paramnames", "ParameterNames")
+ .withMinApiLevel(26)
+ .run();
+ }
+
+ @Test
+ public void repeatAnnotationsNewApi() throws Throwable {
+ // No need to specify minSdk as repeat annotations are handled by javac and we do not have
+ // to do anything to support them. The library methods to access them just have to be in
+ // the system.
+ test("repeat_annotations_new_api", "repeat_annotations", "RepeatAnnotationsNewApi").run();
+ }
+
+ @Test
+ public void repeatAnnotations() throws Throwable {
+ // No need to specify minSdk as repeat annotations are handled by javac and we do not have
+ // to do anything to support them. The library methods to access them just have to be in
+ // the system.
+ test("repeat_annotations", "repeat_annotations", "RepeatAnnotations").run();
+ }
+
+ abstract TestRunner test(String testName, String packageName, String mainClass);
+}
diff --git a/src/test/java/com/android/tools/r8/TestCondition.java b/src/test/java/com/android/tools/r8/TestCondition.java
new file mode 100644
index 0000000..ef2eac3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestCondition.java
@@ -0,0 +1,113 @@
+// 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;
+
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.R8RunArtTestsTest.DexTool;
+import com.android.tools.r8.ToolHelper.DexVm;
+import java.util.Arrays;
+import java.util.EnumSet;
+
+public class TestCondition {
+
+ static class ToolSet {
+
+ final EnumSet<DexTool> set;
+
+ public ToolSet(EnumSet<DexTool> set) {
+ this.set = set;
+ }
+ }
+
+ static class CompilerSet {
+
+ final EnumSet<CompilerUnderTest> set;
+
+ public CompilerSet(EnumSet<CompilerUnderTest> set) {
+ this.set = set;
+ }
+ }
+
+ static class RuntimeSet {
+
+ final EnumSet<DexVm> set;
+
+ public RuntimeSet(EnumSet<DexVm> set) {
+ this.set = set;
+ }
+ }
+
+ public static final CompilerSet D8_COMPILER = compilers(CompilerUnderTest.D8);
+ public static final CompilerSet R8_COMPILER = compilers(CompilerUnderTest.R8);
+
+ private static final ToolSet ANY_TOOL = new ToolSet(EnumSet.allOf(DexTool.class));
+ private static final CompilerSet ANY_COMPILER =
+ new CompilerSet(EnumSet.allOf(CompilerUnderTest.class));
+ private static final RuntimeSet ANY_RUNTIME = new RuntimeSet(EnumSet.allOf(DexVm.class));
+
+ private final EnumSet<DexTool> dexTools;
+ private final EnumSet<CompilerUnderTest> compilers;
+ private final EnumSet<DexVm> dexVms;
+
+ public TestCondition(
+ EnumSet<DexTool> dexTools,
+ EnumSet<CompilerUnderTest> compilers,
+ EnumSet<DexVm> dexVms) {
+ this.dexTools = dexTools;
+ this.compilers = compilers;
+ this.dexVms = dexVms;
+ }
+
+ public static ToolSet tools(DexTool... tools) {
+ assert tools.length > 0;
+ return new ToolSet(EnumSet.copyOf(Arrays.asList(tools)));
+ }
+
+ public static CompilerSet compilers(CompilerUnderTest... compilers) {
+ assert compilers.length > 0;
+ return new CompilerSet(EnumSet.copyOf(Arrays.asList(compilers)));
+ }
+
+ public static RuntimeSet runtimes(DexVm... runtimes) {
+ assert runtimes.length > 0;
+ return new RuntimeSet(EnumSet.copyOf(Arrays.asList(runtimes)));
+ }
+
+ public static TestCondition match(ToolSet tools, CompilerSet compilers, RuntimeSet runtimes) {
+ return new TestCondition(tools.set, compilers.set, runtimes.set);
+ }
+
+ public static TestCondition any() {
+ return match(TestCondition.ANY_TOOL, TestCondition.ANY_COMPILER, TestCondition.ANY_RUNTIME);
+ }
+
+ public static TestCondition match(ToolSet tools) {
+ return match(tools, TestCondition.ANY_COMPILER, TestCondition.ANY_RUNTIME);
+ }
+
+ public static TestCondition match(ToolSet tools, CompilerSet compilers) {
+ return match(tools, compilers, TestCondition.ANY_RUNTIME);
+ }
+
+ public static TestCondition match(ToolSet tools, RuntimeSet runtimes) {
+ return match(tools, TestCondition.ANY_COMPILER, runtimes);
+ }
+
+ public static TestCondition match(CompilerSet compilers) {
+ return match(TestCondition.ANY_TOOL, compilers, TestCondition.ANY_RUNTIME);
+ }
+
+ public static TestCondition match(CompilerSet compilers, RuntimeSet runtimes) {
+ return match(TestCondition.ANY_TOOL, compilers, runtimes);
+ }
+
+ public static TestCondition match(RuntimeSet runtimes) {
+ return match(TestCondition.ANY_TOOL, TestCondition.ANY_COMPILER, runtimes);
+ }
+
+ public boolean test(DexTool dexTool, CompilerUnderTest compilerUnderTest, DexVm dexVm) {
+ return dexTools.contains(dexTool) && compilers.contains(compilerUnderTest)
+ && dexVms.contains(dexVm);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
new file mode 100644
index 0000000..6e45d0b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -0,0 +1,702 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.CharStreams;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import joptsimple.internal.Strings;
+import org.junit.rules.TemporaryFolder;
+
+public class ToolHelper {
+
+ public static final String BUILD_DIR = "build/";
+ public static final String EXAMPLES_DIR = "src/test/examples/";
+ public static final String EXAMPLES_ANDROID_O_DIR = "src/test/examplesAndroidO/";
+ public static final String EXAMPLES_BUILD_DIR = BUILD_DIR + "test/examples/";
+ public static final String EXAMPLES_ANDROID_N_BUILD_DIR = BUILD_DIR + "test/examplesAndroidN/";
+ public static final String EXAMPLES_ANDROID_O_BUILD_DIR = BUILD_DIR + "test/examplesAndroidO/";
+ public static final String SMALI_BUILD_DIR = BUILD_DIR + "test/smali/";
+
+ private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
+ private static final int DEFAULT_MIN_SDK = 14;
+
+ public enum DexVm {
+ ART_4_4_4("4.4.4"),
+ ART_5_1_1("5.1.1"),
+ ART_6_0_1("6.0.1"),
+ ART_7_0_0("7.0.0"),
+ ART_DEFAULT("default");
+
+ private static final ImmutableMap<String, DexVm> SHORT_NAME_MAP =
+ new ImmutableMap.Builder<String, DexVm>()
+ .putAll(
+ Arrays.stream(DexVm.values()).collect(
+ Collectors.toMap(a -> a.toString(), a -> a)))
+ .build();
+
+ public String toString() {
+ return shortName;
+ }
+
+ public static DexVm fromShortName(String shortName) {
+ return SHORT_NAME_MAP.get(shortName);
+ }
+
+ public boolean isNewerThan(DexVm other) {
+ return compareTo(other) > 0;
+ }
+
+ private DexVm(String shortName) {
+ this.shortName = shortName;
+ }
+
+ private final String shortName;
+ }
+
+
+ public abstract static class CommandBuilder {
+
+ private List<String> options = new ArrayList<>();
+ private Map<String, String> systemProperties = new LinkedHashMap<>();
+ private List<String> classpaths = new ArrayList<>();
+ private String mainClass;
+ private List<String> programArguments = new ArrayList<>();
+ private List<String> bootClassPaths = new ArrayList<>();
+
+ public CommandBuilder appendArtOption(String option) {
+ options.add(option);
+ return this;
+ }
+
+ public CommandBuilder appendArtSystemProperty(String key, String value) {
+ systemProperties.put(key, value);
+ return this;
+ }
+
+ public CommandBuilder appendClasspath(String classpath) {
+ classpaths.add(classpath);
+ return this;
+ }
+
+ public CommandBuilder setMainClass(String className) {
+ this.mainClass = className;
+ return this;
+ }
+
+ public CommandBuilder appendProgramArgument(String option) {
+ programArguments.add(option);
+ return this;
+ }
+
+ public CommandBuilder appendBootClassPath(String lib) {
+ bootClassPaths.add(lib);
+ return this;
+ }
+
+ private List<String> command() {
+ List<String> result = new ArrayList<>();
+ // The art script _must_ be run with bash, bots default to /bin/dash for /bin/sh, so
+ // explicitly set it;
+ if (isLinux()) {
+ result.add("/bin/bash");
+ } else {
+ result.add("tools/docker/run.sh");
+ }
+ result.add(getExecutable());
+ for (String option : options) {
+ result.add(option);
+ }
+ for (Map.Entry<String, String> entry : systemProperties.entrySet()) {
+ StringBuilder builder = new StringBuilder("-D");
+ builder.append(entry.getKey());
+ builder.append("=");
+ builder.append(entry.getValue());
+ result.add(builder.toString());
+ }
+ if (!classpaths.isEmpty()) {
+ result.add("-cp");
+ result.add(Strings.join(classpaths, ":"));
+ }
+ if (!bootClassPaths.isEmpty()) {
+ result.add("-Xbootclasspath:" + String.join(":", bootClassPaths));
+ }
+ if (mainClass != null) {
+ result.add(mainClass);
+ }
+ for (String argument : programArguments) {
+ result.add(argument);
+ }
+ return result;
+ }
+
+ public ProcessBuilder asProcessBuilder() {
+ return new ProcessBuilder(command());
+ }
+
+ public String build() {
+ return String.join(" ", command());
+ }
+
+ protected abstract String getExecutable();
+ }
+
+ public static class ArtCommandBuilder extends CommandBuilder {
+
+ private DexVm version;
+
+ public ArtCommandBuilder() {
+ }
+
+ public ArtCommandBuilder(DexVm version) {
+ assert ART_BINARY_VERSIONS.containsKey(version);
+ this.version = version;
+ }
+
+ @Override
+ protected String getExecutable() {
+ return version != null ? getArtBinary(version) : getArtBinary();
+ }
+ }
+
+ public static class DXCommandBuilder extends CommandBuilder {
+
+ public DXCommandBuilder() {
+ appendProgramArgument("--dex");
+ }
+
+ @Override
+ protected String getExecutable() {
+ return DX;
+ }
+ }
+
+ private static class StreamReader implements Runnable {
+
+ private InputStream stream;
+ private String result;
+
+ public StreamReader(InputStream stream) {
+ this.stream = stream;
+ }
+
+ public String getResult() {
+ return result;
+ }
+
+ @Override
+ public void run() {
+ try {
+ result = CharStreams.toString(new InputStreamReader(stream, StandardCharsets.UTF_8));
+ stream.close();
+ } catch (IOException e) {
+ result = "Failed reading result for stream " + stream;
+ }
+ }
+ }
+
+ private static final String TOOLS = "tools";
+ private static final Map<DexVm, String> ART_DIRS =
+ ImmutableMap.of(
+ DexVm.ART_DEFAULT, "art",
+ DexVm.ART_7_0_0, "art-7.0.0",
+ DexVm.ART_6_0_1, "art-6.0.1",
+ DexVm.ART_5_1_1, "art-5.1.1",
+ DexVm.ART_4_4_4, "dalvik");
+ private static final Map<DexVm, String> ART_BINARY_VERSIONS =
+ ImmutableMap.of(
+ DexVm.ART_DEFAULT, "bin/art",
+ DexVm.ART_7_0_0, "bin/art",
+ DexVm.ART_6_0_1, "bin/art",
+ DexVm.ART_5_1_1, "bin/art",
+ DexVm.ART_4_4_4, "bin/dalvik");
+
+ private static final Map<DexVm, String> ART_BINARY_VERSIONS_X64 =
+ ImmutableMap.of(
+ DexVm.ART_DEFAULT, "bin/art",
+ DexVm.ART_7_0_0, "bin/art",
+ DexVm.ART_6_0_1, "bin/art");
+ private static final List<String> ART_BOOT_LIBS =
+ ImmutableList.of(
+ "core-libart-hostdex.jar",
+ "core-oj-hostdex.jar",
+ "apache-xml-hostdex.jar"
+ );
+
+ private static final String LIB_PATH = TOOLS + "/linux/art/lib";
+ private static final String DX = TOOLS + "/linux/dx/bin/dx";
+ private static final String DEX2OAT = TOOLS + "/linux/art/bin/dex2oat";
+ private static final String ANGLER_DIR = TOOLS + "/linux/art/product/angler";
+ private static final String ANGLER_BOOT_IMAGE = ANGLER_DIR + "/system/framework/boot.art";
+
+ public static String getArtDir(DexVm version) {
+ String dir = ART_DIRS.get(version);
+ if (dir == null) {
+ throw new IllegalStateException("Does not support dex vm: " + version);
+ }
+ if (isLinux() || isMac()) {
+ // The Linux version is used on Mac, where it is run in a Docker container.
+ return TOOLS + "/linux/" + dir;
+ }
+ fail("Unsupported platform, we currently only support mac and linux: " + getPlatform());
+ return ""; //never here
+ }
+
+ public static String getArtBinary(DexVm version) {
+ String binary = ART_BINARY_VERSIONS.get(version);
+ if (binary == null) {
+ throw new IllegalStateException("Does not support running with dex vm: " + version);
+ }
+ return getArtDir(version) + "/" + binary;
+ }
+
+ public static String getDefaultAndroidJar() {
+ return getAndroidJar(Constants.DEFAULT_ANDROID_API);
+ }
+
+ public static String getAndroidJar(int minSdkVersion) {
+ return String.format(
+ ANDROID_JAR_PATTERN,
+ minSdkVersion == Constants.DEFAULT_ANDROID_API ? DEFAULT_MIN_SDK : minSdkVersion);
+ }
+
+ public static Path getJdwpTestsJarPath(int minSdk) {
+ if (minSdk >= Constants.ANDROID_N_API) {
+ return Paths.get("third_party", "jdwp-tests", "apache-harmony-jdwp-tests-host.jar");
+ } else {
+ return Paths.get(ToolHelper.BUILD_DIR, "libs", "jdwp-tests-preN.jar");
+ }
+ }
+
+ // For non-Linux platforms create the temporary directory in the repository root to simplify
+ // running Art in a docker container
+ public static TemporaryFolder getTemporaryFolderForTest() {
+ return new TemporaryFolder(ToolHelper.isLinux() ? null : Paths.get("build", "tmp").toFile());
+ }
+
+ public static String getArtBinary() {
+ return getArtBinary(getDexVm());
+ }
+
+ public static Set<DexVm> getArtVersions() {
+ String artVersion = System.getProperty("dex_vm");
+ if (artVersion != null) {
+ DexVm artVersionEnum = getDexVm();
+ if (!ART_BINARY_VERSIONS.containsKey(artVersionEnum)) {
+ throw new RuntimeException("Unsupported Art version " + artVersion);
+ }
+ return ImmutableSet.of(artVersionEnum);
+ } else {
+ if (isLinux()) {
+ return ART_BINARY_VERSIONS.keySet();
+ } else {
+ return ART_BINARY_VERSIONS_X64.keySet();
+ }
+ }
+ }
+
+ public static List<String> getArtBootLibs() {
+ String prefix = getArtDir(getDexVm()) + "/";
+ List<String> result = new ArrayList<>();
+ ART_BOOT_LIBS.stream().forEach(x -> result.add(prefix + "framework/" + x));
+ return result;
+ }
+
+ // Returns if the passed in vm to use is the default.
+ public static boolean isDefaultDexVm() {
+ return getDexVm() == DexVm.ART_DEFAULT;
+ }
+
+ public static DexVm getDexVm() {
+ String artVersion = System.getProperty("dex_vm");
+ if (artVersion == null) {
+ return DexVm.ART_DEFAULT;
+ } else {
+ DexVm artVersionEnum = DexVm.fromShortName(artVersion);
+ if (artVersionEnum == null) {
+ throw new RuntimeException("Unsupported Art version " + artVersion);
+ } else {
+ return artVersionEnum;
+ }
+ }
+ }
+
+ public static int getMinApiLevelForDexVm(DexVm dexVm) {
+ switch (dexVm) {
+ case ART_DEFAULT:
+ return Constants.ANDROID_O_API;
+ case ART_7_0_0:
+ return Constants.ANDROID_N_API;
+ default:
+ return Constants.DEFAULT_ANDROID_API;
+ }
+ }
+
+ private static String getPlatform() {
+ return System.getProperty("os.name");
+ }
+
+ public static boolean isLinux() {
+ return getPlatform().startsWith("Linux");
+ }
+
+ public static boolean isMac() {
+ return getPlatform().startsWith("Mac");
+ }
+
+ public static boolean artSupported() {
+ if (!isLinux() && !isMac()) {
+ System.err.println("Testing on your platform is not fully supported. " +
+ "Art does not work on on your platform.");
+ return false;
+ }
+ return true;
+ }
+
+ public static Path getClassPathForTests() {
+ return Paths.get(BUILD_DIR, "classes", "test");
+ }
+
+ public static Path getClassFileForTestClass(Class clazz) {
+ String[] parts = clazz.getCanonicalName().split("\\.");
+ parts[parts.length - 1] = parts[parts.length - 1] + ".class";
+ return getClassPathForTests().resolve(Paths.get("", parts));
+ }
+
+ public static DexApplication buildApplication(List<String> fileNames)
+ throws IOException, ExecutionException {
+ return new ApplicationReader(
+ AndroidApp.fromProgramFiles(ListUtils.map(fileNames, Paths::get)),
+ new InternalOptions(),
+ new Timing("ToolHelper buildApplication"))
+ .read();
+ }
+
+ public static AndroidApp runR8(AndroidApp app)
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ return runR8(R8Command.builder(app).build());
+ }
+
+ public static AndroidApp runR8(AndroidApp app, Path output)
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ assert output != null;
+ return runR8(R8Command.builder(app).setOutputPath(output).build());
+ }
+
+ public static AndroidApp runR8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
+ throws ProguardRuleParserException, ExecutionException, IOException, CompilationException {
+ return runR8(R8Command.builder(app).build(), optionsConsumer);
+ }
+
+ public static AndroidApp runR8(R8Command command)
+ throws ProguardRuleParserException, ExecutionException, IOException {
+ return runR8(command, null);
+ }
+
+ public static AndroidApp runR8(R8Command command, Consumer<InternalOptions> optionsConsumer)
+ throws ProguardRuleParserException, ExecutionException, IOException {
+ // TODO(zerny): Should we really be adding the android library in ToolHelper?
+ AndroidApp app = command.getInputApp();
+ if (app.getClassLibraryResources().isEmpty()) {
+ app =
+ AndroidApp.builder(app)
+ .addLibraryFiles(Paths.get(getAndroidJar(command.getMinApiLevel())))
+ .build();
+ }
+ InternalOptions options = command.getInternalOptions();
+ // TODO(zerny): Should we really be setting this in ToolHelper?
+ options.ignoreMissingClasses = true;
+ if (optionsConsumer != null) {
+ optionsConsumer.accept(options);
+ }
+ AndroidApp result = R8.runForTesting(app, options);
+ if (command.getOutputPath() != null) {
+ result.write(command.getOutputPath());
+ }
+ return result;
+ }
+
+ public static AndroidApp runR8(String fileName, String out)
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+ return runR8(Collections.singletonList(fileName), out);
+ }
+
+ public static AndroidApp runR8(Collection<String> fileNames, String out)
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+ return R8.run(
+ R8Command.builder()
+ .addProgramFiles(ListUtils.map(fileNames, Paths::get))
+ .setOutputPath(Paths.get(out))
+ .setIgnoreMissingClasses(true)
+ .build());
+ }
+
+ public static AndroidApp runD8(AndroidApp app) throws CompilationException, IOException {
+ return runD8(app, null);
+ }
+
+ public static AndroidApp runD8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
+ throws CompilationException, IOException {
+ return runD8(D8Command.builder(app).build(), optionsConsumer);
+ }
+
+ public static AndroidApp runD8(D8Command command) throws IOException {
+ return runD8(command, null);
+ }
+
+ public static AndroidApp runD8(D8Command command, Consumer<InternalOptions> optionsConsumer)
+ throws IOException {
+ InternalOptions options = command.getInternalOptions();
+ if (optionsConsumer != null) {
+ optionsConsumer.accept(options);
+ }
+ AndroidApp result = D8.runForTesting(command.getInputApp(), options);
+ if (command.getOutputPath() != null) {
+ result.write(command.getOutputPath());
+ }
+ return result;
+ }
+
+ public static AndroidApp runDexer(String fileName, String outDir, String... extraArgs)
+ throws IOException {
+ List<String> args = new ArrayList<>();
+ Collections.addAll(args, extraArgs);
+ Collections.addAll(args, "--output=" + outDir + "/classes.dex", fileName);
+ int result = runDX(args.toArray(new String[args.size()])).exitCode;
+ return result != 0 ? null : AndroidApp.fromProgramDirectory(Paths.get(outDir));
+ }
+
+ public static ProcessResult runDX(String[] args) throws IOException {
+ DXCommandBuilder builder = new DXCommandBuilder();
+ for (String arg : args) {
+ builder.appendProgramArgument(arg);
+ }
+ return runProcess(builder.asProcessBuilder());
+ }
+
+ public static ProcessResult runJava(Class clazz) throws Exception {
+ String main = clazz.getCanonicalName();
+ Path path = getClassPathForTests();
+ return runJava(ImmutableList.of(path.toString()), main);
+ }
+
+ public static ProcessResult runJava(List<String> classpath, String mainClass) throws IOException {
+ ProcessBuilder builder = new ProcessBuilder(
+ getJavaExecutable(), "-cp", String.join(":", classpath), mainClass);
+ return runProcess(builder);
+ }
+
+ public static ProcessResult forkD8(Path dir, String... args)
+ throws IOException, InterruptedException {
+ return forkJava(dir, D8.class, args);
+ }
+
+ public static ProcessResult forkR8(Path dir, String... args)
+ throws IOException, InterruptedException {
+ return forkJava(dir, R8.class, ImmutableList.builder()
+ .addAll(Arrays.asList(args))
+ .add("--ignore-missing-classes")
+ .build()
+ .toArray(new String[0]));
+ }
+
+ private static ProcessResult forkJava(Path dir, Class clazz, String... args)
+ throws IOException, InterruptedException {
+ List<String> command = new ImmutableList.Builder<String>()
+ .add(getJavaExecutable())
+ .add("-cp").add(System.getProperty("java.class.path"))
+ .add(clazz.getCanonicalName())
+ .addAll(Arrays.asList(args))
+ .build();
+ return runProcess(new ProcessBuilder(command).directory(dir.toFile()));
+ }
+
+ public static String getJavaExecutable() {
+ return Paths.get(System.getProperty("java.home"), "bin", "java").toString();
+ }
+
+ public static String runArtNoVerificationErrors(String file, String mainClass)
+ throws IOException {
+ return runArtNoVerificationErrors(Collections.singletonList(file), mainClass, null);
+ }
+
+ public static String runArtNoVerificationErrors(List<String> files, String mainClass,
+ Consumer<ArtCommandBuilder> extras)
+ throws IOException {
+ return runArtNoVerificationErrors(files, mainClass, extras, null);
+ }
+
+ public static String runArtNoVerificationErrors(List<String> files, String mainClass,
+ Consumer<ArtCommandBuilder> extras,
+ DexVm version)
+ throws IOException {
+ ArtCommandBuilder builder =
+ version != null ? new ArtCommandBuilder(version) : new ArtCommandBuilder();
+ files.forEach(builder::appendClasspath);
+ builder.setMainClass(mainClass);
+ if (extras != null) {
+ extras.accept(builder);
+ }
+ return runArtNoVerificationErrors(builder);
+ }
+
+ public static String runArtNoVerificationErrors(ArtCommandBuilder builder) throws IOException {
+ ProcessResult result = runArtProcess(builder);
+ if (result.stderr.contains("Verification error")) {
+ fail("Verification error: \n" + result.stderr);
+ }
+ return result.stdout;
+ }
+
+ private static ProcessResult runArtProcess(ArtCommandBuilder builder) throws IOException {
+ ProcessResult result = runProcess(builder.asProcessBuilder());
+ if (result.exitCode != 0) {
+ fail("Unexpected art failure: '" + result.stderr + "'\n" + result.stdout);
+ }
+ return result;
+ }
+
+ public static String runArt(ArtCommandBuilder builder) throws IOException {
+ ProcessResult result = runArtProcess(builder);
+ return result.stdout;
+ }
+
+ public static String checkArtOutputIdentical(String file1, String file2, String mainClass,
+ DexVm version)
+ throws IOException {
+ return checkArtOutputIdentical(Collections.singletonList(file1),
+ Collections.singletonList(file2), mainClass, null, version);
+ }
+
+ public static String checkArtOutputIdentical(List<String> files1, List<String> files2,
+ String mainClass,
+ Consumer<ArtCommandBuilder> extras,
+ DexVm version)
+ throws IOException {
+ // Run art on original.
+ for (String file : files1) {
+ assertTrue("file1 " + file + " must exists", Files.exists(Paths.get(file)));
+ }
+ String output1 = ToolHelper.runArtNoVerificationErrors(files1, mainClass, extras, version);
+ // Run art on R8 processed version.
+ for (String file : files2) {
+ assertTrue("file2 " + file + " must exists", Files.exists(Paths.get(file)));
+ }
+ String output2 = ToolHelper.runArtNoVerificationErrors(files2, mainClass, extras, version);
+ assertEquals(output1, output2);
+ return output1;
+ }
+
+ public static void runDex2Oat(Path file, Path outFile) throws IOException {
+ assert Files.exists(file);
+ assert ByteStreams.toByteArray(Files.newInputStream(file)).length > 0;
+ List<String> command = new ArrayList<>();
+ command.add(DEX2OAT);
+ command.add("--android-root=" + ANGLER_DIR);
+ command.add("--runtime-arg");
+ command.add("-Xnorelocate");
+ command.add("--boot-image=" + ANGLER_BOOT_IMAGE);
+ command.add("--dex-file=" + file.toAbsolutePath());
+ command.add("--oat-file=" + outFile.toAbsolutePath());
+ command.add("--instruction-set=arm64");
+ command.add("--compiler-filter=interpret-only");
+ ProcessBuilder builder = new ProcessBuilder(command);
+ builder.environment().put("LD_LIBRARY_PATH", LIB_PATH);
+ ProcessResult result = runProcess(builder);
+ if (result.exitCode != 0) {
+ fail("dex2oat failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
+ }
+ if (result.stderr.contains("Verification error")) {
+ fail("Verification error: \n" + result.stderr);
+ }
+ }
+
+ public static class ProcessResult {
+
+ public final int exitCode;
+ public final String stdout;
+ public final String stderr;
+
+ ProcessResult(int exitCode, String stdout, String stderr) {
+ this.exitCode = exitCode;
+ this.stdout = stdout;
+ this.stderr = stderr;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("EXIT CODE: ");
+ builder.append(exitCode);
+ builder.append("\n");
+ builder.append("STDOUT: ");
+ builder.append("\n");
+ builder.append(stdout);
+ builder.append("\n");
+ builder.append("STDERR: ");
+ builder.append("\n");
+ builder.append(stderr);
+ builder.append("\n");
+ return builder.toString();
+ }
+ }
+
+ public static ProcessResult runProcess(ProcessBuilder builder) throws IOException {
+ System.out.println(String.join(" ", builder.command()));
+ Process p = builder.start();
+ // Drain stdout and stderr so that the process does not block. Read stdout and stderr
+ // in parallel to make sure that neither buffer can get filled up which will cause the
+ // C program to block in a call to write.
+ StreamReader stdoutReader = new StreamReader(p.getInputStream());
+ StreamReader stderrReader = new StreamReader(p.getErrorStream());
+ Thread stdoutThread = new Thread(stdoutReader);
+ Thread stderrThread = new Thread(stderrReader);
+ stdoutThread.start();
+ stderrThread.start();
+ try {
+ p.waitFor();
+ stdoutThread.join();
+ stderrThread.join();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Execution interrupted", e);
+ }
+ return new ProcessResult(p.exitValue(), stdoutReader.getResult(), stderrReader.getResult());
+ }
+
+ public static AndroidApp getApp(BaseCommand command) {
+ return command.getInputApp();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/bisect/BisectTest.java b/src/test/java/com/android/tools/r8/bisect/BisectTest.java
new file mode 100644
index 0000000..ba49e64
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/bisect/BisectTest.java
@@ -0,0 +1,92 @@
+// 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.bisect;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.bisect.BisectOptions.Result;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.smali.SmaliTestBase.MethodSignature;
+import com.android.tools.r8.smali.SmaliTestBase.SmaliBuilder;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class BisectTest {
+
+ private final String[] CLASSES = {"A", "B", "C", "D", "E", "F", "G", "H"};
+ private final String ERRONEOUS_CLASS = "F";
+ private final String ERRONEOUS_METHOD = "foo";
+ private final String VALID_METHOD = "bar";
+
+ // Set during build to more easily inspect later.
+ private MethodSignature erroneousMethodSignature = null;
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void bisect() throws Exception {
+ InternalOptions options = new InternalOptions();
+ Timing timing = new Timing("bisect-test");
+
+ // Build "good" application with no method in "F".
+ SmaliBuilder builderGood = new SmaliBuilder();
+ for (String clazz : CLASSES) {
+ builderGood.addClass(clazz);
+ builderGood.addStaticMethod(
+ "void", VALID_METHOD, ImmutableList.of(), 0, "return-void");
+ }
+ AndroidApp inputGood = AndroidApp.fromDexProgramData(builderGood.compile());
+ DexApplication appGood = new ApplicationReader(inputGood, options, timing).read();
+
+ // Build "bad" application with a method "foo" in "F".
+ SmaliBuilder builderBad = new SmaliBuilder();
+ for (String clazz : CLASSES) {
+ builderBad.addClass(clazz);
+ if (clazz.equals(ERRONEOUS_CLASS)) {
+ erroneousMethodSignature = builderBad.addStaticMethod(
+ "void", ERRONEOUS_METHOD, ImmutableList.of(), 0, "return-void");
+ } else {
+ builderBad.addStaticMethod(
+ "void", VALID_METHOD, ImmutableList.of(), 0, "return-void");
+ }
+ }
+ AndroidApp inputBad = AndroidApp.fromDexProgramData(builderBad.compile());
+ DexApplication appBad = new ApplicationReader(inputBad, options, timing).read();
+
+ ExecutorService executor = Executors.newWorkStealingPool();
+ try {
+ BisectState state = new BisectState(appGood, appBad, null);
+ DexProgramClass clazz = Bisect.run(state, this::command, temp.newFolder().toPath(), executor);
+ System.out.println("Found bad class: " + clazz);
+ assertEquals(clazz.type.toString(), ERRONEOUS_CLASS);
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ private Result command(DexApplication application) {
+ DexInspector inspector = new DexInspector(application);
+ if (inspector
+ .clazz(ERRONEOUS_CLASS)
+ .method(erroneousMethodSignature.returnType,
+ erroneousMethodSignature.name,
+ erroneousMethodSignature.parameterTypes)
+ .isPresent()) {
+ return Result.BAD;
+ }
+ return Result.GOOD;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/code/InstructionFactoryTest.java b/src/test/java/com/android/tools/r8/code/InstructionFactoryTest.java
new file mode 100644
index 0000000..ddd4f69
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/code/InstructionFactoryTest.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import java.nio.ByteBuffer;
+import org.junit.Test;
+
+/**
+ * Tests for the InstructionFactory.
+ */
+public class InstructionFactoryTest {
+
+ @Test
+ public void emptyBuffer() {
+ ByteBuffer emptyBuffer = ByteBuffer.allocate(0);
+ InstructionFactory factory = new InstructionFactory();
+ Instruction[] instructions =
+ factory.readSequenceFrom(emptyBuffer, 0, 0, new OffsetToObjectMapping());
+ assertTrue(instructions.length == 0);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java b/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
new file mode 100644
index 0000000..228b9b3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
@@ -0,0 +1,174 @@
+// 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.compatdx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.maindexlist.MainDexListTests;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public class CompatDxTests {
+ private static final String EXAMPLE_JAR_FILE1 = "build/test/examples/arithmetic.jar";
+ private static final String EXAMPLE_JAR_FILE2 = "build/test/examples/barray.jar";
+
+ private static final String NO_LOCALS = "--no-locals";
+ private static final String NO_POSITIONS = "--positions=none";
+ private static final String MULTIDEX = "--multi-dex";
+ private static final String NUM_THREADS_5 = "--num-threads=5";
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void noFilesTest() throws IOException {
+ runDexer("--no-files");
+ }
+
+ @Test
+ public void noOutputTest() throws IOException {
+ runDexerWithoutOutput(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void singleJarInputFile() throws IOException {
+ runDexer(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void multipleJarInputFiles() throws IOException {
+ runDexer(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1, EXAMPLE_JAR_FILE2);
+ }
+
+ @Test
+ public void outputZipFile() throws IOException {
+ runDexerWithOutput("foo.dex.zip", NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void useMultipleThreads() throws IOException {
+ runDexer(NUM_THREADS_5, NO_POSITIONS, NO_LOCALS, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void withPositions() throws IOException {
+ runDexer(NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void withLocals() throws IOException {
+ runDexer(NO_POSITIONS, MULTIDEX, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void withoutMultidex() throws IOException {
+ runDexer(NO_POSITIONS, NO_LOCALS, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void writeToNamedDexFile() throws IOException {
+ runDexerWithOutput("named-output.dex", EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void singleDexProgramIsTooLarge() throws IOException {
+ thrown.expect(CompilationError.class);
+ runDexer(MainDexListTests.getTwoLargeClassesAppPath().toString());
+ }
+
+ private void runDexer(String... args) throws IOException {
+ runDexerWithOutput("", args);
+ }
+
+ private void runDexerWithoutOutput(String... args) throws IOException {
+ runDexerWithOutput(null, args);
+ }
+
+ private void runDexerWithOutput(String out, String... args) throws IOException {
+ Path d8Out = null;
+ Path dxOut = null;
+ if (out != null) {
+ d8Out = temp.newFolder("d8-out").toPath().resolve(out);
+ dxOut = temp.newFolder("dx-out").toPath().resolve(out);
+ assertNotEquals(d8Out, dxOut);
+ }
+
+ List<String> d8Args = new ArrayList<>(args.length + 2);
+ d8Args.add("--dex");
+ if (d8Out != null) {
+ d8Args.add("--output=" + d8Out);
+ }
+ Collections.addAll(d8Args, args);
+ System.out.println("running: d8 " + StringUtils.join(d8Args, " "));
+ CompatDx.main(d8Args.toArray(new String[d8Args.size()]));
+
+ List<String> dxArgs = new ArrayList<>(args.length + 2);
+ if (dxOut != null) {
+ dxArgs.add("--output=" + dxOut);
+ }
+ Collections.addAll(dxArgs, args);
+ System.out.println("running: dx " + StringUtils.join(dxArgs, " "));
+ ToolHelper.runDX(dxArgs.toArray(new String[dxArgs.size()]));
+
+ if (out == null) {
+ // Can't check output if explicitly not writing any.
+ return;
+ }
+
+ List<Path> d8Files = Files.list(Files.isDirectory(d8Out) ? d8Out : d8Out.getParent())
+ .sorted().collect(Collectors.toList());
+ List<Path> dxFiles = Files.list(Files.isDirectory(dxOut) ? dxOut : dxOut.getParent())
+ .sorted().collect(Collectors.toList());
+ assertEquals("Out file names differ",
+ StringUtils.join(dxFiles, "\n", BraceType.NONE, (file) ->
+ file.getFileName().toString()),
+ StringUtils.join(d8Files, "\n", BraceType.NONE, (file) ->
+ file.getFileName().toString()));
+
+ for (int i = 0; i < d8Files.size(); i++) {
+ if (FileUtils.isArchive(d8Files.get(i))) {
+ compareArchiveFiles(d8Files.get(i), dxFiles.get(i));
+ }
+ }
+ }
+
+ private void compareArchiveFiles(Path d8File, Path dxFile) throws IOException {
+ ZipFile d8Zip = new ZipFile(d8File.toFile());
+ ZipFile dxZip = new ZipFile(dxFile.toFile());
+ // TODO(zerny): This should test resource containment too once supported.
+ Set<String> d8Content = d8Zip.stream().map(ZipEntry::getName).collect(Collectors.toSet());
+ Set<String> dxContent = dxZip.stream().map(ZipEntry::getName).collect(Collectors.toSet());
+ for (String entry : d8Content) {
+ assertTrue("Expected dx output to contain " + entry, dxContent.contains(entry));
+ }
+ for (String entry : dxContent) {
+ if (FileUtils.isDexFile(Paths.get(entry))) {
+ assertTrue("Expected d8 output to contain " + entry, d8Content.contains(entry));
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/d8/DexVersionTests.java b/src/test/java/com/android/tools/r8/d8/DexVersionTests.java
new file mode 100644
index 0000000..0d72a52
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/d8/DexVersionTests.java
@@ -0,0 +1,184 @@
+// 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.d8;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.D8Output;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class DexVersionTests {
+
+ private static final Path ARITHMETIC_JAR =
+ Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/arithmetic.jar");
+
+ private static final Path ARRAYACCESS_JAR =
+ Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/arrayaccess.jar");
+
+ @Rule public TemporaryFolder defaultApiFolder1 = ToolHelper.getTemporaryFolderForTest();
+ @Rule public TemporaryFolder defaultApiFolder2 = ToolHelper.getTemporaryFolderForTest();
+ @Rule public TemporaryFolder androidOApiFolder1 = ToolHelper.getTemporaryFolderForTest();
+ @Rule public TemporaryFolder androidOApiFolder2 = ToolHelper.getTemporaryFolderForTest();
+ @Rule public TemporaryFolder androidNApiFolder1 = ToolHelper.getTemporaryFolderForTest();
+ @Rule public TemporaryFolder androidNApiFolder2 = ToolHelper.getTemporaryFolderForTest();
+
+ @Before
+ public void compileVersions() throws CompilationException, IOException {
+ D8Command.Builder arithmeticBuilder = D8Command.builder().addProgramFiles(ARITHMETIC_JAR);
+ D8Command.Builder arrayAccessBuilder = D8Command.builder().addProgramFiles(ARRAYACCESS_JAR);
+ D8Output output = D8.run(arrayAccessBuilder.build());
+ output.write(defaultApiFolder1.getRoot().toPath());
+ output = D8.run(arrayAccessBuilder.setMinApiLevel(Constants.ANDROID_O_API).build());
+ output.write(androidOApiFolder1.getRoot().toPath());
+ output = D8.run(arrayAccessBuilder.setMinApiLevel(Constants.ANDROID_N_API).build());
+ output.write(androidNApiFolder1.getRoot().toPath());
+ output = D8.run(arithmeticBuilder.build());
+ output.write(defaultApiFolder2.getRoot().toPath());
+ output = D8.run(arithmeticBuilder.setMinApiLevel(Constants.ANDROID_O_API).build());
+ output.write(androidOApiFolder2.getRoot().toPath());
+ output = D8.run(arithmeticBuilder.setMinApiLevel(Constants.ANDROID_N_API).build());
+ output.write(androidNApiFolder2.getRoot().toPath());
+ }
+
+ private Path default1() {
+ return defaultApiFolder1.getRoot().toPath().resolve("classes.dex");
+ }
+
+ private Path default2() {
+ return defaultApiFolder2.getRoot().toPath().resolve("classes.dex");
+ }
+
+ private Path androidO1() {
+ return androidOApiFolder1.getRoot().toPath().resolve("classes.dex");
+ }
+
+ private Path androidO2() {
+ return androidOApiFolder2.getRoot().toPath().resolve("classes.dex");
+ }
+
+ private Path androidN1() {
+ return androidNApiFolder1.getRoot().toPath().resolve("classes.dex");
+ }
+
+ private Path androidN2() {
+ return androidNApiFolder2.getRoot().toPath().resolve("classes.dex");
+ }
+
+ @Test
+ public void mergeCompatibleVersions() throws CompilationException, IOException {
+ // Verify that we can merge between all versions when no explicit min sdk version is set.
+ D8.run(D8Command.builder().addProgramFiles(default1()).addProgramFiles(default2()).build());
+ D8.run(D8Command.builder().addProgramFiles(default1()).addProgramFiles(androidO2()).build());
+ D8.run(D8Command.builder().addProgramFiles(default1()).addProgramFiles(androidN2()).build());
+ D8.run(D8Command.builder().addProgramFiles(androidO1()).addProgramFiles(androidN2()).build());
+ D8.run(D8Command.builder().addProgramFiles(androidO1()).addProgramFiles(androidO2()).build());
+ D8.run(D8Command.builder().addProgramFiles(androidN1()).addProgramFiles(androidN2()).build());
+ // Verify that we can merge between all version when api version is explicitly
+ // set to Android O.
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_O_API)
+ .addProgramFiles(default1())
+ .addProgramFiles(default2())
+ .build());
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_O_API)
+ .addProgramFiles(default1())
+ .addProgramFiles(androidO2())
+ .build());
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_O_API)
+ .addProgramFiles(default1())
+ .addProgramFiles(androidN2())
+ .build());
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_O_API)
+ .addProgramFiles(androidO1())
+ .addProgramFiles(androidN2())
+ .build());
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_O_API)
+ .addProgramFiles(androidO1())
+ .addProgramFiles(androidO2())
+ .build());
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_O_API)
+ .addProgramFiles(androidN1())
+ .addProgramFiles(androidN2())
+ .build());
+ // Verify that we can merge up to version N when api version is explicitly set to
+ // Android N.
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_N_API)
+ .addProgramFiles(default1())
+ .addProgramFiles(default2())
+ .build());
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_N_API)
+ .addProgramFiles(default1())
+ .addProgramFiles(androidN2())
+ .build());
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_N_API)
+ .addProgramFiles(androidN1())
+ .addProgramFiles(androidN2())
+ .build());
+ // Verify that we can merge default api version when api version is explicitly set to
+ // Android K.
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_K_API)
+ .addProgramFiles(default1())
+ .addProgramFiles(default2())
+ .build());
+ }
+
+ @Test(expected = CompilationError.class)
+ public void mergeErrorVersionNWithVersionOInput() throws CompilationException, IOException {
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_N_API)
+ .addProgramFiles(default1())
+ .addProgramFiles(androidO2())
+ .build());
+ }
+
+ @Test(expected = CompilationError.class)
+ public void mergeErrorVersionKWithVersionOInput() throws CompilationException, IOException {
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_K_API)
+ .addProgramFiles(default1())
+ .addProgramFiles(androidO2())
+ .build());
+ }
+
+ @Test(expected = CompilationError.class)
+ public void mergeErrorVersionKWithVersionNInput() throws CompilationException, IOException {
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_K_API)
+ .addProgramFiles(default1())
+ .addProgramFiles(androidN2())
+ .build());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
new file mode 100644
index 0000000..0b042a9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -0,0 +1,793 @@
+// 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.debug;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.Event;
+import org.apache.harmony.jpda.tests.framework.jdwp.EventBuilder;
+import org.apache.harmony.jpda.tests.framework.jdwp.EventPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.Frame.Variable;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands.StackFrameCommandSet;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.EventKind;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.StepDepth;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.StepSize;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.SuspendPolicy;
+import org.apache.harmony.jpda.tests.framework.jdwp.Location;
+import org.apache.harmony.jpda.tests.framework.jdwp.ParsedEvent;
+import org.apache.harmony.jpda.tests.framework.jdwp.ParsedEvent.EventThread;
+import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.apache.harmony.jpda.tests.framework.jdwp.VmMirror;
+import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase;
+import org.apache.harmony.jpda.tests.share.JPDATestOptions;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.TestName;
+
+/**
+ *
+ * Base class for debugging tests
+ */
+public abstract class DebugTestBase {
+
+ public static final StepFilter NO_FILTER = new StepFilter.NoStepFilter();
+ public static final StepFilter INTELLIJ_FILTER = new StepFilter.IntelliJStepFilter();
+ private static final StepFilter DEFAULT_FILTER = NO_FILTER;
+
+ // Set to true to run tests with java
+ private static final boolean RUN_DEBUGGEE_WITH_JAVA = false;
+
+ // Set to true to enable verbose logs
+ private static final boolean DEBUG_TESTS = false;
+
+ private static final List<DexVm> UNSUPPORTED_ART_VERSIONS = ImmutableList.of(
+ // Dalvik does not support command ReferenceType.Methods which is used to set breakpoint.
+ // TODO(shertz) use command ReferenceType.MethodsWithGeneric instead
+ DexVm.ART_4_4_4,
+ // Older runtimes fail on buildbot
+ // TODO(shertz) re-enable once issue is solved
+ DexVm.ART_5_1_1,
+ DexVm.ART_6_0_1);
+
+ private static final Path DEBUGGEE_JAR = Paths
+ .get(ToolHelper.BUILD_DIR, "test", "debug_test_resources.jar");
+
+ @ClassRule
+ public static TemporaryFolder temp = new TemporaryFolder();
+ private static Path jdwpDexD8 = null;
+ private static Path debuggeeDexD8 = null;
+
+ @Rule
+ public TestName testName = new TestName();
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ // Convert jar to dex with d8 with debug info
+ int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+ {
+ Path jdwpJar = ToolHelper.getJdwpTestsJarPath(minSdk);
+ Path dexOutputDir = temp.newFolder("d8-jdwp-jar").toPath();
+ jdwpDexD8 = dexOutputDir.resolve("classes.dex");
+ D8.run(
+ D8Command.builder()
+ .addProgramFiles(jdwpJar)
+ .setOutputPath(dexOutputDir)
+ .setMinApiLevel(minSdk)
+ .setMode(CompilationMode.DEBUG)
+ .build());
+ }
+ {
+ Path dexOutputDir = temp.newFolder("d8-debuggee-jar").toPath();
+ debuggeeDexD8 = dexOutputDir.resolve("classes.dex");
+ D8.run(
+ D8Command.builder()
+ .addProgramFiles(DEBUGGEE_JAR)
+ .setOutputPath(dexOutputDir)
+ .setMinApiLevel(minSdk)
+ .setMode(CompilationMode.DEBUG)
+ .build());
+ }
+ }
+
+ protected final void runDebugTest(String debuggeeClass, JUnit3Wrapper.Command... commands)
+ throws Throwable {
+ runDebugTest(debuggeeClass, Arrays.asList(commands));
+ }
+
+ protected final void runDebugTest(String debuggeeClass, List<JUnit3Wrapper.Command> commands)
+ throws Throwable {
+ // Skip test due to unsupported runtime.
+ Assume.assumeTrue("Skipping test " + testName.getMethodName() + " because ART is not supported",
+ ToolHelper.artSupported());
+ Assume.assumeFalse(
+ "Skipping failing test " + testName.getMethodName() + " for runtime " + ToolHelper
+ .getDexVm(), UNSUPPORTED_ART_VERSIONS.contains(ToolHelper.getDexVm()));
+
+ // Run with ART.
+ String[] paths = new String[]{jdwpDexD8.toString(), debuggeeDexD8.toString()};
+ new JUnit3Wrapper(debuggeeClass, paths, commands).runBare();
+ }
+
+ protected final JUnit3Wrapper.Command run() {
+ return new JUnit3Wrapper.Command.RunCommand();
+ }
+
+ protected final JUnit3Wrapper.Command breakpoint(String className, String methodName) {
+ return new JUnit3Wrapper.Command.BreakpointCommand(className, methodName);
+ }
+
+ protected final JUnit3Wrapper.Command stepOver() {
+ return stepOver(DEFAULT_FILTER);
+ }
+
+ protected final JUnit3Wrapper.Command stepOver(StepFilter stepFilter) {
+ return step(StepDepth.OVER, stepFilter);
+ }
+
+ protected final JUnit3Wrapper.Command stepOut() {
+ return stepOut(DEFAULT_FILTER);
+ }
+
+ protected final JUnit3Wrapper.Command stepOut(StepFilter stepFilter) {
+ return step(StepDepth.OUT, stepFilter);
+ }
+
+ protected final JUnit3Wrapper.Command stepInto() {
+ return stepInto(DEFAULT_FILTER);
+ }
+
+ protected final JUnit3Wrapper.Command stepInto(StepFilter stepFilter) {
+ return step(StepDepth.INTO, stepFilter);
+ }
+
+ private JUnit3Wrapper.Command step(byte stepDepth,
+ StepFilter stepFilter) {
+ return new JUnit3Wrapper.Command.StepCommand(stepDepth, stepFilter);
+ }
+
+ protected final JUnit3Wrapper.Command checkLocal(String localName, Value expectedValue) {
+ return inspect(t -> t.checkLocal(localName, expectedValue));
+ }
+
+ protected final JUnit3Wrapper.Command checkNoLocal() {
+ return inspect(t -> Assert.assertTrue(t.getLocalNames().isEmpty()));
+ }
+
+ protected final JUnit3Wrapper.Command checkLine(int line) {
+ return inspect(t -> t.checkLine(line));
+ }
+
+ protected final JUnit3Wrapper.Command checkMethod(String className, String methodName) {
+ return inspect(t -> t.checkMethod(className, methodName));
+ }
+
+ protected final JUnit3Wrapper.Command inspect(Consumer<JUnit3Wrapper.DebuggeeState> inspector) {
+ return t -> inspector.accept(t.debuggeeState);
+ }
+
+ protected final JUnit3Wrapper.Command setLocal(String localName, Value newValue) {
+ return new JUnit3Wrapper.Command.SetLocalCommand(localName, newValue);
+ }
+
+ @Ignore("Prevents Gradle from running the wrapper as a test.")
+ static class JUnit3Wrapper extends JDWPTestCase {
+
+ private final String debuggeeClassName;
+
+ private final String[] debuggeePath;
+
+ // Initially, the runtime is suspended so we're ready to process commands.
+ private State state = State.ProcessCommand;
+
+ /**
+ * Represents the context of the debuggee suspension. This is {@code null} when the debuggee is
+ * not suspended.
+ */
+ private DebuggeeState debuggeeState = null;
+
+ private final Queue<Command> commandsQueue;
+
+ // Active event requests.
+ private final Map<Integer, EventHandler> events = new TreeMap<>();
+
+ JUnit3Wrapper(String debuggeeClassName, String[] debuggeePath, List<Command> commands) {
+ this.debuggeeClassName = debuggeeClassName;
+ this.debuggeePath = debuggeePath;
+ this.commandsQueue = new ArrayDeque<>(commands);
+ }
+
+ @Override
+ protected void runTest() throws Throwable {
+ if (DEBUG_TESTS) {
+ logWriter.println("Starts loop with " + commandsQueue.size() + " command(s) to process");
+ }
+
+ boolean exited = false;
+ while (!exited) {
+ if (DEBUG_TESTS) {
+ logWriter.println("Loop on state " + state.name());
+ }
+ switch (state) {
+ case ProcessCommand: {
+ Command command = commandsQueue.poll();
+ assert command != null;
+ if (DEBUG_TESTS) {
+ logWriter.println("Process command " + command.toString());
+ }
+ command.perform(this);
+ break;
+ }
+ case WaitForEvent:
+ processEvents();
+ break;
+ case Exit:
+ exited = true;
+ break;
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ assertTrue("All commands have NOT been processed", commandsQueue.isEmpty());
+
+ logWriter.println("Finish loop");
+ }
+
+ @Override
+ protected String getDebuggeeClassName() {
+ return debuggeeClassName;
+ }
+
+ private enum State {
+ /**
+ * Process next command
+ */
+ ProcessCommand,
+ /**
+ * Wait for the next event
+ */
+ WaitForEvent,
+ /**
+ * The debuggee has exited
+ */
+ Exit
+ }
+
+ private void processEvents() {
+ EventPacket eventPacket = getMirror().receiveEvent();
+ ParsedEvent[] parsedEvents = ParsedEvent.parseEventPacket(eventPacket);
+ if (DEBUG_TESTS) {
+ logWriter.println("Received " + parsedEvents.length + " event(s)");
+ for (int i = 0; i < parsedEvents.length; ++i) {
+ String msg = String.format("#%d: %s (id=%d)", Integer.valueOf(i),
+ JDWPConstants.EventKind.getName(parsedEvents[i].getEventKind()),
+ Integer.valueOf(parsedEvents[i].getRequestID()));
+ logWriter.println(msg);
+ }
+ }
+ // We only expect one event at a time.
+ assertEquals(1, parsedEvents.length);
+ ParsedEvent parsedEvent = parsedEvents[0];
+ byte eventKind = parsedEvent.getEventKind();
+ int requestID = parsedEvent.getRequestID();
+
+ if (eventKind == JDWPConstants.EventKind.VM_DEATH) {
+ // Special event when debuggee is about to terminate.
+ assertEquals(0, requestID);
+ setState(State.Exit);
+ } else {
+ assert parsedEvent.getSuspendPolicy() == SuspendPolicy.ALL;
+
+ // Capture the context of the event suspension.
+ updateEventContext((EventThread) parsedEvent);
+
+ // Handle event.
+ EventHandler eh = events.get(requestID);
+ assert eh != null;
+ eh.handle(this);
+ }
+ }
+
+ //
+ // Inspection
+ //
+
+ /**
+ * Allows to inspect the state of a debuggee when it is suspended.
+ */
+ public class DebuggeeState {
+
+ private final long threadId;
+ private final long frameId;
+ private final Location location;
+
+ public DebuggeeState(long threadId, long frameId, Location location) {
+ this.threadId = threadId;
+ this.frameId = frameId;
+ this.location = location;
+ }
+
+ public long getThreadId() {
+ return threadId;
+ }
+
+ public long getFrameId() {
+ return frameId;
+ }
+
+ public Location getLocation() {
+ return this.location;
+ }
+
+ public void checkLocal(String localName, Value expectedValue) {
+ Variable localVar = getVariableAt(getLocation(), localName);
+
+ // Get value
+ CommandPacket commandPacket = new CommandPacket(
+ JDWPCommands.StackFrameCommandSet.CommandSetID,
+ JDWPCommands.StackFrameCommandSet.GetValuesCommand);
+ commandPacket.setNextValueAsThreadID(getThreadId());
+ commandPacket.setNextValueAsFrameID(getFrameId());
+ commandPacket.setNextValueAsInt(1);
+ commandPacket.setNextValueAsInt(localVar.getSlot());
+ commandPacket.setNextValueAsByte(localVar.getTag());
+ ReplyPacket replyPacket = getMirror().performCommand(commandPacket);
+ checkReplyPacket(replyPacket, "StackFrame.GetValues command");
+ int valuesCount = replyPacket.getNextValueAsInt();
+ assert valuesCount == 1;
+ Value localValue = replyPacket.getNextValueAsValue();
+ assertAllDataRead(replyPacket);
+
+ Assert.assertEquals(expectedValue, localValue);
+ }
+
+ public void checkLine(int line) {
+ Location location = getLocation();
+ int currentLine = getMirror()
+ .getLineNumber(location.classID, location.methodID, location.index);
+ Assert.assertEquals(line, currentLine);
+ }
+
+ public List<String> getLocalNames() {
+ return getVariablesAt(location).stream().map(v -> v.getName()).collect(Collectors.toList());
+ }
+
+ public void checkMethod(String className, String methodName) {
+ String currentClassSig = getMirror().getClassSignature(location.classID);
+ assert currentClassSig.charAt(0) == 'L';
+ String currentClassName = currentClassSig.substring(1, currentClassSig.length() - 1)
+ .replace('/', '.');
+ Assert.assertEquals("Incorrect class name", className, currentClassName);
+
+ String currentMethodName = getMirror().getMethodName(location.classID, location.methodID);
+ Assert.assertEquals("Incorrect method name", methodName, currentMethodName);
+ }
+ }
+
+ private static boolean inScope(long index, Variable var) {
+ long varStart = var.getCodeIndex();
+ long varEnd = varStart + var.getLength();
+ return index >= varStart && index < varEnd;
+ }
+
+ private Variable getVariableAt(Location location, String localName) {
+ return getVariablesAt(location).stream()
+ .filter(v -> localName.equals(v.getName()))
+ .findFirst()
+ .get();
+ }
+
+ private List<Variable> getVariablesAt(Location location) {
+ // Get variable table and keep only variables visible at this location.
+ return getVariables(location.classID, location.methodID).stream()
+ .filter(v -> inScope(location.index, v))
+ .collect(Collectors.toList());
+ }
+
+ private List<Variable> getVariables(long classID, long methodID) {
+ List<Variable> list = getMirror().getVariableTable(classID, methodID);
+ return list != null ? list : Collections.emptyList();
+ }
+
+ private void setState(State state) {
+ this.state = state;
+ }
+
+ public DebuggeeState getDebuggeeState() {
+ return debuggeeState;
+ }
+
+ private void updateEventContext(EventThread event) {
+ long threadId = event.getThreadID();
+ long frameId = -1;
+ Location location = null;
+ // ART returns an error if we ask for frames when there is none. Workaround by asking the frame
+ // count first.
+ int frameCount = getMirror().getFrameCount(threadId);
+ if (frameCount > 0) {
+ ReplyPacket replyPacket = getMirror().getThreadFrames(threadId, 0, 1);
+ {
+ int number = replyPacket.getNextValueAsInt();
+ assertEquals(1, number);
+ }
+ frameId = replyPacket.getNextValueAsFrameID();
+ location = replyPacket.getNextValueAsLocation();
+ assertAllDataRead(replyPacket);
+ }
+ debuggeeState = new DebuggeeState(threadId, frameId, location);
+ }
+
+ private VmMirror getMirror() {
+ return debuggeeWrapper.vmMirror;
+ }
+
+ private void resume() {
+ debuggeeState = null;
+ getMirror().resume();
+ setState(State.WaitForEvent);
+ }
+
+ private boolean installBreakpoint(BreakpointInfo breakpointInfo) {
+ final long classId = getMirror().getClassID(getClassSignature(breakpointInfo.className));
+ if (classId == -1) {
+ // The class is not ready yet. Request a CLASS_PREPARE to delay the installation of the
+ // breakpoint.
+ ReplyPacket replyPacket = getMirror().setClassPrepared(breakpointInfo.className);
+ int classPrepareRequestId = replyPacket.getNextValueAsInt();
+ assertAllDataRead(replyPacket);
+ events.put(Integer.valueOf(classPrepareRequestId),
+ new ClassPrepareHandler(breakpointInfo, classPrepareRequestId));
+ return false;
+ } else {
+ int breakpointId = getMirror()
+ .setBreakpointAtMethodBegin(classId, breakpointInfo.methodName);
+ // Nothing to do on breakpoint
+ events.put(Integer.valueOf(breakpointId), new DefaultEventHandler());
+ return true;
+ }
+ }
+
+ //
+ // Command processing
+ //
+ public interface Command {
+
+ void perform(JUnit3Wrapper testBase);
+
+ class RunCommand implements Command {
+
+ @Override
+ public void perform(JUnit3Wrapper testBase) {
+ testBase.resume();
+ }
+
+ @Override
+ public String toString() {
+ return "run";
+ }
+ }
+
+ // TODO(shertz) add method signature support (when multiple methods have the same name)
+ class BreakpointCommand implements Command {
+
+ private final String className;
+ private final String methodName;
+
+ public BreakpointCommand(String className, String methodName) {
+ assert className != null;
+ assert methodName != null;
+ this.className = className;
+ this.methodName = methodName;
+ }
+
+ @Override
+ public void perform(JUnit3Wrapper testBase) {
+ testBase.installBreakpoint(new BreakpointInfo(className, methodName));
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("breakpoint");
+ sb.append(" class=");
+ sb.append(className);
+ sb.append(" method=");
+ sb.append(methodName);
+ return sb.toString();
+ }
+ }
+
+ class StepCommand implements Command {
+
+ private final byte stepDepth;
+ private final StepFilter stepFilter;
+
+ public StepCommand(byte stepDepth,
+ StepFilter stepFilter) {
+ this.stepDepth = stepDepth;
+ this.stepFilter = stepFilter;
+ }
+
+ @Override
+ public void perform(JUnit3Wrapper testBase) {
+ long threadId = testBase.getDebuggeeState().getThreadId();
+ int stepRequestID;
+ {
+ EventBuilder eventBuilder = Event.builder(EventKind.SINGLE_STEP, SuspendPolicy.ALL);
+ eventBuilder.setStep(threadId, StepSize.LINE, stepDepth);
+ stepFilter.getExcludedClasses().stream().forEach(s -> eventBuilder.setClassExclude(s));
+ ReplyPacket replyPacket = testBase.getMirror().setEvent(eventBuilder.build());
+ stepRequestID = replyPacket.getNextValueAsInt();
+ testBase.assertAllDataRead(replyPacket);
+ }
+ testBase.events.put(stepRequestID, new StepEventHandler(stepRequestID, stepFilter));
+
+ // Resume all threads.
+ testBase.resume();
+ }
+
+ @Override
+ public String toString() {
+ return "step " + JDWPConstants.StepDepth.getName(stepDepth);
+ }
+ }
+
+ class SetLocalCommand implements Command {
+
+ private final String localName;
+ private final Value newValue;
+
+ public SetLocalCommand(String localName, Value newValue) {
+ this.localName = localName;
+ this.newValue = newValue;
+ }
+
+ @Override
+ public void perform(JUnit3Wrapper testBase) {
+ Variable v = testBase.getVariableAt(testBase.debuggeeState.location, localName);
+ CommandPacket setValues = new CommandPacket(StackFrameCommandSet.CommandSetID,
+ StackFrameCommandSet.SetValuesCommand);
+ setValues.setNextValueAsThreadID(testBase.getDebuggeeState().getThreadId());
+ setValues.setNextValueAsFrameID(testBase.getDebuggeeState().getFrameId());
+ setValues.setNextValueAsInt(1);
+ setValues.setNextValueAsInt(v.getSlot());
+ setValues.setNextValueAsValue(newValue);
+ ReplyPacket replyPacket = testBase.getMirror().performCommand(setValues);
+ testBase.checkReplyPacket(replyPacket, "StackFrame.SetValues");
+ }
+ }
+ }
+
+ //
+ // Event handling
+ //
+ private interface EventHandler {
+
+ void handle(JUnit3Wrapper testBase);
+ }
+
+ private static class DefaultEventHandler implements EventHandler {
+
+ @Override
+ public void handle(JUnit3Wrapper testBase) {
+ testBase.setState(State.ProcessCommand);
+ }
+ }
+
+ private static class StepEventHandler extends DefaultEventHandler {
+
+ private final int stepRequestID;
+ private final StepFilter stepFilter;
+
+ private StepEventHandler(int stepRequestID,
+ StepFilter stepFilter) {
+ this.stepRequestID = stepRequestID;
+ this.stepFilter = stepFilter;
+ }
+
+ @Override
+ public void handle(JUnit3Wrapper testBase) {
+ if (stepFilter
+ .skipLocation(testBase.getMirror(), testBase.getDebuggeeState().getLocation())) {
+ // Keep the step active and resume so that we do another step.
+ testBase.resume();
+ } else {
+ // When hit, the single step must be cleared.
+ testBase.getMirror().clearEvent(EventKind.SINGLE_STEP, stepRequestID);
+ testBase.events.remove(Integer.valueOf(stepRequestID));
+ super.handle(testBase);
+ }
+ }
+ }
+
+ private static class BreakpointInfo {
+
+ private final String className;
+ private final String methodName;
+
+ private BreakpointInfo(String className, String methodName) {
+ this.className = className;
+ this.methodName = methodName;
+ }
+ }
+
+ /**
+ * CLASS_PREPARE signals us that we can install a breakpoint
+ */
+ private static class ClassPrepareHandler implements EventHandler {
+
+ private final BreakpointInfo breakpointInfo;
+ private final int classPrepareRequestId;
+
+ private ClassPrepareHandler(BreakpointInfo breakpointInfo, int classPrepareRequestId) {
+ this.breakpointInfo = breakpointInfo;
+ this.classPrepareRequestId = classPrepareRequestId;
+ }
+
+ @Override
+ public void handle(JUnit3Wrapper testBase) {
+ // Remove the CLASS_PREPARE
+ testBase.events.remove(Integer.valueOf(classPrepareRequestId));
+ testBase.getMirror().clearEvent(JDWPConstants.EventKind.CLASS_PREPARE,
+ classPrepareRequestId);
+
+ // Install breakpoint now.
+ boolean success = testBase.installBreakpoint(breakpointInfo);
+ Assert.assertTrue("Failed to insert breakpoint after class has been prepared", success);
+
+ // Resume now
+ testBase.resume();
+ }
+ }
+
+ @Override
+ protected JPDATestOptions createTestOptions() {
+ if (RUN_DEBUGGEE_WITH_JAVA) {
+ return super.createTestOptions();
+ } else {
+ // Override properties to run debuggee with ART/Dalvik.
+ class ArtTestOptions extends JPDATestOptions {
+
+ ArtTestOptions(String[] debuggeePath) {
+ // Set debuggee command-line.
+ if (!RUN_DEBUGGEE_WITH_JAVA) {
+ ArtCommandBuilder artCommandBuilder = new ArtCommandBuilder(ToolHelper.getDexVm());
+ if (ToolHelper.getDexVm().isNewerThan(DexVm.ART_5_1_1)) {
+ artCommandBuilder.appendArtOption("-Xcompiler-option");
+ artCommandBuilder.appendArtOption("--debuggable");
+ artCommandBuilder.appendArtOption("-Xcompiler-option");
+ artCommandBuilder.appendArtOption("--compiler-filter=interpret-only");
+ }
+ setProperty("jpda.settings.debuggeeJavaPath", artCommandBuilder.build());
+
+ // Set debuggee classpath
+ String debuggeeClassPath = String.join(File.pathSeparator, debuggeePath);
+ setProperty("jpda.settings.debuggeeClasspath", debuggeeClassPath);
+ }
+
+ // Set verbosity
+ setProperty("jpda.settings.verbose", Boolean.toString(DEBUG_TESTS));
+ }
+ }
+ return new ArtTestOptions(debuggeePath);
+ }
+ }
+ }
+
+ //
+ // Step filtering
+ //
+
+ interface StepFilter {
+
+ /**
+ * Provides a list of class name to be skipped when single stepping. This can be a fully
+ * qualified name (like java.lang.String) or a subpackage (like java.util.*).
+ */
+ List<String> getExcludedClasses();
+
+ /**
+ * Indicates whether the given location must be skipped.
+ */
+ boolean skipLocation(VmMirror mirror, Location location);
+
+ /**
+ * A {@link StepFilter} that does not filter anything.
+ */
+ class NoStepFilter implements StepFilter {
+
+ @Override
+ public List<String> getExcludedClasses() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean skipLocation(VmMirror mirror, Location location) {
+ return false;
+ }
+ }
+
+ /**
+ * A {@link StepFilter} that matches the default behavior of IntelliJ regarding single
+ * stepping.
+ */
+ class IntelliJStepFilter implements StepFilter {
+ // This is the value specified by JDWP in documentation of ReferenceType.Methods command.
+ private static final int SYNTHETIC_FLAG = 0xF0000000;
+
+ @Override
+ public List<String> getExcludedClasses() {
+ return Arrays.asList(
+ "com.sun.*",
+ "java.*",
+ "javax.*",
+ "org.omg.*",
+ "sun.*",
+ "jdk.internal.*",
+ "junit.*",
+ "com.intellij.rt.*",
+ "com.yourkit.runtime.*",
+ "com.springsource.loaded.*",
+ "org.springsource.loaded.*",
+ "javassist.*",
+ "org.apache.webbeans.*",
+ "com.ibm.ws.*",
+ "kotlin.*"
+ );
+ }
+
+ @Override
+ public boolean skipLocation(VmMirror mirror, Location location) {
+ // TODO(shertz) we also need to skip class loaders to act like IntelliJ.
+ // Skip synthetic methods.
+ return isSyntheticMethod(mirror, location);
+ }
+
+ private static boolean isSyntheticMethod(VmMirror mirror, Location location) {
+ // We must gather the modifiers of the method. This is only possible using
+ // ReferenceType.Methods command which gather information about all methods in a class.
+ ReplyPacket reply = mirror.getMethods(location.classID);
+ int methodsCount = reply.getNextValueAsInt();
+ for (int i = 0; i < methodsCount; ++i) {
+ long methodId = reply.getNextValueAsMethodID();
+ reply.getNextValueAsString(); // skip method name
+ reply.getNextValueAsString(); // skip method signature
+ int modifiers = reply.getNextValueAsInt();
+ if (methodId == location.methodID &&
+ ((modifiers & SYNTHETIC_FLAG) != 0)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestExamples.java b/src/test/java/com/android/tools/r8/debug/DebugTestExamples.java
new file mode 100644
index 0000000..39091fb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestExamples.java
@@ -0,0 +1,62 @@
+// 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.debug;
+
+import java.util.Collections;
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.junit.Test;
+
+/**
+ * Examples of debug test features.
+ */
+public class DebugTestExamples extends DebugTestBase {
+
+ /**
+ * Simple test that runs the debuggee until it exits.
+ */
+ @Test
+ public void testRun() throws Throwable {
+ runDebugTest("Arithmetic", Collections.singletonList(run()));
+ }
+
+ /**
+ * Tests that we do suspend on breakpoint then continue.
+ */
+ @Test
+ public void testBreakpoint_Hit() throws Throwable {
+ runDebugTest("Arithmetic",
+ breakpoint("Arithmetic", "bitwiseInts"),
+ run(),
+ run());
+ }
+
+ /**
+ * Tests that we can check local variables at a suspension point (breakpoint).
+ */
+ @Test
+ public void testLocalsOnBreakpoint() throws Throwable {
+ runDebugTest("Arithmetic",
+ breakpoint("Arithmetic", "bitwiseInts"),
+ run(),
+ checkLocal("x", Value.createInt(12345)),
+ checkLocal("y", Value.createInt(54321)),
+ run());
+ }
+
+ /**
+ * Tests that we can check local variables at different suspension points (breakpoint then step).
+ */
+ @Test
+ public void testLocalsOnBreakpointThenStep() throws Throwable {
+ runDebugTest("Arithmetic",
+ breakpoint("Arithmetic", "bitwiseInts"),
+ run(),
+ checkLocal("x", Value.createInt(12345)),
+ checkLocal("y", Value.createInt(54321)),
+ stepOver(),
+ checkLocal("x", Value.createInt(12345)),
+ checkLocal("y", Value.createInt(54321)),
+ run());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/ExceptionTest.java b/src/test/java/com/android/tools/r8/debug/ExceptionTest.java
new file mode 100644
index 0000000..96f4499
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/ExceptionTest.java
@@ -0,0 +1,24 @@
+// 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.debug;
+
+import org.junit.Test;
+
+/**
+ * Tests debugging behavior with regards to exception handling
+ */
+public class ExceptionTest extends DebugTestBase {
+
+ @Test
+ public void testStepOnCatch() throws Throwable {
+ runDebugTest("Exceptions",
+ breakpoint("Exceptions", "catchException"),
+ run(),
+ checkLine(9), // line of the method call throwing the exception
+ stepOver(),
+ checkLine(10), // line of the catch declaration
+ run());
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
new file mode 100644
index 0000000..0d72ab9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -0,0 +1,149 @@
+// 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.debug;
+
+import com.android.tools.r8.debug.DebugTestBase;
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.junit.Test;
+
+/**
+ * Tests local variable information.
+ */
+public class LocalsTest extends DebugTestBase {
+
+ @Test
+ public void testNoLocal() throws Throwable {
+ final String className = "Locals";
+ final String methodName = "noLocals";
+ runDebugTest(className,
+ breakpoint(className, methodName),
+ run(),
+ checkMethod(className, methodName),
+ checkLine(8),
+ checkNoLocal(),
+ stepOver(),
+ checkMethod(className, methodName),
+ checkLine(9),
+ checkNoLocal(),
+ run());
+ }
+
+ @Test
+ public void testUnusedLocal() throws Throwable {
+ final String className = "Locals";
+ final String methodName = "unusedLocals";
+ runDebugTest(className,
+ breakpoint(className, methodName),
+ run(),
+ checkMethod(className, methodName),
+ checkLine(12),
+ checkNoLocal(),
+ stepOver(),
+ checkLine(13),
+ checkLocal("i", Value.createInt(Integer.MAX_VALUE)),
+ run());
+ }
+
+ @Test
+ public void testConstantLocal() throws Throwable {
+ final String className = "Locals";
+ final String methodName = "constantLocals";
+ Value pValue = Value.createInt(10);
+ Value cValue = Value.createInt(5);
+ Value vValue = Value.createInt(pValue.getIntValue() + cValue.getIntValue());
+
+ runDebugTest(className,
+ breakpoint(className, methodName),
+ run(),
+ checkMethod(className, methodName),
+ checkLine(17),
+ checkLocal("p", pValue),
+ stepOver(),
+ checkLine(18),
+ checkLocal("p", pValue),
+ checkLocal("c", cValue),
+ stepOver(),
+ checkLine(19),
+ checkLocal("p", pValue),
+ checkLocal("c", cValue),
+ checkLocal("v", vValue),
+ run());
+ }
+
+ @Test
+ public void testConstantLocalWithUpdate() throws Throwable {
+ final String className = "Locals";
+ final String methodName = "constantLocals";
+ Value pValue = Value.createInt(10);
+ Value cValue = Value.createInt(5);
+ Value newValue = Value.createInt(5);
+ Value vValue = Value.createInt(pValue.getIntValue() + newValue.getIntValue());
+
+ runDebugTest(className,
+ breakpoint(className, methodName),
+ run(),
+ checkMethod(className, methodName),
+ checkLine(17),
+ checkLocal("p", pValue),
+ stepOver(),
+ checkLine(18),
+ checkLocal("p", pValue),
+ checkLocal("c", cValue),
+ setLocal("c", newValue),
+ checkLocal("c", newValue), // we should see the updated value
+ stepOver(),
+ checkLine(19),
+ checkLocal("p", pValue),
+ checkLocal("c", newValue),
+ checkLocal("v", vValue),
+ run());
+ }
+
+ @Test
+ public void testZeroLocals() throws Throwable {
+ final String className = "Locals";
+ final String methodName = "zeroLocals";
+ final Value newValueForI = Value.createInt(10);
+ runDebugTest(className,
+ breakpoint(className, methodName),
+ run(),
+ checkMethod(className, methodName),
+ checkLine(23),
+ checkNoLocal(),
+ stepOver(),
+ checkMethod(className, methodName),
+ checkLine(24),
+ checkLocal("i", Value.createInt(0)),
+ setLocal("i", newValueForI),
+ stepOver(),
+ checkLine(25),
+ checkLocal("i", newValueForI),
+ checkLocal("f", Value.createFloat(0)),
+ run());
+ }
+
+ @Test
+ public void testNoFlowOptimization() throws Throwable {
+ final String className = "Locals";
+ final String methodName = "noFlowOptimization";
+ final Value oldValueForI = Value.createInt(0);
+ final Value newValueForI = Value.createInt(10);
+ runDebugTest(className,
+ breakpoint(className, methodName),
+ run(),
+ checkMethod(className, methodName),
+ checkLine(29),
+ checkNoLocal(),
+ stepOver(),
+ checkMethod(className, methodName),
+ checkLine(30),
+ checkLocal("i", oldValueForI),
+ setLocal("i", newValueForI),
+ stepOver(),
+ checkLine(33),
+ checkLocal("i", newValueForI),
+ run());
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/debug/MultipleReturnsTest.java b/src/test/java/com/android/tools/r8/debug/MultipleReturnsTest.java
new file mode 100644
index 0000000..0b7973e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/MultipleReturnsTest.java
@@ -0,0 +1,25 @@
+// 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.debug;
+
+import org.junit.Test;
+
+/**
+ * Tests debugging of method with multiple return statements.
+ */
+public class MultipleReturnsTest extends DebugTestBase {
+
+ @Test
+ public void testMultipleReturns() throws Throwable {
+ runDebugTest("MultipleReturns",
+ breakpoint("MultipleReturns", "multipleReturns"),
+ run(),
+ stepOver(),
+ checkLine(16), // this should be the 1st return statement
+ run(),
+ stepOver(),
+ checkLine(18), // this should be the 2nd return statement
+ run());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/SyntheticMethodTest.java b/src/test/java/com/android/tools/r8/debug/SyntheticMethodTest.java
new file mode 100644
index 0000000..a5b7d4e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/SyntheticMethodTest.java
@@ -0,0 +1,40 @@
+// 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.debug;
+
+import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+
+public class SyntheticMethodTest extends DebugTestBase {
+
+ private void debugInnerAccessors(StepFilter stepFilter) throws Throwable {
+ String debuggeeClass = "InnerAccessors";
+ List<Command> commands = new ArrayList<>();
+ commands.add(breakpoint("InnerAccessors$Inner", "callPrivateMethodInOuterClass"));
+ commands.add(run());
+ commands.add(checkLine(13));
+ commands.add(stepInto(stepFilter)); // skip synthetic accessor
+ if (stepFilter == NO_FILTER) {
+ commands.add(stepInto(stepFilter));
+ }
+ commands.add(checkMethod(debuggeeClass, "privateMethod"));
+ commands.add(checkLine(8));
+ commands.add(run());
+ runDebugTest(debuggeeClass, commands);
+ }
+
+ @Test
+ public void testInnerAccessors_NoFilter() throws Throwable {
+ debugInnerAccessors(NO_FILTER);
+ }
+
+ @Test
+ public void testInnerAccessors_IntelliJ() throws Throwable {
+ debugInnerAccessors(INTELLIJ_FILTER);
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/ArgumentLocalsInLoopTest.java b/src/test/java/com/android/tools/r8/debuginfo/ArgumentLocalsInLoopTest.java
new file mode 100644
index 0000000..2288a84
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/ArgumentLocalsInLoopTest.java
@@ -0,0 +1,20 @@
+// 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.debuginfo;
+
+public class ArgumentLocalsInLoopTest {
+
+ public int foo(int x) {
+ while (true) {
+ if (x <= 0) {
+ return x;
+ }
+ --x;
+ }
+ }
+
+ public static void main(String[] args) {
+ System.out.print(new ArgumentLocalsInLoopTest().foo(42));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/ArgumentLocalsInLoopTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/ArgumentLocalsInLoopTestRunner.java
new file mode 100644
index 0000000..250bf86
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/ArgumentLocalsInLoopTestRunner.java
@@ -0,0 +1,38 @@
+// 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.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class ArgumentLocalsInLoopTestRunner extends DebugInfoTestBase {
+
+ @Test
+ public void testArgumentLocalsInLoop() throws Exception {
+ Class clazz = ArgumentLocalsInLoopTest.class;
+ AndroidApp d8App = compileWithD8(clazz);
+ AndroidApp dxApp = getDxCompiledSources();
+
+ String expected = "0";
+ assertEquals(expected, runOnJava(clazz));
+ assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+ assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+ checkFoo(inspectMethod(d8App, clazz, "int", "foo", "int"), clazz);
+ checkFoo(inspectMethod(dxApp, clazz, "int", "foo", "int"), clazz);
+ }
+
+ private void checkFoo(DebugInfoInspector info, Class clazz) {
+ String[] locals = {"this", clazz.getCanonicalName(), "x", "int"};
+ info.checkStartLine(10);
+ info.checkLineHasExactLocals(10, locals);
+ info.checkLineHasExactLocals(11, locals);
+ info.checkNoLine(12);
+ info.checkLineHasExactLocals(13, locals);
+ info.checkNoLine(14);
+ info.checkNoLine(15);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/BackBranchToSelfTest.java b/src/test/java/com/android/tools/r8/debuginfo/BackBranchToSelfTest.java
new file mode 100644
index 0000000..c202bd3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/BackBranchToSelfTest.java
@@ -0,0 +1,20 @@
+// 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.debuginfo;
+
+public class BackBranchToSelfTest {
+
+ public static int backBranchToSelf(boolean loop) {
+ do {
+ if (loop)
+ continue;
+ }
+ while (loop);
+ return 42;
+ }
+
+ public static void main(String[] args) {
+ System.out.print(backBranchToSelf(false));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/BackBranchToSelfTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/BackBranchToSelfTestRunner.java
new file mode 100644
index 0000000..ebd1b0b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/BackBranchToSelfTestRunner.java
@@ -0,0 +1,43 @@
+// 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.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class BackBranchToSelfTestRunner extends DebugInfoTestBase {
+
+ @Test
+ public void testBackBranchToSelf() throws Exception {
+ Class clazz = BackBranchToSelfTest.class;
+
+ AndroidApp d8App = compileWithD8(clazz);
+ AndroidApp dxApp = getDxCompiledSources();
+
+ String expected = "42";
+ assertEquals(expected, runOnJava(clazz));
+ assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+ assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+ checkBackBranchToSelf(inspectMethod(d8App, clazz, "int", "backBranchToSelf", "boolean"), false);
+ checkBackBranchToSelf(inspectMethod(dxApp, clazz, "int", "backBranchToSelf", "boolean"), true);
+ }
+
+ private void checkBackBranchToSelf(DebugInfoInspector info, boolean dx) {
+ if (dx) {
+ info.checkStartLine(10);
+ // b/37494646 D8/R8 if-simplification has replaced if by a goto and lost this position.
+ info.checkLineHasExactLocals(10, "loop", "boolean");
+ } else {
+ // D8/R8 will always start at the first debuggable line.
+ info.checkStartLine(13);
+ }
+ info.checkNoLine(11);
+ info.checkNoLine(12);
+ info.checkLineHasExactLocals(13, "loop", "boolean");
+ info.checkLineHasExactLocals(14, "loop", "boolean");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/ConstantFoldingTest.java b/src/test/java/com/android/tools/r8/debuginfo/ConstantFoldingTest.java
new file mode 100644
index 0000000..60f65f2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/ConstantFoldingTest.java
@@ -0,0 +1,20 @@
+// 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.debuginfo;
+
+public class ConstantFoldingTest {
+
+ public static int foo(int x) {
+ int res = 2;
+ {
+ int tmp = res + 19;
+ res *= tmp;
+ }
+ return res / 2 + x;
+ }
+
+ public static void main(String[] args) {
+ System.out.print(foo(21));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/ConstantFoldingTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/ConstantFoldingTestRunner.java
new file mode 100644
index 0000000..a229add
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/ConstantFoldingTestRunner.java
@@ -0,0 +1,42 @@
+// 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.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class ConstantFoldingTestRunner extends DebugInfoTestBase {
+
+ @Test
+ public void testLocalsInSwitch() throws Exception {
+ Class clazz = ConstantFoldingTest.class;
+ AndroidApp d8App = compileWithD8(clazz);
+ AndroidApp dxApp = getDxCompiledSources();
+
+ String expected = "42";
+ assertEquals(expected, runOnJava(clazz));
+ assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+ assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+ checkFoo(inspectMethod(d8App, clazz, "int", "foo", "int"), false);
+ checkFoo(inspectMethod(dxApp, clazz, "int", "foo", "int"), true);
+ }
+
+ private void checkFoo(DebugInfoInspector info, boolean dx) {
+ info.checkStartLine(9);
+ info.checkLineHasExactLocals(9, "x", "int");
+ info.checkNoLine(10);
+ info.checkLineHasExactLocals(11, "x", "int", "res", "int");
+ info.checkLineHasExactLocals(12, "x", "int", "res", "int", "tmp", "int");
+ info.checkNoLine(13);
+ info.checkLineHasAtLeastLocals(14, "x", "int");
+ if (!dx) {
+ // DX fails to close the scope of "tmp".
+ info.checkLineHasExactLocals(14, "x", "int", "res", "int");
+ }
+ info.checkNoLine(15);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
new file mode 100644
index 0000000..368a8fc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
@@ -0,0 +1,170 @@
+// 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.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexDebugEntry;
+import com.android.tools.r8.graph.DexDebugEntryBuilder;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.DexInspector;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+public class DebugInfoInspector {
+
+ // Method kept here to aid inspection when debugging the tests.
+ private final DexEncodedMethod method;
+ private final List<DexDebugEntry> entries;
+
+ final DexDebugInfo info;
+
+ public DebugInfoInspector(DexEncodedMethod method, DexItemFactory factory) {
+ this.method = method;
+ info = method.getCode().asDexCode().getDebugInfo();
+ entries = new DexDebugEntryBuilder(method, factory).build();
+ checkConsistentEntries();
+ }
+
+ public DebugInfoInspector(DexInspector inspector, String clazz, MethodSignature method) {
+ this(inspector.clazz(clazz).method(method).getMethod(), inspector.getFactory());
+ }
+
+ public DebugInfoInspector(AndroidApp app, String clazz, MethodSignature method)
+ throws IOException, ExecutionException {
+ this(new DexInspector(app), clazz, method);
+ }
+
+ public void checkStartLine(int i) {
+ assertEquals(i, info.startLine);
+ }
+
+ public int checkLineExists(int line) {
+ int lines = checkLines(line, entry -> {});
+ assertTrue(lines > 0);
+ return lines;
+ }
+
+ public int checkLineHasExactLocals(int line, String... pairs) {
+ int lines = checkLines(line, entry -> checkLocalsEqual(entry, pairs));
+ assertTrue("Failed to find entry for line " + line, lines > 0);
+ return lines;
+ }
+
+ public int checkLineHasNoLocals(int line) {
+ return checkLineHasExactLocals(line);
+ }
+
+ public int checkLineHasAtLeastLocals(int line, String... pairs) {
+ int lines = checkLines(line, entry -> checkLocalsDefined(entry, pairs));
+ assertTrue(lines > 0);
+ return lines;
+ }
+
+ public void checkNoLine(int line) {
+ int lines = checkLines(line, entry -> {});
+ assertEquals(0, lines);
+ }
+
+ public int checkLineHasLocal(int line, String name, String type, String... typeParameters) {
+ int lines = checkLines(line, entry -> {
+ checkLocalDefined(entry, name, type, typeParameters);
+ });
+ assertTrue(lines > 0);
+ return lines;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ for (DexDebugEntry entry : entries) {
+ builder.append(entry).append("\n");
+ }
+ return builder.toString();
+ }
+
+ private void checkConsistentEntries() {
+ DexDebugEntry previousEntry = null;
+ for (DexDebugEntry entry : entries) {
+ if (previousEntry != null) {
+ assertTrue("More than one entry defined for PC " + entry.address,
+ entry.address > previousEntry.address);
+ }
+ previousEntry = entry;
+ }
+ }
+
+ private int checkLines(int line, Consumer<DexDebugEntry> check) {
+ int found = 0;
+ for (DexDebugEntry entry : entries) {
+ if (entry.line == line) {
+ found++;
+ check.accept(entry);
+ }
+ }
+ return found;
+ }
+
+ private static DebugLocalInfo checkLocalDefined(DexDebugEntry entry, String name, String type,
+ String... typeParameters) {
+ DebugLocalInfo found = null;
+ for (DebugLocalInfo local : entry.locals.values()) {
+ if (local.name.toString().equals(name)) {
+ if (found != null) {
+ fail("Line " + entry.line + ". Local defined multiple times for name: " + name);
+ }
+ assertEquals(type, local.type.toString());
+ if (typeParameters.length > 0) {
+ String desc = DescriptorUtils.javaTypeToDescriptor(type);
+ StringBuilder builder = new StringBuilder(desc.substring(0, desc.length() - 1));
+ builder.append("<");
+ for (String parameter : typeParameters) {
+ builder.append(parameter);
+ }
+ builder.append(">;");
+ assertEquals(builder.toString(), local.signature.toString());
+ }
+ found = local;
+ }
+ }
+ assertNotNull("Line " + entry.line + ". Failed to find local with name: " + name, found);
+ return found;
+ }
+
+ private static void checkLocalsDefined(DexDebugEntry entry, String... pairs) {
+ assert pairs.length % 2 == 0;
+ for (int i = 0; i < pairs.length; i += 2) {
+ checkLocalDefined(entry, pairs[i], pairs[i + 1]);
+ }
+ }
+
+ private static void checkLocalsEqual(DexDebugEntry entry, String[] pairs) {
+ assert pairs == null || pairs.length % 2 == 0;
+ int expected = pairs == null ? 0 : pairs.length / 2;
+ Set<DebugLocalInfo> remaining = new HashSet<>(entry.locals.values());
+ if (pairs != null) {
+ for (int i = 0; i < pairs.length; i += 2) {
+ DebugLocalInfo local = checkLocalDefined(entry, pairs[i], pairs[i + 1]);
+ remaining.remove(local);
+ }
+ }
+ assertEquals("Line " + entry.line + ". Found unexpected locals: " +
+ String.join(",", remaining.stream().map(Object::toString).collect(Collectors.toList())),
+ expected, expected + remaining.size());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
new file mode 100644
index 0000000..c16291a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
@@ -0,0 +1,67 @@
+// 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.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+public class DebugInfoTestBase {
+
+ public static final Path DX_PREBUILT =
+ Paths.get(ToolHelper.BUILD_DIR, "test", "debuginfo_examples_dex.jar");
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ static AndroidApp compileWithD8(Class... classes) throws CompilationException, IOException {
+ D8Command.Builder builder = D8Command.builder();
+ for (Class clazz : classes) {
+ builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
+ }
+ return ToolHelper.runD8(builder.setMode(CompilationMode.DEBUG).build());
+ }
+
+ static AndroidApp getDxCompiledSources() throws IOException {
+ return AndroidApp.fromProgramFiles(DX_PREBUILT);
+ }
+
+ public static DebugInfoInspector inspectMethod(
+ AndroidApp app, Class type, String returnType, String methodName, String... parameterTypes)
+ throws IOException, ExecutionException {
+ return new DebugInfoInspector(
+ app, type.getCanonicalName(), new MethodSignature(methodName, returnType, parameterTypes));
+ }
+
+ protected String runOnArt(AndroidApp app, String main) throws IOException {
+ Path out = temp.getRoot().toPath().resolve("out.zip");
+ app.writeToZip(out, true);
+ return ToolHelper.runArtNoVerificationErrors(ImmutableList.of(out.toString()), main, null);
+ }
+
+ protected String runOnJava(Class clazz) throws Exception {
+ ProcessResult result = ToolHelper.runJava(clazz);
+ if (result.exitCode != 0) {
+ System.out.println("Std out:");
+ System.out.println(result.stdout);
+ System.out.println("Std err:");
+ System.out.println(result.stderr);
+ assertEquals(0, result.exitCode);
+ }
+ return result.stdout;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalSwapTest.java b/src/test/java/com/android/tools/r8/debuginfo/LocalSwapTest.java
new file mode 100644
index 0000000..6df6d99
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalSwapTest.java
@@ -0,0 +1,21 @@
+// 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.debuginfo;
+
+public class LocalSwapTest {
+
+ public static int foo(int x, int y) {
+ int sum = x + y;
+ {
+ int t = x;
+ x = y;
+ y = t;
+ }
+ return sum + x + y;
+ }
+
+ public static void main(String[] args) {
+ System.out.print(foo(1, 2));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalSwapTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/LocalSwapTestRunner.java
new file mode 100644
index 0000000..0ee33bf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalSwapTestRunner.java
@@ -0,0 +1,40 @@
+// 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.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class LocalSwapTestRunner extends DebugInfoTestBase {
+
+ @Test
+ public void testLocalSwap() throws Exception {
+ Class clazz = LocalSwapTest.class;
+ AndroidApp d8App = compileWithD8(clazz);
+ AndroidApp dxApp = getDxCompiledSources();
+
+ String expected = "6";
+ assertEquals(expected, runOnJava(clazz));
+ assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+ assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+ checkFoo(inspectMethod(d8App, clazz, "int", "foo", "int", "int"), false);
+ checkFoo(inspectMethod(dxApp, clazz, "int", "foo", "int", "int"), true);
+ }
+
+ private void checkFoo(DebugInfoInspector info, boolean dx) {
+ info.checkStartLine(9);
+ info.checkLineHasExactLocals(9, "x", "int", "y", "int");
+ info.checkLineHasExactLocals(11, "x", "int", "y", "int", "sum", "int");
+ info.checkLineHasExactLocals(12, "x", "int", "y", "int", "sum", "int", "t", "int");
+ info.checkLineExists(13);
+ info.checkLineExists(15);
+ if (!dx) {
+ // DX fails to close the scope of local "t".
+ info.checkLineHasExactLocals(15, "x", "int", "y", "int", "sum", "int");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalsAtThrowTest.java b/src/test/java/com/android/tools/r8/debuginfo/LocalsAtThrowTest.java
new file mode 100644
index 0000000..b07fab8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalsAtThrowTest.java
@@ -0,0 +1,23 @@
+// 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.debuginfo;
+
+public class LocalsAtThrowTest {
+
+ public static int localsAtThrow(int x) {
+ int a = 1;
+ int b = 2;
+ switch (x % 3) {
+ case 1:
+ throw new RuntimeException();
+ case 2:
+ return a + b;
+ }
+ return 42;
+ }
+
+ public static void main(String[] args) {
+ System.out.print(localsAtThrow(11));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalsAtThrowTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/LocalsAtThrowTestRunner.java
new file mode 100644
index 0000000..8df6f79
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalsAtThrowTestRunner.java
@@ -0,0 +1,41 @@
+// 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.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class LocalsAtThrowTestRunner extends DebugInfoTestBase {
+
+ @Test
+ public void testLocalsAtThrow() throws Exception {
+ Class clazz = LocalsAtThrowTest.class;
+
+ AndroidApp d8App = compileWithD8(clazz);
+ AndroidApp dxApp = getDxCompiledSources();
+
+ String expected = "3";
+ assertEquals(expected, runOnJava(clazz));
+ assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+ assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+ checkBackBranchToSelf(inspectMethod(d8App, clazz, "int", "localsAtThrow", "int"));
+ checkBackBranchToSelf(inspectMethod(dxApp, clazz, "int", "localsAtThrow", "int"));
+ }
+
+ private void checkBackBranchToSelf(DebugInfoInspector info) {
+ info.checkStartLine(9);
+ info.checkLineHasExactLocals(9, "x", "int");
+ info.checkLineHasExactLocals(10, "x", "int", "a", "int");
+ info.checkLineHasExactLocals(11, "x", "int", "a", "int", "b", "int");
+ info.checkNoLine(12);
+ info.checkLineHasExactLocals(13, "x", "int", "a", "int", "b", "int");
+ info.checkNoLine(14);
+ info.checkLineHasExactLocals(15, "x", "int", "a", "int", "b", "int");
+ info.checkNoLine(16);
+ info.checkLineHasExactLocals(17, "x", "int", "a", "int", "b", "int");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalsInSwitchTest.java b/src/test/java/com/android/tools/r8/debuginfo/LocalsInSwitchTest.java
new file mode 100644
index 0000000..010d517
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalsInSwitchTest.java
@@ -0,0 +1,61 @@
+// 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.debuginfo;
+
+public class LocalsInSwitchTest {
+
+ public static int noLocals(int x) {
+ switch (x) {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ default:
+ return noLocals(x - 1) + noLocals(x - 2);
+ }
+ }
+
+ public static int tempInCase(int x) {
+ int res = 0;
+ for (int i = 0; i < x; ++i) {
+ int rem = x - i;
+ switch (rem) {
+ case 1:
+ return res;
+ case 5:
+ int tmp = res + x + i;
+ res += tmp;
+ break;
+ case 10:
+ i++;
+ break;
+ default:
+ res += rem;
+ }
+ res += rem % 2;
+ }
+ res *= x;
+ return res;
+ }
+
+ public static int initInCases(int x) {
+ Integer res;
+ switch (x % 3) {
+ case 0:
+ res = 42;
+ case 1:
+ res = x;
+ case 2:
+ default:
+ res = x * x;
+ }
+ return res + 1;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(noLocals(10));
+ System.out.println(tempInCase(42));
+ System.out.println(initInCases(123));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalsInSwitchTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/LocalsInSwitchTestRunner.java
new file mode 100644
index 0000000..020809a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalsInSwitchTestRunner.java
@@ -0,0 +1,110 @@
+// 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.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class LocalsInSwitchTestRunner extends DebugInfoTestBase {
+
+ @Test
+ public void testLocalsInSwitch() throws Exception {
+ Class clazz = LocalsInSwitchTest.class;
+
+ AndroidApp d8App = compileWithD8(clazz);
+ AndroidApp dxApp = getDxCompiledSources();
+
+ String expected = "55\n1862\n15130\n";
+ assertEquals(expected, runOnJava(clazz));
+ assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+ assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+ checkNoLocals(inspectMethod(d8App, clazz, "int", "noLocals", "int"));
+ checkNoLocals(inspectMethod(dxApp, clazz, "int", "noLocals", "int"));
+
+ checkTempInCase(inspectMethod(d8App, clazz, "int", "tempInCase", "int"), false);
+ checkTempInCase(inspectMethod(dxApp, clazz, "int", "tempInCase", "int"), true);
+
+ checkInitInCases(inspectMethod(d8App, clazz, "int", "initInCases", "int"));
+ checkInitInCases(inspectMethod(dxApp, clazz, "int", "initInCases", "int"));
+ }
+
+ private void checkNoLocals(DebugInfoInspector info) {
+ info.checkStartLine(9);
+ info.checkLineHasExactLocals(9, "x", "int");
+ info.checkLineHasExactLocals(11, "x", "int");
+ info.checkLineHasExactLocals(13, "x", "int");
+ info.checkLineHasExactLocals(15, "x", "int");
+ }
+
+ private void checkTempInCase(DebugInfoInspector tempInCase, boolean dx) {
+ // int res =
+ tempInCase.checkStartLine(20);
+ tempInCase.checkLineHasExactLocals(20, "x", "int");
+ // for (int i = ...
+ // The local 'i' is visible on the back edges, but not on the initial entry.
+ tempInCase.checkLineHasAtLeastLocals(21, "x", "int", "res", "int");
+ // int rem =
+ tempInCase.checkLineHasExactLocals(22, "x", "int", "res", "int", "i", "int");
+ // switch (rem) {
+ if (!dx) {
+ // DX contains several entries for 23, one of which does not define 'rem'. Go figure...
+ tempInCase.checkLineHasExactLocals(23, "x", "int", "res", "int", "i", "int", "rem", "int");
+ }
+ // case 0:
+ tempInCase.checkNoLine(24);
+ // return res
+ if (!dx) {
+ // DX does not produce a position at the return statement. Good stuff.
+ tempInCase.checkLineHasExactLocals(25, "x", "int", "res", "int", "i", "int", "rem", "int");
+ }
+ // case 5:
+ tempInCase.checkNoLine(26);
+ // int tmp =
+ tempInCase.checkLineHasExactLocals(27, "x", "int", "res", "int", "i", "int", "rem", "int");
+ // res += tmp
+ tempInCase.checkLineHasExactLocals(28,
+ "x", "int", "res", "int", "i", "int", "rem", "int", "tmp", "int");
+ // break;
+ tempInCase.checkLineHasExactLocals(29,
+ "x", "int", "res", "int", "i", "int", "rem", "int", "tmp", "int");
+ // case 10:
+ tempInCase.checkNoLine(30);
+ // i++
+ tempInCase.checkLineHasExactLocals(31, "x", "int", "res", "int", "i", "int", "rem", "int");
+ // break;
+ tempInCase.checkLineHasExactLocals(32, "x", "int", "res", "int", "i", "int", "rem", "int");
+ // default:
+ tempInCase.checkNoLine(33);
+ // res += rem;
+ tempInCase.checkLineHasExactLocals(34, "x", "int", "res", "int", "i", "int", "rem", "int");
+ // }
+ tempInCase.checkNoLine(35);
+ // res += rem % 2;
+ tempInCase.checkLineHasExactLocals(36, "x", "int", "res", "int", "i", "int", "rem", "int");
+ // }
+ tempInCase.checkNoLine(37);
+ // res *= x;
+ if (!dx) {
+ // DX fails to end the scope of "i" after the loop.
+ tempInCase.checkLineHasExactLocals(38, "x", "int", "res", "int");
+ }
+ // return res;
+ if (!dx) {
+ tempInCase.checkLineHasExactLocals(39, "x", "int", "res", "int");
+ }
+ }
+
+ private void checkInitInCases(DebugInfoInspector info) {
+ info.checkNoLine(43); // No line on uninitialized local declaration.
+ info.checkStartLine(44);
+ info.checkLineHasExactLocals(44, "x", "int"); // Local "res" is still not visible in the case.
+ info.checkLineHasExactLocals(46, "x", "int"); // Ditto.
+ info.checkLineHasExactLocals(48, "x", "int"); // Ditto.
+ info.checkLineHasExactLocals(51, "x", "int"); // Ditto.
+ info.checkLineHasExactLocals(53, "x", "int", "res", "java.lang.Integer");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalsWithTypeParamsRunner.java b/src/test/java/com/android/tools/r8/debuginfo/LocalsWithTypeParamsRunner.java
new file mode 100644
index 0000000..2dd50ed
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalsWithTypeParamsRunner.java
@@ -0,0 +1,51 @@
+// 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.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class LocalsWithTypeParamsRunner extends DebugInfoTestBase {
+
+ static final Class clazzMain = LocalsWithTypeParamsTest.class;
+ static final Class clazzA = A.class;
+ static final Class clazzB = B.class;
+
+ static final String nameMain = clazzMain.getCanonicalName();
+ static final String nameA = clazzA.getCanonicalName();
+ static final String nameB = clazzB.getCanonicalName();
+
+ @Test
+ public void testLocalsWithTypeParams() throws Exception {
+ AndroidApp d8App = compileWithD8(clazzMain, clazzA, clazzB);
+ AndroidApp dxApp = getDxCompiledSources();
+
+ String expected = "42";
+ assertEquals(expected, runOnJava(clazzMain));
+ assertEquals(expected, runOnArt(d8App, nameMain));
+ assertEquals(expected, runOnArt(dxApp, nameMain));
+
+ checkSyncInstance(inspectMethod(d8App, clazzA, "int", "foo", nameB));
+ checkSyncInstance(inspectMethod(dxApp, clazzA, "int", "foo", nameB));
+ }
+
+ private void checkSyncInstance(DebugInfoInspector info) {
+ // Assert that the parameter entry is null since it is explicitly introduced in the stream.
+ assertEquals(1, info.info.parameters.length);
+ assertNull(info.info.parameters[0]);
+
+ info.checkStartLine(8);
+ info.checkLineHasExactLocals(8, "this", nameA, "b", nameB);
+ info.checkLineHasLocal(8, "this", nameA, "TT;");
+ info.checkLineHasLocal(8, "b", nameB, "TT;", "Ljava/lang/String;");
+
+ info.checkLineHasExactLocals(9, "this", nameA, "b", nameB, "otherB", nameB);
+ info.checkLineHasLocal(9, "this", nameA, "TT;");
+ info.checkLineHasLocal(9, "b", nameB, "TT;", "Ljava/lang/String;");
+ info.checkLineHasLocal(9, "otherB", nameB, "Ljava/lang/String;", "Ljava/lang/String;");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalsWithTypeParamsTest.java b/src/test/java/com/android/tools/r8/debuginfo/LocalsWithTypeParamsTest.java
new file mode 100644
index 0000000..ea4ac2b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalsWithTypeParamsTest.java
@@ -0,0 +1,24 @@
+// 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.debuginfo;
+
+class A<T> {
+ int foo(B<T, String> b) {
+ B<String, String> otherB = new B<String, String>();
+ return (b.foo() + otherB.foo()) / 2;
+ }
+}
+
+class B<U, V> {
+ int foo() {
+ return 42;
+ }
+}
+
+public class LocalsWithTypeParamsTest {
+
+ public static void main(String[] args) {
+ System.out.print(new A<Class>().foo(new B<Class, String>()));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/ScopedExceptionsTest.java b/src/test/java/com/android/tools/r8/debuginfo/ScopedExceptionsTest.java
new file mode 100644
index 0000000..127211f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/ScopedExceptionsTest.java
@@ -0,0 +1,26 @@
+// 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.debuginfo;
+
+public class ScopedExceptionsTest {
+
+ private static int scopedExceptions() {
+ try {
+ throwNPE();
+ }
+ catch (NullPointerException e) {}
+ catch (Throwable e) {
+ System.out.println("Unexpected...");
+ }
+ return 42;
+ }
+
+ private static void throwNPE() {
+ throw new NullPointerException();
+ }
+
+ public static void main(String[] args) {
+ System.out.print(scopedExceptions());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/ScopedExceptionsTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/ScopedExceptionsTestRunner.java
new file mode 100644
index 0000000..436334a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/ScopedExceptionsTestRunner.java
@@ -0,0 +1,45 @@
+// 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.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class ScopedExceptionsTestRunner extends DebugInfoTestBase {
+
+ @Test
+ public void testScopedException() throws Exception {
+ Class clazz = ScopedExceptionsTest.class;
+ AndroidApp d8App = compileWithD8(clazz);
+ AndroidApp dxApp = getDxCompiledSources();
+
+ String expected = "42";
+ assertEquals(expected, runOnJava(clazz));
+ assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+ assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+ checkScopedExceptions(inspectMethod(d8App, clazz, "int", "scopedExceptions"), false);
+ checkScopedExceptions(inspectMethod(dxApp, clazz, "int", "scopedExceptions"), true);
+ }
+
+ private void checkScopedExceptions(DebugInfoInspector info, boolean dx) {
+ info.checkStartLine(10);
+ info.checkLineHasNoLocals(10);
+ info.checkNoLine(11);
+ info.checkLineHasNoLocals(12);
+ info.checkLineHasNoLocals(13);
+ info.checkLineHasExactLocals(14, "e", "java.lang.Throwable");
+ // DX does not generate a position at the end of the try-catch blocks, Java does and so does D8.
+ if (!dx) {
+ info.checkLineHasNoLocals(15);
+ }
+ info.checkLineExists(16);
+ // DX will still have an local entry for 'e' after its scope has ended.
+ if (!dx) {
+ info.checkLineHasNoLocals(16);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTest.java b/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTest.java
new file mode 100644
index 0000000..be0a122
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTest.java
@@ -0,0 +1,26 @@
+// 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.debuginfo;
+
+public class SynchronizedMethodTest {
+
+ private static synchronized int syncStatic(int x) {
+ if (x % 2 == 0) {
+ return 42;
+ }
+ return -Math.abs(x);
+ }
+
+ private synchronized int syncInstance(int x) {
+ if (x % 2 == 0) {
+ return 42;
+ }
+ return -Math.abs(x);
+ }
+
+ public static void main(String[] args) {
+ System.out.println(syncStatic(1234));
+ System.out.println(new SynchronizedMethodTest().syncInstance(1234));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTestRunner.java
new file mode 100644
index 0000000..7370ca2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTestRunner.java
@@ -0,0 +1,50 @@
+// 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.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class SynchronizedMethodTestRunner extends DebugInfoTestBase {
+
+ static Class clazz = SynchronizedMethodTest.class;
+
+ @Test
+ public void testSynchronizedMethod() throws Exception {
+ AndroidApp d8App = compileWithD8(clazz);
+ AndroidApp dxApp = getDxCompiledSources();
+
+ String expected = "42\n42\n";
+ assertEquals(expected, runOnJava(clazz));
+ assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+ assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+ checkSyncStatic(inspectMethod(d8App, clazz, "int", "syncStatic", "int"));
+ checkSyncStatic(inspectMethod(dxApp, clazz, "int", "syncStatic", "int"));
+
+ checkSyncInstance(inspectMethod(d8App, clazz, "int", "syncInstance", "int"));
+ checkSyncInstance(inspectMethod(dxApp, clazz, "int", "syncInstance", "int"));
+ }
+
+ private void checkSyncStatic(DebugInfoInspector info) {
+ info.checkStartLine(9);
+ info.checkLineHasExactLocals(9, "x", "int");
+ info.checkLineHasExactLocals(10, "x", "int");
+ info.checkNoLine(11);
+ info.checkLineHasExactLocals(12, "x", "int");
+ info.checkNoLine(13);
+ }
+
+ private void checkSyncInstance(DebugInfoInspector info) {
+ String[] locals = {"this", clazz.getCanonicalName(), "x", "int"};
+ info.checkStartLine(16);
+ info.checkLineHasExactLocals(16, locals);
+ info.checkLineHasExactLocals(17, locals);
+ info.checkNoLine(18);
+ info.checkLineHasExactLocals(19, locals);
+ info.checkNoLine(20);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
new file mode 100644
index 0000000..e5581a4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexDebugEvent;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class DebugByteCodeWriterTest {
+
+ ObjectToOffsetMapping emptyObjectTObjectMapping() {
+ return new ObjectToOffsetMapping(
+ 0,
+ new Builder(new DexItemFactory(), null).build(),
+ new DexProgramClass[] {},
+ new DexProto[] {},
+ new DexType[] {},
+ new DexMethod[] {},
+ new DexField[] {},
+ new DexString[] {},
+ new DexCallSite[] {},
+ new DexMethodHandle[] {});
+ }
+
+ @Test
+ public void testEmptyDebugInfo() {
+ DexDebugInfo debugInfo = new DexDebugInfo(1, new DexString[]{}, new DexDebugEvent[]{});
+ DebugBytecodeWriter writer = new DebugBytecodeWriter(debugInfo, emptyObjectTObjectMapping());
+ Assert.assertEquals(3, writer.generate().length);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/DexItemFactoryTest.java b/src/test/java/com/android/tools/r8/dex/DexItemFactoryTest.java
new file mode 100644
index 0000000..1f9a5a4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/DexItemFactoryTest.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.dex;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import org.junit.Test;
+
+public class DexItemFactoryTest {
+
+ @Test
+ public void commonItems() {
+ DexItemFactory factory = new DexItemFactory();
+
+ Object[] data = new Object[]{
+ "B", factory.byteDescriptor, factory.byteType,
+ "C", factory.charDescriptor, factory.charType,
+ "D", factory.doubleDescriptor, factory.doubleType,
+ "F", factory.floatDescriptor, factory.floatType,
+ "I", factory.intDescriptor, factory.intType,
+ "J", factory.longDescriptor, factory.longType,
+ "S", factory.shortDescriptor, factory.shortType,
+ "V", factory.voidDescriptor, factory.voidType,
+ "Z", factory.booleanDescriptor, factory.booleanType,
+ "Ljava/lang/String;", factory.stringDescriptor, factory.stringType,
+ "Ljava/lang/Object;", factory.objectDescriptor, factory.objectType,
+ };
+
+ for (int i = 0; i < data.length; i += 3) {
+ DexString string1 = factory.createString((String) data[i]);
+ DexString string2 = factory.createString((String) data[i]);
+ DexItem type1 = factory.createType(string1);
+ DexItem type2 = factory.createType(string2);
+ DexItem expectedDexString = (DexString) data[i + 1];
+ DexItem expectedDexType = (DexType) data[i + 2];
+
+ assertSame(expectedDexString, string1);
+ assertSame(expectedDexString, string2);
+ assertSame(expectedDexType, type1);
+ assertSame(expectedDexType, type2);
+ }
+ }
+
+ @Test
+ public void getPrimitiveTypeName() {
+ DexItemFactory factory = new DexItemFactory();
+ assertEquals("boolean", factory.booleanType.getName());
+ assertEquals("byte", factory.byteType.getName());
+ assertEquals("short", factory.shortType.getName());
+ assertEquals("char", factory.charType.getName());
+ assertEquals("int", factory.intType.getName());
+ assertEquals("float", factory.floatType.getName());
+ assertEquals("long", factory.longType.getName());
+ assertEquals("double", factory.doubleType.getName());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/DexStringTest.java b/src/test/java/com/android/tools/r8/dex/DexStringTest.java
new file mode 100644
index 0000000..fa7f332
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/DexStringTest.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.dex;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import org.junit.Test;
+
+public class DexStringTest {
+
+ @Test
+ public void testEncodingLength() {
+ DexItemFactory factory = new DexItemFactory();
+ checkEncodedLength(factory.createString("\u0000"), 2);
+ checkEncodedLength(factory.createString("\u0001"), 1);
+ checkEncodedLength(factory.createString("\u007f"), 1);
+ checkEncodedLength(factory.createString("\u0080"), 2);
+ checkEncodedLength(factory.createString("\u07ff"), 2);
+ checkEncodedLength(factory.createString("\u0800"), 3);
+ checkEncodedLength(factory.createString("\uffff"), 3);
+ checkEncodedLength(factory.createString("\ud800\udc00"), 6);
+ checkEncodedLength(factory.createString("\udbff\udfff"), 6);
+ }
+
+ @Test
+ public void testCompare() {
+ DexItemFactory factory = new DexItemFactory();
+
+ // Test strings in lexicographic order.
+ DexString[] strings = {
+ factory.createString(""),
+ factory.createString("\u0000"),
+ factory.createString("\u0001"),
+ factory.createString("\u0060a"), // 'a' is 0x61.
+ factory.createString("a"),
+ factory.createString("a\u0000a"),
+ factory.createString("a\u0001a"),
+ factory.createString("a\u0060a"), // 'a' is 0x61.
+ factory.createString("aa"),
+ factory.createString("aaa"),
+ factory.createString("a\u007f"), // U+007f is the last code point with one UTF-8 bytes.
+ factory.createString("a\u007fa"),
+ factory.createString("a\u0080"), // U+0080 is the first code point with two UTF-8 bytes.
+ factory.createString("a\u0080a"),
+ factory.createString("a\u07ff"), // U+07ff is the last code point with two UTF-8 bytes.
+ factory.createString("a\u07ffa"),
+ factory.createString("a\u0800"), // U+0800 is the first code point with three UTF-8 bytes.
+ factory.createString("a\u0800a"),
+ factory.createString("a\u0801"),
+ factory.createString("a\u0801a"),
+ factory.createString("a\ud800\udc00a"), // Surrogate pair for U+010000. Sorts per UTF-16.
+ factory.createString("a\udbff\udfffa"), // Surrogate pair for U+10ffff. Sorts per UTF-16.
+ factory.createString("a\uffffa"),
+ factory.createString("\u007f"), // U+007f is the last code point with one UTF-8 bytes.
+ factory.createString("\u0080"), // U+0080 is the first code point with two UTF-8 bytes.
+ factory.createString("\u07ff"), // U+07ff is the last code point with two UTF-8 bytes.
+ factory.createString("\u0800"), // U+0800 is the first code point with three UTF-8 bytes.
+ factory.createString("\u0801"), // U+0800 is the first code point with three UTF-8 bytes.
+ factory.createString("\ud800\udc00"), // Surrogate pair for U+010000. Sorts per UTF-16.
+ factory.createString("\udbff\udfff"), // Surrogate pair for U+10ffff. Sorts per UTF-16.
+ factory.createString("\uffff"),
+ };
+
+
+ for (int i = 0; i < strings.length; i++) {
+ for (int j = 0; j < strings.length; j++) {
+ int expected = Integer.signum(i - j);
+ check(expected, strings[i], strings[j]);
+ check(-expected, strings[j], strings[i]);
+ }
+ }
+ }
+
+ private void check(int expected, DexString s1, DexString s2) {
+ assertEquals(s1.dump() + " " + s2.dump(),
+ expected, Integer.signum(s1.toString().compareTo(s2.toString())));
+ assertEquals(s1.dump() + " " + s2.dump(),
+ expected, Integer.signum(s1.slowCompareTo(s2)));
+ }
+
+ private void checkEncodedLength(DexString s, int encodedLength) {
+ // The terminating zero is not part of the encoding,
+ int length = s.content.length;
+ assertEquals(0, s.content[length - 1]);
+ assertEquals(encodedLength, length - 1);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/EncodedFloatingValueTest.java b/src/test/java/com/android/tools/r8/dex/EncodedFloatingValueTest.java
new file mode 100644
index 0000000..3a4ea04
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/EncodedFloatingValueTest.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import static com.android.tools.r8.dex.Constants.DEX_MAGIC_SIZE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.utils.EncodedValueUtils;
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EncodedFloatingValueTest {
+ private final double value;
+
+ public EncodedFloatingValueTest(double value) {
+ this.value = value;
+ }
+
+ @Parameters(name = "{0}")
+ public static Collection<Double> data() {
+ return Arrays.asList(
+ 0.0,
+ 1.0,
+ 0.5,
+ Double.longBitsToDouble(1), // Lowest bit is 1 in double
+ Double.longBitsToDouble(0x10), // Bits on byte boundary are 1.
+ Double.longBitsToDouble(0x08),
+ Double.longBitsToDouble(4607071218809329336L), // Test a real long (regression).
+ (double) (Float.intBitsToFloat(1)), // Lowest bit is 1 in float
+ (double) (Float.intBitsToFloat(0x10)), // Bits on byte boundary are 1
+ (double) (Float.intBitsToFloat(0x08))
+ );
+ }
+
+ // Create a DexFile with correct file magic followed by the argument bytes. Positions the
+ // DexFile after the file magic.
+ private DexFile createDexFileWithContent(byte[] bytes) {
+ DexOutputBuffer buffer = new DexOutputBuffer();
+ buffer.putBytes(Constants.DEX_FILE_MAGIC_PREFIX);
+ buffer.putBytes(Constants.ANDROID_PRE_N_DEX_VERSION_BYTES);
+ buffer.putByte(Constants.DEX_FILE_MAGIC_SUFFIX);
+ buffer.putBytes(bytes);
+ DexFile dexFile = new DexFile(buffer.asArray());
+ dexFile.position(DEX_MAGIC_SIZE);
+ return dexFile;
+ }
+
+ @Test
+ public void testEncodeDecodeDouble() {
+ byte[] bytes = EncodedValueUtils.encodeDouble(value);
+ assertTrue(bytes.length <= Double.BYTES);
+ DexFile dexFile = createDexFileWithContent(bytes);
+ Assert.assertEquals(value, EncodedValueUtils.parseDouble(dexFile, bytes.length), 0.0);
+ }
+
+ @Test
+ public void testEncodeDecodeFloat() {
+ byte[] bytes = EncodedValueUtils.encodeFloat((float) value);
+ assertTrue(bytes.length <= Float.BYTES);
+ DexFile dexFile = createDexFileWithContent(bytes);
+ Assert.assertEquals((float) value, EncodedValueUtils.parseFloat(dexFile, bytes.length), 0.0f);
+ }
+
+ @Test
+ public void testEncodeDecodeDoubleWithDexBuffer() {
+ DexOutputBuffer buffer = new DexOutputBuffer();
+ int length = EncodedValueUtils.putDouble(buffer, value);
+ assertTrue(length <= Double.BYTES);
+ byte[] bytes = buffer.asArray();
+ DexFile dexFile = createDexFileWithContent(bytes);
+ assertEquals(value, EncodedValueUtils.parseDouble(dexFile, length), 0.0);
+ }
+
+ @Test
+ public void testEncodeDecodeFloatWithDexBuffer() {
+ DexOutputBuffer buffer = new DexOutputBuffer();
+ int length = EncodedValueUtils.putFloat(buffer, (float) value);
+ assertTrue(length <= Float.BYTES);
+ byte[] bytes = buffer.asArray();
+ DexFile dexFile = createDexFileWithContent(bytes);
+ assertEquals((float) value, EncodedValueUtils.parseFloat(dexFile, length), 0.0f);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java b/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java
new file mode 100644
index 0000000..d2aa592
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+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;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class ExtraFileTest {
+
+ private static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_BUILD_DIR;
+ private static final String EXAMPLE_DEX = "memberrebinding/classes.dex";
+ private static final String EXAMPLE_LIB = "memberrebindinglib/classes.dex";
+ private static final String EXAMPLE_CLASS = "memberrebinding.Test";
+ private static final String EXAMPLE_PACKAGE_MAP = "memberrebinding/package.map";
+ private static final String EXAMPLE_PROGUARD_MAP = "memberrebinding/proguard.map";
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Ignore("b/38187737")
+ @Test
+ public void splitMemberRebindingTwoFiles()
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+ if (!ToolHelper.artSupported()) {
+ return;
+ }
+
+ Path out = temp.getRoot().toPath();
+ Path original = Paths.get(EXAMPLE_DIR, EXAMPLE_DEX);
+ Path packageMap = Paths.get(ToolHelper.EXAMPLES_DIR, EXAMPLE_PACKAGE_MAP);
+ Path proguardMap = Paths.get(ToolHelper.EXAMPLES_DIR, EXAMPLE_PROGUARD_MAP);
+ R8Command command =
+ R8Command.builder()
+ .addProgramFiles(original)
+ .setOutputPath(out)
+ .setProguardMapFile(proguardMap)
+ .setPackageDistributionFile(packageMap)
+ .build();
+ ToolHelper.runR8(command);
+ List<String> outs =
+ new ArrayList<>(
+ ImmutableList.of(
+ out.resolve("classes.dex").toString(),
+ out.resolve("classes2.dex").toString(),
+ EXAMPLE_DIR + EXAMPLE_LIB));
+ outs.forEach(f -> Assert.assertTrue("Failed to find file " + f, Files.exists(Paths.get(f))));
+ ToolHelper.checkArtOutputIdentical(
+ ImmutableList.of(original.toString(), EXAMPLE_DIR + EXAMPLE_LIB),
+ outs,
+ EXAMPLE_CLASS,
+ null,
+ null);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/FileNamePrefixTest.java b/src/test/java/com/android/tools/r8/dex/FileNamePrefixTest.java
new file mode 100644
index 0000000..f2fa545
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/FileNamePrefixTest.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class FileNamePrefixTest {
+
+ private final List<String> files;
+ private final String expectedPrefix;
+
+ public FileNamePrefixTest(String[] files, String expectedPrefix) {
+ this.files = ImmutableList.copyOf(files);
+ this.expectedPrefix = expectedPrefix;
+ }
+
+ @Parameters(name = "{index}: {1}")
+ public static Collection<Object[]> data() {
+ return ImmutableList.of(
+ new Object[]{new String[]{"classes.dex", "classes2.dex"}, "classes"},
+ new Object[]{new String[]{"Classes.dex", "Classes2.dex"}, "Classes"},
+ new Object[]{new String[]{"classes.dex", "classes2.dix"}, null},
+ new Object[]{new String[]{"classes.dex", "classes2.xdex"}, null},
+ new Object[]{new String[]{"classes.dex", "classes1.dex"}, null},
+ new Object[]{new String[]{"classes.dex", "fields2.dex"}, null},
+ new Object[]{new String[]{"classes.dex", "classes2.dex", "classes4.dex"}, null}
+ );
+ }
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void computePrefix() {
+ if (expectedPrefix == null) {
+ thrown.expect(RuntimeException.class);
+ }
+ String computedPrefix = VirtualFile.deriveCommonPrefixAndSanityCheck(files);
+ if (expectedPrefix != null) {
+ Assert.assertEquals(expectedPrefix, computedPrefix);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/Leb128Test.java b/src/test/java/com/android/tools/r8/dex/Leb128Test.java
new file mode 100644
index 0000000..2b6ab9e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/Leb128Test.java
@@ -0,0 +1,99 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import static com.android.tools.r8.dex.Constants.DEX_MAGIC_SIZE;
+
+import com.android.tools.r8.utils.LebUtils;
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Test for encoding and decoding Leb128
+ */
+@RunWith(Parameterized.class)
+public class Leb128Test {
+ private final int value;
+
+ public Leb128Test(int value) {
+ this.value = value;
+ }
+
+ @Parameters(name = "{0}")
+ public static Collection<Integer> data() {
+ return Arrays.asList(
+ 0,
+ 0x3f, // Uses 6 bits
+ 0x7f, // Uses 7 bits
+ 0xff, // Uses 8 bits
+ 0x3ff, // Uses 14 bits
+ 0x407f, // Uses 15 bits with 7 consecutive 0s.
+ Integer.MIN_VALUE, // 10...0 pattern
+ 0xffffffc0, // 1..1000000
+ 0xffffff80, // 1..10000000
+ 0xffffff00, // 1..100000000
+ 0xffffc07f, // Uses 15 bits with 7 consecutive 0s.
+ Integer.MIN_VALUE + 1
+ );
+ }
+
+ // Create a DexFile with correct file magic followed by the argument bytes. Positions the
+ // DexFile after the file magic.
+ private DexFile createDexFileWithContent(byte[] bytes) {
+ DexOutputBuffer buffer = new DexOutputBuffer();
+ buffer.putBytes(Constants.DEX_FILE_MAGIC_PREFIX);
+ buffer.putBytes(Constants.ANDROID_PRE_N_DEX_VERSION_BYTES);
+ buffer.putByte(Constants.DEX_FILE_MAGIC_SUFFIX);
+ buffer.putBytes(bytes);
+ DexFile dexFile = new DexFile(buffer.asArray());
+ dexFile.position(DEX_MAGIC_SIZE);
+ return dexFile;
+ }
+
+ @Test
+ public void encodeDecodeLeb128TestWithDexBuffer() {
+ if (value < 0) {
+ return;
+ }
+ DexOutputBuffer buffer = new DexOutputBuffer();
+ LebUtils.putUleb128(buffer, value);
+ Assert.assertEquals(buffer.position(), LebUtils.sizeAsUleb128(value));
+ DexFile file = createDexFileWithContent(buffer.asArray());
+ Assert.assertEquals(value, LebUtils.parseUleb128(file));
+ }
+
+ @Test
+ public void encodeDecodeLeb128Test() {
+ if (value < 0) {
+ return;
+ }
+ byte[] encoded = LebUtils.encodeUleb128(value);
+ Assert.assertEquals(encoded.length, LebUtils.sizeAsUleb128(value));
+ DexFile file = createDexFileWithContent(encoded);
+ Assert.assertEquals(value, LebUtils.parseUleb128(file));
+ }
+
+ @Test
+ public void encodeDecodeSLeb128TestWithDexBuffer() {
+ DexOutputBuffer buffer = new DexOutputBuffer();
+ LebUtils.putSleb128(buffer, value);
+ Assert.assertEquals(buffer.position(), LebUtils.sizeAsSleb128(value));
+ DexFile file = createDexFileWithContent(buffer.asArray());
+ Assert.assertEquals(value, LebUtils.parseSleb128(file));
+ }
+
+
+ @Test
+ public void encodeDecodeSLeb128Test() {
+ byte[] encoded = LebUtils.encodeSleb128(value);
+ Assert.assertEquals(encoded.length, LebUtils.sizeAsSleb128(value));
+ DexFile file = createDexFileWithContent(encoded);
+ Assert.assertEquals(value, LebUtils.parseSleb128(file));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
new file mode 100644
index 0000000..f731e39
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -0,0 +1,151 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class TargetLookupTest extends SmaliTestBase {
+
+ @Test
+ public void lookupDirect() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ builder.addDefaultConstructor();
+
+ builder.addMethodRaw(
+ " .method private static x()I",
+ " .locals 1",
+ " const v0, 0",
+ " return v0",
+ " .end method"
+ );
+
+ // Instance method invoking static method using invoke-direct. This does not run on Art, but
+ // results in an IncompatibleClassChangeError.
+ builder.addMethodRaw(
+ " .method public y()I",
+ " .locals 1",
+ " invoke-direct {p0}, " + builder.getCurrentClassDescriptor() + "->x()I",
+ " move-result v0",
+ " return v0",
+ " .end method"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " new-instance v1, LTest;",
+ " invoke-direct {v1}, " + builder.getCurrentClassDescriptor() + "-><init>()V",
+ " :try_start",
+ " invoke-virtual {v1}, " + builder.getCurrentClassDescriptor() + "->y()I",
+ " :try_end",
+ " const-string v1, \"ERROR\"",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " :return",
+ " return-void",
+ " .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :catch",
+ " :catch",
+ " const-string v1, \"OK\"",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " goto :return"
+ );
+
+ DexApplication application = buildApplication(builder);
+ AppInfo appInfo = new AppInfo(application);
+ DexEncodedMethod method =
+ getMethod(application, DEFAULT_CLASS_NAME, "int", "x", ImmutableList.of());
+ assertNull(appInfo.lookupVirtualTarget(method.method.holder, method.method));
+ assertNull(appInfo.lookupDirectTarget(method.method));
+ assertNotNull(appInfo.lookupStaticTarget(method.method));
+
+ assertEquals("OK", runArt(application, new InternalOptions()));
+ }
+
+ @Test
+ public void lookupDirectSuper() {
+ SmaliBuilder builder = new SmaliBuilder("TestSuper");
+
+ builder.addDefaultConstructor();
+
+ builder.addMethodRaw(
+ " .method private static x()I",
+ " .locals 1",
+ " const v0, 0",
+ " return v0",
+ " .end method"
+ );
+
+ builder.addClass("Test", "TestSuper");
+
+ builder.addDefaultConstructor();
+
+ // Instance method invoking static method in superclass using invoke-direct. This does not run
+ // on Art, but results in an IncompatibleClassChangeError.
+ builder.addMethodRaw(
+ " .method public y()I",
+ " .locals 1",
+ " invoke-direct {p0}, " + builder.getCurrentClassDescriptor() + "->x()I",
+ " move-result v0",
+ " return v0",
+ " .end method"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " new-instance v1, LTest;",
+ " invoke-direct {v1}, " + builder.getCurrentClassDescriptor() + "-><init>()V",
+ " :try_start",
+ " invoke-virtual {v1}, " + builder.getCurrentClassDescriptor() + "->y()I",
+ " :try_end",
+ " const-string v1, \"ERROR\"",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " :return",
+ " return-void",
+ " .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :catch",
+ " :catch",
+ " const-string v1, \"OK\"",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " goto :return"
+ );
+
+ DexApplication application = buildApplication(builder);
+ AppInfo appInfo = new AppInfo(application);
+
+ DexMethod methodXOnTestSuper =
+ getMethod(application, "TestSuper", "int", "x", ImmutableList.of()).method;
+ DexMethod methodYOnTest =
+ getMethod(application, "Test", "int", "y", ImmutableList.of()).method;
+
+ DexType classTestSuper = methodXOnTestSuper.getHolder();
+ DexType classTest = methodYOnTest.getHolder();
+ DexProto methodXProto = methodXOnTestSuper.proto;
+ DexString methodXName = methodXOnTestSuper.name;
+ DexMethod methodXOnTest =
+ application.dexItemFactory.createMethod(classTest, methodXProto, methodXName);
+
+ assertNull(appInfo.lookupVirtualTarget(classTestSuper, methodXOnTestSuper));
+ assertNull(appInfo.lookupVirtualTarget(classTest, methodXOnTestSuper));
+ assertNull(appInfo.lookupVirtualTarget(classTest, methodXOnTest));
+
+ assertNull(appInfo.lookupDirectTarget(methodXOnTestSuper));
+ assertNull(appInfo.lookupDirectTarget(methodXOnTest));
+
+ assertNotNull(appInfo.lookupStaticTarget(methodXOnTestSuper));
+ assertNotNull(appInfo.lookupStaticTarget(methodXOnTest));
+
+ assertEquals("OK", runArt(application, new InternalOptions()));
+ }
+}
+
+
+
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
new file mode 100644
index 0000000..5902a6e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ArtErrorParser;
+import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
+import com.android.tools.r8.utils.ArtErrorParser.ArtErrorParserException;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.ListUtils;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.ComparisonFailure;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+public abstract class CompilationTestBase {
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ public AndroidApp runAndCheckVerification(
+ CompilerUnderTest compiler,
+ String referenceApk,
+ String pgMap,
+ String pgConf,
+ String... inputs)
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ return runAndCheckVerification(
+ compiler, referenceApk, pgMap, pgConf, Arrays.asList(inputs));
+ }
+
+ public AndroidApp runAndCheckVerification(
+ CompilerUnderTest compiler,
+ String referenceApk,
+ String pgMap,
+ String pgConf,
+ List<String> inputs)
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ assertTrue(referenceApk == null || new File(referenceApk).exists());
+ AndroidApp outputApp;
+ if (compiler == CompilerUnderTest.R8) {
+ R8Command.Builder builder = R8Command.builder();
+ builder.addProgramFiles(ListUtils.map(inputs, Paths::get));
+ if (pgMap != null) {
+ builder.setProguardMapFile(Paths.get(pgMap));
+ }
+ if (pgConf != null) {
+ builder.addProguardConfigurationFiles(Paths.get(pgConf));
+ }
+ outputApp = ToolHelper.runR8(builder.build());
+ } else {
+ assert compiler == CompilerUnderTest.D8;
+ outputApp =
+ ToolHelper.runD8(
+ D8Command.builder()
+ .addProgramFiles(ListUtils.map(inputs, Paths::get))
+ .setMode(CompilationMode.DEBUG)
+ .build());
+ }
+ Path out = temp.getRoot().toPath().resolve("all.zip");
+ Path oatFile = temp.getRoot().toPath().resolve("all.oat");
+ outputApp.writeToZip(out);
+ try {
+ ToolHelper.runDex2Oat(out, oatFile);
+ return outputApp;
+ } catch (AssertionError e) {
+ if (referenceApk == null) {
+ throw e;
+ }
+ DexInspector theirs = new DexInspector(Paths.get(referenceApk));
+ DexInspector ours = new DexInspector(out);
+ List<ArtErrorInfo> errors;
+ try {
+ errors = ArtErrorParser.parse(e.getMessage());
+ } catch (ArtErrorParserException parserException) {
+ System.err.println(parserException.toString());
+ throw e;
+ }
+ if (errors.isEmpty()) {
+ throw e;
+ }
+ for (ArtErrorInfo error : errors.subList(0, errors.size() - 1)) {
+ System.err.println(new ComparisonFailure(error.getMessage(),
+ "REFERENCE\n" + error.dump(theirs, false) + "\nEND REFERENCE",
+ "PROCESSED\n" + error.dump(ours, true) + "\nEND PROCESSED").toString());
+ }
+ ArtErrorInfo error = errors.get(errors.size() - 1);
+ throw new ComparisonFailure(error.getMessage(),
+ "REFERENCE\n" + error.dump(theirs, false) + "\nEND REFERENCE",
+ "PROCESSED\n" + error.dump(ours, true) + "\nEND PROCESSED");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/D8GMSCoreV9DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8GMSCoreV9DeployJarVerificationTest.java
new file mode 100644
index 0000000..d5f23d0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/D8GMSCoreV9DeployJarVerificationTest.java
@@ -0,0 +1,20 @@
+// 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.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class D8GMSCoreV9DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
+
+ @Test
+ public void buildFromDeployJar()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ buildFromDeployJar(CompilerUnderTest.D8, GMSCoreCompilationTestBase.GMSCORE_V9_DIR, true);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
new file mode 100644
index 0000000..ca8d019
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+
+public abstract class GMSCoreCompilationTestBase extends CompilationTestBase {
+ public static final String GMSCORE_V4_DIR = "third_party/gmscore/v4/";
+ public static final String GMSCORE_V5_DIR = "third_party/gmscore/v5/";
+ public static final String GMSCORE_V6_DIR = "third_party/gmscore/v6/";
+ public static final String GMSCORE_V7_DIR = "third_party/gmscore/v7/";
+ public static final String GMSCORE_V8_DIR = "third_party/gmscore/v8/";
+ public static final String GMSCORE_V9_DIR = "third_party/gmscore/gmscore_v9/";
+ public static final String GMSCORE_V10_DIR = "third_party/gmscore/gmscore_v10/";
+
+ public static final int GMSCORE_V9_MAX_SIZE = 35000000;
+ public static final int GMSCORE_V10_MAX_SIZE = 35000000;
+
+ static final String GMSCORE_APK = "GMSCore.apk";
+
+ // Files pertaining to the full GMSCore build.
+ static final String PG_MAP = "GmsCore_prod_alldpi_release_all_locales_proguard.map";
+ static final String PG_CONF = "GmsCore_prod_alldpi_release_all_locales_proguard.config";
+ static final String DEPLOY_JAR = "GmsCore_prod_alldpi_release_all_locales_deploy.jar";
+ static final String REFERENCE_APK = "noshrink_x86_GmsCore_prod_alldpi_release_unsigned.apk";
+
+ public void runR8AndCheckVerification(String version)
+ throws ProguardRuleParserException, ExecutionException, IOException, CompilationException {
+ runAndCheckVerification(CompilerUnderTest.R8, version);
+ }
+
+ public void runAndCheckVerification(CompilerUnderTest compiler, String version)
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runAndCheckVerification(
+ compiler, version + GMSCORE_APK, null, null, Paths.get(version, GMSCORE_APK).toString());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
new file mode 100644
index 0000000..ecc7451
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+public class GMSCoreDeployJarVerificationTest extends GMSCoreCompilationTestBase {
+
+ public void buildFromDeployJar(CompilerUnderTest compiler, String base, boolean hasReference)
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runAndCheckVerification(
+ compiler, hasReference ? base + REFERENCE_APK : null, null, null, base + DEPLOY_JAR);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8DisassemblerTest.java b/src/test/java/com/android/tools/r8/internal/R8DisassemblerTest.java
new file mode 100644
index 0000000..1f20559
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8DisassemblerTest.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static com.android.tools.r8.utils.AndroidApp.DEFAULT_PROGUARD_MAP_FILE;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.FileUtils;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.experimental.theories.Theories;
+import org.junit.runner.RunWith;
+
+// Invoke R8 on the dex files extracted from GMSCore.apk to disassemble the dex code.
+@RunWith(Theories.class)
+public class R8DisassemblerTest {
+
+ static final String APP_DIR = "third_party/gmscore/v5/";
+
+ public boolean deobfuscate;
+
+ @Test
+ public void disassemble() throws IOException, ExecutionException, ProguardRuleParserException,
+ CompilationException {
+ // This test only ensures that we do not break disassembling of dex code. It does not
+ // check the generated code. To make it fast, we get rid of the output.
+ PrintStream originalOut = System.out;
+ System.setOut(new PrintStream(new OutputStream() {
+ public void write(int b) { /* ignore*/ }
+ }));
+
+ try {
+ R8Command.Builder builder = R8Command.builder();
+ if (deobfuscate) {
+ builder.setProguardMapFile(Paths.get(APP_DIR, DEFAULT_PROGUARD_MAP_FILE));
+ }
+ builder.addProgramFiles(
+ Files.list(Paths.get(APP_DIR))
+ .filter(FileUtils::isDexFile)
+ .collect(Collectors.toList()));
+ R8.disassemble(builder.build());
+ } finally {
+ // Restore System.out for good measure.
+ System.setOut(originalOut);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
new file mode 100644
index 0000000..1481bc8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalResource;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreDeterministicTest extends GMSCoreCompilationTestBase {
+
+ private AndroidApp doRun()
+ throws IOException, ProguardRuleParserException, CompilationException, ExecutionException {
+ R8Command command =
+ R8Command.builder().addProgramFiles(Paths.get(GMSCORE_V7_DIR, GMSCORE_APK)).build();
+ return ToolHelper.runR8(command, options -> options.testing.randomizeCallGraphLeaves = true);
+ }
+
+ @Test
+ public void deterministic()
+ throws ExecutionException, IOException, ProguardRuleParserException, InterruptedException,
+ CompilationException {
+
+ // Run two independent compilations.
+ AndroidApp app1 = doRun();
+ AndroidApp app2 = doRun();
+
+ // Verify that the result of the two compilations was the same.
+ try (Closer closer = Closer.create()) {
+ List<InternalResource> files1 = app1.getDexProgramResources();
+ List<InternalResource> files2 = app2.getDexProgramResources();
+ assertEquals(files1.size(), files2.size());
+ for (int index = 0; index < files1.size(); index++) {
+ InputStream file1 = files1.get(index).getStream(closer);
+ InputStream file2 = files2.get(index).getStream(closer);
+ byte[] bytes1 = ByteStreams.toByteArray(file1);
+ byte[] bytes2 = ByteStreams.toByteArray(file2);
+ assertArrayEquals("File index " + index, bytes1, bytes2);
+ }
+ }
+
+ // Check that the generated bytecode runs through the dex2oat verifier with no errors.
+ Path combinedInput = temp.getRoot().toPath().resolve("all.jar");
+ Path oatFile = temp.getRoot().toPath().resolve("all.oat");
+ app1.writeToZip(combinedInput);
+ ToolHelper.runDex2Oat(combinedInput, oatFile);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java
new file mode 100644
index 0000000..b6d3dac
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreFixedPointTest extends GMSCoreCompilationTestBase {
+
+ @Test
+ public void fixedPoint()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ // First compilation.
+ AndroidApp app = AndroidApp.fromProgramDirectory(Paths.get(GMSCORE_V7_DIR));
+ AndroidApp app1 = ToolHelper.runR8(app);
+
+ // Second compilation.
+ // Add option --skip-outline-opt for second compilation. The second compilation can find
+ // additional outlining opportunities as member rebinding from the first compilation can move
+ // methods.
+ // See b/33410508 and b/33475705.
+ AndroidApp app2 = ToolHelper.runR8(app1, options -> options.outline.enabled = false);
+
+ // TODO: Require that the results of the two compilations are the same.
+ assertEquals(
+ app1.getDexProgramResources().size(),
+ app2.getDexProgramResources().size());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
new file mode 100644
index 0000000..f88ea49
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.junit.Before;
+import org.junit.Test;
+
+public class R8GMSCoreLookupTest {
+
+ static final String APP_DIR = "third_party/gmscore/v5/";
+ private AndroidApp app;
+ private DexApplication program;
+ private AppInfoWithSubtyping appInfo;
+
+ @Before
+ public void readGMSCore() throws IOException, ExecutionException {
+ app = AndroidApp.fromProgramDirectory(Paths.get(APP_DIR));
+ ExecutorService executorService = Executors.newSingleThreadExecutor();
+ Timing timing = new Timing("ReadGMSCore");
+ program = new ApplicationReader(app, new InternalOptions(), timing).read(executorService);
+ appInfo = new AppInfoWithSubtyping(program);
+ }
+
+ private void testVirtualLookup(DexProgramClass clazz, DexEncodedMethod method) {
+ // Check lookup will produce the same result.
+ DexMethod id = method.method;
+ assertEquals(appInfo.lookupVirtualTarget(id.holder, method.method), method);
+
+ // Check lookup targets with include method.
+ Set<DexEncodedMethod> targets = appInfo.lookupVirtualTargets(method.method);
+ assertTrue(targets.contains(method));
+ }
+
+ private void testInterfaceLookup(DexProgramClass clazz, DexEncodedMethod method) {
+ Set<DexEncodedMethod> targets = appInfo.lookupVirtualTargets(method.method);
+ assertFalse(targets.isEmpty());
+ }
+
+ private void testLookup(DexProgramClass clazz) {
+ assert appInfo.subtypes(clazz.type)
+ != null : "Application class must have non null subtypes.";
+ if (clazz.isInterface()) {
+ for (DexEncodedMethod method : clazz.virtualMethods()) {
+ testInterfaceLookup(clazz, method);
+ }
+ } else {
+ for (DexEncodedMethod method : clazz.virtualMethods()) {
+ testVirtualLookup(clazz, method);
+ }
+ }
+ }
+
+ @Test
+ public void testLookup() {
+ program.classes().forEach(this::testLookup);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
new file mode 100644
index 0000000..2571490
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalResource;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+public class R8GMSCoreTreeShakeJarVerificationTest extends GMSCoreCompilationTestBase {
+
+ public void buildAndTreeShakeFromDeployJar(String base, boolean hasReference, int maxSize)
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ AndroidApp app = runAndCheckVerification(
+ CompilerUnderTest.R8,
+ hasReference ? base + REFERENCE_APK : null,
+ null,
+ base + PG_CONF,
+ // Don't pass any inputs. The input will be read from the -injars in the Proguard
+ // configuration file.
+ ImmutableList.of());
+ int bytes = 0;
+ try (Closer closer = Closer.create()) {
+ for (InternalResource dex : app.getDexProgramResources()) {
+ bytes += ByteStreams.toByteArray(dex.getStream(closer)).length;
+ }
+ }
+ assertTrue("Expected max size of " + maxSize + ", got " + bytes, bytes < maxSize);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
new file mode 100644
index 0000000..5894981
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
@@ -0,0 +1,22 @@
+// 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.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV10DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
+
+ @Test
+ public void buildFromDeployJar()
+ // TODO(tamaskenez): set hasReference = true when we have the noshrink file for V10
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ buildFromDeployJar(CompilerUnderTest.R8, GMSCoreCompilationTestBase.GMSCORE_V10_DIR, false);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10TreeShakeJarVerificationTest.java
new file mode 100644
index 0000000..5253ac2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10TreeShakeJarVerificationTest.java
@@ -0,0 +1,21 @@
+// 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.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV10TreeShakeJarVerificationTest
+ extends R8GMSCoreTreeShakeJarVerificationTest {
+
+ @Test
+ public void buildAndTreeShakeFromDeployJar()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ // TODO(tamaskenez): set hasReference = true when we have the noshrink file for V10
+ buildAndTreeShakeFromDeployJar(GMSCORE_V10_DIR, false, GMSCORE_V10_MAX_SIZE);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV4VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV4VerificationTest.java
new file mode 100644
index 0000000..d7de82e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV4VerificationTest.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV4VerificationTest extends GMSCoreCompilationTestBase {
+ @Test
+ public void verify()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runR8AndCheckVerification(GMSCORE_V4_DIR);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV5VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV5VerificationTest.java
new file mode 100644
index 0000000..6a098a8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV5VerificationTest.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV5VerificationTest extends GMSCoreCompilationTestBase {
+ @Test
+ public void verify()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runR8AndCheckVerification(GMSCORE_V5_DIR);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV6VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV6VerificationTest.java
new file mode 100644
index 0000000..b7ca295
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV6VerificationTest.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV6VerificationTest extends GMSCoreCompilationTestBase {
+ @Test
+ public void verify()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runR8AndCheckVerification(GMSCORE_V6_DIR);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV7VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV7VerificationTest.java
new file mode 100644
index 0000000..f65a8e1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV7VerificationTest.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV7VerificationTest extends GMSCoreCompilationTestBase {
+
+ @Test
+ public void verify()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runR8AndCheckVerification(GMSCORE_V7_DIR);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV8VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV8VerificationTest.java
new file mode 100644
index 0000000..bc0dbe8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV8VerificationTest.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV8VerificationTest extends GMSCoreCompilationTestBase {
+
+ @Test
+ public void verify()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runR8AndCheckVerification(GMSCORE_V8_DIR);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9DeployJarVerificationTest.java
new file mode 100644
index 0000000..d1e6844
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9DeployJarVerificationTest.java
@@ -0,0 +1,21 @@
+// 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.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV9DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
+
+ @Test
+ public void buildFromDeployJar()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ buildFromDeployJar(CompilerUnderTest.R8, GMSCoreCompilationTestBase.GMSCORE_V9_DIR, true);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
new file mode 100644
index 0000000..2f5ed63
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
@@ -0,0 +1,19 @@
+// 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.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV9TreeShakeJarVerificationTest extends R8GMSCoreTreeShakeJarVerificationTest {
+
+ @Test
+ public void buildAndTreeShakeFromDeployJar()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ buildAndTreeShakeFromDeployJar(GMSCORE_V9_DIR, true, GMSCORE_V9_MAX_SIZE);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
new file mode 100644
index 0000000..08b3892
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+public abstract class YouTubeCompilationBase extends CompilationTestBase {
+ static final String BASE = "third_party/youtube/youtube.android_11.47/";
+ static final String APK = "YouTubeRelease_unsigned.apk";
+ static final String DEPLOY_JAR = "YouTubeRelease_deploy.jar";
+ static final String PG_JAR = "YouTubeRelease_proguard.jar";
+ static final String PG_MAP = "YouTubeRelease_proguard.map";
+ static final String PG_CONF = "YouTubeRelease_proguard.config";
+}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java
new file mode 100644
index 0000000..b40ba07
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class YouTubeDeployJarVerificationTest extends YouTubeCompilationBase {
+
+ @Test
+ public void buildFromDeployJar()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runAndCheckVerification(CompilerUnderTest.R8, BASE + APK, null, null, BASE + DEPLOY_JAR);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java
new file mode 100644
index 0000000..19bfe25
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class YouTubeDexVerificationTest extends YouTubeCompilationBase {
+
+ @Test
+ public void buildFromDex()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runAndCheckVerification(CompilerUnderTest.R8, BASE + APK, BASE + PG_MAP, null, BASE + APK);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java
new file mode 100644
index 0000000..0b1e553
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class YouTubeProguardJarVerificationTest extends YouTubeCompilationBase {
+
+ @Test
+ public void buildFromProguardJar()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runAndCheckVerification(CompilerUnderTest.R8, BASE + APK, BASE + PG_MAP, null, BASE + PG_JAR);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
new file mode 100644
index 0000000..c51129c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalResource;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class YouTubeTreeShakeJarVerificationTest extends YouTubeCompilationBase {
+
+ @Test
+ @Ignore("b/35656577")
+ public void buildAndTreeShakeFromDeployJar()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ int maxSize = 16000000;
+ AndroidApp app = runAndCheckVerification(
+ CompilerUnderTest.R8, BASE + APK, null, BASE + PG_CONF, BASE + DEPLOY_JAR);
+ int bytes = 0;
+ try (Closer closer = Closer.create()) {
+ for (InternalResource dex : app.getDexProgramResources()) {
+ bytes += ByteStreams.toByteArray(dex.getStream(closer)).length;
+ }
+ }
+ assertTrue("Expected max size of " + maxSize + ", got " + bytes, bytes < maxSize);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
new file mode 100644
index 0000000..50cee1b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
@@ -0,0 +1,83 @@
+// 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.ir;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.ListIterator;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class BasicBlockIteratorTest extends SmaliTestBase {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ /**
+ * Simple test IR, which has three blocks:
+ *
+ * First block: Argument instructions
+ * Second block: Add instruction
+ * Third block: Return instruction
+ *
+ */
+ IRCode simpleCode() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String returnType = "int";
+ List<String> parameters = ImmutableList.of("int", "int");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 1,
+ " add-int v0, p0, p1",
+ " return p0"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Build the code, and split the code into three blocks.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ ListIterator<BasicBlock> blocks = code.listIterator();
+ InstructionListIterator iter = blocks.next().listIterator();
+ iter.nextUntil(i -> !i.isArgument());
+ iter.previous();
+ iter.split(1, code, blocks);
+ return code;
+ }
+
+ @Test
+ public void removeBeforeNext() {
+ IRCode code = simpleCode();
+
+ ListIterator<BasicBlock> blocks = code.listIterator();
+ thrown.expect(IllegalStateException.class);
+ blocks.remove();
+ }
+
+ @Test
+ public void removeTwice() {
+ IRCode code = simpleCode();
+
+ ListIterator<BasicBlock> blocks = code.listIterator();
+ blocks.next();
+ blocks.next();
+ blocks.remove();
+ thrown.expect(IllegalStateException.class);
+ blocks.remove();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
new file mode 100644
index 0000000..e12d003
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -0,0 +1,1233 @@
+// 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.ir;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import org.junit.Test;
+
+public class InlineTest extends SmaliTestBase {
+
+ TestApplication codeForMethodReplaceTest(int a, int b) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int", "int"),
+ 0,
+ " invoke-static { p0, p1 }, LTest;->a(II)I",
+ " move-result p0",
+ " return p0"
+ );
+
+ MethodSignature signatureA = builder.addStaticMethod(
+ "int",
+ "a",
+ ImmutableList.of("int", "int"),
+ 1,
+ " add-int p0, p0, p1",
+ " return p0"
+ );
+
+ MethodSignature signatureB = builder.addStaticMethod(
+ "int",
+ "b",
+ ImmutableList.of("int", "int"),
+ 1,
+ " if-eq p0, p1, :eq",
+ " const/4 v0, 1",
+ " return v0",
+ " :eq",
+ " const/4 v0, 0",
+ " return v0"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, " + a,
+ " const/4 v2, " + b,
+ " invoke-static { v1, v2 }, LTest;->method(II)I",
+ " move-result v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Return the processed method for inspection.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+ DexEncodedMethod methodA = getMethod(application, signatureA);
+ IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+
+ DexEncodedMethod methodB = getMethod(application, signatureB);
+ IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+
+ return new SmaliTestBase.TestApplication(application, method, code,
+ ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
+ }
+
+ public void runInlineTest(int a, int b, int expectedA, int expectedB) {
+ // Run code without inlining.
+ TestApplication test = codeForMethodReplaceTest(a, b);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ InstructionListIterator iterator;
+
+ // Run code inlining a.
+ test = codeForMethodReplaceTest(a, b);
+ iterator = test.code.blocks.get(0).listIterator();
+ iterator.nextUntil(instruction -> instruction.isInvoke());
+ iterator.previous();
+ iterator.inlineInvoke(test.code, test.additionalCode.get(0));
+ result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ // Run code inlining b (where a is actually called).
+ test = codeForMethodReplaceTest(a, b);
+ iterator = test.code.blocks.get(0).listIterator();
+ iterator.nextUntil(instruction -> instruction.isInvoke());
+ iterator.previous();
+ iterator.inlineInvoke(test.code, test.additionalCode.get(1));
+ result = test.run();
+ assertEquals(Integer.toString(expectedB), result);
+ }
+
+ @Test
+ public void inline() {
+ runInlineTest(1, 1, 2, 0);
+ runInlineTest(1, 2, 3, 1);
+ }
+
+ TestApplication codeForMethodReplaceReturnVoidTest(int a, int b) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int", "int"),
+ 0,
+ " invoke-static { p0, p1 }, LTest;->a(II)V",
+ " return p0"
+ );
+
+ MethodSignature signatureA = builder.addStaticMethod(
+ "void",
+ "a",
+ ImmutableList.of("int", "int"),
+ 1,
+ " add-int p0, p0, p1",
+ " return-void "
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, " + a,
+ " const/4 v2, " + b,
+ " invoke-static { v1, v2 }, LTest;->method(II)I",
+ " move-result v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Return the processed method for inspection.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+ DexEncodedMethod methodA = getMethod(application, signatureA);
+ IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+
+ return new TestApplication(application, method, code,
+ ImmutableList.of(codeA), valueNumberGenerator, options);
+ }
+
+ @Test
+ public void inlineReturnVoid() {
+ // Run code without inlining.
+ TestApplication test = codeForMethodReplaceReturnVoidTest(1, 2);
+ String result = test.run();
+ assertEquals(Integer.toString(1), result);
+
+ InstructionListIterator iterator;
+
+ // Run code inlining a.
+ test = codeForMethodReplaceReturnVoidTest(1, 2);
+ iterator = test.code.blocks.get(0).listIterator();
+ iterator.nextUntil(instruction -> instruction.isInvoke());
+ iterator.previous();
+ iterator.inlineInvoke(test.code, test.additionalCode.get(0));
+ result = test.run();
+ assertEquals(Integer.toString(1), result);
+ }
+
+ TestApplication codeForMultipleMethodReplaceTest(int a, int b) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int", "int"),
+ 0,
+ " invoke-static { p0, p1 }, LTest;->a(II)I",
+ " move-result p0",
+ " invoke-static { p0, p1 }, LTest;->a(II)I",
+ " move-result p0",
+ " invoke-static { p0, p1 }, LTest;->a(II)I",
+ " move-result p0",
+ " return p0"
+ );
+
+ MethodSignature signatureA = builder.addStaticMethod(
+ "int",
+ "a",
+ ImmutableList.of("int", "int"),
+ 1,
+ " add-int p0, p0, p1",
+ " return p0"
+ );
+
+ MethodSignature signatureB = builder.addStaticMethod(
+ "int",
+ "b",
+ ImmutableList.of("int", "int"),
+ 1,
+ " mul-int p0, p0, p1",
+ " return p0"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, " + a,
+ " const/4 v2, " + b,
+ " invoke-static { v1, v2 }, LTest;->method(II)I",
+ " move-result v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Return the processed method for inspection.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+ // Build three copies of a and b for inlining three times.
+ List<IRCode> additionalCode = new ArrayList<>();
+ for (int i = 0; i < 3; i++) {
+ DexEncodedMethod methodA = getMethod(application, signatureA);
+ IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+ additionalCode.add(codeA);
+ }
+
+ for (int i = 0; i < 3; i++) {
+ DexEncodedMethod methodB = getMethod(application, signatureB);
+ IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+ additionalCode.add(codeB);
+ }
+
+ return new TestApplication(application, method, code,
+ additionalCode, valueNumberGenerator, options);
+ }
+
+ public void runInlineMultipleTest(int a, int b, int expectedA, int expectedB) {
+ // Run code without inlining.
+ TestApplication test = codeForMultipleMethodReplaceTest(a, b);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ InstructionListIterator iterator;
+
+ // Run code inlining all invokes with a.
+ test = codeForMultipleMethodReplaceTest(a, b);
+ ListIterator<BasicBlock> blocksIterator = test.code.blocks.listIterator();
+ Iterator<IRCode> inlinee = test.additionalCode.listIterator(); // IR code for a's
+ List<BasicBlock> blocksToRemove = new ArrayList<>();
+ while (blocksIterator.hasNext()) {
+ BasicBlock block = blocksIterator.next();
+ iterator = block.listIterator();
+ Instruction invoke = iterator.nextUntil(instruction -> instruction.isInvoke());
+ if (invoke != null) {
+ iterator.previous();
+ iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove);
+ assert blocksToRemove.isEmpty();
+ }
+ }
+ result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ // Run code inlining all invokes with b.
+ test = codeForMultipleMethodReplaceTest(a, b);
+ blocksIterator = test.code.blocks.listIterator();
+ inlinee = test.additionalCode.listIterator(3); // IR code for b's
+ while (blocksIterator.hasNext()) {
+ BasicBlock block = blocksIterator.next();
+ iterator = block.listIterator();
+ Instruction invoke = iterator.nextUntil(instruction -> instruction.isInvoke());
+ if (invoke != null) {
+ iterator.previous();
+ iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove);
+ assert blocksToRemove.isEmpty();
+ }
+ }
+ result = test.run();
+ assertEquals(Integer.toString(expectedB), result);
+ }
+
+ @Test
+ public void inlineMultiple() {
+ runInlineMultipleTest(1, 1, 4, 1);
+ runInlineMultipleTest(1, 2, 7, 8);
+ }
+
+ TestApplication codeForMethodReplaceTestWithCatchHandler(int a, int b, boolean twoGuards) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String secondGuard = twoGuards ?
+ " .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch" : " ";
+
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int", "int"),
+ 0,
+ " :try_start",
+ " invoke-static { p0, p1 }, LTest;->a(II)I",
+ " :try_end",
+ " move-result p0",
+ " :return",
+ " return p0",
+ " .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+ secondGuard,
+ " :catch",
+ " const/4 p0, -1",
+ " goto :return"
+ );
+
+ MethodSignature signatureA = builder.addStaticMethod(
+ "int",
+ "a",
+ ImmutableList.of("int", "int"),
+ 1,
+ " add-int p0, p0, p1",
+ " return p0"
+ );
+
+ MethodSignature signatureB = builder.addStaticMethod(
+ "int",
+ "b",
+ ImmutableList.of("int", "int"),
+ 1,
+ " if-eq p0, p1, :eq",
+ " const/4 v0, 1",
+ " return v0",
+ " :eq",
+ " const/4 v0, 0",
+ " return v0"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, " + a,
+ " const/4 v2, " + b,
+ " invoke-static { v1, v2 }, LTest;->method(II)I",
+ " move-result v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Return the processed method for inspection.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+ DexEncodedMethod methodA = getMethod(application, signatureA);
+ IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+
+ DexEncodedMethod methodB = getMethod(application, signatureB);
+ IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+
+ return new TestApplication(application, method, code,
+ ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
+ }
+
+ public void runInlineCallerHasCatchHandlersTest(
+ int a, int b, boolean twoGuards, int expectedA, int expectedB) {
+ // Run code without inlining.
+ TestApplication test = codeForMethodReplaceTestWithCatchHandler(a, b, twoGuards);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ InstructionListIterator iterator;
+
+ // Run code inlining a.
+ test = codeForMethodReplaceTestWithCatchHandler(a, b, twoGuards);
+ iterator = test.code.blocks.get(1).listIterator();
+ iterator.nextUntil(instruction -> instruction.isInvoke());
+ iterator.previous();
+ iterator.inlineInvoke(test.code, test.additionalCode.get(0));
+ result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ // Run code inlining b (where a is actually called).
+ test = codeForMethodReplaceTestWithCatchHandler(a, b, twoGuards);
+ iterator = test.code.blocks.get(1).listIterator();
+ iterator.nextUntil(instruction -> instruction.isInvoke());
+ iterator.previous();
+ iterator.inlineInvoke(test.code, test.additionalCode.get(1));
+ result = test.run();
+ assertEquals(Integer.toString(expectedB), result);
+ }
+
+ @Test
+ public void inlineCallerHasCatchHandlers() {
+ runInlineCallerHasCatchHandlersTest(1, 1, false, 2, 0);
+ runInlineCallerHasCatchHandlersTest(1, 2, false, 3, 1);
+ runInlineCallerHasCatchHandlersTest(1, 1, true, 2, 0);
+ runInlineCallerHasCatchHandlersTest(1, 2, true, 3, 1);
+ }
+
+ TestApplication codeForInlineCanThrow(int a, int b, boolean twoGuards) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String secondGuard = twoGuards ?
+ " .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch" : " ";
+
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int", "int"),
+ 0,
+ " invoke-static { p0, p1 }, LTest;->a(II)I",
+ " move-result p0",
+ " return p0"
+ );
+
+ MethodSignature signatureA = builder.addStaticMethod(
+ "int",
+ "a",
+ ImmutableList.of("int", "int"),
+ 0,
+ " div-int p0, p0, p1",
+ " return p0"
+ );
+
+ MethodSignature signatureB = builder.addStaticMethod(
+ "int",
+ "b",
+ ImmutableList.of("int", "int"),
+ 0,
+ " :try_start",
+ " div-int p0, p0, p1",
+ " :try_end",
+ " :return",
+ " return p0",
+ " .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+ secondGuard,
+ " :catch",
+ " const/4 p0, -1",
+ " goto :return"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, " + a,
+ " const/4 v2, " + b,
+ " :try_start",
+ " invoke-static { v1, v2 }, LTest;->method(II)I",
+ " :try_end",
+ " move-result v1",
+ " :print_result",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void",
+ " .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+ " :catch",
+ " const/4 v1, -2",
+ " goto :print_result"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Return the processed method for inspection.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+ DexEncodedMethod methodA = getMethod(application, signatureA);
+ IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+
+ DexEncodedMethod methodB = getMethod(application, signatureB);
+ IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+
+ return new TestApplication(application, method, code,
+ ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
+ }
+
+ public void runInlineCanThrow(
+ int a, int b, boolean twoGuards, int expectedA, int expectedB) {
+ // Run code without inlining.
+ TestApplication test = codeForInlineCanThrow(a, b, twoGuards);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ InstructionListIterator iterator;
+
+ // Run code inlining a.
+ test = codeForInlineCanThrow(a, b, twoGuards);
+ iterator = test.code.blocks.get(0).listIterator();
+ iterator.nextUntil(instruction -> instruction.isInvoke());
+ iterator.previous();
+ iterator.inlineInvoke(test.code, test.additionalCode.get(0));
+ result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ // Run code inlining b (where a is actually called).
+ test = codeForInlineCanThrow(a, b, twoGuards);
+ iterator = test.code.blocks.get(0).listIterator();
+ iterator.nextUntil(instruction -> instruction.isInvoke());
+ iterator.previous();
+ iterator.inlineInvoke(test.code, test.additionalCode.get(1));
+ result = test.run();
+ assertEquals(Integer.toString(expectedB), result);
+ }
+
+ @Test
+ public void inlineCanThrow() {
+ runInlineCanThrow(2, 2, false, 1, 1);
+ runInlineCanThrow(2, 0, false, -2, -1);
+ runInlineCanThrow(2, 2, true, 1, 1);
+ runInlineCanThrow(2, 0, true, -2, -1);
+ }
+
+ private TestApplication codeForInlineAlwaysThrows(boolean twoGuards) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String secondGuard = twoGuards ?
+ " .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch" : " ";
+
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of(),
+ 1,
+ " invoke-static { }, LTest;->a()I",
+ " move-result v0",
+ " return v0"
+ );
+
+ MethodSignature signatureA = builder.addStaticMethod(
+ "int",
+ "a",
+ ImmutableList.of(),
+ 1,
+ " new-instance v0, Ljava/lang/Exception;",
+ " invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+ " throw v0"
+ );
+
+ MethodSignature signatureB = builder.addStaticMethod(
+ "int",
+ "b",
+ ImmutableList.of(),
+ 1,
+ " :try_start",
+ " new-instance v0, Ljava/lang/Exception;",
+ " invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+ " throw v0",
+ " :try_end",
+ " .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+ secondGuard,
+ " :catch",
+ " const/4 v0, -1",
+ " return v0"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " :try_start",
+ " invoke-static { }, LTest;->method()I",
+ " :try_end",
+ " move-result v1",
+ " :print_result",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void",
+ " .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch",
+ " :catch",
+ " const/4 v1, -2",
+ " goto :print_result"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Return the processed method for inspection.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+ DexEncodedMethod methodA = getMethod(application, signatureA);
+ IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+
+ DexEncodedMethod methodB = getMethod(application, signatureB);
+ IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+
+ return new TestApplication(application, method, code,
+ ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
+ }
+
+ private void runInlineAlwaysThrows(boolean twoGuards, int expectedA, int expectedB) {
+ // Run code without inlining.
+ TestApplication test = codeForInlineAlwaysThrows(twoGuards);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ InstructionListIterator iterator;
+
+ // Run code inlining a.
+ test = codeForInlineAlwaysThrows(twoGuards);
+ iterator = test.code.blocks.get(0).listIterator();
+ iterator.nextUntil(Instruction::isInvoke);
+ iterator.previous();
+ iterator.inlineInvoke(test.code, test.additionalCode.get(0));
+
+ result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ // Run code inlining b (where a is actually called).
+ test = codeForInlineAlwaysThrows(twoGuards);
+ iterator = test.code.blocks.get(0).listIterator();
+ iterator.nextUntil(Instruction::isInvoke);
+ iterator.previous();
+ iterator.inlineInvoke(test.code, test.additionalCode.get(1));
+ result = test.run();
+ assertEquals(Integer.toString(expectedB), result);
+ }
+
+ @Test
+ public void inlineAlwaysThrows() {
+ runInlineAlwaysThrows(false, -2, -2);
+ runInlineAlwaysThrows(true, -2, -1);
+ }
+
+ private TestApplication codeForInlineAlwaysThrowsMultiple(boolean twoGuards) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String secondGuard = twoGuards ?
+ " .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch" : " ";
+
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of(),
+ 1,
+ " invoke-static { }, LTest;->a()I",
+ " invoke-static { }, LTest;->a()I",
+ " invoke-static { }, LTest;->a()I",
+ " move-result v0",
+ " return v0"
+ );
+
+ MethodSignature signatureA = builder.addStaticMethod(
+ "int",
+ "a",
+ ImmutableList.of(),
+ 1,
+ " new-instance v0, Ljava/lang/Exception;",
+ " invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+ " throw v0"
+ );
+
+ MethodSignature signatureB = builder.addStaticMethod(
+ "int",
+ "b",
+ ImmutableList.of(),
+ 1,
+ " :try_start",
+ " new-instance v0, Ljava/lang/Exception;",
+ " invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+ " throw v0",
+ " :try_end",
+ " .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+ secondGuard,
+ " :catch",
+ " const/4 v0, -1",
+ " return v0"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " :try_start",
+ " invoke-static { }, LTest;->method()I",
+ " :try_end",
+ " move-result v1",
+ " :print_result",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void",
+ " .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch",
+ " :catch",
+ " const/4 v1, -2",
+ " goto :print_result"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Return the processed method for inspection.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+ // Build three copies of a and b for inlining three times.
+ List<IRCode> additionalCode = new ArrayList<>();
+ for (int i = 0; i < 3; i++) {
+ DexEncodedMethod methodA = getMethod(application, signatureA);
+ IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+ additionalCode.add(codeA);
+ }
+
+ for (int i = 0; i < 3; i++) {
+ DexEncodedMethod methodB = getMethod(application, signatureB);
+ IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+ additionalCode.add(codeB);
+ }
+
+ return new TestApplication(
+ application, method, code, additionalCode, valueNumberGenerator, options);
+ }
+
+ private void runInlineAlwaysThrowsMultiple(boolean twoGuards, int expectedA, int expectedB) {
+ // Run code without inlining.
+ TestApplication test = codeForInlineAlwaysThrows(twoGuards);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ InstructionListIterator iterator;
+
+ // Run code inlining all invokes with a.
+ test = codeForInlineAlwaysThrowsMultiple(twoGuards);
+ ListIterator<BasicBlock> blocksIterator = test.code.blocks.listIterator();
+ Iterator<IRCode> inlinee = test.additionalCode.listIterator(); // IR code for a's.
+ List<BasicBlock> blocksToRemove = new ArrayList<>();
+ while (blocksIterator.hasNext()) {
+ BasicBlock block = blocksIterator.next();
+ if (blocksToRemove.contains(block)) {
+ continue;
+ }
+ iterator = block.listIterator();
+ Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
+ if (invoke != null) {
+ iterator.previous();
+ iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove);
+ assert !blocksToRemove.isEmpty();
+ }
+ }
+ test.code.removeBlocks(blocksToRemove);
+ result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ // Run code inlining all invokes with b.
+ test = codeForInlineAlwaysThrowsMultiple(twoGuards);
+ blocksIterator = test.code.blocks.listIterator();
+ inlinee = test.additionalCode.listIterator(3); // IR code for b's.
+ while (blocksIterator.hasNext()) {
+ BasicBlock block = blocksIterator.next();
+ if (blocksToRemove.contains(block)) {
+ continue;
+ }
+ iterator = block.listIterator();
+ Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
+ if (invoke != null) {
+ iterator.previous();
+ iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove);
+ assert !blocksToRemove.isEmpty();
+ }
+ }
+ test.code.removeBlocks(blocksToRemove);
+ result = test.run();
+ assertEquals(Integer.toString(expectedB), result);
+ }
+
+ @Test
+ public void inlineAlwaysThrowsMultiple() {
+ runInlineAlwaysThrowsMultiple(false, -2, -2);
+ runInlineAlwaysThrowsMultiple(true, -2, -1);
+ }
+
+ private TestApplication codeForInlineAlwaysThrowsMultipleWithControlFlow(
+ int a, boolean twoGuards) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String secondGuard = twoGuards ?
+ " .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch" : " ";
+
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int"),
+ 1,
+ " const/4 v0, 0",
+ " if-ne v0, p0, :not_zero",
+ " invoke-static { }, LTest;->a()I",
+ " :not_zero",
+ " const/4 v0, 1",
+ " if-ne v0, p0, :not_one",
+ " invoke-static { }, LTest;->a()I",
+ " :not_one",
+ " invoke-static { }, LTest;->a()I",
+ " move-result v0",
+ " return v0"
+ );
+
+ MethodSignature signatureA = builder.addStaticMethod(
+ "int",
+ "a",
+ ImmutableList.of(),
+ 1,
+ " new-instance v0, Ljava/lang/Exception;",
+ " invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+ " throw v0"
+ );
+
+ MethodSignature signatureB = builder.addStaticMethod(
+ "int",
+ "b",
+ ImmutableList.of(),
+ 1,
+ " :try_start",
+ " new-instance v0, Ljava/lang/Exception;",
+ " invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+ " throw v0",
+ " :try_end",
+ " .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+ secondGuard,
+ " :catch",
+ " const/4 v0, -1",
+ " return v0"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, " + a,
+ " :try_start",
+ " invoke-static { v1 }, LTest;->method(I)I",
+ " :try_end",
+ " move-result v1",
+ " :print_result",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void",
+ " .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch",
+ " :catch",
+ " const/4 v1, -2",
+ " goto :print_result"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Return the processed method for inspection.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+ // Build three copies of a and b for inlining three times.
+ List<IRCode> additionalCode = new ArrayList<>();
+ for (int i = 0; i < 3; i++) {
+ DexEncodedMethod methodA = getMethod(application, signatureA);
+ IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+ additionalCode.add(codeA);
+ }
+
+ for (int i = 0; i < 3; i++) {
+ DexEncodedMethod methodB = getMethod(application, signatureB);
+ IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+ additionalCode.add(codeB);
+ }
+
+ return new TestApplication(
+ application, method, code, additionalCode, valueNumberGenerator, options);
+ }
+
+ private void runInlineAlwaysThrowsMultipleWithControlFlow(
+ int a, boolean twoGuards, int expectedA, int expectedB) {
+ // Run code without inlining.
+ TestApplication test = codeForInlineAlwaysThrows(twoGuards);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ InstructionListIterator iterator;
+
+ // Run code inlining all invokes with a.
+ test = codeForInlineAlwaysThrowsMultipleWithControlFlow(a, twoGuards);
+ ListIterator<BasicBlock> blocksIterator = test.code.blocks.listIterator();
+ Iterator<IRCode> inlinee = test.additionalCode.listIterator(); // IR code for a's.
+ List<BasicBlock> blocksToRemove = new ArrayList<>();
+ while (blocksIterator.hasNext()) {
+ BasicBlock block = blocksIterator.next();
+ if (blocksToRemove.contains(block)) {
+ continue;
+ }
+ iterator = block.listIterator();
+ Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
+ if (invoke != null) {
+ iterator.previous();
+ iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove);
+ assert !blocksToRemove.isEmpty();
+ }
+ }
+ test.code.removeBlocks(blocksToRemove);
+ result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ // Run code inlining all invokes with b.
+ test = codeForInlineAlwaysThrowsMultipleWithControlFlow(a, twoGuards);
+ blocksIterator = test.code.blocks.listIterator();
+ inlinee = test.additionalCode.listIterator(3); // IR code for b's.
+ while (blocksIterator.hasNext()) {
+ BasicBlock block = blocksIterator.next();
+ if (blocksToRemove.contains(block)) {
+ continue;
+ }
+ iterator = block.listIterator();
+ Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
+ if (invoke != null) {
+ iterator.previous();
+ iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove);
+ assert !blocksToRemove.isEmpty();
+ }
+ }
+ test.code.removeBlocks(blocksToRemove);
+ result = test.run();
+ assertEquals(Integer.toString(expectedB), result);
+ }
+
+ @Test
+ public void inlineAlwaysThrowsMultipleWithControlFlow() {
+ runInlineAlwaysThrowsMultipleWithControlFlow(0, false, -2, -2);
+ runInlineAlwaysThrowsMultipleWithControlFlow(0, true, -2, -1);
+ runInlineAlwaysThrowsMultipleWithControlFlow(1, false, -2, -2);
+ runInlineAlwaysThrowsMultipleWithControlFlow(1, true, -2, -1);
+ runInlineAlwaysThrowsMultipleWithControlFlow(2, false, -2, -2);
+ runInlineAlwaysThrowsMultipleWithControlFlow(2, true, -2, -1);
+ }
+
+ private TestApplication codeForInlineWithHandlersCanThrow(int a, int b, int c,
+ boolean twoGuards, boolean callerHasCatchAll, boolean inlineeHasCatchAll) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String secondGuard = "";
+ String secondGuardCode = "";
+ String callerCatchAllGuard = "";
+ String callerCatchAllCode = "";
+
+ if (twoGuards) {
+ String secondGuardLabel = "catch2";
+ secondGuard =
+ " .catch Ljava/lang/Exception; {:try_start .. :try_end} :" + secondGuardLabel;
+ secondGuardCode =
+ " :" + secondGuardLabel + "\n" +
+ " const p0, -12\n" +
+ " goto :return";
+ }
+
+ if (callerHasCatchAll) {
+ String catchAllLabel = "catch_all";
+ callerCatchAllGuard = " .catchall {:try_start .. :try_end} :" + catchAllLabel;
+ callerCatchAllCode =
+ " :" + catchAllLabel + "\n" +
+ " const p0, -13\n" +
+ " goto :return";
+ }
+
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int", "int", "int"),
+ 0,
+ " :try_start",
+ " invoke-static { p0, p1, p2 }, LTest;->a(III)I",
+ " :try_end",
+ " move-result p0",
+ " :return",
+ " return p0",
+ " .catch Ljava/lang/NullPointerException; {:try_start .. :try_end} :catch",
+ secondGuard,
+ callerCatchAllGuard,
+ " :catch",
+ " const p0, -11",
+ " goto :return",
+ secondGuardCode,
+ callerCatchAllCode
+ );
+
+ MethodSignature signatureA = builder.addStaticMethod(
+ "int",
+ "a",
+ ImmutableList.of("int", "int", "int"),
+ 1,
+ " const v0, 4",
+ " div-int p0, v0, p0",
+ " const v0, 8",
+ " div-int p1, v0, p1",
+ " add-int p0, p0, p1",
+ " const v0, 16",
+ " div-int p2, v0, p2",
+ " add-int p0, p0, p2",
+ " const v0, 3",
+ " if-ne v0, p0, :not_three",
+ " new-instance v0, Ljava/lang/Exception;",
+ " invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+ " throw v0",
+ " :not_three",
+ " return p0"
+ );
+
+ String inlineeSecondGuard = "";
+ String inlineeSecondGuardCode = "";
+ if (twoGuards) {
+ String secondGuardLabel = "catch2";
+ inlineeSecondGuard =
+ " .catch Ljava/lang/Exception; {:try_start .. :try_end} :" + secondGuardLabel;
+ inlineeSecondGuardCode =
+ " :" + secondGuardLabel + "\n" +
+ " const p0, -2\n" +
+ " goto :return";
+ }
+
+ String inlineeCatchAllGuard = "";
+ String inlineeCatchAllCode = "";
+ if (inlineeHasCatchAll) {
+ String catchAllLabel = "catch_all";
+ inlineeCatchAllGuard = " .catchall {:try_start .. :try_end} :" + catchAllLabel;
+ inlineeCatchAllCode =
+ " :" + catchAllLabel + "\n" +
+ " const p0, -3\n" +
+ " goto :return";
+ }
+
+ MethodSignature signatureB = builder.addStaticMethod(
+ "int",
+ "b",
+ ImmutableList.of("int", "int", "int"),
+ 1,
+ " :try_start",
+ " const v0, 4",
+ " div-int p0, v0, p0",
+ " const v0, 8",
+ " div-int p1, v0, p1",
+ " add-int p0, p0, p1",
+ " const v0, 16",
+ " div-int p2, v0, p2",
+ " add-int p0, p0, p2",
+ " const v0, 3",
+ " if-ne v0, p0, :not_three",
+ " new-instance v0, Ljava/lang/Exception;",
+ " invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+ " throw v0",
+ " :not_three",
+ " :try_end",
+ " :return",
+ " return p0",
+ " .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+ inlineeSecondGuard,
+ inlineeCatchAllGuard,
+ " :catch",
+ " const/4 p0, -1",
+ " goto :return",
+ inlineeSecondGuardCode,
+ inlineeCatchAllCode
+ );
+
+ builder.addMainMethod(
+ 3,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const v1, " + a,
+ " const v2, " + b,
+ " const v3, " + c,
+ " :try_start",
+ " invoke-static { v1, v2, v3 }, LTest;->method(III)I",
+ " :try_end",
+ " move-result v1",
+ " :print_result",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void",
+ " .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+ " .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch2",
+ " .catchall {:try_start .. :try_end} :catch_all",
+ " :catch",
+ " const v1, -21",
+ " goto :print_result",
+ " :catch2",
+ " const v1, -22",
+ " goto :print_result",
+ " :catch_all",
+ " const v1, -23",
+ " goto :print_result"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Return the processed method for inspection.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+ DexEncodedMethod methodA = getMethod(application, signatureA);
+ IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+
+ DexEncodedMethod methodB = getMethod(application, signatureB);
+ IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+
+ return new TestApplication(application, method, code,
+ ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
+ }
+
+ private void runInlineWithHandlersCanThrow(int a, int b, int c,
+ boolean twoGuards, boolean callerHasCatchAll, boolean inlineeHasCatchAll,
+ int expectedA, int expectedB) {
+ // Run code without inlining.
+ TestApplication test = codeForInlineWithHandlersCanThrow(
+ a, b, c, twoGuards, callerHasCatchAll, inlineeHasCatchAll);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ InstructionListIterator iterator;
+
+ // Run code inlining a.
+ test = codeForInlineWithHandlersCanThrow(
+ a, b, c, twoGuards, callerHasCatchAll, inlineeHasCatchAll);
+ iterator = test.code.blocks.get(1).listIterator();
+ iterator.nextUntil(Instruction::isInvoke);
+ iterator.previous();
+ iterator.inlineInvoke(test.code, test.additionalCode.get(0));
+ result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
+
+ // Run code inlining b (where a is actually called).
+ test = codeForInlineWithHandlersCanThrow(
+ a, b, c, twoGuards, callerHasCatchAll, inlineeHasCatchAll);
+ iterator = test.code.blocks.get(1).listIterator();
+ iterator.nextUntil(Instruction::isInvoke);
+ iterator.previous();
+ iterator.inlineInvoke(test.code, test.additionalCode.get(1));
+ result = test.run();
+ assertEquals(Integer.toString(expectedB), result);
+ }
+
+ @Test
+ public void inlineCanWithHandlersThrow() {
+ // The base generated code will be:
+ //
+ // int method(int a, int b, int c) {
+ // try {
+ // return a(a, b, c); // Either a or b will be inlined here.
+ // } catch (NullPointerException e) {
+ // return -11;
+ // }
+ // // More handlers can be added.
+ // }
+ //
+ // int a(int a, int b, int c) {
+ // int result = 4 / a + 8 / b + 16 / c;
+ // if (result == 3) throw new Exception();
+ // return result
+ // }
+ //
+ // int b(int a, int b, int c) {
+ // try {
+ // int result = 4 / a + 8 / b + 16 / c;
+ // if (result == 3) throw new Exception();
+ // return result
+ // } catch (ArithmeticException e) {
+ // return -1;
+ // }
+ // // More handlers can be added.
+ // }
+ //
+ // void main(String[] args) {
+ // try {
+ // } catch (ArithmeticException e) {
+ // return -21;
+ // } catch (Exception e) {
+ // return -22;
+ // } catch (Throwable e) { // Smali/dex catchall.
+ // return -23;
+ // }
+ // }
+ //
+ // The flags (secondGuard, callerHasCatchAll and inlineeHasCatchAll) will add more catch
+ // handlers
+ List<Boolean> allBooleans = ImmutableList.of(true, false);
+ for (boolean secondGuard : allBooleans) {
+ for (boolean callerHasCatchAll : allBooleans) {
+ for (boolean inlineeHasCatchAll : allBooleans) {
+ // This throws no exception, but always returns 6.
+ runInlineWithHandlersCanThrow(
+ 2, 4, 8, secondGuard, callerHasCatchAll, inlineeHasCatchAll, 6, 6);
+
+ // This is result for calling a.
+ int resulta =
+ secondGuard ? -12
+ : (callerHasCatchAll ? -13 : -21);
+ // This is result for calling b.
+ int resultb = - 1;
+ // This group all throw ArithmeticException.
+ runInlineWithHandlersCanThrow(
+ 0, 4, 8, secondGuard, callerHasCatchAll, inlineeHasCatchAll, resulta, resultb);
+ runInlineWithHandlersCanThrow(
+ 2, 0, 8, secondGuard, callerHasCatchAll, inlineeHasCatchAll, resulta, resultb);
+ runInlineWithHandlersCanThrow(
+ 2, 4, 0, secondGuard, callerHasCatchAll, inlineeHasCatchAll, resulta, resultb);
+ }
+ }
+ }
+
+ // The following group will throw Exception from the inlinee.
+ runInlineWithHandlersCanThrow(4, 8, 16, false, false, false, -22, -22);
+ runInlineWithHandlersCanThrow(4, 8, 16, true, false, false, -12, -2);
+ runInlineWithHandlersCanThrow(4, 8, 16, false, true, false, -13, -13);
+ runInlineWithHandlersCanThrow(4, 8, 16, true, true, false, -12, -2);
+ runInlineWithHandlersCanThrow(4, 8, 16, false, false, true, -22, -3);
+ runInlineWithHandlersCanThrow(4, 8, 16, true, false, true, -12, -2);
+ runInlineWithHandlersCanThrow(4, 8, 16, false, true, true, -13, -3);
+ runInlineWithHandlersCanThrow(4, 8, 16, true, true, true, -12, -2);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java b/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java
new file mode 100644
index 0000000..1e35475
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java
@@ -0,0 +1,86 @@
+// 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.ir;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.ListIterator;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class InstructionIteratorTest extends SmaliTestBase {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ /**
+ * Simple test IR, which has three blocks:
+ *
+ * First block: Argument instructions
+ * Second block: Add instruction
+ * Third block: Return instruction
+ *
+ */
+ IRCode simpleCode() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String returnType = "int";
+ List<String> parameters = ImmutableList.of("int", "int");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 1,
+ " add-int v0, p0, p1",
+ " return p0"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Build the code, and split the code into three blocks.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ ListIterator<BasicBlock> blocks = code.listIterator();
+ InstructionListIterator iter = blocks.next().listIterator();
+ iter.nextUntil(i -> !i.isArgument());
+ iter.previous();
+ iter.split(1, code, blocks);
+ return code;
+ }
+
+ @Test
+ public void removeBeforeNext() {
+ IRCode code = simpleCode();
+
+ ListIterator<BasicBlock> blocks = code.listIterator();
+ ListIterator<Instruction> instructions = blocks.next().listIterator();
+ thrown.expect(IllegalStateException.class);
+ instructions.remove();
+ }
+
+ @Test
+ public void removeTwice() {
+ IRCode code = simpleCode();
+
+ ListIterator<BasicBlock> blocks = code.listIterator();
+ blocks.next();
+ ListIterator<Instruction> instructions = blocks.next().listIterator();
+ instructions.next();
+ instructions.remove();
+ thrown.expect(IllegalStateException.class);
+ instructions.remove();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
new file mode 100644
index 0000000..291c2c5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -0,0 +1,469 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+
+public class SplitBlockTest extends SmaliTestBase {
+
+ TestApplication codeWithoutCatchHandlers() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String returnType = "int";
+ List<String> parameters = ImmutableList.of("int", "int");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 0,
+ " add-int p0, p0, p0",
+ " sub-int p1, p1, p0",
+ " mul-int p0, p0, p1",
+ " return p0"
+ );
+
+ builder.addMainMethod(
+ 3,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, 1",
+ " const/4 v2, 5",
+ " invoke-static { v1, v2 }, LTest;->method(II)I",
+ " move-result v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Return the processed method for inspection.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+ return new TestApplication(application, method, code, valueNumberGenerator, options);
+ }
+
+ @Test
+ public void noCatchHandlers() {
+ final int initialBlockCount = 1;
+ final int argumentInstructions = 2;
+ final int firstBlockInstructions = 6;
+ // Try split between all non-argument instructions in the first block.
+ for (int i = argumentInstructions; i < firstBlockInstructions; i++) {
+ TestApplication test = codeWithoutCatchHandlers();
+ IRCode code = test.code;
+ assertEquals(initialBlockCount, code.blocks.size());
+
+ BasicBlock block = code.blocks.get(0);
+ int instructionCount = block.getInstructions().size();
+ assertEquals(firstBlockInstructions, instructionCount);
+
+ assertEquals(argumentInstructions, test.countArgumentInstructions());
+ assertEquals(firstBlockInstructions, block.getInstructions().size());
+ assertTrue(!block.getInstructions().get(i).isArgument());
+
+ InstructionListIterator iterator = test.listIteratorAt(block, i);
+ BasicBlock newBlock = iterator.split(code);
+ assertTrue(code.isConsistentSSA());
+
+ assertEquals(initialBlockCount + 1, code.blocks.size());
+ assertEquals(i + 1, code.blocks.get(0).getInstructions().size());
+ assertEquals(instructionCount - i, code.blocks.get(1).getInstructions().size());
+ assertSame(newBlock, code.blocks.get(1));
+
+ // Run code and check result (code in the test object is updated).
+ String result = test.run();
+ assertEquals("6", result);
+ }
+ }
+
+ @Test
+ public void noCatchHandlersSplitThree() {
+ final int initialBlockCount = 1;
+ final int argumentInstructions = 2;
+ final int firstBlockInstructions = 6;
+ // Try split out all non-argument instructions in the first block.
+ for (int i = argumentInstructions; i < firstBlockInstructions - 1; i++) {
+ TestApplication test = codeWithoutCatchHandlers();
+ IRCode code = test.code;
+ assertEquals(initialBlockCount, code.blocks.size());
+
+ BasicBlock block = code.blocks.get(0);
+ int instructionCount = block.getInstructions().size();
+ assertEquals(firstBlockInstructions, instructionCount);
+
+ assertEquals(argumentInstructions, test.countArgumentInstructions());
+ assertEquals(firstBlockInstructions, block.getInstructions().size());
+ assertTrue(!block.getInstructions().get(i).isArgument());
+
+ InstructionListIterator iterator = test.listIteratorAt(block, i);
+ BasicBlock newBlock = iterator.split(1, code);
+ assertTrue(code.isConsistentSSA());
+
+ assertEquals(initialBlockCount + 2, code.blocks.size());
+ assertEquals(i + 1, code.blocks.get(0).getInstructions().size());
+ assertEquals(2, code.blocks.get(1).getInstructions().size());
+ assertEquals(instructionCount - i - 1, code.blocks.get(2).getInstructions().size());
+ assertSame(newBlock, code.blocks.get(1));
+
+ // Run code and check result (code in the test object is updated).
+ String result = test.run();
+ assertEquals("6", result);
+ }
+ }
+
+ TestApplication codeWithCatchHandlers(boolean shouldThrow, boolean twoGuards) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String secondGuard = twoGuards ?
+ " .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch" : " ";
+
+ String returnType = "int";
+ List<String> parameters = ImmutableList.of("int", "int");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 0,
+ " :try_start",
+ " add-int p0, p0, p0",
+ " add-int p1, p1, p1",
+ " div-int p0, p0, p1",
+ " :try_end",
+ " .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+ secondGuard,
+ " :return",
+ " return p0",
+ " :catch",
+ " const/4 p0, -1",
+ " goto :return"
+ );
+
+ builder.addMainMethod(
+ 3,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, 2",
+ " const/4 v2, " + (shouldThrow ? "0" : "1"),
+ " invoke-static { v1, v2 }, LTest;->method(II)I",
+ " move-result v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Return the processed method for inspection.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ return new TestApplication(application, method, code, valueNumberGenerator, options);
+ }
+
+ public void hasCatchandlerIfThrowing(BasicBlock block) {
+ boolean throwing = false;
+ for (Instruction instruction : block.getInstructions()) {
+ throwing |= instruction.instructionTypeCanThrow();
+ }
+ assertEquals(throwing, block.hasCatchHandlers());
+ }
+
+ public void runCatchHandlerTest(boolean codeThrows, boolean twoGuards) {
+ final int secondBlockInstructions = 4;
+ final int initialBlockCount = 5;
+ // Try split between all instructions in second block.
+ for (int i = 1; i < secondBlockInstructions; i++) {
+ TestApplication test = codeWithCatchHandlers(codeThrows, twoGuards);
+ IRCode code = test.code;
+ assertEquals(initialBlockCount, code.blocks.size());
+
+ BasicBlock block = code.blocks.get(1);
+ int instructionCount = block.getInstructions().size();
+ assertEquals(secondBlockInstructions, instructionCount);
+
+ InstructionListIterator iterator = test.listIteratorAt(block, i);
+ BasicBlock newBlock = iterator.split(code);
+ assertTrue(code.isConsistentSSA());
+
+ assertEquals(initialBlockCount + 1, code.blocks.size());
+ assertEquals(i + 1, code.blocks.get(1).getInstructions().size());
+ assertEquals(instructionCount - i, newBlock.getInstructions().size());
+ assertSame(newBlock, code.blocks.get(2));
+
+ code.blocks.forEach(this::hasCatchandlerIfThrowing);
+
+ // Run code and check result (code in the test object is updated).
+ String result = test.run();
+ assertEquals(codeThrows ? "-1" : "2", result);
+ }
+ }
+
+ @Test
+ public void catchHandlers() {
+ runCatchHandlerTest(false, false);
+ runCatchHandlerTest(true, false);
+ runCatchHandlerTest(false, true);
+ runCatchHandlerTest(true, true);
+ }
+
+ public void runCatchHandlerSplitThreeTest(boolean codeThrows, boolean twoGuards) {
+ final int secondBlockInstructions = 4;
+ final int initialBlockCount = 5;
+ // Try split out all instructions in second block.
+ for (int i = 1; i < secondBlockInstructions - 1; i++) {
+ TestApplication test = codeWithCatchHandlers(codeThrows, twoGuards);
+ IRCode code = test.code;
+ assertEquals(initialBlockCount, code.blocks.size());
+
+ BasicBlock block = code.blocks.get(1);
+ int instructionCount = block.getInstructions().size();
+ assertEquals(secondBlockInstructions, instructionCount);
+
+ InstructionListIterator iterator = test.listIteratorAt(block, i);
+ BasicBlock newBlock = iterator.split(1, code);
+ assertTrue(code.isConsistentSSA());
+
+ assertEquals(initialBlockCount + 2, code.blocks.size());
+ assertEquals(i + 1, code.blocks.get(1).getInstructions().size());
+ assertEquals(2, newBlock.getInstructions().size());
+ assertEquals(instructionCount - i - 1, code.blocks.get(3).getInstructions().size());
+ assertSame(newBlock, code.blocks.get(2));
+
+ code.blocks.forEach(this::hasCatchandlerIfThrowing);
+
+ // Run code and check result (code in the test object is updated).
+ String result = test.run();
+ assertEquals(codeThrows ? "-1" : "2", result);
+ }
+ }
+
+ @Test
+ public void catchHandlersSplitThree() {
+ runCatchHandlerSplitThreeTest(false, false);
+ runCatchHandlerSplitThreeTest(true, false);
+ runCatchHandlerSplitThreeTest(false, true);
+ runCatchHandlerSplitThreeTest(true, true);
+ }
+
+ TestApplication codeWithIf(boolean hitTrueBranch) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String returnType = "int";
+ List<String> parameters = ImmutableList.of("int", "int");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 0,
+ " if-eq p0, p1, :eq",
+ " const/4 p0, 1",
+ " return p0",
+ " :eq",
+ " const/4 p0, 0",
+ " return p0"
+ );
+
+ builder.addMainMethod(
+ 3,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, 2",
+ " const/4 v2, " + (hitTrueBranch ? "2" : "3"),
+ " invoke-static { v1, v2 }, LTest;->method(II)I",
+ " move-result v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Return the processed method for inspection.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+ return new TestApplication(application, method, code, valueNumberGenerator, options);
+ }
+
+ public void runWithIfTest(boolean hitTrueBranch) {
+ final int initialBlockCount = 4;
+ final int argumentInstructions = 2;
+ final int firstBlockInstructions = 3;
+ // Try split between all non-argument instructions in the first block.
+ for (int i = argumentInstructions; i < firstBlockInstructions; i++) {
+ TestApplication test = codeWithIf(hitTrueBranch);
+ IRCode code = test.code;
+ assertEquals(initialBlockCount, code.blocks.size());
+
+ BasicBlock block = code.blocks.get(0);
+ int instructionCount = block.getInstructions().size();
+ assertEquals(firstBlockInstructions, instructionCount);
+
+ assertEquals(argumentInstructions, test.countArgumentInstructions());
+ assertEquals(firstBlockInstructions, block.getInstructions().size());
+ assertTrue(!block.getInstructions().get(i).isArgument());
+
+ InstructionListIterator iterator = test.listIteratorAt(block, i);
+ BasicBlock newBlock = iterator.split(code);
+ assertTrue(code.isConsistentSSA());
+
+ assertEquals(initialBlockCount + 1, code.blocks.size());
+ assertEquals(i + 1, code.blocks.get(0).getInstructions().size());
+ assertEquals(instructionCount - i, newBlock.getInstructions().size());
+ assertSame(newBlock, code.blocks.get(1));
+
+ // Run code and check result (code in the test object is updated).
+ String result = test.run();
+ assertEquals(hitTrueBranch ? "0" : "1", result);
+ }
+ }
+
+ @Test
+ public void withIf() {
+ runWithIfTest(false);
+ runWithIfTest(true);
+ }
+
+ public void splitBeforeReturn(boolean hitTrueBranch) {
+ TestApplication test = codeWithIf(hitTrueBranch);
+ IRCode code = test.code;
+ // Locate the exit block and split before the return (the first instruction in the block).
+ BasicBlock originalReturnBlock = code.getNormalExitBlock();
+ BasicBlock newReturnBlock = originalReturnBlock.listIterator().split(code);
+ // Modify the code to make the inserted block add the constant 10 to the original return value.
+ Value newConstValue = new Value(test.valueNumberGenerator.next(), -1, MoveType.SINGLE, null);
+ Value newReturnValue = new Value(test.valueNumberGenerator.next(), -1, MoveType.SINGLE, null);
+ Value oldReturnValue = newReturnBlock.listIterator().next().asReturn().returnValue();
+ newReturnBlock.listIterator().next().asReturn().returnValue().replaceUsers(newReturnValue);
+ Instruction constInstruction = new ConstNumber(ConstType.INT, newConstValue, 10);
+ Instruction addInstruction = new Add(
+ NumericType.INT,
+ newReturnValue,
+ oldReturnValue,
+ newConstValue);
+ InstructionListIterator iterator = originalReturnBlock.listIterator();
+ iterator.add(constInstruction);
+ iterator.add(addInstruction);
+
+ // Run code and check result (code in the test object is updated).
+ String result = test.run();
+ assertEquals(hitTrueBranch ? "10" : "11", result);
+ }
+
+ @Test
+ public void splitBeforeReturn() {
+ splitBeforeReturn(false);
+ splitBeforeReturn(true);
+ }
+
+ TestApplication codeWithSwitch(boolean hitCase) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String returnType = "int";
+ List<String> parameters = ImmutableList.of("int");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 0,
+ " packed-switch p0, :packed_switch_data",
+ " const/4 p0, 0x5",
+ " goto :return",
+ " :case_0",
+ " const/4 p0, 0x2",
+ " goto :return",
+ " :case_1",
+ " const/4 p0, 0x3",
+ " :return",
+ " return p0",
+ " :packed_switch_data",
+ " .packed-switch 0x0",
+ " :case_0",
+ " :case_1",
+ " .end packed-switch"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, " + (hitCase ? "1" : "2"),
+ " invoke-static { v1 }, LTest;->method(I)I",
+ " move-result v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication application = buildApplication(builder, options);
+
+ // Return the processed method for inspection.
+ ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+ DexEncodedMethod method = getMethod(application, signature);
+ IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+ return new TestApplication(application, method, code, valueNumberGenerator, options);
+ }
+
+ public void runWithSwitchTest(boolean hitCase) {
+ final int initialBlockCount = 5;
+ final int argumentInstructions = 1;
+ final int firstBlockInstructions = 2;
+ // Try split between all non-argument instructions in the first block.
+ for (int i = argumentInstructions; i < firstBlockInstructions; i++) {
+ TestApplication test = codeWithSwitch(hitCase);
+ IRCode code = test.code;
+ assertEquals(initialBlockCount, code.blocks.size());
+
+ BasicBlock block = code.blocks.get(0);
+ int instructionCount = block.getInstructions().size();
+ assertEquals(firstBlockInstructions, instructionCount);
+
+ assertEquals(argumentInstructions, test.countArgumentInstructions());
+ assertEquals(firstBlockInstructions, block.getInstructions().size());
+ assertTrue(!block.getInstructions().get(i).isArgument());
+
+ InstructionListIterator iterator = test.listIteratorAt(block, i);
+ BasicBlock newBlock = iterator.split(code);
+ assertTrue(code.isConsistentSSA());
+
+ assertEquals(initialBlockCount + 1, code.blocks.size());
+ assertEquals(i + 1, code.blocks.get(0).getInstructions().size());
+ assertEquals(instructionCount - i, newBlock.getInstructions().size());
+ assertSame(newBlock, code.blocks.get(1));
+
+ // Run code and check result (code in the test object is updated).
+ String result = test.run();
+ assertEquals(hitCase ? "3" : "5", result);
+ }
+ }
+
+ @Test
+ public void withSwitch() {
+ runWithSwitchTest(false);
+ runWithSwitchTest(true);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/deterministic/DeterministicProcessingTest.java b/src/test/java/com/android/tools/r8/ir/deterministic/DeterministicProcessingTest.java
new file mode 100644
index 0000000..eaf9fe3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/deterministic/DeterministicProcessingTest.java
@@ -0,0 +1,47 @@
+// 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.ir.deterministic;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class DeterministicProcessingTest extends SmaliTestBase {
+
+ @Test
+ public void test()
+ throws IOException, ExecutionException, ProguardRuleParserException, CompilationException {
+ final int ITERATIONS = 10;
+ R8Command.Builder builder =
+ R8Command.builder()
+ .addProgramFiles(ToolHelper.getClassFileForTestClass(TestClass.class))
+ .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()));
+ List<byte[]> results = new ArrayList<>();
+ for (int i = 0; i < ITERATIONS; i++) {
+ AndroidApp result =
+ ToolHelper.runR8(
+ builder.build(), options -> options.testing.randomizeCallGraphLeaves = true);
+ List<byte[]> dex = result.writeToMemory();
+ assertEquals(1, dex.size());
+ results.add(dex.get(0));
+ System.out.println(dex.get(0).length);
+ }
+ for (int i = 0; i < ITERATIONS - 1; i++) {
+ assertArrayEquals(results.get(i), results.get(i + 1));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/deterministic/TestClass.java b/src/test/java/com/android/tools/r8/ir/deterministic/TestClass.java
new file mode 100644
index 0000000..5bbfa51
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/deterministic/TestClass.java
@@ -0,0 +1,70 @@
+// 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.ir.deterministic;
+
+public class TestClass {
+
+ // This will be inlined, causing some of the calls below to go away providing more
+ // inlining opportunities.
+ public static boolean alwaysFalse() {
+ return false;
+ }
+
+ public static int a0() {
+ return b0();
+ }
+
+ public static int b0() {
+ if (alwaysFalse()) {
+ a0();
+ return 0;
+ }
+ return 1;
+ }
+
+ public static int a1() {
+ return b1();
+ }
+
+ public static int b1() {
+ if (alwaysFalse()) {
+ a1();
+ return 0;
+ }
+ return 1;
+ }
+
+ public static int a2() {
+ return b2();
+ }
+
+ public static int b2() {
+ return c2();
+ }
+
+ public static int c2() {
+ if (alwaysFalse()) {
+ a2();
+ return 0;
+ }
+ return 1;
+ }
+
+ public static int a3() {
+ return b3();
+ }
+
+ public static int b3() {
+ return c3();
+ }
+
+ public static int c3() {
+ if (alwaysFalse()) {
+ a3();
+ return 0;
+ }
+ return 1;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
new file mode 100644
index 0000000..7eebf9b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
@@ -0,0 +1,65 @@
+// 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.ir.regalloc;
+
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Value;
+import org.junit.Test;
+
+public class IdenticalAfterRegisterAllocationTest {
+
+ private static class MockRegisterAllocator implements RegisterAllocator {
+ @Override
+ public void allocateRegisters(boolean debug) {
+ }
+
+ @Override
+ public int registersUsed() {
+ return 0;
+ }
+
+ @Override
+ public int getRegisterForValue(Value value, int instructionNumber) {
+ // Use the value number as the register number.
+ return value.getNumber();
+ }
+
+ @Override
+ public boolean argumentValueUsesHighRegister(Value value, int instructionNumber) {
+ return false;
+ }
+ }
+
+ @Test
+ public void equalityOfConstantOperands() {
+ RegisterAllocator allocator = new MockRegisterAllocator();
+ Value value0 = new Value(0, -1, MoveType.SINGLE, null);
+ ConstNumber const0 = new ConstNumber(ConstType.INT, value0, 0);
+ Value value1 = new Value(1, -1, MoveType.SINGLE, null);
+ ConstNumber const1 = new ConstNumber(ConstType.INT, value1, 1);
+ Value value2 = new Value(2, -1, MoveType.SINGLE, null);
+ ConstNumber const2 = new ConstNumber(ConstType.INT, value2, 2);
+ Value value3 = new Value(2, -1, MoveType.SINGLE, null);
+ Add add0 = new Add(NumericType.INT, value3, value0, value1);
+ Add add1 = new Add(NumericType.INT, value3, value0, value2);
+ value0.computeNeedsRegister();
+ assertTrue(value0.needsRegister());
+ value1.computeNeedsRegister();
+ assertFalse(value1.needsRegister());
+ value2.computeNeedsRegister();
+ assertFalse(value2.needsRegister());
+ value3.computeNeedsRegister();
+ assertTrue(value3.needsRegister());
+ // value1 and value2 represent different constants and the additions are therefore
+ // not equivalent.
+ assertFalse(add0.identicalAfterRegisterAllocation(add1, allocator));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
new file mode 100644
index 0000000..26b6e18
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -0,0 +1,352 @@
+// 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.ir.regalloc;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Move;
+import com.android.tools.r8.ir.code.MoveType;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import org.junit.Test;
+
+public class RegisterMoveSchedulerTest {
+
+ private static class CollectMovesIterator implements InstructionListIterator {
+
+ private LinkedList<Instruction> list = new LinkedList<>();
+ private ListIterator<Instruction> it = list.listIterator();
+
+ public Move get(int i) {
+ return list.get(i).asMove();
+ }
+
+ public int size() {
+ return list.size();
+ }
+
+ @Override
+ public void replaceCurrentInstruction(Instruction newInstruction) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+
+ @Override
+ public Instruction next() {
+ return it.next();
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ return it.hasPrevious();
+ }
+
+ @Override
+ public Instruction previous() {
+ return it.previous();
+ }
+
+ @Override
+ public int nextIndex() {
+ return it.nextIndex();
+ }
+
+ @Override
+ public int previousIndex() {
+ return it.previousIndex();
+ }
+
+ @Override
+ public void remove() {
+ it.remove();
+ }
+
+ @Override
+ public void detach() {
+ remove();
+ }
+
+ @Override
+ public void set(Instruction instruction) {
+ it.set(instruction);
+ }
+
+ @Override
+ public void add(Instruction instruction) {
+ it.add(instruction);
+ }
+
+ @Override
+ public BasicBlock split(IRCode code, ListIterator<BasicBlock> blockIterator) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public BasicBlock split(int instructions, IRCode code, ListIterator<BasicBlock> blockIterator) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public BasicBlock inlineInvoke(
+ IRCode code, IRCode inlinee, ListIterator<BasicBlock> blockIterator,
+ List<BasicBlock> blocksToRemove) {
+ throw new Unimplemented();
+ }
+ }
+
+ @Test
+ public void testSingleParallelMove() {
+ CollectMovesIterator moves = new CollectMovesIterator();
+ int temp = 42;
+ RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+ scheduler.addMove(new RegisterMove(0, 1, MoveType.SINGLE));
+ scheduler.addMove(new RegisterMove(1, 0, MoveType.SINGLE));
+ scheduler.schedule();
+ assertEquals(3, moves.size());
+ Move tempMove = moves.get(0);
+ Move firstMove = moves.get(1);
+ Move secondMove = moves.get(2);
+ assertEquals(MoveType.SINGLE, tempMove.outType());
+ assertEquals(MoveType.SINGLE, firstMove.outType());
+ assertEquals(MoveType.SINGLE, secondMove.outType());
+ assertEquals(temp, tempMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(
+ tempMove.src().asFixedRegisterValue().getRegister(),
+ firstMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(temp, secondMove.src().asFixedRegisterValue().getRegister());
+ assertEquals(
+ firstMove.src().asFixedRegisterValue().getRegister(),
+ secondMove.dest().asFixedRegisterValue().getRegister());
+ }
+
+ @Test
+ public void testWideParallelMove() {
+ CollectMovesIterator moves = new CollectMovesIterator();
+ int temp = 42;
+ RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+ scheduler.addMove(new RegisterMove(0, 2, MoveType.WIDE));
+ scheduler.addMove(new RegisterMove(2, 0, MoveType.WIDE));
+ scheduler.schedule();
+ assertEquals(3, moves.size());
+ Move tempMove = moves.get(0);
+ Move firstMove = moves.get(1);
+ Move secondMove = moves.get(2);
+ assertEquals(MoveType.WIDE, tempMove.outType());
+ assertEquals(MoveType.WIDE, firstMove.outType());
+ assertEquals(MoveType.WIDE, secondMove.outType());
+ assertEquals(temp, tempMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(
+ tempMove.src().asFixedRegisterValue().getRegister(),
+ firstMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(temp, secondMove.src().asFixedRegisterValue().getRegister());
+ assertEquals(
+ firstMove.src().asFixedRegisterValue().getRegister(),
+ secondMove.dest().asFixedRegisterValue().getRegister());
+ }
+
+ @Test
+ public void testMixedParralelMove() {
+ CollectMovesIterator moves = new CollectMovesIterator();
+ int temp = 42;
+ RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+ scheduler.addMove(new RegisterMove(1, 0, MoveType.WIDE));
+ scheduler.addMove(new RegisterMove(0, 1, MoveType.SINGLE));
+ scheduler.schedule();
+ assertEquals(3, moves.size());
+ Move tempMove = moves.get(0).asMove();
+ Move firstMove = moves.get(1).asMove();
+ Move secondMove = moves.get(2).asMove();
+ assertEquals(MoveType.WIDE, tempMove.outType());
+ assertEquals(MoveType.SINGLE, firstMove.outType());
+ assertEquals(MoveType.WIDE, secondMove.outType());
+ assertEquals(temp, tempMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(
+ tempMove.src().asFixedRegisterValue().getRegister(),
+ firstMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(temp, secondMove.src().asFixedRegisterValue().getRegister());
+ assertEquals(
+ firstMove.src().asFixedRegisterValue().getRegister(),
+ secondMove.dest().asFixedRegisterValue().getRegister());
+ }
+
+ @Test
+ public void testMixedParralelMove2() {
+ CollectMovesIterator moves = new CollectMovesIterator();
+ int temp = 42;
+ RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+ scheduler.addMove(new RegisterMove(0, 1, MoveType.SINGLE));
+ scheduler.addMove(new RegisterMove(1, 0, MoveType.WIDE));
+ scheduler.schedule();
+ assertEquals(3, moves.size());
+ Move tempMove = moves.get(0).asMove();
+ Move firstMove = moves.get(1).asMove();
+ Move secondMove = moves.get(2).asMove();
+ assertEquals(MoveType.WIDE, tempMove.outType());
+ assertEquals(MoveType.SINGLE, firstMove.outType());
+ assertEquals(MoveType.WIDE, secondMove.outType());
+ assertEquals(temp, tempMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(
+ tempMove.src().asFixedRegisterValue().getRegister(),
+ firstMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(temp, secondMove.src().asFixedRegisterValue().getRegister());
+ assertEquals(
+ firstMove.src().asFixedRegisterValue().getRegister(),
+ secondMove.dest().asFixedRegisterValue().getRegister());
+ }
+
+ @Test
+ public void testSlideWideMoves() {
+ CollectMovesIterator moves = new CollectMovesIterator();
+ int temp = 42;
+ RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+ scheduler.addMove(new RegisterMove(0, 1, MoveType.WIDE));
+ scheduler.addMove(new RegisterMove(2, 3, MoveType.WIDE));
+ scheduler.schedule();
+ assertEquals(2, moves.size());
+ Move firstMove = moves.get(0).asMove();
+ Move secondMove = moves.get(1).asMove();
+ assertEquals(MoveType.WIDE, firstMove.outType());
+ assertEquals(MoveType.WIDE, secondMove.outType());
+ assertEquals(0, firstMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(1, firstMove.src().asFixedRegisterValue().getRegister());
+ assertEquals(2, secondMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(3, secondMove.src().asFixedRegisterValue().getRegister());
+ }
+
+ @Test
+ public void testSlideWideMoves2() {
+ CollectMovesIterator moves = new CollectMovesIterator();
+ int temp = 42;
+ RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+ scheduler.addMove(new RegisterMove(2, 1, MoveType.WIDE));
+ scheduler.addMove(new RegisterMove(0, 3, MoveType.WIDE));
+ scheduler.schedule();
+ assertEquals(3, moves.size());
+ Move firstMove = moves.get(0).asMove();
+ Move secondMove = moves.get(1).asMove();
+ Move thirdMove = moves.get(2).asMove();
+ assertEquals(MoveType.WIDE, firstMove.outType());
+ assertEquals(MoveType.WIDE, secondMove.outType());
+ assertEquals(MoveType.WIDE, thirdMove.outType());
+ assertEquals(42, firstMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(1, firstMove.src().asFixedRegisterValue().getRegister());
+ assertEquals(0, secondMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(3, secondMove.src().asFixedRegisterValue().getRegister());
+ assertEquals(2, thirdMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(42, thirdMove.src().asFixedRegisterValue().getRegister());
+ }
+
+ @Test
+ public void testWideBlockedByTwoSingle() {
+ CollectMovesIterator moves = new CollectMovesIterator();
+ int temp = 42;
+ RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+ scheduler.addMove(new RegisterMove(2, 0, MoveType.WIDE));
+ scheduler.addMove(new RegisterMove(0, 2, MoveType.SINGLE));
+ scheduler.addMove(new RegisterMove(1, 3, MoveType.SINGLE));
+ scheduler.schedule();
+ assertEquals(4, moves.size());
+ Move firstMove = moves.get(0).asMove();
+ Move secondMove = moves.get(1).asMove();
+ Move thirdMove = moves.get(2).asMove();
+ Move fourthMove = moves.get(3).asMove();
+ assertEquals(MoveType.WIDE, firstMove.outType());
+ assertEquals(MoveType.SINGLE, secondMove.outType());
+ assertEquals(MoveType.SINGLE, thirdMove.outType());
+ assertEquals(MoveType.WIDE, fourthMove.outType());
+ assertEquals(temp, firstMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(0, secondMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(1, thirdMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(2, fourthMove.dest().asFixedRegisterValue().getRegister());
+ }
+
+ @Test
+ public void testSingleBlockedBySecondPartOfWide() {
+ CollectMovesIterator moves = new CollectMovesIterator();
+ int temp = 42;
+ RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+ scheduler.addMove(new RegisterMove(0, 2, MoveType.WIDE));
+ scheduler.addMove(new RegisterMove(3, 0, MoveType.SINGLE));
+ scheduler.schedule();
+ assertEquals(3, moves.size());
+ Move firstMove = moves.get(0).asMove();
+ Move secondMove = moves.get(1).asMove();
+ Move thirdMove = moves.get(2).asMove();
+ assertEquals(MoveType.WIDE, firstMove.outType());
+ assertEquals(MoveType.SINGLE, secondMove.outType());
+ assertEquals(MoveType.WIDE, thirdMove.outType());
+ assertEquals(temp, firstMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(2, firstMove.src().asFixedRegisterValue().getRegister());
+ assertEquals(3, secondMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(0, secondMove.src().asFixedRegisterValue().getRegister());
+ assertEquals(0, thirdMove.dest().asFixedRegisterValue().getRegister());
+ assertEquals(temp, thirdMove.src().asFixedRegisterValue().getRegister());
+ }
+
+ @Test
+ public void multipleWideMoves() {
+ CollectMovesIterator moves = new CollectMovesIterator();
+ int temp = 42;
+ RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+ scheduler.addMove(new RegisterMove(14, 11, MoveType.WIDE));
+ scheduler.addMove(new RegisterMove(16, 13, MoveType.WIDE));
+ scheduler.addMove(new RegisterMove(10, 17, MoveType.WIDE));
+ scheduler.addMove(new RegisterMove(12, 19, MoveType.WIDE));
+ scheduler.schedule();
+ // In order to resolve these moves, we need to use two temporary register pairs.
+ assertEquals(6, moves.size());
+ assertEquals(42, moves.get(0).asMove().dest().asFixedRegisterValue().getRegister());
+ assertEquals(11, moves.get(0).asMove().src().asFixedRegisterValue().getRegister());
+ assertEquals(44, moves.get(1).asMove().dest().asFixedRegisterValue().getRegister());
+ assertEquals(13, moves.get(1).asMove().src().asFixedRegisterValue().getRegister());
+ assertEquals(12, moves.get(2).asMove().dest().asFixedRegisterValue().getRegister());
+ }
+
+ @Test
+ public void multipleLiveTempRegisters() {
+ CollectMovesIterator moves = new CollectMovesIterator();
+ int temp = 42;
+ RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+ scheduler.addMove(new RegisterMove(26, 22, MoveType.SINGLE));
+ scheduler.addMove(new RegisterMove(29, 24, MoveType.WIDE));
+ scheduler.addMove(new RegisterMove(28, 26, MoveType.OBJECT));
+ scheduler.addMove(new RegisterMove(23, 28, MoveType.WIDE));
+ scheduler.schedule();
+ // For this example we need recursive unblocking.
+ assertEquals(6, moves.size());
+ assertEquals(42, moves.get(0).asMove().dest().asFixedRegisterValue().getRegister());
+ assertEquals(26, moves.get(0).asMove().src().asFixedRegisterValue().getRegister());
+ assertEquals(26, moves.get(1).asMove().dest().asFixedRegisterValue().getRegister());
+ assertEquals(22, moves.get(1).asMove().src().asFixedRegisterValue().getRegister());
+ assertEquals(43, moves.get(2).asMove().dest().asFixedRegisterValue().getRegister());
+ assertEquals(28, moves.get(2).asMove().src().asFixedRegisterValue().getRegister());
+ assertEquals(28, moves.get(3).asMove().dest().asFixedRegisterValue().getRegister());
+ assertEquals(42, moves.get(3).asMove().src().asFixedRegisterValue().getRegister());
+ assertEquals(29, moves.get(4).asMove().dest().asFixedRegisterValue().getRegister());
+ assertEquals(24, moves.get(4).asMove().src().asFixedRegisterValue().getRegister());
+ assertEquals(23, moves.get(5).asMove().dest().asFixedRegisterValue().getRegister());
+ assertEquals(43, moves.get(5).asMove().src().asFixedRegisterValue().getRegister());
+ }
+
+ // Debugging aid.
+ private void printMoves(List<Instruction> moves) {
+ System.out.println("Generated moves:");
+ System.out.println("----------------");
+ for (Instruction move : moves) {
+ System.out.println(move.asMove().dest().asFixedRegisterValue().getRegister() + " <- " +
+ move.asMove().src().asFixedRegisterValue().getRegister() + " (" + move.outType() + ")");
+ }
+ System.out.println("----------------");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSet.jar b/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSet.jar
new file mode 100644
index 0000000..2990356
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSet.jar
Binary files differ
diff --git a/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSetRegressionTest.java b/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSetRegressionTest.java
new file mode 100644
index 0000000..96df2b8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSetRegressionTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jar.UnicodeSetRegression;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ArtErrorParser;
+import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
+import com.android.tools.r8.utils.ArtErrorParser.ArtErrorParserException;
+import com.android.tools.r8.utils.DexInspector;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.ComparisonFailure;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class UnicodeSetRegressionTest {
+
+ private static final String JAR_FILE =
+ "src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSet.jar";
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ private AndroidApp dexFromDX() throws IOException {
+ return ToolHelper.runDexer(JAR_FILE, temp.newFolder("dx-dex").getPath());
+ }
+
+ @Test
+ public void testUnicodeSetFromDex()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ Path combinedInput = temp.getRoot().toPath().resolve("all.zip");
+ Path oatFile = temp.getRoot().toPath().resolve("all.oat");
+ ToolHelper.runR8(dexFromDX(), combinedInput);
+ ToolHelper.runDex2Oat(combinedInput, oatFile);
+ }
+
+ @Test
+ public void testUnicodeSetFromJar()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ Path combinedInput = temp.getRoot().toPath().resolve("all.zip");
+ Path oatFile = temp.getRoot().toPath().resolve("all.oat");
+ AndroidApp result = ToolHelper.runR8(JAR_FILE, combinedInput.toString());
+ try {
+ ToolHelper.runDex2Oat(combinedInput, oatFile);
+ } catch (AssertionError e) {
+ AndroidApp fromDexApp = ToolHelper.runR8(dexFromDX());
+ DexInspector fromDex = new DexInspector(fromDexApp);
+ DexInspector fromJar = new DexInspector(result);
+ List<ArtErrorInfo> errors;
+ try {
+ errors = ArtErrorParser.parse(e.getMessage());
+ } catch (ArtErrorParserException parserException) {
+ System.err.println(parserException.toString());
+ throw e;
+ }
+ if (errors.isEmpty()) {
+ throw e;
+ }
+ for (ArtErrorInfo error : errors.subList(0, errors.size() - 1)) {
+ System.err.println(new ComparisonFailure(error.getMessage(),
+ "REFERENCE\n" + error.dump(fromDex, false) + "\nEND REFERENCE",
+ "PROCESSED\n" + error.dump(fromJar, true) + "\nEND PROCESSED").toString());
+ }
+ ArtErrorInfo error = errors.get(errors.size() - 1);
+ throw new ComparisonFailure(error.getMessage(),
+ "REFERENCE\n" + error.dump(fromDex, false) + "\nEND REFERENCE",
+ "PROCESSED\n" + error.dump(fromJar, true) + "\nEND PROCESSED");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
new file mode 100644
index 0000000..72bbaed
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
@@ -0,0 +1,339 @@
+// 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.jasmin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.debuginfo.DebugInfoInspector;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class DebugLocalTests extends JasminTestBase {
+
+ @Test
+ public void testSwap() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+ MethodSignature foo = clazz.addVirtualMethod("foo", ImmutableList.of("Ljava/lang/String;"), "V",
+ // The first three vars are out-of-order to verify that the order is not relied on.
+ ".var 5 is t I from L4 to L6",
+ ".var 1 is bar Ljava/lang/String; from L0 to L9",
+ ".var 0 is this LTest; from L0 to L9",
+ ".var 2 is x I from L1 to L9",
+ ".var 3 is y I from L2 to L9",
+ ".var 4 is z I from L3 to L9",
+ ".var 5 is foobar Ljava/lang/String; from L7 to L9",
+ ".limit locals 6",
+ ".limit stack 2",
+ "L0:",
+ ".line 23",
+ " iconst_1",
+ " istore 2",
+ "L1:",
+ ".line 24",
+ " iconst_2",
+ " istore 3",
+ "L2:",
+ ".line 25",
+ " iconst_3",
+ " istore 4",
+ "L3:",
+ " .line 27",
+ " iload 3",
+ " istore 5",
+ "L4:",
+ " .line 28",
+ " iload 2",
+ " istore 3",
+ "L5:",
+ " .line 29",
+ " iload 5",
+ " istore 2",
+ "L6:",
+ " .line 32",
+ " new java/lang/StringBuilder",
+ " dup",
+ " invokespecial java/lang/StringBuilder/<init>()V",
+ " ldc \"And the value of y is: \"",
+ " invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " iload 2",
+ " invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
+ " iload 3",
+ " invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
+ " iload 4",
+ " invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
+ " invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;",
+ " astore 5",
+ "L7:",
+ " .line 34",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " aload 5",
+ " invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+ "L8:",
+ " .line 35",
+ " return",
+ "L9:");
+
+ clazz.addMainMethod(
+ ".limit stack 3",
+ ".limit locals 1",
+ " new Test",
+ " dup",
+ " invokespecial Test/<init>()V",
+ " ldc \"Fsg\"",
+ " invokevirtual Test/foo(Ljava/lang/String;)V",
+ " return");
+
+ String expected = "And the value of y is: 213";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+
+ AndroidApp jasminApp = builder.build();
+ AndroidApp d8App = ToolHelper.runD8(jasminApp);
+
+ DexInspector inspector = new DexInspector(d8App);
+ ClassSubject classSubject = inspector.clazz("Test");
+ MethodSubject methodSubject = classSubject.method(foo);
+ DexCode code = methodSubject.getMethod().getCode().asDexCode();
+ DexDebugInfo info = code.getDebugInfo();
+ assertEquals(23, info.startLine);
+ assertEquals(1, info.parameters.length);
+ assertEquals("bar", info.parameters[0].toString());
+
+ // TODO(zerny): Verify the debug computed locals information.
+
+ String artResult = runOnArt(d8App, clazz.name);
+ assertEquals(expected, artResult);
+ }
+
+ @Test
+ public void testNoLocalInfoOnStack() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+ MethodSignature foo = clazz.addVirtualMethod("foo", ImmutableList.of(), "I",
+ ".var 0 is this LTest; from Init to End",
+ ".var 1 is x I from XStart to XEnd",
+ ".limit locals 2",
+ ".limit stack 1",
+ "Init:",
+ ".line 1",
+ " ldc 0",
+ " istore 1",
+ "XStart:",
+ ".line 2",
+ " ldc 42",
+ " istore 1",
+ " iload 1",
+ "XEnd:",
+ ".line 3",
+ " ireturn",
+ "End:");
+
+ clazz.addMainMethod(
+ ".limit stack 3",
+ ".limit locals 1",
+ " new Test",
+ " dup",
+ " invokespecial Test/<init>()V",
+ " invokevirtual Test/foo()I",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " swap",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ String expected = "42";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+
+ AndroidApp jasminApp = builder.build();
+ AndroidApp d8App = ToolHelper.runD8(jasminApp);
+
+ DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
+ info.checkStartLine(1);
+ info.checkLineHasExactLocals(1, "this", "Test");
+ info.checkLineHasExactLocals(2, "this", "Test", "x", "int");
+ info.checkLineHasExactLocals(3, "this", "Test");
+
+ String artResult = runOnArt(d8App, clazz.name);
+ assertEquals(expected, artResult);
+ }
+
+ // Check that we properly handle switching a local slot from one variable to another.
+ @Test
+ public void checkLocalChange() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ MethodSignature foo = clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
+ ".limit stack 2",
+ ".limit locals 2",
+ ".var 0 is param I from MethodStart to MethodEnd",
+ ".var 1 is x I from LabelXStart to LabelXEnd",
+ ".var 1 is y I from LabelYStart to LabelYEnd",
+
+ "MethodStart:",
+ ".line 1",
+
+ "LabelXStart:",
+ " ldc 0",
+ " istore 1",
+ ".line 2",
+ " invokestatic Test/ensureLine()V",
+ "LabelXEnd:",
+
+ " iload 0",
+ " lookupswitch",
+ " 1: Case1",
+ " default: CaseDefault",
+
+ "Case1:",
+ " ldc 42",
+ " istore 1",
+ "LabelYStart:",
+ ".line 3",
+ " invokestatic Test/ensureLine()V",
+ " goto AfterSwitch",
+
+ "CaseDefault:",
+ " ldc -42",
+ " istore 1",
+ ".line 4",
+ " invokestatic Test/ensureLine()V",
+
+ "AfterSwitch:",
+ ".line 5",
+ " iload 1",
+ " ireturn",
+ "LabelYEnd:",
+
+ "MethodEnd:"
+ );
+
+ clazz.addStaticMethod("ensureLine", ImmutableList.of(), "V",
+ ".limit stack 0",
+ ".limit locals 0",
+ " return");
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " ldc 0",
+ " invokestatic Test/foo(I)I",
+ " ldc 1",
+ " invokestatic Test/foo(I)I",
+ " pop",
+ " return");
+
+ String expected = "";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+
+ AndroidApp jasminApp = builder.build();
+ AndroidApp d8App = ToolHelper.runD8(jasminApp);
+ String artResult = runOnArt(d8App, clazz.name);
+ assertEquals(expected, artResult);
+
+ DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
+ info.checkStartLine(1);
+ info.checkLineHasExactLocals(1, "param", "int");
+ info.checkLineHasExactLocals(2, "param", "int", "x", "int");
+ info.checkLineHasExactLocals(3, "param", "int", "y", "int");
+ info.checkLineHasExactLocals(4, "param", "int", "y", "int");
+ info.checkLineHasExactLocals(5, "param", "int", "y", "int");
+ }
+
+ @Test
+ public void testLocalManyRanges() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ MethodSignature foo = clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
+ ".limit stack 2",
+ ".limit locals 2",
+ ".var 0 is param I from Init to End",
+ ".var 1 is x I from LabelStart1 to LabelEnd1",
+ ".var 1 is x I from LabelStart2 to LabelEnd2",
+ ".var 1 is x I from LabelStart3 to LabelEnd3",
+ ".var 1 is x I from LabelStart4 to LabelEnd4",
+ "Init:",
+ ".line 1",
+ " iload 0",
+ " istore 1",
+
+ "LabelStart1:",
+ ".line 2",
+ " invokestatic Test/ensureLine()V",
+ "LabelEnd1:",
+ ".line 3",
+ " invokestatic Test/ensureLine()V",
+
+ "LabelStart2:",
+ ".line 4",
+ " invokestatic Test/ensureLine()V",
+ "LabelEnd2:",
+ ".line 5",
+ " invokestatic Test/ensureLine()V",
+
+ "LabelStart3:",
+ ".line 6",
+ " invokestatic Test/ensureLine()V",
+ "LabelEnd3:",
+ ".line 7",
+ " invokestatic Test/ensureLine()V",
+
+ "LabelStart4:",
+ ".line 8",
+ " invokestatic Test/ensureLine()V",
+ "LabelEnd4:",
+ ".line 9",
+ " invokestatic Test/ensureLine()V",
+ " iload 1",
+ " ireturn",
+ "End:"
+ );
+
+ clazz.addStaticMethod("ensureLine", ImmutableList.of(), "V",
+ ".limit stack 0",
+ ".limit locals 0",
+ " return");
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 42",
+ " invokestatic Test/foo(I)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ String expected = "42";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+
+ AndroidApp jasminApp = builder.build();
+ AndroidApp d8App = ToolHelper.runD8(jasminApp);
+ String artResult = runOnArt(d8App, clazz.name);
+ assertEquals(expected, artResult);
+
+ DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
+ info.checkStartLine(1);
+ info.checkLineHasExactLocals(1, "param", "int");
+ info.checkLineHasExactLocals(2, "param", "int", "x", "int");
+ info.checkLineHasExactLocals(3, "param", "int");
+ info.checkLineHasExactLocals(4, "param", "int", "x", "int");
+ info.checkLineHasExactLocals(5, "param", "int");
+ info.checkLineHasExactLocals(6, "param", "int", "x", "int");
+ info.checkLineHasExactLocals(7, "param", "int");
+ info.checkLineHasExactLocals(8, "param", "int", "x", "int");
+ info.checkLineHasExactLocals(9, "param", "int");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java b/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java
new file mode 100644
index 0000000..984e40e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java
@@ -0,0 +1,125 @@
+// 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.jasmin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class InvalidDebugInfoTests extends JasminTestBase {
+
+ // This is a regression test for invalid live-ranges of locals generated by some old Java
+ // compilers. The issue is that a local slot may have been initialized outside the live-scope of
+ // the variable and then the subsequent live-scope of the variable extends beyond its actual
+ // liveness. In the example below the variable 'y' is initialized outside its range (it is thus
+ // associated with the local 'x' (the SSA value is unaffected by the istore). Finally the 'return'
+ // forces a read of all supposedly live variables before exiting. Here the attempt to read 'y'
+ // will actually be a read of 'x'.
+ @Test
+ public void testInvalidInfoThrow() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of("I"), "V",
+ ".limit stack 3",
+ ".limit locals 4",
+ ".var 0 is x I from LabelInit to LabelExit",
+ ".var 1 is y I from LabelLocalStart to LabelExit",
+ ".var 2 is e Ljava/lang/Exception; from LabelCatchStart to LabelCatchEnd",
+ // var 3 is the jsr address
+ "LabelInit:",
+ "LabelTryStart:",
+ " ldc 84",
+ " iload 0",
+ " dup",
+ " istore 1", // init local[1] to value of local[0] (eg, 'x' since 'y' is not live yet).
+ " idiv",
+ " istore 0",
+ "LabelLocalStart:",
+ " jsr LabelPrint",
+ " goto LabelExit",
+ "LabelTryEnd:",
+ "LabelCatchStart:",
+ " astore 2",
+ " jsr LabelPrint",
+ " return", // y is not actually live here.
+ "LabelCatchEnd:",
+ "LabelPrint:",
+ " astore 3",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iload 0",
+ " invokevirtual java/io/PrintStream/println(I)V",
+ " ret 3",
+ "LabelExit:",
+ " return",
+ ".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatchStart"
+ );
+
+ clazz.addMainMethod(
+ ".limit stack 1",
+ ".limit locals 1",
+ " ldc 2",
+ " invokestatic Test/foo(I)V",
+ " ldc 0",
+ " invokestatic Test/foo(I)V",
+ " return");
+
+ String expected = "42\n0\n";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArtD8(builder, clazz.name);
+ assertEquals(expected, artResult);
+ }
+
+ // Regression test to check that we properly add UninitializedLocal SSA values for methods that
+ // have arguments without local info. To witness this bug, we also need "invalid" debug info, eg,
+ // in this test the scope of "y" (local 2) spans the exceptional edge in which it is not live.
+ @Test
+ public void testInvalidInfoBug37722432() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of("I","I"), "V",
+ ".limit stack 2",
+ ".limit locals 3",
+ ".var 0 is x I from LabelInit to LabelExit",
+ // Synthesized arg (no local info)
+ ".var 2 is y I from LabelLocalStart to LabelExit",
+ ".catch java/lang/Exception from LabelInit to LabelCatch using LabelCatch",
+ "LabelInit:", // Start of try block targets catch with a state excluding 'y'.
+ " ldc 84",
+ " iload 0",
+ " idiv",
+ " istore 2",
+ "LabelLocalStart:",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iload 2",
+ " invokevirtual java/io/PrintStream/println(I)V",
+ " return",
+ "LabelCatch:", // Catch target appears to include 'y' but actually does not.
+ " pop",
+ " return",
+ "LabelExit:"
+ );
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " ldc 2",
+ " ldc 2",
+ " invokestatic Test/foo(II)V",
+ " ldc 0",
+ " ldc 0",
+ " invokestatic Test/foo(II)V",
+ " return");
+
+ String expected = "42\n";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArtD8(builder, clazz.name);
+ assertEquals(expected, artResult);
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvokeSpecialTests.java b/src/test/java/com/android/tools/r8/jasmin/InvokeSpecialTests.java
new file mode 100644
index 0000000..197b4d1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/InvokeSpecialTests.java
@@ -0,0 +1,79 @@
+// 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.jasmin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class InvokeSpecialTests extends JasminTestBase {
+
+ @Test
+ public void testPrivateInvokeSpecial() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addPrivateVirtualMethod("foo", ImmutableList.of(), "I",
+ ".limit stack 1",
+ ".limit locals 1",
+ " ldc 42",
+ " ireturn");
+
+ clazz.addMainMethod(
+ ".limit stack 3",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " new Test",
+ " dup",
+ " invokespecial Test/<init>()V",
+ " invokespecial Test/foo()I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ String expected = "42";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArt(builder, clazz.name);
+ assertEquals(expected, artResult);
+ }
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void testPublicInvokeSpecial() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addVirtualMethod("foo", ImmutableList.of(), "I",
+ ".limit stack 1",
+ ".limit locals 1",
+ " ldc 42",
+ " ireturn");
+
+ clazz.addMainMethod(
+ ".limit stack 3",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " new Test",
+ " dup",
+ " invokespecial Test/<init>()V",
+ " invokespecial Test/foo()I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ String expected = "42";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+
+ // TODO(zerny): Should we fail early on the above code? Art fails with a verification error
+ // because Test.foo is expected to be in the direct method table.
+ thrown.expect(AssertionError.class);
+ String artResult = runOnArt(builder, clazz.name);
+ assertEquals(expected, artResult);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
new file mode 100644
index 0000000..e7511e5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -0,0 +1,150 @@
+// 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.jasmin;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
+import jasmin.ClassFile;
+import java.io.ByteArrayOutputStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JasminBuilder {
+
+ public static class ClassBuilder {
+ public final String name;
+ private final List<String> methods = new ArrayList<>();
+ private boolean makeInit = false;
+
+ public ClassBuilder(String name) {
+ this.name = name;
+ }
+
+ public MethodSignature addVirtualMethod(
+ String name,
+ List<String> argumentTypes,
+ String returnType,
+ String... lines) {
+ makeInit = true;
+ return addMethod("public", name, argumentTypes, returnType, lines);
+ }
+
+ public MethodSignature addPrivateVirtualMethod(
+ String name,
+ List<String> argumentTypes,
+ String returnType,
+ String... lines) {
+ makeInit = true;
+ return addMethod("private", name, argumentTypes, returnType, lines);
+ }
+
+ public MethodSignature addStaticMethod(
+ String name,
+ List<String> argumentTypes,
+ String returnType,
+ String... lines) {
+ return addMethod("public static", name, argumentTypes, returnType, lines);
+ }
+
+ public MethodSignature addMainMethod(String... lines) {
+ return addStaticMethod("main", ImmutableList.of("[Ljava/lang/String;"), "V", lines);
+ }
+
+ private MethodSignature addMethod(
+ String access,
+ String name,
+ List<String> argumentTypes,
+ String returnType,
+ String... lines) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".method ").append(access).append(" ").append(name)
+ .append(StringUtils.join(argumentTypes, "", BraceType.PARENS))
+ .append(returnType).append("\n");
+ for (String line : lines) {
+ builder.append(line).append("\n");
+ }
+ builder.append(".end method\n");
+ methods.add(builder.toString());
+
+ String returnJavaType = DescriptorUtils.descriptorToJavaType(returnType);
+ String[] argumentJavaTypes = new String[argumentTypes.size()];
+ for (int i = 0; i < argumentTypes.size(); i++) {
+ argumentJavaTypes[i] = DescriptorUtils.descriptorToJavaType(argumentTypes.get(i));
+ }
+ return new MethodSignature(name, returnJavaType, argumentJavaTypes);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".source ").append(name).append(".j\n");
+ builder.append(".class public ").append(name).append("\n");
+ builder.append(".super java/lang/Object\n");
+ if (makeInit) {
+ builder
+ .append(".method public <init>()V\n")
+ .append(".limit locals 1\n")
+ .append(".limit stack 1\n")
+ .append(" aload 0\n")
+ .append(" invokespecial java/lang/Object/<init>()V\n")
+ .append(" return\n")
+ .append(".end method\n");
+ }
+ for (String method : methods) {
+ builder.append(method).append("\n");
+ }
+ return builder.toString();
+ }
+ }
+
+ private final List<ClassBuilder> classes = new ArrayList<>();
+
+ public JasminBuilder() {}
+
+ public ClassBuilder addClass(String name) {
+ ClassBuilder builder = new ClassBuilder(name);
+ classes.add(builder);
+ return builder;
+ }
+
+ public ImmutableList<ClassBuilder> getClasses() {
+ return ImmutableList.copyOf(classes);
+ }
+
+ private static byte[] compile(ClassBuilder builder) throws Exception {
+ ClassFile file = new ClassFile();
+ file.readJasmin(new StringReader(builder.toString()), builder.name, false);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ file.write(out);
+ return out.toByteArray();
+ }
+
+ public AndroidApp build() throws Exception {
+ AndroidApp.Builder builder = AndroidApp.builder();
+ for (ClassBuilder clazz : classes) {
+ builder.addClassProgramData(compile(clazz));
+ }
+ return builder.build();
+ }
+
+ public DexApplication read() throws Exception {
+ return read(new InternalOptions());
+ }
+
+ public DexApplication read(InternalOptions options) throws Exception {
+ DexItemFactory factory = new DexItemFactory();
+ Timing timing = new Timing("JasminTest");
+ return new ApplicationReader(build(), options, timing).read();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
new file mode 100644
index 0000000..7db5451
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -0,0 +1,132 @@
+// 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.jasmin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.R8;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import jasmin.ClassFile;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+public class JasminTestBase {
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ protected ProcessResult runOnJavaRaw(JasminBuilder builder, String main) throws Exception {
+ File out = temp.newFolder("classes");
+ for (ClassBuilder clazz : builder.getClasses()) {
+ ClassFile file = new ClassFile();
+ file.readJasmin(new StringReader(clazz.toString()), clazz.name, true);
+ file.write(new FileOutputStream(out.toPath().resolve(clazz.name + ".class").toFile()));
+ }
+ return ToolHelper.runJava(ImmutableList.of(out.getPath()), main);
+ }
+
+ protected String runOnJava(JasminBuilder builder, String main) throws Exception {
+ ProcessResult result = runOnJavaRaw(builder, main);
+ if (result.exitCode != 0) {
+ System.out.println("Std out:");
+ System.out.println(result.stdout);
+ System.out.println("Std err:");
+ System.out.println(result.stderr);
+ assertEquals(0, result.exitCode);
+ }
+ return result.stdout;
+ }
+
+ protected String runOnArt(JasminBuilder builder, String main) throws Exception {
+ // TODO(zerny): Make the compiler depend on tool flag?
+ return runOnArtR8(builder, main, new InternalOptions());
+ }
+
+ protected AndroidApp compileWithD8(JasminBuilder builder) throws Exception {
+ return ToolHelper.runD8(builder.build());
+ }
+
+ protected String runOnArtD8(JasminBuilder builder, String main) throws Exception {
+ return runOnArt(compileWithD8(builder), main);
+ }
+
+ protected String runOnArtR8(JasminBuilder builder, String main, InternalOptions options)
+ throws Exception {
+ DexApplication app = builder.read();
+ app = process(app, options);
+ AppInfo info = new AppInfo(app);
+ AndroidApp outputApp =
+ R8.writeApplication(
+ Executors.newSingleThreadExecutor(),
+ app,
+ info,
+ NamingLens.getIdentityLens(),
+ null,
+ null,
+ options);
+ return runOnArt(outputApp, main);
+ }
+
+ private ProcessResult runDx(JasminBuilder builder, File classes, Path dex) throws Exception {
+ for (ClassBuilder clazz : builder.getClasses()) {
+ ClassFile file = new ClassFile();
+ file.readJasmin(new StringReader(clazz.toString()), clazz.name, true);
+ file.write(new FileOutputStream(classes.toPath().resolve(clazz.name + ".class").toFile()));
+ }
+ List<String> args = new ArrayList<>();
+ args.add("--output=" + dex.toString());
+ args.add(classes.toString());
+ System.out.println("running: dx " + StringUtils.join(args, " "));
+ return ToolHelper.runDX(args.toArray(new String[args.size()]));
+ }
+
+ protected ProcessResult runOnArtDxRaw(JasminBuilder builder) throws Exception {
+ return runDx(builder, temp.newFolder("classes_for_dx"),
+ temp.getRoot().toPath().resolve("classes.dex"));
+ }
+
+ protected String runOnArtDx(JasminBuilder builder, String main) throws Exception {
+ Path dex = temp.getRoot().toPath().resolve("classes.dex");
+ ProcessResult result = runDx(builder, temp.newFolder("classes_for_dx"), dex);
+ if (result.exitCode != 0) {
+ System.out.println("Std out:");
+ System.out.println(result.stdout);
+ System.out.println("Std err:");
+ System.out.println(result.stderr);
+ assertEquals(0, result.exitCode);
+ }
+ return ToolHelper.runArtNoVerificationErrors(dex.toString(), main);
+ }
+
+ protected String runOnArt(AndroidApp app, String main) throws IOException {
+ Path out = temp.getRoot().toPath().resolve("out.zip");
+ app.writeToZip(out);
+ return ToolHelper.runArtNoVerificationErrors(ImmutableList.of(out.toString()), main, null);
+ }
+
+ protected static DexApplication process(DexApplication app, InternalOptions options)
+ throws IOException, ProguardRuleParserException, ExecutionException {
+ return new R8(options).optimize(app, new AppInfoWithSubtyping(app));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineDebugInfoTests.java b/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineDebugInfoTests.java
new file mode 100644
index 0000000..d0d1e83
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineDebugInfoTests.java
@@ -0,0 +1,68 @@
+// 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.jasmin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.debuginfo.DebugInfoInspector;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class JumpSubroutineDebugInfoTests extends JasminTestBase {
+
+ @Test
+ public void testJsrWithStraightlineAndDebugInfoCode() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ MethodSignature foo = clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
+ ".limit stack 3",
+ ".limit locals 3",
+ ".var 0 is x I from LabelInit to LabelExit",
+ "LabelInit:",
+ ".line 1",
+ " ldc 0",
+ " ldc 1",
+ " jsr LabelSub",
+ " ldc 2",
+ " jsr LabelSub",
+ " ldc 3",
+ " jsr LabelSub",
+ ".line 2",
+ " ireturn",
+ "LabelSub:",
+ ".line 3",
+ " astore 1",
+ " iadd",
+ " istore 0", // store and load in local 'x' to ensure we don't optimize out the subroutine.
+ ".line 4",
+ " iload 0",
+ " ret 1",
+ "LabelExit:");
+
+ clazz.addMainMethod(
+ ".limit stack 3",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 1",
+ " invokestatic Test/foo(I)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ String expected = "6";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ AndroidApp d8App = compileWithD8(builder);
+ String artResult = runOnArt(d8App, clazz.name);
+ assertEquals(expected, artResult);
+
+ DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
+ // The first debuggable line will be line 3.
+ info.checkStartLine(3);
+ // Check the subroutine line is duplicated 3 times.
+ assertEquals(3, info.checkLineHasExactLocals(3, "x", "int"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java b/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java
new file mode 100644
index 0000000..34ddd69
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java
@@ -0,0 +1,1540 @@
+// 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.jasmin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class JumpSubroutineTests extends JasminTestBase {
+
+ private void runTest(JasminBuilder builder, String main, String expected) throws Exception {
+ String javaResult = runOnJava(builder, main);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArt(builder, main);
+ assertEquals(expected, artResult);
+ String dxArtResult = runOnArtDx(builder, main);
+ assertEquals(expected, dxArtResult);
+ }
+
+ private void expectDxFailure(JasminBuilder builder) throws Exception {
+ // This expects this dx failure:
+ // Uncaught translation error: com.android.dex.util.ExceptionWithContext: returning from
+ // invalid subroutine
+ // 1 error; aborting
+ ProcessResult result = runOnArtDxRaw(builder);
+ assertNotEquals(0, result.exitCode);
+ assertTrue(result.stderr.contains("Uncaught translation error"));
+ assertTrue(result.stderr.contains("invalid subroutine"));
+ }
+
+ @Test
+ /*
+ * Compilation of the following code with JDK 1.3.0_05 (on Windows).
+ *
+ * package test;
+ *
+ * class Test {
+ * public static void main(String[] args) {
+ * try {
+ * System.out.println(0);
+ * } finally {
+ * System.out.println(2);
+ * }
+ * }
+ * }
+ */
+ public void testJsrJava130TryFinally() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addMainMethod(
+ ".limit stack 3",
+ ".limit locals 3",
+ "TryStart:",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iconst_0",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " jsr Finally",
+ " goto Return",
+ "TryEnd:",
+ "Catch:",
+ " astore_1",
+ " jsr Finally",
+ " aload_1",
+ " athrow",
+ "Finally:",
+ " astore_2",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iconst_1",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " ret 2",
+ "Return:",
+ " return",
+ ".catch all from TryStart to TryEnd using Catch");
+
+ runTest(builder, clazz.name, "01");
+ }
+
+ @Test
+ /*
+ * Compilation of the following code with JDK 1.3.0_05 (on Windows).
+ *
+ * package test;
+ *
+ * class Test {
+ * public static void main(String[] args) {
+ * try {
+ * System.out.println(0);
+ * try {
+ * System.out.println(1);
+ * } finally {
+ * System.out.println(2);
+ * }
+ * } finally {
+ * System.out.println(3);
+ * }
+ * }
+ * }
+ */
+ public void testJsrJava130TryFinallyNested() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addMainMethod(
+ ".limit stack 3",
+ ".limit locals 5",
+ "TryStart:",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iconst_0",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ "TryStartInner:",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iconst_1",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " jsr FinallyInner",
+ " goto DoneInner",
+ "TryEndInner:",
+ "CatchInner:",
+ " astore_1",
+ " jsr FinallyInner",
+ " aload_1",
+ " athrow",
+ "FinallyInner:",
+ " astore_2",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iconst_2",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " ret 2",
+ "DoneInner:",
+ " jsr Finally",
+ " goto Return",
+ "TryEnd:",
+ "Catch:",
+ " astore_3",
+ " jsr Finally",
+ " aload_3",
+ " athrow",
+ "Finally:",
+ " astore 4",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iconst_3",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " ret 4",
+ "Return:",
+ " return",
+ ".catch all from TryStartInner to TryEndInner using CatchInner",
+ ".catch all from TryStart to TryEnd using Catch");
+
+
+ runTest(builder, clazz.name, "0123");
+ }
+
+ @Test
+ public void testJsrWithStraightlineCode() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of(), "I",
+ ".limit stack 3",
+ ".limit locals 3",
+ " ldc 0",
+ " ldc 1",
+ " jsr LabelSub",
+ " ldc 2",
+ " jsr LabelSub",
+ " ldc 3",
+ " jsr LabelSub",
+ " ireturn",
+ "LabelSub:",
+ " astore 1",
+ " iadd",
+ " ret 1");
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " invokestatic Test/foo()I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ runTest(builder, clazz.name, Integer.toString(3 * 4 / 2));
+ }
+
+ @Test
+ public void testJsrWithStraightlineCodeMultiple() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of(), "I",
+ ".limit stack 3",
+ ".limit locals 3",
+ " ldc 0",
+ " ldc 1",
+ " jsr LabelSub1",
+ " ldc 2",
+ " jsr LabelSub2",
+ " ldc 3",
+ " jsr LabelSub3",
+ " ireturn",
+ "LabelSub1:",
+ " astore 1",
+ " iadd",
+ " ret 1",
+ "LabelSub2:",
+ " astore 1",
+ " iadd",
+ " ret 1",
+ "LabelSub3:",
+ " astore 1",
+ " iadd",
+ " ret 1");
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " invokestatic Test/foo()I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ runTest(builder, clazz.name, Integer.toString(3 * 4 / 2));
+ }
+
+ @Test
+ public void testJsrWithStraightlineCodeMultiple2() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of(), "I",
+ ".limit stack 4",
+ ".limit locals 3",
+ " ldc 0",
+ " ldc 1",
+ " jsr LabelSub",
+ " ldc 2",
+ " jsr LabelSub",
+ " ldc 3",
+ " jsr LabelSub",
+ " ldc 4",
+ " ldc 5",
+ " jsr LabelSub2",
+ " ldc 6",
+ " ldc 7",
+ " jsr LabelSub2",
+ " ldc 8",
+ " ldc 9",
+ " jsr LabelSub2",
+ " ireturn",
+ "LabelSub:",
+ " astore 1",
+ " iadd",
+ " ret 1",
+ "LabelSub2:",
+ " astore 1",
+ " iadd",
+ " iadd",
+ " ret 1");
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " invokestatic Test/foo()I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ runTest(builder, clazz.name, Integer.toString(9 * 10 / 2));
+ }
+
+ @Test
+ public void testJsrWithControlFlowCode() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+ ".limit stack 2",
+ ".limit locals 2",
+ " ldc 0",
+ " jsr LabelSub",
+ " ldc 1",
+ " jsr LabelSub",
+ " return",
+ "LabelSub:",
+ " astore 1",
+ " ifeq LabelZero",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc \"Got non-zero\"",
+ " invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+ " goto LabelRet",
+ "LabelZero:",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc \"Got zero\"",
+ " invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+ "LabelRet:",
+ " ret 1");
+
+ clazz.addMainMethod(
+ ".limit stack 0",
+ ".limit locals 1",
+ " invokestatic Test/foo()V",
+ " return");
+
+ runTest(builder, clazz.name, "Got zero\nGot non-zero\n");
+ }
+
+ @Test
+ public void testJsrWithNestedJsr() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+ ".limit stack 2",
+ ".limit locals 3",
+ " ldc 0",
+ " jsr LabelSub", // index 1.
+ " ldc 1",
+ " jsr LabelSub", // index 3.
+ " return",
+ "LabelSub:",
+ " astore 1",
+ " ifeq LabelZero",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc \"Got non-zero, calling nested\"",
+ " invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+ " jsr LabelSub2", // index 11.
+ " goto LabelRet",
+ "LabelZero:",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc \"Got zero\"",
+ " invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+ "LabelRet:",
+ " ret 1",
+ "LabelSub2:",
+ " astore 2",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc \"In nested subroutine\"",
+ " invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+ " ret 2");
+
+ clazz.addMainMethod(
+ ".limit stack 0",
+ ".limit locals 1",
+ " invokestatic Test/foo()V",
+ " return");
+
+ runTest(builder, clazz.name, "Got zero\nGot non-zero, calling nested\nIn nested subroutine\n");
+ }
+
+ @Test
+ public void testJsrWithNestedJsrPopReturnAddress() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+ ".limit stack 1",
+ ".limit locals 1",
+ " jsr LabelSub1",
+ " return",
+ "LabelSub1:",
+ " astore 0",
+ " jsr LabelSub2",
+ "LabelSub2:",
+ " pop",
+ " ret 0");
+
+ clazz.addMainMethod(
+ ".limit stack 1",
+ ".limit locals 1",
+ " invokestatic Test/foo()V",
+ " return");
+
+ String expected = "";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArt(builder, clazz.name);
+ assertEquals(expected, artResult);
+ // This fails with dx.
+ expectDxFailure(builder);
+ }
+
+ @Test
+ public void testJsrWithNestedPopReturnAddress2() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+ ".limit stack 1",
+ ".limit locals 1",
+ " jsr LabelSub1",
+ "LabelSub1:",
+ " pop",
+ " jsr LabelSub2",
+ " return",
+ "LabelSub2:",
+ " astore 0",
+ " ret 0");
+
+ clazz.addMainMethod(
+ ".limit stack 1",
+ ".limit locals 1",
+ " invokestatic Test/foo()V",
+ " return");
+
+ runTest(builder, clazz.name, "");
+ }
+
+ @Test
+ public void testJsrJustThrows() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+ ".limit stack 2",
+ ".limit locals 1",
+ " jsr Jsr",
+ // The return target is the same as the jsr target.
+ "Jsr:",
+ " pop", // Return address is not used.
+ " new java/lang/Exception",
+ " dup",
+ " invokenonvirtual java/lang/Exception.<init>()V",
+ " athrow");
+
+ clazz.addMainMethod(
+ ".limit stack 1",
+ ".limit locals 1",
+ "TryStart:",
+ " invokestatic Test/foo()V",
+ " return",
+ "TryEnd:",
+ "Catch:",
+ " pop",
+ " return",
+ ".catch java/lang/Exception from TryStart to TryEnd using Catch");
+
+ runTest(builder, clazz.name, "");
+ }
+
+ @Test
+ public void testJsrJustThrows2() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+ ".limit stack 2",
+ ".limit locals 1",
+ " jsr Jsr",
+ // The jsr does not return, so this is dead code.
+ " pop",
+ " pop",
+ " pop",
+ " pop",
+ " pop",
+ " pop",
+ " return",
+ "Jsr:",
+ " pop", // Return address is not used.
+ " new java/lang/Exception",
+ " dup",
+ " invokenonvirtual java/lang/Exception.<init>()V",
+ " athrow");
+
+ clazz.addMainMethod(
+ ".limit stack 1",
+ ".limit locals 1",
+ "TryStart:",
+ " invokestatic Test/foo()V",
+ " return",
+ "TryEnd:",
+ "Catch:",
+ " pop",
+ " return",
+ ".catch java/lang/Exception from TryStart to TryEnd using Catch");
+
+ runTest(builder, clazz.name, "");
+ }
+
+ @Test
+ public void testJsrWithException() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+ ".limit stack 3",
+ ".limit locals 2",
+ " ldc 0",
+ " jsr LabelSub",
+ " ldc 1",
+ " jsr LabelSub",
+ " return",
+ "LabelSub:",
+ " astore 1",
+ " ldc 42",
+ " swap",
+ " ldc 42",
+ " swap",
+ "LabelTryStart:",
+ " idiv",
+ " pop",
+ "LabelTryEnd:",
+ " pop",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc \"Divided by non-zero\"",
+ " invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+ " goto LabelRet",
+ "LabelCatch:",
+ " pop",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc \"Divided by zero\"",
+ " invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+ " goto LabelRet",
+ "LabelRet:",
+ " ret 1",
+ ".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatch");
+
+ clazz.addMainMethod(
+ ".limit stack 0",
+ ".limit locals 1",
+ " invokestatic Test/foo()V",
+ " return");
+
+ runTest(builder, clazz.name, "Divided by zero\nDivided by non-zero\n");
+ }
+
+ @Test
+ public void testJsrWithAddressManipulation() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+ ".limit stack 4",
+ ".limit locals 4",
+ " ldc 0",
+ " jsr LabelSub",
+ " ldc 1",
+ " jsr LabelSub",
+ " return",
+ "LabelSub:",
+ " ldc \"junk\"",
+ " swap",
+ " dup",
+ // stack is now ..., arg, "junk", addr, addr
+ " astore 3",
+ " astore 2",
+ " astore 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " swap",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " ret 2");
+
+ clazz.addMainMethod(
+ ".limit stack 0",
+ ".limit locals 1",
+ " invokestatic Test/foo()V",
+ " return");
+
+ runTest(builder, clazz.name, "01");
+ }
+
+ @Test
+ public void testJsrWithSharedExceptionHandler() throws Exception {
+ // Regression test for b/37659886
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of("I"), "V",
+ ".limit stack 4",
+ ".limit locals 2",
+ "LabelTryStart:",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iload 0",
+ " jsr LabelSub",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return",
+ "LabelSub:",
+ " astore 1",
+ " dup",
+ " ldc 168",
+ " swap",
+ " idiv", // First throwing shares the handler with JSR.
+ "LabelTryEnd1:",
+ " swap",
+ " idiv", // Second throwing is in the outer handler, but still opened at the point of JSR.
+ " ret 1",
+ "LabelTryEnd2:",
+ "LabelCatch1:",
+ " return",
+ "LabelCatch2:",
+ " return",
+ ".catch java/lang/Exception from LabelTryStart to LabelTryEnd1 using LabelCatch1",
+ ".catch java/lang/Exception from LabelTryStart to LabelTryEnd2 using LabelCatch2");
+
+ clazz.addMainMethod(
+ ".limit stack 1",
+ ".limit locals 1",
+ " ldc 2",
+ " invokestatic Test/foo(I)V",
+ " return");
+
+ runTest(builder, clazz.name, "42");
+ }
+
+ @Test
+ public void regressJsrHitParentCatchHandler() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("test", ImmutableList.of(), "I",
+ ".limit stack 2",
+ ".limit locals 2",
+ " ldc 10",
+ " istore 1",
+ "TryStart:",
+ " iinc 1 -1",
+ " iload 1",
+ " ifeq LabelReturn",
+ " jsr Jsr",
+ " goto LabelReturn",
+ "Jsr:",
+ " astore 0",
+ " new java/lang/Exception",
+ " dup",
+ " invokenonvirtual java/lang/Exception.<init>()V",
+ " athrow",
+ " ret 0",
+ "LabelReturn:",
+ " ldc 0",
+ " ireturn",
+ "Catch:",
+ " pop",
+ " goto TryStart",
+ "TryEnd:",
+ ".catch java/lang/Exception from TryStart to TryEnd using Catch"
+ );
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " invokestatic Test/test()I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ runTest(builder, clazz.name, "0");
+ }
+
+ private void generateRegressJsrHitParentCatchHandler2(
+ JasminBuilder.ClassBuilder clazz, String name, String exception, boolean decrementInOuter) {
+ String outer = decrementInOuter ? " iinc 0 -1" : "";
+ String inner = decrementInOuter ? "" : " iinc 0 -1";
+ clazz.addStaticMethod(name, ImmutableList.of(), "I",
+ ".limit stack 3",
+ ".limit locals 1",
+ " ldc 10",
+ " istore 0",
+ "TryStart:",
+ outer,
+ " iload 0",
+ " ifeq LabelReturn1",
+ " jsr Jsr1",
+ "Jsr1:",
+ " pop", // Return value is not used.
+ "Jsr1Retry:",
+ inner,
+ " iload 0",
+ " ifeq LabelReturn0",
+ " jsr Jsr2",
+ " ldc 2",
+ " ireturn",
+ "Jsr2:",
+ " pop", // Return value is not used.
+ " new " + exception,
+ " dup",
+ " invokenonvirtual " + exception + ".<init>()V",
+ " athrow",
+ "Jsr2Catch:",
+ " pop",
+ " goto Jsr1Retry",
+ "Jsr2End:",
+ "LabelReturn0:",
+ " ldc 0",
+ " ireturn",
+ "LabelReturn1:",
+ " ldc 1",
+ " ireturn",
+ "Catch:",
+ " pop",
+ " goto TryStart",
+ "TryEnd:",
+ ".catch java/lang/Exception from Jsr2 to Jsr2End using Jsr2Catch",
+ ".catch java/lang/Throwable from TryStart to TryEnd using Catch"
+ );
+ }
+
+ @Test
+ public void regressJsrHitParentCatchHandler2() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ generateRegressJsrHitParentCatchHandler2(clazz, "test1", "java/lang/Exception", false);
+ generateRegressJsrHitParentCatchHandler2(clazz, "test2", "java/lang/Throwable", true);
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " invokestatic Test/test1()I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " invokestatic Test/test2()I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ runTest(builder, clazz.name, "01");
+ }
+
+ @Test
+ // Reduction of b/38156139 causing the infinite loop.
+ //
+ // The original code is this method:
+ // https://github.com/cbeust/testng/blob/4a8459e36f2b0ed057ffa7e470f1057e8e5b0ff9/src/main/java/org/testng/internal/Invoker.java#L1066
+ // compiled with some ancient version of javac generating code with jsr for try/finally.
+ public void regress38156139() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
+ ".limit stack 2",
+ ".limit locals 3",
+ " ldc 10",
+ " istore 2",
+ "LabelLoopStart:",
+ " iinc 2 -1",
+ " iload 2",
+ " ifeq LabelReturn",
+ "LabelTryStart:",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iload 2",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " jsr LabelFinally",
+ " goto LabelLoopStart",
+ "LabelTryEnd:",
+ "LabelReturn:",
+ " ldc 0",
+ " ireturn",
+ "LabelCatch:",
+ " pop",
+ " jsr LabelFinally",
+ " goto LabelLoopStart",
+ "LabelFinally:",
+ " astore 1",
+ " iload 0",
+ " ifeq LabelZero",
+ " goto LabelLoopStart", // Jump to loop start without invoking ret.
+ "LabelZero:",
+ " ret 1", // Invoke ret, which will also continue at the loop start.
+ ".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatch");
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 0",
+ " invokestatic Test/foo(Z)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 1",
+ " invokestatic Test/foo(Z)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ runTest(builder, clazz.name, "98765432109876543210");
+ }
+
+ @Test
+ public void regress37767254() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ // This is the code for the method org.apache.log4j.net.SocketAppender$Connector.run() from
+ // log4j version 1.2.
+ //
+ // https://github.com/apache/log4j/blob/v1_2-branch/src/main/java/org/apache/log4j/net/SocketAppender.java#L373
+ //
+ clazz.addVirtualMethod("run", ImmutableList.of(), "V",
+ ".limit stack 4",
+ ".limit locals 4",
+ ".var 0 is this Lorg/apache/log4j/net/SocketAppender$Connector; from L0 to L26",
+ ".var 1 is socket Ljava/net/Socket; from L5 to L13",
+ ".var 2 is e Ljava/io/IOException; from L22 to L1",
+ "L0:",
+ ".line 367",
+ " goto L1",
+ "L2:",
+ ".line 368",
+ ".line 369",
+ " aload 0",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender.reconnectionDelay I",
+ " i2l",
+ " invokestatic java/lang/Thread.sleep(J)V",
+ "L3:",
+ ".line 370",
+ " new java/lang/StringBuffer",
+ " dup",
+ " ldc \"Attempting connection to \"",
+ " invokenonvirtual java/lang/StringBuffer.<init>(Ljava/lang/String;)V",
+ " aload 0",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
+ " invokevirtual java/net/InetAddress.getHostName()Ljava/lang/String;",
+ " invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
+ " invokevirtual java/lang/StringBuffer.toString()Ljava/lang/String;",
+ " invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
+ "L4:",
+ ".line 371",
+ " new java/net/Socket",
+ " dup",
+ " aload 0",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
+ " aload 0",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender.port I",
+ " invokenonvirtual java/net/Socket.<init>(Ljava/net/InetAddress;I)V",
+ " astore 1",
+ "L5:",
+ ".line 372",
+ " aload 0",
+ " astore 2",
+ " aload 2",
+ " monitorenter",
+ "L6:",
+ ".line 373",
+ " aload 0",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " new java/io/ObjectOutputStream",
+ " dup",
+ " aload 1",
+ " invokevirtual java/net/Socket.getOutputStream()Ljava/io/OutputStream;",
+ " invokenonvirtual java/io/ObjectOutputStream.<init>(Ljava/io/OutputStream;)V",
+ " putfield org/apache/log4j/net/SocketAppender.oos Ljava/io/ObjectOutputStream;",
+ "L7:",
+ ".line 374",
+ " aload 0",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " aconst_null",
+ " invokestatic org/apache/log4j/net/SocketAppender.access$1(Lorg/apache/log4j/net/SocketAppender;Lorg/apache/log4j/net/SocketAppender$Connector;)V",
+ "L8:",
+ ".line 375",
+ " ldc \"Connection established. Exiting connector thread.\"",
+ " invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
+ "L9:",
+ ".line 376",
+ " jsr L10",
+ " goto L11",
+ "L12:",
+ ".line 372",
+ " aload 2",
+ " monitorexit",
+ " athrow",
+ "L10:",
+ " astore 3",
+ " aload 2",
+ " monitorexit",
+ " ret 3",
+ "L13:",
+ ".line 378",
+ " pop",
+ "L14:",
+ ".line 379",
+ " ldc \"Connector interrupted. Leaving loop.\"",
+ " invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
+ "L15:",
+ ".line 380",
+ " return",
+ "L16:",
+ ".line 381",
+ " pop",
+ "L17:",
+ ".line 382",
+ " new java/lang/StringBuffer",
+ " dup",
+ " ldc \"Remote host \"",
+ " invokenonvirtual java/lang/StringBuffer.<init>(Ljava/lang/String;)V",
+ " aload 0",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
+ " invokevirtual java/net/InetAddress.getHostName()Ljava/lang/String;",
+ " invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
+ "L18:",
+ ".line 383",
+ " ldc \" refused connection.\"",
+ " invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
+ " invokevirtual java/lang/StringBuffer.toString()Ljava/lang/String;",
+ "L19:",
+ ".line 382",
+ " invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
+ "L20:",
+ ".line 368",
+ " goto L1",
+ "L21:",
+ ".line 384",
+ " astore 2",
+ "L22:",
+ ".line 385",
+ " new java/lang/StringBuffer",
+ " dup",
+ " ldc \"Could not connect to \"",
+ " invokenonvirtual java/lang/StringBuffer.<init>(Ljava/lang/String;)V",
+ " aload 0",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
+ " invokevirtual java/net/InetAddress.getHostName()Ljava/lang/String;",
+ " invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
+ "L23:",
+ ".line 386",
+ " ldc \". Exception is \"",
+ " invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
+ " aload 2",
+ " invokevirtual java/lang/StringBuffer.append(Ljava/lang/Object;)Ljava/lang/StringBuffer;",
+ " invokevirtual java/lang/StringBuffer.toString()Ljava/lang/String;",
+ "L24:",
+ ".line 385",
+ " invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
+ "L25:",
+ ".line 368",
+ " goto L1",
+ "L1:",
+ ".line 367",
+ " aload 0",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.interrupted Z",
+ " ifeq L2",
+ "L11:",
+ ".line 365",
+ " return",
+ "L26:",
+ ".catch all from L6 to L12 using L12",
+ ".catch java/lang/InterruptedException from L2 to L13 using L13",
+ ".catch java/net/ConnectException from L2 to L13 using L16",
+ ".catch java/io/IOException from L2 to L13 using L21"
+ );
+
+ // Check that the code compiles without an infinite loop. It cannot run by itself.
+ AndroidApp app = compileWithD8(builder);
+ assertNotNull(app);
+ }
+
+ @Test
+ public void regress37888855Reduced() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ // This is the code for the method
+ //
+ // void org.eclipse.jdt.internal.core.JavaModelOperation.run(org.eclipse.core.runtime.IProgressMonitor)
+ //
+ // from struts2/lib/core-3.1.1.jar
+ //
+ clazz.addVirtualMethod("run", ImmutableList.of("Lorg/eclipse/core/runtime/IProgressMonitor;"),
+ "V",
+ ".limit stack 3",
+ ".limit locals 15",
+ "TryStart1:",
+ "TryStart2:",
+ " goto Finally2Start",
+ "TryEnd2:",
+ "Catch2:",
+ " astore 6",
+ " jsr Finally2Code", // 6
+ " aload 6",
+ " athrow",
+ "Finally2Code:", // 9
+ " astore 5",
+ " ret 5",
+ "Finally2Start:",
+ " jsr Finally2Code", // 13
+ "Finally2End:",
+ " goto Finally1Start",
+ "TryEnd1:",
+ "Catch1:",
+ " astore 8",
+ " jsr Finally1Code", // 19
+ "L19:",
+ " aload 8",
+ " athrow",
+ "Finally1Code:",
+ " astore 7",
+ "Try3Start:",
+ " goto Finally3Start",
+ "Try3End:",
+ "Catch3:",
+ " astore 14", // Catch
+ " jsr Finally3Code", // 30
+ " aload 14",
+ " athrow",
+ "Finally3Code:", // Finally
+ " astore 13",
+ " ret 13",
+ "Finally3Start:",
+ " jsr Finally3Code", // 37
+ "Finally3End:",
+ " ret 7",
+ "Finally1Start:",
+ " jsr Finally1Code", // 41
+ "Finally1End:",
+ " return",
+ ".catch all from TryStart2 to TryEnd2 using Catch2", // Block
+ ".catch all from Finally2Start to Finally2End using Catch2", // Finally
+ ".catch all from TryStart1 to TryEnd1 using Catch1", // Block
+ ".catch all from Finally1Start to Finally1End using Catch1", // Finally
+ ".catch all from Try3Start to Try3End using Catch3", // Block
+ ".catch all from Finally3Start to Finally3End using Catch3" // Finally
+ );
+
+ // Check that the code compiles without an infinite loop. It cannot run by itself.
+ AndroidApp app = compileWithD8(builder);
+ assertNotNull(app);
+ }
+
+ @Test
+ public void regress37888855() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ // This is the code for the method
+ //
+ // void org.eclipse.jdt.internal.core.JavaModelOperation.run(org.eclipse.core.runtime.IProgressMonitor)
+ //
+ // from struts2/lib/core-3.1.1.jar
+ //
+ clazz.addVirtualMethod("run", ImmutableList.of("Lorg/eclipse/core/runtime/IProgressMonitor;"), "V",
+ ".limit stack 3",
+ ".limit locals 15",
+ ".var 0 is this Lorg/eclipse/jdt/internal/core/JavaModelOperation; from L0 to L50",
+ ".var 1 is monitor Lorg/eclipse/core/runtime/IProgressMonitor; from L0 to L50",
+ ".var 2 is manager Lorg/eclipse/jdt/internal/core/JavaModelManager; from L1 to L50",
+ ".var 3 is deltaProcessor Lorg/eclipse/jdt/internal/core/DeltaProcessor; from L2 to L50",
+ ".var 4 is previousDeltaCount I from L1 to L50",
+ ".var 9 is i I from L22 to L27",
+ ".var 10 is size I from L23 to L27",
+ ".var 9 is i I from L28 to L38",
+ ".var 10 is length I from L29 to L38",
+ ".var 11 is element Lorg/eclipse/jdt/core/IJavaElement; from L32 to L37",
+ ".var 12 is openable Lorg/eclipse/jdt/internal/core/Openable; from L33 to L37",
+ "L0:",
+ ".line 705",
+ " invokestatic org/eclipse/jdt/internal/core/JavaModelManager.getJavaModelManager()Lorg/eclipse/jdt/internal/core/JavaModelManager;",
+ " astore 2",
+ "L1:",
+ ".line 706",
+ " aload 2",
+ " invokevirtual org/eclipse/jdt/internal/core/JavaModelManager.getDeltaProcessor()Lorg/eclipse/jdt/internal/core/DeltaProcessor;",
+ " astore 3",
+ "L2:",
+ ".line 707",
+ " aload 3",
+ " getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
+ " invokevirtual java/util/ArrayList.size()I",
+ " istore 4",
+ "L3:",
+ ".line 709",
+ " aload 0",
+ " aload 1",
+ " putfield org/eclipse/jdt/internal/core/JavaModelOperation.progressMonitor Lorg/eclipse/core/runtime/IProgressMonitor;",
+ "L4:",
+ ".line 710",
+ " aload 0",
+ " aload 0",
+ " invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.pushOperation(Lorg/eclipse/jdt/internal/core/JavaModelOperation;)V",
+ "L5:",
+ ".line 712",
+ " aload 0",
+ " invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.canModifyRoots()Z",
+ " ifeq L6",
+ "L7:",
+ ".line 715",
+ " invokestatic org/eclipse/jdt/internal/core/JavaModelManager.getJavaModelManager()Lorg/eclipse/jdt/internal/core/JavaModelManager;",
+ " getfield org/eclipse/jdt/internal/core/JavaModelManager.deltaState Lorg/eclipse/jdt/internal/core/DeltaProcessingState;",
+ " invokevirtual org/eclipse/jdt/internal/core/DeltaProcessingState.initializeRoots()V",
+ "L6:",
+ ".line 718",
+ " aload 0",
+ " invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.executeOperation()V",
+ " goto L8",
+ "L9:",
+ ".line 719",
+ " astore 6",
+ " jsr L10",
+ "L11:",
+ ".line 723",
+ " aload 6",
+ " athrow",
+ "L10:",
+ ".line 719",
+ " astore 5",
+ "L12:",
+ ".line 720",
+ " aload 0",
+ " invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.isTopLevelOperation()Z",
+ " ifeq L13",
+ "L14:",
+ ".line 721",
+ " aload 0",
+ " invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.runPostActions()V",
+ "L13:",
+ ".line 723",
+ " ret 5",
+ "L8:",
+ " jsr L10",
+ "L15:",
+ " goto L16",
+ "L17:",
+ ".line 724",
+ " astore 8",
+ " jsr L18",
+ "L19:",
+ ".line 764",
+ " aload 8",
+ " athrow",
+ "L18:",
+ ".line 724",
+ " astore 7",
+ "L20:",
+ ".line 727",
+ " aload 2",
+ " invokevirtual org/eclipse/jdt/internal/core/JavaModelManager.getDeltaProcessor()Lorg/eclipse/jdt/internal/core/DeltaProcessor;",
+ " astore 3",
+ "L21:",
+ ".line 730",
+ " iload 4",
+ " istore 9",
+ "L22:",
+ " aload 3",
+ " getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
+ " invokevirtual java/util/ArrayList.size()I",
+ " istore 10",
+ "L23:",
+ " goto L24",
+ "L25:",
+ ".line 731",
+ " aload 3",
+ " aload 3",
+ " getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
+ " iload 9",
+ " invokevirtual java/util/ArrayList.get(I)Ljava/lang/Object;",
+ " checkcast org/eclipse/jdt/core/IJavaElementDelta",
+ " invokevirtual org/eclipse/jdt/internal/core/DeltaProcessor.updateJavaModel(Lorg/eclipse/jdt/core/IJavaElementDelta;)V",
+ "L26:",
+ ".line 730",
+ " iinc 9 1",
+ "L24:",
+ " iload 9",
+ " iload 10",
+ " if_icmplt L25",
+ "L27:",
+ ".line 737",
+ " iconst_0",
+ " istore 9",
+ "L28:",
+ " aload 0",
+ " getfield org/eclipse/jdt/internal/core/JavaModelOperation.resultElements [Lorg/eclipse/jdt/core/IJavaElement;",
+ " arraylength",
+ " istore 10",
+ "L29:",
+ " goto L30",
+ "L31:",
+ ".line 738",
+ " aload 0",
+ " getfield org/eclipse/jdt/internal/core/JavaModelOperation.resultElements [Lorg/eclipse/jdt/core/IJavaElement;",
+ " iload 9",
+ " aaload",
+ " astore 11",
+ "L32:",
+ ".line 739",
+ " aload 11",
+ " invokeinterface org/eclipse/jdt/core/IJavaElement.getOpenable()Lorg/eclipse/jdt/core/IOpenable; 0",
+ " checkcast org/eclipse/jdt/internal/core/Openable",
+ " astore 12",
+ "L33:",
+ ".line 740",
+ " aload 12",
+ " instanceof org/eclipse/jdt/internal/core/CompilationUnit",
+ " ifeq L34",
+ " aload 12",
+ " checkcast org/eclipse/jdt/internal/core/CompilationUnit",
+ " invokevirtual org/eclipse/jdt/internal/core/CompilationUnit.isWorkingCopy()Z",
+ " ifne L35",
+ "L34:",
+ ".line 741",
+ " aload 12",
+ " invokevirtual org/eclipse/jdt/internal/core/Openable.getParent()Lorg/eclipse/jdt/core/IJavaElement;",
+ " checkcast org/eclipse/jdt/internal/core/JavaElement",
+ " invokevirtual org/eclipse/jdt/internal/core/JavaElement.close()V",
+ "L35:",
+ ".line 743",
+ " aload 11",
+ " invokeinterface org/eclipse/jdt/core/IJavaElement.getElementType()I 0",
+ " tableswitch 3",
+ " L36",
+ " L36",
+ " default: L37",
+ "L36:",
+ ".line 746",
+ " aload 11",
+ " invokeinterface org/eclipse/jdt/core/IJavaElement.getJavaProject()Lorg/eclipse/jdt/core/IJavaProject; 0",
+ " checkcast org/eclipse/jdt/internal/core/JavaProject",
+ " invokevirtual org/eclipse/jdt/internal/core/JavaProject.resetCaches()V",
+ "L37:",
+ ".line 737",
+ " iinc 9 1",
+ "L30:",
+ " iload 9",
+ " iload 10",
+ " if_icmplt L31",
+ "L38:",
+ ".line 755",
+ " aload 0",
+ " invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.isTopLevelOperation()Z",
+ " ifeq L39",
+ "L40:",
+ ".line 756",
+ " aload 3",
+ " getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
+ " invokevirtual java/util/ArrayList.size()I",
+ " iload 4",
+ " if_icmpgt L41",
+ " aload 3",
+ " getfield org/eclipse/jdt/internal/core/DeltaProcessor.reconcileDeltas Ljava/util/HashMap;",
+ " invokevirtual java/util/HashMap.isEmpty()Z",
+ " ifne L39",
+ "L41:",
+ ".line 757",
+ " aload 0",
+ " invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.hasModifiedResource()Z",
+ " ifne L39",
+ "L42:",
+ ".line 758",
+ " aload 3",
+ " aconst_null",
+ " iconst_0",
+ " invokevirtual org/eclipse/jdt/internal/core/DeltaProcessor.fire(Lorg/eclipse/jdt/core/IJavaElementDelta;I)V",
+ " goto L39",
+ "L43:",
+ ".line 761",
+ " astore 14",
+ " jsr L44",
+ "L45:",
+ ".line 763",
+ " aload 14",
+ " athrow",
+ "L44:",
+ ".line 761",
+ " astore 13",
+ "L46:",
+ ".line 762",
+ " aload 0",
+ " invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.popOperation()Lorg/eclipse/jdt/internal/core/JavaModelOperation;",
+ " pop",
+ "L47:",
+ ".line 763",
+ " ret 13",
+ "L39:",
+ " jsr L44",
+ "L48:",
+ ".line 764",
+ " ret 7",
+ "L16:",
+ " jsr L18",
+ "L49:",
+ ".line 765",
+ " return",
+ "L50:",
+ ".catch all from L5 to L9 using L9",
+ ".catch all from L8 to L15 using L9",
+ ".catch all from L3 to L17 using L17",
+ ".catch all from L16 to L49 using L17",
+ ".catch all from L20 to L43 using L43",
+ ".catch all from L39 to L48 using L43"
+ );
+
+ // Check that the code compiles without an infinite loop. It cannot run by itself.
+ AndroidApp app = compileWithD8(builder);
+ assertNotNull(app);
+ }
+
+ // Some jsr tests that fails with org.objectweb.asm.commons.JSRInlinerAdapter.
+
+ @Test
+ // This test is based on the example on http://asm.ow2.org/doc/developer-guide.html.
+ public void testJsrWithNestedJsrRetBasedOnControlFlow() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
+ ".limit stack 2",
+ ".limit locals 3",
+ " jsr LabelSub1",
+ " ldc 1",
+ " ireturn",
+ "LabelSub1:",
+ " astore 1",
+ " jsr LabelSub2",
+ " ldc 0",
+ " ireturn",
+ "LabelSub2:",
+ " astore 2",
+ " iload 0",
+ " ifeq LabelZero",
+ " ret 2",
+ "LabelZero:",
+ " ret 1");
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 0",
+ " invokestatic Test/foo(Z)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 1",
+ " invokestatic Test/foo(Z)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ String expected = "10";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArt(builder, clazz.name);
+ // The ASM jsr inliner does not get the control-flow dependent ret right in his case.
+ assertNotEquals(expected, artResult);
+ // This fails with dx.
+ expectDxFailure(builder);
+ }
+
+ @Test
+ // This test is based on the example on http://asm.ow2.org/doc/developer-guide.html.
+ public void testJsrWithNestedRetBasedOnControlFlow2() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
+ ".limit stack 2",
+ ".limit locals 3",
+ " jsr LabelSub1",
+ " ldc 1",
+ " ireturn",
+ "LabelSub1:",
+ " jsr LabelSub2",
+ " ldc 0",
+ " ireturn",
+ "LabelSub2:",
+ " astore 2",
+ " astore 1",
+ " iload 0",
+ " ifeq LabelZero",
+ " ret 2",
+ "LabelZero:",
+ " ret 1");
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 0",
+ " invokestatic Test/foo(Z)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 1",
+ " invokestatic Test/foo(Z)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ String expected = "10";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArt(builder, clazz.name);
+ // The ASM jsr inliner does not get the control-flow dependent ret right in his case.
+ assertNotEquals(expected, artResult);
+ // This fails with dx.
+ expectDxFailure(builder);
+ }
+
+ @Test
+ // This test is based on the example on http://asm.ow2.org/doc/developer-guide.html.
+ public void testJsrWithNestedRetBasedOnControlFlow3() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
+ ".limit stack 2",
+ ".limit locals 3",
+ " jsr LabelSub1",
+ " ldc 1",
+ " ireturn",
+ "LabelSub1:",
+ " jsr LabelSub2",
+ " ldc 0",
+ " ireturn",
+ "LabelSub2:",
+ " swap",
+ " astore 1",
+ " astore 2",
+ " iload 0",
+ " ifeq LabelZero",
+ " ret 2",
+ "LabelZero:",
+ " ret 1");
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 0",
+ " invokestatic Test/foo(Z)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 1",
+ " invokestatic Test/foo(Z)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ String expected = "10";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArt(builder, clazz.name);
+ // The ASM jsr inliner does not get the control-flow dependent ret right in his case.
+ assertNotEquals(expected, artResult);
+ // This fails with dx.
+ expectDxFailure(builder);
+ }
+
+ // Some jsr tests that fails bytecode verification on the Java VM.
+
+ @Test
+ public void testReuseAddr() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addMainMethod(
+ ".limit stack 3",
+ ".limit locals 2",
+ " jsr LabelSub1",
+ " return",
+ "LabelSub1:",
+ " jsr LabelSub2",
+ " dup",
+ " astore 0",
+ " ret 0",
+ "LabelSub2:",
+ " dup",
+ " astore 0",
+ " ret 0");
+
+ // This does not run on the Java VM (verification error).
+ assertNotEquals(0, runOnJavaRaw(builder, clazz.name));
+ }
+
+ @Test
+ public void testSwitchAddr() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addMainMethod(
+ ".limit stack 3",
+ ".limit locals 2",
+ " jsr LabelSub1",
+ " astore 0",
+ " ret 0",
+ "LabelSub1:",
+ " jsr LabelSub2",
+ " return",
+ "LabelSub2:",
+ " swap",
+ " astore 0",
+ " ret 0");
+
+ // This does not run on the Java VM (verification error).
+ assertNotEquals(0, runOnJavaRaw(builder, clazz.name));
+ }
+
+ @Test
+ public void testJsrPreserveRetAddressOverJsr() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
+ ".limit stack 2",
+ ".limit locals 2",
+ " jsr LabelSub1",
+ " ldc 0",
+ " ireturn",
+ "LabelSub1:",
+ " iload 0",
+ " ifeq StoreInFirst",
+ " pop",
+ " goto NextJsr",
+ "StoreInFirst:",
+ " astore 1",
+ "NextJsr:",
+ " jsr LabelSub2",
+ " ldc 1",
+ " ireturn",
+ "LabelSub2:",
+ " iload 0",
+ " ifeq DontStoreInSecond", // Same as StoreInFirst
+ " astore 1",
+ " goto Ret",
+ "DontStoreInSecond:",
+ " pop",
+ "Ret:",
+ // There will always be an address in local 1 at this point.
+ " ret 1");
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 0",
+ " invokestatic Test/foo(Z)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 1",
+ " invokestatic Test/foo(Z)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ // This does not run on the Java VM (verification error).
+ assertNotEquals(0, runOnJavaRaw(builder, clazz.name));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/TryCatchStateTests.java b/src/test/java/com/android/tools/r8/jasmin/TryCatchStateTests.java
new file mode 100644
index 0000000..92f99f8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/TryCatchStateTests.java
@@ -0,0 +1,157 @@
+// 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.jasmin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class TryCatchStateTests extends JasminTestBase {
+
+ @Test
+ public void testTryCatchStackHeight() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
+ ".limit stack 5",
+ ".limit locals 1",
+ " ldc 42",
+ " ldc 12",
+ " iload 0", // {42, 12, i}
+ "LabelTryStart:",
+ " idiv", // {42, 12/i}
+ "LabelTryEnd:",
+ " swap",
+ " pop", // {12/i}
+ " goto LabelRet",
+ "LabelCatch:", // Entry stack is {java/lang/Throwable}
+ " pop",
+ " ldc 0", // {0}
+ " goto LabelRet",
+ "LabelRet:",
+ " ireturn",
+ ".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatch"
+ );
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 0",
+ " invokestatic Test/foo(I)I",
+ " invokevirtual java/io/PrintStream/println(I)V",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 2",
+ " invokestatic Test/foo(I)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ String expected = "0\n6";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArt(builder, clazz.name);
+ assertEquals(expected, artResult);
+ }
+
+ @Test
+ public void testTryCatchLocals() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
+ ".limit stack 5",
+ ".limit locals 2",
+ " ldc 42",
+ " ldc 12",
+ " iload 0", // {42, 12, i}
+ " ldc 0",
+ " istore 1", // Must initialize local before entry to try-catch block.
+ "LabelTryStart:",
+ " swap",
+ " istore 1",
+ " idiv", // {42/i}
+ "LabelTryEnd:",
+ " goto LabelRet",
+ "LabelCatch:", // Entry stack is {java/lang/Throwable}
+ " pop",
+ " iload 1", // {12}
+ " goto LabelRet",
+ "LabelRet:",
+ " ireturn",
+ ".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatch"
+ );
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 0",
+ " invokestatic Test/foo(I)I",
+ " invokevirtual java/io/PrintStream/println(I)V",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 2",
+ " invokestatic Test/foo(I)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ String expected = "12\n21";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArt(builder, clazz.name);
+ assertEquals(expected, artResult);
+ }
+
+ @Test
+ public void testTryCatchOnUnreachableLabel() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
+ ".limit stack 5",
+ ".limit locals 2",
+ " ldc 42",
+ " ldc 12",
+ " iload 0", // {42, 12, i}
+ " ldc 0",
+ " istore 1", // Must initialize local before entry to try-catch block.
+ " goto RealStart",
+ "LabelTryStart:", // Start the catch range on unreachable label.
+ " goto RealStart",
+ "RealStart:",
+ " swap",
+ " istore 1",
+ " idiv", // {42/i}
+ "LabelTryEnd:",
+ " goto LabelRet",
+ "LabelCatch:", // Entry stack is {java/lang/Throwable}
+ " pop",
+ " iload 1", // {12}
+ " goto LabelRet",
+ "LabelRet:",
+ " ireturn",
+ ".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatch"
+ );
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 0",
+ " invokestatic Test/foo(I)I",
+ " invokevirtual java/io/PrintStream/println(I)V",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 2",
+ " invokestatic Test/foo(I)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ String expected = "12\n21";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+ String artResult = runOnArt(builder, clazz.name);
+ assertEquals(expected, artResult);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
new file mode 100644
index 0000000..1e6e3f7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
@@ -0,0 +1,1116 @@
+// 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.jdwp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Wrapper for the art JDWP tests.
+ *
+ * A new version of this file can be generated using ./tools/create-jdwp-tests.py.
+ */
+public class RunJdwpTests {
+
+ enum Tool {
+ JAVAC,
+ DX,
+ D8
+ }
+
+ // Run all tests (default will only run smoke tests).
+ static final boolean RUN_ALL_TESTS = false;
+
+ // Print test output for passing tests (failing tests output is always printed).
+ static final boolean PRINT_STREAMS = true;
+
+ static final String RUN_SCRIPT = "tools/run-jdwp-tests.py";
+ static final String DEX_LIB = "third_party/jdwp-tests/apache-harmony-jdwp-tests-hostdex.jar";
+ static final String JAR_LIB = "third_party/jdwp-tests/apache-harmony-jdwp-tests-host.jar";
+
+ interface TestPredicate {
+ boolean test(DexVm dexVm, Tool tool);
+ }
+
+ static boolean isAndroidMOrAbove(DexVm dexVm, Tool tool) {
+ return dexVm.isNewerThan(DexVm.ART_5_1_1);
+ }
+
+ static boolean isAndroidNOrAbove(DexVm dexVm, Tool tool) {
+ return dexVm.isNewerThan(DexVm.ART_6_0_1);
+ }
+
+ static boolean isAndroidOOrAbove(DexVm dexVm, Tool tool) {
+ return dexVm.isNewerThan(DexVm.ART_7_0_0);
+ }
+
+ static boolean isLatestRuntime(DexVm dexVm, Tool tool) {
+ return dexVm == DexVm.ART_DEFAULT;
+ }
+
+ static final Map<String, TestPredicate> FAILING_TESTS =
+ ImmutableMap.<String, TestPredicate>builder()
+ .put("ArrayReference.SetValues003Test", RunJdwpTests::isAndroidNOrAbove)
+ .put("ClassType.InvokeMethodAfterMultipleThreadSuspensionTest",
+ RunJdwpTests::isAndroidNOrAbove)
+ .put("ClassType.InvokeMethodWithSuspensionTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("ClassType.NewInstanceAfterMultipleThreadSuspensionTest",
+ RunJdwpTests::isAndroidNOrAbove)
+ .put("ClassType.NewInstanceStringTest", RunJdwpTests::isAndroidOOrAbove)
+ .put("ClassType.NewInstanceTagTest", RunJdwpTests::isAndroidNOrAbove)
+ .put("ClassType.NewInstanceWithSuspensionTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("Events.BreakpointTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("Events.Breakpoint002Test", RunJdwpTests::isAndroidMOrAbove)
+ .put("Events.BreakpointOnCatchTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("Events.ClassPrepare002Test", RunJdwpTests::isAndroidOOrAbove)
+ .put("Events.CombinedExceptionEventsTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("Events.ExceptionCaughtTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("Events.ExceptionUncaughtTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("Events.EventWithExceptionTest", RunJdwpTests::isAndroidNOrAbove)
+ .put("Events.FieldAccessTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("Events.FieldModificationTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("Events.MethodEntryTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("Events.MethodExitTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("Events.MethodExitWithReturnValueTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("Events.SingleStepTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("Events.SingleStepWithPendingExceptionTest", RunJdwpTests::isAndroidNOrAbove)
+ .put("InterfaceType.InvokeMethodTest", RunJdwpTests::isAndroidNOrAbove)
+ .put("Method.IsObsoleteTest", RunJdwpTests::isAndroidNOrAbove)
+ .put("Method.VariableTableWithGenericTest", RunJdwpTests::isAndroidOOrAbove)
+ .put("ObjectReference.InvokeMethodDefaultTest", RunJdwpTests::isAndroidNOrAbove)
+ .put("ObjectReference.InvokeMethodDefault002Test", RunJdwpTests::isAndroidNOrAbove)
+ .put("ObjectReference.InvokeMethodAfterMultipleThreadSuspensionTest",
+ RunJdwpTests::isAndroidNOrAbove)
+ .put("ObjectReference.InvokeMethodWithSuspensionTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("ReferenceType.GetValues006Test", RunJdwpTests::isAndroidOOrAbove)
+ .put("ReferenceType.ClassLoaderTest", RunJdwpTests::isAndroidNOrAbove)
+ .put("StackFrame.GetValuesTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("StackFrame.SetValuesTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("StackFrame.SetValues002Test", RunJdwpTests::isAndroidMOrAbove)
+ .put("VirtualMachine.CapabilitiesNewTest", RunJdwpTests::isLatestRuntime)
+ .put("VirtualMachine.ClassPathsTest", RunJdwpTests::isAndroidMOrAbove)
+ .put("VirtualMachine.DisposeDuringInvokeTest", RunJdwpTests::isAndroidMOrAbove)
+ .build();
+
+ // The smoke tests are the set of tests that fail if there is no debugging info in the dex files.
+ // We avoid running the remaining tests as part of the D8/R8 testing to reduce test time by >30m.
+ static final Set<String> SMOKE_TESTS = ImmutableSet.of(
+ "EventModifiers.InstanceOnlyModifierTest",
+ "Method.VariableTableTest",
+ "Method.VariableTableWithGenericTest",
+ "ObjectReference.ReferringObjectsTest",
+ // This test assumes specific register allocation to make sure that a temporary object
+ // is unreachable at a specific point in a method. That is not guaranteed. See b/36921933.
+ // Currently doesn't fail but may start failing again with a count one higher than expected.
+ "ReferenceType.InstancesTest",
+ "StackFrame.GetValues002Test",
+ "StackFrame.GetValuesTest",
+ "StackFrame.SetValues002Test",
+ "StackFrame.SetValuesTest",
+ "VirtualMachine.InstanceCountsTest"
+ );
+
+ private static File d8Out = null;
+
+ @ClassRule
+ public static TemporaryFolder temp = new TemporaryFolder();
+
+ @BeforeClass
+ public static void compileLibraries() throws IOException, CompilationException {
+ // Selects appropriate jar according to min api level for the selected runtime.
+ int minApi = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+ Path jdwpTestsJar = ToolHelper.getJdwpTestsJarPath(minApi);
+
+ d8Out = temp.newFolder("d8-out");
+ D8.run(
+ D8Command.builder()
+ .addProgramFiles(jdwpTestsJar)
+ .setOutputPath(d8Out.toPath())
+ .setMinApiLevel(minApi)
+ .setMode(CompilationMode.DEBUG)
+ .build());
+ }
+
+ String getTestLib(Tool tool) {
+ if (tool == Tool.JAVAC) {
+ return JAR_LIB;
+ }
+ if (tool == Tool.DX) {
+ return DEX_LIB;
+ }
+ assert tool == Tool.D8;
+ return d8Out.toPath().resolve("classes.dex").toString();
+ }
+
+ DexVm getDexVm() {
+ return ToolHelper.getDexVm();
+ }
+
+ private void skipIfNeeded(String test, Tool tool) {
+ // Is it part of smoke tests ?
+ if (!RUN_ALL_TESTS) {
+ Assume.assumeTrue("Skipping non-smoke test " + test, SMOKE_TESTS.contains(test));
+ }
+ if (tool != Tool.JAVAC) {
+ // Can we run the test on the current ART runtime ?
+ Assume.assumeTrue("Skipping test " + test + " because ART is not supported",
+ ToolHelper.artSupported());
+ }
+ }
+
+ void runTest(String test, Tool tool) throws IOException {
+ skipIfNeeded(test, tool);
+ System.out.println("Running test " + test + " for tool " + tool);
+ String lib = getTestLib(tool);
+
+ List<String> command;
+ if (tool == Tool.JAVAC) {
+ String run = "org.junit.runner.JUnitCore";
+ String pkg = "org.apache.harmony.jpda.tests.jdwp";
+ command = Arrays.asList(
+ ToolHelper.getJavaExecutable(),
+ "-cp", System.getProperty("java.class.path") + ":" + lib,
+ run, pkg + "." + test);
+ } else {
+ command = Arrays.asList(
+ RUN_SCRIPT, "--classpath=" + lib, "--version=" + ToolHelper.getDexVm(), test);
+ }
+ ProcessBuilder builder = new ProcessBuilder(command);
+ ProcessResult result = ToolHelper.runProcess(builder);
+ if (FAILING_TESTS.containsKey(test) && !FAILING_TESTS.get(test).test(getDexVm(), tool)) {
+ if (PRINT_STREAMS || result.exitCode == 0) {
+ printStreams(result);
+ }
+ assertNotEquals("Expected test " + test + " to fail but it succeeded", 0, result.exitCode);
+ } else if (PRINT_STREAMS || result.exitCode != 0) {
+ printStreams(result);
+ assertEquals(0, result.exitCode);
+ }
+ }
+
+ private void printStreams(ProcessResult result) {
+ System.out.println("Test STDOUT");
+ System.out.println(result.stdout);
+ System.out.println("Test STDERR");
+ System.out.println(result.stderr);
+ }
+
+ @Test
+ public void testArrayReference_GetValuesTest_D8() throws IOException {
+ runTest("ArrayReference.GetValuesTest", Tool.D8);
+ }
+
+ @Test
+ public void testArrayReference_LengthTest_D8() throws IOException {
+ runTest("ArrayReference.LengthTest", Tool.D8);
+ }
+
+ @Test
+ public void testArrayReference_SetValues002Test_D8() throws IOException {
+ runTest("ArrayReference.SetValues002Test", Tool.D8);
+ }
+
+ @Test
+ public void testArrayReference_SetValues003Test_D8() throws IOException {
+ runTest("ArrayReference.SetValues003Test", Tool.D8);
+ }
+
+ @Test
+ public void testArrayReference_SetValuesTest_D8() throws IOException {
+ runTest("ArrayReference.SetValuesTest", Tool.D8);
+ }
+
+ @Test
+ public void testArrayType_NewInstanceTest_D8() throws IOException {
+ runTest("ArrayType.NewInstanceTest", Tool.D8);
+ }
+
+ @Test
+ public void testClassLoaderReference_VisibleClassesTest_D8() throws IOException {
+ runTest("ClassLoaderReference.VisibleClassesTest", Tool.D8);
+ }
+
+ @Test
+ public void testClassObjectReference_ReflectedType002Test_D8() throws IOException {
+ runTest("ClassObjectReference.ReflectedType002Test", Tool.D8);
+ }
+
+ @Test
+ public void testClassObjectReference_ReflectedTypeTest_D8() throws IOException {
+ runTest("ClassObjectReference.ReflectedTypeTest", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_InvokeMethod002Test_D8() throws IOException {
+ runTest("ClassType.InvokeMethod002Test", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_InvokeMethod003Test_D8() throws IOException {
+ runTest("ClassType.InvokeMethod003Test", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_InvokeMethodAfterMultipleThreadSuspensionTest_D8()
+ throws IOException {
+ runTest("ClassType.InvokeMethodAfterMultipleThreadSuspensionTest", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_InvokeMethodWithSuspensionTest_D8() throws IOException {
+ runTest("ClassType.InvokeMethodWithSuspensionTest", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_InvokeMethodTest_D8() throws IOException {
+ runTest("ClassType.InvokeMethodTest", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_NewInstance002Test_D8() throws IOException {
+ runTest("ClassType.NewInstance002Test", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_NewInstanceTagTest_D8() throws IOException {
+ runTest("ClassType.NewInstanceTagTest", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_NewInstanceAfterMultipleThreadSuspensionTest_D8()
+ throws IOException {
+ runTest("ClassType.NewInstanceAfterMultipleThreadSuspensionTest", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_NewInstanceStringTest_D8() throws IOException {
+ runTest("ClassType.NewInstanceStringTest", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_NewInstanceTest_D8() throws IOException {
+ runTest("ClassType.NewInstanceTest", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_NewInstanceWithSuspensionTest_D8() throws IOException {
+ runTest("ClassType.NewInstanceWithSuspensionTest", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_SetValues002Test_D8() throws IOException {
+ runTest("ClassType.SetValues002Test", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_SetValuesTest_D8() throws IOException {
+ runTest("ClassType.SetValuesTest", Tool.D8);
+ }
+
+ @Test
+ public void testClassType_SuperClassTest_D8() throws IOException {
+ runTest("ClassType.SuperClassTest", Tool.D8);
+ }
+
+ @Test
+ public void testDeoptimization_DeoptimizationWithExceptionHandlingTest_D8()
+ throws IOException {
+ runTest("Deoptimization.DeoptimizationWithExceptionHandlingTest", Tool.D8);
+ }
+
+ @Test
+ public void testEventModifiers_CountModifierTest_D8() throws IOException {
+ runTest("EventModifiers.CountModifierTest", Tool.D8);
+ }
+
+ @Test
+ public void testEventModifiers_InstanceOnlyModifierTest_D8() throws IOException {
+ runTest("EventModifiers.InstanceOnlyModifierTest", Tool.D8);
+ }
+
+ @Test
+ public void testEventModifiers_ThreadOnlyModifierTest_D8() throws IOException {
+ runTest("EventModifiers.ThreadOnlyModifierTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_Breakpoint002Test_D8() throws IOException {
+ runTest("Events.Breakpoint002Test", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_BreakpointMultipleTest_D8() throws IOException {
+ runTest("Events.BreakpointMultipleTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_BreakpointOnCatchTest_D8() throws IOException {
+ runTest("Events.BreakpointOnCatchTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_BreakpointTest_D8() throws IOException {
+ runTest("Events.BreakpointTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_ClassPrepare002Test_D8() throws IOException {
+ runTest("Events.ClassPrepare002Test", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_ClassPrepareTest_D8() throws IOException {
+ runTest("Events.ClassPrepareTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_CombinedEvents002Test_D8() throws IOException {
+ runTest("Events.CombinedEvents002Test", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_CombinedEvents003Test_D8() throws IOException {
+ runTest("Events.CombinedEvents003Test", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_CombinedEventsTest_D8() throws IOException {
+ runTest("Events.CombinedEventsTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_CombinedExceptionEventsTest_D8() throws IOException {
+ runTest("Events.CombinedExceptionEventsTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_EventWithExceptionTest_D8() throws IOException {
+ runTest("Events.EventWithExceptionTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_ExceptionCaughtTest_D8() throws IOException {
+ runTest("Events.ExceptionCaughtTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_ExceptionUncaughtTest_D8() throws IOException {
+ runTest("Events.ExceptionUncaughtTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_ExceptionWithLocationTest_D8() throws IOException {
+ runTest("Events.ExceptionWithLocationTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_FieldAccessTest_D8() throws IOException {
+ runTest("Events.FieldAccessTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_FieldModification002Test_D8() throws IOException {
+ runTest("Events.FieldModification002Test", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_FieldModificationTest_D8() throws IOException {
+ runTest("Events.FieldModificationTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_FieldWithLocationTest_D8() throws IOException {
+ runTest("Events.FieldWithLocationTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_MethodEntryTest_D8() throws IOException {
+ runTest("Events.MethodEntryTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_MethodExitTest_D8() throws IOException {
+ runTest("Events.MethodExitTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_MethodExitWithReturnValueTest_D8() throws IOException {
+ runTest("Events.MethodExitWithReturnValueTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_SingleStepTest_D8() throws IOException {
+ runTest("Events.SingleStepTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_SingleStepThroughReflectionTest_D8() throws IOException {
+ runTest("Events.SingleStepThroughReflectionTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_SingleStepWithLocationTest_D8() throws IOException {
+ runTest("Events.SingleStepWithLocationTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_SingleStepWithPendingExceptionTest_D8() throws IOException {
+ runTest("Events.SingleStepWithPendingExceptionTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_ThreadEndTest_D8() throws IOException {
+ runTest("Events.ThreadEndTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_ThreadStartTest_D8() throws IOException {
+ runTest("Events.ThreadStartTest", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_VMDeath002Test_D8() throws IOException {
+ runTest("Events.VMDeath002Test", Tool.D8);
+ }
+
+ @Test
+ public void testEvents_VMDeathTest_D8() throws IOException {
+ runTest("Events.VMDeathTest", Tool.D8);
+ }
+
+ @Test
+ public void testInterfaceType_InvokeMethodTest_D8() throws IOException {
+ runTest("InterfaceType.InvokeMethodTest", Tool.D8);
+ }
+
+ @Test
+ public void testMethod_BytecodesTest_D8() throws IOException {
+ runTest("Method.BytecodesTest", Tool.D8);
+ }
+
+ @Test
+ public void testMethod_IsObsoleteTest_D8() throws IOException {
+ runTest("Method.IsObsoleteTest", Tool.D8);
+ }
+
+ @Test
+ public void testMethod_LineTableTest_D8() throws IOException {
+ runTest("Method.LineTableTest", Tool.D8);
+ }
+
+ @Test
+ public void testMethod_VariableTableTest_D8() throws IOException {
+ runTest("Method.VariableTableTest", Tool.JAVAC);
+ }
+
+ @Test
+ public void testMethod_VariableTableWithGenericTest_D8() throws IOException {
+ runTest("Method.VariableTableWithGenericTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_AttachConnectorTest_D8() throws IOException {
+ runTest("MultiSession.AttachConnectorTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_BreakpointTest_D8() throws IOException {
+ runTest("MultiSession.BreakpointTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_ClassObjectIDTest_D8() throws IOException {
+ runTest("MultiSession.ClassObjectIDTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_ClassPrepareTest_D8() throws IOException {
+ runTest("MultiSession.ClassPrepareTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_EnableCollectionTest_D8() throws IOException {
+ runTest("MultiSession.EnableCollectionTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_ExceptionTest_D8() throws IOException {
+ runTest("MultiSession.ExceptionTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_FieldAccessTest_D8() throws IOException {
+ runTest("MultiSession.FieldAccessTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_FieldModificationTest_D8() throws IOException {
+ runTest("MultiSession.FieldModificationTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_ListenConnectorTest_D8() throws IOException {
+ runTest("MultiSession.ListenConnectorTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_MethodEntryExitTest_D8() throws IOException {
+ runTest("MultiSession.MethodEntryExitTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_RefTypeIDTest_D8() throws IOException {
+ runTest("MultiSession.RefTypeIDTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_ResumeTest_D8() throws IOException {
+ runTest("MultiSession.ResumeTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_SingleStepTest_D8() throws IOException {
+ runTest("MultiSession.SingleStepTest", Tool.D8);
+ }
+
+ @Test
+ public void testMultiSession_VMDeathTest_D8() throws IOException {
+ runTest("MultiSession.VMDeathTest", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_DisableCollectionTest_D8() throws IOException {
+ runTest("ObjectReference.DisableCollectionTest", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_EnableCollectionTest_D8() throws IOException {
+ runTest("ObjectReference.EnableCollectionTest", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_GetValues002Test_D8() throws IOException {
+ runTest("ObjectReference.GetValues002Test", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_GetValues003Test_D8() throws IOException {
+ runTest("ObjectReference.GetValues003Test", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_GetValuesTest_D8() throws IOException {
+ runTest("ObjectReference.GetValuesTest", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_InvokeMethod002Test_D8() throws IOException {
+ runTest("ObjectReference.InvokeMethod002Test", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_InvokeMethod003Test_D8() throws IOException {
+ runTest("ObjectReference.InvokeMethod003Test", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_InvokeMethodTest_D8() throws IOException {
+ runTest("ObjectReference.InvokeMethodTest", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_InvokeMethodAfterMultipleThreadSuspensionTest_D8()
+ throws IOException {
+ runTest("ObjectReference.InvokeMethodAfterMultipleThreadSuspensionTest", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_InvokeMethodDefault002Test_D8() throws IOException {
+ runTest("ObjectReference.InvokeMethodDefault002Test", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_InvokeMethodDefaultTest_D8() throws IOException {
+ runTest("ObjectReference.InvokeMethodDefaultTest", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_InvokeMethodWithSuspensionTest_D8() throws IOException {
+ runTest("ObjectReference.InvokeMethodWithSuspensionTest", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_IsCollectedTest_D8() throws IOException {
+ runTest("ObjectReference.IsCollectedTest", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_MonitorInfoTest_D8() throws IOException {
+ runTest("ObjectReference.MonitorInfoTest", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_ReferenceTypeTest_D8() throws IOException {
+ runTest("ObjectReference.ReferenceTypeTest", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_ReferringObjectsTest_D8() throws IOException {
+ runTest("ObjectReference.ReferringObjectsTest", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_SetValues002Test_D8() throws IOException {
+ runTest("ObjectReference.SetValues002Test", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_SetValues003Test_D8() throws IOException {
+ runTest("ObjectReference.SetValues003Test", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_SetValues004Test_D8() throws IOException {
+ runTest("ObjectReference.SetValues004Test", Tool.D8);
+ }
+
+ @Test
+ public void testObjectReference_SetValuesTest_D8() throws IOException {
+ runTest("ObjectReference.SetValuesTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_ClassLoaderTest_D8() throws IOException {
+ runTest("ReferenceType.ClassLoaderTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_ClassObjectTest_D8() throws IOException {
+ runTest("ReferenceType.ClassObjectTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_ConstantPoolTest_D8() throws IOException {
+ runTest("ReferenceType.ConstantPoolTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_FieldsTest_D8() throws IOException {
+ runTest("ReferenceType.FieldsTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_FieldsWithGenericTest_D8() throws IOException {
+ runTest("ReferenceType.FieldsWithGenericTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_GetValues002Test_D8() throws IOException {
+ runTest("ReferenceType.GetValues002Test", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_GetValues003Test_D8() throws IOException {
+ runTest("ReferenceType.GetValues003Test", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_GetValues004Test_D8() throws IOException {
+ runTest("ReferenceType.GetValues004Test", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_GetValues005Test_D8() throws IOException {
+ runTest("ReferenceType.GetValues005Test", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_GetValues006Test_D8() throws IOException {
+ runTest("ReferenceType.GetValues006Test", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_GetValues007Test_D8() throws IOException {
+ runTest("ReferenceType.GetValues007Test", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_GetValuesTest_D8() throws IOException {
+ runTest("ReferenceType.GetValuesTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_InstancesTest_D8() throws IOException {
+ runTest("ReferenceType.InstancesTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_InterfacesTest_D8() throws IOException {
+ runTest("ReferenceType.InterfacesTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_MethodsTest_D8() throws IOException {
+ runTest("ReferenceType.MethodsTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_MethodsWithGenericTest_D8() throws IOException {
+ runTest("ReferenceType.MethodsWithGenericTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_ModifiersTest_D8() throws IOException {
+ runTest("ReferenceType.ModifiersTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_Signature002Test_D8() throws IOException {
+ runTest("ReferenceType.Signature002Test", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_SignatureTest_D8() throws IOException {
+ runTest("ReferenceType.SignatureTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_SignatureWithGenericTest_D8() throws IOException {
+ runTest("ReferenceType.SignatureWithGenericTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_SourceDebugExtensionTest_D8() throws IOException {
+ runTest("ReferenceType.SourceDebugExtensionTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_SourceFileTest_D8() throws IOException {
+ runTest("ReferenceType.SourceFileTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_StatusTest_D8() throws IOException {
+ runTest("ReferenceType.StatusTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_SyntheticFieldsTest_D8() throws IOException {
+ runTest("ReferenceType.SyntheticFieldsTest", Tool.D8);
+ }
+
+ @Test
+ public void testReferenceType_SyntheticMethodsTest_D8() throws IOException {
+ runTest("ReferenceType.SyntheticMethodsTest", Tool.D8);
+ }
+
+ @Test
+ public void testStackFrame_GetValues002Test_D8() throws IOException {
+ runTest("StackFrame.GetValues002Test", Tool.D8);
+ }
+
+ @Test
+ public void testStackFrame_GetValuesTest_D8() throws IOException {
+ runTest("StackFrame.GetValuesTest", Tool.D8);
+ }
+
+ @Test
+ public void testStackFrame_PopFrames002Test_D8() throws IOException {
+ runTest("StackFrame.PopFrames002Test", Tool.D8);
+ }
+
+ @Test
+ public void testStackFrame_PopFramesTest_D8() throws IOException {
+ runTest("StackFrame.PopFramesTest", Tool.D8);
+ }
+
+ @Test
+ public void testStackFrame_ProxyThisObjectTest_D8() throws IOException {
+ runTest("StackFrame.ProxyThisObjectTest", Tool.D8);
+ }
+
+ @Test
+ public void testStackFrame_SetValues002Test_D8() throws IOException {
+ runTest("StackFrame.SetValues002Test", Tool.D8);
+ }
+
+ @Test
+ public void testStackFrame_SetValuesTest_D8() throws IOException {
+ runTest("StackFrame.SetValuesTest", Tool.D8);
+ }
+
+ @Test
+ public void testStackFrame_ThisObjectTest_D8() throws IOException {
+ runTest("StackFrame.ThisObjectTest", Tool.D8);
+ }
+
+ @Test
+ public void testStringReference_ValueTest_D8() throws IOException {
+ runTest("StringReference.ValueTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadGroupReference_ChildrenTest_D8() throws IOException {
+ runTest("ThreadGroupReference.ChildrenTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadGroupReference_NameTest_D8() throws IOException {
+ runTest("ThreadGroupReference.NameTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadGroupReference_ParentTest_D8() throws IOException {
+ runTest("ThreadGroupReference.ParentTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_CurrentContendedMonitorTest_D8() throws IOException {
+ runTest("ThreadReference.CurrentContendedMonitorTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_ForceEarlyReturn002Test_D8() throws IOException {
+ runTest("ThreadReference.ForceEarlyReturn002Test", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_ForceEarlyReturn003Test_D8() throws IOException {
+ runTest("ThreadReference.ForceEarlyReturn003Test", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_ForceEarlyReturn004Test_D8() throws IOException {
+ runTest("ThreadReference.ForceEarlyReturn004Test", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_ForceEarlyReturn005Test_D8() throws IOException {
+ runTest("ThreadReference.ForceEarlyReturn005Test", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_ForceEarlyReturn006Test_D8() throws IOException {
+ runTest("ThreadReference.ForceEarlyReturn006Test", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_ForceEarlyReturnTest_D8() throws IOException {
+ runTest("ThreadReference.ForceEarlyReturnTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_FrameCountTest_D8() throws IOException {
+ runTest("ThreadReference.FrameCountTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_FramesTest_D8() throws IOException {
+ runTest("ThreadReference.FramesTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_InterruptTest_D8() throws IOException {
+ runTest("ThreadReference.InterruptTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_NameTest_D8() throws IOException {
+ runTest("ThreadReference.NameTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_OwnedMonitorsStackDepthInfoTest_D8() throws IOException {
+ runTest("ThreadReference.OwnedMonitorsStackDepthInfoTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_OwnedMonitorsTest_D8() throws IOException {
+ runTest("ThreadReference.OwnedMonitorsTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_ResumeTest_D8() throws IOException {
+ runTest("ThreadReference.ResumeTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_Status002Test_D8() throws IOException {
+ runTest("ThreadReference.Status002Test", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_Status003Test_D8() throws IOException {
+ runTest("ThreadReference.Status003Test", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_Status004Test_D8() throws IOException {
+ runTest("ThreadReference.Status004Test", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_Status005Test_D8() throws IOException {
+ runTest("ThreadReference.Status005Test", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_Status006Test_D8() throws IOException {
+ runTest("ThreadReference.Status006Test", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_StatusTest_D8() throws IOException {
+ runTest("ThreadReference.StatusTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_SuspendCountTest_D8() throws IOException {
+ runTest("ThreadReference.SuspendCountTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_SuspendTest_D8() throws IOException {
+ runTest("ThreadReference.SuspendTest", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_ThreadGroup002Test_D8() throws IOException {
+ runTest("ThreadReference.ThreadGroup002Test", Tool.D8);
+ }
+
+ @Test
+ public void testThreadReference_ThreadGroupTest_D8() throws IOException {
+ runTest("ThreadReference.ThreadGroupTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_AllClassesTest_D8() throws IOException {
+ runTest("VirtualMachine.AllClassesTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_AllClassesWithGenericTest_D8() throws IOException {
+ runTest("VirtualMachine.AllClassesWithGenericTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_AllThreadsTest_D8() throws IOException {
+ runTest("VirtualMachine.AllThreadsTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_CapabilitiesNewTest_D8() throws IOException {
+ runTest("VirtualMachine.CapabilitiesNewTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_CapabilitiesTest_D8() throws IOException {
+ runTest("VirtualMachine.CapabilitiesTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_ClassesBySignatureTest_D8() throws IOException {
+ runTest("VirtualMachine.ClassesBySignatureTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_ClassPathsTest_D8() throws IOException {
+ runTest("VirtualMachine.ClassPathsTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_CreateStringTest_D8() throws IOException {
+ runTest("VirtualMachine.CreateStringTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_DisposeDuringInvokeTest_D8() throws IOException {
+ runTest("VirtualMachine.DisposeDuringInvokeTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_DisposeTest_D8() throws IOException {
+ runTest("VirtualMachine.DisposeTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_DisposeObjectsTest_D8() throws IOException {
+ runTest("VirtualMachine.DisposeObjectsTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_ExitTest_D8() throws IOException {
+ runTest("VirtualMachine.ExitTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_IDSizesTest_D8() throws IOException {
+ runTest("VirtualMachine.IDSizesTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_InstanceCountsTest_D8() throws IOException {
+ runTest("VirtualMachine.InstanceCountsTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_RedefineClassesTest_D8() throws IOException {
+ runTest("VirtualMachine.RedefineClassesTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_Resume002Test_D8() throws IOException {
+ runTest("VirtualMachine.Resume002Test", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_ResumeTest_D8() throws IOException {
+ runTest("VirtualMachine.ResumeTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_SetDefaultStratumTest_D8() throws IOException {
+ runTest("VirtualMachine.SetDefaultStratumTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_SuspendTest_D8() throws IOException {
+ runTest("VirtualMachine.SuspendTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_TopLevelThreadGroupsTest_D8() throws IOException {
+ runTest("VirtualMachine.TopLevelThreadGroupsTest", Tool.D8);
+ }
+
+ @Test
+ public void testVirtualMachine_VersionTest_D8() throws IOException {
+ runTest("VirtualMachine.VersionTest", Tool.D8);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jsr45/HelloKt.class b/src/test/java/com/android/tools/r8/jsr45/HelloKt.class
new file mode 100644
index 0000000..936fc80
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jsr45/HelloKt.class
Binary files differ
diff --git a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
new file mode 100644
index 0000000..66137df
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
@@ -0,0 +1,190 @@
+// 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.jsr45;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.AnnotationSubject;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.google.common.io.Closer;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class JSR45Tests {
+
+ private static final String DEFAULT_MAP_FILENAME = "proguard.map";
+ private static final Path INPUT_PATH =
+ Paths.get("src/test/java/com/android/tools/r8/jsr45/HelloKt.class");
+ private static final Path DONT_SHRINK_DONT_OBFUSCATE_CONFIG =
+ Paths.get("src/test/java/com/android/tools/r8/jsr45/keep-rules-1.txt");
+ private static final Path DONT_SHRINK_CONFIG =
+ Paths.get("src/test/java/com/android/tools/r8/jsr45/keep-rules-2.txt");
+ private static final Path SHRINK_KEEP_CONFIG =
+ Paths.get("src/test/java/com/android/tools/r8/jsr45/keep-rules-3.txt");
+ private static final Path SHRINK_NO_KEEP_CONFIG =
+ Paths.get("src/test/java/com/android/tools/r8/jsr45/keep-rules-4.txt");
+
+ @Rule
+ public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
+
+ void compileWithD8(Path intputPath, Path outputPath) throws IOException, CompilationException {
+ D8.run(
+ D8Command.builder()
+ .setMinApiLevel(Constants.ANDROID_O_API)
+ .addProgramFiles(intputPath)
+ .setOutputPath(outputPath)
+ .build());
+ }
+
+ void compileWithR8(Path inputPath, Path outputPath, Path keepRulesPath)
+ throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+ AndroidApp androidApp =
+ R8.run(
+ R8Command.builder()
+ .addProgramFiles(inputPath)
+ .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+ .setOutputPath(outputPath)
+ .addProguardConfigurationFiles(keepRulesPath)
+ .build());
+ if (androidApp.hasProguardMap()) {
+ try (Closer closer = Closer.create()) {
+ androidApp.writeProguardMap(closer, new FileOutputStream(
+ Paths.get(tmpOutputDir.getRoot().getCanonicalPath(), DEFAULT_MAP_FILENAME).toFile()));
+ }
+ }
+ }
+
+ static class ReadSourceDebugExtensionAttribute extends ClassVisitor {
+
+ private ReadSourceDebugExtensionAttribute(int api, ClassVisitor cv) {
+ super(api, cv);
+ }
+
+ private String debugSourceExtension = null;
+
+ @Override
+ public void visitSource(String source, String debug) {
+ debugSourceExtension = debug;
+ super.visitSource(source, debug);
+ }
+ }
+
+ @Test
+ public void testSourceDebugExtensionWithD8()
+ throws IOException, CompilationException, ExecutionException {
+ Path outputPath = tmpOutputDir.newFolder().toPath();
+
+ compileWithD8(INPUT_PATH, outputPath);
+
+ checkAnnotationContent(INPUT_PATH, outputPath);
+ }
+
+ /**
+ * Check that when dontshrink and dontobfuscate is used the annotation is transmitted.
+ */
+ @Test
+ public void testSourceDebugExtensionWithShriking1()
+ throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+ Path outputPath = tmpOutputDir.newFolder().toPath();
+ compileWithR8(INPUT_PATH, outputPath, DONT_SHRINK_DONT_OBFUSCATE_CONFIG);
+ checkAnnotationContent(INPUT_PATH, outputPath);
+ }
+
+ /**
+ * Check that when dontshrink is used the annotation is not removed due to obfuscation.
+ */
+ @Test
+ public void testSourceDebugExtensionWithShrinking2()
+ throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+ Path outputPath = tmpOutputDir.newFolder().toPath();
+ compileWithR8(INPUT_PATH, outputPath, DONT_SHRINK_CONFIG);
+ checkAnnotationContent(INPUT_PATH, outputPath);
+ }
+
+ /**
+ * Check that the annotation is transmitted when shrinking is enabled with a keepattribute option.
+ */
+ @Test
+ public void testSourceDebugExtensionWithShrinking3()
+ throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+ Path outputPath = tmpOutputDir.newFolder().toPath();
+
+ compileWithR8(INPUT_PATH, outputPath, SHRINK_KEEP_CONFIG);
+
+ checkAnnotationContent(INPUT_PATH, outputPath);
+ }
+
+ /**
+ * Check that the annotation is removed when shriking is enabled and that there is not
+ * keepattributes option.
+ */
+ @Test
+ public void testSourceDebugExtensionWithShrinking4()
+ throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+ Path outputPath = tmpOutputDir.newFolder().toPath();
+
+ compileWithR8(INPUT_PATH, outputPath, SHRINK_NO_KEEP_CONFIG);
+
+ DexInspector dexInspector =
+ new DexInspector(outputPath.resolve("classes.dex"), getGeneratedProguardMap());
+ ClassSubject classSubject = dexInspector.clazz("HelloKt");
+ AnnotationSubject annotationSubject =
+ classSubject.annotation("dalvik.annotation.SourceDebugExtension");
+ Assert.assertFalse(annotationSubject.isPresent());
+ }
+
+ private void checkAnnotationContent(Path inputPath, Path outputPath)
+ throws IOException, ExecutionException {
+ ClassReader classReader = new ClassReader(new FileInputStream(inputPath.toFile()));
+ ReadSourceDebugExtensionAttribute sourceDebugExtensionReader =
+ new ReadSourceDebugExtensionAttribute(Opcodes.ASM5, null);
+ classReader.accept(sourceDebugExtensionReader, 0);
+
+ DexInspector dexInspector =
+ new DexInspector(outputPath.resolve("classes.dex"), getGeneratedProguardMap());
+ ClassSubject classSubject = dexInspector.clazz("HelloKt");
+
+ AnnotationSubject annotationSubject =
+ classSubject.annotation("dalvik.annotation.SourceDebugExtension");
+ Assert.assertTrue(annotationSubject.isPresent());
+ DexAnnotationElement[] annotationElement = annotationSubject.getAnnotation().elements;
+ Assert.assertNotNull(annotationElement);
+ Assert.assertTrue(annotationElement.length == 1);
+ Assert.assertEquals("value", annotationElement[0].name.toString());
+ Assert.assertTrue(annotationElement[0].value instanceof DexValueString);
+ Assert.assertEquals(
+ sourceDebugExtensionReader.debugSourceExtension,
+ ((DexValueString) annotationElement[0].value).value.toSourceString());
+ }
+
+ private String getGeneratedProguardMap() throws IOException {
+ Path mapFile = Paths.get(tmpOutputDir.getRoot().getCanonicalPath(), DEFAULT_MAP_FILENAME);
+ if (Files.exists(mapFile)) {
+ return mapFile.toAbsolutePath().toString();
+ }
+ return null;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jsr45/keep-rules-1.txt b/src/test/java/com/android/tools/r8/jsr45/keep-rules-1.txt
new file mode 100644
index 0000000..04e2d30
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jsr45/keep-rules-1.txt
@@ -0,0 +1,7 @@
+# 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.
+
+-dontshrink
+-dontobfuscate
+
diff --git a/src/test/java/com/android/tools/r8/jsr45/keep-rules-2.txt b/src/test/java/com/android/tools/r8/jsr45/keep-rules-2.txt
new file mode 100644
index 0000000..916ee1c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jsr45/keep-rules-2.txt
@@ -0,0 +1,8 @@
+# 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.
+
+-dontshrink
+# allow access modification to enable minification
+-allowaccessmodification
+
diff --git a/src/test/java/com/android/tools/r8/jsr45/keep-rules-3.txt b/src/test/java/com/android/tools/r8/jsr45/keep-rules-3.txt
new file mode 100644
index 0000000..69395dc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jsr45/keep-rules-3.txt
@@ -0,0 +1,10 @@
+# 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.
+
+-keep,allowobfuscation public class HelloKt {
+ public static void main(...);
+}
+# allow access modification to enable minification
+-allowaccessmodification
+-keepattributes SourceDebugExtension
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/jsr45/keep-rules-4.txt b/src/test/java/com/android/tools/r8/jsr45/keep-rules-4.txt
new file mode 100644
index 0000000..1651fda
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jsr45/keep-rules-4.txt
@@ -0,0 +1,9 @@
+# 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.
+
+-keep,allowobfuscation public class HelloKt {
+ public static void main(...);
+}
+# allow access modification to enable minification
+-allowaccessmodification
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
new file mode 100644
index 0000000..7840b6e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -0,0 +1,416 @@
+// 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.maindexlist;
+
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.ApplicationWriter;
+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.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Switch.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.SourceCode;
+import com.android.tools.r8.ir.desugar.SynthesizedCode;
+import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.MainDexList;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public class MainDexListTests {
+
+ private static final Path BASE =
+ Paths.get("src", "test", "java", "com", "android", "tools", "r8", "maindexlist");
+
+ private static boolean verifyApplications = true;
+ private static boolean regenerateApplications = false;
+
+ private static final int MAX_METHOD_COUNT = Constants.U16BIT_MAX;
+ private static final List<String> TWO_LARGE_CLASSES = ImmutableList.of("A", "B");
+ private static final String TWO_LARGE_CLASSES_APP = "two-large-classes.zip";
+
+ private static final int MANY_CLASSES_COUNT = 1000;
+ private static final List<String> MANY_CLASSES;
+ private static final String MANY_CLASSES_APP = "many-classes.zip";
+ static {
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ for (int i = 0; i < MANY_CLASSES_COUNT; ++i) {
+ String pkg = i % 2 == 0 ? "a" : "b";
+ builder.add(pkg + ".Class" + i);
+ }
+ MANY_CLASSES = builder.build();
+ }
+
+ public static Path getTwoLargeClassesAppPath() {
+ return BASE.resolve(TWO_LARGE_CLASSES_APP);
+ }
+
+ public static Path getManyClassesAppPath() {
+ return BASE.resolve(MANY_CLASSES_APP);
+ }
+
+ // Generates an application with two classes, each with the maximum possible number of methods.
+ @Test
+ public void generateTwoLargeClasses() throws IOException, ExecutionException {
+ if (!verifyApplications && !regenerateApplications) {
+ return;
+ }
+ AndroidApp generated = generateApplication(TWO_LARGE_CLASSES, MAX_METHOD_COUNT);
+ if (regenerateApplications) {
+ generated.write(getTwoLargeClassesAppPath(), true);
+ } else {
+ AndroidApp cached = AndroidApp.fromProgramFiles(getTwoLargeClassesAppPath());
+ compareToCachedVersion(cached, generated, TWO_LARGE_CLASSES_APP);
+ }
+ }
+
+ // Generates an application with many classes, every even in one package and every odd in another.
+ @Test
+ public void generateManyClasses() throws IOException, ExecutionException {
+ if (!verifyApplications && !regenerateApplications) {
+ return;
+ }
+ AndroidApp generated = generateApplication(MANY_CLASSES, 1);
+ if (regenerateApplications) {
+ generated.write(getManyClassesAppPath(), true);
+ } else {
+ AndroidApp cached = AndroidApp.fromProgramFiles(getManyClassesAppPath());
+ compareToCachedVersion(cached, generated, MANY_CLASSES_APP);
+ }
+ }
+
+ private static void compareToCachedVersion(AndroidApp cached, AndroidApp generated, String name)
+ throws IOException {
+ assertEquals("On-disk cached app (" + name + ") differs in file count from regeneration"
+ + "Set 'regenerateApplications = true' and rerun this test to update the cache.",
+ cached.getDexProgramResources().size(), generated.getDexProgramResources().size());
+ try (Closer closer = Closer.create()) {
+ for (int i = 0; i < cached.getDexProgramResources().size(); i++) {
+ byte[] cachedBytes =
+ ByteStreams.toByteArray(cached.getDexProgramResources().get(i).getStream(closer));
+ byte[] generatedBytes =
+ ByteStreams.toByteArray(generated.getDexProgramResources().get(i).getStream(closer));
+ assertArrayEquals("On-disk cached app differs in byte content from regeneration"
+ + "Set 'regenerateApplications = true' and rerun this test to update the cache.",
+ cachedBytes, generatedBytes);
+ }
+ }
+ }
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void putFirstClassInMainDexList() throws Throwable {
+ verifyMainDexContains(TWO_LARGE_CLASSES.subList(0, 1), getTwoLargeClassesAppPath());
+ }
+
+ @Test
+ public void putSecondClassInMainDexList() throws Throwable {
+ verifyMainDexContains(TWO_LARGE_CLASSES.subList(1, 2), getTwoLargeClassesAppPath());
+ }
+
+ @Test
+ public void cannotFitBothIntoMainDex() throws Throwable {
+ thrown.expect(CompilationError.class);
+ verifyMainDexContains(TWO_LARGE_CLASSES, getTwoLargeClassesAppPath());
+ }
+
+ @Test
+ public void everySecondClassInMainDex() throws Throwable {
+ ImmutableList.Builder<String> mainDexBuilder = ImmutableList.builder();
+ for (int i = 0; i < MANY_CLASSES.size(); i++) {
+ String clazz = MANY_CLASSES.get(i);
+ if (i % 3 == 0) {
+ mainDexBuilder.add(clazz);
+ }
+ }
+ verifyMainDexContains(mainDexBuilder.build(), getManyClassesAppPath());
+ }
+
+ @Test
+ public void validEntries() throws IOException {
+ List<String> list = ImmutableList.of(
+ "A.class",
+ "a/b/c/D.class",
+ "a/b/c/D$E.class"
+ );
+ DexItemFactory factory = new DexItemFactory();
+ Path mainDexList = temp.getRoot().toPath().resolve("valid.txt");
+ FileUtils.writeTextFile(mainDexList, list);
+ Set<DexType> types = MainDexList.parse(mainDexList, factory);
+ for (String entry : list) {
+ assertTrue(types.contains(factory.createType("L" + entry.replace(".class", "") + ";")));
+ }
+ }
+
+ @Test
+ public void invalidQualifiedEntry() throws IOException {
+ thrown.expect(CompilationError.class);
+ DexItemFactory factory = new DexItemFactory();
+ Path mainDexList = temp.getRoot().toPath().resolve("invalid.txt");
+ FileUtils.writeTextFile(mainDexList, ImmutableList.of("a.b.c.D.class"));
+ MainDexList.parse(mainDexList, factory);
+ }
+
+ private static String typeToEntry(String type) {
+ return type.replace(".", "/") + CLASS_EXTENSION;
+ }
+
+ private void verifyMainDexContains(List<String> mainDex, Path app)
+ throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+ AndroidApp originalApp = AndroidApp.fromProgramFiles(app);
+ DexInspector originalInspector = new DexInspector(originalApp);
+ for (String clazz : mainDex) {
+ assertTrue("Class " + clazz + " does not exist in input",
+ originalInspector.clazz(clazz).isPresent());
+ }
+ Path outDir = temp.newFolder("out").toPath();
+ Path mainDexList = temp.getRoot().toPath().resolve("main-dex-list.txt");
+ FileUtils.writeTextFile(mainDexList, ListUtils.map(mainDex, MainDexListTests::typeToEntry));
+ Path packageDistribution = temp.getRoot().toPath().resolve("package.map");
+ FileUtils.writeTextFile(packageDistribution, ImmutableList.of("a.*:2", "b.*:1"));
+ R8Command command =
+ R8Command.builder()
+ .addProgramFiles(app)
+ .setPackageDistributionFile(packageDistribution)
+ .setMainDexListFile(mainDexList)
+ .setOutputPath(outDir)
+ .setTreeShaking(false)
+ .setMinification(false)
+ .build();
+ ToolHelper.runR8(command);
+ assertTrue("Output run only produced one dex file. Invalid test",
+ 1 < Files.list(outDir).filter(FileUtils::isDexFile).count());
+ DexInspector inspector =
+ new DexInspector(AndroidApp.fromProgramFiles(outDir.resolve("classes.dex")));
+ for (String clazz : mainDex) {
+ if (!inspector.clazz(clazz).isPresent()) {
+ Files.list(outDir)
+ .filter(FileUtils::isDexFile)
+ .forEach(
+ p -> {
+ try {
+ DexInspector i = new DexInspector(AndroidApp.fromProgramFiles(p));
+ assertFalse("Found " + clazz + " in file " + p, i.clazz(clazz).isPresent());
+ } catch (IOException | ExecutionException e) {
+ e.printStackTrace();
+ }
+ });
+ fail("Failed to find class " + clazz + "in any file...");
+ }
+ }
+ }
+
+ private static AndroidApp generateApplication(List<String> classes, int methodCount)
+ throws IOException, ExecutionException {
+ Timing timing = new Timing("MainDexListTests");
+ InternalOptions options = new InternalOptions();
+ DexItemFactory factory = options.itemFactory;
+ DexApplication.Builder builder = new DexApplication.Builder(factory, timing);
+ for (String clazz : classes) {
+ DexString desc = factory.createString(DescriptorUtils.javaTypeToDescriptor(clazz));
+ DexType type = factory.createType(desc);
+ DexEncodedMethod[] virtualMethods = new DexEncodedMethod[methodCount];
+ for (int i = 0; i < methodCount; i++) {
+ DexAccessFlags access = new DexAccessFlags();
+ access.setPublic();
+ access.setStatic();
+ Code code = new SynthesizedCode(new ReturnVoidCode());
+ DexEncodedMethod method =
+ new DexEncodedMethod(
+ factory.createMethod(
+ desc,
+ factory.createString("method" + i),
+ factory.voidDescriptor,
+ DexString.EMPTY_ARRAY),
+ access,
+ DexAnnotationSet.empty(),
+ DexAnnotationSetRefList.empty(),
+ code);
+ IRCode ir = code.buildIR(method, options);
+ RegisterAllocator allocator = new LinearScanRegisterAllocator(ir);
+ method.setCode(ir, allocator);
+ virtualMethods[i] = method;
+ }
+ builder.addClassPromise(
+ new DexProgramClass(
+ type,
+ DexClass.Origin.Synthetic,
+ new DexAccessFlags(),
+ factory.objectType,
+ DexTypeList.empty(),
+ null,
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedMethod.EMPTY_ARRAY,
+ virtualMethods));
+ }
+ DexApplication application = builder.build();
+ AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
+ ApplicationWriter writer =
+ new ApplicationWriter(application, appInfo, options, NamingLens.getIdentityLens(), null);
+ ExecutorService executor = ThreadUtils.getExecutorService(options);
+ try {
+ return writer.write(null, executor);
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ // Code stub to generate methods with "return-void" bodies.
+ private static class ReturnVoidCode implements SourceCode {
+
+ @Override
+ public int instructionCount() {
+ return 1;
+ }
+
+ @Override
+ public int instructionIndex(int instructionOffset) {
+ return instructionOffset;
+ }
+
+ @Override
+ public int instructionOffset(int instructionIndex) {
+ return instructionIndex;
+ }
+
+ @Override
+ public boolean needsPrelude() {
+ return false;
+ }
+
+ @Override
+ public DebugLocalInfo getCurrentLocal(int register) {
+ return null;
+ }
+
+ @Override
+ public boolean traceInstruction(int instructionIndex, IRBuilder builder) {
+ return true;
+ }
+
+ @Override
+ public void closedCurrentBlockWithFallthrough(int fallthroughInstructionIndex) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void closedCurrentBlock() {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void setUp() {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void clear() {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void buildPrelude(IRBuilder builder) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void buildInstruction(IRBuilder builder, int instructionIndex) {
+ assert instructionIndex == 0;
+ builder.addReturn();
+ }
+
+ @Override
+ public void buildPostlude(IRBuilder builder) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void resolveAndBuildSwitch(
+ Type type, int value, int fallthroughOffset, int payloadOffset, IRBuilder builder) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void resolveAndBuildNewArrayFilledData(
+ int arrayRef, int payloadOffset, IRBuilder builder) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public CatchHandlers<Integer> getCurrentCatchHandlers() {
+ return null;
+ }
+
+ @Override
+ public boolean verifyRegister(int register) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public boolean verifyCurrentInstructionCanThrow() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public boolean verifyLocalInScope(DebugLocalInfo local) {
+ throw new Unreachable();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/many-classes.zip b/src/test/java/com/android/tools/r8/maindexlist/many-classes.zip
new file mode 100644
index 0000000..3c1530d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/many-classes.zip
Binary files differ
diff --git a/src/test/java/com/android/tools/r8/maindexlist/two-large-classes.zip b/src/test/java/com/android/tools/r8/maindexlist/two-large-classes.zip
new file mode 100644
index 0000000..97b5fde
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/two-large-classes.zip
Binary files differ
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
new file mode 100644
index 0000000..e45b639
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
@@ -0,0 +1,263 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.memberrebinding;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.FieldAccessInstructionSubject;
+import com.android.tools.r8.utils.DexInspector.InstructionSubject;
+import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.ListUtils;
+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.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MemberRebindingTest {
+
+ private static final String ANDROID_JAR = ToolHelper.getDefaultAndroidJar();
+ private static final List<String> JAR_LIBRARIES = ImmutableList
+ .of(ANDROID_JAR, ToolHelper.EXAMPLES_BUILD_DIR + "memberrebindinglib.jar");
+ private static final List<String> DEX_LIBRARIES = ImmutableList
+ .of(ANDROID_JAR, ToolHelper.EXAMPLES_BUILD_DIR + "memberrebindinglib/classes.dex");
+
+ private enum Frontend {
+ DEX, JAR;
+
+ @Override
+ public String toString() {
+ return this == DEX ? ".dex" : ".jar";
+ }
+ }
+
+ private final Frontend kind;
+ private final Path originalDex;
+ private final Path programFile;
+ private final Consumer<DexInspector> inspection;
+ private final Consumer<DexInspector> originalInspection;
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ public MemberRebindingTest(
+ String test, Frontend kind,
+ Consumer<DexInspector> inspection,
+ Consumer<DexInspector> originalInspection) {
+ this.kind = kind;
+ originalDex = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + test + "/classes.dex");
+ if (kind == Frontend.DEX) {
+ this.programFile = originalDex;
+ } else {
+ this.programFile = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + test + ".jar");
+ }
+ this.inspection = inspection;
+ this.originalInspection = originalInspection;
+ }
+
+ @Before
+ public void runR8()
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+ // Generate R8 processed version without library option.
+ String out = temp.getRoot().getCanonicalPath();
+ List<String> libs = kind == Frontend.DEX ? DEX_LIBRARIES : JAR_LIBRARIES;
+ // NOTE: It is important to turn off inlining to ensure
+ // dex inspection of invokes is predictable.
+ ToolHelper.runR8(
+ R8Command.builder()
+ .setOutputPath(Paths.get(out))
+ .addProgramFiles(programFile)
+ .addLibraryFiles(ListUtils.map(libs, Paths::get))
+ .build(),
+ options -> options.inlineAccessors = false);
+ }
+
+ private static boolean coolInvokes(InstructionSubject instruction) {
+ if (!instruction.isInvokeVirtual() && !instruction.isInvokeInterface()) {
+ return false;
+ }
+ InvokeInstructionSubject invoke = (InvokeInstructionSubject) instruction;
+ return !invoke.holder().is("java.io.PrintStream");
+ }
+
+ private static void inspectOriginalMain(DexInspector inspector) {
+ MethodSubject main = inspector.clazz("memberrebinding.Test").method(DexInspector.MAIN);
+ Iterator<InvokeInstructionSubject> iterator =
+ main.iterateInstructions(MemberRebindingTest::coolInvokes);
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+ assertTrue(iterator.next().holder().is("memberrebinding.subpackage.PublicClass"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsOtherLibraryClass"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsOtherLibraryClass"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsOtherLibraryClass"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsOtherLibraryClass"));
+ assertTrue(iterator.next().holder().is("memberrebindinglib.AnIndependentInterface"));
+ assertTrue(
+ iterator.next().holder().is("memberrebinding.SuperClassOfClassExtendsOtherLibraryClass"));
+ assertTrue(
+ iterator.next().holder().is("memberrebinding.SuperClassOfClassExtendsOtherLibraryClass"));
+ assertFalse(iterator.hasNext());
+ }
+
+ private static void inspectMain(DexInspector inspector) {
+ MethodSubject main = inspector.clazz("memberrebinding.Test").method(DexInspector.MAIN);
+ Iterator<InvokeInstructionSubject> iterator =
+ main.iterateInstructions(MemberRebindingTest::coolInvokes);
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassInMiddleOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding.SuperClassOfAll"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+ assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+ // For the next three - test that we re-bind to library methods (holder is java.util.ArrayList).
+ assertTrue(iterator.next().holder().is("java.util.ArrayList"));
+ assertTrue(iterator.next().holder().is("java.util.ArrayList"));
+ assertTrue(iterator.next().holder().is("java.util.ArrayList"));
+ assertTrue(iterator.next().holder().is("memberrebinding.subpackage.PackagePrivateClass"));
+ // For the next three - test that we re-bind to the lowest library class.
+ assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
+ assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
+ assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
+ // The next one is already precise.
+ assertTrue(
+ iterator.next().holder().is("memberrebinding.SuperClassOfClassExtendsOtherLibraryClass"));
+ // Some dispatches on interfaces.
+ assertTrue(iterator.next().holder().is("memberrebindinglib.AnIndependentInterface"));
+ assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
+ assertTrue(iterator.next().holder().is("memberrebindinglib.ImplementedInProgramClass"));
+ assertFalse(iterator.hasNext());
+ }
+
+ private static void inspectOriginalMain2(DexInspector inspector) {
+ MethodSubject main = inspector.clazz("memberrebinding2.Test").method(DexInspector.MAIN);
+ Iterator<FieldAccessInstructionSubject> iterator =
+ main.iterateInstructions(InstructionSubject::isFieldAccess);
+ // Run through instance put, static put, instance get and instance get.
+ for (int i = 0; i < 4; i++) {
+ assertTrue(iterator.next().holder().is("memberrebinding2.ClassAtBottomOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding2.ClassAtBottomOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding2.ClassAtBottomOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding2.subpackage.PublicClass"));
+ }
+ }
+
+ private static void inspectMain2(DexInspector inspector) {
+ MethodSubject main = inspector.clazz("memberrebinding2.Test").method(DexInspector.MAIN);
+ Iterator<FieldAccessInstructionSubject> iterator =
+ main.iterateInstructions(InstructionSubject::isFieldAccess);
+ // Run through instance put, static put, instance get and instance get.
+ for (int i = 0; i < 4; i++) {
+ assertTrue(iterator.next().holder().is("memberrebinding2.ClassAtBottomOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding2.ClassInMiddleOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding2.SuperClassOfAll"));
+ assertTrue(iterator.next().holder().is("memberrebinding2.subpackage.PackagePrivateClass"));
+ }
+ }
+
+ public static MethodSignature TEST =
+ new MethodSignature("test", "void", new String[]{});
+
+ private static void inspectOriginal3(DexInspector inspector) {
+ MethodSubject main = inspector.clazz("memberrebinding3.Test").method(TEST);
+ Iterator<InvokeInstructionSubject> iterator =
+ main.iterateInstructions(InstructionSubject::isInvoke);
+ assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
+ }
+
+ private static void inspect3(DexInspector inspector) {
+ MethodSubject main = inspector.clazz("memberrebinding3.Test").method(TEST);
+ Iterator<InvokeInstructionSubject> iterator =
+ main.iterateInstructions(InstructionSubject::isInvoke);
+ assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding3.ClassInMiddleOfChain"));
+ assertTrue(iterator.next().holder().is("memberrebinding3.SuperClassOfAll"));
+ }
+
+ @Parameters(name = "{0}{1}")
+ public static Collection<Object[]> data() {
+ List<String> tests =
+ ImmutableList.of("memberrebinding", "memberrebinding2", "memberrebinding3");
+
+ Map<String, List<Consumer<DexInspector>>> inspections = new HashMap<>();
+ inspections.put("memberrebinding", ImmutableList.of(
+ MemberRebindingTest::inspectMain,
+ MemberRebindingTest::inspectOriginalMain));
+ inspections.put("memberrebinding2", ImmutableList.of(
+ MemberRebindingTest::inspectMain2,
+ MemberRebindingTest::inspectOriginalMain2));
+ inspections.put("memberrebinding3", ImmutableList.of(
+ MemberRebindingTest::inspect3,
+ MemberRebindingTest::inspectOriginal3));
+
+ List<Object[]> testCases = new ArrayList<>();
+ Set<String> usedInspections = new HashSet<>();
+
+ for (String test : tests) {
+ List<Consumer<DexInspector>> inspection = inspections.get(test);
+ assert inspection != null;
+ usedInspections.add(test);
+ testCases.add(new Object[]{test, Frontend.JAR, inspection.get(0), inspection.get(1)});
+ testCases.add(new Object[]{test, Frontend.DEX, inspection.get(0), inspection.get(1)});
+ }
+
+ assert usedInspections.size() == inspections.size();
+
+ return testCases;
+ }
+
+ @Test
+ public void memberRebindingTest() throws IOException, InterruptedException, ExecutionException {
+ if (!ToolHelper.artSupported()) {
+ return;
+ }
+ String out = temp.getRoot().getCanonicalPath();
+ Path processed = Paths.get(out, "classes.dex");
+
+ if (kind == Frontend.DEX) {
+ DexInspector inspector = new DexInspector(originalDex);
+ originalInspection.accept(inspector);
+ }
+
+ DexInspector inspector = new DexInspector(processed);
+ inspection.accept(inspector);
+
+ // We don't run Art, as the test R8RunExamplesTest already does that.
+ // ToolHelper.checkArtOutputIdentical(originalDex, processed, mainClass, null);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
new file mode 100644
index 0000000..dccfd8a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.ToolHelper;
+import java.io.IOException;
+import java.nio.file.Paths;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ProguardMapReaderTest {
+
+ public static final String ROOT = ToolHelper.EXAMPLES_BUILD_DIR;
+ public static final String EXAMPLE_MAP = "throwing/throwing.map";
+
+ public static final String EXAMPLE_MAP_WITH_PACKAGE_INFO =
+ "dagger.android.package-info -> dagger.android.package-info\n";
+
+ @Test
+ public void parseThrowingMap() throws IOException {
+ ProguardMapReader.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
+ }
+
+ @Test
+ public void roundTripTest() throws IOException {
+ ClassNameMapper firstMapper = ProguardMapReader.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
+ ClassNameMapper secondMapper = ProguardMapReader.mapperFromString(firstMapper.toString());
+ Assert.assertEquals(firstMapper, secondMapper);
+ }
+
+ @Test
+ public void parseMapWithPackageInfo() throws IOException {
+ ClassNameMapper mapper = ProguardMapReader.mapperFromString(EXAMPLE_MAP_WITH_PACKAGE_INFO);
+ Assert.assertTrue(mapper.getObfuscatedToOriginalMapping().isEmpty());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/R8DebugStrippingTest.java b/src/test/java/com/android/tools/r8/optimize/R8DebugStrippingTest.java
new file mode 100644
index 0000000..72b452f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/R8DebugStrippingTest.java
@@ -0,0 +1,234 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.ClassNaming;
+import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.naming.MemberNaming.InlineInformation;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.ProguardMapReader;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class R8DebugStrippingTest {
+
+ private static final String ROOT = ToolHelper.EXAMPLES_BUILD_DIR;
+ private static final String EXAMPLE_DEX = "throwing/classes.dex";
+ private static final String EXAMPLE_MAP = "throwing/throwing.map";
+ private static final String EXAMPLE_CLASS = "throwing.Throwing";
+ private static final String EXAMPLE_JAVA = "Throwing.java";
+
+ private static final String MAIN_NAME = "main";
+ private static final String[] MAIN_PARAMETERS = new String[]{"java.lang.String[]"};
+ private static final String VOID_RETURN = "void";
+
+ private static final String OTHER_NAME = "throwInAFunctionThatIsNotInlinedAndCalledTwice";
+ private static final String[] NO_PARAMETERS = new String[0];
+ private static final String INT_RETURN = "int";
+
+ private static final String THIRD_NAME = "aFunctionThatCallsAnInlinedMethodThatThrows";
+ private static final String[] LIST_PARAMETER = new String[]{"java.util.List"};
+
+ private static final String FORTH_NAME = "anotherFunctionThatCallsAnInlinedMethodThatThrows";
+ private static final String[] STRING_PARAMETER = new String[]{"java.lang.String"};
+
+ private static final Map<String, Signature> SIGNATURE_MAP = ImmutableMap.of(
+ MAIN_NAME, new MethodSignature(MAIN_NAME, VOID_RETURN, MAIN_PARAMETERS),
+ OTHER_NAME, new MethodSignature(OTHER_NAME, INT_RETURN, NO_PARAMETERS),
+ THIRD_NAME, new MethodSignature(THIRD_NAME, INT_RETURN, LIST_PARAMETER),
+ FORTH_NAME, new MethodSignature(FORTH_NAME, INT_RETURN, STRING_PARAMETER)
+ );
+
+ private ClassNameMapper mapper;
+
+ @Parameter(0)
+ public boolean compressRanges;
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Before
+ public void loadRangeInformation() throws IOException {
+ mapper = ProguardMapReader.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
+ }
+
+ @Parameters(name = "compressLineNumers={0}")
+ public static Object[] parameters() {
+ return new Object[]{true, false};
+ }
+
+ @Test
+ public void testStackTraces()
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+
+ // Temporary directory for R8 output.
+ Path out = temp.getRoot().toPath();
+
+ R8Command command =
+ R8Command.builder()
+ .addProgramFiles(Paths.get(ROOT, EXAMPLE_DEX))
+ .setOutputPath(out)
+ .setProguardMapFile(Paths.get(ROOT, EXAMPLE_MAP))
+ .build();
+
+ // Generate R8 processed version.
+ AndroidApp result =
+ ToolHelper.runR8(command, (options) -> options.skipDebugLineNumberOpt = !compressRanges);
+
+ ClassNameMapper classNameMapper;
+ try (Closer closer = Closer.create()) {
+ classNameMapper = ProguardMapReader.mapperFromInputStream(result.getProguardMap(closer));
+ }
+ if (compressRanges) {
+ classNameMapper.forAllClassNamings(this::ensureRangesAreUniquePerClass);
+ }
+
+ if (!ToolHelper.artSupported()) {
+ return;
+ }
+ // Run art on original.
+ String originalOutput =
+ ToolHelper.runArtNoVerificationErrors(ROOT + EXAMPLE_DEX, EXAMPLE_CLASS);
+ // Run art on R8 processed version.
+ String otherOutput =
+ ToolHelper.runArtNoVerificationErrors(out + "/classes.dex", EXAMPLE_CLASS);
+ // Check that exceptions are in same range
+ assertStacktracesMatchRanges(originalOutput, otherOutput, classNameMapper);
+
+ // Check that we have debug information in all the places required.
+ DexInspector inspector = new DexInspector(out.resolve("classes.dex"));
+ BiMap<String, String> obfuscationMap
+ = classNameMapper.getObfuscatedToOriginalMapping().inverse();
+ ClassSubject overloaded = inspector.clazz(obfuscationMap.get("throwing.Overloaded"));
+ assertTrue(overloaded.isPresent());
+ ensureDebugInfosExist(overloaded);
+ }
+
+ private void ensureDebugInfosExist(ClassSubject overloaded) {
+ final Map<DexString, Boolean> hasDebugInfo = Maps.newIdentityHashMap();
+ overloaded.forAllMethods(method -> {
+ if (!method.isAbstract()) {
+ DexCode code = method.getMethod().getCode().asDexCode();
+ DexString name = method.getMethod().method.name;
+ Boolean previous = hasDebugInfo.get(name);
+ boolean current = code.getDebugInfo() != null;
+ // If we have seen one before, it should be the same as now.
+ assertTrue(previous == null || (previous == current));
+ hasDebugInfo.put(name, current);
+ }
+ }
+ );
+ }
+
+ private void ensureRangesAreUniquePerClass(ClassNaming naming) {
+ final Map<String, Set<Integer>> rangeMap = new HashMap<>();
+ naming.forAllMemberNaming(memberNaming -> {
+ if (memberNaming.isMethodNaming()) {
+ if (memberNaming.topLevelRange != MemberNaming.fakeZeroRange) {
+ int startLine = memberNaming.topLevelRange.from;
+ Set<Integer> used = rangeMap
+ .computeIfAbsent(memberNaming.getRenamedName(), any -> new HashSet<>());
+ assertFalse(used.contains(startLine));
+ used.add(startLine);
+ }
+ }
+ });
+ }
+
+ private String extractRangeIndex(String line, ClassNameMapper mapper) {
+ int position = line.lastIndexOf(EXAMPLE_JAVA);
+ assertNotSame("Malformed stackframe: " + line, -1, position);
+ String numberPart = line.substring(position + EXAMPLE_JAVA.length() + 1, line.lastIndexOf(')'));
+ int number = Integer.parseInt(numberPart);
+ // Search the signature map for all signatures that actually match. We do this by first looking
+ // up the renamed signature and then checking whether it is contained in the line. We prepend
+ // the class name to make sure we do not match random characters in the line.
+ for (Entry<String, Signature> entry : SIGNATURE_MAP.entrySet()) {
+ MemberNaming naming = mapper.getClassNaming(EXAMPLE_CLASS)
+ .lookupByOriginalSignature(entry.getValue());
+ if (!line.contains(EXAMPLE_CLASS + "." + naming.getRenamedName())) {
+ continue;
+ }
+ if (naming.topLevelRange.contains(number)) {
+ return entry.getKey() + ":" + 0;
+ }
+ int rangeNo = 1;
+ for (InlineInformation inlineInformation : naming.inlineInformation) {
+ if (inlineInformation.inlinedRange.contains(number)) {
+ return entry.getKey() + ":" + rangeNo;
+ }
+ rangeNo++;
+ }
+ }
+ fail("Number not in any range " + number);
+ return null;
+ }
+
+ private MemberNaming selectRanges(String line, ClassNameMapper mapper) {
+ Signature signature;
+ for (Entry<String, Signature> entry : SIGNATURE_MAP.entrySet()) {
+ if (line.contains(entry.getKey())) {
+ return mapper.getClassNaming(EXAMPLE_CLASS).lookup(entry.getValue());
+ }
+ }
+ Assert.fail("unknown method in line " + line);
+ return null;
+ }
+
+ private void assertStacktracesMatchRanges(String before, String after,
+ ClassNameMapper newMapper) {
+ String[] beforeLines = before.split("\n");
+ String[] afterLines = after.split("\n");
+ assertEquals("Output length differs", beforeLines.length,
+ afterLines.length);
+ for (int i = 0; i < beforeLines.length; i++) {
+ if (!beforeLines[i].startsWith("FRAME:")) {
+ continue;
+ }
+ String beforeLine = beforeLines[i];
+ String expected = extractRangeIndex(beforeLine, mapper);
+ String afterLine = afterLines[i];
+ String generated = extractRangeIndex(afterLine, newMapper);
+ assertEquals("Ranges match", expected, generated);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/Regress37740372.java b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
new file mode 100644
index 0000000..5258b31
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
@@ -0,0 +1,146 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.regress;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import java.util.Base64;
+import java.util.List;
+import org.junit.Test;
+
+public class Regress37740372 extends SmaliTestBase {
+
+ /*
+ Binary class file data for a java.lang.Object stub implementation. Runnong javap -c on the
+ bytecode produces the following output:
+
+ Compiled from "Object.java"
+ public class java.lang.Object {
+ public java.lang.Object();
+ Code:
+ 0: new #1 // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #2 // String Stub!
+ 6: invokespecial #3 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ protected java.lang.Object clone() throws java.lang.CloneNotSupportedException;
+ Code:
+ 0: new #1 // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #2 // String Stub!
+ 6: invokespecial #3 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public boolean equals(java.lang.Object);
+ Code:
+ 0: new #1 // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #2 // String Stub!
+ 6: invokespecial #3 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ protected void finalize() throws java.lang.Throwable;
+ Code:
+ 0: new #1 // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #2 // String Stub!
+ 6: invokespecial #3 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public final native java.lang.Class<?> getClass();
+
+ public native int hashCode();
+
+ public final native void notify();
+
+ public final native void notifyAll();
+
+ public java.lang.String toString();
+ Code:
+ 0: new #1 // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #2 // String Stub!
+ 6: invokespecial #3 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public final void wait() throws java.lang.InterruptedException;
+ Code:
+ 0: new #1 // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #2 // String Stub!
+ 6: invokespecial #3 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public final void wait(long) throws java.lang.InterruptedException;
+ Code:
+ 0: new #1 // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #2 // String Stub!
+ 6: invokespecial #3 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public final native void wait(long, int) throws java.lang.InterruptedException;
+ }
+ */
+ String javaLangObjectClassFile =
+ "yv66vgAAADEALwcAJwgAKAoAAQApBwAqAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1i" +
+ "ZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABJMamF2YS9sYW5nL09iamVjdDsB" +
+ "AAVjbG9uZQEAFCgpTGphdmEvbGFuZy9PYmplY3Q7AQAKRXhjZXB0aW9ucwcAKwEABmVxdWFscwEA" +
+ "FShMamF2YS9sYW5nL09iamVjdDspWgEAAW8BAAhmaW5hbGl6ZQcALAEACGdldENsYXNzAQATKClM" +
+ "amF2YS9sYW5nL0NsYXNzOwEACVNpZ25hdHVyZQEAFigpTGphdmEvbGFuZy9DbGFzczwqPjsBAAho" +
+ "YXNoQ29kZQEAAygpSQEABm5vdGlmeQEACW5vdGlmeUFsbAEACHRvU3RyaW5nAQAUKClMamF2YS9s" +
+ "YW5nL1N0cmluZzsBAAR3YWl0BwAtAQAEKEopVgEABm1pbGxpcwEAAUoBAAUoSkkpVgEAClNvdXJj" +
+ "ZUZpbGUBAAtPYmplY3QuamF2YQEAGmphdmEvbGFuZy9SdW50aW1lRXhjZXB0aW9uAQAFU3R1YiEM" +
+ "AAUALgEAEGphdmEvbGFuZy9PYmplY3QBACRqYXZhL2xhbmcvQ2xvbmVOb3RTdXBwb3J0ZWRFeGNl" +
+ "cHRpb24BABNqYXZhL2xhbmcvVGhyb3dhYmxlAQAeamF2YS9sYW5nL0ludGVycnVwdGVkRXhjZXB0" +
+ "aW9uAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABAAAAAAAAAAMAAEABQAGAAEABwAAADQAAwAB" +
+ "AAAACrsAAVkSArcAA78AAAACAAgAAAAGAAEAAAAEAAkAAAAMAAEAAAAKAAoACwAAAAQADAANAAIA" +
+ "BwAAADQAAwABAAAACrsAAVkSArcAA78AAAACAAgAAAAGAAEAAAAFAAkAAAAMAAEAAAAKAAoACwAA" +
+ "AA4AAAAEAAEADwABABAAEQABAAcAAAA+AAMAAgAAAAq7AAFZEgK3AAO/AAAAAgAIAAAABgABAAAA" +
+ "BgAJAAAAFgACAAAACgAKAAsAAAAAAAoAEgALAAEABAATAAYAAgAHAAAANAADAAEAAAAKuwABWRIC" +
+ "twADvwAAAAIACAAAAAYAAQAAAAcACQAAAAwAAQAAAAoACgALAAAADgAAAAQAAQAUAREAFQAWAAEA" +
+ "FwAAAAIAGAEBABkAGgAAAREAGwAGAAABEQAcAAYAAAABAB0AHgABAAcAAAA0AAMAAQAAAAq7AAFZ" +
+ "EgK3AAO/AAAAAgAIAAAABgABAAAADAAJAAAADAABAAAACgAKAAsAAAARAB8ABgACAAcAAAA0AAMA" +
+ "AQAAAAq7AAFZEgK3AAO/AAAAAgAIAAAABgABAAAADQAJAAAADAABAAAACgAKAAsAAAAOAAAABAAB" +
+ "ACAAEQAfACEAAgAHAAAAPgADAAMAAAAKuwABWRICtwADvwAAAAIACAAAAAYAAQAAAA4ACQAAABYA" +
+ "AgAAAAoACgALAAAAAAAKACIAIwABAA4AAAAEAAEAIAERAB8AJAABAA4AAAAEAAEAIAABACUAAAAC" +
+ "ACY=";
+
+ private void assertIsJavaLangObjet(ClassSubject clazz) {
+ assertTrue(clazz.getOriginalDescriptor().equals("Ljava/lang/Object;"));
+ assertNull(clazz.getDexClass().superType);
+ }
+
+ private void checkApplicationOnlyHasJavaLangObject(AndroidApp app) throws Throwable {
+ DexInspector inspector = new DexInspector(app);
+ inspector.forAllClasses(this::assertIsJavaLangObjet);
+ }
+
+ @Test
+ public void test() throws Throwable {
+ // Build an application with the java.lang.Object stub from a class file.
+ AndroidApp output =
+ ToolHelper.runD8(
+ D8Command.builder()
+ .addClassProgramData(Base64.getDecoder().decode(javaLangObjectClassFile))
+ .build());
+ checkApplicationOnlyHasJavaLangObject(output);
+
+ // Build an application with the java.lang.Object stub from a dex file.
+ List<byte[]> dex = output.writeToMemory();
+ assertEquals(1, dex.size());
+ checkApplicationOnlyHasJavaLangObject(
+ ToolHelper.runD8(D8Command.builder().addDexProgramData(dex).build()));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
new file mode 100644
index 0000000..5fb3007
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
@@ -0,0 +1,74 @@
+// 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.rewrite.longcompare;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.InstructionSubject;
+import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Tests checking that Long.compare() is always rewritten into long compare instruction.
+ */
+public class LongCompare {
+
+ @Rule
+ public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
+
+ void compileWithD8(Path intputPath, Path outputPath)
+ throws IOException, CompilationException {
+ D8.run(D8Command.builder().addProgramFiles(intputPath).setOutputPath(outputPath).build());
+ }
+
+ void runTest(Path dexFile) {
+ ArtCommandBuilder builder = new ArtCommandBuilder(ToolHelper.getDexVm());
+ builder.appendClasspath(dexFile.toString());
+ builder.setMainClass("rewrite.LongCompare");
+ try {
+ String output = ToolHelper.runArt(builder);
+ Assert.assertEquals("-1\n1\n-1\n0\ntrue\nfalse\nfalse\nfalse\n", output);
+ } catch (IOException e) {
+ Assert.fail();
+ }
+ }
+
+ @Test
+ public void testLongCompareIsRewritten()
+ throws IOException, CompilationException, ExecutionException {
+ final Path inputPath = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/rewrite.jar");
+ Path outputPath = tmpOutputDir.newFolder().toPath();
+
+ compileWithD8(inputPath, outputPath);
+
+ Path dexPath = outputPath.resolve("classes.dex");
+
+ DexInspector dexInspector = new DexInspector(dexPath);
+ ClassSubject classSubject = dexInspector.clazz("rewrite.LongCompare");
+ MethodSubject methodSubject = classSubject
+ .method("int", "simpleCompare", Arrays.asList("long", "long"));
+ // Check that exception handler is removed since it is no longer needed.
+ Assert.assertFalse(methodSubject.getMethod().getCode().asDexCode().usesExceptionHandling());
+ Iterator<InvokeInstructionSubject> iterator =
+ methodSubject.iterateInstructions(InstructionSubject::isInvoke);
+ Assert.assertFalse(iterator.hasNext());
+
+ runTest(dexPath);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
new file mode 100644
index 0000000..4cbae16
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.rewrite.longcompare;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.InstructionSubject;
+import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class RequireNonNullRewriteTest {
+
+ @Rule
+ public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
+
+ void compileWithD8(Path intputPath, Path outputPath, CompilationMode mode)
+ throws IOException, CompilationException {
+ D8.run(
+ D8Command.builder()
+ .setMode(mode)
+ .addProgramFiles(intputPath)
+ .setOutputPath(outputPath)
+ .build());
+ }
+
+ void runTest(Path jarFile, Path dexFile) {
+ String mainClass = "rewrite.RequireNonNull";
+ ArtCommandBuilder builder = new ArtCommandBuilder(ToolHelper.getDexVm());
+ builder.appendClasspath(dexFile.toString());
+ builder.setMainClass(mainClass);
+ try {
+ String output = ToolHelper.runArt(builder);
+ ProcessResult javaResult = ToolHelper
+ .runJava(ImmutableList.of(jarFile.toString()), mainClass);
+ Assert.assertEquals(javaResult.stdout, output);
+ } catch (IOException e) {
+ Assert.fail();
+ }
+ }
+
+ @Test
+ public void testDebugRequireNonNullIsRewritten()
+ throws IOException, CompilationException, ExecutionException {
+ runTest(CompilationMode.DEBUG);
+ }
+
+ @Test
+ public void testReleaseRequireNonNullIsRewritten()
+ throws IOException, CompilationException, ExecutionException {
+ runTest(CompilationMode.RELEASE);
+ }
+
+ private void runTest(CompilationMode mode)
+ throws IOException, CompilationException, ExecutionException {
+ final Path inputPath = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/rewrite.jar");
+ Path outputPath = tmpOutputDir.newFolder().toPath();
+
+ compileWithD8(inputPath, outputPath, mode);
+
+ Path dexPath = outputPath.resolve("classes.dex");
+
+ DexInspector dexInspector = new DexInspector(dexPath);
+ ClassSubject classSubject = dexInspector.clazz("rewrite.RequireNonNull");
+ MethodSubject methodSubject = classSubject
+ .method("java.lang.Object", "nonnullRemove", Collections.emptyList());
+
+ Iterator<InvokeInstructionSubject> iterator =
+ methodSubject.iterateInstructions(InstructionSubject::isInvoke);
+ Assert.assertTrue(iterator.hasNext());
+ Assert.assertEquals("getClass", iterator.next().invokedMethod().name.toString());
+ Assert.assertFalse(iterator.hasNext());
+
+ runTest(inputPath, dexPath);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
new file mode 100644
index 0000000..2362028
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -0,0 +1,334 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexItemFactory;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class ProguardConfigurationParserTest {
+
+ private static final String VALID_PROGUARD_DIR = "src/test/proguard/valid/";
+ private static final String INVALID_PROGUARD_DIR = "src/test/proguard/invalid/";
+ private static final String PROGUARD_SPEC_FILE = VALID_PROGUARD_DIR + "proguard.flags";
+ private static final String MULTIPLE_NAME_PATTERNS_FILE =
+ VALID_PROGUARD_DIR + "multiple-name-patterns.flags";
+ private static final String ACCESS_FLAGS_FILE = VALID_PROGUARD_DIR + "access-flags.flags";
+ private static final String WHY_ARE_YOU_KEEPING_FILE =
+ VALID_PROGUARD_DIR + "why-are-you-keeping.flags";
+ private static final String ASSUME_NO_SIDE_EFFECTS =
+ VALID_PROGUARD_DIR + "assume-no-side-effects.flags";
+ private static final String ASSUME_NO_SIDE_EFFECTS_WITH_RETURN_VALUE =
+ VALID_PROGUARD_DIR + "assume-no-side-effects-with-return-value.flags";
+ private static final String ASSUME_VALUES_WITH_RETURN_VALUE =
+ VALID_PROGUARD_DIR + "assume-values-with-return-value.flags";
+ private static final String INCLUDING =
+ VALID_PROGUARD_DIR + "including.flags";
+ private static final String INVALID_INCLUDING_1 =
+ INVALID_PROGUARD_DIR + "including-1.flags";
+ private static final String INVALID_INCLUDING_2 =
+ INVALID_PROGUARD_DIR + "including-2.flags";
+ private static final String LIBRARY_JARS =
+ VALID_PROGUARD_DIR + "library-jars.flags";
+ private static final String SEEDS =
+ VALID_PROGUARD_DIR + "seeds.flags";
+ private static final String SEEDS_2 =
+ VALID_PROGUARD_DIR + "seeds-2.flags";
+ private static final String VERBOSE =
+ VALID_PROGUARD_DIR + "verbose.flags";
+ private static final String DONT_OBFUSCATE =
+ VALID_PROGUARD_DIR + "dontobfuscate.flags";
+ private static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES =
+ VALID_PROGUARD_DIR + "dontskipnonpubliclibraryclasses.flags";
+ private static final String DONT_OPTIMIZE =
+ VALID_PROGUARD_DIR + "dontoptimize.flags";
+ private static final String SKIP_NON_PUBLIC_LIBRARY_CLASSES =
+ VALID_PROGUARD_DIR + "skipnonpubliclibraryclasses.flags";
+ private static final String PARSE_AND_SKIP_SINGLE_ARGUMENT =
+ VALID_PROGUARD_DIR + "parse-and-skip-single-argument.flags";
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void parse() throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(PROGUARD_SPEC_FILE));
+ List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
+ assertEquals(24, rules.size());
+ assertEquals(1, rules.get(0).getMemberRules().size());
+ }
+
+ @Test
+ public void parseMultipleNamePatterns() throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(MULTIPLE_NAME_PATTERNS_FILE));
+ List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
+ assertEquals(1, rules.size());
+ ProguardConfigurationRule rule = rules.get(0);
+ assertEquals(1, rule.getMemberRules().size());
+ assertEquals("com.company.hello.**", rule.getClassNames().get(0).toString());
+ assertEquals("com.company.world.**", rule.getClassNames().get(1).toString());
+ assertEquals(ProguardKeepRuleType.KEEP, ((ProguardKeepRule) rule).getType());
+ assertTrue(rule.getInheritanceIsExtends());
+ assertEquals("some.library.Class", rule.getInheritanceClassName().toString());
+ ProguardMemberRule memberRule = rule.getMemberRules().iterator().next();
+ assertTrue(memberRule.getAccessFlags().isProtected());
+ assertEquals(ProguardNameMatcher.create("getContents"), memberRule.getName());
+ assertEquals("java.lang.Object[][]", memberRule.getType().toString());
+ assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
+ assertEquals(0, memberRule.getArguments().size());
+ }
+
+ @Test
+ public void parseAccessFlags() throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(ACCESS_FLAGS_FILE));
+ List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
+ assertEquals(1, rules.size());
+ ProguardConfigurationRule rule = rules.get(0);
+ DexAccessFlags publicAndFinalFlags = new DexAccessFlags(0);
+ publicAndFinalFlags.setPublic();
+ publicAndFinalFlags.setFinal();
+ assertTrue(rule.getClassAccessFlags().containsNoneOf(publicAndFinalFlags));
+ assertTrue(rule.getNegatedClassAccessFlags().containsAllOf(publicAndFinalFlags));
+ DexAccessFlags abstractFlags = new DexAccessFlags(0);
+ abstractFlags.setAbstract();
+ assertTrue(rule.getClassAccessFlags().containsAllOf(abstractFlags));
+ assertTrue(rule.getNegatedClassAccessFlags().containsNoneOf(abstractFlags));
+ for (ProguardMemberRule member : rule.getMemberRules()) {
+ if (member.getRuleType() == ProguardMemberType.ALL_FIELDS) {
+ DexAccessFlags publicFlags = new DexAccessFlags(0);
+ publicAndFinalFlags.setPublic();
+ assertTrue(member.getAccessFlags().containsAllOf(publicFlags));
+ assertTrue(member.getNegatedAccessFlags().containsNoneOf(publicFlags));
+ DexAccessFlags staticFlags = new DexAccessFlags(0);
+ staticFlags.setStatic();
+ assertTrue(member.getAccessFlags().containsNoneOf(staticFlags));
+ assertTrue(member.getNegatedAccessFlags().containsAllOf(staticFlags));
+ } else {
+ assertTrue(member.getRuleType() == ProguardMemberType.ALL_METHODS);
+ DexAccessFlags publicProtectedVolatileFlags = new DexAccessFlags(0);
+ publicProtectedVolatileFlags.setPublic();
+ publicProtectedVolatileFlags.setProtected();
+ publicProtectedVolatileFlags.setVolatile();
+ assertTrue(member.getAccessFlags().containsNoneOf(publicProtectedVolatileFlags));
+ assertTrue(member.getNegatedAccessFlags().containsAllOf(publicProtectedVolatileFlags));
+ }
+ }
+ }
+
+ @Test
+ public void parseWhyAreYouKeeping() throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(WHY_ARE_YOU_KEEPING_FILE));
+ List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
+ assertEquals(1, rules.size());
+ ProguardConfigurationRule rule = rules.get(0);
+ assertEquals(1, rule.getClassNames().size());
+ assertEquals("*", rule.getClassNames().get(0).toString());
+ assertTrue(rule.getInheritanceIsExtends());
+ assertEquals("foo.bar", rule.getInheritanceClassName().toString());
+ }
+
+ @Test
+ public void parseAssumeNoSideEffects() throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(ASSUME_NO_SIDE_EFFECTS));
+ List<ProguardConfigurationRule> assumeNoSideEffects = parser.getConfig().getRules();
+ assertEquals(1, assumeNoSideEffects.size());
+ assumeNoSideEffects.get(0).getMemberRules().forEach(rule -> {
+ assertFalse(rule.hasReturnValue());
+ });
+ }
+
+ @Test
+ public void parseAssumeNoSideEffectsWithReturnValue()
+ throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(ASSUME_NO_SIDE_EFFECTS_WITH_RETURN_VALUE));
+ List<ProguardConfigurationRule> assumeNoSideEffects = parser.getConfig().getRules();
+ assertEquals(1, assumeNoSideEffects.size());
+ assumeNoSideEffects.get(0).getMemberRules().forEach(rule -> {
+ assertTrue(rule.hasReturnValue());
+ if (rule.getName().matches("returnsTrue") || rule.getName().matches("returnsFalse")) {
+ assertTrue(rule.getReturnValue().isBoolean());
+ assertFalse(rule.getReturnValue().isValueRange());
+ assertFalse(rule.getReturnValue().isField());
+ assertEquals(rule.getName().matches("returnsTrue"), rule.getReturnValue().getBoolean());
+ } else if (rule.getName().matches("returns1")) {
+ assertFalse(rule.getReturnValue().isBoolean());
+ assertTrue(rule.getReturnValue().isValueRange());
+ assertFalse(rule.getReturnValue().isField());
+ assertTrue(rule.getReturnValue().isSingleValue());
+ assertEquals(1, rule.getReturnValue().getValueRange().getMin());
+ assertEquals(1, rule.getReturnValue().getValueRange().getMax());
+ assertEquals(1, rule.getReturnValue().getSingleValue());
+ } else if (rule.getName().matches("returns2To4")) {
+ assertFalse(rule.getReturnValue().isBoolean());
+ assertTrue(rule.getReturnValue().isValueRange());
+ assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isSingleValue());
+ assertEquals(2, rule.getReturnValue().getValueRange().getMin());
+ assertEquals(4, rule.getReturnValue().getValueRange().getMax());
+ } else if (rule.getName().matches("returnsField")) {
+ assertFalse(rule.getReturnValue().isBoolean());
+ assertFalse(rule.getReturnValue().isValueRange());
+ assertTrue(rule.getReturnValue().isField());
+ assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString());
+ assertEquals("int", rule.getReturnValue().getField().type.toString());
+ assertEquals("X", rule.getReturnValue().getField().name.toString());
+ }
+ });
+ }
+
+ @Test
+ public void parseAssumeValuesWithReturnValue()
+ throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(ASSUME_VALUES_WITH_RETURN_VALUE));
+ List<ProguardConfigurationRule> assumeValues = parser.getConfig().getRules();
+ assertEquals(1, assumeValues.size());
+ assumeValues.get(0).getMemberRules().forEach(rule -> {
+ assertTrue(rule.hasReturnValue());
+ if (rule.getName().matches("isTrue") || rule.getName().matches("isFalse")) {
+ assertTrue(rule.getReturnValue().isBoolean());
+ assertFalse(rule.getReturnValue().isValueRange());
+ assertFalse(rule.getReturnValue().isField());
+ assertEquals(rule.getName().matches("isTrue"), rule.getReturnValue().getBoolean());
+ } else if (rule.getName().matches("is1")) {
+ assertFalse(rule.getReturnValue().isBoolean());
+ assertTrue(rule.getReturnValue().isValueRange());
+ assertFalse(rule.getReturnValue().isField());
+ assertTrue(rule.getReturnValue().isSingleValue());
+ assertEquals(1, rule.getReturnValue().getValueRange().getMin());
+ assertEquals(1, rule.getReturnValue().getValueRange().getMax());
+ assertEquals(1, rule.getReturnValue().getSingleValue());
+ } else if (rule.getName().matches("is2To4")) {
+ assertFalse(rule.getReturnValue().isBoolean());
+ assertTrue(rule.getReturnValue().isValueRange());
+ assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isSingleValue());
+ assertEquals(2, rule.getReturnValue().getValueRange().getMin());
+ assertEquals(4, rule.getReturnValue().getValueRange().getMax());
+ } else if (rule.getName().matches("isField")) {
+ assertFalse(rule.getReturnValue().isBoolean());
+ assertFalse(rule.getReturnValue().isValueRange());
+ assertTrue(rule.getReturnValue().isField());
+ assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString());
+ assertEquals("int", rule.getReturnValue().getField().type.toString());
+ assertEquals("X", rule.getReturnValue().getField().name.toString());
+ }
+ });
+ }
+
+ @Test
+ public void parseDontobfuscate() throws IOException, ProguardRuleParserException {
+ new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(DONT_OBFUSCATE));
+ }
+
+ @Test
+ public void parseIncluding() throws IOException, ProguardRuleParserException {
+ new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(INCLUDING));
+ }
+
+ @Test
+ public void parseInvalidIncluding1() throws IOException {
+ try {
+ new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(INVALID_INCLUDING_1));
+ fail();
+ } catch (ProguardRuleParserException e) {
+ assertTrue(e.getMessage().contains("6")); // line
+ assertTrue(e.getMessage().contains("including-1.flags")); // file in error
+ assertTrue(e.getMessage().contains("does-not-exist.flags")); // missing file
+ }
+ }
+
+ @Test
+ public void parseInvalidIncluding2() throws IOException {
+ try {
+ new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(INVALID_INCLUDING_2));
+ fail();
+ } catch (ProguardRuleParserException e) {
+ String message = e.getMessage();
+ assertTrue(message, message.contains("6")); // line
+ assertTrue(message, message.contains("including-2.flags")); // file in error
+ assertTrue(message, message.contains("does-not-exist.flags")); // missing file
+ }
+ }
+
+ @Test
+ public void parseLibraryJars() throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(LIBRARY_JARS));
+ assertEquals(4, parser.getConfig().getLibraryjars().size());
+ }
+
+ @Test
+ public void parseSeeds() throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(SEEDS));
+ ProguardConfiguration config = parser.getConfig();
+ assertTrue(config.getPrintSeeds());
+ assertNull(config.getSeedFile());
+ }
+
+ @Test
+ public void parseSeeds2() throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(SEEDS_2));
+ ProguardConfiguration config = parser.getConfig();
+ assertTrue(config.getPrintSeeds());
+ assertNotNull(config.getSeedFile());
+ }
+
+ @Test
+ public void parseVerbose() throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(VERBOSE));
+ ProguardConfiguration config = parser.getConfig();
+ assertTrue(config.isVerbose());
+ }
+
+ @Test
+ public void parseDontSkipNonPublicLibraryClasses()
+ throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES));
+ }
+
+ @Test
+ public void parseDontOptimize()
+ throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(DONT_OPTIMIZE));
+ }
+
+ @Test
+ public void parseSkipNonPublicLibraryClasses()
+ throws IOException, ProguardRuleParserException {
+ thrown.expect(ProguardRuleParserException.class);
+ thrown.expectMessage("Unsupported option: -skipnonpubliclibraryclasses");
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(SKIP_NON_PUBLIC_LIBRARY_CLASSES));
+ }
+
+ @Test
+ public void parseAndskipSingleArgument() throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(PARSE_AND_SKIP_SINGLE_ARGUMENT));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
new file mode 100644
index 0000000..c38c379
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
+import com.android.tools.r8.utils.DescriptorUtils;
+import org.junit.Test;
+
+public class ProguardNameMatchingTest {
+ private static final String[] BASIC_TYPES = {
+ "char", "byte", "short", "int", "long", "float", "double", "boolean"
+ };
+
+ private static final DexItemFactory dexItemFactory = new DexItemFactory();
+
+ private static boolean matchTypeName(String pattern, String typeName,
+ DexItemFactory dexItemFactory) {
+ return ProguardTypeMatcher.create(pattern, ClassOrType.TYPE, dexItemFactory)
+ .matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(typeName)));
+ }
+
+ private static boolean matchClassName(String pattern, String className,
+ DexItemFactory dexItemFactory) {
+ return ProguardTypeMatcher.create(pattern, ClassOrType.CLASS, dexItemFactory)
+ .matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(className)));
+ }
+
+ @Test
+ public void matchClassNames() {
+ assertTrue(matchClassName("**", "", dexItemFactory));
+ assertTrue(matchClassName("**", "a", dexItemFactory));
+ assertTrue(matchClassName("*", "", dexItemFactory));
+ assertTrue(matchClassName("*", "a", dexItemFactory));
+ assertFalse(matchClassName("?", "", dexItemFactory));
+ assertTrue(matchClassName("?", "a", dexItemFactory));
+
+ assertTrue(matchClassName("**", "java.lang.Object", dexItemFactory));
+ assertFalse(
+ matchClassName("ja*", "java.lang.Object", dexItemFactory));
+ assertTrue(
+ matchClassName("ja**", "java.lang.Object", dexItemFactory));
+ assertTrue(matchClassName("ja**ject", "java.lang.Object",
+ dexItemFactory));
+ // Oddly, the proguard specification for this makes a lonely * synonymous with **.
+ assertTrue(matchClassName("*", "java.lang.Object", dexItemFactory));
+ assertFalse(matchClassName("ja*ject", "java.lang.Object",
+ dexItemFactory));
+
+ assertTrue(matchClassName("java.*g.O*", "java.lang.Object",
+ dexItemFactory));
+ assertTrue(matchClassName("java.*g.O?je?t", "java.lang.Object",
+ dexItemFactory));
+ assertFalse(matchClassName("java.*g.O?je?t?", "java.lang.Object",
+ dexItemFactory));
+ assertFalse(matchClassName("java?lang.Object", "java.lang.Object",
+ dexItemFactory));
+ assertTrue(matchClassName("*a*.*a**", "java.lang.Object",
+ dexItemFactory));
+ assertTrue(matchClassName("*a**a**", "java.lang.Object",
+ dexItemFactory));
+ }
+
+ private void assertMatchesBasicTypes(String pattern) {
+ for (String type : BASIC_TYPES) {
+ assertTrue(matchTypeName(pattern, type, dexItemFactory));
+ }
+ }
+
+ private void assertDoesNotMatchBasicTypes(String pattern) {
+ for (String type : BASIC_TYPES) {
+ assertFalse(matchTypeName(pattern, type, dexItemFactory));
+ }
+ }
+
+ @Test
+ public void matchTypeNames() {
+ assertTrue(matchTypeName("**", "java.lang.Object", dexItemFactory));
+ assertDoesNotMatchBasicTypes("**");
+ assertDoesNotMatchBasicTypes("*");
+ assertFalse(
+ matchTypeName("**", "java.lang.Object[]", dexItemFactory));
+ assertFalse(
+ matchTypeName("**z", "java.lang.Object", dexItemFactory));
+ assertFalse(matchTypeName("java.**", "java.lang.Object[]",
+ dexItemFactory));
+ assertTrue(
+ matchTypeName("***", "java.lang.Object[]", dexItemFactory));
+ assertTrue(matchTypeName("***", "java.lang.Object[][]",
+ dexItemFactory));
+ assertFalse(matchTypeName("%", "java.lang.Object", dexItemFactory));
+ assertTrue(matchTypeName("**", "a", dexItemFactory));
+ assertTrue(matchTypeName("**[]", "java.lang.Object[]",
+ dexItemFactory));
+ assertFalse(matchTypeName("**[]", "java.lang.Object[][]",
+ dexItemFactory));
+ assertTrue(matchTypeName("*", "abc", dexItemFactory));
+ assertMatchesBasicTypes("***");
+ assertMatchesBasicTypes("%");
+ }
+
+ @Test
+ public void matchFieldOrMethodNames() {
+ assertTrue(ProguardNameMatcher.matchFieldOrMethodName("*", ""));
+ assertTrue(ProguardNameMatcher.matchFieldOrMethodName("*", "get"));
+ assertTrue(ProguardNameMatcher.matchFieldOrMethodName("get*", "get"));
+ assertTrue(ProguardNameMatcher.matchFieldOrMethodName("get*", "getObject"));
+ assertTrue(ProguardNameMatcher.matchFieldOrMethodName("*t", "get"));
+ assertTrue(ProguardNameMatcher.matchFieldOrMethodName("g*t*", "getObject"));
+ assertTrue(
+ ProguardNameMatcher.matchFieldOrMethodName("g*t***************", "getObject"));
+ assertFalse(ProguardNameMatcher.matchFieldOrMethodName("get*y", "getObject"));
+ assertFalse(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject"));
+ assertTrue(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject1"));
+ assertTrue(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject5"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/R8Shaking2LookupTest.java b/src/test/java/com/android/tools/r8/shaking/R8Shaking2LookupTest.java
new file mode 100644
index 0000000..be28b38
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/R8Shaking2LookupTest.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+
+public class R8Shaking2LookupTest {
+
+ static final String APP_FILE_NAME = ToolHelper.EXAMPLES_BUILD_DIR + "shaking2/classes.dex";
+ private DexApplication program;
+ private DexItemFactory dexItemFactory;
+ private AppInfoWithSubtyping appInfo;
+
+ @Before
+ public void readApp() throws IOException, ExecutionException {
+ program = ToolHelper.buildApplication(ImmutableList.of(APP_FILE_NAME));
+ dexItemFactory = program.dexItemFactory;
+ appInfo = new AppInfoWithSubtyping(program);
+ }
+
+ private void validateSubtype(DexType super_type, DexType sub_type) {
+ assertFalse(super_type.equals(sub_type));
+ assertTrue(appInfo.subtypes(super_type).contains(sub_type));
+ assertTrue(sub_type.isSubtypeOf(super_type, appInfo));
+ assertFalse(appInfo.subtypes(sub_type).contains(super_type));
+ assertFalse(super_type.isSubtypeOf(sub_type, appInfo));
+ }
+
+ private void validateSubtypeSize(DexType type, int size) {
+ assertEquals(size, appInfo.subtypes(type).size());
+ }
+
+ @Test
+ public void testLookup() {
+ DexType object_type = dexItemFactory.createType("Ljava/lang/Object;");
+
+ DexType interface_type = dexItemFactory.createType("Lshaking2/Interface;");
+ DexType superInterface1_type = dexItemFactory.createType("Lshaking2/SuperInterface1;");
+ DexType superInterface2_type = dexItemFactory.createType("Lshaking2/SuperInterface2;");
+
+ DexType superclass_type = dexItemFactory.createType("Lshaking2/SuperClass;");
+ DexType subClass1_type = dexItemFactory.createType("Lshaking2/SubClass1;");
+ DexType subClass2_type = dexItemFactory.createType("Lshaking2/SubClass2;");
+
+ validateSubtype(object_type, interface_type);
+ validateSubtypeSize(interface_type, 4);
+ validateSubtype(interface_type, superclass_type);
+ validateSubtype(superInterface1_type, subClass1_type);
+ validateSubtype(superInterface2_type, subClass2_type);
+ validateSubtype(superclass_type, subClass2_type);
+ validateSubtype(superInterface1_type, interface_type);
+ validateSubtype(superInterface2_type, interface_type);
+ validateSubtypeSize(subClass2_type, 0);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
new file mode 100644
index 0000000..f5b2b1a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR;
+import static com.android.tools.r8.ToolHelper.EXAMPLES_DIR;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.CompilationError;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public class TreeShakingSpecificTest {
+ private static final String VALID_PROGUARD_DIR = "src/test/proguard/valid/";
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void testIgnoreWarnings()
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+ // Generate R8 processed version without library option.
+ Path out = temp.getRoot().toPath();
+ String test = "shaking2";
+ Path originalDex = Paths.get(EXAMPLES_BUILD_DIR, test, "classes.dex");
+ Path keepRules = Paths.get(EXAMPLES_DIR, test, "keep-rules.txt");
+ Path ignoreWarnings = Paths.get(VALID_PROGUARD_DIR, "ignorewarnings.flags");
+ R8.run(
+ R8Command.builder()
+ .addProgramFiles(originalDex)
+ .setOutputPath(out)
+ .addProguardConfigurationFiles(keepRules, ignoreWarnings)
+ .build());
+ }
+
+ @Test
+ public void testMissingLibrary()
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+ // Generate R8 processed version without library option.
+ Path out = temp.getRoot().toPath();
+ String test = "shaking2";
+ Path originalDex = Paths.get(EXAMPLES_BUILD_DIR, test, "classes.dex");
+ Path keepRules = Paths.get(EXAMPLES_DIR, test, "keep-rules.txt");
+ thrown.expect(CompilationError.class);
+ thrown.expectMessage("Shrinking can't be performed because some library classes are missing");
+ R8.run(
+ R8Command.builder()
+ .addProgramFiles(originalDex)
+ .setOutputPath(out)
+ .addProguardConfigurationFiles(keepRules)
+ .build());
+ }
+
+ @Test
+ public void testPrintMapping()
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+ // Generate R8 processed version without library option.
+ String test = "shaking1";
+ Path out = temp.getRoot().toPath();
+ Path originalDex = Paths.get(EXAMPLES_BUILD_DIR, test, "classes.dex");
+ Path keepRules = Paths.get(EXAMPLES_DIR, test, "keep-rules.txt");
+
+ // Create a flags file in temp dir requesting dump of the mapping.
+ // The mapping file will be created alongside the flags file in temp dir.
+ Path printMapping = out.resolve("printmapping.flags");
+ try (PrintStream mapping = new PrintStream(printMapping.toFile())) {
+ mapping.println("-printmapping mapping.txt");
+ }
+
+ ToolHelper.runR8(
+ R8Command.builder()
+ .addProgramFiles(originalDex)
+ .setOutputPath(out)
+ .addProguardConfigurationFiles(keepRules, printMapping)
+ .build());
+ Path outputmapping = out.resolve("mapping.txt");
+ String actualMapping;
+ actualMapping = new String(Files.readAllBytes(outputmapping), StandardCharsets.UTF_8);
+ String refMapping = new String(Files.readAllBytes(
+ Paths.get(EXAMPLES_DIR, "shaking1", "print-mapping.ref")), StandardCharsets.UTF_8);
+ Assert.assertEquals(refMapping, actualMapping);
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
new file mode 100644
index 0000000..6e03c0e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -0,0 +1,783 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.FieldAccessInstructionSubject;
+import com.android.tools.r8.utils.DexInspector.FieldSubject;
+import com.android.tools.r8.utils.DexInspector.FoundFieldSubject;
+import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
+import com.android.tools.r8.utils.DexInspector.InstructionSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.ListUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TreeShakingTest {
+
+ private static final String ANDROID_JAR = ToolHelper.getDefaultAndroidJar();
+ private static final List<String> JAR_LIBRARIES = ImmutableList
+ .of(ANDROID_JAR, ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib.jar");
+ private static final List<String> DEX_LIBRARIES = ImmutableList
+ .of(ANDROID_JAR, ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib/classes.dex");
+ private static final String EMPTY_FLAGS = "src/test/proguard/valid/empty.flags";
+ private static Set<String> IGNORED = ImmutableSet.of(
+ // there's no point in running those without obfuscation
+ "shaking1:keep-rules-repackaging.txt:DEX:false",
+ "shaking1:keep-rules-repackaging.txt:JAR:false",
+ "shaking16:keep-rules-1.txt:DEX:false",
+ "shaking16:keep-rules-1.txt:JAR:false",
+ "shaking16:keep-rules-2.txt:DEX:false",
+ "shaking16:keep-rules-2.txt:JAR:false",
+ "shaking15:keep-rules.txt:DEX:false",
+ "shaking15:keep-rules.txt:JAR:false"
+ );
+ private final boolean minify;
+
+
+ private enum Frontend {
+ DEX, JAR
+ }
+
+ private final Frontend kind;
+ private final String originalDex;
+ private final String programFile;
+ private final String mainClass;
+ private final List<String> keepRulesFiles;
+ private final Consumer<DexInspector> inspection;
+ private final BiConsumer<String, String> outputComparator;
+ private BiConsumer<DexInspector, DexInspector> dexComparator;
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ public TreeShakingTest(String test, Frontend kind, String mainClass, List<String> keepRulesFiles,
+ boolean minify, Consumer<DexInspector> inspection,
+ BiConsumer<String, String> outputComparator,
+ BiConsumer<DexInspector, DexInspector> dexComparator) {
+ this.kind = kind;
+ originalDex = ToolHelper.EXAMPLES_BUILD_DIR + test + "/classes.dex";
+ if (kind == Frontend.DEX) {
+ this.programFile = originalDex;
+ } else {
+ this.programFile = ToolHelper.EXAMPLES_BUILD_DIR + test + ".jar";
+ }
+ this.mainClass = mainClass;
+ this.keepRulesFiles = keepRulesFiles;
+ this.inspection = inspection;
+ this.minify = minify;
+ this.outputComparator = outputComparator;
+ this.dexComparator = dexComparator;
+ }
+
+ @Before
+ public void generateTreeShakedVersion()
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+ // Generate R8 processed version without library option.
+ Path out = temp.getRoot().toPath();
+ List<String> libs = kind == Frontend.DEX ? DEX_LIBRARIES : JAR_LIBRARIES;
+ boolean inline = programFile.contains("inlining");
+
+ R8Command command =
+ R8Command.builder()
+ .setOutputPath(out)
+ .addProgramFiles(Paths.get(programFile))
+ .addProguardConfigurationFiles(ListUtils.map(keepRulesFiles, Paths::get))
+ .addLibraryFiles(ListUtils.map(libs, Paths::get))
+ .setMinification(minify)
+ .build();
+ ToolHelper.runR8(command, options -> options.inlineAccessors = inline);
+ }
+
+ public static void shaking1HasNoClassUnused(DexInspector inspector) {
+ Assert.assertFalse(inspector.clazz("shaking1.Unused").isPresent());
+ ClassSubject used = inspector.clazz("shaking1.Used");
+ Assert.assertTrue(used.isPresent());
+ Assert.assertTrue(
+ used.method("java.lang.String", "aMethodThatIsNotUsedButKept", Collections.emptyList())
+ .isPresent());
+ Assert.assertTrue(used.field("int", "aStaticFieldThatIsNotUsedButKept").isPresent());
+ }
+
+ public static void shaking1IsCorrectlyRepackaged(DexInspector inspector) {
+ inspector.forAllClasses(clazz -> {
+ String descriptor = clazz.getFinalDescriptor();
+ Assert.assertTrue(descriptor,
+ DescriptorUtils.getSimpleClassNameFromDescriptor(descriptor).equals("Shaking")
+ || DescriptorUtils.getPackageNameFromDescriptor(descriptor).equals("repackaged"));
+ });
+ }
+
+ private static void shaking2SuperClassIsAbstract(DexInspector inspector) {
+ ClassSubject clazz = inspector.clazz("shaking2.SuperClass");
+ Assert.assertTrue(clazz.isAbstract());
+ Assert.assertTrue(clazz.method("void", "virtualMethod", Collections.emptyList()).isAbstract());
+ Assert.assertTrue(clazz.method("void", "virtualMethod2", ImmutableList
+ .of("int", "int", "int", "int", "int", "int", "int", "int")).isAbstract());
+ }
+
+ public static void shaking3HasNoClassB(DexInspector inspector) {
+ Assert.assertFalse(inspector.clazz("shaking3.B").isPresent());
+ ClassSubject classA = inspector.clazz("shaking3.A");
+ Assert.assertTrue(classA.isPresent());
+ Assert.assertFalse(classA.method("void", "unused", ImmutableList.of()).isPresent());
+ }
+
+ public static void shaking3HasNoPrivateClass(DexInspector inspector) {
+ Assert.assertTrue(inspector.clazz("shaking3.B").isPresent());
+ Assert.assertFalse(inspector.clazz("shaking3.AnAbstractClass").isPresent());
+ }
+
+ private static void shaking5Inspection(DexInspector inspector) {
+ Assert.assertFalse(inspector.clazz("shaking5.Superclass")
+ .method("void", "virtualMethod", Collections.emptyList()).isPresent());
+ }
+
+ private static void hasNoPrivateMethods(DexInspector inspector) {
+ inspector.forAllClasses(clazz -> clazz.forAllMethods(
+ method -> Assert.assertTrue(method.hasNone(new DexAccessFlags(Constants.ACC_PRIVATE)))
+ ));
+ }
+
+ private static void hasNoPublicMethodsButPrivate(DexInspector inspector) {
+ inspector.forAllClasses(clazz -> clazz.forAllMethods(method -> {
+ if (!method.isStatic() && !method.isFinal()) {
+ Assert.assertTrue(method.hasNone(new DexAccessFlags(Constants.ACC_PUBLIC)));
+ }
+ }));
+ Assert.assertTrue(inspector.clazz("shaking6.Superclass")
+ .method("void", "justAMethod", Collections.emptyList()).isPresent());
+ }
+
+ private static void hasNoPrivateJustAMethod(DexInspector inspector) {
+ Assert.assertFalse(
+ inspector.clazz("shaking6.Superclass")
+ .method("void", "justAMethod", Collections.emptyList())
+ .isPresent());
+ ClassSubject subclass = inspector.clazz("shaking6.Subclass");
+ Assert.assertTrue(subclass.isPresent());
+ Assert.assertTrue(
+ subclass.method("void", "justAMethod", Collections.emptyList())
+ .isPresent());
+ Assert.assertTrue(
+ subclass.method("void", "justAMethod", Collections.singletonList("int"))
+ .isPresent());
+ Assert.assertTrue(
+ subclass.method("void", "justAMethod", Collections.singletonList("boolean"))
+ .isPresent());
+ Assert.assertFalse(
+ subclass.method("int", "justAMethod", Collections.singletonList("double"))
+ .isPresent());
+ }
+
+ private static void hasOnlyIntJustAMethod(DexInspector inspector) {
+ Assert.assertFalse(
+ inspector.clazz("shaking6.Superclass")
+ .method("void", "justAMethod", Collections.emptyList())
+ .isPresent());
+ ClassSubject subclass = inspector.clazz("shaking6.Subclass");
+ Assert.assertTrue(subclass.isPresent());
+ Assert.assertFalse(
+ subclass.method("void", "justAMethod", Collections.emptyList())
+ .isPresent());
+ Assert.assertTrue(
+ subclass.method("void", "justAMethod", Collections.singletonList("int"))
+ .isPresent());
+ Assert.assertFalse(
+ subclass.method("void", "justAMethod", Collections.singletonList("boolean"))
+ .isPresent());
+ Assert.assertFalse(
+ subclass.method("int", "justAMethod", Collections.singletonList("double"))
+ .isPresent());
+ }
+
+ private static void shaking7HasOnlyPublicFields(DexInspector inspector) {
+ inspector.forAllClasses(clazz -> {
+ clazz.forAllFields(field -> {
+ Assert.assertTrue(field.hasAll(new DexAccessFlags(Constants.ACC_PUBLIC)));
+ });
+ });
+ ClassSubject subclass = inspector.clazz("shaking7.Subclass");
+ Assert.assertTrue(subclass.field("int", "theIntField").isPresent());
+ Assert.assertTrue(subclass.field("double", "theDoubleField").isPresent());
+ Assert.assertTrue(inspector.clazz("shaking7.Superclass")
+ .field("double", "theDoubleField").isPresent());
+ Assert.assertTrue(inspector.clazz("shaking7.Liar").field("int", "theDoubleField").isPresent());
+ }
+
+ private static void shaking7HasOnlyDoubleFields(DexInspector inspector) {
+ inspector.forAllClasses(clazz -> {
+ clazz.forAllFields(field -> {
+ Assert.assertTrue(field.type().is("double"));
+ });
+ });
+ Assert.assertTrue(
+ inspector.clazz("shaking7.Subclass").field("double", "theDoubleField").isPresent());
+ Assert.assertTrue(
+ inspector.clazz("shaking7.Superclass").field("double", "theDoubleField").isPresent());
+ Assert.assertFalse(
+ inspector.clazz("shaking7.Liar").field("int", "theDoubleField").isPresent());
+ }
+
+ private static void shaking7HasOnlyPublicFieldsNamedTheDoubleField(DexInspector inspector) {
+ inspector.forAllClasses(clazz -> {
+ clazz.forAllFields(field -> {
+ Assert.assertTrue(field.hasAll(new DexAccessFlags(Constants.ACC_PUBLIC)));
+ });
+ });
+ ClassSubject subclass = inspector.clazz("shaking7.Subclass");
+ Assert.assertFalse(subclass.field("int", "theIntField").isPresent());
+ Assert.assertTrue(subclass.field("double", "theDoubleField").isPresent());
+ Assert.assertTrue(inspector.clazz("shaking7.Superclass")
+ .field("double", "theDoubleField").isPresent());
+ Assert.assertTrue(inspector.clazz("shaking7.Liar").field("int", "theDoubleField").isPresent());
+ }
+
+ private static void shaking7HasOnlyPublicFieldsNamedTheIntField(DexInspector inspector) {
+ inspector.forAllClasses(clazz -> {
+ clazz.forAllFields(field -> {
+ Assert.assertTrue(field.hasAll(new DexAccessFlags(Constants.ACC_PUBLIC)));
+ });
+ });
+ ClassSubject subclass = inspector.clazz("shaking7.Subclass");
+ Assert.assertTrue(subclass.field("int", "theIntField").isPresent());
+ Assert.assertFalse(subclass.field("double", "theDoubleField").isPresent());
+ Assert.assertFalse(inspector.clazz("shaking7.Superclass")
+ .field("double", "theDoubleField").isPresent());
+ ClassSubject liar = inspector.clazz("shaking7.Liar");
+ Assert.assertFalse(liar.field("int", "theDoubleField").isPresent());
+ Assert.assertTrue(liar.field("double", "theIntField").isPresent());
+ }
+
+ private static void shaking8ThingClassIsAbstractAndEmpty(DexInspector inspector) {
+ ClassSubject clazz = inspector.clazz("shaking8.Thing");
+ Assert.assertTrue(clazz.isAbstract());
+ clazz.forAllMethods((method) -> Assert.fail());
+ clazz = inspector.clazz("shaking8.YetAnotherThing");
+ Assert.assertTrue(clazz.isAbstract());
+ clazz.forAllMethods((method) -> Assert.fail());
+ }
+
+ private static void shaking9OnlySuperMethodsKept(DexInspector inspector) {
+ ClassSubject superclass = inspector.clazz("shaking9.Superclass");
+ Assert.assertTrue(superclass.isAbstract());
+ Assert.assertTrue(superclass.method("void", "aMethod", ImmutableList.of()).isPresent());
+ ClassSubject subclass = inspector.clazz("shaking9.Subclass");
+ Assert.assertFalse(subclass.method("void", "aMethod", ImmutableList.of()).isPresent());
+ }
+
+ private static void shaking11OnlyOneClassKept(DexInspector dexInspector) {
+ Assert.assertFalse(dexInspector.clazz("shaking11.Subclass").isPresent());
+ Assert.assertTrue(dexInspector.clazz("shaking11.SubclassWithMethod").isPresent());
+ }
+
+ private static void shaking11BothMethodsKept(DexInspector dexInspector) {
+ Assert.assertFalse(
+ dexInspector.clazz("shaking11.Subclass").method("void", "aMethod", Collections.emptyList())
+ .isPresent());
+ Assert.assertTrue(
+ dexInspector.clazz("shaking11.SuperClass")
+ .method("void", "aMethod", Collections.emptyList())
+ .isPresent());
+ Assert.assertTrue(dexInspector.clazz("shaking11.SubclassWithMethod")
+ .method("void", "aMethod", Collections.emptyList()).isPresent());
+ }
+
+ private static void shaking12OnlyInstantiatedClassesHaveConstructors(DexInspector inspector) {
+ ClassSubject animalClass = inspector.clazz("shaking12.AnimalClass");
+ Assert.assertTrue(animalClass.isPresent());
+ Assert.assertFalse(
+ animalClass.method("void", "<init>", Collections.emptyList()).isPresent());
+ Assert.assertTrue(inspector.clazz("shaking12.MetaphorClass").isAbstract());
+ ClassSubject peopleClass = inspector.clazz("shaking12.PeopleClass");
+ Assert.assertTrue((peopleClass.isPresent() && !peopleClass.isAbstract()));
+ Assert.assertTrue(
+ peopleClass.method("void", "<init>", Collections.emptyList()).isPresent());
+ ClassSubject thingClass = inspector.clazz("shaking12.ThingClass");
+ Assert.assertTrue((thingClass.isPresent() && !thingClass.isAbstract()));
+ Assert.assertTrue(
+ thingClass.method("void", "<init>", Collections.emptyList()).isPresent());
+ }
+
+ private static void shaking13EnsureFieldWritesCorrect(DexInspector inspector) {
+ ClassSubject mainClass = inspector.clazz("shaking13.Shaking");
+ MethodSubject testMethod = mainClass.method("void", "fieldTest", Collections.emptyList());
+ Assert.assertTrue(testMethod.isPresent());
+ Iterator<FieldAccessInstructionSubject> iterator =
+ testMethod.iterateInstructions(InstructionSubject::isFieldAccess);
+ Assert.assertTrue(iterator.hasNext() && iterator.next().holder().is("shakinglib.LibraryClass"));
+ Assert.assertTrue(iterator.hasNext() && iterator.next().holder().is("shakinglib.LibraryClass"));
+ Assert.assertFalse(iterator.hasNext());
+ }
+
+ private static void shaking14EnsureRightStaticMethodsLive(DexInspector inspector) {
+ ClassSubject superclass = inspector.clazz("shaking14.Superclass");
+ Assert.assertFalse(superclass.method("int", "aMethod", ImmutableList.of("int")).isPresent());
+ Assert.assertFalse(
+ superclass.method("double", "anotherMethod", ImmutableList.of("double")).isPresent());
+ ClassSubject subclass = inspector.clazz("shaking14.Subclass");
+ Assert.assertTrue(subclass.method("int", "aMethod", ImmutableList.of("int")).isPresent());
+ Assert.assertTrue(
+ subclass.method("double", "anotherMethod", ImmutableList.of("double")).isPresent());
+ }
+
+ private static List<String> names =
+ ImmutableList.of("pqr", "vw$", "abc", "def", "stu", "ghi", "jkl", "ea", "xyz_", "mno");
+
+ private static void checkFieldInDictionary(FieldSubject field) {
+ if (!names.contains(field.getField().field.name.toSourceString())) {
+ throw new AssertionError();
+ }
+ }
+
+ private static void checkMethodInDictionary(MethodSubject method) {
+ String name = method.getMethod().method.name.toSourceString();
+ if (!names.contains(name) && !name.equals("<init>") && !name.equals("main")) {
+ throw new AssertionError();
+ }
+ }
+
+ private static void checkClassAndMemberInDictionary(ClassSubject clazz) {
+ String name = clazz.getDexClass().type.getName();
+ if (!names.contains(name) && !name.equals("Shaking")) {
+ throw new AssertionError();
+ }
+
+ clazz.forAllMethods(method -> checkMethodInDictionary(method));
+ clazz.forAllFields(field -> checkFieldInDictionary(field));
+ }
+
+ private static void shaking15testDictionary(DexInspector inspector) {
+ inspector.forAllClasses((clazz) -> checkClassAndMemberInDictionary(clazz));
+ }
+
+
+ private static void assumenosideeffects1CheckOutput(String output1, String output2) {
+ Assert.assertEquals("noSideEffectVoid\nnoSideEffectInt\n", output1);
+ Assert.assertEquals("", output2);
+ }
+
+ private static void assumenosideeffects2CheckOutput(String output1, String output2) {
+ Assert.assertEquals("Hello, world!\n", output1);
+ Assert.assertEquals("", output2);
+ }
+
+ private static void assumenosideeffects3CheckOutput(String output1, String output2) {
+ Assert.assertEquals("0\n1\n0L\n1L\n", output1);
+ Assert.assertEquals("1\n0\n1L\n0L\n", output2);
+ }
+
+ private static void assumenosideeffects4CheckOutput(String output1, String output2) {
+ Assert.assertEquals("method0\n0\nmethod1\n1\nmethod0L\n0L\nmethod1L\n1L\n", output1);
+ Assert.assertEquals("1\n0\n1L\n0L\n", output2);
+ }
+
+ private static void assumenosideeffects5CheckOutput(String output1, String output2) {
+ Assert.assertEquals("methodTrue\ntrue\nmethodFalse\nfalse\n", output1);
+ Assert.assertEquals("false\ntrue\n", output2);
+ }
+
+ private static void assumevalues1CheckOutput(String output1, String output2) {
+ Assert.assertEquals("3\n3L\n", output1);
+ Assert.assertEquals("1\n1L\n", output2);
+ }
+
+ private static void assumevalues2CheckOutput(String output1, String output2) {
+ Assert.assertEquals("1\n2\n3\n4\n1L\n2L\n3L\n4L\n", output1);
+ Assert.assertEquals("2\n3\n2L\n3L\n", output2);
+ }
+
+ private static void assumevalues3CheckOutput(String output1, String output2) {
+ Assert.assertEquals("3\n3L\n", output1);
+ Assert.assertEquals("1\n1L\n", output2);
+ }
+
+ private static void assumevalues4CheckOutput(String output1, String output2) {
+ Assert.assertEquals("method0\n0\nmethod1\n1\nmethod0L\n0L\nmethod1L\n1L\n", output1);
+ Assert.assertEquals("method0\n1\nmethod1\n0\nmethod0L\n1L\nmethod1L\n0L\n", output2);
+ }
+
+ private static void assumevalues5CheckOutput(String output1, String output2) {
+ Assert.assertEquals("methodTrue\ntrue\nmethodFalse\nfalse\n", output1);
+ Assert.assertEquals("methodTrue\nfalse\nmethodFalse\ntrue\n", output2);
+ }
+
+ private static void annotationRemovalHasAllInnerClassAnnotations(DexInspector inspector) {
+ ClassSubject outer = inspector.clazz("annotationremoval.OuterClass");
+ Assert.assertTrue(outer.isPresent());
+ Assert.assertTrue(outer.annotation("dalvik.annotation.MemberClasses").isPresent());
+ ClassSubject inner = inspector.clazz("annotationremoval.OuterClass$InnerClass");
+ Assert.assertTrue(inner.isPresent());
+ Assert.assertFalse(inner.annotation("dalvik.annotation.EnclosingMethod").isPresent());
+ Assert.assertTrue(inner.annotation("dalvik.annotation.EnclosingClass").isPresent());
+ Assert.assertTrue(inner.annotation("dalvik.annotation.InnerClass").isPresent());
+ ClassSubject anonymous = inspector.clazz("annotationremoval.OuterClass$1");
+ Assert.assertTrue(anonymous.isPresent());
+ Assert.assertFalse(anonymous.annotation("dalvik.annotation.EnclosingClass").isPresent());
+ Assert.assertTrue(anonymous.annotation("dalvik.annotation.EnclosingMethod").isPresent());
+ Assert.assertTrue(anonymous.annotation("dalvik.annotation.InnerClass").isPresent());
+ ClassSubject local = inspector.clazz("annotationremoval.OuterClass$1LocalMagic");
+ Assert.assertTrue(local.isPresent());
+ Assert.assertFalse(local.annotation("dalvik.annotation.EnclosingClass").isPresent());
+ Assert.assertTrue(local.annotation("dalvik.annotation.EnclosingMethod").isPresent());
+ Assert.assertTrue(local.annotation("dalvik.annotation.InnerClass").isPresent());
+ }
+
+ private static void annotationRemovalHasNoInnerClassAnnotations(DexInspector inspector) {
+ ClassSubject outer = inspector.clazz("annotationremoval.OuterClass");
+ Assert.assertTrue(outer.isPresent());
+ Assert.assertFalse(outer.annotation("dalvik.annotation.MemberClasses").isPresent());
+ ClassSubject inner = inspector.clazz("annotationremoval.OuterClass$InnerClass");
+ Assert.assertTrue(inner.isPresent());
+ Assert.assertFalse(inner.annotation("dalvik.annotation.EnclosingMethod").isPresent());
+ Assert.assertFalse(inner.annotation("dalvik.annotation.EnclosingClass").isPresent());
+ Assert.assertFalse(inner.annotation("dalvik.annotation.InnerClass").isPresent());
+ ClassSubject anonymous = inspector.clazz("annotationremoval.OuterClass$1");
+ Assert.assertTrue(anonymous.isPresent());
+ Assert.assertFalse(anonymous.annotation("dalvik.annotation.EnclosingClass").isPresent());
+ Assert.assertFalse(anonymous.annotation("dalvik.annotation.EnclosingMethod").isPresent());
+ Assert.assertFalse(anonymous.annotation("dalvik.annotation.InnerClass").isPresent());
+ ClassSubject local = inspector.clazz("annotationremoval.OuterClass$1LocalMagic");
+ Assert.assertTrue(local.isPresent());
+ Assert.assertFalse(local.annotation("dalvik.annotation.EnclosingClass").isPresent());
+ Assert.assertFalse(local.annotation("dalvik.annotation.EnclosingMethod").isPresent());
+ Assert.assertFalse(local.annotation("dalvik.annotation.InnerClass").isPresent());
+ }
+
+ private static void checkSameStructure(DexInspector ref, DexInspector inspector) {
+ ref.forAllClasses(refClazz -> checkSameStructure(refClazz,
+ inspector.clazz(refClazz.getDexClass().toSourceString())));
+ }
+
+ private static void checkSameStructure(ClassSubject refClazz, ClassSubject clazz) {
+ Assert.assertTrue(clazz.isPresent());
+ refClazz.forAllFields(refField -> checkSameStructure(refField, clazz));
+ refClazz.forAllMethods(refMethod -> checkSameStructure(refMethod, clazz));
+ }
+
+ private static void checkSameStructure(FoundMethodSubject refMethod, ClassSubject clazz) {
+ MethodSignature signature = refMethod.getOriginalSignature();
+ Assert.assertTrue("Missing Method: " + clazz.getDexClass().toSourceString() + "."
+ + signature.toString(),
+ clazz.method(signature).isPresent());
+ }
+
+ private static void checkSameStructure(FoundFieldSubject refField, ClassSubject clazz) {
+ FieldSignature signature = refField.getOriginalSignature();
+ Assert.assertTrue(
+ "Missing field: " + signature.type + " " + clazz.getOriginalDescriptor()
+ + "." + signature.name,
+ clazz.field(signature.type, signature.name).isPresent());
+ }
+
+ @Parameters(name = "dex: {0} frontend: {1} keep: {3} minify: {4}")
+ public static Collection<Object[]> data() {
+ List<String> tests = Arrays
+ .asList(
+ "shaking1",
+ "shaking2",
+ "shaking3",
+ "shaking4",
+ "shaking5",
+ "shaking6",
+ "shaking7",
+ "shaking8",
+ "shaking9",
+ "shaking10",
+ "shaking11",
+ "shaking12",
+ "shaking13",
+ "shaking14",
+ "shaking15",
+ "shaking16",
+ "inlining",
+ "minification",
+ "assumenosideeffects1",
+ "assumenosideeffects2",
+ "assumenosideeffects3",
+ "assumenosideeffects4",
+ "assumenosideeffects5",
+ "assumevalues1",
+ "assumevalues2",
+ "assumevalues3",
+ "assumevalues4",
+ "assumevalues5",
+ "annotationremoval");
+
+ // Keys can be the name of the test or the name of the test followed by a colon and the name
+ // of the keep file.
+ Map<String, Consumer<DexInspector>> inspections = new HashMap<>();
+ inspections.put("shaking1:keep-rules.txt", TreeShakingTest::shaking1HasNoClassUnused);
+ inspections
+ .put("shaking1:keep-rules-repackaging.txt", TreeShakingTest::shaking1IsCorrectlyRepackaged);
+ inspections.put("shaking2:keep-rules.txt", TreeShakingTest::shaking2SuperClassIsAbstract);
+ inspections.put("shaking3:keep-by-tag.txt", TreeShakingTest::shaking3HasNoClassB);
+ inspections.put("shaking3:keep-by-tag-default.txt", TreeShakingTest::shaking3HasNoClassB);
+ inspections.put("shaking3:keep-by-tag-with-pattern.txt", TreeShakingTest::shaking3HasNoClassB);
+ inspections.put("shaking3:keep-by-tag-via-interface.txt", TreeShakingTest::shaking3HasNoClassB);
+ inspections.put("shaking3:keep-by-tag-on-method.txt", TreeShakingTest::shaking3HasNoClassB);
+ inspections
+ .put("shaking3:keep-no-abstract-classes.txt", TreeShakingTest::shaking3HasNoPrivateClass);
+ inspections.put("shaking5", TreeShakingTest::shaking5Inspection);
+ inspections.put("shaking6:keep-public.txt", TreeShakingTest::hasNoPrivateMethods);
+ inspections.put("shaking6:keep-non-public.txt", TreeShakingTest::hasNoPublicMethodsButPrivate);
+ inspections
+ .put("shaking6:keep-justAMethod-public.txt", TreeShakingTest::hasNoPrivateJustAMethod);
+ inspections.put("shaking6:keep-justAMethod-OnInt.txt", TreeShakingTest::hasOnlyIntJustAMethod);
+ inspections
+ .put("shaking7:keep-public-fields.txt", TreeShakingTest::shaking7HasOnlyPublicFields);
+ inspections
+ .put("shaking7:keep-double-fields.txt", TreeShakingTest::shaking7HasOnlyDoubleFields);
+ inspections
+ .put("shaking7:keep-public-theDoubleField-fields.txt",
+ TreeShakingTest::shaking7HasOnlyPublicFieldsNamedTheDoubleField);
+ inspections
+ .put("shaking7:keep-public-theIntField-fields.txt",
+ TreeShakingTest::shaking7HasOnlyPublicFieldsNamedTheIntField);
+ inspections
+ .put("shaking8:keep-rules.txt", TreeShakingTest::shaking8ThingClassIsAbstractAndEmpty);
+ inspections
+ .put("shaking9:keep-rules.txt", TreeShakingTest::shaking9OnlySuperMethodsKept);
+ inspections
+ .put("shaking11:keep-rules.txt", TreeShakingTest::shaking11OnlyOneClassKept);
+ inspections
+ .put("shaking11:keep-rules-keep-method.txt", TreeShakingTest::shaking11BothMethodsKept);
+ inspections
+ .put("shaking12:keep-rules.txt",
+ TreeShakingTest::shaking12OnlyInstantiatedClassesHaveConstructors);
+ inspections
+ .put("shaking13:keep-rules.txt",
+ TreeShakingTest::shaking13EnsureFieldWritesCorrect);
+ inspections
+ .put("shaking14:keep-rules.txt",
+ TreeShakingTest::shaking14EnsureRightStaticMethodsLive);
+ inspections.put("shaking15:keep-rules.txt",
+ TreeShakingTest::shaking15testDictionary);
+ inspections
+ .put("annotationremoval:keep-rules.txt",
+ TreeShakingTest::annotationRemovalHasNoInnerClassAnnotations);
+ inspections
+ .put("annotationremoval:keep-rules-keep-innerannotation.txt",
+ TreeShakingTest::annotationRemovalHasAllInnerClassAnnotations);
+
+ // Keys can be the name of the test or the name of the test followed by a colon and the name
+ // of the keep file.
+ Map<String, Collection<List<String>>> optionalRules = new HashMap<>();
+ optionalRules.put("shaking1", ImmutableList.of(
+ Collections.singletonList(EMPTY_FLAGS),
+ Lists.newArrayList(EMPTY_FLAGS, EMPTY_FLAGS)));
+ List<Object[]> testCases = new ArrayList<>();
+
+ Map<String, BiConsumer<String, String>> outputComparators = new HashMap<>();
+ outputComparators
+ .put("assumenosideeffects1",
+ TreeShakingTest::assumenosideeffects1CheckOutput);
+ outputComparators
+ .put("assumenosideeffects2",
+ TreeShakingTest::assumenosideeffects2CheckOutput);
+ outputComparators
+ .put("assumenosideeffects3",
+ TreeShakingTest::assumenosideeffects3CheckOutput);
+ outputComparators
+ .put("assumenosideeffects4",
+ TreeShakingTest::assumenosideeffects4CheckOutput);
+ outputComparators
+ .put("assumenosideeffects5",
+ TreeShakingTest::assumenosideeffects5CheckOutput);
+ outputComparators
+ .put("assumevalues1",
+ TreeShakingTest::assumevalues1CheckOutput);
+ outputComparators
+ .put("assumevalues2",
+ TreeShakingTest::assumevalues2CheckOutput);
+ outputComparators
+ .put("assumevalues3",
+ TreeShakingTest::assumevalues3CheckOutput);
+ outputComparators
+ .put("assumevalues4",
+ TreeShakingTest::assumevalues4CheckOutput);
+ outputComparators
+ .put("assumevalues5",
+ TreeShakingTest::assumevalues5CheckOutput);
+
+ Map<String, BiConsumer<DexInspector, DexInspector>> dexComparators = new HashMap<>();
+ dexComparators
+ .put("shaking1:keep-rules-dont-shrink.txt", TreeShakingTest::checkSameStructure);
+ dexComparators
+ .put("shaking2:keep-rules-dont-shrink.txt", TreeShakingTest::checkSameStructure);
+ dexComparators
+ .put("shaking4:keep-rules-dont-shrink.txt", TreeShakingTest::checkSameStructure);
+
+ Set<String> usedInspections = new HashSet<>();
+ Set<String> usedOptionalRules = new HashSet<>();
+ Set<String> usedOutputComparators = new HashSet<>();
+ Set<String> usedDexComparators = new HashSet<>();
+
+ for (String test : tests) {
+ String mainClass = deriveMainClass(test);
+ File[] keepFiles = new File(ToolHelper.EXAMPLES_DIR + "/" + test)
+ .listFiles(file -> file.isFile() && file.getName().endsWith(".txt"));
+ for (File keepFile : keepFiles) {
+ String keepName = keepFile.getName();
+ Consumer<DexInspector> inspection =
+ getTestOptionalParameter(inspections, usedInspections, test, keepName);
+ Collection<List<String>> additionalRules =
+ getTestOptionalParameter(optionalRules, usedOptionalRules, test, keepName);
+
+ BiConsumer<String, String> outputComparator =
+ getTestOptionalParameter(outputComparators, usedOutputComparators, test, keepName);
+ BiConsumer<DexInspector, DexInspector> dexComparator =
+ getTestOptionalParameter(dexComparators, usedDexComparators, test, keepName);
+
+ addTestCases(testCases, test, mainClass, keepName,
+ Collections.singletonList(keepFile.getPath()), inspection,
+ outputComparator, dexComparator);
+
+ if (additionalRules != null) {
+ for (List<String> list : additionalRules) {
+ List<String> keepList = new ArrayList<>(list.size());
+ keepList.add(keepFile.getPath());
+ keepList.addAll(list);
+ addTestCases(testCases, test, mainClass, keepName, keepList, inspection,
+ outputComparator, dexComparator);
+ }
+ }
+ }
+ }
+
+ assert usedInspections.size() == inspections.size();
+ assert usedOptionalRules.size() == optionalRules.size();
+ assert usedOutputComparators.size() == outputComparators.size();
+
+ return testCases;
+ }
+
+ private static void addTestCases(List<Object[]> testCases, String test, String mainClass,
+ String keepName, List<String> keepList, Consumer<DexInspector> inspection,
+ BiConsumer<String, String> outputComparator,
+ BiConsumer<DexInspector, DexInspector> dexComparator) {
+ addTestCase(testCases, test, Frontend.JAR, mainClass, keepName, keepList, false, inspection,
+ outputComparator, dexComparator);
+ addTestCase(testCases, test, Frontend.DEX, mainClass, keepName, keepList, false, inspection,
+ outputComparator, dexComparator);
+ addTestCase(testCases, test, Frontend.JAR, mainClass, keepName, keepList, true, inspection,
+ outputComparator, dexComparator);
+ addTestCase(testCases, test, Frontend.DEX, mainClass, keepName, keepList, true, inspection,
+ outputComparator, dexComparator);
+ }
+
+ private static void addTestCase(List<Object[]> testCases, String test, Frontend kind,
+ String mainClass, String keepName, List<String> keepList, boolean minify,
+ Consumer<DexInspector> inspection, BiConsumer<String, String> outputComparator,
+ BiConsumer<DexInspector, DexInspector> dexComparator) {
+ if (!IGNORED.contains(test + ":" + keepName + ":" + kind + ":" + minify)) {
+ testCases.add(new Object[]{
+ test, kind, mainClass, keepList, minify, inspection, outputComparator, dexComparator});
+ }
+ }
+
+ private static <T> T getTestOptionalParameter(
+ Map<String, T> specifications, Set<String> usedSpecifications, String test,
+ String keepName) {
+ T parameter = specifications.get(test);
+ if (parameter == null) {
+ parameter = specifications.get(test + ":" + keepName);
+ if (parameter != null) {
+ usedSpecifications.add(test + ":" + keepName);
+ }
+ } else {
+ usedSpecifications.add(test);
+ }
+ return parameter;
+ }
+
+ private static String deriveMainClass(String testName) {
+ StringBuilder mainClass = new StringBuilder(testName.length() * 2 + 1);
+ mainClass.append(testName);
+ mainClass.append('.');
+ mainClass.append(Character.toUpperCase(testName.charAt(0)));
+ for (int i = 1; i < testName.length(); i++) {
+ char next = testName.charAt(i);
+ if (!Character.isAlphabetic(next)) {
+ break;
+ }
+ mainClass.append(next);
+ }
+ return mainClass.toString();
+ }
+
+ @Test
+ public void treeShakingTest() throws IOException, InterruptedException, ExecutionException {
+ if (!ToolHelper.artSupported()) {
+ return;
+ }
+ String out = temp.getRoot().getCanonicalPath();
+ Path generated = Paths.get(out, "classes.dex");
+ Consumer<ArtCommandBuilder> extraArtArgs = builder -> {
+ builder.appendClasspath(ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib/classes.dex");
+ };
+
+ if (outputComparator != null) {
+ String output1 = ToolHelper.runArtNoVerificationErrors(
+ Collections.singletonList(originalDex), mainClass, extraArtArgs, null);
+ String output2 = ToolHelper.runArtNoVerificationErrors(
+ Collections.singletonList(generated.toString()), mainClass, extraArtArgs, null);
+ outputComparator.accept(output1, output2);
+ } else {
+ ToolHelper.checkArtOutputIdentical(Collections.singletonList(originalDex),
+ Collections.singletonList(generated.toString()), mainClass,
+ extraArtArgs, null);
+ }
+
+ if (dexComparator != null) {
+ DexInspector ref = new DexInspector(Paths.get(originalDex));
+ DexInspector inspector = new DexInspector(generated,
+ minify ? temp.getRoot().toPath().resolve("proguard.map").toString() : null);
+ dexComparator.accept(ref, inspector);
+ }
+
+ if (inspection != null) {
+ DexInspector inspector = new DexInspector(generated,
+ minify ? temp.getRoot().toPath().resolve("proguard.map").toString() : null);
+ inspection.accept(inspector);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/BinopLiteralTest.java b/src/test/java/com/android/tools/r8/smali/BinopLiteralTest.java
new file mode 100644
index 0000000..7429b43
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/BinopLiteralTest.java
@@ -0,0 +1,120 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.code.Const16;
+import com.android.tools.r8.code.Format22b;
+import com.android.tools.r8.code.Format22s;
+import com.android.tools.r8.code.Return;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class BinopLiteralTest extends SmaliTestBase {
+
+ int[] lit8Values = new int[]{
+ Constants.S8BIT_MIN,
+ Constants.S8BIT_MIN + 1,
+ Constants.S4BIT_MIN - 1,
+ Constants.S4BIT_MIN,
+ Constants.S4BIT_MIN + 1,
+ 1,
+ 0,
+ 1,
+ Constants.S4BIT_MAX - 1,
+ Constants.S4BIT_MAX,
+ Constants.S4BIT_MAX + 1,
+ Constants.S8BIT_MAX - 1,
+ Constants.S8BIT_MAX,
+ };
+
+ int[] lit16Values = new int[]{
+ Short.MIN_VALUE,
+ Short.MIN_VALUE + 1,
+ Constants.S8BIT_MIN - 1,
+ Constants.S8BIT_MAX + 1,
+ Short.MAX_VALUE - 1,
+ Short.MAX_VALUE,
+ };
+
+ @Test
+ public void lit8PassthroughTest() {
+ List<String> lit8Binops = Arrays.asList(
+ "add", "rsub", "mul", "div", "rem", "and", "or", "xor", "shl", "shr", "ushr"
+ );
+
+ for (String binop : lit8Binops) {
+ for (int lit8Value : lit8Values) {
+ DexEncodedMethod method = oneMethodApplication(
+ "int", Collections.singletonList("int"),
+ 0,
+ // E.g. add-int/lit8 p0, p0, -128
+ " " + binop + "-int/lit8 p0, p0, " + lit8Value,
+ " return p0"
+ );
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof Format22b);
+ assertEquals(lit8Value, ((Format22b) code.instructions[0]).CC);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+ }
+ }
+
+ @Test
+ public void lit16PassthroughTest() {
+ List<String> lit16Binops = Arrays.asList(
+ "add", "rsub", "mul", "div", "rem", "and", "or", "xor"
+ );
+
+ for (String binop : lit16Binops) {
+ for (int lit16Value : lit16Values) {
+ String lit16Postfix = !binop.equals("rsub") ? "/lit16" : "";
+ DexEncodedMethod method = oneMethodApplication(
+ "int", Collections.singletonList("int"),
+ 0,
+ // E.g. add-int/lit16 p0, p0, -32768
+ " " + binop + "-int" + lit16Postfix + " p0, p0, " + lit16Value,
+ " return p0"
+ );
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof Format22s);
+ assertEquals(lit16Value, ((Format22s) code.instructions[0]).CCCC);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+ }
+ }
+
+ @Test
+ public void lit16NotSupported() {
+ String[] lit8OnlyBinops = new String[]{
+ "shl", "shr", "ushr",
+ };
+ for (String binop : lit8OnlyBinops) {
+ for (int lit16Value : lit16Values) {
+ DexEncodedMethod method = oneMethodApplication(
+ "int", Collections.singletonList("int"),
+ 1,
+ " const/16 v0, " + lit16Value,
+ " " + binop + "-int/2addr p0, v0 ",
+ " return p0"
+ );
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(3, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof Const16);
+ assertEquals(lit16Value, ((Const16) code.instructions[0]).BBBB);
+ assertTrue(code.instructions[2] instanceof Return);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java b/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
new file mode 100644
index 0000000..5e67ad1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.Test;
+
+/**
+ * Regression test to ensure that we do not ignore the exceptional / on-throw value of a
+ * throwing instruction in the special case where the exceptional edge and the normal edge target
+ * the same block.
+ */
+public class CatchSuccessorFallthroughTest extends SmaliTestBase {
+
+ @Test
+ public void catchSuccessorFallthroughTest() {
+
+ SmaliBuilder builder = new SmaliBuilder("Test");
+
+ builder.addStaticMethod("int", "maybeThrow", Arrays.asList("int"), 0,
+ " if-eqz v0, :throw",
+ " const v0, 42",
+ " return v0",
+ ":throw",
+ " div-int/2addr v0, v0",
+ " return v0");
+
+ MethodSignature methodSig = builder.addStaticMethod(
+ "int", "method", Collections.singletonList("int"), 0,
+ ":try_start",
+ " invoke-static {v0}, LTest;->maybeThrow(I)I",
+ " move-result v0",
+ ":try_end",
+ " .catch Ljava/lang/Throwable; {:try_start .. :try_end} :return",
+ ":return",
+ " return v0"
+ );
+
+ builder.addStaticMethod(
+ "void", "main", Arrays.asList("java.lang.String[]"), 2,
+ " sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const v0, 1",
+ " invoke-static {v0}, LTest;->method(I)I",
+ " move-result v0",
+ " invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V",
+ " const v0, 0",
+ " invoke-static {v0}, LTest;->method(I)I",
+ " move-result v0",
+ " invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V",
+ " return-void");
+
+ InternalOptions options = new InternalOptions();
+ DexApplication originalApplication = buildApplication(builder, options);
+
+
+ DexEncodedMethod method = getMethod(originalApplication, methodSig);
+ // Get the IR pre-optimization.
+ IRCode code = method.buildIR(options);
+
+ // Find the exit block and assert that the value is a phi merging the exceptional edge
+ // with the normal edge.
+ boolean hasReturn = false;
+ for (BasicBlock block : code.blocks) {
+ if (block.exit() instanceof Return) {
+ // Find the return block.
+ // Check it has one phi with two operands / two predecessors.
+ Return ret = block.exit().asReturn();
+ assertEquals(2, block.getPredecessors().size());
+ assertEquals(1, block.getPhis().size());
+ assertEquals(ret.returnValue(), block.getPhis().get(0));
+ // Next we find and check that the phi values come from the expected predecessor.
+ boolean hasNormalPredecessor = false;
+ boolean hasExceptionalPredecessor = false;
+ Phi phi = block.getPhis().get(0);
+ for (Value operand : phi.getOperands()) {
+ BasicBlock defBlock = operand.definition.getBlock();
+ if (defBlock.canThrow()) {
+ // Found the invoke instruction / block.
+ assertEquals(2, defBlock.getSuccessors().size());
+ assertTrue(
+ defBlock.getInstructions().get(defBlock.getInstructions().size() - 2).isInvoke());
+ for (BasicBlock returnPredecessor : block.getPredecessors()) {
+ if (defBlock.isCatchSuccessor(returnPredecessor)) {
+ hasExceptionalPredecessor = true;
+ } else if (defBlock == returnPredecessor) {
+ // Normal flow goes to return.
+ hasNormalPredecessor = true;
+ } else if (defBlock.getSuccessors().contains(returnPredecessor)) {
+ // Normal flow goes to return after an edge split.
+ assertTrue(returnPredecessor.isTrivialGoto());
+ hasNormalPredecessor = true;
+ }
+ }
+ }
+ }
+ assertTrue(hasNormalPredecessor);
+ assertTrue(hasExceptionalPredecessor);
+ hasReturn = true;
+ }
+ }
+ assertTrue(hasReturn);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java b/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java
new file mode 100644
index 0000000..64aa946
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.smali;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.Test;
+
+public class ComputeBlockTryRangeTest extends SmaliTestBase {
+
+ @Test
+ public void jumpIntoTryRange() {
+
+ SmaliBuilder builder = new SmaliBuilder("Test");
+
+ builder.addStaticMethod(
+ "void", "main", Arrays.asList("java.lang.String[]"), 2,
+ " sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const v0, 1",
+ " invoke-static {v0}, LTest;->method(I)I",
+ " move-result v0",
+ " invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V",
+ " const v0, 0",
+ " invoke-static {v0}, LTest;->method(I)I",
+ " move-result v0",
+ " invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V",
+ " return-void");
+
+ MethodSignature methodSig = builder.addStaticMethod(
+ "int", "method", Collections.singletonList("int"), 1,
+ " const v0, 42",
+ " goto :in_try",
+ ":try_start",
+ ":dead_code",
+ " const v0, 0",
+ ":in_try",
+ " div-int/2addr v0, v1",
+ " return v0",
+ ":try_end",
+ " .catch Ljava/io/IOException; {:try_start .. :try_end} :dead_code",
+ " .catch Ljava/lang/Throwable; {:try_start .. :try_end} :return_half",
+ ":return_half",
+ " const v1, 2",
+ " goto :in_try"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication originalApplication = buildApplication(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+
+ DexEncodedMethod method = getMethod(processedApplication, methodSig);
+ assert method.getCode().asDexCode().tries.length > 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java b/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
new file mode 100644
index 0000000..2a2fe32
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
@@ -0,0 +1,739 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.DivIntLit8;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.RemIntLit8;
+import com.android.tools.r8.code.Return;
+import com.android.tools.r8.code.ReturnWide;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.code.SingleConstant;
+import com.android.tools.r8.ir.code.WideConstant;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class ConstantFoldingTest extends SmaliTestBase {
+
+ public void generateBinopTest(String type, String op, List<Long> values, Long result) {
+ boolean wide = type.equals("long") || type.equals("double");
+ StringBuilder source = new StringBuilder();
+ int factor = wide ? 2 : 1;
+ for (int i = 0; i < values.size(); i++) {
+ source.append(" ");
+ source.append(wide ? "const-wide " : "const ");
+ source.append("v" + (i * factor));
+ source.append(", ");
+ source.append("0x" + Long.toHexString(values.get(i)));
+ source.append(wide ? "L" : "");
+ source.append("\n");
+ }
+
+ for (int i = 0; i < values.size() - 1; i++) {
+ source.append(" ");
+ source.append(op + "-" + type + "/2addr ");
+ source.append("v" + ((i + 1) * factor));
+ source.append(", ");
+ source.append("v" + (i * factor));
+ source.append("\n");
+ }
+
+ source.append(" ");
+ source.append(wide ? "return-wide " : "return ");
+ source.append("v" + ((values.size() - 1) * factor));
+
+ DexEncodedMethod method = oneMethodApplication(
+ type, Collections.singletonList(type),
+ values.size() * factor,
+ source.toString());
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ if (wide) {
+ assertTrue(code.instructions[0] instanceof WideConstant);
+ assertEquals(result.longValue(), ((WideConstant) code.instructions[0]).decodedValue());
+ assertTrue(code.instructions[1] instanceof ReturnWide);
+ } else {
+ assertTrue(code.instructions[0] instanceof SingleConstant);
+ assertEquals(
+ result.longValue(), (long) ((SingleConstant) code.instructions[0]).decodedValue());
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+ }
+
+ private long floatBits(float f) {
+ return Float.floatToIntBits(f);
+ }
+
+ private long doubleBits(double d) {
+ return Double.doubleToLongBits(d);
+ }
+
+ ImmutableList<Long> arguments = ImmutableList.of(1L, 2L, 3L, 4L);
+ ImmutableList<Long> floatArguments = ImmutableList.of(
+ floatBits(1.0f), floatBits(2.0f), floatBits(3.0f), floatBits(4.0f));
+ ImmutableList<Long> doubleArguments = ImmutableList.of(
+ doubleBits(1.0), doubleBits(2.0), doubleBits(3.0), doubleBits(4.0));
+
+ @Test
+ public void addFold() {
+ generateBinopTest("int", "add", arguments, 10L);
+ generateBinopTest("long", "add", arguments, 10L);
+ generateBinopTest("float", "add", floatArguments, floatBits(10.0f));
+ generateBinopTest("double", "add", doubleArguments, doubleBits(10.0));
+ }
+
+ @Test
+ public void mulFold() {
+ generateBinopTest("int", "mul", arguments, 24L);
+ generateBinopTest("long", "mul", arguments, 24L);
+ generateBinopTest("float", "mul", floatArguments, floatBits(24.0f));
+ generateBinopTest("double", "mul", doubleArguments, doubleBits(24.0));
+ }
+
+ @Test
+ public void subFold() {
+ generateBinopTest("int", "sub", arguments.reverse(), -2L);
+ generateBinopTest("long", "sub", arguments.reverse(), -2L);
+ generateBinopTest("float", "sub", floatArguments.reverse(), floatBits(-2.0f));
+ generateBinopTest("double", "sub", doubleArguments.reverse(), doubleBits(-2.0));
+ }
+
+ @Test
+ public void divFold() {
+ ImmutableList<Long> arguments = ImmutableList.of(2L, 24L, 48L, 4L);
+ ImmutableList<Long> floatArguments = ImmutableList.of(
+ floatBits(2.0f), floatBits(24.0f), floatBits(48.0f), floatBits(4.0f));
+ ImmutableList<Long> doubleArguments = ImmutableList.of(
+ doubleBits(2.0), doubleBits(24.0), doubleBits(48.0), doubleBits(4.0));
+
+ generateBinopTest("int", "div", arguments, 1L);
+ generateBinopTest("long", "div", arguments, 1L);
+ generateBinopTest("float", "div", floatArguments, floatBits(1.0f));
+ generateBinopTest("double", "div", doubleArguments, doubleBits(1.0));
+ }
+
+
+ @Test
+ public void remFold() {
+ ImmutableList<Long> arguments = ImmutableList.of(10L, 6L, 3L, 2L);
+ ImmutableList<Long> floatArguments = ImmutableList.of(
+ floatBits(10.0f), floatBits(6.0f), floatBits(3.0f), floatBits(2.0f));
+ ImmutableList<Long> doubleArguments = ImmutableList.of(
+ doubleBits(10.0), doubleBits(6.0), doubleBits(3.0), doubleBits(2.0));
+
+ generateBinopTest("int", "rem", arguments, 2L);
+ generateBinopTest("long", "rem", arguments, 2L);
+ generateBinopTest("float", "rem", floatArguments, floatBits(2.0f));
+ generateBinopTest("double", "rem", doubleArguments, doubleBits(2.0));
+ }
+
+ @Test
+ public void divIntFoldDivByZero() {
+ DexEncodedMethod method = oneMethodApplication(
+ "int", Collections.singletonList("int"),
+ 2,
+ " const/4 v0, 1 ",
+ " const/4 v1, 0 ",
+ " div-int/2addr v0, v1 ",
+ " return v0\n "
+ );
+ DexCode code = method.getCode().asDexCode();
+ // Division by zero is not folded, but div-int/lit8 is used.
+ assertEquals(3, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof Const4);
+ assertTrue(code.instructions[1] instanceof DivIntLit8);
+ assertEquals(0, ((DivIntLit8) code.instructions[1]).CC);
+ assertTrue(code.instructions[2] instanceof Return);
+ }
+
+ @Test
+ public void divIntFoldRemByZero() {
+ DexEncodedMethod method = oneMethodApplication(
+ "int", Collections.singletonList("int"),
+ 2,
+ " const/4 v0, 1 ",
+ " const/4 v1, 0 ",
+ " rem-int/2addr v0, v1 ",
+ " return v0\n "
+ );
+ DexCode code = method.getCode().asDexCode();
+ // Division by zero is not folded, but rem-int/lit8 is used.
+ assertEquals(3, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof Const4);
+ assertTrue(code.instructions[1] instanceof RemIntLit8);
+ assertEquals(0, ((RemIntLit8) code.instructions[1]).CC);
+ assertTrue(code.instructions[2] instanceof Return);
+ }
+
+ public void generateUnopTest(String type, String op, Long value, Long result) {
+ boolean wide = type.equals("long") || type.equals("double");
+ StringBuilder source = new StringBuilder();
+ source.append(" ");
+ source.append(wide ? "const-wide " : "const ");
+ source.append("v0 , ");
+ source.append("0x" + Long.toHexString(value));
+ source.append(wide ? "L" : "");
+ source.append("\n");
+
+ source.append(" ");
+ source.append(op + "-" + type + " v0, v0\n");
+
+ source.append(" ");
+ source.append(wide ? "return-wide v0" : "return v0");
+
+ DexEncodedMethod method = oneMethodApplication(
+ type, Collections.singletonList(type),
+ wide ? 2 : 1,
+ source.toString());
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ if (wide) {
+ assertTrue(code.instructions[0] instanceof WideConstant);
+ assertEquals(result.longValue(), ((WideConstant) code.instructions[0]).decodedValue());
+ assertTrue(code.instructions[1] instanceof ReturnWide);
+ } else {
+ assertTrue(code.instructions[0] instanceof SingleConstant);
+ assertEquals(
+ result.longValue(), (long) ((SingleConstant) code.instructions[0]).decodedValue());
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+ }
+
+ @Test
+ public void negFold() {
+ generateUnopTest("int", "neg", 2L, -2L);
+ generateUnopTest("int", "neg", -2L, 2L);
+ generateUnopTest("long", "neg", 2L, -2L);
+ generateUnopTest("long", "neg", -2L, 2L);
+ generateUnopTest("float", "neg", floatBits(2.0f), floatBits(-2.0f));
+ generateUnopTest("float", "neg", floatBits(-2.0f), floatBits(2.0f));
+ generateUnopTest("float", "neg", floatBits(0.0f), floatBits(-0.0f));
+ generateUnopTest("float", "neg", floatBits(-0.0f), floatBits(0.0f));
+ generateUnopTest("double", "neg", doubleBits(2.0), doubleBits(-2.0));
+ generateUnopTest("double", "neg", doubleBits(-2.0), doubleBits(2.0));
+ generateUnopTest("double", "neg", doubleBits(0.0), doubleBits(-0.0));
+ generateUnopTest("double", "neg", doubleBits(-0.0), doubleBits(0.0));
+ }
+
+ private void assertConstValue(int expected, Instruction insn) {
+ assertTrue(insn instanceof SingleConstant);
+ assertEquals(expected, ((SingleConstant) insn).decodedValue());
+ }
+
+ private void assertConstValue(long expected, Instruction insn) {
+ assertTrue(insn instanceof WideConstant);
+ assertEquals(expected, ((WideConstant) insn).decodedValue());
+ }
+
+ public void testLogicalOperatorsFolding(String op, int[] v) {
+ int v0 = v[0];
+ int v1 = v[1];
+ int v2 = v[2];
+ int v3 = v[3];
+
+ int expected = 0;
+ switch (op) {
+ case "and":
+ expected = v0 & v1 & v2 & v3;
+ break;
+ case "or":
+ expected = v0 | v1 | v2 | v3;
+ break;
+ case "xor":
+ expected = v0 ^ v1 ^ v2 ^ v3;
+ break;
+ default:
+ fail("Unsupported logical binop " + op);
+ }
+
+ DexEncodedMethod method = oneMethodApplication(
+ "int", Collections.singletonList("int"),
+ 4,
+ " const v0, " + v0,
+ " const v1, " + v1,
+ " const v2, " + v2,
+ " const v3, " + v3,
+ // E.g. and-int//2addr v1, v0
+ " " + op + "-int/2addr v1, v0 ",
+ " " + op + "-int/2addr v2, v1 ",
+ " " + op + "-int/2addr v3, v2 ",
+ " return v3\n "
+ );
+ DexCode code = method.getCode().asDexCode();
+ // Test that this just returns a constant.
+ assertEquals(2, code.instructions.length);
+ assertConstValue(expected, code.instructions[0]);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+
+ @Test
+ public void logicalOperatorsFolding() {
+ int[][] testValues = new int[][]{
+ new int[]{0x00, 0x00, 0x00, 0x00},
+ new int[]{0x0b, 0x06, 0x03, 0x00},
+ new int[]{0x0f, 0x07, 0x03, 0x01},
+ new int[]{0x08, 0x04, 0x02, 0x01},
+ };
+
+ for (int[] values : testValues) {
+ testLogicalOperatorsFolding("and", values);
+ testLogicalOperatorsFolding("or", values);
+ testLogicalOperatorsFolding("xor", values);
+ }
+ }
+
+ private void testShiftOperatorsFolding(String op, int[] v) {
+ int v0 = v[0];
+ int v1 = v[1];
+ int v2 = v[2];
+ int v3 = v[3];
+
+ int expected = 0;
+ switch (op) {
+ case "shl":
+ v0 = v0 << v1;
+ v0 = v0 << v2;
+ v0 = v0 << v3;
+ break;
+ case "shr":
+ v0 = v0 >> v1;
+ v0 = v0 >> v2;
+ v0 = v0 >> v3;
+ break;
+ case "ushr":
+ v0 = v0 >>> v1;
+ v0 = v0 >>> v2;
+ v0 = v0 >>> v3;
+ break;
+ default:
+ fail("Unsupported shift " + op);
+ }
+ expected = v0;
+
+ DexEncodedMethod method = oneMethodApplication(
+ "int", Collections.singletonList("int"),
+ 4,
+ " const v0, " + v[0],
+ " const v1, " + v[1],
+ " const v2, " + v[2],
+ " const v3, " + v[3],
+ // E.g. and-int//2addr v1, v0
+ " " + op + "-int/2addr v0, v1 ",
+ " " + op + "-int/2addr v0, v2 ",
+ " " + op + "-int/2addr v0, v3 ",
+ " return v0\n "
+ );
+ DexCode code = method.getCode().asDexCode();
+ // Test that this just returns a constant.
+ assertEquals(2, code.instructions.length);
+ assertConstValue(expected, code.instructions[0]);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+
+ @Test
+ public void shiftOperatorsFolding() {
+ int[][] testValues = new int[][]{
+ new int[]{0x01, 0x01, 0x01, 0x01},
+ new int[]{0x01, 0x02, 0x03, 0x04},
+ new int[]{0x7f000000, 0x01, 0x2, 0x03},
+ new int[]{0x80000000, 0x01, 0x2, 0x03},
+ new int[]{0xffffffff, 0x01, 0x2, 0x03},
+ };
+
+ for (int[] values : testValues) {
+ testShiftOperatorsFolding("shl", values);
+ testShiftOperatorsFolding("shr", values);
+ testShiftOperatorsFolding("ushr", values);
+ }
+ }
+
+ private void testShiftOperatorsFoldingWide(String op, long[] v) {
+ long v0 = v[0];
+ int v2 = (int) v[1];
+ int v4 = (int) v[2];
+ int v6 = (int) v[3];
+
+ long expected = 0;
+ switch (op) {
+ case "shl":
+ v0 = v0 << v2;
+ v0 = v0 << v4;
+ v0 = v0 << v6;
+ break;
+ case "shr":
+ v0 = v0 >> v2;
+ v0 = v0 >> v4;
+ v0 = v0 >> v6;
+ break;
+ case "ushr":
+ v0 = v0 >>> v2;
+ v0 = v0 >>> v4;
+ v0 = v0 >>> v6;
+ break;
+ default:
+ fail("Unsupported shift " + op);
+ }
+ expected = v0;
+
+ DexEncodedMethod method = oneMethodApplication(
+ "long", Collections.singletonList("long"),
+ 5,
+ " const-wide v0, 0x" + Long.toHexString(v[0]) + "L",
+ " const v2, " + v[1],
+ " const v3, " + v[2],
+ " const v4, " + v[3],
+ // E.g. and-long//2addr v1, v0
+ " " + op + "-long/2addr v0, v2 ",
+ " " + op + "-long/2addr v0, v3 ",
+ " " + op + "-long/2addr v0, v4 ",
+ " return-wide v0\n "
+ );
+ DexCode code = method.getCode().asDexCode();
+ // Test that this just returns a constant.
+ assertEquals(2, code.instructions.length);
+ assertConstValue(expected, code.instructions[0]);
+ assertTrue(code.instructions[1] instanceof ReturnWide);
+ }
+
+ @Test
+ public void shiftOperatorsFoldingWide() {
+ long[][] testValues = new long[][]{
+ new long[]{0x01, 0x01, 0x01, 0x01},
+ new long[]{0x01, 0x02, 0x03, 0x04},
+ new long[]{0x7f0000000000L, 0x01, 0x2, 0x03},
+ new long[]{0x800000000000L, 0x01, 0x2, 0x03},
+ new long[]{0x7f00000000000000L, 0x01, 0x2, 0x03},
+ new long[]{0x8000000000000000L, 0x01, 0x2, 0x03},
+ new long[]{0xffffffffffffffffL, 0x01, 0x2, 0x03},
+ };
+
+ for (long[] values : testValues) {
+ testShiftOperatorsFoldingWide("shl", values);
+ testShiftOperatorsFoldingWide("shr", values);
+ testShiftOperatorsFoldingWide("ushr", values);
+ }
+ }
+
+ @Test
+ public void notIntFold() {
+ int[] testValues = new int[]{0, 1, 0xff, 0xffffffff, 0xff000000, 0x80000000};
+ for (int value : testValues) {
+ DexEncodedMethod method = oneMethodApplication(
+ "int", Collections.emptyList(),
+ 1,
+ " const v0, " + value,
+ " not-int v0, v0",
+ " return v0"
+ );
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertConstValue(~value, code.instructions[0]);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+ }
+
+ @Test
+ public void notLongFold() {
+ long[] testValues = new long[]{
+ 0L,
+ 1L,
+ 0xffL,
+ 0xffffffffffffffffL,
+ 0x00ffffffffffffffL,
+ 0xff00000000000000L,
+ 0x8000000000000000L
+ };
+ for (long value : testValues) {
+ DexEncodedMethod method = oneMethodApplication(
+ "long", Collections.emptyList(),
+ 2,
+ " const-wide v0, 0x" + Long.toHexString(value) + "L",
+ " not-long v0, v0",
+ " return-wide v0"
+ );
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertConstValue(~value, code.instructions[0]);
+ assertTrue(code.instructions[1] instanceof ReturnWide);
+ }
+ }
+
+ @Test
+ public void negIntFold() {
+ int[] testValues = new int[]{0, 1, 0xff, 0xffffffff, 0xff000000, 0x80000000};
+ for (int value : testValues) {
+ DexEncodedMethod method = oneMethodApplication(
+ "int", Collections.emptyList(),
+ 1,
+ " const v0, " + value,
+ " neg-int v0, v0",
+ " return v0"
+ );
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertConstValue(-value, code.instructions[0]);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+ }
+
+ @Test
+ public void negLongFold() {
+ long[] testValues = new long[]{
+ 0L,
+ 1L,
+ 0xffL,
+ 0xffffffffffffffffL,
+ 0x00ffffffffffffffL,
+ 0xff00000000000000L,
+ 0x8000000000000000L
+ };
+ for (long value : testValues) {
+ DexEncodedMethod method = oneMethodApplication(
+ "long", Collections.emptyList(),
+ 2,
+ " const-wide v0, 0x" + Long.toHexString(value) + "L",
+ " neg-long v0, v0",
+ " return-wide v0"
+ );
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ long expected = -value;
+ assertConstValue(-value, code.instructions[0]);
+ assertTrue(code.instructions[1] instanceof ReturnWide);
+ }
+ }
+
+ @Test
+ public void cmpFloatFold() {
+ String[] ifOpcode = new String[6];
+ ifOpcode[Type.EQ.ordinal()] = "if-eqz";
+ ifOpcode[Type.NE.ordinal()] = "if-nez";
+ ifOpcode[Type.LE.ordinal()] = "if-lez";
+ ifOpcode[Type.GE.ordinal()] = "if-gez";
+ ifOpcode[Type.LT.ordinal()] = "if-ltz";
+ ifOpcode[Type.GT.ordinal()] = "if-gtz";
+
+ class FloatTestData {
+
+ final float a;
+ final float b;
+ final boolean results[];
+
+ FloatTestData(float a, float b) {
+ this.a = a;
+ this.b = b;
+ results = new boolean[6];
+ results[Type.EQ.ordinal()] = a == b;
+ results[Type.NE.ordinal()] = a != b;
+ results[Type.LE.ordinal()] = a <= b;
+ results[Type.GE.ordinal()] = a >= b;
+ results[Type.LT.ordinal()] = a < b;
+ results[Type.GT.ordinal()] = a > b;
+ }
+ }
+
+ float[] testValues = new float[]{
+ Float.NEGATIVE_INFINITY,
+ -100.0f,
+ -0.0f,
+ 0.0f,
+ 100.0f,
+ Float.POSITIVE_INFINITY,
+ Float.NaN
+ };
+
+ List<FloatTestData> tests = new ArrayList<>();
+ for (int i = 0; i < testValues.length; i++) {
+ for (int j = 0; j < testValues.length; j++) {
+ tests.add(new FloatTestData(testValues[i], testValues[j]));
+ }
+ }
+
+ for (FloatTestData test : tests) {
+ for (Type type : Type.values()) {
+ for (Bias bias : Bias.values()) {
+ if (bias == Bias.NONE) {
+ // Bias NONE is only for long comparison.
+ continue;
+ }
+ // If no NaNs are involved either bias produce the same result.
+ if (Float.isNaN(test.a) || Float.isNaN(test.b)) {
+ // For NaN comparison only test with the bias that provide Java semantics.
+ // The Java Language Specification 4.2.3. Floating-Point Types, Formats, and Values
+ // says:
+ //
+ // The numerical comparison operators <, <=, >, and >= return false if either or both
+ // operands are NaN
+ if ((type == Type.GE || type == Type.GT) && bias == Bias.GT) {
+ continue;
+ }
+ if ((type == Type.LE || type == Type.LT) && bias == Bias.LT) {
+ continue;
+ }
+ }
+ String cmpInstruction;
+ if (bias == Bias.LT) {
+ cmpInstruction = " cmpl-float v0, v0, v1";
+ } else {
+ cmpInstruction = " cmpg-float v0, v0, v1";
+ }
+ DexEncodedMethod method = oneMethodApplication(
+ "int", Collections.emptyList(),
+ 2,
+ " const v0, 0x" + Integer.toHexString(Float.floatToRawIntBits(test.a)),
+ " const v1, 0x" + Integer.toHexString(Float.floatToRawIntBits(test.b)),
+ cmpInstruction,
+ " " + ifOpcode[type.ordinal()] + " v0, :label_2",
+ " const v0, 0",
+ ":label_1",
+ " return v0",
+ ":label_2",
+ " const v0, 1",
+ " goto :label_1"
+ );
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ int expected = test.results[type.ordinal()] ? 1 : 0;
+ assertConstValue(expected, code.instructions[0]);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+ }
+ }
+ }
+
+ @Test
+ public void cmpDoubleFold() {
+ String[] ifOpcode = new String[6];
+ ifOpcode[Type.EQ.ordinal()] = "if-eqz";
+ ifOpcode[Type.NE.ordinal()] = "if-nez";
+ ifOpcode[Type.LE.ordinal()] = "if-lez";
+ ifOpcode[Type.GE.ordinal()] = "if-gez";
+ ifOpcode[Type.LT.ordinal()] = "if-ltz";
+ ifOpcode[Type.GT.ordinal()] = "if-gtz";
+
+ class DoubleTestData {
+
+ final double a;
+ final double b;
+ final boolean results[];
+
+ DoubleTestData(double a, double b) {
+ this.a = a;
+ this.b = b;
+ results = new boolean[6];
+ results[Type.EQ.ordinal()] = a == b;
+ results[Type.NE.ordinal()] = a != b;
+ results[Type.LE.ordinal()] = a <= b;
+ results[Type.GE.ordinal()] = a >= b;
+ results[Type.LT.ordinal()] = a < b;
+ results[Type.GT.ordinal()] = a > b;
+ }
+ }
+
+ double[] testValues = new double[]{
+ Double.NEGATIVE_INFINITY,
+ -100.0f,
+ -0.0f,
+ 0.0f,
+ 100.0f,
+ Double.POSITIVE_INFINITY,
+ Double.NaN
+ };
+
+ List<DoubleTestData> tests = new ArrayList<>();
+ for (int i = 0; i < testValues.length; i++) {
+ for (int j = 0; j < testValues.length; j++) {
+ tests.add(new DoubleTestData(testValues[i], testValues[j]));
+ }
+ }
+
+ for (DoubleTestData test : tests) {
+ for (Type type : Type.values()) {
+ for (Bias bias : Bias.values()) {
+ if (bias == Bias.NONE) {
+ // Bias NONE is only for long comparison.
+ continue;
+ }
+ if (Double.isNaN(test.a) || Double.isNaN(test.b)) {
+ // For NaN comparison only test with the bias that provide Java semantics.
+ // The Java Language Specification 4.2.3. Doubleing-Point Types, Formats, and Values
+ // says:
+ //
+ // The numerical comparison operators <, <=, >, and >= return false if either or both
+ // operands are NaN
+ if ((type == Type.GE || type == Type.GT) && bias == Bias.GT) {
+ continue;
+ }
+ if ((type == Type.LE || type == Type.LT) && bias == Bias.LT) {
+ continue;
+ }
+ }
+ String cmpInstruction;
+ if (bias == Bias.LT) {
+ cmpInstruction = " cmpl-double v0, v0, v2";
+ } else {
+ cmpInstruction = " cmpg-double v0, v0, v2";
+ }
+ DexEncodedMethod method = oneMethodApplication(
+ "int", Collections.emptyList(),
+ 4,
+ " const-wide v0, 0x" + Long.toHexString(Double.doubleToRawLongBits(test.a)) + "L",
+ " const-wide v2, 0x" + Long.toHexString(Double.doubleToRawLongBits(test.b)) + "L",
+ cmpInstruction,
+ " " + ifOpcode[type.ordinal()] + " v0, :label_2",
+ " const v0, 0",
+ ":label_1",
+ " return v0",
+ ":label_2",
+ " const v0, 1",
+ " goto :label_1"
+ );
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ int expected = test.results[type.ordinal()] ? 1 : 0;
+ assertConstValue(expected, code.instructions[0]);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+ }
+ }
+ }
+
+ @Test
+ public void cmpLongFold() {
+ long[][] longValues = new long[][]{
+ {Long.MIN_VALUE, 1L},
+ {Long.MAX_VALUE, 1L},
+ {Long.MIN_VALUE, 0L},
+ {Long.MAX_VALUE, 0L},
+ {Long.MIN_VALUE, -1L},
+ {Long.MAX_VALUE, -1L},
+ };
+
+ for (long[] values : longValues) {
+ DexEncodedMethod method = oneMethodApplication(
+ "int", Collections.emptyList(),
+ 4,
+ " const-wide v0, 0x" + Long.toHexString(values[0]) + "L",
+ " const-wide v2, 0x" + Long.toHexString(values[1]) + "L",
+ " cmp-long v0, v0, v2",
+ " return v0"
+ );
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertConstValue(Long.compare(values[0], values[1]), code.instructions[0]);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
new file mode 100644
index 0000000..c3f6d67
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
@@ -0,0 +1,396 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.Return;
+import com.android.tools.r8.code.ReturnObject;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.If.Type;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class IfSimplificationTest extends SmaliTestBase {
+
+ @Test
+ public void ifZeroNeqZero() {
+ DexEncodedMethod method = oneMethodApplication(
+ "int",
+ Collections.emptyList(),
+ 1,
+ " const v0, 0",
+ " if-nez v0, :label_2",
+ ":label_1",
+ " return v0",
+ ":label_2",
+ " const v0, 1",
+ " goto :label_1");
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof Const4);
+ assertEquals(0, ((Const4) code.instructions[0]).B);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+
+ @Test
+ public void ifTwoEqZero() {
+ DexEncodedMethod method = oneMethodApplication(
+ "int",
+ Collections.emptyList(),
+ 1,
+ " const v0, 2",
+ " if-eqz v0, :label_2",
+ ":label_1",
+ " return v0",
+ ":label_2",
+ " const v0, 1",
+ " goto :label_1");
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof Const4);
+ assertEquals(2, ((Const4) code.instructions[0]).B);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+
+ @Test
+ public void b() {
+ DexEncodedMethod method = oneMethodApplication(
+ "int",
+ Collections.singletonList("int"),
+ 1,
+ " const v0, 0",
+ " if-nez v0, :label_2",
+ ":label_1",
+ " return v0",
+ ":label_2",
+ " if-nez p0, :label_3",
+ " const v0, 1",
+ " goto :label_1",
+ ":label_3",
+ " const v0, 2",
+ " goto :label_1");
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof Const4);
+ assertEquals(0, ((Const4) code.instructions[0]).B);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+
+ @Test
+ public void c() {
+ DexEncodedMethod method = oneMethodApplication(
+ "int",
+ Collections.singletonList("int"),
+ 1,
+ " const v0, 0",
+ " if-nez v0, :label_2",
+ ":label_1",
+ " return v0",
+ ":label_2",
+ " if-nez p0, :label_3",
+ " const v0, 1",
+ " goto :label_1",
+ ":label_3",
+ " const p0, 0",
+ " goto :label_2");
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof Const4);
+ assertEquals(0, ((Const4) code.instructions[0]).B);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+
+ @Test
+ public void d() {
+ DexEncodedMethod method = oneMethodApplication(
+ "int",
+ Collections.singletonList("int"),
+ 1,
+ " const v0, 0",
+ " if-nez v0, :label_2",
+ ":label_1",
+ " return v0",
+ ":label_2",
+ " if-nez p0, :label_3",
+ " const v0, 1",
+ " goto :label_4",
+ ":label_3",
+ " const p0, 0",
+ " goto :label_2",
+ ":label_4",
+ " if-nez p0, :label_5",
+ " const v0, 1",
+ " goto :label_4",
+ ":label_5",
+ " const p0, 0",
+ " goto :label_2");
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof Const4);
+ assertEquals(0, ((Const4) code.instructions[0]).B);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+
+ @Test
+ public void e() {
+ DexEncodedMethod method = oneMethodApplication(
+ "int",
+ ImmutableList.of("int", "int", "int"),
+ 1,
+ " const v0, 0",
+ " if-nez v0, :x",
+ " const v0, 1",
+ " if-nez p0, :x",
+ " const v0, 2",
+ " if-nez p1, :x",
+ " const v0, 3",
+ " if-nez p2, :return",
+ " const v0, 4",
+ " goto :return",
+ ":x",
+ " add-int v0, v0, p0",
+ ":return",
+ " return v0");
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(12, code.instructions.length);
+ assertTrue(code.instructions[11] instanceof Return);
+ }
+
+ @Test
+ public void f() {
+ DexEncodedMethod method = oneMethodApplication(
+ "int",
+ Collections.singletonList("int"),
+ 1,
+ " const v0, 0",
+ " if-nez v0, :label_2",
+ ":label_1",
+ " return v0",
+ ":label_2",
+ " const v0, 1",
+ " goto :label_2");
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof Const4);
+ assertEquals(0, ((Const4) code.instructions[0]).B);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+
+ @Test
+ public void simplifyNonZeroTests() {
+ String[] ifOpcode = new String[6];
+ ifOpcode[Type.EQ.ordinal()] = "if-eq";
+ ifOpcode[Type.NE.ordinal()] = "if-ne";
+ ifOpcode[Type.LE.ordinal()] = "if-le";
+ ifOpcode[Type.GE.ordinal()] = "if-ge";
+ ifOpcode[Type.LT.ordinal()] = "if-lt";
+ ifOpcode[Type.GT.ordinal()] = "if-gt";
+
+ class TestData {
+
+ final int a;
+ final int b;
+ final boolean results[];
+
+ TestData(int a, int b) {
+ this.a = a;
+ this.b = b;
+ results = new boolean[6];
+ results[Type.EQ.ordinal()] = a == b;
+ results[Type.NE.ordinal()] = a != b;
+ results[Type.LE.ordinal()] = a <= b;
+ results[Type.GE.ordinal()] = a >= b;
+ results[Type.LT.ordinal()] = a < b;
+ results[Type.GT.ordinal()] = a > b;
+ }
+ }
+
+ int[] testValues = new int[]{
+ 100,
+ 1,
+ 0,
+ -1,
+ 100
+ };
+
+ List<TestData> tests = new ArrayList<>();
+ for (int i = 0; i < testValues.length; i++) {
+ for (int j = 0; j < testValues.length; j++) {
+ tests.add(new TestData(testValues[i], testValues[j]));
+ }
+ }
+
+ for (TestData test : tests) {
+ for (Type type : Type.values()) {
+ DexEncodedMethod method = oneMethodApplication(
+ "int",
+ Collections.singletonList("int"),
+ 2,
+ " const v0, 0x" + Integer.toHexString(test.a),
+ " const v1, 0x" + Integer.toHexString(test.b),
+ " " + ifOpcode[type.ordinal()] + " v0, v1, :label_2",
+ " const v0, 0",
+ ":label_1",
+ " return v0",
+ ":label_2",
+ " const v0, 1",
+ " goto :label_1");
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof Const4);
+ int expected = test.results[type.ordinal()] ? 1 : 0;
+ assertEquals(expected, ((Const4) code.instructions[0]).B);
+ assertTrue(code.instructions[1] instanceof Return);
+ }
+ }
+ }
+
+ @Test
+ public void x() {
+ DexEncodedMethod method = oneMethodApplication(
+ "Test",
+ Lists.newArrayList("Test", "java.lang.String[]", "java.lang.String",
+ "java.lang.String[]", "java.lang.String"),
+ 10,
+ " const/4 v4, 0x00 # 0",
+ " invoke-virtual { v10 }, LTest;->a()LTest;",
+ " if-nez v4, :label_8",
+ " move-object v0, v4",
+ " :label_7",
+ " return-object v0",
+ " :label_8",
+ " invoke-static { v14 }, LTest;->a([Ljava/lang/String;)LTest;",
+ " move-result-object v2",
+ " invoke-virtual { v2 }, LTest;->a()Z",
+ " move-result v0",
+ " if-nez v0, :label_20",
+ " move-object v0, v4",
+ " goto :label_7",
+ " :label_20",
+ " iget-wide v0, v2, LTest;->a:J",
+ " iget-wide v6, v2, LTest;->b:J",
+ " invoke-virtual { v2 }, LTest;->c()Z",
+ " move-result v2",
+ " if-eqz v2, :label_33",
+ " invoke-virtual { v4 }, LTest;->a()V",
+ " :label_33",
+ " new-instance v5, LTest;",
+ " sget-object v2, LTest;->a:[Ljava/lang/String;",
+ " invoke-direct { v5, v2 }, LTest;-><init>([Ljava/lang/String;)V",
+ " invoke-virtual { v10 }, LTest;->a()LTest;",
+ " invoke-virtual { v4, v0, v1, v6, v7 }, LTest;->a(JJ)Ljava/util/List;",
+ " move-result-object v2",
+ " invoke-interface { v2 }, Ljava/util/List;->iterator()Ljava/util/Iterator;",
+ " move-result-object v6",
+ " move-wide v2, v0",
+ " :label_52",
+ " invoke-interface { v6 }, Ljava/util/Iterator;->hasNext()Z",
+ " move-result v0",
+ " if-eqz v0, :label_107",
+ " invoke-interface { v6 }, Ljava/util/Iterator;->next()Ljava/lang/Object;",
+ " move-result-object v0",
+ " check-cast v0, LTest;",
+ " const-wide/16 v8, 0x0000000000000001 # 1",
+ " add-long/2addr v2, v8",
+ " invoke-virtual { v5 }, LTest;->newRow()LTest;",
+ " move-result-object v1",
+ " invoke-static { v2, v3 }, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;",
+ " move-result-object v7",
+ " invoke-virtual { v1, v7 }, LTest;->a(Ljava/lang/Object;)LTest;",
+ " move-result-object v1",
+ " const-string v7, \"add\"",
+ " invoke-virtual { v1, v7 }, LTest;->a(Ljava/lang/Object;)LTest;",
+ " move-result-object v1",
+ " iget-object v7, v0, LTest;->a:Ljava/lang/String;",
+ " invoke-virtual { v1, v7 }, LTest;->a(Ljava/lang/Object;)LTest;",
+ " move-result-object v1",
+ " iget v7, v0, LTest;->b:I",
+ " invoke-static { v7 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
+ " move-result-object v7",
+ " invoke-virtual { v1, v7 }, LTest;->add(Ljava/lang/Object;)LTest;",
+ " move-result-object v1",
+ " iget-object v0, v0, LTest;->a:Ljava/lang/String;",
+ " invoke-virtual { v1, v0 }, LTest;->add(Ljava/lang/Object;)LTest;",
+ " goto :label_52",
+ " :label_107",
+ " iget-object v0, v4, LTest;->a:LTest;",
+ " const-string v1, \"text 1\"",
+ " const/4 v2, 0x00 # 0",
+ " invoke-virtual { v0, v1, v2 }, LTest;->a(Ljava/lang/String;I)LTest;",
+ " move-result-object v0",
+ " const-string v1, \"text 2\"",
+ " const-string v2, \"\"",
+ " invoke-interface { v0, v1, v2 }, LTest;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+ " move-result-object v0",
+ " invoke-static { v5, v0 }, LTest;->a(LTest;Ljava/lang/String;)LTest;",
+ " move-result-object v0",
+ " goto :label_7"
+ );
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(3, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof InvokeVirtual);
+ assertTrue(code.instructions[1] instanceof Const4);
+ assertEquals(0, ((Const4) code.instructions[1]).B);
+ assertTrue(code.instructions[2] instanceof ReturnObject);
+ }
+
+ @Test
+ public void y() {
+ DexEncodedMethod method = oneMethodApplication(
+ "boolean",
+ Lists.newArrayList("Test", "java.lang.Object"),
+ 6,
+ " const-wide/16 v4, 0x0000000000000000L # 0",
+ " const/4 v0, 0x01 # 1",
+ " const/4 v3, 0x00 # 0",
+ " const/4 v1, 0x00 # 0",
+ " if-ne v6, v7, :label_8",
+ " :label_7",
+ " return v0",
+ " :label_8",
+ " if-nez v7, :label_12",
+ " move v0, v1",
+ " goto :label_7",
+ " :label_12",
+ " instance-of v2, v7, LTest;",
+ " if-nez v2, :label_18",
+ " move v0, v1",
+ " goto :label_7",
+ " :label_18",
+ " check-cast v7, LTest;",
+ " cmp-long v2, v4, v4",
+ " if-nez v2, :label_50",
+ " invoke-static { v3, v3 }, LTest;->a(Ljava/lang/Object;Ljava/lang/Object;)Z",
+ " move-result v2",
+ " if-eqz v2, :label_50",
+ " invoke-static { v3, v3 }, LTest;->a(Ljava/lang/Object;Ljava/lang/Object;)Z",
+ " move-result v2",
+ " if-eqz v2, :label_50",
+ " invoke-static { v1 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
+ " move-result-object v2",
+ " invoke-static { v1 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
+ " move-result-object v3",
+ " invoke-static { v2, v3 }, LTest;->a(Ljava/lang/Object;Ljava/lang/Object;)Z",
+ " move-result v2",
+ " if-nez v2, :label_7",
+ " :label_50",
+ " move v0, v1",
+ " goto :label_7"
+ );
+ DexCode code = method.getCode().asDexCode();
+ // TODO(sgjesse): Maybe this test is too fragile, as it leaves quite a lot of code, so the
+ // expectation might need changing with other optimizations.
+ assertEquals(29, code.instructions.length);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/JumboStringTest.java b/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
new file mode 100644
index 0000000..79ef0b9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class JumboStringTest extends SmaliTestBase {
+
+ @Test
+ public void test() {
+ StringBuilder builder = new StringBuilder();
+ StringBuilder expectedBuilder = new StringBuilder();
+ builder.append(" new-instance v0, Ljava/lang/StringBuilder;\n");
+ builder.append(" invoke-direct { v0 }, Ljava/lang/StringBuilder;-><init>()V\n");
+ for (int i = 0; i <= 0xffff + 2; i++) {
+ String prefixed = StringUtils.zeroPrefix(i, 5);
+ expectedBuilder.append(prefixed);
+ expectedBuilder.append("\n");
+ builder.append(" const-string v1, \"" + prefixed + "\\n\"\n");
+ builder.append(
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n");
+ }
+ builder.append(
+ " invoke-virtual { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n");
+ builder.append(" move-result-object v0\n");
+ builder.append(" return-object v0\n");
+
+ SmaliBuilder smaliBuilder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ MethodSignature signature = smaliBuilder.addStaticMethod(
+ "java.lang.String",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of(),
+ 2,
+ builder.toString()
+ );
+
+ smaliBuilder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " invoke-static {}, LTest;->method()Ljava/lang/String;",
+ " move-result-object v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication originalApplication = buildApplication(smaliBuilder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ String result = runArt(processedApplication, options);
+
+ assertEquals(expectedBuilder.toString(), result);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
new file mode 100644
index 0000000..4808f33
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -0,0 +1,1468 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.ConstString;
+import com.android.tools.r8.code.ConstWide;
+import com.android.tools.r8.code.ConstWideHigh16;
+import com.android.tools.r8.code.DivInt;
+import com.android.tools.r8.code.InvokeStatic;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.MoveResult;
+import com.android.tools.r8.code.MoveResultWide;
+import com.android.tools.r8.code.Return;
+import com.android.tools.r8.code.ReturnObject;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.code.ReturnWide;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+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.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.antlr.runtime.RecognitionException;
+import org.junit.Test;
+
+public class OutlineTest extends SmaliTestBase {
+
+ DexEncodedMethod getInvokedMethod(DexApplication application, InvokeStatic invoke) {
+ DexInspector inspector = new DexInspector(application);
+ ClassSubject clazz = inspector.clazz(invoke.getMethod().holder.toSourceString());
+ assertTrue(clazz.isPresent());
+ DexMethod invokedMethod = invoke.getMethod();
+ invokedMethod.proto.returnType.toSourceString();
+ MethodSubject method = clazz.method(
+ invokedMethod.proto.returnType.toSourceString(),
+ invokedMethod.name.toString(),
+ Arrays.stream(invokedMethod.proto.parameters.values)
+ .map(p -> p.toSourceString())
+ .collect(Collectors.toList()));
+ assertTrue(method.isPresent());
+ return method.getMethod();
+ }
+
+ String firstOutlineMethodName(InternalOptions options) {
+ StringBuilder builder = new StringBuilder(options.outline.className);
+ builder.append('.');
+ builder.append(options.outline.methodPrefix);
+ builder.append("0");
+ return builder.toString();
+ }
+
+ MethodSignature firstOutlineMethodSignature(
+ String returnType, List<String> parameterTypes, InternalOptions options) {
+ return new MethodSignature(
+ options.outline.className, options.outline.methodPrefix + "0", returnType, parameterTypes);
+ }
+
+ boolean isOutlineMethodName(InternalOptions options, String qualifiedName) {
+ StringBuilder builder = new StringBuilder(options.outline.className);
+ builder.append('.');
+ builder.append(options.outline.methodPrefix);
+ return qualifiedName.indexOf(builder.toString()) == 0;
+ }
+
+ @Test
+ public void a() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String returnType = "java.lang.String";
+ List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 2,
+ " move v0, p0",
+ " const-string v1, \"Test\"",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " move-result-object v0",
+ " return-object v0"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " new-instance v1, Ljava/lang/StringBuilder;",
+ " invoke-direct { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+ " invoke-static { v1 }, LTest;->method(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+ " move-result-object v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ for (int i = 2; i < 6; i++) {
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = i;
+ options.outline.maxSize = i;
+
+ DexApplication originalApplication = buildApplication(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Return the processed method for inspection.
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+
+ DexCode code = method.getCode().asDexCode();
+ assertTrue(code.instructions[0] instanceof ConstString);
+ assertTrue(code.instructions[1] instanceof InvokeStatic);
+ InvokeStatic invoke = (InvokeStatic) code.instructions[1];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ assertEquals("TestTestTestTest", result);
+ }
+ }
+
+ @Test
+ public void b() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String returnType = "java.lang.String";
+ List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 2,
+ " move v0, p0",
+ " const-string v1, \"Test1\"",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " const-string v1, \"Test2\"",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " const-string v1, \"Test3\"",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " const-string v1, \"Test4\"",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " move-result-object v0",
+ " return-object v0"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " new-instance v1, Ljava/lang/StringBuilder;",
+ " invoke-direct { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+ " invoke-static { v1 }, LTest;->method(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+ " move-result-object v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ for (int i = 2; i < 6; i++) {
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = i;
+ options.outline.maxSize = i;
+
+ DexApplication originalApplication = buildApplication(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Return the processed method for inspection.
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+
+ DexCode code = method.getCode().asDexCode();
+
+ // Up to 4 const instructions before the invoke of the outline.
+ int firstOutlineInvoke = Math.min(i, 4);
+ for (int j = 0; j < firstOutlineInvoke; j++) {
+ assertTrue(code.instructions[j] instanceof ConstString);
+ }
+ assertTrue(code.instructions[firstOutlineInvoke] instanceof InvokeStatic);
+ InvokeStatic invoke = (InvokeStatic) code.instructions[firstOutlineInvoke];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ assertEquals("Test1Test2Test3Test4", result);
+ }
+ }
+
+ @Test
+ public void c() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ // Method with const instructions after the outline.
+ String returnType = "int";
+ List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 2,
+ " move v0, p0",
+ " const-string v1, \"Test\"",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " move-result-object v0",
+ " const v0, 0",
+ " const v1, 1",
+ " add-int v1, v1, v0",
+ " return v1"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " new-instance v1, Ljava/lang/StringBuilder;",
+ " invoke-direct { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+ " invoke-static { v1 }, LTest;->method(Ljava/lang/StringBuilder;)I",
+ " move-result v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ DexApplication originalApplication = buildApplication(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Return the processed method for inspection.
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+
+ DexCode code = method.getCode().asDexCode();
+ assertTrue(code.instructions[0] instanceof ConstString);
+ assertTrue(code.instructions[1] instanceof InvokeStatic);
+ InvokeStatic invoke = (InvokeStatic) code.instructions[1];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ assertEquals("1", result);
+ }
+
+ @Test
+ public void d() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ // Method with mixed use of arguments and locals.
+ String returnType = "java.lang.String";
+ List<String> parameters = ImmutableList.of(
+ "java.lang.StringBuilder", "java.lang.String", "java.lang.String");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 2,
+ " invoke-virtual { p0, p1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object p0",
+ " const-string v0, \"Test1\"",
+ " invoke-virtual { p0, v0 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object p0",
+ " invoke-virtual { p0, p2 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object p0",
+ " const-string v1, \"Test2\"",
+ " invoke-virtual { p0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object p0",
+ " const-string v1, \"Test3\"",
+ " invoke-virtual { p0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object p0",
+ " invoke-virtual { p0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " move-result-object v1",
+ " return-object v1"
+ );
+
+ builder.addMainMethod(
+ 4,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " new-instance v1, Ljava/lang/StringBuilder;",
+ " invoke-direct { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+ " const-string v2, \"TestX\"",
+ " const-string v3, \"TestY\"",
+ " invoke-static { v1, v2, v3 }, LTest;->method(Ljava/lang/StringBuilder;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+ " move-result-object v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ DexApplication originalApplication = buildApplication(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Return the processed method for inspection.
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+
+ DexCode code = method.getCode().asDexCode();
+ assertTrue(code.instructions[0] instanceof ConstString);
+ assertTrue(code.instructions[1] instanceof ConstString);
+ assertTrue(code.instructions[2] instanceof InvokeStatic);
+ InvokeStatic invoke = (InvokeStatic) code.instructions[2];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ assertEquals("TestXTest1TestYTest2Test3", result);
+ }
+
+ @Test
+ public void longArguments() throws Throwable {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String returnType = "java.lang.String";
+ List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 3,
+ " move v0, p0",
+ " const-wide v1, 0x7fffffff00000000L",
+ " invoke-virtual { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " move-result-object v0",
+ " return-object v0"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " new-instance v1, Ljava/lang/StringBuilder;",
+ " invoke-direct { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+ " invoke-static { v1 }, LTest;->method(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+ " move-result-object v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ for (int i = 2; i < 4; i++) {
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = i;
+ options.outline.maxSize = i;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Return the processed method for inspection.
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+
+ DexCode code = method.getCode().asDexCode();
+ assertTrue(code.instructions[0] instanceof ConstWide);
+ if (i < 3) {
+ assertTrue(code.instructions[1] instanceof InvokeStatic);
+ InvokeStatic invoke = (InvokeStatic) code.instructions[1];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ } else {
+ assertTrue(code.instructions[1] instanceof InvokeVirtual);
+ assertTrue(code.instructions[2] instanceof InvokeVirtual);
+ assertTrue(code.instructions[3] instanceof InvokeStatic);
+ InvokeStatic invoke = (InvokeStatic) code.instructions[3];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ }
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ StringBuilder resultBuilder = new StringBuilder();
+ for (int j = 0; j < 4; j++) {
+ resultBuilder.append(0x7fffffff00000000L);
+ }
+ assertEquals(resultBuilder.toString(), result);
+ }
+ }
+
+ @Test
+ public void doubleArguments() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String returnType = "java.lang.String";
+ List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 3,
+ " move v0, p0",
+ " const-wide v1, 0x3ff0000000000000L",
+ " invoke-virtual { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(D)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(D)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(D)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(D)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " move-result-object v0",
+ " return-object v0"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " new-instance v1, Ljava/lang/StringBuilder;",
+ " invoke-direct { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+ " invoke-static { v1 }, LTest;->method(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+ " move-result-object v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ for (int i = 2; i < 4; i++) {
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = i;
+ options.outline.maxSize = i;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Return the processed method for inspection.
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+
+ DexCode code = method.getCode().asDexCode();
+ assertTrue(code.instructions[0] instanceof ConstWideHigh16);
+ if (i < 3) {
+ assertTrue(code.instructions[1] instanceof InvokeStatic);
+ InvokeStatic invoke = (InvokeStatic) code.instructions[1];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ } else {
+ assertTrue(code.instructions[1] instanceof InvokeVirtual);
+ assertTrue(code.instructions[2] instanceof InvokeVirtual);
+ assertTrue(code.instructions[3] instanceof InvokeStatic);
+ InvokeStatic invoke = (InvokeStatic) code.instructions[3];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ }
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ StringBuilder resultBuilder = new StringBuilder();
+ for (int j = 0; j < 4; j++) {
+ resultBuilder.append(1.0d);
+ }
+ assertEquals(resultBuilder.toString(), result);
+ }
+ }
+
+ @Test
+ public void invokeStatic() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String returnType = "void";
+ List<String> parameters = ImmutableList.of("java.lang.StringBuilder", "int");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 1,
+ " invoke-virtual { p0, p1 }, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { p0, p1 }, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " return-void"
+ );
+
+ MethodSignature mainSignature = builder.addMainMethod(
+ 2,
+ " new-instance v0, Ljava/lang/StringBuilder;",
+ " invoke-direct { v0 }, Ljava/lang/StringBuilder;-><init>()V",
+ " const/4 v1, 0x1",
+ " invoke-static { v0, v1 }, LTest;->method(Ljava/lang/StringBuilder;I)V",
+ " const/4 v1, 0x2",
+ " invoke-static { v0, v1 }, LTest;->method(Ljava/lang/StringBuilder;I)V",
+ " invoke-virtual { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " move-result-object v1",
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ for (int i = 2; i < 6; i++) {
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = i;
+ options.outline.maxSize = i;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Return the processed main method for inspection.
+ DexEncodedMethod mainMethod = getMethod(processedApplication, mainSignature);
+ DexCode mainCode = mainMethod.getCode().asDexCode();
+
+ if (i == 2 || i == 3) {
+ assert mainCode.instructions.length == 10;
+ } else if (i == 4) {
+ assert mainCode.instructions.length == 9;
+ } else {
+ assert i == 5;
+ assert mainCode.instructions.length == 7;
+ }
+ if (i == 2) {
+ InvokeStatic invoke = (InvokeStatic) mainCode.instructions[4];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ } else if (i == 3) {
+ InvokeStatic invoke = (InvokeStatic) mainCode.instructions[1];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ } else {
+ assert i == 4 || i == 5;
+ InvokeStatic invoke = (InvokeStatic) mainCode.instructions[2];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ }
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ assertEquals("1122", result);
+ }
+ }
+
+ @Test
+ public void constructor() throws IOException, RecognitionException {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ MethodSignature signature1 = builder.addStaticMethod(
+ "java.lang.String",
+ "method1",
+ Collections.emptyList(),
+ 3,
+ " new-instance v0, Ljava/lang/StringBuilder;",
+ " invoke-direct { v0 }, Ljava/lang/StringBuilder;-><init>()V",
+ " const-string v1, \"Test1\"",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " move-result-object v0",
+ " return-object v0"
+ );
+
+ MethodSignature signature2 = builder.addStaticMethod(
+ "java.lang.String",
+ "method2",
+ Collections.emptyList(),
+ 3,
+ " const/4 v1, 7",
+ " new-instance v0, Ljava/lang/StringBuilder;",
+ " invoke-direct { v0, v1 }, Ljava/lang/StringBuilder;-><init>(I)V",
+ " const-string v1, \"Test2\"",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " move-result-object v0",
+ " return-object v0"
+ );
+
+ MethodSignature mainSignature = builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " invoke-static {}, LTest;->method1()Ljava/lang/String;",
+ " move-result-object v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " invoke-static {}, LTest;->method2()Ljava/lang/String;",
+ " move-result-object v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = 7;
+ options.outline.maxSize = 7;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ DexCode code1 = getMethod(processedApplication, signature1).getCode().asDexCode();
+ assertEquals(4, code1.instructions.length);
+ assertTrue(code1.instructions[1] instanceof InvokeStatic);
+ InvokeStatic invoke1 = (InvokeStatic) code1.instructions[1];
+ assertTrue(isOutlineMethodName(options, invoke1.getMethod().qualifiedName()));
+
+ DexCode code2 = getMethod(processedApplication, signature2).getCode().asDexCode();
+ assertEquals(5, code2.instructions.length);
+ assertTrue(code2.instructions[2] instanceof InvokeStatic);
+ InvokeStatic invoke2 = (InvokeStatic) code2.instructions[2];
+ assertTrue(isOutlineMethodName(options, invoke1.getMethod().qualifiedName()));
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ assertEquals("Test1Test1Test1Test1Test2Test2Test2Test2", result);
+ }
+
+ @Test
+ public void constructorDontSplitNewInstanceAndInit() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ MethodSignature signature = builder.addStaticMethod(
+ "java.lang.String",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("java.lang.StringBuilder"),
+ 2,
+ " const-string v0, \"Test1\"",
+ " invoke-virtual { p0, v0 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object p0",
+ " invoke-virtual { p0, v0 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object p0",
+ " new-instance v1, Ljava/lang/StringBuilder;",
+ " invoke-direct { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+ " const-string v0, \"Test2\"",
+ " invoke-virtual { v1, v0 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v1",
+ " invoke-virtual { v1, v0 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v1",
+ " invoke-virtual { v1 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " move-result-object v0",
+ " return-object v0"
+ );
+
+ MethodSignature mainSignature = builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " new-instance v1, Ljava/lang/StringBuilder;",
+ " invoke-direct { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+ " invoke-static { v1 }, LTest;->method(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+ " move-result-object v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ for (int i = 2; i < 8; i++) {
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = i;
+ options.outline.maxSize = i;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ DexCode code = getMethod(processedApplication, signature).getCode().asDexCode();
+ InvokeStatic invoke;
+ int outlineInstructionIndex;
+ switch (i) {
+ case 2:
+ case 4:
+ outlineInstructionIndex = 1;
+ break;
+ case 3:
+ outlineInstructionIndex = 4;
+ break;
+ default:
+ outlineInstructionIndex = 2;
+ }
+ invoke = (InvokeStatic) code.instructions[outlineInstructionIndex];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ assertEquals("Test2Test2", result);
+ }
+ }
+
+ @Test
+ public void outlineWithoutArguments() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ MethodSignature signature1 = builder.addStaticMethod(
+ "java.lang.String",
+ DEFAULT_METHOD_NAME,
+ Collections.emptyList(),
+ 1,
+ " new-instance v0, Ljava/lang/StringBuilder;",
+ " invoke-direct { v0 }, Ljava/lang/StringBuilder;-><init>()V",
+ " invoke-virtual { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " move-result-object v0",
+ " return-object v0"
+ );
+
+ MethodSignature mainSignature = builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " invoke-static {}, LTest;->method()Ljava/lang/String;",
+ " move-result-object v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = 3;
+ options.outline.maxSize = 3;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ DexCode code = getMethod(processedApplication, signature1).getCode().asDexCode();
+ InvokeStatic invoke;
+ assertTrue(code.instructions[0] instanceof InvokeStatic);
+ invoke = (InvokeStatic) code.instructions[0];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ assertEquals("", result);
+ }
+
+ @Test
+ public void outlineDifferentReturnType() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+
+ // The naming of the methods in this test is important. The method name that don't use the
+ // output from StringBuilder.toString must sort before the method name that does.
+ String returnType1 = "void";
+ MethodSignature signature1 = builder.addStaticMethod(
+ returnType1,
+ "method1",
+ parameters,
+ 2,
+ " move v0, p0",
+ " const-string v1, \"Test\"",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " return-void"
+ );
+
+ String returnType2 = "java.lang.String";
+ MethodSignature signature2 = builder.addStaticMethod(
+ returnType2,
+ "method2",
+ parameters,
+ 2,
+ " move v0, p0",
+ " const-string v1, \"Test\"",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " move-result-object v0",
+ " return-object v0"
+ );
+
+ builder.addMainMethod(
+ 3,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " new-instance v1, Ljava/lang/StringBuilder;",
+ " invoke-direct { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+ " invoke-static { v1 }, LTest;->method1(Ljava/lang/StringBuilder;)V",
+ " invoke-static { v1 }, LTest;->method2(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+ " move-result-object v2",
+ " invoke-virtual { v0, v2 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = 3;
+ options.outline.maxSize = 3;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Check that three outlining methods was created.
+ DexInspector inspector = new DexInspector(processedApplication);
+ ClassSubject clazz = inspector.clazz(options.outline.className);
+ assertTrue(clazz.isPresent());
+ assertEquals(3, clazz.getDexClass().directMethods.length);
+ // Collect the return types of the putlines for the body of method1 and method2.
+ List<DexType> r = new ArrayList<>();
+ for (int i = 0; i < clazz.getDexClass().directMethods.length; i++) {
+ if (clazz.getDexClass().directMethods[i].getCode().asDexCode().instructions[0]
+ instanceof InvokeVirtual) {
+ r.add(clazz.getDexClass().directMethods[i].method.proto.returnType);
+ }
+ }
+ assert r.size() == 2;
+ DexType r1 = r.get(0);
+ DexType r2 = r.get(1);
+ DexItemFactory factory = processedApplication.dexItemFactory;
+ assertTrue(r1 == factory.voidType && r2 == factory.stringType ||
+ r1 == factory.stringType && r2 == factory.voidType);
+
+ // Run the code.
+ String result = runArt(processedApplication, options);
+ assertEquals("TestTestTestTest", result);
+ }
+
+ @Test
+ public void outlineMultipleTimes() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ String returnType = "java.lang.String";
+ List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+ MethodSignature signature = builder.addStaticMethod(
+ returnType,
+ DEFAULT_METHOD_NAME,
+ parameters,
+ 2,
+ " move v0, p0",
+ " const-string v1, \"Test\"",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ " move-result-object v0",
+ " invoke-virtual { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+ " move-result-object v0",
+ " return-object v0"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " new-instance v1, Ljava/lang/StringBuilder;",
+ " invoke-direct { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+ " invoke-static { v1 }, LTest;->method(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+ " move-result-object v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = 3;
+ options.outline.maxSize = 3;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ final int count = 10;
+ // Process the application several times. Each time will outline the previous outline.
+ for (int i = 0; i < count; i++) {
+ // Build a new application with the Outliner class.
+ DexApplication.Builder appBuilder =
+ new DexApplication.Builder(processedApplication);
+ originalApplication = appBuilder.build();
+ processedApplication = processApplication(originalApplication, options);
+ assertEquals(i + 3, Iterables.size(processedApplication.classes()));
+ }
+
+ // Process the application several times. No more outlining as threshold has been raised.
+ options.outline.threshold = 2;
+ for (int i = 0; i < count; i++) {
+ // Build a new application with the Outliner class.
+ DexApplication.Builder appBuilder =
+ new DexApplication.Builder(processedApplication);
+ originalApplication = appBuilder.build();
+ processedApplication = processApplication(originalApplication, options);
+ assertEquals(count - 1 + 3, Iterables.size(processedApplication.classes()));
+ }
+
+ // Run the application with several levels of outlining.
+ String result = runArt(processedApplication, options);
+ assertEquals("TestTestTestTest", result);
+ }
+
+ @Test
+ public void outlineReturnLong() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ MethodSignature signature = builder.addStaticMethod(
+ "long",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int"),
+ 2,
+ " new-instance v0, Ljava/util/GregorianCalendar;",
+ " invoke-direct { v0 }, Ljava/util/GregorianCalendar;-><init>()V",
+ " invoke-virtual { v0, p0, p0 }, Ljava/util/Calendar;->set(II)V",
+ " invoke-virtual { v0, p0, p0 }, Ljava/util/Calendar;->set(II)V",
+ " invoke-virtual { v0 }, Ljava/util/Calendar;->getTimeInMillis()J",
+ " move-result-wide v0",
+ " return-wide v0"
+ );
+
+ builder.addMainMethod(
+ 3,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, 0",
+ " invoke-static { v1 }, LTest;->method(I)J",
+ " move-result-wide v1",
+ " invoke-virtual { v0, v1, v2 }, Ljava/io/PrintStream;->print(J)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = 5;
+ options.outline.maxSize = 5;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Return the processed method for inspection.
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+ // The calls to set, set and getTimeInMillis was outlined.
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(3, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof InvokeStatic);
+ assertTrue(code.instructions[1] instanceof MoveResultWide);
+ assertTrue(code.instructions[2] instanceof ReturnWide);
+ InvokeStatic invoke = (InvokeStatic) code.instructions[0];
+ assertEquals(firstOutlineMethodName(options), invoke.getMethod().qualifiedName());
+
+ // Run the code and expect a parsable long.
+ String result = runArt(processedApplication, options);
+ Long.parseLong(result);
+ }
+
+ @Test
+ public void outlineArrayType() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ builder.addStaticMethod(
+ "void",
+ "addToList",
+ ImmutableList.of("java.util.List", "int[]"),
+ 0,
+ " invoke-interface { p0, p1 }, Ljava/util/List;->add(Ljava/lang/Object;)Z",
+ " return-void "
+ );
+
+ MethodSignature signature = builder.addStaticMethod(
+ "int[]",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int[]", "int[]"),
+ 1,
+ " new-instance v0, Ljava/util/ArrayList;",
+ " invoke-direct { v0 }, Ljava/util/ArrayList;-><init>()V",
+ " invoke-static { v0, p0 }, LTest;->addToList(Ljava/util/List;[I)V",
+ " invoke-static { v0, p1 }, LTest;->addToList(Ljava/util/List;[I)V",
+ " return-object p0"
+ );
+
+ builder.addMainMethod(
+ 3,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, 0",
+ " invoke-static { v1, v1 }, LTest;->method([I[I)[I",
+ " move-result-object v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = 4;
+ options.outline.maxSize = 4;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Return the processed method for inspection.
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof InvokeStatic);
+ assertTrue(code.instructions[1] instanceof ReturnObject);
+ InvokeStatic invoke = (InvokeStatic) code.instructions[0];
+ assertEquals(firstOutlineMethodName(options), invoke.getMethod().qualifiedName());
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ assertEquals("null", result);
+ }
+
+ @Test
+ public void outlineArithmeticBinop() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ builder.addStaticMethod(
+ "void",
+ "addToList",
+ ImmutableList.of("java.util.List", "int[]"),
+ 0,
+ " invoke-interface { p0, p1 }, Ljava/util/List;->add(Ljava/lang/Object;)Z",
+ " return-void "
+ );
+
+ MethodSignature signature1 = builder.addStaticMethod(
+ "int",
+ "method1",
+ ImmutableList.of("int", "int"),
+ 1,
+ " add-int v0, v1, v2",
+ " sub-int v0, v0, v1",
+ " mul-int v0, v2, v0",
+ " div-int v0, v0, v1",
+ " return v0"
+ );
+
+ MethodSignature signature2 = builder.addStaticMethod(
+ "int",
+ "method2",
+ ImmutableList.of("int", "int"),
+ 1,
+ " add-int v0, v2, v1",
+ " sub-int v0, v0, v1",
+ " mul-int v0, v0, v2",
+ " div-int v0, v0, v1",
+ " return v0"
+ );
+
+ builder.addMainMethod(
+ 4,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, 1",
+ " const/4 v2, 2",
+ " invoke-static { v1, v2 }, LTest;->method1(II)I",
+ " move-result v4",
+ " invoke-virtual { v0, v4 }, Ljava/io/PrintStream;->print(I)V",
+ " invoke-static { v1, v2 }, LTest;->method2(II)I",
+ " move-result v4",
+ " invoke-virtual { v0, v4 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = 4;
+ options.outline.maxSize = 4;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Return the processed method for inspection.
+ DexEncodedMethod method1 = getMethod(processedApplication, signature1);
+ DexCode code1 = method1.getCode().asDexCode();
+ assertEquals(3, code1.instructions.length);
+ assertTrue(code1.instructions[0] instanceof InvokeStatic);
+ assertTrue(code1.instructions[1] instanceof MoveResult);
+ assertTrue(code1.instructions[2] instanceof Return);
+ InvokeStatic invoke1 = (InvokeStatic) code1.instructions[0];
+ assertTrue(isOutlineMethodName(options, invoke1.getMethod().qualifiedName()));
+
+ DexEncodedMethod method2 = getMethod(processedApplication, signature2);
+ DexCode code2 = method2.getCode().asDexCode();
+ assertTrue(code2.instructions[0] instanceof InvokeStatic);
+ InvokeStatic invoke2 = (InvokeStatic) code2.instructions[0];
+ assertEquals(invoke1.getMethod().qualifiedName(), invoke2.getMethod().qualifiedName());
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ assertEquals("44", result);
+ }
+
+ @Test
+ public void outlineWithHandler() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ builder.addStaticMethod(
+ "void",
+ "addToList",
+ ImmutableList.of("java.util.List", "int[]"),
+ 0,
+ " invoke-interface { p0, p1 }, Ljava/util/List;->add(Ljava/lang/Object;)Z",
+ " return-void "
+ );
+
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ "method",
+ ImmutableList.of("int", "int"),
+ 1,
+ " :try_start",
+ // Throwing instruction to ensure the handler range does not get collapsed.
+ " div-int v0, v1, v2",
+ " add-int v0, v1, v2",
+ " sub-int v0, v0, v1",
+ " mul-int v0, v2, v0",
+ " div-int v0, v0, v1",
+ " :try_end",
+ " :return",
+ " return v0",
+ " .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+ " :catch",
+ " const/4 v0, -1",
+ " goto :return"
+ );
+
+ builder.addMainMethod(
+ 4,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, 1",
+ " const/4 v2, 2",
+ " invoke-static { v1, v2 }, LTest;->method(II)I",
+ " move-result v4",
+ " invoke-virtual { v0, v4 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = 3; // Outline add, sub and mul.
+ options.outline.maxSize = 3;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Return the processed method for inspection.
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(7, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof DivInt);
+ assertTrue(code.instructions[1] instanceof InvokeStatic);
+ assertTrue(code.instructions[2] instanceof MoveResult);
+ assertTrue(code.instructions[3] instanceof DivInt);
+ assertTrue(code.instructions[4] instanceof Return);
+ assertTrue(code.instructions[5] instanceof Const4);
+ assertTrue(code.instructions[6] instanceof Return);
+ InvokeStatic invoke = (InvokeStatic) code.instructions[1];
+ assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ assertEquals("4", result);
+ }
+
+ @Test
+ public void outlineUnusedOutValue() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ // The result from neither the div-int is never used.
+ MethodSignature signature = builder.addStaticMethod(
+ "void",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int", "int"),
+ 1,
+ " div-int v0, p0, p1",
+ " new-instance v0, Ljava/lang/StringBuilder;",
+ " invoke-direct { v0 }, Ljava/lang/StringBuilder;-><init>()V",
+ " return-void"
+ );
+
+ builder.addMainMethod(
+ 2,
+ " const v0, 1",
+ " const v1, 2",
+ " invoke-static { v0, v1 }, LTest;->method(II)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = 3;
+ options.outline.maxSize = 3;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Return the processed method for inspection.
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof InvokeStatic);
+ assertTrue(code.instructions[1] instanceof ReturnVoid);
+ InvokeStatic invoke = (InvokeStatic) code.instructions[0];
+ assertEquals(firstOutlineMethodName(options), invoke.getMethod().qualifiedName());
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ assertEquals("", result);
+ }
+
+ @Test
+ public void outlineUnusedNewInstanceOutValue() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ // The result from the new-instance instructions are never used (<init> is not even called).
+ MethodSignature signature = builder.addStaticMethod(
+ "void",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of(),
+ 1,
+ " new-instance v0, Ljava/lang/StringBuilder;",
+ " new-instance v0, Ljava/lang/StringBuilder;",
+ " new-instance v0, Ljava/lang/StringBuilder;",
+ " return-void"
+ );
+
+ builder.addMainMethod(
+ 0,
+ " invoke-static { }, LTest;->method()V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 1;
+ options.outline.minSize = 3;
+ options.outline.maxSize = 3;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Return the processed method for inspection.
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+ DexCode code = method.getCode().asDexCode();
+ assertEquals(2, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof InvokeStatic);
+ assertTrue(code.instructions[1] instanceof ReturnVoid);
+ InvokeStatic invoke = (InvokeStatic) code.instructions[0];
+ assertEquals(firstOutlineMethodName(options), invoke.getMethod().qualifiedName());
+
+ // Run code and check result.
+ String result = runArt(processedApplication, options);
+ assertEquals("", result);
+ }
+
+ @Test
+ public void regress33733666() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ builder.addStaticMethod(
+ "void",
+ DEFAULT_METHOD_NAME,
+ Collections.emptyList(),
+ 4,
+ " new-instance v0, Ljava/util/Hashtable;",
+ " invoke-direct { v0 }, Ljava/util/Hashtable;-><init>()V",
+ " const-string v1, \"http://schemas.google.com/g/2005#home\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x01 # 1",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \" http://schemas.google.com/g/2005#work\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x02 # 2",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#other\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x03 # 3",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#primary\"",
+ " const/4 v2, 0x04 # 4",
+ " invoke-static { v2 }, Ljava/lang/Byte;->valueOf(B)Ljava/lang/Byte;",
+ " move-result-object v2",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " sput-object v0, LA;->A:Ljava/util/Hashtable;",
+ " invoke-static { v0 }, LA;->a(Ljava/util/Hashtable;)Ljava/util/Hashtable;",
+ " move-result-object v0",
+ " sput-object v0, LA;->B:Ljava/util/Hashtable;",
+ " new-instance v0, Ljava/util/Hashtable;",
+ " invoke-direct { v0 }, Ljava/util/Hashtable;-><init>()V",
+ " const-string v1, \"http://schemas.google.com/g/2005#home\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x02 # 2",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#mobile\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x01 # 1",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#pager\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x06 # 6",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#work\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x03 # 3",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#home_fax\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x05 # 5",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#work_fax\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x04 # 4",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#other\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x07 # 7",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " sput-object v0, LA;->C:Ljava/util/Hashtable;",
+ " invoke-static { v0 }, LA;->a(Ljava/util/Hashtable;)Ljava/util/Hashtable;",
+ " move-result-object v0",
+ " sput-object v0, LA;->D:Ljava/util/Hashtable;",
+ " new-instance v0, Ljava/util/Hashtable;",
+ " invoke-direct { v0 }, Ljava/util/Hashtable;-><init>()V",
+ " const-string v1, \"http://schemas.google.com/g/2005#home\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x01 # 1",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#work\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x02 # 2",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#other\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x03 # 3",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " sput-object v0, LA;->E:Ljava/util/Hashtable;",
+ " invoke-static { v0 }, LA;->a(Ljava/util/Hashtable;)Ljava/util/Hashtable;",
+ " move-result-object v0",
+ " sput-object v0, LA;->F:Ljava/util/Hashtable;",
+ " new-instance v0, Ljava/util/Hashtable;",
+ " invoke-direct { v0 }, Ljava/util/Hashtable;-><init>()V",
+ " const-string v1, \"http://schemas.google.com/g/2005#home\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x01 # 1",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#work\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x02 # 2",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#other\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x03 # 3",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " sput-object v0, LA;->G:Ljava/util/Hashtable;",
+ " invoke-static { v0 }, LA;->a(Ljava/util/Hashtable;)Ljava/util/Hashtable;",
+ " move-result-object v0",
+ " sput-object v0, LA;->H:Ljava/util/Hashtable;",
+ " new-instance v0, Ljava/util/Hashtable;",
+ " invoke-direct { v0 }, Ljava/util/Hashtable;-><init>()V",
+ " const-string v1, \"http://schemas.google.com/g/2005#work\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x01 # 1",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#other\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x02 # 2",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " sput-object v0, LA;->I:Ljava/util/Hashtable;",
+ " invoke-static { v0 }, LA;->a(Ljava/util/Hashtable;)Ljava/util/Hashtable;",
+ " move-result-object v0",
+ " sput-object v0, LA;->J:Ljava/util/Hashtable;",
+ " new-instance v0, Ljava/util/Hashtable;",
+ " invoke-direct { v0 }, Ljava/util/Hashtable;-><init>()V",
+ " const-string v1, \"http://schemas.google.com/g/2005#AIM\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x02 # 2",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#MSN\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x03 # 3",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#YAHOO\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x04 # 4",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#SKYPE\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x05 # 5",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#QQ\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x06 # 6",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#GOOGLE_TALK\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/4 v3, 0x07 # 7",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#ICQ\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/16 v3, 0x0008 # 8",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " const-string v1, \"http://schemas.google.com/g/2005#JABBER\"",
+ " new-instance v2, Ljava/lang/Byte;",
+ " const/16 v3, 0x0009 # 9",
+ " invoke-direct { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+ " invoke-virtual { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ " sput-object v0, LA;->K:Ljava/util/Hashtable;",
+ " invoke-static { v0 }, LA;->a(Ljava/util/Hashtable;)Ljava/util/Hashtable;",
+ " move-result-object v0",
+ " sput-object v0, LA;->L:Ljava/util/Hashtable;",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ options.outline.threshold = 2;
+
+ DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, Iterables.size(processedApplication.classes()));
+
+ // Verify the code.
+ runDex2Oat(processedApplication, options);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/Regress38014736.java b/src/test/java/com/android/tools/r8/smali/Regress38014736.java
new file mode 100644
index 0000000..91d8023
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/Regress38014736.java
@@ -0,0 +1,58 @@
+// 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.smali;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.utils.InternalOptions;
+import org.junit.Test;
+
+public class Regress38014736 extends SmaliTestBase {
+
+ @Test
+ public void handlerRangeStartingOnMoveResult() {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ builder.addMainMethod(
+ 2,
+ "new-instance v1, Ljava/lang/Integer;",
+ "const/4 v0, 0x00",
+ "invoke-direct { v1, v0 }, Ljava/lang/Integer;-><init>(I)V",
+ "invoke-virtual { v1 }, Ljava/lang/Integer;->intValue()I",
+ "move-result v1",
+ "invoke-static { v1 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
+ "move-result-object v1",
+ "invoke-virtual { v1 }, Ljava/lang/Integer;->intValue()I",
+ ":try_start", // The try block starts on the move result instruction.
+ "move-result v1",
+ "sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ "invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
+ "const/4 v1, 0x00 # 0",
+ "invoke-static { v1 }, "
+ + "Ljava/lang/Integer;->valueOf(Ljava/lang/String;)Ljava/lang/Integer;",
+ "move-result-object v1",
+ "invoke-virtual { v1 }, Ljava/lang/Integer;->intValue()I",
+ ":try_end", // The try block ends on the move result instruction.
+ "move-result v1",
+ "sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ "invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
+ ".catch Ljava/lang/NumberFormatException; {:try_start .. :try_end} :catch_block",
+ "return-void",
+ ":catch_block",
+ "move-exception v1",
+ "sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ "invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V",
+ "return-void");
+
+ InternalOptions options = new InternalOptions();
+ DexApplication originalApplication = buildApplication(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ String result = runArt(processedApplication, options);
+ // The art runtime changed the way exceptions are printed. Therefore, we only check
+ // for the type of the exception and that the message mentions null.
+ assertTrue(result.startsWith("0\njava.lang.NumberFormatException:"));
+ assertTrue(result.contains("null"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java b/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java
new file mode 100644
index 0000000..45cd8cd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.code.ConstString;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.code.SgetObject;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Iterables;
+import org.junit.Test;
+
+public class RunArtSmokeTest extends SmaliTestBase {
+
+ @Test
+ public void test() {
+ // Build simple "Hello, world!" application.
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+ MethodSignature mainSignature = builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const-string v1, \"Hello, world!\"",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication originalApplication = buildApplication(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ assertEquals(1, Iterables.size(processedApplication.classes()));
+
+ // Return the processed method for inspection.
+ DexEncodedMethod main = getMethod(processedApplication, mainSignature);
+ assert main != null;
+
+ DexCode code = main.getCode().asDexCode();
+ assertTrue(code.instructions[0] instanceof SgetObject);
+ assertTrue(code.instructions[1] instanceof ConstString);
+ assertTrue(code.instructions[2] instanceof InvokeVirtual);
+ assertTrue(code.instructions[3] instanceof ReturnVoid);
+
+ // Run the generated code in Art.
+ String result = runArt(processedApplication, options);
+ assertEquals("Hello, world!\n", result);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
new file mode 100644
index 0000000..edcd454
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.InternalOptions;
+import java.io.IOException;
+import java.nio.file.Paths;
+import org.antlr.runtime.RecognitionException;
+import org.junit.Test;
+
+public class SmaliBuildTest extends SmaliTestBase {
+
+ private void checkJavaLangString(DexApplication application, boolean present) {
+ DexInspector inspector = new DexInspector(application);
+ ClassSubject clazz = inspector.clazz("java.lang.String");
+ assertEquals(present, clazz.isPresent());
+ }
+
+ @Test
+ public void buildWithoutLibrary() {
+ // Build simple "Hello, world!" application.
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const-string v1, \"Hello, world!\"",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ // No libraries added - java.lang.String is not present.
+ DexApplication originalApplication = buildApplication(builder, options);
+ checkJavaLangString(originalApplication, false);
+
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ checkJavaLangString(processedApplication, false);
+ }
+
+ @Test
+ public void buildWithLibrary() throws IOException, RecognitionException {
+ // Build simple "Hello, world!" application.
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const-string v1, \"Hello, world!\"",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ AndroidApp app = AndroidApp.builder()
+ .addDexProgramData(builder.compile())
+ .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+ .build();
+
+ // Java standard library added - java.lang.String is present.
+ DexApplication originalApplication = buildApplication(app, options);
+ checkJavaLangString(originalApplication, true);
+
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ checkJavaLangString(processedApplication, true);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
new file mode 100644
index 0000000..b1fd38f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
@@ -0,0 +1,371 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Smali;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.concurrent.ExecutionException;
+import org.antlr.runtime.RecognitionException;
+import org.junit.Test;
+
+public class SmaliDisassembleTest extends SmaliTestBase {
+
+ // Run the provided smali through R8 smali disassembler and expect the exact same output.
+ void roundTripRawSmali(String smali) {
+ try {
+ DexApplication application =
+ new ApplicationReader(
+ AndroidApp.fromDexProgramData(Smali.compile(smali)),
+ new InternalOptions(),
+ new Timing("SmaliTest"))
+ .read();
+ assertEquals(smali, application.smali(new InternalOptions()));
+ } catch (IOException | RecognitionException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void simpleSmokeTest() {
+ DexApplication application = singleMethodApplication(
+ "int", Collections.singletonList("int"),
+ 4,
+ " const/4 v0, 1 ",
+ " const/16 v1, 2 ",
+ " const v2, 3 ",
+ " const-wide v3, -1 ",
+ " add-int/2addr v1, v0 ",
+ " mul-int/2addr v2, v1 ",
+ " div-int/2addr v3, v2 ",
+ " return v3\n "
+ );
+
+ String expected =
+ ".class public LTest;\n" +
+ "\n" +
+ ".super Ljava/lang/Object;\n" +
+ "\n" +
+ ".method public static method(I)I\n" +
+ " .registers 5\n" +
+ "\n" +
+ " const/4 v0, 0x01 # 1\n" +
+ " const/16 v1, 0x0002 # 2\n" +
+ " const v2, 0x00000003 # 3\n" +
+ " const-wide v3, 0xffffffffffffffffL # -1\n" +
+ " add-int/2addr v1, v0\n" +
+ " mul-int/2addr v2, v1\n" +
+ " div-int/2addr v3, v2\n" +
+ " return v3\n" +
+ ".end method\n";
+
+ assertEquals(expected, application.smali(new InternalOptions()));
+
+ roundTripRawSmali(expected);
+ }
+
+ @Test
+ public void sparseSwitchTest() {
+ DexApplication application = singleMethodApplication(
+ "int", Collections.singletonList("int"),
+ 0,
+ " sparse-switch v0, :sparse_switch_data",
+ " const/4 v0, 0x0",
+ " goto :return",
+ " :case_1",
+ " const/4 v0, 0x1",
+ " goto :return",
+ " :case_2",
+ " const/4 v0, 0x2",
+ " :return",
+ " return v0",
+ " :sparse_switch_data ",
+ " .sparse-switch ",
+ " 0x1 -> :case_1 ",
+ " 0x2 -> :case_2 ",
+ " .end sparse-switch "
+
+ );
+
+ String expected =
+ ".class public LTest;\n" +
+ "\n" +
+ ".super Ljava/lang/Object;\n" +
+ "\n" +
+ ".method public static method(I)I\n" +
+ " .registers 1\n" +
+ "\n" +
+ " sparse-switch v0, :label_10\n" +
+ " const/4 v0, 0x00 # 0\n" +
+ " goto :label_8\n" +
+ " :label_5\n" +
+ " const/4 v0, 0x01 # 1\n" +
+ " goto :label_8\n" +
+ " :label_7\n" +
+ " const/4 v0, 0x02 # 2\n" +
+ " :label_8\n" +
+ " return v0\n" +
+ " nop\n" +
+ " :label_10\n" +
+ " .sparse-switch\n" +
+ " 0x00000001 -> :label_5 # 1\n" +
+ " 0x00000002 -> :label_7 # 2\n" +
+ " .end sparse-switch\n" +
+ ".end method\n";
+
+ assertEquals(expected, application.smali(new InternalOptions()));
+
+ roundTripRawSmali(expected);
+ }
+
+ @Test
+ public void packedSwitchTest() {
+ DexApplication application = singleMethodApplication(
+ "int", Collections.singletonList("int"),
+ 0,
+ " packed-switch v0, :packed_switch_data",
+ " const/4 v0, 0x0 ",
+ " goto :return ",
+ " :case_1 ",
+ " const/4 v0, 0x1 ",
+ " goto :return ",
+ " :case_2 ",
+ " const/4 v0, 0x2 ",
+ " :return ",
+ " return v0 ",
+ " :packed_switch_data ",
+ " .packed-switch 0x1 ",
+ " :case_1 ",
+ " :case_2 ",
+ " .end packed-switch "
+
+ );
+
+ String expected =
+ ".class public LTest;\n" +
+ "\n" +
+ ".super Ljava/lang/Object;\n" +
+ "\n" +
+ ".method public static method(I)I\n" +
+ " .registers 1\n" +
+ "\n" +
+ " packed-switch v0, :label_10\n" +
+ " const/4 v0, 0x00 # 0\n" +
+ " goto :label_8\n" +
+ " :label_5\n" +
+ " const/4 v0, 0x01 # 1\n" +
+ " goto :label_8\n" +
+ " :label_7\n" +
+ " const/4 v0, 0x02 # 2\n" +
+ " :label_8\n" +
+ " return v0\n" +
+ " nop\n" +
+ " :label_10\n" +
+ " .packed-switch 0x00000001 # 1\n" +
+ " :label_5\n" +
+ " :label_7\n" +
+ " .end packed-switch\n" +
+ ".end method\n";
+
+ assertEquals(expected, application.smali(new InternalOptions()));
+
+ roundTripRawSmali(expected);
+ }
+
+ @Test
+ public void fillArrayDataTest8Bit() {
+ DexApplication application = singleMethodApplication(
+ "int[]", ImmutableList.of(),
+ 2,
+ " const/4 v1, 3",
+ " new-array v0, v1, [I",
+ " fill-array-data v0, :array_data",
+ " return-object v0",
+ " :array_data",
+ " .array-data 1",
+ " 1 2 255",
+ " .end array-data"
+ );
+
+ String expected =
+ ".class public LTest;\n" +
+ "\n" +
+ ".super Ljava/lang/Object;\n" +
+ "\n" +
+ ".method public static method()[I\n" +
+ " .registers 2\n" +
+ "\n" +
+ " const/4 v1, 0x03 # 3\n" +
+ " new-array v0, v1, [I\n" +
+ " fill-array-data v0, :label_8\n" +
+ " return-object v0\n" +
+ " nop\n" +
+ " :label_8\n" +
+ " .array-data 0x1 # 1\n" +
+ " 0x01 # 1\n" +
+ " 0x02 # 2\n" +
+ " 0xff # 255\n" +
+ " .end array-data\n" +
+ ".end method\n";
+
+ assertEquals(expected, application.smali(new InternalOptions()));
+
+ roundTripRawSmali(expected);
+ }
+
+ @Test
+ public void fillArrayDataTest16Bit() {
+ DexApplication application = singleMethodApplication(
+ "int[]", ImmutableList.of(),
+ 2,
+ " const/4 v1, 3",
+ " new-array v0, v1, [I",
+ " fill-array-data v0, :array_data",
+ " return-object v0",
+ " :array_data",
+ " .array-data 2",
+ " 1 2 65535",
+ " .end array-data"
+ );
+
+ String expected =
+ ".class public LTest;\n" +
+ "\n" +
+ ".super Ljava/lang/Object;\n" +
+ "\n" +
+ ".method public static method()[I\n" +
+ " .registers 2\n" +
+ "\n" +
+ " const/4 v1, 0x03 # 3\n" +
+ " new-array v0, v1, [I\n" +
+ " fill-array-data v0, :label_8\n" +
+ " return-object v0\n" +
+ " nop\n" +
+ " :label_8\n" +
+ " .array-data 0x2 # 2\n" +
+ " 0x0001 # 1\n" +
+ " 0x0002 # 2\n" +
+ " 0xffff # 65535\n" +
+ " .end array-data\n" +
+ ".end method\n";
+
+ assertEquals(expected, application.smali(new InternalOptions()));
+
+ roundTripRawSmali(expected);
+ }
+
+ @Test
+ public void fillArrayDataTest32Bit() {
+ DexApplication application = singleMethodApplication(
+ "int[]", ImmutableList.of(),
+ 2,
+ " const/4 v1, 3",
+ " new-array v0, v1, [I",
+ " fill-array-data v0, :array_data",
+ " return-object v0",
+ " :array_data",
+ " .array-data 4",
+ " 1 2 4294967295",
+ " .end array-data"
+ );
+
+ String expected =
+ ".class public LTest;\n" +
+ "\n" +
+ ".super Ljava/lang/Object;\n" +
+ "\n" +
+ ".method public static method()[I\n" +
+ " .registers 2\n" +
+ "\n" +
+ " const/4 v1, 0x03 # 3\n" +
+ " new-array v0, v1, [I\n" +
+ " fill-array-data v0, :label_8\n" +
+ " return-object v0\n" +
+ " nop\n" +
+ " :label_8\n" +
+ " .array-data 0x4 # 4\n" +
+ " 0x00000001 # 1\n" +
+ " 0x00000002 # 2\n" +
+ " 0xffffffff # 4294967295\n" +
+ " .end array-data\n" +
+ ".end method\n";
+
+ assertEquals(expected, application.smali(new InternalOptions()));
+
+ roundTripRawSmali(expected);
+ }
+
+ @Test
+ public void fillArrayDataTest64Bit() {
+ DexApplication application = singleMethodApplication(
+ "int[]", ImmutableList.of(),
+ 2,
+ " const/4 v1, 3",
+ " new-array v0, v1, [I",
+ " fill-array-data v0, :array_data",
+ " return-object v0",
+ " :array_data",
+ " .array-data 8",
+ " 1 2 -1",
+ " .end array-data"
+ );
+
+ String expected =
+ ".class public LTest;\n" +
+ "\n" +
+ ".super Ljava/lang/Object;\n" +
+ "\n" +
+ ".method public static method()[I\n" +
+ " .registers 2\n" +
+ "\n" +
+ " const/4 v1, 0x03 # 3\n" +
+ " new-array v0, v1, [I\n" +
+ " fill-array-data v0, :label_8\n" +
+ " return-object v0\n" +
+ " nop\n" +
+ " :label_8\n" +
+ " .array-data 0x8 # 8\n" +
+ " 0x0000000000000001 # 1\n" +
+ " 0x0000000000000002 # 2\n" +
+ " 0xffffffffffffffff # -1\n" +
+ " .end array-data\n" +
+ ".end method\n";
+
+ assertEquals(expected, application.smali(new InternalOptions()));
+
+ roundTripRawSmali(expected);
+ }
+
+
+ @Test
+ public void interfaceClass() {
+ SmaliBuilder builder = new SmaliBuilder();
+ builder.addInterface("Test");
+ builder.addAbstractMethod("int", "test", ImmutableList.of());
+ DexApplication application = buildApplication(builder);
+ assertEquals(1, Iterables.size(application.classes()));
+
+ String expected =
+ ".class public interface abstract LTest;\n" +
+ "\n" +
+ ".super Ljava/lang/Object;\n" +
+ "\n" +
+ ".method public abstract test()I\n" +
+ ".end method\n";
+
+ assertEquals(expected, application.smali(new InternalOptions()));
+
+ roundTripRawSmali(expected);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
new file mode 100644
index 0000000..314adb1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -0,0 +1,490 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.R8;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+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.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Smali;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Iterables;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import org.antlr.runtime.RecognitionException;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+public class SmaliTestBase {
+
+ public static final String DEFAULT_CLASS_NAME = "Test";
+ public static final String DEFAULT_MAIN_CLASS_NAME = DEFAULT_CLASS_NAME;
+ public static final String DEFAULT_METHOD_NAME = "method";
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ public static class MethodSignature {
+
+ public final String clazz;
+ public final String name;
+ public final String returnType;
+ public final List<String> parameterTypes;
+
+ public MethodSignature(String clazz, String name, String returnType,
+ List<String> parameterTypes) {
+ this.clazz = clazz;
+ this.name = name;
+ this.returnType = returnType;
+ this.parameterTypes = parameterTypes;
+ }
+ }
+
+ public static class SmaliBuilder {
+
+ abstract class Builder {
+ String name;
+ String superName;
+ List<String> source = new ArrayList<>();
+
+ Builder(String name, String superName) {
+ this.name = name;
+ this.superName = superName;
+ }
+
+ protected void writeSource(StringBuilder builder) {
+ for (String sourceLine : source) {
+ builder.append(sourceLine);
+ builder.append("\n");
+ }
+ }
+ }
+
+ public class ClassBuilder extends Builder {
+
+ ClassBuilder(String name, String superName) {
+ super(name, superName);
+ }
+
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".class public ");
+ builder.append(DescriptorUtils.javaTypeToDescriptor(name));
+ builder.append("\n");
+ builder.append(".super ");
+ builder.append(DescriptorUtils.javaTypeToDescriptor(superName));
+ builder.append("\n\n");
+ writeSource(builder);
+ return builder.toString();
+ }
+ }
+
+ public class InterfaceBuilder extends Builder {
+
+ InterfaceBuilder(String name, String superName) {
+ super(name, superName);
+ }
+
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".class public interface abstract ");
+ builder.append(DescriptorUtils.javaTypeToDescriptor(name));
+ builder.append("\n");
+ builder.append(".super ");
+ builder.append(DescriptorUtils.javaTypeToDescriptor(superName));
+ builder.append("\n\n");
+ writeSource(builder);
+ return builder.toString();
+ }
+ }
+
+ private String currentClassName;
+ private final Map<String, Builder> classes = new HashMap<>();
+
+ public SmaliBuilder() {
+ // No default class.
+ }
+
+ public SmaliBuilder(String name) {
+ addClass(name);
+ }
+
+ public SmaliBuilder(String name, String superName) {
+ addClass(name, superName);
+ }
+
+ private List<String> getSource(String clazz) {
+ return classes.get(clazz).source;
+ }
+
+ public String getCurrentClassName() {
+ return currentClassName;
+ }
+
+ public String getCurrentClassDescriptor() {
+ return DescriptorUtils.javaTypeToDescriptor(currentClassName);
+ }
+
+ public void addClass(String name) {
+ addClass(name, "java.lang.Object");
+ }
+
+ public void addClass(String name, String superName) {
+ assert !classes.containsKey(name);
+ currentClassName = name;
+ classes.put(name, new ClassBuilder(name, superName));
+ }
+
+ public void addInterface(String name) {
+ addInterface(name, "java.lang.Object");
+ }
+
+ public void addInterface(String name, String superName) {
+ assert !classes.containsKey(name);
+ currentClassName = name;
+ classes.put(name, new InterfaceBuilder(name, superName));
+ }
+
+ public void addDefaultConstructor() {
+ String superDescriptor =
+ DescriptorUtils.javaTypeToDescriptor(classes.get(currentClassName).superName);
+ addMethodRaw(
+ " .method public constructor <init>()V",
+ " .locals 0",
+ " invoke-direct {p0}, " + superDescriptor + "-><init>()V",
+ " return-void",
+ " .end method"
+ );
+ }
+
+ public MethodSignature addStaticMethod(String returnType, String name, List<String> parameters,
+ int locals, String... instructions) {
+ StringBuilder builder = new StringBuilder();
+ for (String instruction : instructions) {
+ builder.append(instruction);
+ builder.append("\n");
+ }
+ return addStaticMethod(returnType, name, parameters, locals, builder.toString());
+ }
+
+ public MethodSignature addStaticMethod(String returnType, String name, List<String> parameters,
+ int locals, String code) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".method public static ");
+ builder.append(name);
+ builder.append("(");
+ for (String parameter : parameters) {
+ builder.append(DescriptorUtils.javaTypeToDescriptor(parameter));
+ }
+ builder.append(")");
+ builder.append(DescriptorUtils.javaTypeToDescriptor(returnType));
+ builder.append("\n");
+ builder.append(".locals ");
+ builder.append(locals);
+ builder.append("\n\n");
+ builder.append(code);
+ builder.append(".end method");
+ getSource(currentClassName).add(builder.toString());
+ return new MethodSignature(currentClassName, name, returnType, parameters);
+ }
+
+ public MethodSignature addAbstractMethod(
+ String returnType, String name, List<String> parameters) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".method public abstract ");
+ builder.append(name);
+ builder.append("(");
+ for (String parameter : parameters) {
+ builder.append(DescriptorUtils.javaTypeToDescriptor(parameter));
+ }
+ builder.append(")");
+ builder.append(DescriptorUtils.javaTypeToDescriptor(returnType));
+ builder.append("\n");
+ builder.append(".end method");
+ getSource(currentClassName).add(builder.toString());
+ return new MethodSignature(currentClassName, name, returnType, parameters);
+ }
+
+ public MethodSignature addMainMethod(int locals, String... instructions) {
+ return addStaticMethod(
+ "void", "main", Collections.singletonList("java.lang.String[]"), locals, instructions);
+ }
+
+ public void addMethodRaw(String... source) {
+ StringBuilder builder = new StringBuilder();
+ for (String line : source) {
+ builder.append(line);
+ builder.append("\n");
+ }
+ getSource(currentClassName).add(builder.toString());
+ }
+
+ public List<String> build() {
+ List<String> result = new ArrayList<>(classes.size());
+ for (String clazz : classes.keySet()) {
+ Builder classBuilder = classes.get(clazz);
+ result.add(classBuilder.toString());
+ }
+ return result;
+ }
+
+ public byte[] compile() throws IOException, RecognitionException {
+ return Smali.compile(build());
+ }
+
+ @Override
+ public String toString() {
+ return String.join("\n\n", build());
+ }
+ }
+
+ public class TestApplication {
+
+ public final DexApplication application;
+ public final DexEncodedMethod method;
+ public final IRCode code;
+ public final List<IRCode> additionalCode;
+ public final ValueNumberGenerator valueNumberGenerator;
+ public final InternalOptions options;
+
+ public TestApplication(
+ DexApplication application,
+ DexEncodedMethod method,
+ IRCode code,
+ ValueNumberGenerator valueNumberGenerator,
+ InternalOptions options) {
+ this(application, method, code, null, valueNumberGenerator, options);
+ }
+
+ public TestApplication(
+ DexApplication application,
+ DexEncodedMethod method,
+ IRCode code,
+ List<IRCode> additionalCode,
+ ValueNumberGenerator valueNumberGenerator,
+ InternalOptions options) {
+ this.application = application;
+ this.method = method;
+ this.code = code;
+ this.additionalCode = additionalCode;
+ this.valueNumberGenerator = valueNumberGenerator;
+ this.options = options;
+ }
+
+ public int countArgumentInstructions() {
+ int count = 0;
+ ListIterator<Instruction> iterator = code.blocks.get(0).listIterator();
+ while (iterator.next().isArgument()) {
+ count++;
+ }
+ return count;
+ }
+
+ public InstructionListIterator listIteratorAt(BasicBlock block, int index) {
+ InstructionListIterator iterator = block.listIterator();
+ for (int i = 0; i < index; i++) {
+ iterator.next();
+ }
+ return iterator;
+ }
+
+ public String run() {
+ AppInfo appInfo = new AppInfo(application);
+ IRConverter converter = new IRConverter(application, appInfo, options, null);
+ converter.replaceCodeForTesting(method, code);
+ return runArt(application, options);
+ }
+ }
+
+ protected DexApplication buildApplication(SmaliBuilder builder) {
+ return buildApplication(builder, new InternalOptions());
+ }
+
+ protected DexApplication buildApplication(SmaliBuilder builder, InternalOptions options) {
+ try {
+ return buildApplication(AndroidApp.fromDexProgramData(builder.compile()), options);
+ } catch (IOException | RecognitionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected DexApplication buildApplicationWithAndroidJar(
+ SmaliBuilder builder, InternalOptions options) {
+ try {
+ AndroidApp input = AndroidApp.builder()
+ .addDexProgramData(builder.compile())
+ .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+ .build();
+ return buildApplication(input, options);
+ } catch (IOException | RecognitionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected DexApplication buildApplication(AndroidApp input, InternalOptions options) {
+ try {
+ options.itemFactory.resetSortedIndices();
+ return new ApplicationReader(input, options, new Timing("SmaliTest")).read();
+ } catch (IOException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected DexApplication processApplication(DexApplication application, InternalOptions options) {
+ try {
+ R8 r8 = new R8(options);
+ return r8.optimize(application, new AppInfoWithSubtyping(application));
+ } catch (IOException | ProguardRuleParserException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected DexClass getClass(DexApplication application, String className) {
+ DexInspector inspector = new DexInspector(application);
+ ClassSubject clazz = inspector.clazz(className);
+ assertTrue(clazz.isPresent());
+ return clazz.getDexClass();
+ }
+
+ protected DexClass getClass(DexApplication application, MethodSignature signature) {
+ return getClass(application, signature.clazz);
+ }
+
+ protected DexEncodedMethod getMethod(DexApplication application, String className,
+ String returnType, String methodName, List<String> parameters) {
+ DexInspector inspector = new DexInspector(application);
+ ClassSubject clazz = inspector.clazz(className);
+ assertTrue(clazz.isPresent());
+ MethodSubject method = clazz.method(returnType, methodName, parameters);
+ assertTrue(method.isPresent());
+ return method.getMethod();
+ }
+
+ protected DexEncodedMethod getMethod(DexApplication application, MethodSignature signature) {
+ return getMethod(application,
+ signature.clazz, signature.returnType, signature.name, signature.parameterTypes);
+ }
+
+ /**
+ * Create an application with one method, and processed that application using R8.
+ *
+ * Returns the processed method for inspection.
+ *
+ * @param returnType the return type for the method
+ * @param parameters the parameter types for the method
+ * @param locals number of locals needed for the application
+ * @param instructions instructions for the method
+ * @return the processed method for inspection
+ */
+ public DexApplication singleMethodApplication(String returnType, List<String> parameters,
+ int locals, String... instructions) {
+ // Build a one class method.
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+ builder.addStaticMethod(returnType, DEFAULT_METHOD_NAME, parameters, locals, instructions);
+
+ // Read the one class method as an application.
+ DexApplication application = buildApplication(builder);
+ assertEquals(1, Iterables.size(application.classes()));
+ return application;
+ }
+
+ /**
+ * Create an application with one method, and processed that application using R8.
+ *
+ * Returns the processed method for inspection.
+ *
+ * @param returnType the return type for the method
+ * @param parameters the parameter types for the method
+ * @param locals number of locals needed for the application
+ * @param instructions instructions for the method
+ * @return the processed method for inspection
+ */
+ public DexEncodedMethod oneMethodApplication(String returnType, List<String> parameters,
+ int locals, String... instructions) {
+ InternalOptions options = new InternalOptions();
+
+ // Build a one class application.
+ DexApplication application = singleMethodApplication(
+ returnType, parameters, locals, instructions);
+
+ // Process the application with R8.
+ DexApplication processdApplication = processApplication(application, options);
+ assertEquals(1, Iterables.size(processdApplication.classes()));
+
+ // Return the processed method for inspection.
+ return getMethod(
+ processdApplication, DEFAULT_CLASS_NAME, returnType, DEFAULT_METHOD_NAME, parameters);
+ }
+
+ public String runArt(DexApplication application, InternalOptions options) {
+ try {
+ AndroidApp app = writeDex(application, options);
+ Path out = temp.getRoot().toPath().resolve("run-art-input.zip");
+ // TODO(sgjesse): Pass in a unique temp directory for each run.
+ app.writeToZip(out, true);
+ return ToolHelper.runArtNoVerificationErrors(out.toString(), DEFAULT_MAIN_CLASS_NAME);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void runDex2Oat(DexApplication application, InternalOptions options) {
+ try {
+ AndroidApp app = writeDex(application, options);
+ Path dexOut = temp.getRoot().toPath().resolve("run-dex2oat-input.zip");
+ Path oatFile = temp.getRoot().toPath().resolve("oat-file");
+ app.writeToZip(dexOut);
+ ToolHelper.runDex2Oat(dexOut, oatFile);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public AndroidApp writeDex(DexApplication application, InternalOptions options) {
+ AppInfo appInfo = new AppInfo(application);
+ try {
+ return R8.writeApplication(
+ Executors.newSingleThreadExecutor(),
+ application,
+ appInfo,
+ NamingLens.getIdentityLens(),
+ null,
+ null,
+ options);
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
new file mode 100644
index 0000000..12bb1f6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
@@ -0,0 +1,277 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.R8;
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.IfEq;
+import com.android.tools.r8.code.IfEqz;
+import com.android.tools.r8.code.PackedSwitch;
+import com.android.tools.r8.code.SparseSwitch;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class SwitchRewritingTest extends SmaliTestBase {
+
+ private void runSingleCaseDexTest(Switch.Type type, int key) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+ String switchInstruction;
+ String switchData;
+ if (type == Switch.Type.PACKED) {
+ switchInstruction = "packed-switch";
+ switchData = StringUtils.join(
+ "\n",
+ " :switch_data",
+ " .packed-switch " + key,
+ " :case_0",
+ " .end packed-switch");
+ } else {
+ switchInstruction = "sparse-switch";
+ switchData = StringUtils.join(
+ "\n",
+ " :switch_data",
+ " .sparse-switch",
+ " " + key + " -> :case_0",
+ " .end sparse-switch");
+ }
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int"),
+ 0,
+ " " + switchInstruction + " p0, :switch_data",
+ " const/4 p0, 0x5",
+ " goto :return",
+ " :case_0",
+ " const/4 p0, 0x3",
+ " :return",
+ " return p0",
+ switchData);
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, 0",
+ " invoke-static { v1 }, LTest;->method(I)I",
+ " move-result v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication originalApplication = buildApplication(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+ DexCode code = method.getCode().asDexCode();
+
+ if (key == 0) {
+ assertEquals(5, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof IfEqz);
+ } else {
+ assertEquals(6, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof Const4);
+ assertTrue(code.instructions[1] instanceof IfEq);
+ }
+ }
+
+ @Test
+ public void singleCaseDex() {
+ runSingleCaseDexTest(Switch.Type.PACKED, 0);
+ runSingleCaseDexTest(Switch.Type.SPARSE, 0);
+ runSingleCaseDexTest(Switch.Type.PACKED, 1);
+ runSingleCaseDexTest(Switch.Type.SPARSE, 1);
+ }
+
+ private void runTwoCaseSparseToPackedDexTest(int key1, int key2) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int"),
+ 0,
+ " sparse-switch p0, :sparse_switch_data",
+ " const/4 v0, 0x5",
+ " goto :return",
+ " :case_1",
+ " const/4 v0, 0x3",
+ " goto :return",
+ " :case_2",
+ " const/4 v0, 0x4",
+ " :return",
+ " return v0",
+ " :sparse_switch_data",
+ " .sparse-switch",
+ " " + key1 + " -> :case_1",
+ " " + key2 + " -> :case_2",
+ " .end sparse-switch");
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, 0",
+ " invoke-static { v1 }, LTest;->method(I)I",
+ " move-result v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ InternalOptions options = new InternalOptions();
+ DexApplication originalApplication = buildApplication(builder, options);
+ DexApplication processedApplication = processApplication(originalApplication, options);
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+ DexCode code = method.getCode().asDexCode();
+ if (key1 + 1 == key2) {
+ assertTrue(code.instructions[0] instanceof PackedSwitch);
+ } else {
+ assertTrue(code.instructions[0] instanceof SparseSwitch);
+ }
+ }
+
+ @Test
+ public void twoCaseSparseToPackedDex() {
+ runTwoCaseSparseToPackedDexTest(0, 1);
+ runTwoCaseSparseToPackedDexTest(-1, 0);
+ runTwoCaseSparseToPackedDexTest(Integer.MIN_VALUE, Integer.MIN_VALUE + 1);
+ runTwoCaseSparseToPackedDexTest(Integer.MAX_VALUE - 1, Integer.MAX_VALUE);
+ runTwoCaseSparseToPackedDexTest(0, 2);
+ runTwoCaseSparseToPackedDexTest(-1, 1);
+ runTwoCaseSparseToPackedDexTest(Integer.MIN_VALUE, Integer.MIN_VALUE + 2);
+ runTwoCaseSparseToPackedDexTest(Integer.MAX_VALUE - 2, Integer.MAX_VALUE);
+ }
+
+ private void runSingleCaseJarTest(Switch.Type type, int key) throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ String switchCode;
+ if (type == Switch.Type.PACKED) {
+ switchCode = StringUtils.join(
+ "\n",
+ " tableswitch " + key,
+ " case_0",
+ " default : case_default");
+ } else {
+ switchCode = StringUtils.join(
+ "\n",
+ " lookupswitch",
+ " " + key + " : case_0",
+ " default : case_default");
+ }
+
+ clazz.addStaticMethod("test", ImmutableList.of("I"), "I",
+ " .limit stack 1",
+ " .limit locals 1",
+ " iload 0",
+ switchCode,
+ " case_0:",
+ " iconst_3",
+ " goto return_",
+ " case_default:",
+ " ldc 5",
+ " return_:",
+ " ireturn");
+
+ clazz.addMainMethod(
+ " .limit stack 2",
+ " .limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 2",
+ " invokestatic Test/test(I)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ DexApplication app = builder.read();
+ app = new R8(new InternalOptions()).optimize(app, new AppInfoWithSubtyping(app));
+
+ MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
+ DexEncodedMethod method = getMethod(app, signature);
+ DexCode code = method.getCode().asDexCode();
+ if (key == 0) {
+ assertEquals(5, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof IfEqz);
+ } else {
+ assertEquals(6, code.instructions.length);
+ assertTrue(code.instructions[1] instanceof IfEq);
+ assertTrue(code.instructions[2] instanceof Const4);
+ }
+ }
+
+ @Test
+ public void singleCaseJar() throws Exception {
+ runSingleCaseJarTest(Switch.Type.PACKED, 0);
+ runSingleCaseJarTest(Switch.Type.SPARSE, 0);
+ runSingleCaseJarTest(Switch.Type.PACKED, 1);
+ runSingleCaseJarTest(Switch.Type.SPARSE, 1);
+ }
+
+ private void runTwoCaseSparseToPackedJarTest(int key1, int key2) throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("test", ImmutableList.of("I"), "I",
+ " .limit stack 1",
+ " .limit locals 1",
+ " iload 0",
+ " lookupswitch",
+ " " + key1 + " : case_1",
+ " " + key2 + " : case_2",
+ " default : case_default",
+ " case_1:",
+ " iconst_3",
+ " goto return_",
+ " case_2:",
+ " iconst_4",
+ " goto return_",
+ " case_default:",
+ " iconst_5",
+ " return_:",
+ " ireturn");
+
+ clazz.addMainMethod(
+ " .limit stack 2",
+ " .limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 2",
+ " invokestatic Test/test(I)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ DexApplication app = builder.read();
+ app = new R8(new InternalOptions()).optimize(app, new AppInfoWithSubtyping(app));
+
+ MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
+ DexEncodedMethod method = getMethod(app, signature);
+ DexCode code = method.getCode().asDexCode();
+ if (key1 + 1 == key2) {
+ assertTrue(code.instructions[0] instanceof PackedSwitch);
+ } else {
+ assertTrue(code.instructions[0] instanceof SparseSwitch);
+ }
+ }
+
+ @Test
+ public void twoCaseSparseToPackedJar() throws Exception {
+ runTwoCaseSparseToPackedJarTest(0, 1);
+ runTwoCaseSparseToPackedJarTest(-1, 0);
+ runTwoCaseSparseToPackedJarTest(Integer.MIN_VALUE, Integer.MIN_VALUE + 1);
+ runTwoCaseSparseToPackedJarTest(Integer.MAX_VALUE - 1, Integer.MAX_VALUE);
+ runTwoCaseSparseToPackedJarTest(0, 2);
+ runTwoCaseSparseToPackedJarTest(-1, 1);
+ runTwoCaseSparseToPackedJarTest(Integer.MIN_VALUE, Integer.MIN_VALUE + 2);
+ runTwoCaseSparseToPackedJarTest(Integer.MAX_VALUE - 2, Integer.MAX_VALUE);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/ArtCommandBuilderTest.java b/src/test/java/com/android/tools/r8/utils/ArtCommandBuilderTest.java
new file mode 100644
index 0000000..0344a27
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/ArtCommandBuilderTest.java
@@ -0,0 +1,106 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.ToolHelper.DexVm;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ArtCommandBuilderTest {
+
+ @Test
+ public void noArguments() {
+ ArtCommandBuilder builder = new ArtCommandBuilder();
+ Assert.assertEquals("/bin/bash " + ToolHelper.getArtBinary(), builder.build());
+ }
+
+ @Test
+ public void simple() {
+ ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+ builder.appendClasspath("xxx.dex").setMainClass("Test");
+ assertEquals("/bin/bash " + ToolHelper.getArtBinary() + " -cp xxx.dex Test", builder.build());
+ }
+
+ @Test
+ public void classpath() {
+ ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+ builder.appendClasspath("xxx.dex").appendClasspath("yyy.jar");
+ assertEquals("/bin/bash " + ToolHelper.getArtBinary() + " -cp xxx.dex:yyy.jar",
+ builder.build());
+ }
+
+ @Test
+ public void artOptions() {
+ ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+ builder.appendArtOption("-d").appendArtOption("--test");
+ assertEquals("/bin/bash " + ToolHelper.getArtBinary() + " -d --test", builder.build());
+ }
+
+ @Test
+ public void artSystemProperties() {
+ ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+ builder.appendArtSystemProperty("a.b.c", "1").appendArtSystemProperty("x.y.z", "2");
+ assertEquals("/bin/bash " + ToolHelper.getArtBinary() + " -Da.b.c=1 -Dx.y.z=2",
+ builder.build());
+ }
+
+ @Test
+ public void programOptions() {
+ ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+ builder.setMainClass("Test").appendProgramArgument("hello").appendProgramArgument("world");
+ assertEquals("/bin/bash " + ToolHelper.getArtBinary() + " Test hello world", builder.build());
+ }
+
+ @Test
+ public void allOfTheAbove() {
+ ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+ builder
+ .appendArtOption("-d")
+ .appendArtOption("--test")
+ .appendArtSystemProperty("a.b.c", "1")
+ .appendArtSystemProperty("x.y.z", "2")
+ .appendClasspath("xxx.dex")
+ .appendClasspath("yyy.jar")
+ .setMainClass("Test")
+ .appendProgramArgument("hello")
+ .appendProgramArgument("world");
+ assertEquals(
+ "/bin/bash " + ToolHelper.getArtBinary()
+ + " -d --test -Da.b.c=1 -Dx.y.z=2 -cp xxx.dex:yyy.jar Test hello world",
+ builder.build());
+ }
+
+ @Test
+ public void allOfTheAboveWithClasspathAndSystemPropertiesBeforeOptions() {
+ ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+ builder
+ .appendClasspath("xxx.dex")
+ .appendClasspath("yyy.jar")
+ .appendArtSystemProperty("a.b.c", "1")
+ .appendArtSystemProperty("x.y.z", "2")
+ .appendArtOption("-d")
+ .appendArtOption("--test")
+ .setMainClass("Test")
+ .appendProgramArgument("hello")
+ .appendProgramArgument("world");
+ assertEquals(
+ "/bin/bash " + ToolHelper.getArtBinary()
+ + " -d --test -Da.b.c=1 -Dx.y.z=2 -cp xxx.dex:yyy.jar Test hello world",
+ builder.build());
+ }
+
+ @Test
+ public void testVersion() {
+ for (DexVm version : ToolHelper.getArtVersions()) {
+ ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder(version);
+ builder.setMainClass("Test").appendProgramArgument("hello").appendProgramArgument("world");
+ assertEquals("/bin/bash " + ToolHelper.getArtBinary(version)
+ + " Test hello world", builder.build());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/ArtErrorParser.java b/src/test/java/com/android/tools/r8/utils/ArtErrorParser.java
new file mode 100644
index 0000000..406c63f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/ArtErrorParser.java
@@ -0,0 +1,293 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ArtErrorParser {
+ private static final String VERIFICATION_ERROR_HEADER = "Verification error in ";
+ private static final String METHOD_EXCEEDS_INSTRUCITON_LIMIT =
+ "Method exceeds compiler instruction limit: ";
+
+ public static class ArtErrorParserException extends Exception {
+ public ArtErrorParserException(String message) {
+ super(message);
+ }
+ }
+
+ private static class ParseInput {
+ public final String input;
+ public int pos = 0;
+
+ public ParseInput(String input) {
+ this.input = input;
+ }
+
+ public boolean hasNext() {
+ return pos < input.length();
+ }
+
+ public char next() {
+ assert hasNext();
+ return input.charAt(pos++);
+ }
+
+ public char peek() {
+ return input.charAt(pos);
+ }
+
+ public void whitespace() {
+ while (peek() == ' ') {
+ next();
+ }
+ }
+
+ public String until(int end) {
+ String result = input.substring(pos, end);
+ pos = end;
+ return result;
+ }
+
+ public String until(char match) throws ArtErrorParserException {
+ int end = input.indexOf(match, pos);
+ if (end < 0) {
+ throw new ArtErrorParserException("Expected to find " + match + " at " + pos);
+ }
+ return until(end);
+ }
+
+ public void check(char expected) throws ArtErrorParserException {
+ if (peek() != expected) {
+ throw new ArtErrorParserException("Expected " + expected + " found " + pos);
+ }
+ }
+ }
+
+ public static abstract class ArtErrorInfo {
+ public abstract int consumedLines();
+ public abstract String getMessage();
+ public abstract String dump(DexInspector inspector, boolean markLocation);
+ }
+
+ private static class ArtMethodError extends ArtErrorInfo {
+ public final String className;
+ public final String methodName;
+ public final String methodReturn;
+ public final List<String> methodFormals;
+ public final String methodSignature;
+ public final String errorMessage;
+ public final List<Long> locations;
+ private final int consumedLines;
+
+ public ArtMethodError(String className,
+ String methodName, String methodReturn, List<String> methodFormals,
+ String methodSignature, String errorMessage,
+ List<Long> locations,
+ int consumedLines) {
+ this.className = className;
+ this.methodName = methodName;
+ this.methodReturn = methodReturn;
+ this.methodFormals = methodFormals;
+ this.methodSignature = methodSignature;
+ this.errorMessage = errorMessage;
+ this.locations = locations;
+ this.consumedLines = consumedLines;
+ }
+
+ @Override
+ public int consumedLines() {
+ return consumedLines;
+ }
+
+ @Override
+ public String getMessage() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("\n")
+ .append(VERIFICATION_ERROR_HEADER)
+ .append(methodSignature)
+ .append(":\n")
+ .append(errorMessage);
+ return builder.toString();
+ }
+
+ @Override
+ public String dump(DexInspector inspector, boolean markLocation) {
+ ClassSubject clazz = inspector.clazz(className);
+ MethodSubject method = clazz.method(methodReturn, methodName, methodFormals);
+ DexEncodedMethod dex = method.getMethod();
+ if (dex == null) {
+ StringBuilder builder = new StringBuilder("Failed to lookup method: ");
+ builder.append(className).append(".").append(methodName);
+ StringUtils.append(builder, methodFormals);
+ return builder.toString();
+ }
+
+ String code = method.getMethod().codeToString();
+ if (markLocation && !locations.isEmpty()) {
+ for (Long location : locations) {
+ String locationString = "" + location + ":";
+ code = code.replaceFirst(":(\\s*) " + locationString, ":$1*" + locationString);
+ }
+ }
+ return code;
+ }
+ }
+
+ public static List<ArtErrorInfo> parse(String message) throws ArtErrorParserException {
+ List<ArtErrorInfo> errors = new ArrayList<>();
+ String[] lines = message.split("\n");
+ for (int i = 0; i < lines.length; i++) {
+ ArtErrorInfo error = parse(lines, i);
+ if (error != null) {
+ errors.add(error);
+ i += error.consumedLines();
+ }
+ }
+ return errors;
+ }
+
+ private static ArtErrorInfo parse(String[] lines, final int line) throws ArtErrorParserException {
+ String methodSig = null;
+ StringBuilder errorMessageContent = new StringBuilder();
+ int currentLine = line;
+ {
+ int index = lines[currentLine].indexOf(VERIFICATION_ERROR_HEADER);
+ if (index >= 0) {
+ // Read/skip the header line.
+ String lineContent = lines[currentLine++].substring(index);
+ // Append the content of each subsequent line that can be parsed as an "error message".
+ for (; currentLine < lines.length; ++currentLine) {
+ lineContent = lines[currentLine].substring(index);
+ String[] parts = lineContent.split(":");
+ if (parts.length == 2) {
+ if (methodSig == null) {
+ methodSig = parts[0];
+ errorMessageContent.append(parts[1]);
+ } else if (methodSig.equals(parts[0])) {
+ errorMessageContent.append(parts[1]);
+ } else {
+ break;
+ }
+ } else if (parts.length >= 3) {
+ if (methodSig == null) {
+ methodSig = parts[1];
+ for (int i = 2; i < parts.length; ++i) {
+ errorMessageContent.append(parts[i]);
+ }
+ } else if (methodSig.equals(parts[1])) {
+ for (int i = 2; i < parts.length; ++i) {
+ errorMessageContent.append(parts[i]);
+ }
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ if (methodSig == null) {
+ throw new ArtErrorParserException("Unexpected art error message: " + lineContent);
+ }
+ }
+ }
+ if (methodSig == null) {
+ int index = lines[currentLine].indexOf(METHOD_EXCEEDS_INSTRUCITON_LIMIT);
+ if (index >= 0) {
+ String lineContent = lines[currentLine++].substring(index);
+ String[] parts = lineContent.split(":");
+ if (parts.length == 2) {
+ errorMessageContent.append(lineContent);
+ methodSig = parts[1].substring(parts[1].indexOf(" in ") + 4);
+ } else {
+ throw new ArtErrorParserException("Unexpected art error message parts: " + parts);
+ }
+ }
+ }
+
+ // Return if we failed to identify an error description.
+ if (methodSig == null) {
+ return null;
+ }
+
+ String errorMessage = errorMessageContent.toString();
+ ParseInput input = new ParseInput(methodSig);
+ String methodReturn = parseType(input);
+ String[] qualifiedName = parseQualifiedName(input);
+ List<String> methodFormals = parseTypeList(input);
+ List<Long> locations = parseLocations(errorMessage);
+ return new ArtMethodError(getClassName(qualifiedName), getMethodName(qualifiedName),
+ methodReturn, methodFormals, methodSig, errorMessage, locations, currentLine - line);
+ }
+
+ private static String getClassName(String[] parts) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < parts.length - 1; i++) {
+ if (i != 0) {
+ builder.append('.');
+ }
+ builder.append(parts[i]);
+ }
+ return builder.toString();
+ }
+
+ private static String getMethodName(String[] parts) {
+ return parts[parts.length - 1];
+ }
+
+ private static String parseType(ParseInput input) throws ArtErrorParserException {
+ input.whitespace();
+ String type = input.until(' ');
+ assert !type.contains("<");
+ input.whitespace();
+ return type;
+ }
+
+ private static String[] parseQualifiedName(ParseInput input) throws ArtErrorParserException {
+ input.whitespace();
+ return input.until('(').split("\\.");
+ }
+
+ private static List<String> parseTypeList(ParseInput input) throws ArtErrorParserException {
+ input.check('(');
+ input.next();
+ String content = input.until(')').trim();
+ if (content.isEmpty()) {
+ return new ArrayList<>();
+ }
+ String[] rawList = content.split(",");
+ List<String> types = new ArrayList<>();
+ for (String type : rawList) {
+ types.add(type.trim());
+ }
+ input.check(')');
+ input.next();
+ return types;
+ }
+
+ private static boolean isHexChar(char c) {
+ return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F';
+ }
+
+ private static List<Long> parseLocations(String input) {
+ List<Long> locations = new ArrayList<>();
+ int length = input.length();
+ int start = 0;
+ while (start < length && (start = input.indexOf("0x", start)) >= 0) {
+ int end = start + 2;
+ while (end < length && isHexChar(input.charAt(end))) {
+ ++end;
+ }
+ long l = Long.parseLong(input.substring(start + 2, end), 16);
+ if (l >= 0) {
+ locations.add(l);
+ }
+ start = end;
+ }
+ return locations;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/D8CommandTest.java b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
new file mode 100644
index 0000000..a203323
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
@@ -0,0 +1,125 @@
+// 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 static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public class D8CommandTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void emptyCommand() throws Throwable {
+ verifyEmptyCommand(D8Command.builder().build());
+ verifyEmptyCommand(D8Command.parse(new String[]{}).build());
+ }
+
+ private void verifyEmptyCommand(D8Command command) {
+ assertEquals(0, ToolHelper.getApp(command).getDexProgramResources().size());
+ assertEquals(0, ToolHelper.getApp(command).getClassProgramResources().size());
+ assertEquals(0, ToolHelper.getApp(command).getDexLibraryResources().size());
+ assertEquals(0, ToolHelper.getApp(command).getClassLibraryResources().size());
+ assertFalse(ToolHelper.getApp(command).hasMainDexList());
+ assertFalse(ToolHelper.getApp(command).hasProguardMap());
+ assertFalse(ToolHelper.getApp(command).hasProguardSeeds());
+ assertFalse(ToolHelper.getApp(command).hasPackageDistribution());
+ assertNull(command.getOutputPath());
+ assertEquals(CompilationMode.DEBUG, command.getMode());
+ }
+
+ @Test
+ public void defaultOutIsCwd() throws IOException, InterruptedException {
+ Path working = temp.getRoot().toPath();
+ Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar").toAbsolutePath();
+ Path output = working.resolve("classes.dex");
+ assertFalse(Files.exists(output));
+ assertEquals(0, ToolHelper.forkD8(working, input.toString()).exitCode);
+ assertTrue(Files.exists(output));
+ }
+
+ @Test
+ public void validOutputPath() throws Throwable {
+ Path existingDir = temp.getRoot().toPath();
+ Path nonExistingZip = existingDir.resolve("a-non-existing-archive.zip");
+ assertEquals(
+ existingDir,
+ D8Command.builder().setOutputPath(existingDir).build().getOutputPath());
+ assertEquals(
+ nonExistingZip,
+ D8Command.builder().setOutputPath(nonExistingZip).build().getOutputPath());
+ assertEquals(
+ existingDir,
+ parse("--output", existingDir.toString()).getOutputPath());
+ assertEquals(
+ nonExistingZip,
+ parse("--output", nonExistingZip.toString()).getOutputPath());
+ }
+
+ @Test
+ public void nonExistingOutputDir() throws Throwable {
+ thrown.expect(CompilationException.class);
+ Path nonExistingDir = temp.getRoot().toPath().resolve("a/path/that/does/not/exist");
+ D8Command.builder().setOutputPath(nonExistingDir).build();
+ }
+
+ @Test
+ public void existingOutputZip() throws Throwable {
+ thrown.expect(CompilationException.class);
+ Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
+ D8Command.builder().setOutputPath(existingZip).build();
+ }
+
+ @Test
+ public void invalidOutputFileType() throws Throwable {
+ thrown.expect(CompilationException.class);
+ Path invalidType = temp.getRoot().toPath().resolve("an-invalid-output-file-type.foobar");
+ D8Command.builder().setOutputPath(invalidType).build();
+ }
+
+ @Test
+ public void nonExistingOutputDirParse() throws Throwable {
+ thrown.expect(CompilationException.class);
+ Path nonExistingDir = temp.getRoot().toPath().resolve("a/path/that/does/not/exist");
+ parse("--output", nonExistingDir.toString());
+ }
+
+ @Test
+ public void existingOutputZipParse() throws Throwable {
+ thrown.expect(CompilationException.class);
+ Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
+ parse("--output", existingZip.toString());
+ }
+
+ @Test
+ public void invalidOutputFileTypeParse() throws Throwable {
+ thrown.expect(CompilationException.class);
+ Path invalidType = temp.getRoot().toPath().resolve("an-invalid-output-file-type.foobar");
+ parse("--output", invalidType.toString());
+ }
+
+ private D8Command parse(String... args) throws IOException, CompilationException {
+ return D8Command.parse(args).build();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/DescriptorUtilsTest.java b/src/test/java/com/android/tools/r8/utils/DescriptorUtilsTest.java
new file mode 100644
index 0000000..034a288
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/DescriptorUtilsTest.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import org.junit.Test;
+
+public class DescriptorUtilsTest {
+
+ @Test
+ public void toShorty() throws IOException {
+ assertEquals("Z", DescriptorUtils.javaTypeToShorty("boolean"));
+ assertEquals("B", DescriptorUtils.javaTypeToShorty("byte"));
+ assertEquals("S", DescriptorUtils.javaTypeToShorty("short"));
+ assertEquals("C", DescriptorUtils.javaTypeToShorty("char"));
+ assertEquals("I", DescriptorUtils.javaTypeToShorty("int"));
+ assertEquals("J", DescriptorUtils.javaTypeToShorty("long"));
+ assertEquals("F", DescriptorUtils.javaTypeToShorty("float"));
+ assertEquals("D", DescriptorUtils.javaTypeToShorty("double"));
+ assertEquals("L", DescriptorUtils.javaTypeToShorty("int[]"));
+ assertEquals("L", DescriptorUtils.javaTypeToShorty("int[][]"));
+ assertEquals("L", DescriptorUtils.javaTypeToShorty("java.lang.Object"));
+ assertEquals("L", DescriptorUtils.javaTypeToShorty("a.b.C"));
+ }
+
+ @Test
+ public void toDescriptor() throws IOException {
+ assertEquals("Z", DescriptorUtils.javaTypeToDescriptor("boolean"));
+ assertEquals("B", DescriptorUtils.javaTypeToDescriptor("byte"));
+ assertEquals("S", DescriptorUtils.javaTypeToDescriptor("short"));
+ assertEquals("C", DescriptorUtils.javaTypeToDescriptor("char"));
+ assertEquals("I", DescriptorUtils.javaTypeToDescriptor("int"));
+ assertEquals("J", DescriptorUtils.javaTypeToDescriptor("long"));
+ assertEquals("F", DescriptorUtils.javaTypeToDescriptor("float"));
+ assertEquals("D", DescriptorUtils.javaTypeToDescriptor("double"));
+ assertEquals("[I", DescriptorUtils.javaTypeToDescriptor("int[]"));
+ assertEquals("[[I", DescriptorUtils.javaTypeToDescriptor("int[][]"));
+ assertEquals("Ljava/lang/Object;", DescriptorUtils.javaTypeToDescriptor("java.lang.Object"));
+ assertEquals("La/b/C;", DescriptorUtils.javaTypeToDescriptor("a.b.C"));
+ }
+
+ @Test
+ public void toJavaType() throws IOException {
+ assertEquals("boolean", DescriptorUtils.descriptorToJavaType("Z"));
+ assertEquals("byte", DescriptorUtils.descriptorToJavaType("B"));
+ assertEquals("short", DescriptorUtils.descriptorToJavaType("S"));
+ assertEquals("char", DescriptorUtils.descriptorToJavaType("C"));
+ assertEquals("int", DescriptorUtils.descriptorToJavaType("I"));
+ assertEquals("long", DescriptorUtils.descriptorToJavaType("J"));
+ assertEquals("float", DescriptorUtils.descriptorToJavaType("F"));
+ assertEquals("double", DescriptorUtils.descriptorToJavaType("D"));
+ assertEquals("int[]", DescriptorUtils.descriptorToJavaType("[I"));
+ assertEquals("int[][]", DescriptorUtils.descriptorToJavaType("[[I"));
+ assertEquals("java.lang.Object", DescriptorUtils.descriptorToJavaType("Ljava/lang/Object;"));
+ assertEquals("a.b.C", DescriptorUtils.descriptorToJavaType("La/b/C;"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
new file mode 100644
index 0000000..694ccc5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -0,0 +1,1059 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.code.Iget;
+import com.android.tools.r8.code.IgetBoolean;
+import com.android.tools.r8.code.IgetByte;
+import com.android.tools.r8.code.IgetChar;
+import com.android.tools.r8.code.IgetObject;
+import com.android.tools.r8.code.IgetShort;
+import com.android.tools.r8.code.IgetWide;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.InvokeDirectRange;
+import com.android.tools.r8.code.InvokeInterface;
+import com.android.tools.r8.code.InvokeInterfaceRange;
+import com.android.tools.r8.code.InvokeStatic;
+import com.android.tools.r8.code.InvokeStaticRange;
+import com.android.tools.r8.code.InvokeSuper;
+import com.android.tools.r8.code.InvokeSuperRange;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.InvokeVirtualRange;
+import com.android.tools.r8.code.Iput;
+import com.android.tools.r8.code.IputBoolean;
+import com.android.tools.r8.code.IputByte;
+import com.android.tools.r8.code.IputChar;
+import com.android.tools.r8.code.IputObject;
+import com.android.tools.r8.code.IputShort;
+import com.android.tools.r8.code.IputWide;
+import com.android.tools.r8.code.Sget;
+import com.android.tools.r8.code.SgetBoolean;
+import com.android.tools.r8.code.SgetByte;
+import com.android.tools.r8.code.SgetChar;
+import com.android.tools.r8.code.SgetObject;
+import com.android.tools.r8.code.SgetShort;
+import com.android.tools.r8.code.SgetWide;
+import com.android.tools.r8.code.Sput;
+import com.android.tools.r8.code.SputBoolean;
+import com.android.tools.r8.code.SputByte;
+import com.android.tools.r8.code.SputChar;
+import com.android.tools.r8.code.SputObject;
+import com.android.tools.r8.code.SputShort;
+import com.android.tools.r8.code.SputWide;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.ClassNaming;
+import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.ProguardMapReader;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class DexInspector {
+
+ private final DexApplication application;
+ private final DexItemFactory dexItemFactory;
+ private final ClassNameMapper mapping;
+ private final BiMap<String, String> originalToObfuscatedMapping;
+
+ private final InstructionSubjectFactory factory = new InstructionSubjectFactory();
+
+ public static MethodSignature MAIN =
+ new MethodSignature("main", "void", new String[]{"java.lang.String[]"});
+
+ public DexInspector(Path file, String mappingFile) throws IOException, ExecutionException {
+ this(Collections.singletonList(file), mappingFile);
+ }
+
+ public DexInspector(Path file) throws IOException, ExecutionException {
+ this(Collections.singletonList(file), null);
+ }
+
+ public DexInspector(List<Path> files) throws IOException, ExecutionException {
+ this(files, null);
+ }
+
+ public DexInspector(List<Path> files, String mappingFile)
+ throws IOException, ExecutionException {
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ if (mappingFile != null) {
+ this.mapping = ProguardMapReader.mapperFromFile(Paths.get(mappingFile));
+ originalToObfuscatedMapping = this.mapping.getObfuscatedToOriginalMapping().inverse();
+ } else {
+ this.mapping = null;
+ originalToObfuscatedMapping = null;
+ }
+ Timing timing = new Timing("DexInspector");
+ InternalOptions options = new InternalOptions();
+ dexItemFactory = options.itemFactory;
+ AndroidApp input = AndroidApp.fromProgramFiles(files);
+ application = new ApplicationReader(input, options, timing).read(executor);
+ executor.shutdown();
+ }
+
+ public DexInspector(AndroidApp app) throws IOException, ExecutionException {
+ this(new ApplicationReader(app, new InternalOptions(), new Timing("DexInspector")).read());
+ }
+
+ public DexInspector(DexApplication application) {
+ dexItemFactory = application.dexItemFactory;
+ this.application = application;
+ this.mapping = application.getProguardMap();
+ originalToObfuscatedMapping =
+ mapping == null ? null : mapping.getObfuscatedToOriginalMapping().inverse();
+ }
+
+ public DexItemFactory getFactory() {
+ return dexItemFactory;
+ }
+
+ private DexType toDexType(String string) {
+ return dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(string));
+ }
+
+ private static <S, T extends Subject> void forAll(S[] items,
+ BiFunction<S, FoundClassSubject, ? extends T> constructor,
+ FoundClassSubject clazz,
+ Consumer<T> consumer) {
+ for (S item : items) {
+ consumer.accept(constructor.apply(item, clazz));
+ }
+ }
+
+ private static <S, T extends Subject> void forAll(Iterable<S> items, Function<S, T> constructor,
+ Consumer<T> consumer) {
+ for (S item : items) {
+ consumer.accept(constructor.apply(item));
+ }
+ }
+
+
+ public ClassSubject clazz(String name) {
+ ClassNaming naming = null;
+ if (mapping != null) {
+ String obfuscated = originalToObfuscatedMapping.get(name);
+ if (obfuscated != null) {
+ naming = mapping.getClassNaming(obfuscated);
+ name = obfuscated;
+ }
+ }
+ DexClass clazz = application.definitionFor(toDexType(name));
+ if (clazz == null) {
+ return new AbsentClassSubject();
+ }
+ return new FoundClassSubject(clazz, naming);
+ }
+
+ public void forAllClasses(Consumer<FoundClassSubject> inspection) {
+ forAll(application.classes(), clazz -> {
+ ClassNaming naming = null;
+ if (mapping != null) {
+ String obfuscated = originalToObfuscatedMapping.get(clazz.type.toSourceString());
+ if (obfuscated != null) {
+ naming = mapping.getClassNaming(obfuscated);
+ }
+ }
+ return new FoundClassSubject(clazz, naming);
+ }, inspection);
+ }
+
+ private String getObfuscatedTypeName(String originalTypeName) {
+ String obfuscatedType = null;
+ if (mapping != null) {
+ obfuscatedType = originalToObfuscatedMapping.get(originalTypeName);
+ }
+ obfuscatedType = obfuscatedType == null ? originalTypeName : obfuscatedType;
+ return obfuscatedType;
+ }
+
+ public abstract class Subject {
+
+ public abstract boolean isPresent();
+ }
+
+ public abstract class AnnotationSubject extends Subject {
+
+ public abstract DexEncodedAnnotation getAnnotation();
+ }
+
+ public class FoundAnnotationSubject extends AnnotationSubject {
+
+ private final DexAnnotation annotation;
+
+ private FoundAnnotationSubject(DexAnnotation annotation) {
+ this.annotation = annotation;
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ @Override
+ public DexEncodedAnnotation getAnnotation() {
+ return annotation.annotation;
+ }
+ }
+
+ public class AbsentAnnotationSubject extends AnnotationSubject {
+
+ @Override
+ public boolean isPresent() {
+ return false;
+ }
+
+ @Override
+ public DexEncodedAnnotation getAnnotation() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+
+ public abstract class ClassSubject extends Subject {
+
+ public abstract void forAllMethods(Consumer<FoundMethodSubject> inspection);
+
+ public abstract MethodSubject method(String returnType, String name, List<String> parameters);
+
+ public MethodSubject method(MethodSignature signature) {
+ return method(signature.type, signature.name, ImmutableList.copyOf(signature.parameters));
+ }
+
+ public abstract void forAllFields(Consumer<FoundFieldSubject> inspection);
+
+ public abstract FieldSubject field(String type, String name);
+
+ public abstract boolean isAbstract();
+
+ public String dumpMethods() {
+ StringBuilder dump = new StringBuilder();
+ forAllMethods((FoundMethodSubject method) ->
+ dump.append(method.getMethod().toString())
+ .append(method.getMethod().codeToString()));
+ return dump.toString();
+ }
+
+ public abstract DexClass getDexClass();
+
+ public abstract AnnotationSubject annotation(String name);
+
+ public abstract String getOriginalDescriptor();
+
+ public abstract String getFinalDescriptor();
+ }
+
+ private class AbsentClassSubject extends ClassSubject {
+
+ @Override
+ public boolean isPresent() {
+ return false;
+ }
+
+ @Override
+ public void forAllMethods(Consumer<FoundMethodSubject> inspection) {
+ }
+
+ @Override
+ public MethodSubject method(String returnType, String name, List<String> parameters) {
+ return new AbsentMethodSubject();
+ }
+
+ @Override
+ public void forAllFields(Consumer<FoundFieldSubject> inspection) {
+ }
+
+ @Override
+ public FieldSubject field(String type, String name) {
+ return new AbsentFieldSubject();
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return false;
+ }
+
+ @Override
+ public DexClass getDexClass() {
+ return null;
+ }
+
+ @Override
+ public AnnotationSubject annotation(String name) {
+ return new AbsentAnnotationSubject();
+ }
+
+ @Override
+ public String getOriginalDescriptor() {
+ return null;
+ }
+
+ @Override
+ public String getFinalDescriptor() {
+ return null;
+ }
+ }
+
+ public class FoundClassSubject extends ClassSubject {
+
+ private final DexClass dexClass;
+ private final ClassNaming naming;
+
+ private FoundClassSubject(DexClass dexClass, ClassNaming naming) {
+ this.dexClass = dexClass;
+ this.naming = naming;
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ @Override
+ public void forAllMethods(Consumer<FoundMethodSubject> inspection) {
+ forAll(dexClass.directMethods(), FoundMethodSubject::new, this, inspection);
+ forAll(dexClass.virtualMethods(), FoundMethodSubject::new, this, inspection);
+ }
+
+ @Override
+ public MethodSubject method(String returnType, String name, List<String> parameters) {
+ DexType[] parameterTypes = new DexType[parameters.size()];
+ for (int i = 0; i < parameters.size(); i++) {
+ parameterTypes[i] = toDexType(getObfuscatedTypeName(parameters.get(i)));
+ }
+ DexProto proto = dexItemFactory.createProto(toDexType(getObfuscatedTypeName(returnType)),
+ parameterTypes);
+ if (naming != null) {
+ String[] parameterStrings = new String[parameterTypes.length];
+ Signature signature = new MethodSignature(name, returnType,
+ parameters.toArray(parameterStrings));
+ MemberNaming methodNaming = naming.lookupByOriginalSignature(signature);
+ if (methodNaming != null) {
+ name = methodNaming.getRenamedName();
+ }
+ }
+ DexMethod dexMethod =
+ dexItemFactory.createMethod(dexClass.type, proto, dexItemFactory.createString(name));
+ DexEncodedMethod encoded = findMethod(dexClass.directMethods(), dexMethod);
+ if (encoded == null) {
+ encoded = findMethod(dexClass.virtualMethods(), dexMethod);
+ }
+ return encoded == null ? new AbsentMethodSubject() : new FoundMethodSubject(encoded, this);
+ }
+
+ private DexEncodedMethod findMethod(DexEncodedMethod[] methods, DexMethod dexMethod) {
+ for (DexEncodedMethod method : methods) {
+ if (method.method.equals(dexMethod)) {
+ return method;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void forAllFields(Consumer<FoundFieldSubject> inspection) {
+ forAll(dexClass.staticFields(), FoundFieldSubject::new, this, inspection);
+ forAll(dexClass.instanceFields(), FoundFieldSubject::new, this, inspection);
+ }
+
+ @Override
+ public FieldSubject field(String type, String name) {
+ String obfuscatedType = getObfuscatedTypeName(type);
+ MemberNaming fieldNaming = null;
+ if (naming != null) {
+ fieldNaming = naming.lookupByOriginalSignature(
+ new FieldSignature(name, type));
+ }
+ String obfuscatedName = fieldNaming == null ? name : fieldNaming.getRenamedName();
+
+ DexField field = dexItemFactory.createField(dexClass.type,
+ toDexType(obfuscatedType), dexItemFactory.createString(obfuscatedName));
+ DexEncodedField encoded = findField(dexClass.staticFields(), field);
+ if (encoded == null) {
+ encoded = findField(dexClass.instanceFields(), field);
+ }
+ return encoded == null ? new AbsentFieldSubject() : new FoundFieldSubject(encoded, this);
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return dexClass.accessFlags.isAbstract();
+ }
+
+ private DexEncodedField findField(DexEncodedField[] fields, DexField dexField) {
+ for (DexEncodedField field : fields) {
+ if (field.field.equals(dexField)) {
+ return field;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public DexClass getDexClass() {
+ return dexClass;
+ }
+
+ @Override
+ public AnnotationSubject annotation(String name) {
+ DexAnnotation annotation = findAnnotation(name);
+ return annotation == null
+ ? new AbsentAnnotationSubject()
+ : new FoundAnnotationSubject(annotation);
+ }
+
+ private DexAnnotation findAnnotation(String name) {
+ for (DexAnnotation annotation : dexClass.annotations.annotations) {
+ DexType type = annotation.annotation.type;
+ String original = mapping == null ? type.toSourceString() : mapping.originalNameOf(type);
+ if (original.equals(name)) {
+ return annotation;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getOriginalDescriptor() {
+ if (naming != null) {
+ return DescriptorUtils.javaTypeToDescriptor(naming.originalName);
+ } else {
+ return getFinalDescriptor();
+ }
+ }
+
+ @Override
+ public String getFinalDescriptor() {
+ return dexClass.type.descriptor.toString();
+ }
+ }
+
+ public abstract class MemberSubject extends Subject {
+
+ public abstract boolean hasAll(DexAccessFlags flags);
+
+ public abstract boolean hasNone(DexAccessFlags flags);
+
+ public abstract boolean isStatic();
+
+ public abstract boolean isFinal();
+
+ public abstract Signature getOriginalSignature();
+ }
+
+ public abstract class MethodSubject extends MemberSubject {
+
+ public abstract boolean isAbstract();
+
+ public abstract DexEncodedMethod getMethod();
+
+ public Iterator<InstructionSubject> iterateInstructions() {
+ return null;
+ }
+
+ public <T extends InstructionSubject> Iterator<T> iterateInstructions(
+ Predicate<InstructionSubject> filter) {
+ return null;
+ }
+ }
+
+ public class AbsentMethodSubject extends MethodSubject {
+
+ @Override
+ public boolean isPresent() {
+ return false;
+ }
+
+ @Override
+ public boolean hasAll(DexAccessFlags flags) {
+ return false;
+ }
+
+ @Override
+ public boolean hasNone(DexAccessFlags flags) {
+ return true;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return false;
+ }
+
+ @Override
+ public boolean isFinal() {
+ return false;
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return false;
+ }
+
+ @Override
+ public DexEncodedMethod getMethod() {
+ return null;
+ }
+
+ @Override
+ public Signature getOriginalSignature() {
+ return null;
+ }
+ }
+
+ public class FoundMethodSubject extends MethodSubject {
+
+ private final FoundClassSubject clazz;
+ private final DexEncodedMethod dexMethod;
+
+ public FoundMethodSubject(DexEncodedMethod encoded, FoundClassSubject clazz) {
+ this.clazz = clazz;
+ this.dexMethod = encoded;
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ @Override
+ public boolean hasAll(DexAccessFlags flags) {
+ return dexMethod.accessFlags.containsAllOf(flags);
+ }
+
+ @Override
+ public boolean hasNone(DexAccessFlags flags) {
+ return dexMethod.accessFlags.containsNoneOf(flags);
+ }
+
+ @Override
+ public boolean isStatic() {
+ return dexMethod.accessFlags.isStatic();
+ }
+
+ @Override
+ public boolean isFinal() {
+ return dexMethod.accessFlags.isFinal();
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return dexMethod.accessFlags.isAbstract();
+ }
+
+ @Override
+ public DexEncodedMethod getMethod() {
+ return dexMethod;
+ }
+
+ @Override
+ public MethodSignature getOriginalSignature() {
+ MethodSignature signature = MemberNaming.MethodSignature.fromDexMethod(dexMethod.method);
+ return clazz.naming != null ?
+ (MethodSignature) clazz.naming.lookup(signature).getOriginalSignature() :
+ signature;
+ }
+
+ @Override
+ public Iterator<InstructionSubject> iterateInstructions() {
+ return new InstructionIterator(this);
+ }
+
+ @Override
+ public <T extends InstructionSubject> Iterator<T> iterateInstructions(
+ Predicate<InstructionSubject> filter) {
+ return new FilteredInstructionIterator<>(this, filter);
+ }
+ }
+
+ public abstract class FieldSubject extends MemberSubject {
+
+ public abstract DexEncodedField getField();
+ }
+
+ public class AbsentFieldSubject extends FieldSubject {
+
+ @Override
+ public boolean hasAll(DexAccessFlags flags) {
+ return false;
+ }
+
+ @Override
+ public boolean hasNone(DexAccessFlags flags) {
+ return true;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return false;
+ }
+
+ @Override
+ public boolean isFinal() {
+ return false;
+ }
+
+ @Override
+ public boolean isPresent() {
+ return false;
+ }
+
+ @Override
+ public Signature getOriginalSignature() {
+ return null;
+ }
+
+ @Override
+ public DexEncodedField getField() {
+ return null;
+ }
+ }
+
+ public class FoundFieldSubject extends FieldSubject {
+
+ private final FoundClassSubject clazz;
+ private final DexEncodedField dexField;
+
+ public FoundFieldSubject(DexEncodedField dexField, FoundClassSubject clazz) {
+ this.clazz = clazz;
+ this.dexField = dexField;
+ }
+
+ @Override
+ public boolean hasAll(DexAccessFlags flags) {
+ return dexField.accessFlags.containsAllOf(flags);
+ }
+
+ @Override
+ public boolean hasNone(DexAccessFlags flags) {
+ return dexField.accessFlags.containsNoneOf(flags);
+ }
+
+ @Override
+ public boolean isStatic() {
+ return dexField.accessFlags.isStatic();
+ }
+
+ @Override
+ public boolean isFinal() {
+ return dexField.accessFlags.isFinal();
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ public TypeSubject type() {
+ return new TypeSubject(dexField.field.type);
+ }
+
+ @Override
+ public FieldSignature getOriginalSignature() {
+ FieldSignature signature = MemberNaming.FieldSignature.fromDexField(dexField.field);
+ return clazz.naming != null ?
+ (FieldSignature) clazz.naming.lookup(signature).getOriginalSignature() :
+ signature;
+ }
+
+ @Override
+ public DexEncodedField getField() {
+ return dexField;
+ }
+ }
+
+ public class TypeSubject extends Subject {
+
+ private final DexType dexType;
+
+ public TypeSubject(DexType dexType) {
+ this.dexType = dexType;
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ public boolean is(String type) {
+ return dexType.equals(toDexType(type));
+ }
+
+ public String toString() {
+ return dexType.toSourceString();
+ }
+ }
+
+ private class InstructionSubjectFactory {
+
+ InstructionSubject create(Instruction instruction) {
+ if (isInvoke(instruction)) {
+ return new InvokeInstructionSubject(this, instruction);
+ } else if (isFieldAccess(instruction)) {
+ return new FieldAccessInstructionSubject(this, instruction);
+ } else {
+ return new InstructionSubject(this, instruction);
+ }
+ }
+
+ boolean isInvoke(Instruction instruction) {
+ return isInvokeVirtual(instruction)
+ || isInvokeInterface(instruction)
+ || isInvokeDirect(instruction)
+ || isInvokeSuper(instruction)
+ || isInvokeStatic(instruction);
+ }
+
+ boolean isInvokeVirtual(Instruction instruction) {
+ return instruction instanceof InvokeVirtual || instruction instanceof InvokeVirtualRange;
+ }
+
+ boolean isInvokeInterface(Instruction instruction) {
+ return instruction instanceof InvokeInterface || instruction instanceof InvokeInterfaceRange;
+ }
+
+ boolean isInvokeDirect(Instruction instruction) {
+ return instruction instanceof InvokeDirect || instruction instanceof InvokeDirectRange;
+ }
+
+ boolean isInvokeSuper(Instruction instruction) {
+ return instruction instanceof InvokeSuper || instruction instanceof InvokeSuperRange;
+ }
+
+ boolean isInvokeStatic(Instruction instruction) {
+ return instruction instanceof InvokeStatic || instruction instanceof InvokeStaticRange;
+ }
+
+ boolean isFieldAccess(Instruction instruction) {
+ return isInstanceGet(instruction)
+ || isInstancePut(instruction)
+ || isStaticGet(instruction)
+ || isStaticSet(instruction);
+ }
+
+ boolean isInstanceGet(Instruction instruction) {
+ return instruction instanceof Iget
+ || instruction instanceof IgetBoolean
+ || instruction instanceof IgetByte
+ || instruction instanceof IgetShort
+ || instruction instanceof IgetChar
+ || instruction instanceof IgetWide
+ || instruction instanceof IgetObject;
+ }
+
+ boolean isInstancePut(Instruction instruction) {
+ return instruction instanceof Iput
+ || instruction instanceof IputBoolean
+ || instruction instanceof IputByte
+ || instruction instanceof IputShort
+ || instruction instanceof IputChar
+ || instruction instanceof IputWide
+ || instruction instanceof IputObject;
+ }
+
+ boolean isStaticGet(Instruction instruction) {
+ return instruction instanceof Sget
+ || instruction instanceof SgetBoolean
+ || instruction instanceof SgetByte
+ || instruction instanceof SgetShort
+ || instruction instanceof SgetChar
+ || instruction instanceof SgetWide
+ || instruction instanceof SgetObject;
+ }
+
+ boolean isStaticSet(Instruction instruction) {
+ return instruction instanceof Sput
+ || instruction instanceof SputBoolean
+ || instruction instanceof SputByte
+ || instruction instanceof SputShort
+ || instruction instanceof SputChar
+ || instruction instanceof SputWide
+ || instruction instanceof SputObject;
+ }
+ }
+
+ public class InstructionSubject {
+
+ protected final InstructionSubjectFactory factory;
+ protected final Instruction instruction;
+
+ protected InstructionSubject(InstructionSubjectFactory factory, Instruction instruction) {
+ this.factory = factory;
+ this.instruction = instruction;
+ }
+
+ public boolean isInvoke() {
+ return factory.isInvoke(instruction);
+ }
+
+ public boolean isFieldAccess() {
+ return factory.isFieldAccess(instruction);
+ }
+
+ public boolean isInvokeVirtual() {
+ return factory.isInvokeVirtual(instruction);
+ }
+
+ public boolean isInvokeInterface() {
+ return factory.isInvokeInterface(instruction);
+ }
+
+ public boolean isInvokeDirect() {
+ return factory.isInvokeDirect(instruction);
+ }
+
+ public boolean isInvokeSuper() {
+ return factory.isInvokeSuper(instruction);
+ }
+
+ public boolean isInvokeStatic() {
+ return factory.isInvokeStatic(instruction);
+ }
+
+ boolean isFieldAccess(Instruction instruction) {
+ return factory.isFieldAccess(instruction);
+ }
+ }
+
+ public class InvokeInstructionSubject extends InstructionSubject {
+
+ InvokeInstructionSubject(InstructionSubjectFactory factory, Instruction instruction) {
+ super(factory, instruction);
+ assert isInvoke();
+ }
+
+ public TypeSubject holder() {
+ return new TypeSubject(invokedMethod().getHolder());
+ }
+
+ public DexMethod invokedMethod() {
+ if (instruction instanceof InvokeVirtual) {
+ return ((InvokeVirtual) instruction).getMethod();
+ }
+ if (instruction instanceof InvokeVirtualRange) {
+ return ((InvokeVirtualRange) instruction).getMethod();
+ }
+ if (instruction instanceof InvokeInterface) {
+ return ((InvokeInterface) instruction).getMethod();
+ }
+ if (instruction instanceof InvokeInterfaceRange) {
+ return ((InvokeInterfaceRange) instruction).getMethod();
+ }
+ if (instruction instanceof InvokeDirect) {
+ return ((InvokeDirect) instruction).getMethod();
+ }
+ if (instruction instanceof InvokeDirectRange) {
+ return ((InvokeDirectRange) instruction).getMethod();
+ }
+ if (instruction instanceof InvokeSuper) {
+ return ((InvokeSuper) instruction).getMethod();
+ }
+ if (instruction instanceof InvokeSuperRange) {
+ return ((InvokeSuperRange) instruction).getMethod();
+ }
+ if (instruction instanceof InvokeDirect) {
+ return ((InvokeDirect) instruction).getMethod();
+ }
+ if (instruction instanceof InvokeDirectRange) {
+ return ((InvokeDirectRange) instruction).getMethod();
+ }
+ assert false;
+ return null;
+ }
+ }
+
+ public class FieldAccessInstructionSubject extends InstructionSubject {
+
+ FieldAccessInstructionSubject(InstructionSubjectFactory factory, Instruction instruction) {
+ super(factory, instruction);
+ assert isFieldAccess();
+ }
+
+ public TypeSubject holder() {
+ return new TypeSubject(accessedField().getHolder());
+ }
+
+ public DexField accessedField() {
+ if (instruction instanceof Iget) {
+ return ((Iget) instruction).getField();
+ }
+ if (instruction instanceof IgetBoolean) {
+ return ((IgetBoolean) instruction).getField();
+ }
+ if (instruction instanceof IgetByte) {
+ return ((IgetByte) instruction).getField();
+ }
+ if (instruction instanceof IgetShort) {
+ return ((IgetShort) instruction).getField();
+ }
+ if (instruction instanceof IgetChar) {
+ return ((IgetChar) instruction).getField();
+ }
+ if (instruction instanceof IgetWide) {
+ return ((IgetWide) instruction).getField();
+ }
+ if (instruction instanceof IgetObject) {
+ return ((IgetObject) instruction).getField();
+ }
+ if (instruction instanceof Iput) {
+ return ((Iput) instruction).getField();
+ }
+ if (instruction instanceof IputBoolean) {
+ return ((IputBoolean) instruction).getField();
+ }
+ if (instruction instanceof IputByte) {
+ return ((IputByte) instruction).getField();
+ }
+ if (instruction instanceof IputShort) {
+ return ((IputShort) instruction).getField();
+ }
+ if (instruction instanceof IputChar) {
+ return ((IputChar) instruction).getField();
+ }
+ if (instruction instanceof IputWide) {
+ return ((IputWide) instruction).getField();
+ }
+ if (instruction instanceof IputObject) {
+ return ((IputObject) instruction).getField();
+ }
+ if (instruction instanceof Sget) {
+ return ((Sget) instruction).getField();
+ }
+ if (instruction instanceof SgetBoolean) {
+ return ((SgetBoolean) instruction).getField();
+ }
+ if (instruction instanceof SgetByte) {
+ return ((SgetByte) instruction).getField();
+ }
+ if (instruction instanceof SgetShort) {
+ return ((SgetShort) instruction).getField();
+ }
+ if (instruction instanceof SgetChar) {
+ return ((SgetChar) instruction).getField();
+ }
+ if (instruction instanceof SgetWide) {
+ return ((SgetWide) instruction).getField();
+ }
+ if (instruction instanceof SgetObject) {
+ return ((SgetObject) instruction).getField();
+ }
+ if (instruction instanceof Sput) {
+ return ((Sput) instruction).getField();
+ }
+ if (instruction instanceof SputBoolean) {
+ return ((SputBoolean) instruction).getField();
+ }
+ if (instruction instanceof SputByte) {
+ return ((SputByte) instruction).getField();
+ }
+ if (instruction instanceof SputShort) {
+ return ((SputShort) instruction).getField();
+ }
+ if (instruction instanceof SputChar) {
+ return ((SputChar) instruction).getField();
+ }
+ if (instruction instanceof SputWide) {
+ return ((SputWide) instruction).getField();
+ }
+ if (instruction instanceof SputObject) {
+ return ((SputObject) instruction).getField();
+ }
+ assert false;
+ return null;
+ }
+ }
+
+ private class InstructionIterator implements Iterator<InstructionSubject> {
+
+ private final DexCode code;
+ private int index;
+
+ InstructionIterator(MethodSubject method) {
+ assert method.isPresent();
+ this.code = method.getMethod().getCode().asDexCode();
+ this.index = 0;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return index < code.instructions.length;
+ }
+
+ @Override
+ public InstructionSubject next() {
+ if (index == code.instructions.length) {
+ throw new NoSuchElementException();
+ }
+ return factory.create(code.instructions[index++]);
+ }
+ }
+
+ private class FilteredInstructionIterator<T extends InstructionSubject> implements Iterator<T> {
+
+ private final InstructionIterator iterator;
+ private final Predicate<InstructionSubject> predicate;
+ private InstructionSubject pendingNext = null;
+
+ FilteredInstructionIterator(MethodSubject method, Predicate<InstructionSubject> predicate) {
+ this.iterator = new InstructionIterator(method);
+ this.predicate = predicate;
+ hasNext();
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (pendingNext == null) {
+ while (iterator.hasNext()) {
+ pendingNext = iterator.next();
+ if (predicate.test(pendingNext)) {
+ break;
+ }
+ pendingNext = null;
+ }
+ }
+ return pendingNext != null;
+ }
+
+ @Override
+ public T next() {
+ hasNext();
+ if (pendingNext == null) {
+ throw new NoSuchElementException();
+ }
+ // We cannot tell if the provided predicate will only match instruction subjects of type T.
+ @SuppressWarnings("unchecked")
+ T result = (T) pendingNext;
+ pendingNext = null;
+ return result;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/HashMapIntTest.java b/src/test/java/com/android/tools/r8/utils/HashMapIntTest.java
new file mode 100644
index 0000000..5d8fffe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/HashMapIntTest.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.util.Random;
+import java.util.Set;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class HashMapIntTest {
+
+ static private String getString(int i) {
+ String SALTCHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+ StringBuilder salt = new StringBuilder();
+ Random rnd = new Random();
+ while (salt.length() < 18) {
+ int index = (int) (rnd.nextFloat() * SALTCHARS.length());
+ salt.append(SALTCHARS.charAt(index));
+ }
+ salt.append(" ");
+ salt.append(i);
+ return salt.toString();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void putInvalid() throws Exception {
+ HashMapInt<Object> map = new HashMapInt<>();
+ map.put(null, 12);
+ }
+
+ @Test
+ public void put() throws Exception {
+ HashMapInt<Object> map = new HashMapInt<>();
+ Object key = new Object();
+ int value = 0;
+ map.put(key, value);
+ Assert.assertTrue(map.containsKey(key));
+ Assert.assertFalse(map.containsKey(new Object[0]));
+ Assert.assertEquals(map.get(key), value);
+ Assert.assertEquals(map.size(), 1);
+ }
+
+ @Test
+ public void random() throws Exception {
+ final int length = 5999;
+ HashMapInt<String> map = new HashMapInt<>();
+ String[] array = new String[length];
+ for (int i = 0; i < length; i++) {
+ array[i] = getString(i);
+ map.put(array[i], i * 3);
+ }
+ Assert.assertEquals(length, map.size());
+ for (int i = 0; i < length; i++) {
+ Assert.assertEquals(map.get(array[i]), i * 3);
+ }
+ for (int i : map.values()) {
+ Assert.assertTrue(i < length * 3 && i >= 0 && i % 3 == 0);
+ }
+ Assert.assertEquals(length, Iterables.size(map.values()));
+ Set<String> items = ImmutableSet.copyOf(array);
+ for (String s : map.keys()) {
+ Assert.assertTrue(items.contains(s));
+ }
+ Assert.assertEquals(length, Iterables.size(map.keys()));
+ }
+
+ @Test
+ public void overwrite() throws Exception {
+ HashMapInt<Object> map = new HashMapInt<>();
+ Object key = new Object();
+ int value1 = 0;
+ map.put(key, value1);
+ Assert.assertEquals(map.get(key), value1);
+ int value2 = -1;
+ map.put(key, value2);
+ Assert.assertEquals(map.get(key), value2);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/IdentityHashMapIntTest.java b/src/test/java/com/android/tools/r8/utils/IdentityHashMapIntTest.java
new file mode 100644
index 0000000..dd57960
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/IdentityHashMapIntTest.java
@@ -0,0 +1,78 @@
+// 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.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Set;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class IdentityHashMapIntTest {
+
+ @Test(expected = RuntimeException.class)
+ public void putInvalid() throws Exception {
+ IdentityHashMapInt<Object> map = new IdentityHashMapInt<>();
+ map.put(null, 12);
+ }
+
+ @Test
+ public void put() throws Exception {
+ IdentityHashMapInt<Object> map = new IdentityHashMapInt<>();
+ Object key = new AllEqual();
+ int value = 0;
+ map.put(key, value);
+ Assert.assertTrue(map.containsKey(key));
+ Assert.assertFalse(map.containsKey(new Object()));
+ Assert.assertFalse(map.containsKey(new AllEqual()));
+ Assert.assertEquals(map.get(key), value);
+ Assert.assertEquals(map.size(), 1);
+ }
+
+ @Test
+ public void random() throws Exception {
+ final int length = 5999;
+ IdentityHashMapInt<Object> map = new IdentityHashMapInt<>();
+ AllEqual[] array = new AllEqual[length];
+ for (int i = 0; i < length; i++) {
+ array[i] = new AllEqual();
+ map.put(array[i], i * 3);
+ }
+ Assert.assertEquals(length, map.size());
+ for (int i = 0; i < length; i++) {
+ Assert.assertEquals(map.get(array[i]), i * 3);
+ }
+ for (int i : map.values()) {
+ Assert.assertTrue(i < length * 3 && i >= 0 && i % 3 == 0);
+ }
+ Assert.assertEquals(length, Iterables.size(map.values()));
+ Set<Object> items = Sets.newIdentityHashSet();
+ Collections.addAll(items, array);
+ for (Object o : map.keys()) {
+ Assert.assertTrue(items.contains(o));
+ }
+ Assert.assertEquals(length, Iterables.size(map.keys()));
+ }
+
+ @Test
+ public void overwrite() throws Exception {
+ IdentityHashMapInt<Object> map = new IdentityHashMapInt<>();
+ Object key = new Object();
+ int value1 = 0;
+ map.put(key, value1);
+ Assert.assertEquals(map.get(key), value1);
+ int value2 = -1;
+ map.put(key, value2);
+ Assert.assertEquals(map.get(key), value2);
+ }
+
+ private static class AllEqual {
+
+ @Override
+ public boolean equals(Object o) {
+ return true;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/IntHashMapTest.java b/src/test/java/com/android/tools/r8/utils/IntHashMapTest.java
new file mode 100644
index 0000000..c4b4dc2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/IntHashMapTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.util.Random;
+import java.util.Set;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class IntHashMapTest {
+
+ static private String getRandomString() {
+ String SALTCHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+ StringBuilder salt = new StringBuilder();
+ Random rnd = new Random();
+ while (salt.length() < 18) {
+ int index = (int) (rnd.nextFloat() * SALTCHARS.length());
+ salt.append(SALTCHARS.charAt(index));
+ }
+ return salt.toString();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void putInvalid() throws Exception {
+ IntHashMap<Object> map = new IntHashMap<>();
+ map.put(12, null);
+ }
+
+ @Test
+ public void put() throws Exception {
+ IntHashMap<Object> map = new IntHashMap<>();
+ int key = 1;
+ Object value = new Object();
+ map.put(key, value);
+ Assert.assertEquals(map.get(key), value);
+ Assert.assertEquals(map.size(), 1);
+ map.put(key + 1, value);
+ Assert.assertEquals(map.get(key + 1), value);
+ Assert.assertEquals(map.size(), 2);
+ Assert.assertEquals(map.get(0), null);
+ map.put(0, value);
+ Assert.assertEquals(map.get(0), value);
+ }
+
+ @Test
+ public void random() throws Exception {
+ final int length = 5999;
+ IntHashMap<String> map = new IntHashMap<>();
+ String[] array = new String[length];
+ for (int i = 0; i < length; i++) {
+ array[i] = getRandomString();
+ map.put(i, array[i]);
+ }
+ Assert.assertEquals(length, map.size());
+ for (int i = 0; i < length; i++) {
+ Assert.assertEquals(map.get(i), array[i]);
+ }
+ Set<String> items = ImmutableSet.copyOf(array);
+ for (String s : map.values()) {
+ Assert.assertTrue(items.contains(s));
+ }
+ Assert.assertEquals(length, Iterables.size(map.values()));
+ for (int i : map.keys()) {
+ Assert.assertTrue(i < length && i >= 0);
+ }
+ Assert.assertEquals(length, Iterables.size(map.keys()));
+ }
+
+ @Test
+ public void overwrite() throws Exception {
+ IntHashMap<Object> map = new IntHashMap<>();
+ int key = 1;
+ Object value1 = new Object();
+ map.put(key, value1);
+ Assert.assertEquals(map.get(key), value1);
+ Object value2 = new Object[0];
+ map.put(key, value2);
+ Assert.assertEquals(map.get(key), value2);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/IntIntHashMapTest.java b/src/test/java/com/android/tools/r8/utils/IntIntHashMapTest.java
new file mode 100644
index 0000000..d129a03
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/IntIntHashMapTest.java
@@ -0,0 +1,75 @@
+// 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.google.common.collect.Iterables;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Random;
+import java.util.Set;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class IntIntHashMapTest {
+
+ @Test(expected = RuntimeException.class)
+ public void putInvalid() throws Exception {
+ IntIntHashMap map = new IntIntHashMap();
+ map.put(-1, 12);
+ }
+
+ @Test
+ public void put() throws Exception {
+ IntIntHashMap map = new IntIntHashMap();
+ int key = 22;
+ int value = 0;
+ map.put(key, value);
+ Assert.assertTrue(map.containsKey(key));
+ Assert.assertFalse(map.containsKey(33));
+ Assert.assertEquals(map.get(key), value);
+ Assert.assertEquals(map.size(), 1);
+ }
+
+ @Test
+ public void random() throws Exception {
+ final int length = 5999;
+ IntIntHashMap map = new IntIntHashMap();
+ Random rnd = new Random();
+ Set<Integer> seen = new LinkedHashSet<>();
+ String[] array = new String[length];
+ for (int i = 0; i < length; i++) {
+ int next;
+ do {
+ next = rnd.nextInt(4 * length);
+ } while (seen.contains(next));
+ seen.add(next);
+ map.put(next, i * 3);
+ }
+ Assert.assertEquals(seen.size(), map.size());
+ Iterator<Integer> it = seen.iterator();
+ for (int i = 0; i < length; i++) {
+ Assert.assertEquals(map.get(it.next()), i * 3);
+ }
+ for (int i : map.values()) {
+ Assert.assertTrue(i < length * 3 && i >= 0 && i % 3 == 0);
+ }
+ Assert.assertEquals(length, Iterables.size(map.values()));
+ for (Integer key : map.keys()) {
+ Assert.assertTrue(seen.contains(key));
+ }
+ Assert.assertEquals(seen.size(), Iterables.size(map.keys()));
+ }
+
+ @Test
+ public void overwrite() throws Exception {
+ IntIntHashMap map = new IntIntHashMap();
+ int key = 42;
+ int value1 = 0;
+ map.put(key, value1);
+ Assert.assertEquals(map.get(key), value1);
+ int value2 = -1;
+ map.put(key, value2);
+ Assert.assertEquals(map.get(key), value2);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/JarBuilder.java b/src/test/java/com/android/tools/r8/utils/JarBuilder.java
new file mode 100644
index 0000000..9186ff4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/JarBuilder.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.google.common.io.ByteStreams;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+
+public class JarBuilder {
+ public static void buildJar(File[] files, File jarFile) throws IOException {
+ JarOutputStream target = new JarOutputStream(new FileOutputStream(jarFile));
+ for (File file : files) {
+ // Only use the file name in the JAR entry (classes.dex, classes2.dex, ...)
+ JarEntry entry = new JarEntry(file.getName());
+ entry.setTime(file.lastModified());
+ target.putNextEntry(entry);
+ InputStream in = new BufferedInputStream(new FileInputStream(file));
+ ByteStreams.copy(in, target);
+ in.close();
+ target.closeEntry();
+ }
+ target.close();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/R8CommandTest.java b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
new file mode 100644
index 0000000..a6e10a8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
@@ -0,0 +1,149 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+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;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public class R8CommandTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void emptyCommand() throws Throwable {
+ verifyEmptyCommand(R8Command.builder().build());
+ verifyEmptyCommand(parse());
+ }
+
+ private void verifyEmptyCommand(R8Command command) {
+ assertEquals(0, ToolHelper.getApp(command).getDexProgramResources().size());
+ assertEquals(0, ToolHelper.getApp(command).getClassProgramResources().size());
+ assertEquals(0, ToolHelper.getApp(command).getDexLibraryResources().size());
+ assertEquals(0, ToolHelper.getApp(command).getClassLibraryResources().size());
+ assertFalse(ToolHelper.getApp(command).hasMainDexList());
+ assertFalse(ToolHelper.getApp(command).hasProguardMap());
+ assertFalse(ToolHelper.getApp(command).hasProguardSeeds());
+ assertFalse(ToolHelper.getApp(command).hasPackageDistribution());
+ assertNull(command.getOutputPath());
+ assertFalse(command.useMinification());
+ assertFalse(command.useTreeShaking());
+ assertEquals(CompilationMode.RELEASE, command.getMode());
+ }
+
+ @Test
+ public void defaultOutIsCwd() throws IOException, InterruptedException {
+ Path working = temp.getRoot().toPath();
+ Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar").toAbsolutePath();
+ Path output = working.resolve("classes.dex");
+ assertFalse(Files.exists(output));
+ ProcessResult result = ToolHelper.forkR8(working, input.toString());
+ assertEquals("R8 run failed: " + result.stderr, 0, result.exitCode);
+ assertTrue(Files.exists(output));
+ }
+
+ @Test
+ public void validOutputPath() throws Throwable {
+ Path existingDir = temp.getRoot().toPath();
+ Path nonExistingZip = existingDir.resolve("a-non-existing-archive.zip");
+ assertEquals(
+ existingDir,
+ R8Command.builder().setOutputPath(existingDir).build().getOutputPath());
+ assertEquals(
+ nonExistingZip,
+ R8Command.builder().setOutputPath(nonExistingZip).build().getOutputPath());
+ assertEquals(
+ existingDir,
+ parse("--output", existingDir.toString()).getOutputPath());
+ assertEquals(
+ nonExistingZip,
+ parse("--output", nonExistingZip.toString()).getOutputPath());
+ }
+
+ @Test
+ public void nonExistingOutputDir() throws Throwable {
+ thrown.expect(CompilationException.class);
+ Path nonExistingDir = temp.getRoot().toPath().resolve("a/path/that/does/not/exist");
+ R8Command.builder().setOutputPath(nonExistingDir).build();
+ }
+
+ @Test
+ public void existingOutputZip() throws Throwable {
+ thrown.expect(CompilationException.class);
+ Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
+ R8Command.builder().setOutputPath(existingZip).build();
+ }
+
+ @Test
+ public void invalidOutputFileType() throws Throwable {
+ thrown.expect(CompilationException.class);
+ Path invalidType = temp.getRoot().toPath().resolve("an-invalid-output-file-type.foobar");
+ R8Command.builder().setOutputPath(invalidType).build();
+ }
+
+ @Test
+ public void nonExistingOutputDirParse() throws Throwable {
+ thrown.expect(CompilationException.class);
+ Path nonExistingDir = temp.getRoot().toPath().resolve("a/path/that/does/not/exist");
+ parse("--output", nonExistingDir.toString());
+ }
+
+ @Test
+ public void existingOutputZipParse() throws Throwable {
+ thrown.expect(CompilationException.class);
+ Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
+ parse("--output", existingZip.toString());
+ }
+
+ @Test
+ public void invalidOutputFileTypeParse() throws Throwable {
+ thrown.expect(CompilationException.class);
+ Path invalidType = temp.getRoot().toPath().resolve("an-invalid-output-file-type.foobar");
+ parse("--output", invalidType.toString());
+ }
+
+ @Test
+ public void argumentsInFile() throws Throwable {
+ Path inputFile = temp.newFile("foobar.dex").toPath();
+ Path pgConfFile = temp.newFile("pgconf.config").toPath();
+ Path argsFile = temp.newFile("more-args.txt").toPath();
+ FileUtils.writeTextFile(argsFile, ImmutableList.of(
+ "--debug --no-minification",
+ "--pg-conf " + pgConfFile,
+ inputFile.toString()
+ ));
+ R8Command command = parse("@" + argsFile.toString());
+ assertEquals(CompilationMode.DEBUG, command.getMode());
+ assertFalse(command.useMinification());
+ assertTrue(command.useTreeShaking());
+ assertEquals(1, ToolHelper.getApp(command).getDexProgramResources().size());
+ }
+
+ private R8Command parse(String... args)
+ throws CompilationException, ProguardRuleParserException, IOException {
+ return R8Command.parse(args).build();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/R8InliningTest.java b/src/test/java/com/android/tools/r8/utils/R8InliningTest.java
new file mode 100644
index 0000000..50db347
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/R8InliningTest.java
@@ -0,0 +1,138 @@
+// 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 static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class R8InliningTest {
+
+ private static final String JAR_EXTENSION = ".jar";
+ private static final String DEFAULT_DEX_FILENAME = "classes.dex";
+ private static final String DEFAULT_MAP_FILENAME = "proguard.map";
+
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][]{{"Inlining"}});
+ }
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+ private final String name;
+ private final String keepRulesFile;
+
+ public R8InliningTest(String name) {
+ this.name = name.toLowerCase();
+ this.keepRulesFile = ToolHelper.EXAMPLES_DIR + this.name + "/keep-rules.txt";
+ }
+
+ private Path getInputFile() {
+ return Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, name + JAR_EXTENSION);
+ }
+
+ private Path getOriginalDexFile() {
+ return Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, name, DEFAULT_DEX_FILENAME);
+ }
+
+ private Path getGeneratedDexFile() throws IOException {
+ return Paths.get(temp.getRoot().getCanonicalPath(), DEFAULT_DEX_FILENAME);
+ }
+
+ private String getGeneratedProguardMap() throws IOException {
+ Path mapFile = Paths.get(temp.getRoot().getCanonicalPath(), DEFAULT_MAP_FILENAME);
+ if (Files.exists(mapFile)) {
+ return mapFile.toAbsolutePath().toString();
+ }
+ return null;
+ }
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Before
+ public void generateR8Version()
+ throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+ Path out = temp.getRoot().toPath();
+ R8Command command =
+ R8Command.builder()
+ .addProgramFiles(getInputFile())
+ .setOutputPath(out)
+ .addProguardConfigurationFiles(Paths.get(keepRulesFile))
+ .build();
+ ToolHelper.runR8(command);
+ ToolHelper.runArtNoVerificationErrors(out + "/classes.dex", "inlining.Inlining");
+ }
+
+ private void checkAbsent(ClassSubject clazz, String name) {
+ assertTrue(clazz.isPresent());
+ MethodSubject method = clazz.method("boolean", name, Collections.emptyList());
+ assertFalse(method.isPresent());
+ }
+
+ private void dump(DexEncodedMethod method) {
+ System.out.println(method);
+ System.out.println(method.codeToString());
+ }
+
+ private void dump(Path path, String title) throws Throwable {
+ System.out.println(title + ":");
+ DexInspector inspector = new DexInspector(path.toAbsolutePath());
+ inspector.clazz("inlining.Inlining").forAllMethods(m -> dump(m.getMethod()));
+ System.out.println(title + " size: " + Files.size(path));
+ }
+
+ @Test
+ public void checkNoInvokes() throws Throwable {
+ DexInspector inspector = new DexInspector(getGeneratedDexFile().toAbsolutePath(),
+ getGeneratedProguardMap());
+ ClassSubject clazz = inspector.clazz("inlining.Inlining");
+ // Simple constant inlining.
+ checkAbsent(clazz, "longExpression");
+ checkAbsent(clazz, "intExpression");
+ checkAbsent(clazz, "doubleExpression");
+ checkAbsent(clazz, "floatExpression");
+ // Simple return argument inlining.
+ checkAbsent(clazz, "longArgumentExpression");
+ checkAbsent(clazz, "intArgumentExpression");
+ checkAbsent(clazz, "doubleArgumentExpression");
+ checkAbsent(clazz, "floatArgumentExpression");
+ }
+
+ @Test
+ public void processedFileIsSmaller() throws Throwable {
+ long original = Files.size(getOriginalDexFile());
+ long generated = Files.size(getGeneratedDexFile());
+ final boolean ALWAYS_DUMP = false; // Used for debugging.
+ if (ALWAYS_DUMP || generated > original) {
+ dump(getOriginalDexFile(), "Original");
+ dump(getGeneratedDexFile(), "Generated");
+ }
+ assertTrue("Inlining failed to reduce size", original > generated);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
new file mode 100644
index 0000000..fbc5e16
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.List;
+import org.antlr.runtime.CommonTokenStream;
+import org.antlr.runtime.RecognitionException;
+import org.antlr.runtime.TokenSource;
+import org.antlr.runtime.tree.CommonTree;
+import org.antlr.runtime.tree.CommonTreeNodeStream;
+import org.jf.dexlib2.Opcodes;
+import org.jf.dexlib2.writer.builder.DexBuilder;
+import org.jf.dexlib2.writer.io.MemoryDataStore;
+import org.jf.smali.LexerErrorInterface;
+import org.jf.smali.smaliFlexLexer;
+import org.jf.smali.smaliParser;
+import org.jf.smali.smaliTreeWalker;
+
+// Adapted from org.jf.smali.SmaliTestUtils.
+public class Smali {
+
+ public static byte[] compile(String smaliText) throws RecognitionException, IOException {
+ return compile(smaliText, 15);
+ }
+
+ public static byte[] compile(String... smaliText) throws RecognitionException, IOException {
+ return compile(Arrays.asList(smaliText), 15);
+ }
+
+ public static byte[] compile(String smaliText, int apiLevel)
+ throws IOException, RecognitionException {
+ return compile(ImmutableList.of(smaliText), apiLevel);
+ }
+
+ public static byte[] compile(List<String> smaliTexts)
+ throws RecognitionException, IOException {
+ return compile(smaliTexts, 15);
+ }
+
+ public static byte[] compile(List<String> smaliTexts, int apiLevel)
+ throws RecognitionException, IOException {
+ DexBuilder dexBuilder = new DexBuilder(Opcodes.forApi(apiLevel));
+
+ for (String smaliText : smaliTexts) {
+ Reader reader = new StringReader(smaliText);
+
+ LexerErrorInterface lexer = new smaliFlexLexer(reader);
+ CommonTokenStream tokens = new CommonTokenStream((TokenSource) lexer);
+
+ smaliParser parser = new smaliParser(tokens);
+ parser.setVerboseErrors(true);
+ parser.setAllowOdex(false);
+ parser.setApiLevel(apiLevel);
+
+ smaliParser.smali_file_return result = parser.smali_file();
+
+ if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) {
+ throw new RuntimeException(
+ "Error occured while compiling text:\n" + StringUtils.join(smaliTexts, "\n"));
+ }
+
+ CommonTree t = result.getTree();
+
+ CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
+ treeStream.setTokenStream(tokens);
+
+ smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
+ dexGen.setApiLevel(apiLevel);
+ dexGen.setVerboseErrors(true);
+ dexGen.setDexBuilder(dexBuilder);
+ dexGen.smali_file();
+
+ if (dexGen.getNumberOfSyntaxErrors() > 0) {
+ throw new RuntimeException("Error occured while compiling text");
+ }
+ }
+
+ MemoryDataStore dataStore = new MemoryDataStore();
+
+ dexBuilder.writeTo(dataStore);
+
+ // TODO(sgjesse): This returns the full backingstore from MemoryDataStore, which by default
+ // is 1024k bytes. Our dex file reader does not complain though.
+ return dataStore.getData();
+ }
+}
diff --git a/src/test/proguard/invalid/including-1.flags b/src/test/proguard/invalid/including-1.flags
new file mode 100644
index 0000000..c61ff34
--- /dev/null
+++ b/src/test/proguard/invalid/including-1.flags
@@ -0,0 +1,6 @@
+# Some empty lines to move error away from first lines
+
+
+
+
+-include does-not-exist.flags
\ No newline at end of file
diff --git a/src/test/proguard/invalid/including-2.flags b/src/test/proguard/invalid/including-2.flags
new file mode 100644
index 0000000..e1018ee
--- /dev/null
+++ b/src/test/proguard/invalid/including-2.flags
@@ -0,0 +1,6 @@
+# Some empty lines to move error away from first lines
+
+
+
+
+@does-not-exist.flags
\ No newline at end of file
diff --git a/src/test/proguard/valid/access-flags.flags b/src/test/proguard/valid/access-flags.flags
new file mode 100644
index 0000000..8096b59
--- /dev/null
+++ b/src/test/proguard/valid/access-flags.flags
@@ -0,0 +1,4 @@
+-keep !public abstract !final @com.company.Something class * {
+ public !static <fields>;
+ !public !protected ! volatile <methods>;
+}
\ No newline at end of file
diff --git a/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags b/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags
new file mode 100644
index 0000000..5f9485e
--- /dev/null
+++ b/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags
@@ -0,0 +1,11 @@
+# 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.
+
+-assumenosideeffects class com.example.FeatureFlags {
+ public static boolean returnsTrue() return true;
+ public static boolean returnsFalse() return false;
+ public static int returns1() return 1;
+ public static int returns2To4() return 2..4;
+ public static int returnsField() return com.google.C.X;
+}
diff --git a/src/test/proguard/valid/assume-no-side-effects.flags b/src/test/proguard/valid/assume-no-side-effects.flags
new file mode 100644
index 0000000..9377b0b
--- /dev/null
+++ b/src/test/proguard/valid/assume-no-side-effects.flags
@@ -0,0 +1,11 @@
+# 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.
+
+-assumenosideeffects class android.util.Log {
+ public static boolean isLoggable(java.lang.String, int);
+ public static int v(...);
+ public static int i(...);
+ public static int d(...);
+}
+
diff --git a/src/test/proguard/valid/assume-values-with-return-value.flags b/src/test/proguard/valid/assume-values-with-return-value.flags
new file mode 100644
index 0000000..6877f10
--- /dev/null
+++ b/src/test/proguard/valid/assume-values-with-return-value.flags
@@ -0,0 +1,11 @@
+# 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.
+
+-assumevalues class com.example.SomeClass {
+ public static final boolean isTrue() return true;
+ public static final boolean isFalse() return false;
+ public static final int is1() return 1;
+ public static final int is2To4() return 2..4;
+ public static final int isField() return com.google.C.X;
+}
\ No newline at end of file
diff --git a/src/test/proguard/valid/dontobfuscate.flags b/src/test/proguard/valid/dontobfuscate.flags
new file mode 100644
index 0000000..0674e77
--- /dev/null
+++ b/src/test/proguard/valid/dontobfuscate.flags
@@ -0,0 +1 @@
+-dontobfuscate
\ No newline at end of file
diff --git a/src/test/proguard/valid/dontoptimize.flags b/src/test/proguard/valid/dontoptimize.flags
new file mode 100644
index 0000000..50ed61c
--- /dev/null
+++ b/src/test/proguard/valid/dontoptimize.flags
@@ -0,0 +1,5 @@
+# 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.
+
+-dontoptimize
diff --git a/src/test/proguard/valid/dontskipnonpubliclibraryclasses.flags b/src/test/proguard/valid/dontskipnonpubliclibraryclasses.flags
new file mode 100644
index 0000000..3e76e60
--- /dev/null
+++ b/src/test/proguard/valid/dontskipnonpubliclibraryclasses.flags
@@ -0,0 +1,5 @@
+# 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.
+
+-dontskipnonpubliclibraryclasses
diff --git a/src/test/proguard/valid/empty.flags b/src/test/proguard/valid/empty.flags
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/test/proguard/valid/empty.flags
diff --git a/src/test/proguard/valid/ignorewarnings.flags b/src/test/proguard/valid/ignorewarnings.flags
new file mode 100644
index 0000000..7079fd3
--- /dev/null
+++ b/src/test/proguard/valid/ignorewarnings.flags
@@ -0,0 +1 @@
+-ignorewarnings
\ No newline at end of file
diff --git a/src/test/proguard/valid/including.flags b/src/test/proguard/valid/including.flags
new file mode 100644
index 0000000..a2e3e71
--- /dev/null
+++ b/src/test/proguard/valid/including.flags
@@ -0,0 +1,10 @@
+-include empty.flags
+@empty.flags
+-include sub/sub-empty.flags
+@sub/sub-empty.flags
+-basedirectory sub
+-include sub-including.flags
+@sub-including.flags
+-basedirectory ..
+-include sub/sub-including.flags
+@sub/sub-including.flags
diff --git a/src/test/proguard/valid/library-jars.flags b/src/test/proguard/valid/library-jars.flags
new file mode 100644
index 0000000..9cb77d4
--- /dev/null
+++ b/src/test/proguard/valid/library-jars.flags
@@ -0,0 +1,2 @@
+-libraryjars some/library.jar
+-libraryjars some/library.jar:some/other/library.jar:yet/another/library.jar
\ No newline at end of file
diff --git a/src/test/proguard/valid/multiple-name-patterns.flags b/src/test/proguard/valid/multiple-name-patterns.flags
new file mode 100644
index 0000000..3d996a5
--- /dev/null
+++ b/src/test/proguard/valid/multiple-name-patterns.flags
@@ -0,0 +1,6 @@
+-keep class
+ com.company.hello.**,
+ com.company.world.**
+ extends some.library.Class {
+ protected java.lang.Object[][] getContents();
+}
\ No newline at end of file
diff --git a/src/test/proguard/valid/parse-and-skip-single-argument.flags b/src/test/proguard/valid/parse-and-skip-single-argument.flags
new file mode 100644
index 0000000..ef5ba4c
--- /dev/null
+++ b/src/test/proguard/valid/parse-and-skip-single-argument.flags
@@ -0,0 +1 @@
+-renamesourcefileattribute RenamedAttribute
\ No newline at end of file
diff --git a/src/test/proguard/valid/proguard.flags b/src/test/proguard/valid/proguard.flags
new file mode 100644
index 0000000..99692d3
--- /dev/null
+++ b/src/test/proguard/valid/proguard.flags
@@ -0,0 +1,95 @@
+# This is a comment.
+
+ # I bet there will be many of those.
+
+-keep @Annotation class hello.Hello {
+ public *;
+}
+
+-keep @Annotation class hello.Hello {
+ public <methods>;
+}
+
+-keep @Annotation class hello.Hello {
+ public <fields>;
+}
+
+-keep @Annotation class hello.Hello {
+ public <init>();
+}
+
+-keep @Annotation class hello.Hello {
+ public <init>(hello.***.B, hello.**.A, hello.?);
+}
+
+-keep @Annotation class hello.Hello{ # Missing space before '{' on purpose.
+ public <init>(hello.A);
+}
+
+-keep class hello.Hello {
+ public static static void main(java.lang.String[]);
+}
+
+-keep class hello.Hello {
+ public static static % method(...);
+}
+
+-keep class hello.Hello {
+ public static boolean theFlag;
+}
+
+-keep class hello.Hello {
+ public Hello(...);
+}
+
+-keepnames class hello.Hello extends Hello.Base {
+ public *;
+}
+
+-keepclassmembers class hello.Hello implements @Hep Hello.Base {
+ public *;
+}
+
+-keepclasseswithmembers class hello.Hello {
+ public *;
+}
+
+-keepclassmembernames class hello.Hello {
+ public *;
+}
+
+-keepclasseswithmembernames class hello.Hello {
+ public *;
+}
+
+-keepclassmembers,includedescriptorclasses @com.company.Annotation* public class **JNI {
+ public static *** methodPrefix_*(...);
+}
+
+-keepclassmembernames,includedescriptorclasses @com.company.Annotation* public class * {
+ public <methods>;
+ protected <methods>;
+}
+
+-keepclasseswithmembernames public class **JNI {
+ public final static native <methods>;
+}
+
+-keep class com.company.some.package.MyClass
+
+-keep @interface com.company.SomeAnnotation
+
+-keep @com.company.SomeAnnotation class *
+
+-keepclasseswithmembers class * {
+ @com.company.SomeAnnotation <fields>;
+}
+
+-keepclasseswithmembers class * {
+ @com.company.SomeAnnotation <methods>;
+}
+
+-keep public @com.company.Something class * {
+ public <fields>;
+ public <methods>;
+}
diff --git a/src/test/proguard/valid/seeds-2.flags b/src/test/proguard/valid/seeds-2.flags
new file mode 100644
index 0000000..18ce6b5
--- /dev/null
+++ b/src/test/proguard/valid/seeds-2.flags
@@ -0,0 +1 @@
+-printseeds path/to/seedfile.txt
\ No newline at end of file
diff --git a/src/test/proguard/valid/seeds.flags b/src/test/proguard/valid/seeds.flags
new file mode 100644
index 0000000..c4d1ca2
--- /dev/null
+++ b/src/test/proguard/valid/seeds.flags
@@ -0,0 +1 @@
+-printseeds
\ No newline at end of file
diff --git a/src/test/proguard/valid/skipnonpubliclibraryclasses.flags b/src/test/proguard/valid/skipnonpubliclibraryclasses.flags
new file mode 100644
index 0000000..e6e9f8b
--- /dev/null
+++ b/src/test/proguard/valid/skipnonpubliclibraryclasses.flags
@@ -0,0 +1,5 @@
+# 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.
+
+-skipnonpubliclibraryclasses
diff --git a/src/test/proguard/valid/sub/sub-empty.flags b/src/test/proguard/valid/sub/sub-empty.flags
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/test/proguard/valid/sub/sub-empty.flags
diff --git a/src/test/proguard/valid/sub/sub-including.flags b/src/test/proguard/valid/sub/sub-including.flags
new file mode 100644
index 0000000..5de6ef5
--- /dev/null
+++ b/src/test/proguard/valid/sub/sub-including.flags
@@ -0,0 +1,14 @@
+@sub-empty.flags
+-include sub-empty.flags
+@../empty.flags
+-include ../empty.flags
+-basedirectory ..
+@empty.flags
+-include empty.flags
+@sub/sub-empty.flags
+-include sub/sub-empty.flags
+-basedirectory sub
+@sub-empty.flags
+-include sub-empty.flags
+@../empty.flags
+-include ../empty.flags
diff --git a/src/test/proguard/valid/verbose.flags b/src/test/proguard/valid/verbose.flags
new file mode 100644
index 0000000..4254a8d
--- /dev/null
+++ b/src/test/proguard/valid/verbose.flags
@@ -0,0 +1,5 @@
+# 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.
+
+-verbose
diff --git a/src/test/proguard/valid/why-are-you-keeping.flags b/src/test/proguard/valid/why-are-you-keeping.flags
new file mode 100644
index 0000000..e57da11
--- /dev/null
+++ b/src/test/proguard/valid/why-are-you-keeping.flags
@@ -0,0 +1 @@
+-whyareyoukeeping class * extends foo.bar { *; }
\ No newline at end of file
diff --git a/src/test/smali/arithmetic/Arithmetic.smali b/src/test/smali/arithmetic/Arithmetic.smali
new file mode 100644
index 0000000..f76b9b3
--- /dev/null
+++ b/src/test/smali/arithmetic/Arithmetic.smali
@@ -0,0 +1,254 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method static addMinusOne(I)I
+ .locals 1
+
+ const/4 v0, -1
+ add-int v0, p0, v0
+ return v0
+.end method
+
+.method static addTwoConstants()I
+ .locals 2
+
+ const/4 v0, 1
+ const/4 v1, 2
+ add-int v0, v0, v1
+ return v0
+.end method
+
+.method static addSameTwoConstants()I
+ .locals 2
+
+ const/4 v0, 1
+ add-int v0, v0, v0
+ return v0
+.end method
+
+.method static subMinusOne(I)I
+ .locals 1
+
+ const/4 v0, -1
+ sub-int v0, p0, v0
+ return v0
+.end method
+
+.method static subSameTwoConstants()I
+ .locals 2
+
+ const/4 v0, 1
+ sub-int v0, v0, v0
+ return v0
+.end method
+
+.method static subtractConstants()I
+ .locals 9
+
+ const/4 v0, 0
+
+ const v1, 127 # Max 8-bit signed integer.
+ const v2, -128 # Min 8-bit signed integer.
+ const v3, 128 # Max 8-bit signed integer plus one.
+ const v4, -129 # Min 8-bit signed integer minus one.
+
+ const v5, 32767 # Max 16-bit signed integer.
+ const v6, -32768 # Min 16-bit signed integer.
+ const v7, 32768 # Max 16-bit signed integer plus one.
+ const v8, -32769 # Min 16-bit signed integer minus one.
+
+ sub-int v0, v0, v1
+ sub-int v0, v0, v2
+ sub-int v0, v0, v3
+ sub-int v0, v0, v4
+ sub-int v0, v0, v5
+ sub-int v0, v0, v6
+ sub-int v0, v0, v7
+ sub-int v0, v0, v8
+ sub-int v0, v1, v0
+ sub-int v0, v2, v0
+ sub-int v0, v3, v0
+ sub-int v0, v4, v0
+ sub-int v0, v5, v0
+ sub-int v0, v6, v0
+ sub-int v0, v7, v0
+ sub-int v0, v8, v0
+
+ return v0
+.end method
+
+.method static sixteenIntArgMethod(IIIIIIIIIIIIIIII)V
+ .locals 0
+ return-void
+.end method
+
+# Same code as subtractConstants, but try to force the register allocator to allocate registers for
+# the arithmetic operations above 15.
+.method static subtractConstants8bitRegisters()I
+ .locals 32
+
+ const/4 v0, 0
+
+ const v1, 127 # Max 8-bit signed integer.
+ const v2, -128 # Min 8-bit signed integer.
+ const v3, 128 # Max 8-bit signed integer plus one.
+ const v4, -129 # Min 8-bit signed integer minus one.
+
+ const v5, 32767 # Max 16-bit signed integer.
+ const v6, -32768 # Min 16-bit signed integer.
+ const v7, 32768 # Max 16-bit signed integer plus one.
+ const v8, -32769 # Min 16-bit signed integer minus one.
+
+ const v9, 9
+ const v10, 10
+ const v11, 11
+ const v12, 12
+ const v13, 13
+ const v14, 14
+ const v15, 15
+
+ sub-int v16, v0, v1
+ sub-int v17, v16, v2
+ sub-int v18, v17, v3
+ sub-int v19, v18, v4
+ sub-int v20, v19, v5
+ sub-int v21, v20, v6
+ sub-int v22, v21, v7
+ sub-int v23, v22, v8
+ sub-int v24, v1, v23
+ sub-int v25, v2, v24
+ sub-int v26, v3, v25
+ sub-int v27, v4, v26
+ sub-int v28, v5, v27
+ sub-int v29, v6, v28
+ sub-int v30, v7, v29
+ sub-int v31, v8, v30
+
+ invoke-static/range {v16 .. v31}, LTest;->sixteenIntArgMethod(IIIIIIIIIIIIIIII)V
+
+ return v31
+.end method
+
+.method static addConstantUsedTwice()I
+ .locals 4
+
+ const/4 v0, 0
+ const/4 v1, 1
+ add-int/2addr v0, v1
+ add-int/2addr v0, v1
+ return v0
+.end method
+
+.method static addTwoLongConstants()J
+ .locals 4
+
+ const-wide v0, 1
+ const-wide v2, 2
+ add-long v0, v0, v2
+ return-wide v0
+.end method
+
+.method static addTwoDoubleConstants()D
+ .locals 4
+
+ const-wide v0, 0x3ff0000000000000L # 1.0
+ const-wide v2, 0x4000000000000000L # 2.0
+ add-double v0, v0, v2
+ return-wide v0
+.end method
+
+.method static cmpFold()I
+ .locals 4
+
+ const-wide v0, 0
+ const-wide v2, 0
+ cmp-long v0, v0, v2
+ return v0
+.end method
+
+.method static addFoldLeft(I)I
+ .locals 2
+
+ const/4 v0, 1
+ const/4 v1, 2
+ add-int/2addr v0, v1
+ add-int/2addr v0, p0
+ return v0
+.end method
+
+.method static subFoldLeft(I)I
+ .locals 2
+
+ const/4 v0, 1
+ const/4 v1, 2
+ sub-int/2addr v0, v1
+ sub-int/2addr v0, p0
+ return v0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 3
+
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ # Calculate: 0 + (-1).
+ const/4 v1, 0
+ invoke-static {v1}, LTest;->addMinusOne(I)I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->addTwoConstants()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->addSameTwoConstants()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->addTwoLongConstants()J
+ move-result-wide v1
+ invoke-virtual {v0, v1, v2}, Ljava/io/PrintStream;->println(J)V
+
+ invoke-static {}, LTest;->addTwoDoubleConstants()D
+ move-result-wide v1
+ invoke-virtual {v0, v1, v2}, Ljava/io/PrintStream;->println(D)V
+
+ # Calculate: 0 - (-1).
+ const/4 v1, 0
+ invoke-static {v1}, LTest;->subMinusOne(I)I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->subSameTwoConstants()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->subtractConstants()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->subtractConstants8bitRegisters()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->addConstantUsedTwice()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ const/4 v1, 1
+ invoke-static {v1}, LTest;->addFoldLeft(I)I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ const/4 v1, 1
+ invoke-static {v1}, LTest;->subFoldLeft(I)I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ return-void
+.end method
diff --git a/src/test/smali/bad-codegen/Test.java b/src/test/smali/bad-codegen/Test.java
new file mode 100644
index 0000000..f301ed7
--- /dev/null
+++ b/src/test/smali/bad-codegen/Test.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import java.util.Map;
+
+public class Test {
+ public Test a;
+ public static Test P;
+
+ public Test() {
+ }
+
+ public Test(int a, Test b, Test c, Long d, Test e, Map f) {
+ }
+
+ public Test(int i1, int i2, int i3) {
+ }
+
+ public long b() {
+ return 0;
+ }
+
+ public boolean c() {
+ return false;
+ }
+
+ public boolean d() {
+ return false;
+ }
+
+ public static void main(String[] args) {
+ Test test = new Test();
+ try {
+ new TestObject().a(test, test, test, test, true);
+ } catch (Throwable t) {
+ System.out.println(t);
+ }
+ }
+}
diff --git a/src/test/smali/bad-codegen/TestObject.java b/src/test/smali/bad-codegen/TestObject.java
new file mode 100644
index 0000000..dc96fca
--- /dev/null
+++ b/src/test/smali/bad-codegen/TestObject.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+ public Test a(Test a, Test b, Test c, Test d, boolean e) {
+ return new Test();
+ }
+}
diff --git a/src/test/smali/bad-codegen/TestObject.smali b/src/test/smali/bad-codegen/TestObject.smali
new file mode 100644
index 0000000..b0a1a58
--- /dev/null
+++ b/src/test/smali/bad-codegen/TestObject.smali
@@ -0,0 +1,1488 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field public a:LTest;
+.field public b:Ljava/util/List;
+
+.method public final a(LTest;LTest;LTest;LTest;Z)LTest;
+ .registers 34
+ move-object/from16 v0, p0
+ iget-object v12, v0, LTestObject;->a:LTest;
+ iget-object v4, v12, LTest;->a:LTest;
+ invoke-virtual {v4}, LTest;->b()J
+ move-result-wide v14
+ const/4 v10, 0x0
+ const/4 v4, 0x0
+ if-eqz p2, :cond_9cf
+ move-object/from16 v0, p2
+ iget-object v10, v0, LTest;->a:LTest;
+ move-object/from16 v0, p2
+ iget-object v4, v0, LTest;->b:Ljava/util/List;
+ move-object v13, v4
+ :goto_17
+ if-eqz v10, :cond_1f
+ invoke-virtual {v10}, LTest;->d()Z
+ move-result v4
+ if-nez v4, :cond_21d
+ :cond_1f
+ new-instance v4, LTest;
+ const/4 v5, 0x0
+ const/4 v6, 0x0
+ sget-object v7, LTest;->P:LTest;
+ const-wide/16 v8, 0x0
+ const/4 v11, 0x0
+ invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+ move-object v14, v4
+ :goto_2c
+ if-nez p4, :cond_44
+ iget-object v4, v14, LTest;->a:LTest;
+ sget-object v5, LTest;->P:LTest;
+ if-ne v4, v5, :cond_44
+ iget-object v4, v14, LTest;->a:LTest;
+ new-instance p4, LTest;
+ iget v5, v4, LTest;->c:I
+ iget v4, v4, LTest;->d:I
+ const v6, 0x2faf080
+ move-object/from16 v0, p4
+ invoke-direct {v0, v5, v4, v6}, LTest;-><init>(III)V
+ :cond_44
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->a:LTest;
+ invoke-virtual {v4}, LTest;->a()Z
+ move-result v5
+ if-eqz v5, :cond_6c5
+ iget-object v8, v4, LTest;->a:LTest;
+ if-nez p1, :cond_61e
+ const/4 v4, 0x0
+ :cond_53
+ :goto_53
+ if-eqz v4, :cond_6c8
+ move-object v12, v4
+ :goto_56
+ iget-object v4, v12, LTest;->a:LTest;
+ sget-object v5, LTest;->P:LTest;
+ if-ne v4, v5, :cond_931
+ const/4 v4, 0x1
+ :goto_5d
+ iget-object v5, v14, LTest;->a:LTest;
+ sget-object v6, LTest;->P:LTest;
+ if-ne v5, v6, :cond_934
+ const/4 v5, 0x1
+ :goto_64
+ if-nez v4, :cond_937
+ if-nez v5, :cond_937
+ const/4 v4, 0x0
+ move-object v13, v4
+ :goto_6a
+ sget-object v4, LTest;->P:LTest;
+ invoke-static {v4}, LTest;->a(LTest;)Z
+ move-result v4
+ if-eqz v4, :cond_212
+ const/4 v5, 0x0
+ const/4 v4, 0x0
+ if-eqz v13, :cond_9c8
+ if-ne v13, v14, :cond_976
+ const/4 v5, 0x1
+ move v11, v4
+ move v15, v5
+ :goto_7b
+ if-eqz v12, :cond_97b
+ iget-object v4, v12, LTest;->a:LTest;
+ sget-object v5, LTest;->P:LTest;
+ if-ne v4, v5, :cond_97b
+ const/4 v4, 0x1
+ :goto_84
+ if-eqz v4, :cond_9c
+ const/4 v7, 0x0
+ iget v4, v12, LTest;->a:I
+ packed-switch v4, :pswitch_data_9d2
+ :goto_8c
+ :pswitch_8c
+ if-eqz v7, :cond_9c
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->a:LTest;
+ const-string v5, ""
+ const-string v6, ""
+ const-wide/16 v8, 0x1
+ const/4 v10, 0x1
+ invoke-interface/range {v4 .. v10}, LTest;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V
+ :cond_9c
+ iget-object v4, v14, LTest;->a:LTest;
+ sget-object v5, LTest;->P:LTest;
+ if-ne v4, v5, :cond_98e
+ const/4 v4, 0x1
+ :goto_a3
+ if-eqz v4, :cond_bb
+ const/4 v7, 0x0
+ iget v4, v14, LTest;->a:I
+ packed-switch v4, :pswitch_data_9e0
+ :goto_ab
+ if-eqz v7, :cond_bb
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->a:LTest;
+ const-string v5, ""
+ const-string v6, ""
+ const-wide/16 v8, 0x1
+ const/4 v10, 0x1
+ invoke-interface/range {v4 .. v10}, LTest;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V
+ :cond_bb
+ if-eqz v15, :cond_999
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->a:LTest;
+ const-string v5, ""
+ const-string v6, ""
+ const-string v7, ""
+ const-wide/16 v8, 0x1
+ const/4 v10, 0x1
+ invoke-interface/range {v4 .. v10}, LTest;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V
+ :goto_cd
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->a:LTest;
+ if-eqz v4, :cond_119
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->a:LTest;
+ move-object/from16 v0, p0
+ iget-object v5, v0, LTestObject;->a:LTest;
+ const-string v6, ""
+ invoke-virtual {v4}, LTest;->a()Z
+ move-result v7
+ if-eqz v7, :cond_f6
+ iget-object v4, v4, LTest;->a:LTest;
+ iget-object v7, v4, LTest;->a:LTest;
+ if-eqz v7, :cond_f6
+ iget-object v7, v4, LTest;->a:LTest;
+ iget-object v7, v7, LTest;->a:LTest;
+ if-eqz v7, :cond_f6
+ iget-object v4, v4, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ invoke-virtual {v4, v5, v6}, LTest;->a(LTest;Ljava/lang/String;)V
+ :cond_f6
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->a:LTest;
+ move-object/from16 v0, p0
+ iget-object v5, v0, LTestObject;->a:LTest;
+ const-string v6, ""
+ invoke-virtual {v4}, LTest;->b()Z
+ move-result v7
+ if-eqz v7, :cond_119
+ iget-object v4, v4, LTest;->a:LTest;
+ iget-object v7, v4, LTest;->a:LTest;
+ if-eqz v7, :cond_119
+ iget-object v7, v4, LTest;->a:LTest;
+ iget-object v7, v7, LTest;->a:LTest;
+ if-eqz v7, :cond_119
+ iget-object v4, v4, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ invoke-virtual {v4, v5, v6}, LTest;->a(LTest;Ljava/lang/String;)V
+ :cond_119
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->a:LTest;
+ if-eqz v4, :cond_212
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->a:LTest;
+ move-object/from16 v0, p0
+ iget-object v5, v0, LTestObject;->a:LTest;
+ const-string v6, ""
+ invoke-virtual {v4}, LTest;->a()Z
+ move-result v7
+ if-eqz v7, :cond_212
+ iget-object v4, v4, LTest;->a:LTest;
+ invoke-static {v5}, LTest;->a(Ljava/lang/Object;)Ljava/lang/Object;
+ invoke-static {v6}, LTest;->a(Ljava/lang/Object;)Ljava/lang/Object;
+ iget-object v7, v4, LTest;->a:LTest;
+ iget-object v15, v7, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ iget-object v7, v4, LTest;->a:LTest;
+ if-eqz v7, :cond_9bf
+ iget-object v4, v4, LTest;->a:LTest;
+ invoke-virtual {v4}, LTest;->b()J
+ move-result-wide v8
+ move-wide/from16 v16, v8
+ :goto_149
+ iget-wide v8, v15, LTest;->l:J
+ const-wide/16 v10, -0x1
+ cmp-long v4, v8, v10
+ if-eqz v4, :cond_191
+ iget-wide v8, v15, LTest;->l:J
+ cmp-long v4, v16, v8
+ if-lez v4, :cond_191
+ iget-wide v8, v15, LTest;->l:J
+ sub-long v8, v16, v8
+ long-to-float v4, v8
+ const v7, 0x4ca4cb80 # 8.64E7f
+ div-float v10, v4, v7
+ const-string v7, ""
+ iget v4, v15, LTest;->g:I
+ int-to-long v8, v4
+ invoke-static/range {v5 .. v10}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JF)V
+ const-string v7, ""
+ iget v4, v15, LTest;->c:I
+ int-to-long v8, v4
+ invoke-static/range {v5 .. v10}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JF)V
+ const-string v7, ""
+ iget v4, v15, LTest;->b:I
+ int-to-long v8, v4
+ invoke-static/range {v5 .. v10}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JF)V
+ const-string v7, ""
+ iget v4, v15, LTest;->a:I
+ int-to-long v8, v4
+ invoke-static/range {v5 .. v10}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JF)V
+ const-string v7, ""
+ iget v4, v15, LTest;->d:I
+ int-to-long v8, v4
+ invoke-static/range {v5 .. v10}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JF)V
+ const-string v7, ""
+ iget v4, v15, LTest;->h:I
+ int-to-long v8, v4
+ invoke-static/range {v5 .. v10}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JF)V
+ :cond_191
+ const-string v7, ""
+ iget v4, v15, LTest;->h:I
+ int-to-long v8, v4
+ iget v10, v15, LTest;->g:I
+ const/4 v11, 0x0
+ invoke-static/range {v5 .. v11}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JII)I
+ move-result v4
+ iput v4, v15, LTest;->h:I
+ const-string v7, ""
+ iget-wide v8, v15, LTest;->k:J
+ iget v10, v15, LTest;->g:I
+ const/4 v11, 0x0
+ invoke-static/range {v5 .. v11}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JII)I
+ move-result v4
+ int-to-long v8, v4
+ iput-wide v8, v15, LTest;->k:J
+ const-string v7, ""
+ iget v4, v15, LTest;->i:I
+ int-to-long v8, v4
+ iget v10, v15, LTest;->j:I
+ const/4 v11, 0x0
+ invoke-static/range {v5 .. v11}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JII)I
+ move-result v4
+ iput v4, v15, LTest;->i:I
+ const/4 v4, 0x0
+ iput v4, v15, LTest;->j:I
+ const-string v7, ""
+ iget v4, v15, LTest;->e:I
+ int-to-long v8, v4
+ iget v10, v15, LTest;->a:I
+ const/4 v11, 0x0
+ invoke-static/range {v5 .. v11}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JII)I
+ move-result v4
+ iput v4, v15, LTest;->e:I
+ const-string v4, ""
+ iget v7, v15, LTest;->a:I
+ const/4 v8, 0x0
+ invoke-static {v5, v6, v4, v7, v8}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;II)I
+ move-result v4
+ iput v4, v15, LTest;->a:I
+ const-string v4, ""
+ iget v7, v15, LTest;->b:I
+ const/4 v8, 0x0
+ invoke-static {v5, v6, v4, v7, v8}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;II)I
+ move-result v4
+ iput v4, v15, LTest;->b:I
+ const-string v4, ""
+ iget v7, v15, LTest;->c:I
+ const/4 v8, 0x0
+ invoke-static {v5, v6, v4, v7, v8}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;II)I
+ move-result v4
+ iput v4, v15, LTest;->c:I
+ const-string v4, ""
+ iget v7, v15, LTest;->d:I
+ const/4 v8, 0x0
+ invoke-static {v5, v6, v4, v7, v8}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;II)I
+ move-result v4
+ iput v4, v15, LTest;->d:I
+ const-string v4, ""
+ iget v7, v15, LTest;->f:I
+ const/4 v8, 0x0
+ invoke-static {v5, v6, v4, v7, v8}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;II)I
+ move-result v4
+ iput v4, v15, LTest;->f:I
+ const-string v4, ""
+ iget v7, v15, LTest;->g:I
+ const/4 v8, 0x0
+ invoke-static {v5, v6, v4, v7, v8}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;II)I
+ move-result v4
+ iput v4, v15, LTest;->g:I
+ move-wide/from16 v0, v16
+ iput-wide v0, v15, LTest;->l:J
+ :cond_212
+ if-eqz p5, :cond_9c5
+ if-ne v13, v14, :cond_9c5
+ const/4 v4, 0x1
+ :goto_217
+ new-instance v5, LTest;
+ invoke-direct {v5, v13, v12, v14, v4}, LTest;-><init>(LTest;LTest;LTest;Z)V
+ return-object v5
+ :cond_21d
+ invoke-virtual {v12}, LTest;->a()Z
+ move-result v4
+ if-eqz v4, :cond_57c
+ iget-object v8, v12, LTest;->a:LTest;
+ iget-wide v4, v8, LTest;->b:J
+ const-wide/16 v6, 0x1
+ add-long/2addr v4, v6
+ iput-wide v4, v8, LTest;->b:J
+ if-eqz v10, :cond_578
+ iget v4, v10, LTest;->k:I
+ const/4 v5, 0x3
+ if-ne v4, v5, :cond_29b
+ const/4 v4, 0x1
+ :goto_234
+ if-eqz v4, :cond_578
+ iget-object v9, v8, LTest;->a:LTest;
+ invoke-static {v10}, LTest;->a(LTest;)J
+ move-result-wide v16
+ const-wide/16 v4, 0x0
+ cmp-long v4, v16, v4
+ if-gez v4, :cond_2ca
+ const/4 v4, 0x0
+ :cond_243
+ :goto_243
+ if-nez v4, :cond_543
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ iget v5, v4, LTest;->d:I
+ add-int/lit8 v5, v5, 0x1
+ iput v5, v4, LTest;->d:I
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v5, v4, LTest;->a:LTest;
+ iget-boolean v4, v5, LTest;->b:Z
+ if-nez v4, :cond_53c
+ iget-object v4, v5, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->f:Ljava/lang/String;
+ if-eqz v4, :cond_539
+ const/4 v4, 0x1
+ :goto_260
+ if-eqz v4, :cond_53c
+ iget-object v4, v5, LTest;->a:LTest;
+ invoke-virtual {v4}, LTest;->b()J
+ move-result-wide v6
+ iget-wide v8, v5, LTest;->c:J
+ sub-long/2addr v6, v8
+ iget v4, v5, LTest;->j:I
+ int-to-long v4, v4
+ cmp-long v4, v6, v4
+ if-lez v4, :cond_53c
+ const/4 v4, 0x1
+ :goto_273
+ if-eqz v4, :cond_53f
+ new-instance v4, LTest;
+ const/4 v5, 0x0
+ const/4 v6, 0x0
+ sget-object v7, LTest;->P:LTest;
+ const-wide/16 v8, 0x0
+ const/4 v11, 0x0
+ invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+ move-object v6, v4
+ :goto_282
+ if-eqz v6, :cond_57c
+ iget-object v4, v6, LTest;->a:LTest;
+ sget-object v5, LTest;->P:LTest;
+ if-ne v4, v5, :cond_57c
+ new-instance v4, LTest;
+ iget v5, v6, LTest;->a:I
+ iget-object v6, v6, LTest;->a:LTest;
+ sget-object v7, LTest;->P:LTest;
+ iget-wide v8, v10, LTest;->i:J
+ const/4 v11, 0x0
+ invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+ move-object v14, v4
+ goto/16 :goto_2c
+ :cond_29b
+ const/4 v5, 0x4
+ if-ne v4, v5, :cond_2c7
+ sget-object v4, LTest;->P:LTest;
+ invoke-virtual {v4}, LTest;->b()Ljava/lang/Object;
+ move-result-object v4
+ check-cast v4, Ljava/lang/Integer;
+ invoke-virtual {v4}, Ljava/lang/Integer;->intValue()I
+ move-result v4
+ and-int/lit8 v4, v4, 0x1
+ if-nez v4, :cond_2c4
+ sget-object v4, LTest;->P:LTest;
+ invoke-virtual {v4}, LTest;->a()Ljava/lang/Object;
+ move-result-object v4
+ check-cast v4, Ljava/lang/Long;
+ invoke-virtual {v4}, Ljava/lang/Long;->intValue()I
+ move-result v4
+ and-int/lit8 v4, v4, 0x1
+ if-nez v4, :cond_2c1
+ const/4 v4, 0x1
+ goto/16 :goto_234
+ :cond_2c1
+ const/4 v4, 0x0
+ goto/16 :goto_234
+ :cond_2c4
+ const/4 v4, 0x1
+ goto/16 :goto_234
+ :cond_2c7
+ const/4 v4, 0x0
+ goto/16 :goto_234
+ :cond_2ca
+ iget-object v11, v9, LTest;->a:LTest;
+ iget-object v4, v11, LTest;->a:LTest;
+ if-eqz v4, :cond_325
+ iget-wide v4, v11, LTest;->d:J
+ cmp-long v4, v16, v4
+ if-nez v4, :cond_325
+ iget-object v4, v11, LTest;->a:LTest;
+ :goto_2d8
+ if-nez v4, :cond_243
+ invoke-virtual {v10}, LTest;->a()Ljava/lang/String;
+ move-result-object v5
+ iget v6, v10, LTest;->k:I
+ const/4 v7, 0x3
+ if-ne v6, v7, :cond_4e1
+ const-string v6, ""
+ invoke-virtual {v5, v6}, Ljava/lang/String;->split(Ljava/lang/String;)[Ljava/lang/String;
+ move-result-object v5
+ array-length v6, v5
+ const/4 v7, 0x5
+ if-ne v6, v7, :cond_2f7
+ const/4 v6, 0x0
+ aget-object v6, v5, v6
+ invoke-static {v6}, LTest;->a(Ljava/lang/String;)I
+ move-result v6
+ const/4 v7, 0x3
+ if-eq v6, v7, :cond_489
+ :cond_2f7
+ const/4 v5, 0x0
+ :goto_2f8
+ if-eqz v5, :cond_2fe
+ iget-object v6, v9, LTest;->a:LTest;
+ iput-object v5, v6, LTest;->f:Ljava/lang/String;
+ :cond_2fe
+ iget-object v5, v9, LTest;->a:LTest;
+ invoke-virtual {v5}, LTest;->b()J
+ move-result-wide v6
+ iget-wide v0, v9, LTest;->g:J
+ move-wide/from16 v16, v0
+ sub-long v16, v6, v16
+ iget-wide v0, v9, LTest;->e:J
+ move-wide/from16 v18, v0
+ cmp-long v5, v16, v18
+ if-lez v5, :cond_243
+ iget-wide v0, v9, LTest;->f:J
+ move-wide/from16 v16, v0
+ sub-long v16, v6, v16
+ iget-object v5, v9, LTest;->a:LTest;
+ iget-object v5, v5, LTest;->a:LTest;
+ move-wide/from16 v0, v16
+ invoke-virtual {v5, v0, v1}, LTest;->a(J)V
+ iput-wide v6, v9, LTest;->g:J
+ goto/16 :goto_243
+ :cond_325
+ iget-object v4, v11, LTest;->a:LTest;
+ const/4 v5, 0x1
+ move-wide/from16 v0, v16
+ invoke-virtual {v4, v0, v1, v5}, LTest;->a(JZ)[B
+ move-result-object v5
+ if-nez v5, :cond_332
+ const/4 v4, 0x0
+ goto :goto_2d8
+ :cond_332
+ iget-object v0, v11, LTest;->a:LTest;
+ move-object/from16 v18, v0
+ new-instance v4, LTest;
+ sget-object v6, LTest;->P:LTest;
+ invoke-direct {v4, v6}, LTest;-><init>(LTest;)V
+ array-length v6, v5
+ const/4 v7, 0x6
+ if-ge v6, v7, :cond_346
+ const/4 v4, 0x0
+ :cond_342
+ :goto_342
+ if-nez v4, :cond_481
+ const/4 v4, 0x0
+ goto :goto_2d8
+ :cond_346
+ const/4 v6, 0x0
+ invoke-static {v5, v6}, LTest;->b([BI)I
+ move-result v19
+ const/4 v6, 0x3
+ move-object/from16 v0, v18
+ move/from16 v1, v19
+ invoke-virtual {v0, v1, v5, v6}, LTest;->a(I[BI)[D
+ move-result-object v6
+ if-eqz v6, :cond_35d
+ array-length v7, v6
+ const/16 v20, 0x3
+ move/from16 v0, v20
+ if-eq v7, v0, :cond_35f
+ :cond_35d
+ const/4 v4, 0x0
+ goto :goto_342
+ :cond_35f
+ const/4 v7, 0x2
+ const/16 v20, 0x0
+ aget-wide v20, v6, v20
+ const-wide v22, 0x416312d000000000L # 1.0E7
+ mul-double v20, v20, v22
+ move-wide/from16 v0, v20
+ double-to-int v0, v0
+ move/from16 v20, v0
+ move/from16 v0, v20
+ int-to-long v0, v0
+ move-wide/from16 v20, v0
+ move-wide/from16 v0, v20
+ invoke-virtual {v4, v7, v0, v1}, LTest;->b(IJ)LTest;
+ const/4 v7, 0x3
+ const/16 v20, 0x1
+ aget-wide v20, v6, v20
+ const-wide v22, 0x416312d000000000L # 1.0E7
+ mul-double v20, v20, v22
+ move-wide/from16 v0, v20
+ double-to-int v0, v0
+ move/from16 v20, v0
+ move/from16 v0, v20
+ int-to-long v0, v0
+ move-wide/from16 v20, v0
+ move-wide/from16 v0, v20
+ invoke-virtual {v4, v7, v0, v1}, LTest;->b(IJ)LTest;
+ const/4 v7, 0x4
+ const/16 v20, 0x2
+ aget-wide v20, v6, v20
+ move-wide/from16 v0, v20
+ double-to-float v6, v0
+ invoke-virtual {v4, v7, v6}, LTest;->b(IF)LTest;
+ array-length v6, v5
+ const/4 v7, 0x6
+ if-eq v6, v7, :cond_47e
+ array-length v6, v5
+ add-int/lit8 v6, v6, -0x6
+ new-array v0, v6, [B
+ move-object/from16 v20, v0
+ const/4 v7, 0x6
+ const/16 v21, 0x0
+ move-object/from16 v0, v20
+ move/from16 v1, v21
+ invoke-static {v5, v7, v0, v1, v6}, Ljava/lang/System;->arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V
+ new-instance v21, LTest;
+ move-object/from16 v0, v21
+ move-object/from16 v1, v20
+ invoke-direct {v0, v1}, LTest;-><init>([B)V
+ const/4 v5, 0x0
+ move-object/from16 v0, v21
+ iput v5, v0, LTest;->a:I
+ :goto_3c3
+ move-object/from16 v0, v21
+ iget v5, v0, LTest;->a:I
+ move-object/from16 v0, v21
+ iget-object v6, v0, LTest;->b:[B
+ array-length v6, v6
+ add-int/lit8 v6, v6, -0x1
+ if-ge v5, v6, :cond_47e
+ move-object/from16 v0, v21
+ iget v6, v0, LTest;->a:I
+ invoke-virtual/range {v21 .. v21}, LTest;->a()I
+ move-result v5
+ move-object/from16 v0, v20
+ array-length v7, v0
+ add-int/2addr v5, v6
+ if-ge v7, v5, :cond_3e7
+ const/4 v5, 0x0
+ :goto_3df
+ if-nez v5, :cond_46b
+ const/4 v5, 0x0
+ :goto_3e2
+ if-nez v5, :cond_342
+ const/4 v4, 0x0
+ goto/16 :goto_342
+ :cond_3e7
+ new-instance v5, LTest;
+ sget-object v7, LTest;->P:LTest;
+ invoke-direct {v5, v7}, LTest;-><init>(LTest;)V
+ move-object/from16 v0, v20
+ invoke-static {v0, v6}, LTest;->a([BI)I
+ move-result v22
+ add-int/lit8 v6, v6, 0x1
+ aget-byte v23, v20, v6
+ add-int/lit8 v7, v6, 0x1
+ const/4 v6, 0x0
+ :goto_3fb
+ move/from16 v0, v22
+ if-ge v6, v0, :cond_41a
+ move-object/from16 v0, v20
+ move/from16 v1, v23
+ invoke-static {v0, v7, v1, v6}, LTest;->a([BIBI)I
+ move-result v24
+ const/16 v25, 0x2
+ move/from16 v0, v24
+ int-to-long v0, v0
+ move-wide/from16 v26, v0
+ move/from16 v0, v25
+ move-wide/from16 v1, v26
+ invoke-virtual {v5, v0, v1, v2}, LTest;->a(IJ)V
+ add-int/lit8 v7, v7, 0x1
+ add-int/lit8 v6, v6, 0x1
+ goto :goto_3fb
+ :cond_41a
+ move-object/from16 v0, v18
+ move/from16 v1, v19
+ move-object/from16 v2, v20
+ invoke-virtual {v0, v1, v2, v7}, LTest;->a(I[BI)[D
+ move-result-object v6
+ if-nez v6, :cond_428
+ const/4 v5, 0x0
+ goto :goto_3df
+ :cond_428
+ const/4 v7, 0x3
+ const/16 v22, 0x0
+ aget-wide v22, v6, v22
+ const-wide v24, 0x416312d000000000L # 1.0E7
+ mul-double v22, v22, v24
+ move-wide/from16 v0, v22
+ double-to-int v0, v0
+ move/from16 v22, v0
+ move/from16 v0, v22
+ int-to-long v0, v0
+ move-wide/from16 v22, v0
+ move-wide/from16 v0, v22
+ invoke-virtual {v5, v7, v0, v1}, LTest;->a(IJ)V
+ const/4 v7, 0x4
+ const/16 v22, 0x1
+ aget-wide v22, v6, v22
+ const-wide v24, 0x416312d000000000L # 1.0E7
+ mul-double v22, v22, v24
+ move-wide/from16 v0, v22
+ double-to-int v0, v0
+ move/from16 v22, v0
+ move/from16 v0, v22
+ int-to-long v0, v0
+ move-wide/from16 v22, v0
+ move-wide/from16 v0, v22
+ invoke-virtual {v5, v7, v0, v1}, LTest;->a(IJ)V
+ const/4 v7, 0x5
+ const/16 v22, 0x2
+ aget-wide v22, v6, v22
+ move-wide/from16 v0, v22
+ double-to-float v6, v0
+ invoke-virtual {v5, v7, v6}, LTest;->a(IF)V
+ goto/16 :goto_3df
+ :cond_46b
+ const/4 v6, 0x5
+ invoke-virtual {v4, v6, v5}, LTest;->a(ILjava/lang/Object;)V
+ move-object/from16 v0, v21
+ iget v5, v0, LTest;->a:I
+ invoke-virtual/range {v21 .. v21}, LTest;->a()I
+ move-result v6
+ add-int/2addr v5, v6
+ move-object/from16 v0, v21
+ iput v5, v0, LTest;->a:I
+ goto/16 :goto_3c3
+ :cond_47e
+ const/4 v5, 0x1
+ goto/16 :goto_3e2
+ :cond_481
+ iput-object v4, v11, LTest;->a:LTest;
+ move-wide/from16 v0, v16
+ iput-wide v0, v11, LTest;->d:J
+ goto/16 :goto_2d8
+ :cond_489
+ new-instance v6, Ljava/lang/StringBuilder;
+ invoke-direct {v6}, Ljava/lang/StringBuilder;-><init>()V
+ const/4 v7, 0x0
+ aget-object v7, v5, v7
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const-string v7, ""
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const/4 v7, 0x1
+ aget-object v7, v5, v7
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const-string v7, ""
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const/4 v7, 0x2
+ aget-object v7, v5, v7
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const-string v7, ""
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const/4 v7, 0x4
+ aget-object v7, v5, v7
+ invoke-static {v7}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I
+ move-result v7
+ shr-int/lit8 v7, v7, 0x10
+ const v11, 0xffff
+ and-int/2addr v7, v11
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const-string v7, ""
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const/4 v7, 0x4
+ aget-object v5, v5, v7
+ invoke-static {v5}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I
+ move-result v5
+ const v7, 0xffff
+ and-int/2addr v5, v7
+ invoke-virtual {v6, v5}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
+ move-result-object v5
+ invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
+ move-result-object v5
+ goto/16 :goto_2f8
+ :cond_4e1
+ iget v6, v10, LTest;->k:I
+ const/4 v7, 0x4
+ if-ne v6, v7, :cond_536
+ const-string v6, ""
+ invoke-virtual {v5, v6}, Ljava/lang/String;->split(Ljava/lang/String;)[Ljava/lang/String;
+ move-result-object v5
+ array-length v6, v5
+ const/4 v7, 0x4
+ if-ne v6, v7, :cond_4fa
+ const/4 v6, 0x0
+ aget-object v6, v5, v6
+ invoke-static {v6}, LTest;->a(Ljava/lang/String;)I
+ move-result v6
+ const/4 v7, 0x4
+ if-eq v6, v7, :cond_4fd
+ :cond_4fa
+ const/4 v5, 0x0
+ goto/16 :goto_2f8
+ :cond_4fd
+ new-instance v6, Ljava/lang/StringBuilder;
+ invoke-direct {v6}, Ljava/lang/StringBuilder;-><init>()V
+ const/4 v7, 0x0
+ aget-object v7, v5, v7
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const-string v7, ""
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const/4 v7, 0x1
+ aget-object v7, v5, v7
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const-string v7, ""
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const/4 v7, 0x2
+ aget-object v7, v5, v7
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const-string v7, ""
+ invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v6
+ const/4 v7, 0x3
+ aget-object v5, v5, v7
+ invoke-virtual {v6, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ move-result-object v5
+ invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
+ move-result-object v5
+ goto/16 :goto_2f8
+ :cond_536
+ const/4 v5, 0x0
+ goto/16 :goto_2f8
+ :cond_539
+ const/4 v4, 0x0
+ goto/16 :goto_260
+ :cond_53c
+ const/4 v4, 0x0
+ goto/16 :goto_273
+ :cond_53f
+ const/4 v4, 0x0
+ move-object v6, v4
+ goto/16 :goto_282
+ :cond_543
+ iget-object v5, v8, LTest;->a:LTest;
+ iget-object v5, v5, LTest;->a:LTest;
+ iget-object v6, v10, LTest;->j:Ljava/util/Collection;
+ invoke-interface {v6}, Ljava/util/Collection;->size()I
+ move-result v6
+ iget v7, v5, LTest;->e:I
+ add-int/2addr v6, v7
+ iput v6, v5, LTest;->e:I
+ invoke-virtual {v8, v10, v4}, LTest;->a(LTest;LTest;)LTest;
+ move-result-object v6
+ if-eqz v6, :cond_578
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ iget v5, v4, LTest;->c:I
+ add-int/lit8 v5, v5, 0x1
+ iput v5, v4, LTest;->c:I
+ iget-wide v4, v8, LTest;->c:J
+ const-wide/16 v16, 0x1
+ add-long v4, v4, v16
+ iput-wide v4, v8, LTest;->c:J
+ new-instance v4, LTest;
+ const/4 v5, 0x2
+ sget-object v7, LTest;->P:LTest;
+ iget-wide v8, v10, LTest;->i:J
+ const/4 v11, 0x0
+ invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+ move-object v6, v4
+ goto/16 :goto_282
+ :cond_578
+ const/4 v4, 0x0
+ move-object v6, v4
+ goto/16 :goto_282
+ :cond_57c
+ const/4 v4, 0x0
+ invoke-virtual {v12, v10, v4, v14, v15}, LTest;->a(LTest;Ljava/util/Map;J)LTest;
+ move-result-object v4
+ if-nez v4, :cond_5a1
+ iget-object v4, v12, LTest;->a:LTest;
+ invoke-interface {v4}, LTest;->c()J
+ move-result-wide v4
+ invoke-virtual {v10, v4, v5}, LTest;->a(J)LTest;
+ move-result-object v4
+ move-object/from16 v0, p3
+ invoke-virtual {v0, v4}, LTest;->a(LTest;)V
+ new-instance v4, LTest;
+ const/4 v5, 0x1
+ const/4 v6, 0x0
+ sget-object v7, LTest;->P:LTest;
+ const-wide/16 v8, 0x0
+ const/4 v11, 0x0
+ invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+ move-object v14, v4
+ goto/16 :goto_2c
+ :cond_5a1
+ invoke-virtual {v4}, LTest;->b()Z
+ move-result v4
+ if-nez v4, :cond_5b6
+ new-instance v4, LTest;
+ const/4 v5, 0x0
+ const/4 v6, 0x0
+ sget-object v7, LTest;->P:LTest;
+ const-wide/16 v8, 0x0
+ const/4 v11, 0x0
+ invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+ move-object v14, v4
+ goto/16 :goto_2c
+ :cond_5b6
+ if-nez v13, :cond_60a
+ const/4 v4, 0x0
+ :goto_5b9
+ new-instance v16, LTest;
+ add-int/lit8 v4, v4, 0x1
+ move-object/from16 v0, v16
+ invoke-direct {v0, v4}, LTest;-><init>(I)V
+ new-instance v11, LTest;
+ const/4 v13, 0x0
+ invoke-direct/range {v11 .. v16}, LTest;-><init>(LTest;Ljava/util/Map;JLTest;)V
+ iget-wide v0, v10, LTest;->i:J
+ move-wide/from16 v18, v0
+ const-wide/16 v20, 0x7530
+ move-object/from16 v17, p2
+ move-object/from16 v22, v11
+ invoke-virtual/range {v17 .. v22}, LTest;->a(JJLTest;)V
+ new-instance v6, LTest;
+ invoke-virtual/range {v16 .. v16}, LTest;->a()D
+ move-result-wide v4
+ invoke-static {v4, v5}, LTest;->a(D)I
+ move-result v4
+ invoke-virtual/range {v16 .. v16}, LTest;->b()D
+ move-result-wide v8
+ invoke-static {v8, v9}, LTest;->a(D)I
+ move-result v5
+ invoke-virtual/range {v16 .. v16}, LTest;->c()I
+ move-result v7
+ invoke-static {v7}, LTest;->b(I)I
+ move-result v7
+ move-object/from16 v0, v16
+ iget v8, v0, LTest;->d:I
+ invoke-direct {v6, v4, v5, v7, v8}, LTest;-><init>(IIII)V
+ invoke-static {v6}, LTest;->c(LTest;)Z
+ move-result v4
+ if-eqz v4, :cond_60f
+ new-instance v4, LTest;
+ const/4 v5, 0x1
+ sget-object v7, LTest;->P:LTest;
+ iget-wide v8, v10, LTest;->i:J
+ const/4 v11, 0x0
+ invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+ move-object v14, v4
+ goto/16 :goto_2c
+ :cond_60a
+ invoke-interface {v13}, Ljava/util/LTest;->size()I
+ move-result v4
+ goto :goto_5b9
+ :cond_60f
+ new-instance v4, LTest;
+ const/4 v5, 0x0
+ const/4 v6, 0x0
+ sget-object v7, LTest;->P:LTest;
+ const-wide/16 v8, 0x0
+ const/4 v11, 0x0
+ invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+ move-object v14, v4
+ goto/16 :goto_2c
+ :cond_61e
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v5, v4, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ iget v6, v5, LTest;->d:I
+ add-int/lit8 v6, v6, 0x1
+ iput v6, v5, LTest;->d:I
+ invoke-virtual {v4}, LTest;->c()J
+ move-result-wide v6
+ iput-wide v6, v5, LTest;->f:J
+ move-object/from16 v0, p1
+ invoke-virtual {v8, v0}, LTest;->a(LTest;)LTest;
+ move-result-object v6
+ iget-object v4, v8, LTest;->a:LTest;
+ invoke-virtual {v4}, LTest;->a()V
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v5, v4, LTest;->a:LTest;
+ invoke-virtual {v5}, LTest;->b()J
+ move-result-wide v10
+ iget-wide v12, v4, LTest;->m:J
+ sub-long v12, v10, v12
+ iget-wide v0, v4, LTest;->k:J
+ move-wide/from16 v16, v0
+ cmp-long v5, v12, v16
+ if-lez v5, :cond_65f
+ iget-wide v12, v4, LTest;->l:J
+ sub-long v12, v10, v12
+ iget-object v5, v4, LTest;->a:LTest;
+ invoke-virtual {v5, v12, v13}, LTest;->a(J)V
+ iget-object v5, v4, LTest;->a:LTest;
+ invoke-virtual {v5, v12, v13}, LTest;->a(J)V
+ iput-wide v10, v4, LTest;->m:J
+ :cond_65f
+ iget-object v5, v4, LTest;->a:LTest;
+ invoke-virtual {v5}, LTest;->a()V
+ iget-object v5, v4, LTest;->a:LTest;
+ invoke-virtual {v5}, LTest;->a()V
+ iget-object v5, v4, LTest;->a:LTest;
+ invoke-virtual {v5}, LTest;->a()V
+ iget-object v4, v4, LTest;->a:LTest;
+ invoke-virtual {v4}, LTest;->a()V
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v5, v4, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ iget-wide v10, v5, LTest;->f:J
+ const-wide/16 v12, -0x1
+ cmp-long v7, v10, v12
+ if-eqz v7, :cond_698
+ invoke-virtual {v4}, LTest;->c()J
+ move-result-wide v10
+ iget-wide v12, v5, LTest;->f:J
+ sub-long/2addr v10, v12
+ const-wide/16 v12, -0x1
+ iput-wide v12, v5, LTest;->f:J
+ const-wide/16 v12, 0x0
+ cmp-long v4, v10, v12
+ if-ltz v4, :cond_698
+ const-wide/16 v12, 0x7530
+ cmp-long v4, v10, v12
+ if-lez v4, :cond_6b0
+ :cond_698
+ :goto_698
+ const/4 v4, 0x0
+ if-eqz v6, :cond_53
+ new-instance v4, LTest;
+ const/4 v5, 0x4
+ sget-object v7, LTest;->P:LTest;
+ iget-object v8, v8, LTest;->a:LTest;
+ iget-object v8, v8, LTest;->a:LTest;
+ invoke-virtual {v8}, LTest;->c()J
+ move-result-wide v8
+ const/4 v11, 0x0
+ move-object/from16 v10, p1
+ invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+ goto/16 :goto_53
+ :cond_6b0
+ long-to-int v4, v10
+ iput v4, v5, LTest;->a:I
+ if-eqz v6, :cond_6c0
+ iget v4, v5, LTest;->e:I
+ add-int/lit8 v4, v4, 0x1
+ iput v4, v5, LTest;->e:I
+ iget v4, v5, LTest;->a:I
+ iput v4, v5, LTest;->b:I
+ goto :goto_698
+ :cond_6c0
+ iget v4, v5, LTest;->a:I
+ iput v4, v5, LTest;->c:I
+ goto :goto_698
+ :cond_6c5
+ const/4 v4, 0x0
+ goto/16 :goto_53
+ :cond_6c8
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->a:LTest;
+ invoke-virtual {v4}, LTest;->b()Z
+ move-result v5
+ if-eqz v5, :cond_91d
+ iget-object v8, v4, LTest;->a:LTest;
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v5, v4, LTest;->a:LTest;
+ iget-boolean v4, v5, LTest;->d:Z
+ if-nez v4, :cond_716
+ sget-object v4, LTest;->P:LTest;
+ :goto_6de
+ iput-object v4, v8, LTest;->a:LTest;
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v5, v8, LTest;->a:LTest;
+ iget-object v5, v5, LTest;->a:LTest;
+ invoke-virtual {v5}, LTest;->b()J
+ move-result-wide v6
+ invoke-virtual {v4, v6, v7}, LTest;->a(J)V
+ if-nez p1, :cond_721
+ iget-object v4, v8, LTest;->a:LTest;
+ const/4 v5, 0x3
+ iget-object v6, v8, LTest;->a:LTest;
+ iget-object v6, v6, LTest;->a:LTest;
+ invoke-virtual {v6}, LTest;->b()J
+ move-result-wide v6
+ invoke-virtual {v4, v5, v6, v7}, LTest;->a(IJ)V
+ const/4 v4, 0x0
+ :goto_6fe
+ if-eqz v4, :cond_920
+ iget-object v5, v4, LTest;->a:LTest;
+ sget-object v6, LTest;->P:LTest;
+ if-ne v5, v6, :cond_9cc
+ move-object/from16 v0, p0
+ iget-object v5, v0, LTestObject;->a:LTest;
+ move-object/from16 v0, p1
+ move-object/from16 v1, p4
+ move-object/from16 v2, p3
+ invoke-virtual {v5, v0, v1, v2}, LTest;->a(LTest;LTest;LTest;)LTest;
+ move-object v12, v4
+ goto/16 :goto_56
+ :cond_716
+ new-instance v4, LTest;
+ invoke-direct {v4}, LTest;-><init>()V
+ iget-object v5, v5, LTest;->a:LTest;
+ invoke-virtual {v5, v4}, LTest;->a(Ljava/lang/Object;)Ljava/lang/Object;
+ goto :goto_6de
+ :cond_721
+ iget-object v4, v8, LTest;->a:LTest;
+ move-object/from16 v0, p1
+ invoke-virtual {v4, v0}, LTest;->a(LTest;)V
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v5, v4, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ const-string v6, ""
+ invoke-virtual {v5, v6}, LTest;->a(Ljava/lang/String;)LTest;
+ move-result-object v5
+ iget v6, v5, LTest;->c:I
+ add-int/lit8 v6, v6, 0x1
+ iput v6, v5, LTest;->c:I
+ invoke-virtual {v4}, LTest;->c()J
+ move-result-wide v6
+ iput-wide v6, v5, LTest;->b:J
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v5, v4, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ const-string v6, ""
+ invoke-virtual {v5, v6}, LTest;->a(Ljava/lang/String;)LTest;
+ move-result-object v5
+ iget v6, v5, LTest;->c:I
+ add-int/lit8 v6, v6, 0x1
+ iput v6, v5, LTest;->c:I
+ invoke-virtual {v4}, LTest;->c()J
+ move-result-wide v6
+ iput-wide v6, v5, LTest;->b:J
+ move-object/from16 v0, p1
+ invoke-virtual {v8, v0}, LTest;->a(LTest;)Z
+ move-result v4
+ iget-object v5, v8, LTest;->a:LTest;
+ iget-object v6, v5, LTest;->a:LTest;
+ iget-object v5, v5, LTest;->a:LTest;
+ const-string v7, ""
+ invoke-virtual {v6, v7}, LTest;->a(Ljava/lang/String;)LTest;
+ move-result-object v6
+ iget-wide v10, v6, LTest;->b:J
+ const-wide/16 v12, -0x1
+ cmp-long v7, v10, v12
+ if-eqz v7, :cond_789
+ invoke-virtual {v5}, LTest;->c()J
+ move-result-wide v10
+ iget-wide v12, v6, LTest;->b:J
+ sub-long/2addr v10, v12
+ const-wide/16 v12, -0x1
+ iput-wide v12, v6, LTest;->b:J
+ const-wide/16 v12, 0x0
+ cmp-long v5, v10, v12
+ if-ltz v5, :cond_789
+ const-wide/16 v12, 0x7530
+ cmp-long v5, v10, v12
+ if-lez v5, :cond_83e
+ :cond_789
+ :goto_789
+ if-eqz v4, :cond_793
+ iget-object v4, v8, LTest;->a:LTest;
+ invoke-virtual {v4}, LTest;->c()Z
+ move-result v4
+ if-eqz v4, :cond_843
+ :cond_793
+ iget-object v4, v8, LTest;->a:LTest;
+ invoke-virtual {v4}, LTest;->a()V
+ const/4 v6, 0x0
+ :goto_799
+ iget-object v4, v8, LTest;->a:LTest;
+ const/4 v5, 0x0
+ invoke-virtual {v4, v5}, LTest;->a(Ljava/util/LTest;)V
+ iget-object v4, v8, LTest;->a:LTest;
+ invoke-virtual {v4}, LTest;->a()V
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v5, v4, LTest;->a:LTest;
+ invoke-virtual {v5}, LTest;->b()J
+ move-result-wide v10
+ iget-wide v12, v4, LTest;->q:J
+ sub-long v12, v10, v12
+ iget-wide v0, v4, LTest;->o:J
+ move-wide/from16 v16, v0
+ cmp-long v5, v12, v16
+ if-lez v5, :cond_7c3
+ iget-wide v12, v4, LTest;->p:J
+ sub-long v12, v10, v12
+ iget-object v5, v4, LTest;->a:LTest;
+ invoke-virtual {v5, v12, v13}, LTest;->a(J)V
+ iput-wide v10, v4, LTest;->q:J
+ :cond_7c3
+ iget-wide v12, v4, LTest;->s:J
+ sub-long v12, v10, v12
+ iget-wide v0, v4, LTest;->r:J
+ move-wide/from16 v16, v0
+ cmp-long v5, v12, v16
+ if-lez v5, :cond_7d6
+ iget-object v5, v4, LTest;->a:LTest;
+ invoke-virtual {v5}, LTest;->a()V
+ iput-wide v10, v4, LTest;->s:J
+ :cond_7d6
+ iget-object v4, v4, LTest;->a:LTest;
+ invoke-virtual {v4}, LTest;->a()V
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v5, v4, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ const-string v7, ""
+ invoke-virtual {v5, v7}, LTest;->a(Ljava/lang/String;)LTest;
+ move-result-object v7
+ iget-wide v10, v7, LTest;->b:J
+ const-wide/16 v12, -0x1
+ cmp-long v9, v10, v12
+ if-eqz v9, :cond_806
+ invoke-virtual {v4}, LTest;->c()J
+ move-result-wide v10
+ iget-wide v12, v7, LTest;->b:J
+ sub-long/2addr v10, v12
+ const-wide/16 v12, -0x1
+ iput-wide v12, v7, LTest;->b:J
+ const-wide/16 v12, 0x0
+ cmp-long v4, v10, v12
+ if-ltz v4, :cond_806
+ const-wide/16 v12, 0x7530
+ cmp-long v4, v10, v12
+ if-lez v4, :cond_8d7
+ :cond_806
+ :goto_806
+ if-eqz v6, :cond_8dc
+ iget v4, v5, LTest;->f:I
+ add-int/lit8 v4, v4, 0x1
+ iput v4, v5, LTest;->f:I
+ iget v4, v7, LTest;->a:I
+ iput v4, v5, LTest;->g:I
+ :goto_812
+ iget-object v4, v8, LTest;->a:LTest;
+ invoke-virtual {v4}, LTest;->c()Z
+ move-result v4
+ if-eqz v4, :cond_8e2
+ iget-object v4, v8, LTest;->a:LTest;
+ const/4 v5, 0x7
+ iget-object v6, v8, LTest;->a:LTest;
+ iget-object v6, v6, LTest;->a:LTest;
+ invoke-virtual {v6}, LTest;->b()J
+ move-result-wide v6
+ invoke-virtual {v4, v5, v6, v7}, LTest;->a(IJ)V
+ new-instance v4, LTest;
+ const/4 v5, 0x0
+ const/4 v6, 0x0
+ sget-object v7, LTest;->P:LTest;
+ iget-object v8, v8, LTest;->a:LTest;
+ iget-object v8, v8, LTest;->a:LTest;
+ invoke-virtual {v8}, LTest;->c()J
+ move-result-wide v8
+ const/4 v11, 0x0
+ move-object/from16 v10, p1
+ invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+ goto/16 :goto_6fe
+ :cond_83e
+ long-to-int v5, v10
+ iput v5, v6, LTest;->a:I
+ goto/16 :goto_789
+ :cond_843
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v5, v4, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ const-string v6, ""
+ invoke-virtual {v5, v6}, LTest;->a(Ljava/lang/String;)LTest;
+ move-result-object v5
+ iget v6, v5, LTest;->c:I
+ add-int/lit8 v6, v6, 0x1
+ iput v6, v5, LTest;->c:I
+ invoke-virtual {v4}, LTest;->c()J
+ move-result-wide v6
+ iput-wide v6, v5, LTest;->b:J
+ iget-object v4, v8, LTest;->a:LTest;
+ invoke-virtual {v4}, LTest;->b()LTest;
+ move-result-object v4
+ iput-object v4, v8, LTest;->a:LTest;
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-object v5, v4, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ const-string v6, ""
+ invoke-virtual {v5, v6}, LTest;->a(Ljava/lang/String;)LTest;
+ move-result-object v5
+ iget-wide v6, v5, LTest;->b:J
+ const-wide/16 v10, -0x1
+ cmp-long v6, v6, v10
+ if-eqz v6, :cond_88e
+ invoke-virtual {v4}, LTest;->c()J
+ move-result-wide v6
+ iget-wide v10, v5, LTest;->b:J
+ sub-long/2addr v6, v10
+ const-wide/16 v10, -0x1
+ iput-wide v10, v5, LTest;->b:J
+ const-wide/16 v10, 0x0
+ cmp-long v4, v6, v10
+ if-ltz v4, :cond_88e
+ const-wide/16 v10, 0x7530
+ cmp-long v4, v6, v10
+ if-lez v4, :cond_8a7
+ :cond_88e
+ :goto_88e
+ iget-object v4, v8, LTest;->a:LTest;
+ invoke-virtual {v4}, LTest;->b()Z
+ move-result v4
+ if-nez v4, :cond_8ab
+ iget-object v4, v8, LTest;->a:LTest;
+ const/4 v5, 0x4
+ iget-object v6, v8, LTest;->a:LTest;
+ iget-object v6, v6, LTest;->a:LTest;
+ invoke-virtual {v6}, LTest;->b()J
+ move-result-wide v6
+ invoke-virtual {v4, v5, v6, v7}, LTest;->a(IJ)V
+ const/4 v6, 0x0
+ goto/16 :goto_799
+ :cond_8a7
+ long-to-int v4, v6
+ iput v4, v5, LTest;->a:I
+ goto :goto_88e
+ :cond_8ab
+ iget-object v4, v8, LTest;->a:LTest;
+ iget-wide v4, v4, LTest;->a:J
+ const/4 v6, 0x2
+ new-array v6, v6, [I
+ invoke-static {v4, v5, v6}, LTest;->a(J[I)[I
+ move-result-object v4
+ iget-object v5, v8, LTest;->a:LTest;
+ iget-object v6, v8, LTest;->a:LTest;
+ iget-object v6, v6, LTest;->a:LTest;
+ iget v6, v6, LTest;->b:I
+ iget-object v7, v8, LTest;->a:LTest;
+ iget-object v7, v7, LTest;->a:LTest;
+ iget v7, v7, LTest;->b:I
+ invoke-virtual {v5, v6, v7, v4}, LTest;->a(II[I)I
+ move-result v5
+ new-instance v6, LTest;
+ const/4 v7, 0x0
+ aget v7, v4, v7
+ const/4 v9, 0x1
+ aget v4, v4, v9
+ const/16 v9, 0x6d
+ invoke-direct {v6, v7, v4, v5, v9}, LTest;-><init>(IIII)V
+ goto/16 :goto_799
+ :cond_8d7
+ long-to-int v4, v10
+ iput v4, v7, LTest;->a:I
+ goto/16 :goto_806
+ :cond_8dc
+ iget v4, v7, LTest;->a:I
+ iput v4, v5, LTest;->h:I
+ goto/16 :goto_812
+ :cond_8e2
+ if-nez v6, :cond_8f5
+ iget-object v4, v8, LTest;->a:LTest;
+ const/4 v5, 0x5
+ iget-object v6, v8, LTest;->a:LTest;
+ iget-object v6, v6, LTest;->a:LTest;
+ invoke-virtual {v6}, LTest;->b()J
+ move-result-wide v6
+ invoke-virtual {v4, v5, v6, v7}, LTest;->a(IJ)V
+ const/4 v4, 0x0
+ goto/16 :goto_6fe
+ :cond_8f5
+ iget-object v4, v8, LTest;->a:LTest;
+ invoke-virtual {v4, v6}, LTest;->a(LTest;)V
+ iget-object v4, v8, LTest;->a:LTest;
+ const/4 v5, 0x1
+ iget-object v7, v8, LTest;->a:LTest;
+ iget-object v7, v7, LTest;->a:LTest;
+ invoke-virtual {v7}, LTest;->b()J
+ move-result-wide v10
+ invoke-virtual {v4, v5, v10, v11}, LTest;->a(IJ)V
+ new-instance v4, LTest;
+ const/4 v5, 0x5
+ sget-object v7, LTest;->P:LTest;
+ iget-object v8, v8, LTest;->a:LTest;
+ iget-object v8, v8, LTest;->a:LTest;
+ invoke-virtual {v8}, LTest;->c()J
+ move-result-wide v8
+ const/4 v11, 0x0
+ move-object/from16 v10, p1
+ invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+ goto/16 :goto_6fe
+ :cond_91d
+ const/4 v4, 0x0
+ goto/16 :goto_6fe
+ :cond_920
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->a:LTest;
+ move-object/from16 v0, p1
+ move-object/from16 v1, p4
+ move-object/from16 v2, p3
+ invoke-virtual {v4, v0, v1, v2}, LTest;->a(LTest;LTest;LTest;)LTest;
+ move-result-object v4
+ move-object v12, v4
+ goto/16 :goto_56
+ :cond_931
+ const/4 v4, 0x0
+ goto/16 :goto_5d
+ :cond_934
+ const/4 v5, 0x0
+ goto/16 :goto_64
+ :cond_937
+ if-nez v4, :cond_93c
+ move-object v13, v14
+ goto/16 :goto_6a
+ :cond_93c
+ if-eqz v5, :cond_973
+ iget-object v5, v12, LTest;->a:LTest;
+ iget-object v6, v14, LTest;->a:LTest;
+ invoke-static {v5, v6}, LTest;->a(LTest;LTest;)I
+ move-result v4
+ iget v7, v5, LTest;->e:I
+ iget v8, v6, LTest;->e:I
+ add-int/2addr v7, v8
+ const v8, 0x3567e0
+ invoke-static {v7, v8}, Ljava/lang/Math;->max(II)I
+ move-result v7
+ div-int/lit16 v7, v7, 0x3e8
+ if-gt v4, v7, :cond_965
+ const/4 v4, 0x1
+ :goto_957
+ if-eqz v4, :cond_969
+ iget v4, v5, LTest;->e:I
+ iget v5, v6, LTest;->e:I
+ if-le v4, v5, :cond_967
+ const/4 v4, 0x1
+ :goto_960
+ if-eqz v4, :cond_973
+ move-object v13, v14
+ goto/16 :goto_6a
+ :cond_965
+ const/4 v4, 0x0
+ goto :goto_957
+ :cond_967
+ const/4 v4, 0x0
+ goto :goto_960
+ :cond_969
+ iget v4, v5, LTest;->f:I
+ iget v5, v6, LTest;->f:I
+ if-ge v4, v5, :cond_971
+ const/4 v4, 0x1
+ goto :goto_960
+ :cond_971
+ const/4 v4, 0x0
+ goto :goto_960
+ :cond_973
+ move-object v13, v12
+ goto/16 :goto_6a
+ :cond_976
+ const/4 v4, 0x1
+ move v11, v4
+ move v15, v5
+ goto/16 :goto_7b
+ :cond_97b
+ const/4 v4, 0x0
+ goto/16 :goto_84
+ :pswitch_97e
+ const-string v7, ""
+ goto/16 :goto_8c
+ :pswitch_982
+ const-string v7, ""
+ goto/16 :goto_8c
+ :pswitch_986
+ const-string v7, ""
+ goto/16 :goto_8c
+ :pswitch_98a
+ const-string v7, ""
+ goto/16 :goto_8c
+ :cond_98e
+ const/4 v4, 0x0
+ goto/16 :goto_a3
+ :pswitch_991
+ const-string v7, ""
+ goto/16 :goto_ab
+ :pswitch_995
+ const-string v7, ""
+ goto/16 :goto_ab
+ :cond_999
+ if-eqz v11, :cond_9ad
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->a:LTest;
+ const-string v5, ""
+ const-string v6, ""
+ const-string v7, ""
+ const-wide/16 v8, 0x1
+ const/4 v10, 0x1
+ invoke-virtual/range {v4 .. v10}, LTest;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V
+ goto/16 :goto_cd
+ :cond_9ad
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->a:LTest;
+ const-string v5, ""
+ const-string v6, ""
+ const-string v7, ""
+ const-wide/16 v8, 0x1
+ const/4 v10, 0x1
+ invoke-virtual/range {v4 .. v10}, LTest;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V
+ goto/16 :goto_cd
+ :cond_9bf
+ const-wide/16 v8, 0x0
+ move-wide/from16 v16, v8
+ goto/16 :goto_149
+ :cond_9c5
+ const/4 v4, 0x0
+ goto/16 :goto_217
+ :cond_9c8
+ move v11, v4
+ move v15, v5
+ goto/16 :goto_7b
+ :cond_9cc
+ move-object v12, v4
+ goto/16 :goto_56
+ :cond_9cf
+ move-object v13, v4
+ goto/16 :goto_17
+ :pswitch_data_9d2
+ .packed-switch 0x1
+ :pswitch_97e
+ :pswitch_982
+ :pswitch_8c
+ :pswitch_986
+ :pswitch_98a
+ .end packed-switch
+ :pswitch_data_9e0
+ .packed-switch 0x1
+ :pswitch_991
+ :pswitch_995
+ .end packed-switch
+.end method
\ No newline at end of file
diff --git a/src/test/smali/chain-of-loops/Test.java b/src/test/smali/chain-of-loops/Test.java
new file mode 100644
index 0000000..77d335d
--- /dev/null
+++ b/src/test/smali/chain-of-loops/Test.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+ public int b(int i, String s) {
+ throw new RuntimeException("b(ILjava/lang/String;)");
+ }
+
+ public int b(Test t) {
+ throw new RuntimeException("b(LTest;)");
+ }
+
+ public static int f(int i0, int i1) {
+ throw new RuntimeException("f(II)");
+ }
+
+ public static void main(String[] args) {
+ try {
+ new TestObject().method();
+ } catch (Exception e) {
+ System.out.println(e);
+ }
+ }
+}
diff --git a/src/test/smali/chain-of-loops/TestObject.java b/src/test/smali/chain-of-loops/TestObject.java
new file mode 100644
index 0000000..85cb78a
--- /dev/null
+++ b/src/test/smali/chain-of-loops/TestObject.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+ public final int method() {
+ return 0;
+ }
+}
diff --git a/src/test/smali/chain-of-loops/TestObject.smali b/src/test/smali/chain-of-loops/TestObject.smali
new file mode 100644
index 0000000..d516ee3
--- /dev/null
+++ b/src/test/smali/chain-of-loops/TestObject.smali
@@ -0,0 +1,1028 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field public i:I
+.field public e:Ljava/lang/String;
+.field public z:Ljava/lang/String;
+.field public f:LTest;
+.field public a:[LTest;
+
+.method public final method()I
+ .registers 7
+ .prologue
+ const/4 v5, 0x1
+ const/4 v1, 0x0
+ invoke-virtual {p0}, LTestObject;->method2()I
+ move-result v0
+ iget-object v2, p0, LTestObject;->e:Ljava/lang/String;
+ if-eqz v2, :cond_1b
+ iget-object v2, p0, LTestObject;->e:Ljava/lang/String;
+ const-string v3, ""
+ invoke-virtual {v2, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
+ move-result v2
+ if-nez v2, :cond_1b
+ iget-object v2, p0, LTestObject;->e:Ljava/lang/String;
+ invoke-static {v5, v2}, LTest;->b(ILjava/lang/String;)I
+ move-result v2
+ add-int/2addr v0, v2
+ :cond_1b
+ iget-object v2, p0, LTestObject;->f:LTest;
+ if-eqz v2, :cond_27
+ const/4 v2, 0x2
+ iget-object v3, p0, LTestObject;->f:LTest;
+ invoke-static {v2, v3}, LTest;->d(ILTest;)I
+ move-result v2
+ add-int/2addr v0, v2
+ :cond_27
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_47
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_47
+ move v2, v0
+ move v0, v1
+ :goto_32
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_46
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_43
+ const/4 v4, 0x3
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_43
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_32
+ :cond_46
+ move v0, v2
+ :cond_47
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_67
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_67
+ move v2, v0
+ move v0, v1
+ :goto_52
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_66
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_63
+ const/4 v4, 0x4
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_63
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_52
+ :cond_66
+ move v0, v2
+ :cond_67
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_87
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_87
+ move v2, v0
+ move v0, v1
+ :goto_72
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_86
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_83
+ const/4 v4, 0x5
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_83
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_72
+ :cond_86
+ move v0, v2
+ :cond_87
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_a7
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_a7
+ move v2, v0
+ move v0, v1
+ :goto_92
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_a6
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_a3
+ const/4 v4, 0x6
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_a3
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_92
+ :cond_a6
+ move v0, v2
+ :cond_a7
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_c7
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_c7
+ move v2, v0
+ move v0, v1
+ :goto_b2
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_c6
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_c3
+ const/4 v4, 0x7
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_c3
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_b2
+ :cond_c6
+ move v0, v2
+ :cond_c7
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_e8
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_e8
+ move v2, v0
+ move v0, v1
+ :goto_d2
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_e7
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_e4
+ const/16 v4, 0x8
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_e4
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_d2
+ :cond_e7
+ move v0, v2
+ :cond_e8
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_109
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_109
+ move v2, v0
+ move v0, v1
+ :goto_f3
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_108
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_105
+ const/16 v4, 0x9
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_105
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_f3
+ :cond_108
+ move v0, v2
+ :cond_109
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_12a
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_12a
+ move v2, v0
+ move v0, v1
+ :goto_114
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_129
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_126
+ const/16 v4, 0xa
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_126
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_114
+ :cond_129
+ move v0, v2
+ :cond_12a
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_14b
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_14b
+ move v2, v0
+ move v0, v1
+ :goto_135
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_14a
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_147
+ const/16 v4, 0xb
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_147
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_135
+ :cond_14a
+ move v0, v2
+ :cond_14b
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_16c
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_16c
+ move v2, v0
+ move v0, v1
+ :goto_156
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_16b
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_168
+ const/16 v4, 0xc
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_168
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_156
+ :cond_16b
+ move v0, v2
+ :cond_16c
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_18d
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_18d
+ move v2, v0
+ move v0, v1
+ :goto_177
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_18c
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_189
+ const/16 v4, 0xd
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_189
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_177
+ :cond_18c
+ move v0, v2
+ :cond_18d
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_1ae
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_1ae
+ move v2, v0
+ move v0, v1
+ :goto_198
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_1ad
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_1aa
+ const/16 v4, 0xe
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_1aa
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_198
+ :cond_1ad
+ move v0, v2
+ :cond_1ae
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_1cf
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_1cf
+ move v2, v0
+ move v0, v1
+ :goto_1b9
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_1ce
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_1cb
+ const/16 v4, 0xf
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_1cb
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_1b9
+ :cond_1ce
+ move v0, v2
+ :cond_1cf
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_1f0
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_1f0
+ move v2, v0
+ move v0, v1
+ :goto_1da
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_1ef
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_1ec
+ const/16 v4, 0x10
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_1ec
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_1da
+ :cond_1ef
+ move v0, v2
+ :cond_1f0
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_211
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_211
+ move v2, v0
+ move v0, v1
+ :goto_1fb
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_210
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_20d
+ const/16 v4, 0x11
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_20d
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_1fb
+ :cond_210
+ move v0, v2
+ :cond_211
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_232
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_232
+ move v2, v0
+ move v0, v1
+ :goto_21c
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_231
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_22e
+ const/16 v4, 0x12
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_22e
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_21c
+ :cond_231
+ move v0, v2
+ :cond_232
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_253
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_253
+ move v2, v0
+ move v0, v1
+ :goto_23d
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_252
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_24f
+ const/16 v4, 0x13
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_24f
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_23d
+ :cond_252
+ move v0, v2
+ :cond_253
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_274
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_274
+ move v2, v0
+ move v0, v1
+ :goto_25e
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_273
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_270
+ const/16 v4, 0x14
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_270
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_25e
+ :cond_273
+ move v0, v2
+ :cond_274
+ iget-object v2, p0, LTestObject;->z:Ljava/lang/String;
+ if-eqz v2, :cond_28b
+ iget-object v2, p0, LTestObject;->z:Ljava/lang/String;
+ const-string v3, ""
+ invoke-virtual {v2, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
+ move-result v2
+ if-nez v2, :cond_28b
+ const/16 v2, 0x15
+ iget-object v3, p0, LTestObject;->z:Ljava/lang/String;
+ invoke-static {v2, v3}, LTest;->b(ILjava/lang/String;)I
+ move-result v2
+ add-int/2addr v0, v2
+ :cond_28b
+ iget v2, p0, LTestObject;->i:I
+ if-eq v2, v5, :cond_298
+ const/16 v2, 0x16
+ iget v3, p0, LTestObject;->i:I
+ invoke-static {v2, v3}, LTest;->f(II)I
+ move-result v2
+ add-int/2addr v0, v2
+ :cond_298
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_2b9
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_2b9
+ move v2, v0
+ move v0, v1
+ :goto_2a3
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_2b8
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_2b5
+ const/16 v4, 0x17
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_2b5
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_2a3
+ :cond_2b8
+ move v0, v2
+ :cond_2b9
+ iget-object v2, p0, LTestObject;->z:Ljava/lang/String;
+ if-eqz v2, :cond_2d0
+ iget-object v2, p0, LTestObject;->z:Ljava/lang/String;
+ const-string v3, ""
+ invoke-virtual {v2, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
+ move-result v2
+ if-nez v2, :cond_2d0
+ const/16 v2, 0x18
+ iget-object v3, p0, LTestObject;->z:Ljava/lang/String;
+ invoke-static {v2, v3}, LTest;->b(ILjava/lang/String;)I
+ move-result v2
+ add-int/2addr v0, v2
+ :cond_2d0
+ iget-object v2, p0, LTest;->M:LTest;
+ if-eqz v2, :cond_2dd
+ const/16 v2, 0x19
+ iget-object v3, p0, LTest;->M:LTest;
+ invoke-static {v2, v3}, LTest;->d(ILTest;)I
+ move-result v2
+ add-int/2addr v0, v2
+ :cond_2dd
+ iget-object v2, p0, LTestObject;->z:Ljava/lang/String;
+ if-eqz v2, :cond_2f4
+ iget-object v2, p0, LTestObject;->z:Ljava/lang/String;
+ const-string v3, ""
+ invoke-virtual {v2, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
+ move-result v2
+ if-nez v2, :cond_2f4
+ const/16 v2, 0x1a
+ iget-object v3, p0, LTestObject;->z:Ljava/lang/String;
+ invoke-static {v2, v3}, LTest;->b(ILjava/lang/String;)I
+ move-result v2
+ add-int/2addr v0, v2
+ :cond_2f4
+ iget-object v2, p0, LTest;->o:[LTest;
+ if-eqz v2, :cond_315
+ iget-object v2, p0, LTest;->o:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_315
+ move v2, v0
+ move v0, v1
+ :goto_2ff
+ iget-object v3, p0, LTest;->o:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_314
+ iget-object v3, p0, LTest;->o:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_311
+ const/16 v4, 0x1b
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_311
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_2ff
+ :cond_314
+ move v0, v2
+ :cond_315
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_336
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_336
+ move v2, v0
+ move v0, v1
+ :goto_320
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_335
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_332
+ const/16 v4, 0x1c
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_332
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_320
+ :cond_335
+ move v0, v2
+ :cond_336
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_357
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_357
+ move v2, v0
+ move v0, v1
+ :goto_341
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_356
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_353
+ const/16 v4, 0x1d
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_353
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_341
+ :cond_356
+ move v0, v2
+ :cond_357
+ iget-object v2, p0, LTest;->t:[LTest;
+ if-eqz v2, :cond_378
+ iget-object v2, p0, LTest;->t:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_378
+ move v2, v0
+ move v0, v1
+ :goto_362
+ iget-object v3, p0, LTest;->t:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_377
+ iget-object v3, p0, LTest;->t:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_374
+ const/16 v4, 0x1e
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_374
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_362
+ :cond_377
+ move v0, v2
+ :cond_378
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_399
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_399
+ move v2, v0
+ move v0, v1
+ :goto_383
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_398
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_395
+ const/16 v4, 0x1f
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_395
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_383
+ :cond_398
+ move v0, v2
+ :cond_399
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_3ba
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_3ba
+ move v2, v0
+ move v0, v1
+ :goto_3a4
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_3b9
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_3b6
+ const/16 v4, 0x20
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_3b6
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_3a4
+ :cond_3b9
+ move v0, v2
+ :cond_3ba
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_3db
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_3db
+ move v2, v0
+ move v0, v1
+ :goto_3c5
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_3da
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_3d7
+ const/16 v4, 0x21
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_3d7
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_3c5
+ :cond_3da
+ move v0, v2
+ :cond_3db
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_3fc
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_3fc
+ move v2, v0
+ move v0, v1
+ :goto_3e6
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_3fb
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_3f8
+ const/16 v4, 0x22
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_3f8
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_3e6
+ :cond_3fb
+ move v0, v2
+ :cond_3fc
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_41d
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_41d
+ move v2, v0
+ move v0, v1
+ :goto_407
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_41c
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_419
+ const/16 v4, 0x23
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_419
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_407
+ :cond_41c
+ move v0, v2
+ :cond_41d
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_43e
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_43e
+ move v2, v0
+ move v0, v1
+ :goto_428
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_43d
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_43a
+ const/16 v4, 0x24
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_43a
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_428
+ :cond_43d
+ move v0, v2
+ :cond_43e
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_45f
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_45f
+ move v2, v0
+ move v0, v1
+ :goto_449
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_45e
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_45b
+ const/16 v4, 0x25
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_45b
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_449
+ :cond_45e
+ move v0, v2
+ :cond_45f
+ iget-object v2, p0, LTest;->T:LTest;
+ if-eqz v2, :cond_46c
+ const/16 v2, 0x26
+ iget-object v3, p0, LTest;->T:LTest;
+ invoke-static {v2, v3}, LTest;->d(ILTest;)I
+ move-result v2
+ add-int/2addr v0, v2
+ :cond_46c
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_48d
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_48d
+ move v2, v0
+ move v0, v1
+ :goto_477
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_48c
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_489
+ const/16 v4, 0x27
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_489
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_477
+ :cond_48c
+ move v0, v2
+ :cond_48d
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_4ae
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_4ae
+ move v2, v0
+ move v0, v1
+ :goto_498
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_4ad
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_4aa
+ const/16 v4, 0x28
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_4aa
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_498
+ :cond_4ad
+ move v0, v2
+ :cond_4ae
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_4cf
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_4cf
+ move v2, v0
+ move v0, v1
+ :goto_4b9
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_4ce
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_4cb
+ const/16 v4, 0x29
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_4cb
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_4b9
+ :cond_4ce
+ move v0, v2
+ :cond_4cf
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_4f0
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_4f0
+ move v2, v0
+ move v0, v1
+ :goto_4da
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_4ef
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_4ec
+ const/16 v4, 0x2a
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_4ec
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_4da
+ :cond_4ef
+ move v0, v2
+ :cond_4f0
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_511
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_511
+ move v2, v0
+ move v0, v1
+ :goto_4fb
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_510
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_50d
+ const/16 v4, 0x2b
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_50d
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_4fb
+ :cond_510
+ move v0, v2
+ :cond_511
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_532
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_532
+ move v2, v0
+ move v0, v1
+ :goto_51c
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_531
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_52e
+ const/16 v4, 0x2c
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_52e
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_51c
+ :cond_531
+ move v0, v2
+ :cond_532
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_553
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_553
+ move v2, v0
+ move v0, v1
+ :goto_53d
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ array-length v3, v3
+ if-ge v0, v3, :cond_552
+ iget-object v3, p0, LTestObject;->a:[LTest;
+ aget-object v3, v3, v0
+ if-eqz v3, :cond_54f
+ const/16 v4, 0x2d
+ invoke-static {v4, v3}, LTest;->d(ILTest;)I
+ move-result v3
+ add-int/2addr v2, v3
+ :cond_54f
+ add-int/lit8 v0, v0, 0x1
+ goto :goto_53d
+ :cond_552
+ move v0, v2
+ :cond_553
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ if-eqz v2, :cond_571
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-lez v2, :cond_571
+ :goto_55c
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ array-length v2, v2
+ if-ge v1, v2, :cond_571
+ iget-object v2, p0, LTestObject;->a:[LTest;
+ aget-object v2, v2, v1
+ if-eqz v2, :cond_56e
+ const/16 v3, 0x2e
+ invoke-static {v3, v2}, LTest;->d(ILTest;)I
+ move-result v2
+ add-int/2addr v0, v2
+ :cond_56e
+ add-int/lit8 v1, v1, 0x1
+ goto :goto_55c
+ :cond_571
+ iget-object v1, p0, LTest;->V:LTest;
+ if-eqz v1, :cond_57e
+ const/16 v1, 0x64
+ iget-object v2, p0, LTest;->V:LTest;
+ invoke-static {v1, v2}, LTest;->d(ILTest;)I
+ move-result v1
+ add-int/2addr v0, v1
+ :cond_57e
+ return v0
+.end method
+
+.method public final method2()I
+ .registers 1
+ .prologue
+ const/4 v0, 0x1
+ return v0
+.end method
diff --git a/src/test/smali/controlflow/ControlFlow.smali b/src/test/smali/controlflow/ControlFlow.smali
new file mode 100644
index 0000000..35881fe
--- /dev/null
+++ b/src/test/smali/controlflow/ControlFlow.smali
@@ -0,0 +1,225 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method static constantEqTrue()I
+ .locals 1
+
+ const v0, 0
+ if-eqz v0, :equals
+ const v0, 1
+ return v0
+ :equals
+ const v0, 2
+ return v0
+.end method
+
+.method static constantEqFalse()I
+ .locals 1
+
+ const v0, 1
+ if-eqz v0, :equals
+ const v0, 1
+ return v0
+ :equals
+ const v0, 2
+ return v0
+.end method
+
+.method static constantNeTrue()I
+ .locals 1
+
+ const v0, 1
+ if-nez v0, :equals
+ const v0, 1
+ return v0
+ :equals
+ const v0, 2
+ return v0
+.end method
+
+.method static constantNeFalse()I
+ .locals 1
+
+ const v0, 0
+ if-nez v0, :equals
+ const v0, 1
+ return v0
+ :equals
+ const v0, 2
+ return v0
+.end method
+
+.method static constantLtzTrue()I
+ .locals 1
+
+ const v0, -1
+ if-ltz v0, :equals
+ const v0, 1
+ return v0
+ :equals
+ const v0, 2
+ return v0
+.end method
+
+.method static constantLtzFalse()I
+ .locals 1
+
+ const v0, 0
+ if-ltz v0, :equals
+ const v0, 1
+ return v0
+ :equals
+ const v0, 2
+ return v0
+.end method
+
+.method static constantLezTrue()I
+ .locals 1
+
+ const v0, 0
+ if-lez v0, :equals
+ const v0, 1
+ return v0
+ :equals
+ const v0, 2
+ return v0
+.end method
+
+.method static constantLezFalse()I
+ .locals 1
+
+ const v0, 1
+ if-lez v0, :equals
+ const v0, 1
+ return v0
+ :equals
+ const v0, 2
+ return v0
+.end method
+
+.method static constantGtzTrue()I
+ .locals 1
+
+ const v0, 1
+ if-gtz v0, :equals
+ const v0, 1
+ return v0
+ :equals
+ const v0, 2
+ return v0
+.end method
+
+.method static constantGtzFalse()I
+ .locals 1
+
+ const v0, 0
+ if-gtz v0, :equals
+ const v0, 1
+ return v0
+ :equals
+ const v0, 2
+ return v0
+.end method
+
+.method static constantGezTrue()I
+ .locals 1
+
+ const v0, 0
+ if-gez v0, :equals
+ const v0, 1
+ return v0
+ :equals
+ const v0, 2
+ return v0
+.end method
+
+.method static constantGezFalse()I
+ .locals 1
+
+ const v0, -1
+ if-gez v0, :equals
+ const v0, 1
+ return v0
+ :equals
+ const v0, 2
+ return v0
+.end method
+
+.method static cmpConstantLong()I
+ .locals 4
+
+ const-wide v0, 0
+ const-wide v2, 0
+ cmp-long v0, v0, v2
+ if-eqz v0, :equals
+ const v0, 1
+ return v0
+ :equals
+ const v0, 2
+ return v0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 3
+
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ invoke-static {}, LTest;->constantEqTrue()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->constantEqFalse()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->constantNeTrue()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->constantNeFalse()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->constantLtzTrue()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->constantLtzFalse()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->constantLezTrue()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->constantLezFalse()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->constantGtzTrue()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->constantGtzFalse()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->constantGezTrue()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->constantGezFalse()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ invoke-static {}, LTest;->cmpConstantLong()I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ return-void
+.end method
diff --git a/src/test/smali/fibonacci/Fibonacci.smali b/src/test/smali/fibonacci/Fibonacci.smali
new file mode 100644
index 0000000..3345ec0
--- /dev/null
+++ b/src/test/smali/fibonacci/Fibonacci.smali
@@ -0,0 +1,129 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method static fibonacci(I)I
+ .locals 2
+
+ if-eqz p0, :return
+ const/4 v0, 0x1
+ if-ne p0, v0, :calc
+
+ :return
+ return p0
+
+ :calc
+ add-int/lit8 v0, p0, -0x1
+ invoke-static {v0}, LTest;->fibonacci(I)I
+ move-result v0
+ add-int/lit8 v1, p0, -0x2
+ invoke-static {v1}, LTest;->fibonacci(I)I
+ move-result v1
+ add-int p0, v0, v1
+ goto :return
+.end method
+
+.method static fibonacciLong(J)J
+ .locals 4
+
+ const-wide/16 v2, 0x1
+ const-wide/16 v0, 0x0
+ cmp-long v0, p0, v0
+ if-eqz v0, :return
+ cmp-long v0, p0, v2
+ if-nez v0, :calc
+
+ :return
+ return-wide p0
+
+ :calc
+ sub-long v0, p0, v2
+ invoke-static {v0, v1}, LTest;->fibonacciLong(J)J
+ move-result-wide v0
+ const-wide/16 v2, 0x2
+ sub-long v2, p0, v2
+ invoke-static {v2, v3}, LTest;->fibonacciLong(J)J
+ move-result-wide v2
+ add-long p0, v0, v2
+ goto :return
+.end method
+
+.method static fibonacciJack(I)I
+ .locals 2
+
+ if-eqz p0, :return
+ const/4 v0, 0x1
+ if-ne p0, v0, :calc
+
+ :return
+ return p0
+
+ :calc
+ add-int/lit8 v0, p0, -0x1
+ invoke-static {v0}, LTest;->fibonacciJack(I)I
+ move-result v0
+ add-int/lit8 v1, p0, -0x2
+ invoke-static {v1}, LTest;->fibonacciJack(I)I
+ move-result v1
+ add-int/2addr v0, v1
+ return v0
+.end method
+
+.method static fibonacciLongJack(J)J
+ .locals 4
+
+ const-wide/16 v2, 0x1
+ const-wide/16 v0, 0x0
+ cmp-long v0, p0, v0
+ if-eqz v0, :return
+ cmp-long v0, p0, v2
+ if-nez v0, :calc
+
+ :return
+ return-wide p0
+
+ :calc
+ sub-long v0, p0, v2
+ invoke-static {v0, v1}, LTest;->fibonacciLongJack(J)J
+ move-result-wide v0
+ const-wide/16 v2, 0x2
+ sub-long v2, p0, v2
+ invoke-static {v2, v3}, LTest;->fibonacciLongJack(J)J
+ move-result-wide v2
+ add-long/2addr v0, v2
+ return-wide v0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .registers 5
+
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ const/16 v1, 0xa
+ invoke-static {v1}, LTest;->fibonacci(I)I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-wide/16 v2, 0xa
+ invoke-static {v2, v3}, LTest;->fibonacciLong(J)J
+ move-result-wide v2
+ invoke-virtual {v0, v2, v3}, Ljava/io/PrintStream;->println(J)V
+
+ const/16 v1, 0xa
+ invoke-static {v1}, LTest;->fibonacciJack(I)I
+ move-result v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-wide/16 v2, 0xa
+ invoke-static {v2, v3}, LTest;->fibonacciLongJack(J)J
+ move-result-wide v2
+ invoke-virtual {v0, v2, v3}, Ljava/io/PrintStream;->println(J)V
+
+ return-void
+.end method
diff --git a/src/test/smali/fill-array-data/FillArrayData.smali b/src/test/smali/fill-array-data/FillArrayData.smali
new file mode 100644
index 0000000..81a5834
--- /dev/null
+++ b/src/test/smali/fill-array-data/FillArrayData.smali
@@ -0,0 +1,61 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+# Simple fill array data
+.method public static test1()[I
+ .registers 2
+
+ const/4 v1, 3
+ new-array v0, v1, [I
+ fill-array-data v0, :array_data
+ return-object v0
+
+ :array_data
+ .array-data 4
+ 1 2 3
+ .end array-data
+.end method
+
+# Fill array data after data
+.method public static test2()[I
+ .registers 2
+
+ goto :start
+
+ :array_data
+ .array-data 4
+ 4 5 6
+ .end array-data
+
+ :start
+ const/4 v1, 3
+ new-array v0, v1, [I
+ fill-array-data v0, :array_data
+ return-object v0
+
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 2
+
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ invoke-static {}, LTest;->test1()[I
+ move-result-object v1
+ invoke-static {v1}, Ljava/util/Arrays;->toString([I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ invoke-static {}, LTest;->test2()[I
+ move-result-object v1
+ invoke-static {v1}, Ljava/util/Arrays;->toString([I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ return-void
+.end method
diff --git a/src/test/smali/filled-new-array/FilledNewArray.smali b/src/test/smali/filled-new-array/FilledNewArray.smali
new file mode 100644
index 0000000..2550e3f
--- /dev/null
+++ b/src/test/smali/filled-new-array/FilledNewArray.smali
@@ -0,0 +1,93 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+# Filled new array from arguments.
+.method public static test1(III)[I
+ .locals 1
+
+ filled-new-array {p0, p1, p2}, [I
+ move-result-object v0
+ return-object v0
+.end method
+
+# Filled new array from constants.
+.method public static test2()[I
+ .locals 3
+
+ const/4 v0, 4
+ const/4 v1, 5
+ const/4 v2, 6
+ filled-new-array {v0, v1, v2}, [I
+ move-result-object v0
+ return-object v0
+.end method
+
+# Filled new array range from arguments.
+.method public static test3(IIIIII)[I
+ .locals 1
+
+ filled-new-array/range {p0 .. p5}, [I
+ move-result-object v0
+ return-object v0
+.end method
+
+# Filled new array range from constants.
+.method public static test4()[I
+ .locals 6
+
+ const/4 v0, 6
+ const/4 v1, 5
+ const/4 v2, 4
+ const/4 v3, 3
+ const/4 v4, 2
+ const/4 v5, 1
+ filled-new-array/range {v0 .. v5}, [I
+ move-result-object v0
+ return-object v0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 7
+
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ const/4 v1, 1
+ const/4 v2, 2
+ const/4 v3, 3
+ invoke-static {v1, v2, v3}, LTest;->test1(III)[I
+ move-result-object v1
+ invoke-static {v1}, Ljava/util/Arrays;->toString([I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ invoke-static {}, LTest;->test2()[I
+ move-result-object v1
+ invoke-static {v1}, Ljava/util/Arrays;->toString([I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ const/4 v1, 1
+ const/4 v2, 2
+ const/4 v3, 3
+ const/4 v4, 4
+ const/4 v5, 5
+ const/4 v6, 6
+ invoke-static/range {v1 .. v6}, LTest;->test3(IIIIII)[I
+ move-result-object v1
+ invoke-static {v1}, Ljava/util/Arrays;->toString([I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ invoke-static {}, LTest;->test4()[I
+ move-result-object v1
+ invoke-static {v1}, Ljava/util/Arrays;->toString([I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ return-void
+.end method
diff --git a/src/test/smali/infinite-loop/InfiniteLoop1.smali b/src/test/smali/infinite-loop/InfiniteLoop1.smali
new file mode 100644
index 0000000..a93923b
--- /dev/null
+++ b/src/test/smali/infinite-loop/InfiniteLoop1.smali
@@ -0,0 +1,53 @@
+# 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.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method public static loop1()V
+ .locals 0
+ goto :a
+
+ :a
+ goto :b
+
+ :b
+ goto :c
+
+ :c
+ goto :d
+
+ :d
+ goto :b
+
+ return-void
+.end method
+
+.method public static loop2()V
+ .locals 2
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const/4 v1, 0x1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+ goto :a
+
+ :a
+ goto :b
+
+ :b
+ goto :c
+
+ :c
+ goto :d
+
+ :d
+ goto :b
+
+ return-void
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 0
+ return-void
+.end method
diff --git a/src/test/smali/merge-blocks-regression/Test.java b/src/test/smali/merge-blocks-regression/Test.java
new file mode 100644
index 0000000..ca61ff4
--- /dev/null
+++ b/src/test/smali/merge-blocks-regression/Test.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+
+ public Object b;
+ public Test d;
+ public Test e;
+ public Test g;
+ public Test f;
+
+ public void a() { throw new RuntimeException("Test.a()"); }
+ public void a(long p1) { throw new RuntimeException("Test.a(long)"); }
+ public void a(long p1, boolean p2) { throw new RuntimeException("Test.a(long, boolean)"); }
+ public Test a(java.io.DataInputStream p1) { throw new RuntimeException("Test.a(input-stream)"); }
+ public Test a(byte[] p1, Test p2) { throw new RuntimeException("Test.a(B[], Test)"); }
+ public java.io.File b() { throw new RuntimeException("Test.b()"); }
+ public Test bW_() { throw new RuntimeException("Test.bW_()"); }
+ public long c() { throw new RuntimeException("Test.d()"); }
+ public void c(boolean p1) { throw new RuntimeException("Test.c(boolean)"); }
+ public void c(java.nio.ByteBuffer p1) { throw new RuntimeException("Test.c(byte-buf)"); }
+ public byte[] cB() { throw new RuntimeException("Test.cB()"); }
+ public long d() { throw new RuntimeException("Test.d()"); }
+ public void d(boolean p1) { throw new RuntimeException("Test.d(boolean)"); }
+ public Test e() { throw new RuntimeException("Test.e()"); }
+ public void eV() { throw new RuntimeException("Test.eV()"); }
+
+ public static void main(String[] args) {
+ try {
+ new TestObject().f();
+ } catch (Exception e) {
+ System.out.println(e);
+ }
+ }
+}
diff --git a/src/test/smali/merge-blocks-regression/TestObject.java b/src/test/smali/merge-blocks-regression/TestObject.java
new file mode 100644
index 0000000..59b46f6
--- /dev/null
+++ b/src/test/smali/merge-blocks-regression/TestObject.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+ public void f() {
+ }
+}
diff --git a/src/test/smali/merge-blocks-regression/TestObject.smali b/src/test/smali/merge-blocks-regression/TestObject.smali
new file mode 100644
index 0000000..5a9ddec
--- /dev/null
+++ b/src/test/smali/merge-blocks-regression/TestObject.smali
@@ -0,0 +1,193 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field public c:LTest;
+.field public b:LTest;
+.field public d:LTest;
+.field public e:LTest;
+.field public h:LTest;
+.field public i:LTest;
+.field public j:LTest;
+
+.method public final f()V
+ .registers 15
+ const/16 v11, 0xd
+ const/4 v2, 0x0
+ const/4 v1, 0x1
+ iget-object v3, p0, LTestObject;->c:LTest;
+ iget-object v0, p0, LTestObject;->b:LTest;
+ invoke-virtual {v0}, LTest;->bW_()LTest;
+ move-result-object v0
+ iget-object v4, p0, LTestObject;->b:LTest;
+ invoke-virtual {v4}, LTest;->e()LTest;
+ move-result-object v4
+ new-instance v5, Ljava/io/File;
+ invoke-virtual {v0}, LTest;->b()Ljava/io/File;
+ move-result-object v0
+ const-string v6, "nlp_state"
+ invoke-direct {v5, v0, v6}, Ljava/io/File;-><init>(Ljava/io/File;Ljava/lang/String;)V
+ invoke-virtual {v4}, LTest;->d()J
+ move-result-wide v6
+ invoke-virtual {v4}, LTest;->c()J
+ move-result-wide v8
+ :try_start_25
+ new-instance v0, Ljava/io/FileInputStream;
+ invoke-direct {v0, v5}, Ljava/io/FileInputStream;-><init>(Ljava/io/File;)V
+ new-instance v4, Ljava/io/BufferedInputStream;
+ invoke-direct {v4, v0}, Ljava/io/BufferedInputStream;-><init>(Ljava/io/InputStream;)V
+ iget-object v0, v3, LTest;->g:LTest;
+ invoke-virtual {v0}, LTest;->cB()[B
+ :try_end_34
+ .catch Ljava/io/FileNotFoundException; {:try_start_25 .. :try_end_34} :catch_bf
+ .catch Ljava/lang/SecurityException; {:try_start_25 .. :try_end_34} :catch_c8
+ .catch Ljava/io/IOException; {:try_start_25 .. :try_end_34} :catch_d8
+ move-result-object v0
+ :try_start_35
+ new-instance v5, Ljava/io/DataInputStream;
+ invoke-direct {v5, v4}, Ljava/io/DataInputStream;-><init>(Ljava/io/InputStream;)V
+ invoke-virtual {v5}, Ljava/io/DataInputStream;->readUnsignedShort()I
+ move-result v10
+ if-ne v10, v11, :cond_c4
+ if-ne v10, v11, :cond_b2
+ iget-object v10, v3, LTest;->f:LTest;
+ if-nez v10, :cond_4d
+ const/4 v10, 0x0
+ invoke-static {v0, v10}, LTest;->a([BLTest;)LTest;
+ move-result-object v0
+ iput-object v0, v3, LTest;->f:LTest;
+ :cond_4d
+ iget-object v0, v3, LTest;->f:LTest;
+ invoke-virtual {v0, v5}, LTest;->a(Ljava/io/DataInputStream;)LTest;
+ move-result-object v0
+ iget-object v0, v0, LTest;->b:Ljava/lang/Object;
+ check-cast v0, [B
+ invoke-static {v0}, Ljava/nio/ByteBuffer;->wrap([B)Ljava/nio/ByteBuffer;
+ :try_end_5a
+ .catch Ljava/io/IOException; {:try_start_35 .. :try_end_5a} :catch_ba
+ .catch Ljava/io/FileNotFoundException; {:try_start_35 .. :try_end_5a} :catch_bf
+ .catch Ljava/lang/SecurityException; {:try_start_35 .. :try_end_5a} :catch_c8
+ move-result-object v5
+ :try_start_5b
+ invoke-virtual {v5}, Ljava/nio/ByteBuffer;->getLong()J
+ move-result-wide v10
+ invoke-virtual {v5}, Ljava/nio/ByteBuffer;->getLong()J
+ move-result-wide v12
+ add-long/2addr v10, v12
+ sub-long v6, v10, v6
+ invoke-static {v8, v9, v6, v7}, Ljava/lang/Math;->min(JJ)J
+ move-result-wide v6
+ invoke-virtual {v5}, Ljava/nio/ByteBuffer;->get()B
+ move-result v0
+ if-ne v0, v1, :cond_cd
+ move v0, v1
+ :goto_71
+ invoke-virtual {v3, v6, v7, v0}, LTest;->a(JZ)V
+ :try_end_74
+ .catch Ljava/lang/IllegalArgumentException; {:try_start_5b .. :try_end_74} :catch_cf
+ .catch Ljava/nio/BufferUnderflowException; {:try_start_5b .. :try_end_74} :catch_dd
+ .catch Ljava/io/IOException; {:try_start_5b .. :try_end_74} :catch_ba
+ .catch Ljava/io/FileNotFoundException; {:try_start_5b .. :try_end_74} :catch_bf
+ .catch Ljava/lang/SecurityException; {:try_start_5b .. :try_end_74} :catch_c8
+ :try_start_74
+ iget-object v0, v3, LTest;->d:LTest;
+ invoke-virtual {v0, v5}, LTest;->c(Ljava/nio/ByteBuffer;)V
+ iget-object v0, v3, LTest;->e:LTest;
+ invoke-virtual {v0, v5}, LTest;->c(Ljava/nio/ByteBuffer;)V
+ :try_end_7e
+ .catch Ljava/io/IOException; {:try_start_74 .. :try_end_7e} :catch_ba
+ .catch Ljava/io/FileNotFoundException; {:try_start_74 .. :try_end_7e} :catch_bf
+ .catch Ljava/lang/SecurityException; {:try_start_74 .. :try_end_7e} :catch_c8
+ :goto_7e
+ :try_start_7e
+ invoke-virtual {v4}, Ljava/io/BufferedInputStream;->close()V
+ :try_end_81
+ .catch Ljava/io/FileNotFoundException; {:try_start_7e .. :try_end_81} :catch_bf
+ .catch Ljava/lang/SecurityException; {:try_start_7e .. :try_end_81} :catch_c8
+ .catch Ljava/io/IOException; {:try_start_7e .. :try_end_81} :catch_d8
+ :goto_81
+ iget-object v0, p0, LTestObject;->i:LTest;
+ invoke-virtual {v0, v1}, LTest;->c(Z)V
+ iget-object v0, p0, LTestObject;->i:LTest;
+ invoke-virtual {v0, v2}, LTest;->d(Z)V
+ iget-object v0, p0, LTestObject;->d:LTest;
+ if-eqz v0, :cond_94
+ iget-object v0, p0, LTestObject;->d:LTest;
+ invoke-virtual {v0}, LTest;->a()V
+ :cond_94
+ iget-object v0, p0, LTestObject;->e:LTest;
+ if-eqz v0, :cond_9d
+ iget-object v0, p0, LTestObject;->e:LTest;
+ invoke-virtual {v0}, LTest;->eV()V
+ :cond_9d
+ iget-object v0, p0, LTestObject;->h:LTest;
+ iget-object v1, p0, LTestObject;->b:LTest;
+ invoke-virtual {v1}, LTest;->e()LTest;
+ move-result-object v1
+ invoke-virtual {v1}, LTest;->c()J
+ move-result-wide v2
+ invoke-virtual {v0, v2, v3}, LTest;->a(J)V
+ iget-object v0, p0, LTestObject;->j:LTest;
+ invoke-virtual {v0}, LTest;->a()V
+ return-void
+ :cond_b2
+ :try_start_b2
+ new-instance v0, Ljava/io/IOException;
+ const-string v4, "Incompatible version."
+ invoke-direct {v0, v4}, Ljava/io/IOException;-><init>(Ljava/lang/String;)V
+ throw v0
+ :try_end_ba
+ .catch Ljava/io/IOException; {:try_start_b2 .. :try_end_ba} :catch_ba
+ .catch Ljava/io/FileNotFoundException; {:try_start_b2 .. :try_end_ba} :catch_bf
+ .catch Ljava/lang/SecurityException; {:try_start_b2 .. :try_end_ba} :catch_c8
+ :catch_ba
+ move-exception v0
+ :try_start_bb
+ invoke-virtual {v3, v8, v9}, LTest;->a(J)V
+ throw v0
+ :try_end_bf
+ .catch Ljava/io/FileNotFoundException; {:try_start_bb .. :try_end_bf} :catch_bf
+ .catch Ljava/lang/SecurityException; {:try_start_bb .. :try_end_bf} :catch_c8
+ .catch Ljava/io/IOException; {:try_start_bb .. :try_end_bf} :catch_d8
+ :catch_bf
+ move-exception v0
+ invoke-virtual {v3, v8, v9}, LTest;->a(J)V
+ goto :goto_81
+ :cond_c4
+ :try_start_c4
+ invoke-virtual {v3, v8, v9}, LTest;->a(J)V
+ :try_end_c7
+ .catch Ljava/io/IOException; {:try_start_c4 .. :try_end_c7} :catch_ba
+ .catch Ljava/io/FileNotFoundException; {:try_start_c4 .. :try_end_c7} :catch_bf
+ .catch Ljava/lang/SecurityException; {:try_start_c4 .. :try_end_c7} :catch_c8
+ goto :goto_7e
+ :catch_c8
+ move-exception v0
+ invoke-virtual {v3, v8, v9}, LTest;->a(J)V
+ goto :goto_81
+ :cond_cd
+ move v0, v2
+ goto :goto_71
+ :catch_cf
+ move-exception v0
+ :goto_d0
+ :try_start_d0
+ new-instance v4, Ljava/io/IOException;
+ const-string v5, "Byte buffer read failed."
+ invoke-direct {v4, v5, v0}, Ljava/io/IOException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
+ throw v4
+ :try_end_d8
+ .catch Ljava/io/IOException; {:try_start_d0 .. :try_end_d8} :catch_ba
+ .catch Ljava/io/FileNotFoundException; {:try_start_d0 .. :try_end_d8} :catch_bf
+ .catch Ljava/lang/SecurityException; {:try_start_d0 .. :try_end_d8} :catch_c8
+ :catch_d8
+ move-exception v0
+ invoke-virtual {v3, v8, v9}, LTest;->a(J)V
+ goto :goto_81
+ :catch_dd
+ move-exception v0
+ goto :goto_d0
+.end method
diff --git a/src/test/smali/multiple-returns/MultipleReturns.smali b/src/test/smali/multiple-returns/MultipleReturns.smali
new file mode 100644
index 0000000..145736b
--- /dev/null
+++ b/src/test/smali/multiple-returns/MultipleReturns.smali
@@ -0,0 +1,126 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.field static private result:Ljava/lang/String;
+
+.method public static test1(Z)Ljava/lang/String;
+ .locals 1
+ .param p0
+
+ if-eqz p0, :false_branch
+
+ const-string v0, "T"
+ return-object v0
+
+ :false_branch
+ const-string v0, "F"
+ return-object v0
+.end method
+
+.method public static test2(Z)V
+ .locals 1
+ .param p0
+
+ if-eqz p0, :false_branch
+
+ const-string v0, "t"
+ sput-object v0, LTest;->result:Ljava/lang/String;
+ return-void
+
+ :false_branch
+ const-string v0, "f"
+ sput-object v0, LTest;->result:Ljava/lang/String;
+ return-void
+.end method
+
+.method public static test3(Z)J
+ .locals 2
+ .param p0
+
+ if-eqz p0, :false_branch
+
+ const-wide v0, 0x1
+ return-wide v0
+
+ :false_branch
+ const-wide/high16 v0, 0x4000000000000000L
+ return-wide v0
+.end method
+
+.method public static test4(Z)Z
+ .locals 0
+ .param p0
+
+ if-eqz p0, :false_branch
+
+ return p0
+
+ :false_branch
+ return p0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 3
+ .param p0
+
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ const/4 v1, 0x1
+ invoke-static {v1}, LTest;->test1(Z)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ const/4 v1, 0x0
+ invoke-static {v1}, LTest;->test1(Z)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ const/4 v1, 0x1
+ invoke-static {v1}, LTest;->test2(Z)V
+ sget-object v1, LTest;->result:Ljava/lang/String;
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ const/4 v1, 0x0
+ invoke-static {v1}, LTest;->test2(Z)V
+ sget-object v1, LTest;->result:Ljava/lang/String;
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ invoke-virtual {v0}, Ljava/io/PrintStream;->println()V
+
+ const/4 v1, 0x1
+ invoke-static {v1}, LTest;->test3(Z)J
+ move-result-wide v1
+ invoke-static {v1, v2}, Ljava/lang/Long;->toString(J)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ const/4 v1, 0x0
+ invoke-static {v1}, LTest;->test3(Z)J
+ move-result-wide v1
+ invoke-static {v1, v2}, Ljava/lang/Long;->toString(J)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ const/4 v1, 0x1
+ invoke-static {v1}, LTest;->test4(Z)Z
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Boolean;->toString(Z)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ const/4 v1, 0x0
+ invoke-static {v1}, LTest;->test4(Z)Z
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Boolean;->toString(Z)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ return-void
+.end method
+
+
diff --git a/src/test/smali/new-instance-and-init/Test.java b/src/test/smali/new-instance-and-init/Test.java
new file mode 100644
index 0000000..a47e69d
--- /dev/null
+++ b/src/test/smali/new-instance-and-init/Test.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+
+ public Test(int i) {
+ System.out.println("Test(" + i + ")");
+ }
+
+ public static void main(String[] args) {
+ TestObject.allocate(0);
+ TestObject.allocate(4);
+ TestObject.allocate(10);
+ }
+}
diff --git a/src/test/smali/new-instance-and-init/TestObject.java b/src/test/smali/new-instance-and-init/TestObject.java
new file mode 100644
index 0000000..003f09d
--- /dev/null
+++ b/src/test/smali/new-instance-and-init/TestObject.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+ public static void allocate(int i) {
+ }
+}
diff --git a/src/test/smali/new-instance-and-init/TestObject.smali b/src/test/smali/new-instance-and-init/TestObject.smali
new file mode 100644
index 0000000..665de1b
--- /dev/null
+++ b/src/test/smali/new-instance-and-init/TestObject.smali
@@ -0,0 +1,26 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.method public static allocate(I)V
+ .registers 4
+ new-instance v2, LTest;
+ if-nez v3, :cond_9
+ const/4 v1, 0x0
+ invoke-direct {v2, v1}, LTest;-><init>(I)V
+ :goto_8
+ return-void
+ :cond_9
+ const/16 v0, 0xa
+ if-ge v3, v0, :cond_14
+ const/4 v1, 0x0
+ invoke-direct {v2, v1}, LTest;-><init>(I)V
+ goto :goto_8
+ :cond_14
+ const/4 v1, 0x0
+ invoke-direct {v2, v1}, LTest;-><init>(I)V
+ goto :goto_8
+.end method
\ No newline at end of file
diff --git a/src/test/smali/overlapping-long-registers/Overlapping.smali b/src/test/smali/overlapping-long-registers/Overlapping.smali
new file mode 100644
index 0000000..167eb1b
--- /dev/null
+++ b/src/test/smali/overlapping-long-registers/Overlapping.smali
@@ -0,0 +1,53 @@
+# 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.
+
+# Some versions of dalvik had a bug where you cannot use the second half of an input long
+# as the first part of an output long. This smali code explicitly has that issue so the
+# generated dex file can be used to test art/dalvik versions.
+#
+# The issue was that if you have
+#
+# add-long v3, v0, v2
+#
+# dalvik would add v0 and v2 and store the result in v3 before adding v1 and v3 (now clobbered).
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method static add(JJ)J
+ .locals 5
+ move-wide v0, p0
+ move-wide v2, p2
+ add-long v3, v0, v2
+ return-wide v3
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 10
+
+ sget-object v5, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ const-wide/high16 v0, 0x4000000000000000L
+ const-wide/high16 v2, 0x4100000000000000L
+ invoke-static {v0, v1, v2, v3}, LTest;->add(JJ)J
+ move-result-wide v6
+ invoke-virtual {v5, v6, v7}, Ljava/io/PrintStream;->println(J)V
+
+ # Adding loop in an attempt to get the jit to process the add method.
+ const v8, 1000000
+ const v9, 1
+ :loop
+ if-eqz v8, :exit
+ const-wide v0, 0x4000000000040000L
+ const-wide v2, 0x4100000000041000L
+ invoke-static {v0, v1, v2, v3}, LTest;->add(JJ)J
+ move-result-wide v6
+ sub-int v8, v8, v9
+ goto :loop
+
+ :exit
+ invoke-virtual {v5, v6, v7}, Ljava/io/PrintStream;->println(J)V
+ return-void
+.end method
diff --git a/src/test/smali/packed-switch/PackedSwitch.smali b/src/test/smali/packed-switch/PackedSwitch.smali
new file mode 100644
index 0000000..30ab926
--- /dev/null
+++ b/src/test/smali/packed-switch/PackedSwitch.smali
@@ -0,0 +1,106 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+# Empty packed switch
+.method public static test1(I)I
+ .registers 1
+ packed-switch v0, :packed_switch_data
+ const/4 v0, 0x1
+ return v0
+
+ :packed_switch_data
+ .packed-switch 0x0
+ .end packed-switch
+.end method
+
+# Empty packed switch after data
+.method public static test2(I)I
+ .registers 1
+
+ goto :packed_switch
+
+ :packed_switch_data
+ .packed-switch 0x0
+ .end packed-switch
+
+ :packed_switch
+ packed-switch v0, :packed_switch_data
+ const/4 v0, 0x2
+ return v0
+.end method
+
+# Packed switch after data
+.method public static test3(I)I
+ .registers 1
+
+ goto :packed_switch
+
+ :case_0
+ const/4 v0, 0x3
+ goto :return
+
+ :packed_switch_data
+ .packed-switch 0x0
+ :case_0
+ :case_1
+ .end packed-switch
+
+ :packed_switch
+ packed-switch v0, :packed_switch_data
+ const/4 v0, 0x5
+ goto :return
+
+ :case_1
+ const/4 v0, 0x4
+
+ :return
+ return v0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 2
+
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ const/4 v1, 0x0
+ invoke-static {v1}, LTest;->test1(I)I
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ const/4 v1, 0x0
+ invoke-static {v1}, LTest;->test2(I)I
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ const/4 v1, 0x0
+ invoke-static {v1}, LTest;->test3(I)I
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ const/4 v1, 0x1
+ invoke-static {v1}, LTest;->test3(I)I
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ const/4 v1, 0x2
+ invoke-static {v1}, LTest;->test3(I)I
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ return-void
+.end method
diff --git a/src/test/smali/phi-removal-regression/Test.java b/src/test/smali/phi-removal-regression/Test.java
new file mode 100644
index 0000000..7accd02
--- /dev/null
+++ b/src/test/smali/phi-removal-regression/Test.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+ boolean returnBoolean() {
+ System.out.println("returnBoolean");
+ return true;
+ }
+
+ boolean returnTheOtherBoolean(Test a) {
+ System.out.println("returnTheOtherBoolean");
+ return a.returnBoolean();
+ }
+
+ public static void main(String[] args) {
+ new TestObject().a(new Test(), 42);
+ }
+}
diff --git a/src/test/smali/phi-removal-regression/TestObject.java b/src/test/smali/phi-removal-regression/TestObject.java
new file mode 100644
index 0000000..8a93611
--- /dev/null
+++ b/src/test/smali/phi-removal-regression/TestObject.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+ public boolean a(Test a, int i) {
+ return true;
+ }
+}
diff --git a/src/test/smali/phi-removal-regression/TestObject.smali b/src/test/smali/phi-removal-regression/TestObject.smali
new file mode 100644
index 0000000..2b5f9e0
--- /dev/null
+++ b/src/test/smali/phi-removal-regression/TestObject.smali
@@ -0,0 +1,58 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field final list:Ljava/util/ArrayList;
+.field private final s:Z
+
+.method public final a(LTest;I)Z
+ .registers 9
+ .prologue
+ const/4 v2, 0x1
+ const/4 v1, 0x0
+ invoke-virtual {p1}, LTest;->returnBoolean()Z
+ move-result v0
+ if-eqz v0, :cond_a
+ move v0, v1
+ :goto_9
+ return v0
+ :cond_a
+ and-int/lit8 v0, p2, 0x2
+ if-nez v0, :cond_14
+ iget-boolean v0, p0, LTestObject;->b:Z
+ if-eqz v0, :cond_14
+ move v0, v2
+ goto :goto_9
+ :cond_14
+ iget-object v0, p0, LTestObject;->list:Ljava/util/ArrayList;
+ invoke-virtual {v0}, Ljava/util/ArrayList;->size()I
+ move-result v4
+ move v3, v1
+ :goto_1b
+ if-ge v3, v4, :cond_3b
+ iget-object v0, p0, LTestObject;->list:Ljava/util/ArrayList;
+ invoke-virtual {v0, v3}, Ljava/util/ArrayList;->get(I)Ljava/lang/Object;
+ move-result-object v0
+ check-cast v0, LTest;
+ and-int/lit8 v5, p2, 0x1
+ if-eqz v5, :cond_2f
+ invoke-virtual {v0}, LTest;->returnBoolean()Z
+ move-result v5
+ if-nez v5, :cond_37
+ :cond_2f
+ invoke-virtual {v0, p1}, LTest;->returnTheOtherBoolean(LTest;)Z
+ move-result v0
+ if-eqz v0, :cond_37
+ move v0, v2
+ goto :goto_9
+ :cond_37
+ add-int/lit8 v0, v3, 0x1
+ move v3, v0
+ goto :goto_1b
+ :cond_3b
+ move v0, v1
+ goto :goto_9
+.end method
diff --git a/src/test/smali/regression/33336471/33336471.smali b/src/test/smali/regression/33336471/33336471.smali
new file mode 100644
index 0000000..87e9cf6
--- /dev/null
+++ b/src/test/smali/regression/33336471/33336471.smali
@@ -0,0 +1,204 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+# This method is not called by the test, and is only for reference of dex code failing on
+# art from Android 5.1.1 with mixed int/float constants.
+# When run dex2oat prints the warning:
+#
+# dex2oat W 7568 7571 art/compiler/dex/vreg_analysis.cc:367]
+# void Test.intAndFloatZeroConstantsNotWorking() op at block 6 has both fp and core/ref uses for
+# same def.
+.method static intAndFloatZeroConstantsNotWorking()V
+ .locals 8
+
+ const-string v6, "START"
+ sget-object v7, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v7, v6}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ const/high16 v0, 0x3f800000 # 1.0
+ const/4 v1, 0x00 # 0 / 0.0
+ const/4 v3, 2
+ move v4, v1
+
+ :label_a
+ invoke-virtual {v7, v4}, Ljava/io/PrintStream;->println(I)V
+ invoke-virtual {v7, v3}, Ljava/io/PrintStream;->println(I)V
+ if-ge v4, v3, :label_b
+ const-string v6, "LOOP"
+ invoke-virtual {v7, v6}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ add-int/lit8 v4, v4, 0x01 # 1
+ goto :label_a
+
+ :label_b
+ const/4 v5, 0x01
+ new-array v5, v5, [F
+ const/4 v3, 0x00
+ aget v4, v5, v3
+ cmpl-float v1, v4, v1
+ if-nez v1, :label_c
+ cmpl-float v0, v4, v0
+ if-eqz v0, :label_c
+ const-string v0, "DONE"
+ invoke-virtual {v7, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ goto :label_d
+
+ :label_c
+ const-string v0, "FLOAT COMPARISON FAILED"
+ invoke-virtual {v7, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ :label_d
+ return-void
+.end method
+
+.method static intAndFloatZeroConstants()V
+ .locals 8
+
+ const-string v6, "START"
+ sget-object v7, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v7, v6}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ const/high16 v0, 0x3f800000 # 1.0
+ const/4 v1, 0x00 # 0
+ const/4 v2, 0x00 # 0.0
+ const/4 v3, 2
+ move v4, v1
+
+ :label_a
+ invoke-virtual {v7, v4}, Ljava/io/PrintStream;->println(I)V
+ invoke-virtual {v7, v3}, Ljava/io/PrintStream;->println(I)V
+ if-ge v4, v3, :label_b
+ const-string v6, "LOOP"
+ invoke-virtual {v7, v6}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ add-int/lit8 v4, v4, 0x01 # 1
+ goto :label_a
+
+ :label_b
+ const/4 v5, 0x01
+ new-array v5, v5, [F
+ const/4 v3, 0x00
+ aget v4, v5, v3
+ cmpl-float v1, v4, v2
+ if-nez v1, :label_c
+ cmpl-float v0, v4, v0
+ if-eqz v0, :label_c
+ const-string v0, "DONE"
+ invoke-virtual {v7, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ goto :label_d
+
+ :label_c
+ const-string v0, "FLOAT COMPARISON FAILED"
+ invoke-virtual {v7, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ :label_d
+ return-void
+.end method
+
+# This method is not called by the test, and is only for reference of dex code failing on
+# art from Android 5.1.1 with mixed long/double constants.
+# This code does actually work, but dex2oat still prints the warning:
+#
+# dex2oat W 7568 7571 art/compiler/dex/vreg_analysis.cc:367]
+# void Test.longAndDoubleZeroConstantsNotWorking() op at block 6 has both fp and core/ref uses
+#for same def.
+.method static longAndDoubleZeroConstantsNotWorking()V
+ .locals 14
+
+ const-string v12, "START"
+ sget-object v13, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v13, v12}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ const-wide/high16 v0, 0x3f80000000000000L # 1.0 0->0 1->2 2->4 3->6 4->8 5->10+11 6->12 7->13
+ const-wide/16 v2, 0x00L # 0 / 0.0
+ const-wide/16 v6, 2
+ move-wide v8, v2
+
+ :label_a
+ invoke-virtual {v13, v8, v9}, Ljava/io/PrintStream;->println(J)V
+ invoke-virtual {v13, v6, v7}, Ljava/io/PrintStream;->println(J)V
+ cmp-long v12, v8, v6
+ if-gez v12, :label_b
+ const-string v12, "LOOP"
+ invoke-virtual {v13, v12}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ const-wide/16 v10, 0x01
+ add-long v8, v8, v10
+ goto :label_a
+
+ :label_b
+ const/4 v10, 0x01
+ new-array v10, v10, [D
+ const/4 v6, 0x00
+ aget-wide v8, v10, v6
+ cmpl-double v2, v8, v2
+ if-nez v2, :label_c
+ cmpl-double v0, v8, v0
+ if-eqz v0, :label_c
+ const-string v0, "DONE"
+ invoke-virtual {v13, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ goto :label_d
+
+ :label_c
+ const-string v0, "FLOAT COMPARISON FAILED"
+ invoke-virtual {v13, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ :label_d
+ return-void
+.end method
+
+.method static longAndDoubleZeroConstants()V
+ .locals 14
+
+ const-string v12, "START"
+ sget-object v13, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v13, v12}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ const-wide/high16 v0, 0x3f80000000000000L
+ const-wide/16 v2, 0x00L # 0
+ const-wide/16 v4, 0x00L # 0.0
+ const-wide/16 v6, 2
+ move-wide v8, v2
+
+ :label_a
+ invoke-virtual {v13, v8, v9}, Ljava/io/PrintStream;->println(J)V
+ invoke-virtual {v13, v6, v7}, Ljava/io/PrintStream;->println(J)V
+ cmp-long v12, v8, v6
+ if-gez v12, :label_b
+ const-string v12, "LOOP"
+ invoke-virtual {v13, v12}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ const-wide/16 v10, 0x01
+ add-long v8, v8, v10
+ goto :label_a
+
+ :label_b
+ const/4 v10, 0x01
+ new-array v10, v10, [D
+ const/4 v6, 0x00
+ aget-wide v8, v10, v6
+ cmpl-double v2, v8, v4
+ if-nez v2, :label_c
+ cmpl-double v0, v8, v0
+ if-eqz v0, :label_c
+ const-string v0, "DONE"
+ invoke-virtual {v13, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ goto :label_d
+
+ :label_c
+ const-string v0, "DOUBLE COMPARISON FAILED"
+ invoke-virtual {v13, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ :label_d
+ return-void
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 0
+
+ invoke-static {}, LTest;->intAndFloatZeroConstants()V
+ invoke-static {}, LTest;->longAndDoubleZeroConstants()V
+
+ return-void
+.end method
diff --git a/src/test/smali/regression/33846227/33846227.smali b/src/test/smali/regression/33846227/33846227.smali
new file mode 100644
index 0000000..c8553a7
--- /dev/null
+++ b/src/test/smali/regression/33846227/33846227.smali
@@ -0,0 +1,36 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Before Lollipop (Art 5.1.1) Art/Dalvik failed on verification if an empty sparse switch payload
+# was the last instruction in a method. This was originally reported as 19827056, and fixed in
+# https://android.googlesource.com/platform/art/+/9ccd151d0d27a729f88af9d00285afe4d147981a
+
+# This test is copied from
+# https://android.googlesource.com/platform/art/+/9ccd151d0d27a729f88af9d00285afe4d147981a
+.class public LTest;
+.super Ljava/lang/Object;
+
+.method public static run()V
+ .registers 2
+
+ :start
+ const v0, 0
+
+ sparse-switch v0, :SparseSwitch
+
+ return-void
+
+ :SparseSwitch
+ .sparse-switch
+ .end sparse-switch
+
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 0
+
+ invoke-static {}, LTest;->run()V
+
+ return-void
+.end method
diff --git a/src/test/smali/self-is-catch-block/SelfIsCatchBlock.smali b/src/test/smali/self-is-catch-block/SelfIsCatchBlock.smali
new file mode 100644
index 0000000..04f0cf1
--- /dev/null
+++ b/src/test/smali/self-is-catch-block/SelfIsCatchBlock.smali
@@ -0,0 +1,43 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method private static throwOnPositive(I)V
+ .registers 2
+ if-lez v1, :cond_nothrow
+ new-instance v0, Ljava/lang/RuntimeException;
+ invoke-direct {v0}, Ljava/lang/RuntimeException;-><init>()V
+ throw v0
+ :cond_nothrow
+ return-void
+.end method
+
+# Tests the flow of values in the pathological case that the block is its own catch handler.
+# This tests that the register allocator does not insert moves at the end of the throwing
+# block since in the case of a throw the block does not actually complete.
+.method static loopWhileThrow(I)I
+ .registers 4
+ :catchall_0
+ move v0, p0
+ add-int/lit8 p0, p0, -0x1
+ :try_start_6
+ invoke-static {v0}, LTest;->throwOnPositive(I)V
+ :try_end_9
+ .catchall {:try_start_6 .. :try_end_9} :catchall_0
+ return p0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .registers 2
+ const v0, 0x64
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V
+ invoke-static {v0}, LTest;->loopWhileThrow(I)I
+ move-result v0
+ invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V
+ return-void
+.end method
diff --git a/src/test/smali/sparse-switch/SparseSwitch.smali b/src/test/smali/sparse-switch/SparseSwitch.smali
new file mode 100644
index 0000000..908220e
--- /dev/null
+++ b/src/test/smali/sparse-switch/SparseSwitch.smali
@@ -0,0 +1,106 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+# Empty sparse switch
+.method public static test1(I)I
+ .registers 1
+ sparse-switch v0, :sparse_switch_data
+ const/4 v0, 0x1
+ return v0
+
+ :sparse_switch_data
+ .sparse-switch
+ .end sparse-switch
+.end method
+
+# Empty sparse switch after data
+.method public static test2(I)I
+ .registers 1
+
+ goto :sparse_switch
+
+ :sparse_switch_data
+ .sparse-switch
+ .end sparse-switch
+
+ :sparse_switch
+ sparse-switch v0, :sparse_switch_data
+ const/4 v0, 0x2
+ return v0
+.end method
+
+# Sparse switch after data
+.method public static test3(I)I
+ .registers 1
+
+ goto :sparse_switch
+
+ :case_2
+ const/4 v0, 0x3
+ goto :return
+
+ :sparse_switch_data
+ .sparse-switch
+ 0x2 -> :case_2
+ 0x4 -> :case_4
+ .end sparse-switch
+
+ :sparse_switch
+ sparse-switch v0, :sparse_switch_data
+ const/4 v0, 0x5
+ goto :return
+
+ :case_4
+ const/4 v0, 0x4
+
+ :return
+ return v0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 2
+
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ const/4 v1, 0x0
+ invoke-static {v1}, LTest;->test1(I)I
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ const/4 v1, 0x0
+ invoke-static {v1}, LTest;->test2(I)I
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ const/4 v1, 0x2
+ invoke-static {v1}, LTest;->test3(I)I
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ const/4 v1, 0x4
+ invoke-static {v1}, LTest;->test3(I)I
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ const/4 v1, 0x6
+ invoke-static {v1}, LTest;->test3(I)I
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ return-void
+.end method
diff --git a/src/test/smali/try-catch/TryCatch.smali b/src/test/smali/try-catch/TryCatch.smali
new file mode 100644
index 0000000..2becd5f
--- /dev/null
+++ b/src/test/smali/try-catch/TryCatch.smali
@@ -0,0 +1,73 @@
+# 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.
+
+.class LTest;
+.super Ljava/lang/Object;
+
+# Fall through to catch block.
+.method public test1()V
+ .registers 1
+ :try_start
+ monitor-enter p0
+ monitor-exit p0
+ :try_end
+ return-void
+ .catchall {:try_start .. :try_end} :try_end
+.end method
+
+.method public static test2()I
+ .locals 1
+ const v0, 0
+ :try_start
+ const v0, 1
+ goto :return
+ :try_end
+ .catch Ljava/lang/Exception; {:try_start .. :try_end} :return
+ .catch Ljava/lang/Throwable; {:try_start .. :try_end} :error
+ :error
+ move-exception v0
+ const v0, 2
+ :return
+ return v0
+.end method
+
+# Dead catch block.
+.method public test3()I
+ .locals 1
+ const v0, 0
+ return v0
+ :start
+ nop
+ :end
+ .catchall {:start .. :end} :catch
+ nop
+ :catch
+ nop
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 0
+ return-void
+.end method
+
+.method public test4(I)V
+ .locals 1
+ const/4 v0, 0
+ if-nez p0, :not_zero
+ const/4 v0, 1
+ goto :try_end
+ :not_zero
+ const/4 v0, 2
+ :try_start
+ invoke-static {}, Ltest/X;->f()V
+ const/4 v0, 3
+ :try_end
+ return-void
+ .catchall {:try_start .. :try_end} :try_end
+.end method
+
+.method public f()V
+ .locals 0
+ return-void
+.end method
diff --git a/src/test/smali/type-confusion-regression/Test.java b/src/test/smali/type-confusion-regression/Test.java
new file mode 100644
index 0000000..ef41f6b
--- /dev/null
+++ b/src/test/smali/type-confusion-regression/Test.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Test extends Throwable {
+ public long[] al = { 1, 2, 3 };
+ public int[] ai = null;
+ public boolean b = false;
+ public List a = new ArrayList();
+ public long l = 1 << 53;
+ public int i = 32;
+ public double d = 0.123;
+ public Test h = null;
+
+ public Test(int i) {
+ throw new RuntimeException("Test(i)");
+ }
+
+ public Test(int i, int j) {
+ throw new RuntimeException("Test.<init>(II)");
+ }
+
+ public Test(String s) {
+ throw new RuntimeException("Test.<init>(Ljava/lang/String;)");
+ }
+
+ public Test() {
+ throw new RuntimeException("Test.<init>()");
+ }
+
+ public static int a(Test t) {
+ throw new RuntimeException("Test.a(Test)");
+ }
+
+ public Test e() {
+ throw new RuntimeException("Test.e()");
+ }
+
+ public static void main(String[] args) {
+ try {
+ TestObject.a(new Test(), new Test(), new Test(), new Test());
+ } catch (RuntimeException e) {
+ System.out.println(e);
+ }
+ }
+}
diff --git a/src/test/smali/type-confusion-regression/TestObject.java b/src/test/smali/type-confusion-regression/TestObject.java
new file mode 100644
index 0000000..ee66c35
--- /dev/null
+++ b/src/test/smali/type-confusion-regression/TestObject.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+ public static void a(Test a, Test b, Test c, Test d) {
+ }
+}
diff --git a/src/test/smali/type-confusion-regression/TestObject.smali b/src/test/smali/type-confusion-regression/TestObject.smali
new file mode 100644
index 0000000..832cab9
--- /dev/null
+++ b/src/test/smali/type-confusion-regression/TestObject.smali
@@ -0,0 +1,385 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.method public static a(LTest;LTest;LTest;LTest;)V
+ .registers 40
+ .prologue
+ invoke-virtual/range {p2 .. p2}, LTest;->e()LTest;
+ move-result-object v2
+ iget-wide v0, v2, LTest;->l:J
+ move-wide/from16 v22, v0
+ invoke-virtual/range {p2 .. p2}, LTest;->e()LTest;
+ move-result-object v2
+ iget-wide v2, v2, LTest;->l:J
+ const-wide/16 v4, 0x3e8
+ mul-long/2addr v2, v4
+ const-wide/16 v4, 0x3e8
+ mul-long/2addr v2, v4
+ div-long v2, v2, v22
+ move-object/from16 v0, p0
+ iput-wide v2, v0, LTest;->l:J
+ invoke-virtual/range {p1 .. p1}, LTest;->e()LTest;
+ move-result-object v2
+ iget-wide v4, v2, LTest;->d:D
+ double-to-int v3, v4
+ move-object/from16 v0, p0
+ iput v3, v0, LTest;->i:I
+ iget-wide v4, v2, LTest;->d:D
+ double-to-int v3, v4
+ move-object/from16 v0, p0
+ iput v3, v0, LTest;->i:I
+ iget-object v2, v2, LTest;->h:LTest;
+ invoke-static {v2}, LTest;->a(LTest;)I
+ move-result v2
+ move-object/from16 v0, p0
+ iput v2, v0, LTest;->i:I
+ const/4 v3, 0x0
+ invoke-virtual/range {p3 .. p3}, LTest;->e()LTest;
+ move-result-object v2
+ if-eqz v2, :cond_59
+ invoke-virtual/range {p3 .. p3}, LTest;->e()LTest;
+ move-result-object v2
+ iget-object v2, v2, LTest;->a:Ljava/util/List;
+ invoke-interface {v2}, Ljava/util/List;->iterator()Ljava/util/Iterator;
+ move-result-object v4
+ :cond_47
+ invoke-interface {v4}, Ljava/util/Iterator;->hasNext()Z
+ move-result v2
+ if-eqz v2, :cond_59
+ invoke-interface {v4}, Ljava/util/Iterator;->next()Ljava/lang/Object;
+ move-result-object v2
+ check-cast v2, LTest;
+ iget v2, v2, LTest;->i:I
+ if-eqz v2, :cond_47
+ const/4 v2, 0x1
+ move v3, v2
+ :cond_59
+ move-object/from16 v0, p0
+ iput-boolean v3, v0, LTest;->b:Z
+ invoke-virtual/range {p3 .. p3}, LTest;->e()LTest;
+ move-result-object v2
+ iget-object v0, v2, LTest;->a:Ljava/util/List;
+ move-object/from16 v17, v0
+ const/4 v2, 0x0
+ invoke-interface/range {v17 .. v17}, Ljava/util/List;->iterator()Ljava/util/Iterator;
+ move-result-object v5
+ move v4, v2
+ :goto_6b
+ invoke-interface {v5}, Ljava/util/Iterator;->hasNext()Z
+ move-result v2
+ if-eqz v2, :cond_8c
+ invoke-interface {v5}, Ljava/util/Iterator;->next()Ljava/lang/Object;
+ move-result-object v2
+ check-cast v2, LTest;
+ iget-wide v6, v2, LTest;->l:J
+ const-wide/16 v8, 0x0
+ cmp-long v2, v6, v8
+ if-gez v2, :cond_87
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_87
+ int-to-long v8, v4
+ add-long/2addr v6, v8
+ long-to-int v2, v6
+ move v4, v2
+ goto :goto_6b
+ :cond_8c
+ if-gtz v4, :cond_96
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_96
+ invoke-virtual/range {p3 .. p3}, LTest;->e()LTest;
+ move-result-object v5
+ const/4 v2, 0x0
+ if-eqz v5, :cond_260
+ iget-object v2, v5, LTest;->al:[J
+ if-eqz v2, :cond_a4
+ array-length v5, v2
+ if-nez v5, :cond_ac
+ :cond_a4
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_ac
+ array-length v5, v2
+ add-int/lit8 v5, v5, -0x1
+ aget-wide v6, v2, v5
+ int-to-long v8, v4
+ cmp-long v5, v6, v8
+ if-lez v5, :cond_be
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_be
+ move-object v5, v2
+ :goto_bf
+ const/4 v2, 0x0
+ const/4 v6, 0x0
+ invoke-virtual/range {p3 .. p3}, LTest;->e()LTest;
+ move-result-object v7
+ if-eqz v7, :cond_f2
+ iget-object v7, v7, LTest;->a:Ljava/util/List;
+ invoke-interface {v7}, Ljava/util/List;->iterator()Ljava/util/Iterator;
+ move-result-object v8
+ move v6, v2
+ :goto_ce
+ invoke-interface {v8}, Ljava/util/Iterator;->hasNext()Z
+ move-result v2
+ if-eqz v2, :cond_f0
+ invoke-interface {v8}, Ljava/util/Iterator;->next()Ljava/lang/Object;
+ move-result-object v2
+ check-cast v2, LTest;
+ iget v2, v2, LTest;->i:I
+ int-to-long v10, v2
+ const-wide/16 v12, 0x0
+ cmp-long v2, v10, v12
+ if-gez v2, :cond_eb
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_eb
+ int-to-long v12, v6
+ add-long/2addr v10, v12
+ long-to-int v2, v10
+ move v6, v2
+ goto :goto_ce
+ :cond_f0
+ move v2, v6
+ move-object v6, v7
+ :cond_f2
+ if-eqz v2, :cond_fe
+ if-eq v2, v4, :cond_fe
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_fe
+ if-eqz v5, :cond_149
+ new-instance v2, LTest;
+ array-length v7, v5
+ invoke-direct {v2, v4, v7}, LTest;-><init>(II)V
+ move-object v4, v2
+ :goto_107
+ iget-object v0, v4, LTest;->al:[J
+ move-object/from16 v24, v0
+ iget-object v0, v4, LTest;->ai:[I
+ move-object/from16 v25, v0
+ const/16 v16, 0x0
+ const/4 v7, -0x1
+ const-wide/16 v14, 0x0
+ if-eqz v6, :cond_150
+ invoke-interface {v6}, Ljava/util/List;->size()I
+ move-result v2
+ if-lez v2, :cond_150
+ invoke-interface {v6}, Ljava/util/List;->iterator()Ljava/util/Iterator;
+ move-result-object v2
+ move-object v6, v2
+ :goto_121
+ const-wide/16 v12, 0x0
+ const-wide/16 v10, 0x0
+ const-wide/16 v8, 0x0
+ invoke-interface/range {v17 .. v17}, Ljava/util/List;->iterator()Ljava/util/Iterator;
+ move-result-object v26
+ :cond_12b
+ invoke-interface/range {v26 .. v26}, Ljava/util/Iterator;->hasNext()Z
+ move-result v2
+ if-eqz v2, :cond_22d
+ invoke-interface/range {v26 .. v26}, Ljava/util/Iterator;->next()Ljava/lang/Object;
+ move-result-object v2
+ check-cast v2, LTest;
+ iget-wide v0, v2, LTest;->l:J
+ move-wide/from16 v28, v0
+ const-wide/16 v18, 0x0
+ cmp-long v17, v28, v18
+ if-gez v17, :cond_153
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_149
+ new-instance v2, LTest;
+ invoke-direct {v2, v4}, LTest;-><init>(I)V
+ move-object v4, v2
+ goto :goto_107
+ :cond_150
+ const/4 v2, 0x0
+ move-object v6, v2
+ goto :goto_121
+ :cond_153
+ iget-wide v0, v2, LTest;->l:J
+ move-wide/from16 v18, v0
+ move-wide/from16 v20, v18
+ :goto_159
+ const-wide/16 v18, 0x0
+ cmp-long v2, v20, v18
+ if-lez v2, :cond_12b
+ if-eqz v6, :cond_192
+ move-wide/from16 v18, v12
+ :goto_163
+ const-wide/16 v12, 0x0
+ cmp-long v2, v18, v12
+ if-gtz v2, :cond_178
+ invoke-interface {v6}, Ljava/util/Iterator;->next()Ljava/lang/Object;
+ move-result-object v2
+ check-cast v2, LTest;
+ iget v10, v2, LTest;->i:I
+ int-to-long v0, v10
+ move-wide/from16 v18, v0
+ iget v2, v2, LTest;->i:I
+ int-to-long v10, v2
+ goto :goto_163
+ :cond_178
+ if-nez v16, :cond_17b
+ move-wide v8, v10
+ :cond_17b
+ add-long v12, v14, v10
+ sub-long/2addr v12, v8
+ move-wide/from16 v34, v12
+ move-wide v12, v10
+ move-wide v10, v8
+ move-wide/from16 v8, v34
+ :goto_184
+ const-wide/16 v30, 0x0
+ cmp-long v2, v8, v30
+ if-gez v2, :cond_198
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_192
+ move-wide/from16 v18, v12
+ move-wide v12, v10
+ move-wide v10, v8
+ move-wide v8, v14
+ goto :goto_184
+ :cond_198
+ const-wide/16 v30, 0x3e8
+ mul-long v8, v8, v30
+ const-wide/16 v30, 0x3e8
+ mul-long v8, v8, v30
+ div-long v8, v8, v22
+ move/from16 v2, v16
+ :goto_1a4
+ if-lez v2, :cond_1cb
+ add-int/lit8 v17, v2, -0x1
+ aget-wide v30, v24, v17
+ cmp-long v17, v30, v8
+ if-lez v17, :cond_1cb
+ add-int/lit8 v17, v2, -0x1
+ aget-wide v30, v24, v17
+ aput-wide v30, v24, v2
+ if-eqz v25, :cond_1c8
+ if-ltz v7, :cond_1c8
+ add-int/lit8 v17, v2, -0x1
+ aget v27, v25, v7
+ move/from16 v0, v17
+ move/from16 v1, v27
+ if-ne v0, v1, :cond_1c8
+ aget v17, v25, v7
+ add-int/lit8 v17, v17, 0x1
+ aput v17, v25, v7
+ :cond_1c8
+ add-int/lit8 v2, v2, -0x1
+ goto :goto_1a4
+ :cond_1cb
+ aput-wide v8, v24, v2
+ if-lez v2, :cond_1ea
+ add-int/lit8 v17, v2, -0x1
+ aget-wide v30, v24, v17
+ cmp-long v8, v30, v8
+ if-nez v8, :cond_1ea
+ const/4 v3, 0x1
+ if-ne v2, v3, :cond_1e2
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_1e2
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_1ea
+ if-eqz v5, :cond_216
+ add-int/lit8 v8, v7, 0x1
+ array-length v9, v5
+ if-ge v8, v9, :cond_216
+ move/from16 v0, v16
+ int-to-long v8, v0
+ add-int/lit8 v17, v7, 0x1
+ aget-wide v30, v5, v17
+ const-wide/16 v32, 0x1
+ sub-long v30, v30, v32
+ cmp-long v8, v8, v30
+ if-nez v8, :cond_216
+ add-int/lit8 v7, v7, 0x1
+ aput v2, v25, v7
+ if-lez v7, :cond_216
+ add-int/lit8 v2, v7, -0x1
+ aget v2, v25, v2
+ aget v8, v25, v7
+ if-lt v2, v8, :cond_216
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_216
+ add-int/lit8 v2, v16, 0x1
+ add-long v16, v14, v28
+ const-wide/16 v8, 0x1
+ sub-long v14, v18, v8
+ const-wide/16 v8, 0x1
+ sub-long v8, v20, v8
+ move-wide/from16 v20, v8
+ move-wide v8, v10
+ move-wide v10, v12
+ move-wide v12, v14
+ move-wide/from16 v14, v16
+ move/from16 v16, v2
+ goto/16 :goto_159
+ :cond_22d
+ iget-object v2, v4, LTest;->al:[J
+ move-object/from16 v0, p0
+ iput-object v2, v0, LTest;->al:[J
+ iget-object v2, v4, LTest;->ai:[I
+ if-eqz v3, :cond_241
+ if-nez v2, :cond_241
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_241
+ if-eqz v2, :cond_25b
+ array-length v3, v2
+ if-gtz v3, :cond_24e
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_24e
+ const/4 v3, 0x0
+ aget v3, v2, v3
+ if-eqz v3, :cond_25b
+ new-instance v2, LTest;
+ const-string v3, "string"
+ invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+ throw v2
+ :cond_25b
+ move-object/from16 v0, p0
+ iput-object v2, v0, LTest;->ai:[I
+ return-void
+ :cond_260
+ move-object v5, v2
+ goto/16 :goto_bf
+.end method
\ No newline at end of file
diff --git a/src/test/smali/type-confusion-regression2/Test.java b/src/test/smali/type-confusion-regression2/Test.java
new file mode 100644
index 0000000..4caa81b
--- /dev/null
+++ b/src/test/smali/type-confusion-regression2/Test.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+ public static void main(String[] args) {
+ try {
+ new TestObject().b();
+ } catch (Exception e) {
+ System.out.println(e);
+ }
+ }
+}
diff --git a/src/test/smali/type-confusion-regression2/TestObject.java b/src/test/smali/type-confusion-regression2/TestObject.java
new file mode 100644
index 0000000..677b429
--- /dev/null
+++ b/src/test/smali/type-confusion-regression2/TestObject.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+ public void b() {
+ }
+}
diff --git a/src/test/smali/type-confusion-regression2/TestObject.smali b/src/test/smali/type-confusion-regression2/TestObject.smali
new file mode 100644
index 0000000..8bac9e0
--- /dev/null
+++ b/src/test/smali/type-confusion-regression2/TestObject.smali
@@ -0,0 +1,187 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field private static final l:[I
+
+.field public a:I
+.field public b:I
+.field public c:[I
+.field public d:[D
+.field public e:I
+.field public f:I
+.field public g:[D
+.field public h:[D
+.field public i:[D
+.field public j:I
+.field private k:[D
+
+.method public final b()V
+ .registers 29
+ move-object/from16 v0, p0
+ iget v2, v0, LTestObject;->a:I
+ const/4 v3, 0x1
+ if-ne v2, v3, :cond_8
+ :cond_7
+ return-void
+ :cond_8
+ move-object/from16 v0, p0
+ iget v2, v0, LTestObject;->a:I
+ mul-int/lit8 v12, v2, 0x2
+ const/4 v5, 0x0
+ move-object/from16 v0, p0
+ iget v2, v0, LTestObject;->a:I
+ const/4 v3, 0x0
+ const/4 v4, 0x0
+ :goto_15
+ add-int/lit8 v6, v4, 0x1
+ const/4 v4, 0x4
+ if-gt v6, v4, :cond_55
+ sget-object v4, LTestObject;->l:[I
+ add-int/lit8 v5, v6, -0x1
+ aget v4, v4, v5
+ move v5, v4
+ move v4, v3
+ :goto_22
+ div-int v3, v2, v5
+ mul-int v7, v5, v3
+ sub-int v7, v2, v7
+ if-nez v7, :cond_f9
+ add-int/lit8 v2, v4, 0x1
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->g:[D
+ add-int/lit8 v7, v2, 0x1
+ add-int/2addr v7, v12
+ int-to-double v8, v5
+ aput-wide v8, v4, v7
+ const/4 v4, 0x2
+ if-ne v5, v4, :cond_64
+ const/4 v4, 0x1
+ if-eq v2, v4, :cond_64
+ const/4 v4, 0x2
+ :goto_3d
+ if-gt v4, v2, :cond_5a
+ sub-int v7, v2, v4
+ add-int/lit8 v7, v7, 0x2
+ add-int/2addr v7, v12
+ move-object/from16 v0, p0
+ iget-object v8, v0, LTestObject;->g:[D
+ add-int/lit8 v9, v7, 0x1
+ move-object/from16 v0, p0
+ iget-object v10, v0, LTestObject;->g:[D
+ aget-wide v10, v10, v7
+ aput-wide v10, v8, v9
+ add-int/lit8 v4, v4, 0x1
+ goto :goto_3d
+ :cond_55
+ add-int/lit8 v4, v5, 0x2
+ move v5, v4
+ move v4, v3
+ goto :goto_22
+ :cond_5a
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->g:[D
+ add-int/lit8 v7, v12, 0x2
+ const-wide/high16 v8, 0x4000000000000000L
+ aput-wide v8, v4, v7
+ :cond_64
+ const/4 v4, 0x1
+ if-ne v3, v4, :cond_f5
+ move-object/from16 v0, p0
+ iget-object v3, v0, LTestObject;->g:[D
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->a:I
+ int-to-double v4, v4
+ aput-wide v4, v3, v12
+ move-object/from16 v0, p0
+ iget-object v3, v0, LTestObject;->g:[D
+ add-int/lit8 v4, v12, 0x1
+ int-to-double v6, v2
+ aput-wide v6, v3, v4
+ const-wide v4, 0x401921fb54442d18L
+ move-object/from16 v0, p0
+ iget v3, v0, LTestObject;->a:I
+ int-to-double v6, v3
+ div-double v14, v4, v6
+ const/4 v6, 0x0
+ add-int/lit8 v13, v2, -0x1
+ const/4 v2, 0x1
+ if-eqz v13, :cond_7
+ const/4 v3, 0x1
+ move v9, v2
+ move v11, v3
+ :goto_90
+ if-gt v11, v13, :cond_7
+ move-object/from16 v0, p0
+ iget-object v2, v0, LTestObject;->g:[D
+ add-int/lit8 v3, v11, 0x1
+ add-int/2addr v3, v12
+ aget-wide v2, v2, v3
+ double-to-int v3, v2
+ const/4 v2, 0x0
+ mul-int v10, v9, v3
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->a:I
+ div-int v16, v4, v10
+ add-int/lit8 v17, v3, -0x1
+ const/4 v3, 0x1
+ move v8, v3
+ :goto_a9
+ move/from16 v0, v17
+ if-gt v8, v0, :cond_f0
+ add-int v7, v2, v9
+ int-to-double v2, v7
+ mul-double v18, v2, v14
+ const-wide/16 v4, 0x0
+ const/4 v2, 0x3
+ move v3, v6
+ :goto_b6
+ move/from16 v0, v16
+ if-gt v2, v0, :cond_e9
+ add-int/lit8 v3, v3, 0x2
+ const-wide/high16 v20, 0x3ff0000000000000L
+ add-double v4, v4, v20
+ mul-double v20, v4, v18
+ move-object/from16 v0, p0
+ iget v0, v0, LTestObject;->a:I
+ move/from16 v22, v0
+ add-int v22, v22, v3
+ move-object/from16 v0, p0
+ iget-object v0, v0, LTestObject;->g:[D
+ move-object/from16 v23, v0
+ add-int/lit8 v24, v22, -0x2
+ invoke-static/range {v20 .. v21}, Ljava/lang/Math;->cos(D)D
+ move-result-wide v26
+ aput-wide v26, v23, v24
+ move-object/from16 v0, p0
+ iget-object v0, v0, LTestObject;->g:[D
+ move-object/from16 v23, v0
+ add-int/lit8 v22, v22, -0x1
+ invoke-static/range {v20 .. v21}, Ljava/lang/Math;->sin(D)D
+ move-result-wide v20
+ aput-wide v20, v23, v22
+ add-int/lit8 v2, v2, 0x2
+ goto :goto_b6
+ :cond_e9
+ add-int v6, v6, v16
+ add-int/lit8 v2, v8, 0x1
+ move v8, v2
+ move v2, v7
+ goto :goto_a9
+ :cond_f0
+ add-int/lit8 v2, v11, 0x1
+ move v9, v10
+ move v11, v2
+ goto :goto_90
+ :cond_f5
+ move v4, v2
+ move v2, v3
+ goto/16 :goto_22
+ :cond_f9
+ move v3, v4
+ move v4, v6
+ goto/16 :goto_15
+.end method
\ No newline at end of file
diff --git a/src/test/smali/type-confusion-regression3/Test.java b/src/test/smali/type-confusion-regression3/Test.java
new file mode 100644
index 0000000..0f1f0f2
--- /dev/null
+++ b/src/test/smali/type-confusion-regression3/Test.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test extends Throwable {
+ public byte[] a;
+
+ public Test(String s) {
+ throw new RuntimeException("Test(Ljava/lang/String;");
+ }
+
+ public Test(int a, Test b) {
+ throw new RuntimeException("Test(ILTest;)");
+ }
+
+ public Test() {
+ }
+
+ public long c() {
+ throw new RuntimeException("Test.c()");
+ }
+
+ public Object valueAt(int i) {
+ throw new RuntimeException("Test.valueAt(I)");
+ }
+
+ public void b(int i) {
+ throw new RuntimeException("Test.b(I)");
+ }
+
+ public void b(byte[] a, int b, int c) {
+ throw new RuntimeException("Test.b([BII)");
+ }
+
+ public void a() {
+ throw new RuntimeException("Test.a()");
+ }
+
+ public void a(Test a) {
+ throw new RuntimeException("Test.a(LTest;)");
+ }
+
+ public void a(Test a, int i) {
+ throw new RuntimeException("Test.a(LTest;I)");
+ }
+
+ public boolean a(byte[] a, int b, int c, boolean d) {
+ throw new RuntimeException("Test.a");
+ }
+
+ public void a(long a, int b, int c, int d, byte[] e) {
+ throw new RuntimeException("Test.a(JIII[B)");
+ }
+
+ public void c(int i) {
+ throw new RuntimeException("Test.c(I)");
+ }
+
+ public int n() {
+ throw new RuntimeException("Test.n()");
+ }
+
+ public int size() {
+ throw new RuntimeException("Test.size");
+ }
+
+ public static void main(String[] args) {
+ try {
+ new TestObject().a(new Test(), new Test());
+ } catch (Exception e) {
+ System.out.println(e);
+ }
+ }
+}
diff --git a/src/test/smali/type-confusion-regression3/TestObject.java b/src/test/smali/type-confusion-regression3/TestObject.java
new file mode 100644
index 0000000..f20168c
--- /dev/null
+++ b/src/test/smali/type-confusion-regression3/TestObject.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+ public int a(Test a, Test b) {
+ return 0;
+ }
+}
diff --git a/src/test/smali/type-confusion-regression3/TestObject.smali b/src/test/smali/type-confusion-regression3/TestObject.smali
new file mode 100644
index 0000000..ce33419
--- /dev/null
+++ b/src/test/smali/type-confusion-regression3/TestObject.smali
@@ -0,0 +1,918 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field private i:I
+.field private o:LTest;
+
+.method public final a(LTest;LTest;)I
+ .registers 28
+ .prologue
+ :cond_0
+ :goto_0
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->i:I
+ packed-switch v4, :pswitch_data_60a
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->i:I
+ const/4 v5, 0x3
+ if-ne v4, v5, :cond_4ac
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ if-nez v4, :cond_431
+ move-object/from16 v0, p0
+ iget-object v11, v0, LTestObject;->o:LTest;
+ const/4 v5, 0x0
+ const-wide v8, 0x7fffffffffffffffL
+ invoke-virtual {v11}, LTest;->size()I
+ move-result v12
+ const/4 v4, 0x0
+ move v10, v4
+ :goto_24
+ if-ge v10, v12, :cond_3e8
+ invoke-virtual {v11, v10}, LTest;->valueAt(I)Ljava/lang/Object;
+ move-result-object v4
+ check-cast v4, LTest;
+ iget v6, v4, LTest;->e:I
+ iget-object v7, v4, LTest;->a:LTest;
+ iget v7, v7, LTest;->d:I
+ if-eq v6, v7, :cond_5fe
+ iget-object v6, v4, LTest;->a:LTest;
+ iget-wide v6, v6, LTest;->b:J
+ cmp-long v13, v6, v8
+ if-gez v13, :cond_5fe
+ move-wide/from16 v23, v6
+ move-object v6, v4
+ move-wide/from16 v4, v23
+ :goto_41
+ add-int/lit8 v7, v10, 0x1
+ move v10, v7
+ move-wide v8, v4
+ move-object v5, v6
+ goto :goto_24
+ :pswitch_47
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->i:I
+ if-nez v4, :cond_8a
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget-object v4, v4, LTest;->a:[B
+ const/4 v5, 0x0
+ const/16 v6, 0x8
+ const/4 v7, 0x1
+ move-object/from16 v0, p1
+ invoke-virtual {v0, v4, v5, v6, v7}, LTest;->a([BIIZ)Z
+ move-result v4
+ if-nez v4, :cond_64
+ const/4 v4, 0x0
+ :goto_60
+ if-nez v4, :cond_0
+ const/4 v4, -0x1
+ :goto_63
+ return v4
+ :cond_64
+ const/16 v4, 0x8
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ const/4 v5, 0x0
+ invoke-virtual {v4, v5}, LTest;->c(I)V
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ invoke-virtual {v4}, LTest;->h()J
+ move-result-wide v4
+ move-object/from16 v0, p0
+ iput-wide v4, v0, LTestObject;->n:J
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ invoke-virtual {v4}, LTest;->j()I
+ move-result v4
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ :cond_8a
+ move-object/from16 v0, p0
+ iget-wide v4, v0, LTestObject;->n:J
+ const-wide/16 v6, 0x1
+ cmp-long v4, v4, v6
+ if-nez v4, :cond_b9
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget-object v4, v4, LTest;->a:[B
+ const/16 v5, 0x8
+ const/16 v6, 0x8
+ move-object/from16 v0, p1
+ invoke-virtual {v0, v4, v5, v6}, LTest;->b([BII)V
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->i:I
+ add-int/lit8 v4, v4, 0x8
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ invoke-virtual {v4}, LTest;->p()J
+ move-result-wide v4
+ move-object/from16 v0, p0
+ iput-wide v4, v0, LTestObject;->n:J
+ :cond_b9
+ invoke-virtual/range {p1 .. p1}, LTest;->c()J
+ move-result-wide v4
+ move-object/from16 v0, p0
+ iget v6, v0, LTestObject;->i:I
+ int-to-long v6, v6
+ sub-long v6, v4, v6
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->i:I
+ sget v5, LTest;->J:I
+ if-ne v4, v5, :cond_ec
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ invoke-virtual {v4}, LTest;->size()I
+ move-result v8
+ const/4 v4, 0x0
+ move v5, v4
+ :goto_d6
+ if-ge v5, v8, :cond_ec
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ invoke-virtual {v4, v5}, LTest;->valueAt(I)Ljava/lang/Object;
+ move-result-object v4
+ check-cast v4, LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ iput-wide v6, v4, LTest;->c:J
+ iput-wide v6, v4, LTest;->b:J
+ add-int/lit8 v4, v5, 0x1
+ move v5, v4
+ goto :goto_d6
+ :cond_ec
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->i:I
+ sget v5, LTest;->h:I
+ if-ne v4, v5, :cond_11e
+ const/4 v4, 0x0
+ move-object/from16 v0, p0
+ iput-object v4, v0, LTestObject;->o:LTest;
+ move-object/from16 v0, p0
+ iget-wide v4, v0, LTestObject;->n:J
+ add-long/2addr v4, v6
+ move-object/from16 v0, p0
+ iput-wide v4, v0, LTestObject;->q:J
+ move-object/from16 v0, p0
+ iget-boolean v4, v0, LTestObject;->w:Z
+ if-nez v4, :cond_116
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ sget-object v5, LTest;->f:LTest;
+ invoke-virtual {v4, v5}, LTest;->a(LTest;)V
+ const/4 v4, 0x1
+ move-object/from16 v0, p0
+ iput-boolean v4, v0, LTestObject;->w:Z
+ :cond_116
+ const/4 v4, 0x2
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ :goto_11b
+ const/4 v4, 0x1
+ goto/16 :goto_60
+ :cond_11e
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->i:I
+ sget v5, LTest;->A:I
+ if-eq v4, v5, :cond_146
+ sget v5, LTest;->C:I
+ if-eq v4, v5, :cond_146
+ sget v5, LTest;->D:I
+ if-eq v4, v5, :cond_146
+ sget v5, LTest;->E:I
+ if-eq v4, v5, :cond_146
+ sget v5, LTest;->F:I
+ if-eq v4, v5, :cond_146
+ sget v5, LTest;->J:I
+ if-eq v4, v5, :cond_146
+ sget v5, LTest;->K:I
+ if-eq v4, v5, :cond_146
+ sget v5, LTest;->L:I
+ if-eq v4, v5, :cond_146
+ sget v5, LTest;->O:I
+ if-ne v4, v5, :cond_178
+ :cond_146
+ const/4 v4, 0x1
+ :goto_147
+ if-eqz v4, :cond_17e
+ invoke-virtual/range {p1 .. p1}, LTest;->c()J
+ move-result-wide v4
+ move-object/from16 v0, p0
+ iget-wide v6, v0, LTestObject;->n:J
+ add-long/2addr v4, v6
+ const-wide/16 v6, 0x8
+ sub-long/2addr v4, v6
+ move-object/from16 v0, p0
+ iget-object v6, v0, LTestObject;->k:Ljava/util/Stack;
+ new-instance v7, LTest;
+ move-object/from16 v0, p0
+ iget v8, v0, LTestObject;->i:I
+ invoke-direct {v7, v8, v4, v5}, LTest;-><init>(IJ)V
+ invoke-virtual {v6, v7}, Ljava/util/Stack;->add(Ljava/lang/Object;)Z
+ move-object/from16 v0, p0
+ iget-wide v6, v0, LTestObject;->n:J
+ move-object/from16 v0, p0
+ iget v8, v0, LTestObject;->i:I
+ int-to-long v8, v8
+ cmp-long v6, v6, v8
+ if-nez v6, :cond_17a
+ move-object/from16 v0, p0
+ invoke-direct {v0, v4, v5}, LTestObject;->a(J)V
+ goto :goto_11b
+ :cond_178
+ const/4 v4, 0x0
+ goto :goto_147
+ :cond_17a
+ invoke-direct/range {p0 .. p0}, LTestObject;->a()V
+ goto :goto_11b
+ :cond_17e
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->i:I
+ sget v5, LTest;->R:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->Q:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->B:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->z:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->S:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->v:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->w:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->N:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->x:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->y:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->T:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->ab:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->ac:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->ag:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->ad:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->ae:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->af:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->P:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->M:I
+ if-eq v4, v5, :cond_1d2
+ sget v5, LTest;->aD:I
+ if-ne v4, v5, :cond_1e5
+ :cond_1d2
+ const/4 v4, 0x1
+ :goto_1d3
+ if-eqz v4, :cond_222
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->i:I
+ const/16 v5, 0x8
+ if-eq v4, v5, :cond_1e7
+ new-instance v4, LTest;
+ const-string v5, "a"
+ invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+ throw v4
+ :cond_1e5
+ const/4 v4, 0x0
+ goto :goto_1d3
+ :cond_1e7
+ move-object/from16 v0, p0
+ iget-wide v4, v0, LTestObject;->n:J
+ const-wide/32 v6, 0x7fffffff
+ cmp-long v4, v4, v6
+ if-lez v4, :cond_1fa
+ new-instance v4, LTest;
+ const-string v5, "a"
+ invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+ throw v4
+ :cond_1fa
+ new-instance v4, LTest;
+ move-object/from16 v0, p0
+ iget-wide v6, v0, LTestObject;->n:J
+ long-to-int v5, v6
+ invoke-direct {v4, v5}, LTest;-><init>(I)V
+ move-object/from16 v0, p0
+ iput-object v4, v0, LTestObject;->o:LTest;
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget-object v4, v4, LTest;->a:[B
+ const/4 v5, 0x0
+ move-object/from16 v0, p0
+ iget-object v6, v0, LTestObject;->o:LTest;
+ iget-object v6, v6, LTest;->a:[B
+ const/4 v7, 0x0
+ const/16 v8, 0x8
+ invoke-static {v4, v5, v6, v7, v8}, Ljava/lang/System;->arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V
+ const/4 v4, 0x1
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ goto/16 :goto_11b
+ :cond_222
+ move-object/from16 v0, p0
+ iget-wide v4, v0, LTestObject;->n:J
+ const-wide/32 v6, 0x7fffffff
+ cmp-long v4, v4, v6
+ if-lez v4, :cond_235
+ new-instance v4, LTest;
+ const-string v5, "a"
+ invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+ throw v4
+ :cond_235
+ const/4 v4, 0x0
+ move-object/from16 v0, p0
+ iput-object v4, v0, LTestObject;->o:LTest;
+ const/4 v4, 0x1
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ goto/16 :goto_11b
+ :pswitch_241
+ move-object/from16 v0, p0
+ iget-wide v4, v0, LTestObject;->n:J
+ long-to-int v4, v4
+ move-object/from16 v0, p0
+ iget v5, v0, LTestObject;->i:I
+ sub-int/2addr v4, v5
+ move-object/from16 v0, p0
+ iget-object v5, v0, LTestObject;->o:LTest;
+ if-eqz v5, :cond_368
+ move-object/from16 v0, p0
+ iget-object v5, v0, LTestObject;->o:LTest;
+ iget-object v5, v5, LTest;->a:[B
+ const/16 v6, 0x8
+ move-object/from16 v0, p1
+ invoke-virtual {v0, v5, v6, v4}, LTest;->b([BII)V
+ new-instance v5, LTest;
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->i:I
+ move-object/from16 v0, p0
+ iget-object v6, v0, LTestObject;->o:LTest;
+ invoke-direct {v5, v4, v6}, LTest;-><init>(ILTest;)V
+ invoke-virtual/range {p1 .. p1}, LTest;->c()J
+ move-result-wide v10
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->k:Ljava/util/Stack;
+ invoke-virtual {v4}, Ljava/util/Stack;->isEmpty()Z
+ move-result v4
+ if-nez v4, :cond_291
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->k:Ljava/util/Stack;
+ invoke-virtual {v4}, Ljava/util/Stack;->peek()Ljava/lang/Object;
+ move-result-object v4
+ check-cast v4, LTest;
+ invoke-virtual {v4, v5}, LTest;->a(LTest;)V
+ :cond_286
+ :goto_286
+ invoke-virtual/range {p1 .. p1}, LTest;->c()J
+ move-result-wide v4
+ move-object/from16 v0, p0
+ invoke-direct {v0, v4, v5}, LTestObject;->a(J)V
+ goto/16 :goto_0
+ :cond_291
+ iget v4, v5, LTest;->aL:I
+ sget v6, LTest;->z:I
+ if-ne v4, v6, :cond_359
+ iget-object v0, v5, LTest;->aM:LTest;
+ move-object/from16 v16, v0
+ const/16 v4, 0x8
+ move-object/from16 v0, v16
+ invoke-virtual {v0, v4}, LTest;->c(I)V
+ invoke-virtual/range {v16 .. v16}, LTest;->j()I
+ move-result v4
+ invoke-static {v4}, LTest;->a(I)I
+ move-result v4
+ const/4 v5, 0x4
+ move-object/from16 v0, v16
+ invoke-virtual {v0, v5}, LTest;->d(I)V
+ invoke-virtual/range {v16 .. v16}, LTest;->h()J
+ move-result-wide v8
+ if-nez v4, :cond_304
+ invoke-virtual/range {v16 .. v16}, LTest;->h()J
+ move-result-wide v6
+ invoke-virtual/range {v16 .. v16}, LTest;->h()J
+ move-result-wide v4
+ add-long/2addr v4, v10
+ move-wide v10, v4
+ move-wide v4, v6
+ :goto_2c1
+ const/4 v6, 0x2
+ move-object/from16 v0, v16
+ invoke-virtual {v0, v6}, LTest;->d(I)V
+ invoke-virtual/range {v16 .. v16}, LTest;->e()I
+ move-result v17
+ move/from16 v0, v17
+ new-array v0, v0, [I
+ move-object/from16 v18, v0
+ move/from16 v0, v17
+ new-array v0, v0, [J
+ move-object/from16 v19, v0
+ move/from16 v0, v17
+ new-array v0, v0, [J
+ move-object/from16 v20, v0
+ move/from16 v0, v17
+ new-array v0, v0, [J
+ move-object/from16 v21, v0
+ const-wide/32 v6, 0xf4240
+ invoke-static/range {v4 .. v9}, LTest;->a(JJJ)J
+ move-result-wide v12
+ const/4 v6, 0x0
+ move-wide v14, v10
+ move v10, v6
+ move-wide v6, v4
+ move-wide v4, v12
+ :goto_2ef
+ move/from16 v0, v17
+ if-ge v10, v0, :cond_33e
+ invoke-virtual/range {v16 .. v16}, LTest;->j()I
+ move-result v11
+ const/high16 v12, -0x80000000
+ and-int/2addr v12, v11
+ if-eqz v12, :cond_310
+ new-instance v4, LTest;
+ const-string v5, "a"
+ invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+ throw v4
+ :cond_304
+ invoke-virtual/range {v16 .. v16}, LTest;->p()J
+ move-result-wide v6
+ invoke-virtual/range {v16 .. v16}, LTest;->p()J
+ move-result-wide v4
+ add-long/2addr v4, v10
+ move-wide v10, v4
+ move-wide v4, v6
+ goto :goto_2c1
+ :cond_310
+ invoke-virtual/range {v16 .. v16}, LTest;->h()J
+ move-result-wide v12
+ const v22, 0x7fffffff
+ and-int v11, v11, v22
+ aput v11, v18, v10
+ aput-wide v14, v19, v10
+ aput-wide v4, v21, v10
+ add-long v4, v6, v12
+ const-wide/32 v6, 0xf4240
+ invoke-static/range {v4 .. v9}, LTest;->a(JJJ)J
+ move-result-wide v12
+ aget-wide v6, v21, v10
+ sub-long v6, v12, v6
+ aput-wide v6, v20, v10
+ const/4 v6, 0x4
+ move-object/from16 v0, v16
+ invoke-virtual {v0, v6}, LTest;->d(I)V
+ aget v6, v18, v10
+ int-to-long v6, v6
+ add-long/2addr v14, v6
+ add-int/lit8 v6, v10, 0x1
+ move v10, v6
+ move-wide v6, v4
+ move-wide v4, v12
+ goto :goto_2ef
+ :cond_33e
+ new-instance v4, LTest;
+ move-object/from16 v0, v18
+ move-object/from16 v1, v19
+ move-object/from16 v2, v20
+ move-object/from16 v3, v21
+ invoke-direct {v4, v0, v1, v2, v3}, LTest;-><init>([I[J[J[J)V
+ move-object/from16 v0, p0
+ iget-object v5, v0, LTestObject;->o:LTest;
+ invoke-virtual {v5, v4}, LTest;->a(LTest;)V
+ const/4 v4, 0x1
+ move-object/from16 v0, p0
+ iput-boolean v4, v0, LTestObject;->w:Z
+ goto/16 :goto_286
+ :cond_359
+ iget v4, v5, LTest;->aL:I
+ sget v6, LTest;->aD:I
+ if-ne v4, v6, :cond_286
+ iget-object v4, v5, LTest;->aM:LTest;
+ move-object/from16 v0, p0
+ invoke-virtual {v0, v4}, LTestObject;->a(LTest;)V
+ goto/16 :goto_286
+ :cond_368
+ move-object/from16 v0, p1
+ invoke-virtual {v0, v4}, LTest;->b(I)V
+ goto/16 :goto_286
+ :pswitch_36f
+ const/4 v5, 0x0
+ const-wide v6, 0x7fffffffffffffffL
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ invoke-virtual {v4}, LTest;->size()I
+ move-result v9
+ const/4 v4, 0x0
+ move v8, v4
+ :goto_37f
+ if-ge v8, v9, :cond_3b1
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ invoke-virtual {v4, v8}, LTest;->valueAt(I)Ljava/lang/Object;
+ move-result-object v4
+ check-cast v4, LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ iget-boolean v10, v4, LTest;->m:Z
+ if-eqz v10, :cond_602
+ iget-wide v10, v4, LTest;->c:J
+ cmp-long v10, v10, v6
+ if-gez v10, :cond_602
+ iget-wide v6, v4, LTest;->c:J
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ invoke-virtual {v4, v8}, LTest;->valueAt(I)Ljava/lang/Object;
+ move-result-object v4
+ check-cast v4, LTest;
+ move-wide/from16 v23, v6
+ move-object v6, v4
+ move-wide/from16 v4, v23
+ :goto_3a8
+ add-int/lit8 v7, v8, 0x1
+ move v8, v7
+ move-wide/from16 v23, v4
+ move-object v5, v6
+ move-wide/from16 v6, v23
+ goto :goto_37f
+ :cond_3b1
+ if-nez v5, :cond_3ba
+ const/4 v4, 0x3
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ goto/16 :goto_0
+ :cond_3ba
+ invoke-virtual/range {p1 .. p1}, LTest;->c()J
+ move-result-wide v8
+ sub-long/2addr v6, v8
+ long-to-int v4, v6
+ if-gez v4, :cond_3ca
+ new-instance v4, LTest;
+ const-string v5, "a"
+ invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+ throw v4
+ :cond_3ca
+ move-object/from16 v0, p1
+ invoke-virtual {v0, v4}, LTest;->b(I)V
+ iget-object v4, v5, LTest;->a:LTest;
+ iget-object v5, v4, LTest;->l:LTest;
+ iget-object v5, v5, LTest;->a:[B
+ const/4 v6, 0x0
+ iget v7, v4, LTest;->k:I
+ move-object/from16 v0, p1
+ invoke-virtual {v0, v5, v6, v7}, LTest;->b([BII)V
+ iget-object v5, v4, LTest;->l:LTest;
+ const/4 v6, 0x0
+ invoke-virtual {v5, v6}, LTest;->c(I)V
+ const/4 v5, 0x0
+ iput-boolean v5, v4, LTest;->m:Z
+ goto/16 :goto_0
+ :cond_3e8
+ move-object/from16 v0, p0
+ iput-object v5, v0, LTestObject;->o:LTest;
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ if-nez v4, :cond_414
+ move-object/from16 v0, p0
+ iget-wide v4, v0, LTestObject;->q:J
+ invoke-virtual/range {p1 .. p1}, LTest;->c()J
+ move-result-wide v6
+ sub-long/2addr v4, v6
+ long-to-int v4, v4
+ if-gez v4, :cond_406
+ new-instance v4, LTest;
+ const-string v5, ""
+ invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+ throw v4
+ :cond_406
+ move-object/from16 v0, p1
+ invoke-virtual {v0, v4}, LTest;->b(I)V
+ invoke-direct/range {p0 .. p0}, LTestObject;->a()V
+ const/4 v4, 0x0
+ :goto_40f
+ if-eqz v4, :cond_0
+ const/4 v4, 0x0
+ goto/16 :goto_63
+ :cond_414
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ iget-wide v4, v4, LTest;->b:J
+ invoke-virtual/range {p1 .. p1}, LTest;->c()J
+ move-result-wide v6
+ sub-long/2addr v4, v6
+ long-to-int v4, v4
+ if-gez v4, :cond_42c
+ new-instance v4, LTest;
+ const-string v5, ""
+ invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+ throw v4
+ :cond_42c
+ move-object/from16 v0, p1
+ invoke-virtual {v0, v4}, LTest;->b(I)V
+ :cond_431
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ iget-object v4, v4, LTest;->e:[I
+ move-object/from16 v0, p0
+ iget-object v5, v0, LTestObject;->o:LTest;
+ iget v5, v5, LTest;->e:I
+ aget v4, v4, v5
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget-object v4, v4, LTest;->a:LTest;
+ iget-boolean v4, v4, LTest;->i:Z
+ if-eqz v4, :cond_553
+ move-object/from16 v0, p0
+ iget-object v5, v0, LTestObject;->o:LTest;
+ iget-object v6, v5, LTest;->a:LTest;
+ iget-object v7, v6, LTest;->l:LTest;
+ iget-object v4, v6, LTest;->a:LTest;
+ iget v4, v4, LTest;->a:I
+ iget-object v8, v6, LTest;->n:LTest;
+ if-eqz v8, :cond_534
+ iget-object v4, v6, LTest;->n:LTest;
+ :goto_461
+ iget v8, v4, LTest;->a:I
+ iget-object v4, v6, LTest;->j:[Z
+ iget v6, v5, LTest;->e:I
+ aget-boolean v6, v4, v6
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget-object v9, v4, LTest;->a:[B
+ const/4 v10, 0x0
+ if-eqz v6, :cond_53c
+ const/16 v4, 0x80
+ :goto_474
+ or-int/2addr v4, v8
+ int-to-byte v4, v4
+ aput-byte v4, v9, v10
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ const/4 v9, 0x0
+ invoke-virtual {v4, v9}, LTest;->c(I)V
+ iget-object v4, v5, LTest;->b:LTest;
+ move-object/from16 v0, p0
+ iget-object v5, v0, LTestObject;->o:LTest;
+ const/4 v9, 0x1
+ invoke-virtual {v4, v5, v9}, LTest;->a(LTest;I)V
+ invoke-virtual {v4, v7, v8}, LTest;->a(LTest;I)V
+ if-nez v6, :cond_53f
+ add-int/lit8 v4, v8, 0x1
+ :goto_491
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->i:I
+ move-object/from16 v0, p0
+ iget v5, v0, LTestObject;->i:I
+ add-int/2addr v4, v5
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ :goto_4a2
+ const/4 v4, 0x4
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ const/4 v4, 0x0
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ :cond_4ac
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget-object v12, v4, LTest;->a:LTest;
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget-object v9, v4, LTest;->c:LTest;
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget-object v5, v4, LTest;->b:LTest;
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget v8, v4, LTest;->e:I
+ iget v4, v9, LTest;->n:I
+ const/4 v6, -0x1
+ if-eq v4, v6, :cond_57a
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget-object v4, v4, LTest;->a:[B
+ const/4 v6, 0x0
+ const/4 v7, 0x0
+ aput-byte v7, v4, v6
+ const/4 v6, 0x1
+ const/4 v7, 0x0
+ aput-byte v7, v4, v6
+ const/4 v6, 0x2
+ const/4 v7, 0x0
+ aput-byte v7, v4, v6
+ iget v4, v9, LTest;->n:I
+ iget v6, v9, LTest;->n:I
+ rsub-int/lit8 v6, v6, 0x4
+ :goto_4e1
+ move-object/from16 v0, p0
+ iget v7, v0, LTestObject;->i:I
+ move-object/from16 v0, p0
+ iget v10, v0, LTestObject;->i:I
+ if-ge v7, v10, :cond_59e
+ move-object/from16 v0, p0
+ iget v7, v0, LTestObject;->i:I
+ if-nez v7, :cond_55a
+ move-object/from16 v0, p0
+ iget-object v7, v0, LTestObject;->o:LTest;
+ iget-object v7, v7, LTest;->a:[B
+ move-object/from16 v0, p1
+ invoke-virtual {v0, v7, v6, v4}, LTest;->b([BII)V
+ move-object/from16 v0, p0
+ iget-object v7, v0, LTestObject;->o:LTest;
+ const/4 v10, 0x0
+ invoke-virtual {v7, v10}, LTest;->c(I)V
+ move-object/from16 v0, p0
+ iget-object v7, v0, LTestObject;->o:LTest;
+ invoke-virtual {v7}, LTest;->n()I
+ move-result v7
+ move-object/from16 v0, p0
+ iput v7, v0, LTestObject;->i:I
+ move-object/from16 v0, p0
+ iget-object v7, v0, LTestObject;->o:LTest;
+ const/4 v10, 0x0
+ invoke-virtual {v7, v10}, LTest;->c(I)V
+ move-object/from16 v0, p0
+ iget-object v7, v0, LTestObject;->o:LTest;
+ const/4 v10, 0x4
+ invoke-virtual {v5, v7, v10}, LTest;->a(LTest;I)V
+ move-object/from16 v0, p0
+ iget v7, v0, LTestObject;->i:I
+ add-int/lit8 v7, v7, 0x4
+ move-object/from16 v0, p0
+ iput v7, v0, LTestObject;->i:I
+ move-object/from16 v0, p0
+ iget v7, v0, LTestObject;->i:I
+ add-int/2addr v7, v6
+ move-object/from16 v0, p0
+ iput v7, v0, LTestObject;->i:I
+ goto :goto_4e1
+ :cond_534
+ iget-object v8, v5, LTest;->c:LTest;
+ iget-object v8, v8, LTest;->k:[LTest;
+ aget-object v4, v8, v4
+ goto/16 :goto_461
+ :cond_53c
+ const/4 v4, 0x0
+ goto/16 :goto_474
+ :cond_53f
+ invoke-virtual {v7}, LTest;->e()I
+ move-result v5
+ const/4 v6, -0x2
+ invoke-virtual {v7, v6}, LTest;->d(I)V
+ mul-int/lit8 v5, v5, 0x6
+ add-int/lit8 v5, v5, 0x2
+ invoke-virtual {v4, v7, v5}, LTest;->a(LTest;I)V
+ add-int/lit8 v4, v8, 0x1
+ add-int/2addr v4, v5
+ goto/16 :goto_491
+ :cond_553
+ const/4 v4, 0x0
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ goto/16 :goto_4a2
+ :cond_55a
+ move-object/from16 v0, p0
+ iget v7, v0, LTestObject;->i:I
+ const/4 v10, 0x0
+ move-object/from16 v0, p1
+ invoke-virtual {v5, v0, v7, v10}, LTest;->a(LTest;IZ)I
+ move-result v7
+ move-object/from16 v0, p0
+ iget v10, v0, LTestObject;->i:I
+ add-int/2addr v10, v7
+ move-object/from16 v0, p0
+ iput v10, v0, LTestObject;->i:I
+ move-object/from16 v0, p0
+ iget v10, v0, LTestObject;->i:I
+ sub-int v7, v10, v7
+ move-object/from16 v0, p0
+ iput v7, v0, LTestObject;->i:I
+ goto/16 :goto_4e1
+ :cond_57a
+ :goto_57a
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->i:I
+ move-object/from16 v0, p0
+ iget v6, v0, LTestObject;->i:I
+ if-ge v4, v6, :cond_59e
+ move-object/from16 v0, p0
+ iget v4, v0, LTestObject;->i:I
+ move-object/from16 v0, p0
+ iget v6, v0, LTestObject;->i:I
+ sub-int/2addr v4, v6
+ const/4 v6, 0x0
+ move-object/from16 v0, p1
+ invoke-virtual {v5, v0, v4, v6}, LTest;->a(LTest;IZ)I
+ move-result v4
+ move-object/from16 v0, p0
+ iget v6, v0, LTestObject;->i:I
+ add-int/2addr v4, v6
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ goto :goto_57a
+ :cond_59e
+ iget-object v4, v12, LTest;->g:[J
+ aget-wide v6, v4, v8
+ iget-object v4, v12, LTest;->f:[I
+ aget v4, v4, v8
+ int-to-long v10, v4
+ add-long/2addr v6, v10
+ const-wide/16 v10, 0x3e8
+ mul-long/2addr v6, v10
+ iget-boolean v4, v12, LTest;->i:Z
+ if-eqz v4, :cond_5f3
+ const/4 v4, 0x2
+ :goto_5b0
+ iget-object v10, v12, LTest;->h:[Z
+ aget-boolean v8, v10, v8
+ if-eqz v8, :cond_5f5
+ const/4 v8, 0x1
+ :goto_5b7
+ or-int/2addr v8, v4
+ iget-object v4, v12, LTest;->a:LTest;
+ iget v4, v4, LTest;->a:I
+ const/4 v11, 0x0
+ iget-boolean v10, v12, LTest;->i:Z
+ if-eqz v10, :cond_5ca
+ iget-object v10, v12, LTest;->n:LTest;
+ if-eqz v10, :cond_5f7
+ iget-object v4, v12, LTest;->n:LTest;
+ iget-object v4, v4, LTest;->b:[B
+ :goto_5c9
+ move-object v11, v4
+ :cond_5ca
+ move-object/from16 v0, p0
+ iget v9, v0, LTestObject;->i:I
+ const/4 v10, 0x0
+ invoke-virtual/range {v5 .. v11}, LTest;->a(JIII[B)V
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget v5, v4, LTest;->e:I
+ add-int/lit8 v5, v5, 0x1
+ iput v5, v4, LTest;->e:I
+ move-object/from16 v0, p0
+ iget-object v4, v0, LTestObject;->o:LTest;
+ iget v4, v4, LTest;->e:I
+ iget v5, v12, LTest;->d:I
+ if-ne v4, v5, :cond_5eb
+ const/4 v4, 0x0
+ move-object/from16 v0, p0
+ iput-object v4, v0, LTestObject;->o:LTest;
+ :cond_5eb
+ const/4 v4, 0x3
+ move-object/from16 v0, p0
+ iput v4, v0, LTestObject;->i:I
+ const/4 v4, 0x1
+ goto/16 :goto_40f
+ :cond_5f3
+ const/4 v4, 0x0
+ goto :goto_5b0
+ :cond_5f5
+ const/4 v8, 0x0
+ goto :goto_5b7
+ :cond_5f7
+ iget-object v9, v9, LTest;->k:[LTest;
+ aget-object v4, v9, v4
+ iget-object v4, v4, LTest;->b:[B
+ goto :goto_5c9
+ :cond_5fe
+ move-object v6, v5
+ move-wide v4, v8
+ goto/16 :goto_41
+ :cond_602
+ move-wide/from16 v23, v6
+ move-object v6, v5
+ move-wide/from16 v4, v23
+ goto/16 :goto_3a8
+ nop
+ :pswitch_data_60a
+ .packed-switch 0x0
+ :pswitch_47
+ :pswitch_241
+ :pswitch_36f
+ .end packed-switch
+.end method
+
+.method private final a(J)V
+ .registers 3
+ .prologue
+ return-void
+.end method
+
diff --git a/src/test/smali/type-confusion-regression4/Test.java b/src/test/smali/type-confusion-regression4/Test.java
new file mode 100644
index 0000000..252d6c8
--- /dev/null
+++ b/src/test/smali/type-confusion-regression4/Test.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+
+ public boolean b = false;
+ public Test a = null;
+
+ public Test() {
+ }
+
+ public Test(int i, Test a) {
+ throw new RuntimeException("Test(ILTest;)");
+ }
+
+ public int nextIndex() {
+ throw new RuntimeException("nextIndex()");
+ }
+
+ public int previousIndex() {
+ throw new RuntimeException("previousIndex()");
+ }
+
+ public boolean hasNext() {
+ throw new RuntimeException("hasNext()");
+ }
+
+ public boolean hasPrevious() {
+ throw new RuntimeException("hasPrevious()");
+ }
+
+ public static void main(String[] args) {
+ try {
+ new TestObject().a(new Test(), new Test());
+ } catch (Exception e) {
+ System.out.println(e);
+ }
+ }
+}
diff --git a/src/test/smali/type-confusion-regression4/TestObject.java b/src/test/smali/type-confusion-regression4/TestObject.java
new file mode 100644
index 0000000..340e820
--- /dev/null
+++ b/src/test/smali/type-confusion-regression4/TestObject.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+ public final Test a(Test t0, Test t1) {
+ return null;
+ }
+}
diff --git a/src/test/smali/type-confusion-regression4/TestObject.smali b/src/test/smali/type-confusion-regression4/TestObject.smali
new file mode 100644
index 0000000..c190f81
--- /dev/null
+++ b/src/test/smali/type-confusion-regression4/TestObject.smali
@@ -0,0 +1,82 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.method public final a(LTest;LTest;)LTest;
+ .registers 7
+ .prologue
+ const/4 v0, 0x0
+ if-eqz p1, :cond_23
+ iget-boolean v1, p1, LTest;->b:Z
+ if-eqz v1, :cond_35
+ iget-object v1, p0, LTest;->f:LTest;
+ iget-object v1, v1, LTest;->b:LTest;
+ invoke-virtual {v1}, LTest;->hasPrevious()Z
+ move-result v1
+ if-eqz v1, :cond_35
+ new-instance p2, LTest;
+ const/4 v1, 0x1
+ iget-object v2, p0, LTest;->f:LTest;
+ iget-object v3, v2, LTest;->b:LTest;
+ invoke-virtual {v3}, LTest;->hasPrevious()Z
+ move-result v3
+ if-nez v3, :cond_24
+ :goto_1e
+ iget-object v0, v0, LTest;->a:LTest;
+ invoke-direct {p2, v1, v0}, LTest;-><init>(ILTest;)V
+ :cond_23
+ :goto_23
+ return-object p2
+ :cond_24
+ iget-object v0, v2, LTest;->b:LTest;
+ iget-object v0, v0, LTest;->a:Ljava/util/ArrayList;
+ iget-object v2, v2, LTest;->b:LTest;
+ invoke-virtual {v2}, LTest;->previousIndex()I
+ move-result v2
+ invoke-interface {v0, v2}, Ljava/util/List;->get(I)Ljava/lang/Object;
+ move-result-object v0
+ check-cast v0, LTest;
+ goto :goto_1e
+ :cond_35
+ iget-boolean v1, p1, LTest;->b:Z
+ if-eqz v1, :cond_67
+ iget-object v1, p0, LTest;->f:LTest;
+ iget-object v1, v1, LTest;->b:LTest;
+ invoke-virtual {v1}, LTest;->hasNext()Z
+ move-result v1
+ if-eqz v1, :cond_67
+ new-instance p2, LTest;
+ const/4 v1, 0x2
+ iget-object v2, p0, LTest;->f:LTest;
+ iget-object v3, v2, LTest;->b:LTest;
+ invoke-virtual {v3}, LTest;->hasNext()Z
+ move-result v3
+ if-nez v3, :cond_56
+ :goto_50
+ iget-object v0, v0, LTest;->a:LTest;
+ invoke-direct {p2, v1, v0}, LTest;-><init>(ILTest;)V
+ goto :goto_23
+ :cond_56
+ iget-object v0, v2, LTest;->b:LTest;
+ iget-object v0, v0, LTest;->a:Ljava/util/ArrayList;
+ iget-object v2, v2, LTest;->b:LTest;
+ invoke-virtual {v2}, LTest;->nextIndex()I
+ move-result v2
+ invoke-interface {v0, v2}, Ljava/util/List;->get(I)Ljava/lang/Object;
+ move-result-object v0
+ check-cast v0, LTest;
+ goto :goto_50
+ :cond_67
+ iget-object v0, p1, LTest;->a:LTest;
+ if-eqz v0, :cond_23
+ new-instance p2, LTest;
+ const/4 v0, 0x0
+ new-instance v1, LTest;
+ iget-object v2, p1, LTest;->a:LTest;
+ invoke-direct {v1, v2}, LTest;-><init>(LTest;)V
+ invoke-direct {p2, v0, v1}, LTest;-><init>(ILTest;)V
+ goto :goto_23
+.end method
\ No newline at end of file
diff --git a/src/test/smali/type-confusion-regression5/Test.java b/src/test/smali/type-confusion-regression5/Test.java
new file mode 100644
index 0000000..e450d6b
--- /dev/null
+++ b/src/test/smali/type-confusion-regression5/Test.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+
+ public int getId() {
+ throw new RuntimeException("getId()I");
+ }
+
+ public boolean a() {
+ throw new RuntimeException("a()Z");
+ }
+
+ public void a(Test t0) {
+ throw new RuntimeException("a(LTest;)V");
+ }
+
+ public void a(Test t0, Test t1) {
+ throw new RuntimeException("a(LTest;LTest;)V");
+ }
+
+ public static boolean b(Test t0, Test t1) {
+ throw new RuntimeException("b()Z");
+ }
+
+ public Test c() {
+ throw new RuntimeException("c()LTest;");
+ }
+
+ public static boolean c(Test t) {
+ throw new RuntimeException("c(LTest;)Z");
+ }
+
+ public void g() {
+ throw new RuntimeException("g()V");
+ }
+
+ public boolean pageScroll(int i) {
+ throw new RuntimeException("pageScroll(I)Z");
+ }
+
+ public static void main(String[] args) {
+ try {
+ new TestObject().onClick(new Test());
+ } catch (Exception e) {
+ System.out.println(e);
+ }
+ }
+}
diff --git a/src/test/smali/type-confusion-regression5/TestObject.java b/src/test/smali/type-confusion-regression5/TestObject.java
new file mode 100644
index 0000000..a3cc1f9
--- /dev/null
+++ b/src/test/smali/type-confusion-regression5/TestObject.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+ public final void onClick(Test t) {
+ }
+}
diff --git a/src/test/smali/type-confusion-regression5/TestObject.smali b/src/test/smali/type-confusion-regression5/TestObject.smali
new file mode 100644
index 0000000..9ca1697
--- /dev/null
+++ b/src/test/smali/type-confusion-regression5/TestObject.smali
@@ -0,0 +1,171 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field private c:LTest;
+
+.method public onClick(LTest;)V
+ .registers 10
+ const/4 v2, 0x00 # 0
+ invoke-virtual { v9 }, LTest;->getId()I
+ move-result v0
+ const v1, 0x7f0f01bf # 2131689919
+ if-ne v0, v1, :label_247
+ invoke-virtual { v8 }, LTestObject;->getActivity()LTest;
+ move-result-object v0
+ invoke-static { v0, v9 }, LTest;->b(LTest;LTest;)Z
+ iget-object v0, v8, LTestObject;->c:LTest;
+ invoke-virtual { v0 }, LTest;->a()Z
+ move-result v0
+ if-eqz v0, :label_231
+ iget-object v0, v8, LTestObject;->d:Landroid/widget/Button;
+ const/4 v1, 0x00 # 0
+ invoke-virtual { v0, v1 }, Landroid/widget/Button;->setEnabled(Z)V
+ iget-object v0, v8, LTestObject;->b:Landroid/widget/LinearLayout;
+ if-nez v0, :label_72
+ const-string v0, "a"
+ const/4 v1, 0x05 # 5
+ invoke-static { v0, v1 }, Landroid/util/Log;->isLoggable(Ljava/lang/String;I)Z
+ move-result v0
+ if-eqz v0, :label_51
+ const-string v0, "b"
+ const-string v1, "c"
+ invoke-static { v0, v1 }, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I
+ :label_51
+ invoke-static { v2 }, LTest;->c(LTest;)Z
+ move-result v0
+ if-eqz v0, :label_66
+ iget-object v0, v8, LTestObject;->a:LTest;
+ sget-object v1, Ljok;->d:LTest;
+ sget-object v3, Ljol;->c:LTest;
+ invoke-virtual { v0, v1, v3 }, LTest;->a(LTest;LTest;)V
+ :label_66
+ iget-object v0, v8, LTestObject;->g:LTest;
+ invoke-virtual { v0, v2 }, LTest;->a(LTest;)V
+ :label_71
+ return-void
+ :label_72
+ new-instance v5, Ljava/util/ArrayList;
+ invoke-direct { v5 }, Ljava/util/ArrayList;-><init>()V
+ iget-object v0, v8, LTestObject;->b:Landroid/widget/LinearLayout;
+ invoke-direct { v8, v0 }, LTestObject;->a(Landroid/widget/LinearLayout;)Ljava/util/List;
+ move-result-object v0
+ invoke-interface { v0 }, Ljava/util/List;->iterator()Ljava/util/Iterator;
+ move-result-object v6
+ move-object v1, v2
+ move-object v4, v2
+ :label_89
+ invoke-interface { v6 }, Ljava/util/Iterator;->hasNext()Z
+ move-result v0
+ if-eqz v0, :label_151
+ invoke-interface { v6 }, Ljava/util/Iterator;->next()Ljava/lang/Object;
+ move-result-object v0
+ check-cast v0, LTest;
+ invoke-virtual { v0 }, LTest;->c()LTest;
+ move-result-object v3
+ if-eqz v3, :label_148
+ instance-of v7, v0, LTest;
+ if-eqz v7, :label_127
+ if-eqz v1, :label_116
+ invoke-virtual { v5, v1 }, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
+ :label_116
+ invoke-virtual { v5, v3 }, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
+ if-eqz v4, :label_124
+ invoke-virtual { v5, v4 }, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
+ :label_124
+ move-object v1, v2
+ move-object v4, v2
+ goto :label_89
+ :label_127
+ instance-of v7, v0, LTest;
+ if-eqz v7, :label_133
+ move-object v1, v3
+ goto :label_89
+ :label_133
+ instance-of v0, v0, LTest;
+ if-eqz v0, :label_139
+ move-object v4, v3
+ goto :label_89
+ :label_139
+ if-eqz v4, :label_145
+ invoke-virtual { v5, v4 }, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
+ move-object v4, v2
+ :label_145
+ invoke-virtual { v5, v3 }, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
+ :label_148
+ move-object v3, v4
+ move-object v4, v3
+ goto :label_89
+ :label_151
+ if-eqz v4, :label_156
+ invoke-virtual { v5, v4 }, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
+ :label_156
+ new-instance v1, LTest;
+ invoke-direct { v1 }, LTest;-><init>()V
+ const-string v0, "d"
+ iput-object v0, v1, LTest;->b:Ljava/lang/String;
+ iget-object v0, v1, LTest;->c:Ljava/util/Set;
+ const/4 v3, 0x06 # 6
+ invoke-static { v3 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
+ move-result-object v3
+ invoke-interface { v0, v3 }, Ljava/util/Set;->add(Ljava/lang/Object;)Z
+ new-instance v3, LTest;
+ invoke-direct { v3 }, LTest;-><init>()V
+ iput-object v5, v3, LTest;->a:Ljava/util/List;
+ iget-object v0, v3, LTest;->b:Ljava/util/Set;
+ const/4 v4, 0x02 # 2
+ invoke-static { v4 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
+ move-result-object v4
+ invoke-interface { v0, v4 }, Ljava/util/Set;->add(Ljava/lang/Object;)Z
+ new-instance v0, LTest;
+ iget-object v4, v3, LTest;->b:Ljava/util/Set;
+ iget-object v3, v3, LTest;->a:Ljava/util/List;
+ invoke-direct { v0, v4, v3 }, LTest;-><init>(Ljava/util/Set;Ljava/util/List;)V
+ check-cast v0, LTest;
+ iput-object v0, v1, LTest;->a:LTest;
+ iget-object v0, v1, LTest;->c:Ljava/util/Set;
+ const/4 v3, 0x04 # 4
+ invoke-static { v3 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
+ move-result-object v3
+ invoke-interface { v0, v3 }, Ljava/util/Set;->add(Ljava/lang/Object;)Z
+ new-instance v0, LTest;
+ iget-object v3, v1, LTest;->c:Ljava/util/Set;
+ iget-object v4, v1, LTest;->a:LTest;
+ iget-object v1, v1, LTest;->b:Ljava/lang/String;
+ invoke-direct { v0, v3, v2, v4, v1 }, LTest;-><init>(Ljava/util/Set;LTest;LTest;Ljava/lang/String;)V
+ check-cast v0, LTest;
+ move-object v2, v0
+ goto/16 :label_51
+ :label_231
+ iget-object v0, v8, LTestObject;->c:LTest;
+ const/16 v1, 0x0082 # 130
+ invoke-virtual { v0, v1 }, LTest;->pageScroll(I)Z
+ iget-object v0, v8, LTestObject;->a:LTest;
+ sget-object v1, Ljok;->k:LTest;
+ invoke-virtual { v0, v1 }, LTest;->a(LTest;)V
+ goto/16 :label_71
+ :label_247
+ const v1, 0x7f0f0204 # 2131689988
+ if-ne v0, v1, :label_71
+ iget-object v0, v8, LTestObject;->a:LTest;
+ sget-object v1, Ljok;->q:LTest;
+ invoke-virtual { v0, v1 }, LTest;->a(LTest;)V
+ iget-object v0, v8, LTestObject;->g:LTest;
+ invoke-virtual { v0 }, LTest;->g()V
+ goto/16 :label_71
+.end method
+
+.method public getActivity()LTest;
+ .registers 1
+ const/4 v0, 0x00
+ return-object v0
+.end method
+
+.method private a(Landroid/widget/LinearLayout;)Ljava/util/List;
+ .registers 2
+ const/4 v0, 0x00
+ return-object v0
+.end method
diff --git a/src/test/smali/unreachable-code-1/UnreachableCode1.smali b/src/test/smali/unreachable-code-1/UnreachableCode1.smali
new file mode 100644
index 0000000..a7ba2f1
--- /dev/null
+++ b/src/test/smali/unreachable-code-1/UnreachableCode1.smali
@@ -0,0 +1,87 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method public static test1()I
+ .registers 1
+ goto :return
+
+ :dummy
+ const/4 v0, 0x1
+
+ :return
+ const/4 v0, 0x7
+ return v0
+
+.end method
+
+.method public static test2()I
+ .registers 1
+ goto :return
+
+ :dummy1
+ const/4 v0, 0x1
+
+ :dummy2
+ const/4 v0, 0x2
+
+ :return
+ const/4 v0, 0x7
+ return v0
+
+.end method
+
+.method public static test3()I
+ .registers 1
+ goto :return
+
+ :dummy1
+ const/4 v0, 0x1
+ goto :dummy3
+
+ :dummy2
+ const/4 v0, 0x2
+ goto :return
+
+ :dummy3
+ const/4 v0, 0x3
+ goto :return
+
+ :dummy4
+ const/4 v0, 0x4
+
+ :return
+ const/4 v0, 0x7
+ return v0
+
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 2
+
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ invoke-static {}, LTest;->test1()I
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ invoke-static {}, LTest;->test2()I
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ invoke-static {}, LTest;->test3()I
+ move-result v1
+ invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ return-void
+.end method