Version 1.6.57
Cherry-pick: Testing utilities for transforming classfiles. (with modifications)
CL: https://r8-review.googlesource.com/c/r8/+/45621
Cherry-pick: Add a test for b/146957343 (with modifications)
CL: https://r8-review.googlesource.com/c/r8/+/47139
Cherry-pick: Turn off uninstantiated type optimization for interfaces
CL: https://r8-review.googlesource.com/c/r8/+/47160
Bug: 146957343
Bug: 147153808
Change-Id: I66544813e8457f2811efa18a801f0891acd61fb9
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 8e7e7fd..34e9cba 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.6.56";
+ public static final String LABEL = "1.6.57";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 92a4963..54fbf9f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -238,6 +238,10 @@
return this;
}
+ public static DexProgramClass asProgramClassOrNull(DexClass clazz) {
+ return clazz != null ? clazz.asProgramClass() : null;
+ }
+
@Override
public boolean isNotProgramClass() {
return false;
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 055b120..b0e5373 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.DISPATCH_CLASS_NAME_SUFFIX;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.EMULATE_LIBRARY_CLASS_NAME_SUFFIX;
@@ -71,10 +72,16 @@
public boolean isAlwaysNull(AppView<AppInfoWithLiveness> appView) {
if (isClassType()) {
- DexClass clazz = appView.definitionFor(this);
- return clazz != null
- && clazz.isProgramClass()
- && !appView.appInfo().isInstantiatedDirectlyOrIndirectly(this);
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(this));
+ if (clazz == null) {
+ return false;
+ }
+ if (appView.options().enableUninstantiatedTypeOptimizationForInterfaces) {
+ return !appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz.type);
+ } else {
+ return !clazz.isInterface()
+ && !appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz.type);
+ }
}
return false;
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 0098813..d826aa9 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -240,6 +240,8 @@
public boolean enableValuePropagation = true;
public boolean enableFieldTypePropagation = true;
public boolean enableUninstantiatedTypeOptimization = true;
+ // Currently disabled, see b/146957343.
+ public boolean enableUninstantiatedTypeOptimizationForInterfaces = false;
// TODO(b/138917494): Disable until we have numbers on potential performance penalties.
public boolean enableRedundantConstNumberOptimization = false;
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 797d185..3f140b8 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.serviceloader.ServiceLoaderMultipleTest.Greeter;
+import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AndroidAppConsumers;
@@ -166,6 +167,10 @@
return testForMainDexListGenerator(temp);
}
+ public static ClassFileTransformer transformer(Class<?> clazz) throws IOException {
+ return ClassFileTransformer.create(clazz);
+ }
+
// Actually running Proguard should only be during development.
private static final boolean RUN_PROGUARD = System.getProperty("run_proguard") != null;
// Actually running r8.jar in a forked process.
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
index 4dd10a1..7440dba 100644
--- a/src/test/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@@ -99,6 +100,15 @@
return self();
}
+ public RR assertFailureWithErrorThatThrows(Class<? extends Throwable> expectedError) {
+ assertFailure();
+ assertThat(
+ errorMessage("Run stderr incorrect.", expectedError.getName()),
+ result.stderr,
+ containsString(expectedError.getName()));
+ return self();
+ }
+
public CodeInspector inspector() throws IOException, ExecutionException {
// Inspection post run implies success. If inspection of an invalid program is needed it should
// be done on the compilation result or on the input.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java
new file mode 100644
index 0000000..f454913
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use 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.uninstantiatedtypes;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class B146957343 extends TestBase implements Opcodes {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public B146957343(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private byte[] getAimplementsI() throws IOException {
+ return transformer(A.class).setImplements(I.class).transform();
+ }
+
+ @Test
+ public void testWithoutR8() throws Exception {
+ testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+ .addProgramClasses(I.class, J.class, Main.class)
+ .addProgramClassFileData(getAimplementsI())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("In A.f()");
+ }
+
+ @Test
+ public void testWithUninstantiatedTypeOptimizationForInterfaces() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(I.class, J.class, Main.class)
+ .addProgramClassFileData(getAimplementsI())
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-keep class **A { createA(); }")
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(
+ options -> options.enableUninstantiatedTypeOptimizationForInterfaces = true)
+ .compile()
+ .disassemble()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class);
+ }
+
+ @Test
+ public void testWithoutUninstantiatedTypeOptimizationForInterfaces() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(I.class, J.class, Main.class)
+ .addProgramClassFileData(getAimplementsI())
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-keep class **A { createA(); }")
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(
+ options -> options.enableUninstantiatedTypeOptimizationForInterfaces = false)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("In A.f()");
+ }
+
+ public interface I {}
+
+ public interface J extends I {}
+
+ public static class A implements J {
+ public static J createA() {
+ return new A();
+ }
+
+ public void f() {
+ System.out.println("In A.f()");
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ ((A) A.createA()).f();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
index b951b4f..b42be41 100644
--- a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
@@ -40,7 +40,8 @@
D8,
JAVAC,
PROGUARD,
- R8
+ R8,
+ R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
}
private enum Mode {
@@ -207,6 +208,27 @@
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), mainClass.name);
checkTestRunResult(r8Result, Compiler.R8);
+
+ R8TestRunResult r8ResultWithUninstantiatedTypeOptimizationForInterfaces =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(inputJar)
+ .addKeepMainRule(mainClass.name)
+ .addKeepRules(
+ "-keep class TestClass { public static I g; }",
+ "-neverinline class TestClass { public static void m(); }")
+ .enableProguardTestOptions()
+ .addOptionsModification(
+ options -> {
+ if (mode == Mode.INVOKE_UNVERIFIABLE_METHOD) {
+ options.testing.allowTypeErrors = true;
+ }
+ options.enableUninstantiatedTypeOptimizationForInterfaces = true;
+ })
+ .setMinApi(parameters.getRuntime())
+ .run(parameters.getRuntime(), mainClass.name);
+ checkTestRunResult(
+ r8ResultWithUninstantiatedTypeOptimizationForInterfaces,
+ Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES);
}
private void checkTestRunResult(TestRunResult<?> result, Compiler compiler) {
@@ -219,7 +241,9 @@
if (useInterface) {
result.assertSuccessWithOutput(getExpectedOutput(compiler));
} else {
- if (compiler == Compiler.R8 || compiler == Compiler.PROGUARD) {
+ if (compiler == Compiler.R8
+ || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
+ || compiler == Compiler.PROGUARD) {
result.assertSuccessWithOutput(getExpectedOutput(compiler));
} else {
result
@@ -233,7 +257,8 @@
if (useInterface) {
result.assertSuccessWithOutput(getExpectedOutput(compiler));
} else {
- if (compiler == Compiler.R8) {
+ if (compiler == Compiler.R8
+ || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES) {
result
.assertFailureWithOutput(getExpectedOutput(compiler))
.assertFailureWithErrorThatMatches(
@@ -261,7 +286,9 @@
if (useInterface) {
return StringUtils.joinLines("Hello!", "In verifiable method!", "Goodbye!", "");
} else {
- if (compiler == Compiler.R8 || compiler == Compiler.PROGUARD) {
+ if (compiler == Compiler.R8
+ || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
+ || compiler == Compiler.PROGUARD) {
// The unverifiable method has been removed as a result of tree shaking, so the code does
// not fail with a verification error when trying to load class `UnverifiableClass`.
return StringUtils.joinLines("Hello!", "In verifiable method!", "Goodbye!", "");
@@ -274,14 +301,14 @@
}
assert mode == Mode.INVOKE_UNVERIFIABLE_METHOD;
if (useInterface) {
- if (compiler == Compiler.R8) {
+ if (compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES) {
return StringUtils.joinLines(
"Hello!",
"Unexpected outcome of getstatic",
"Unexpected outcome of checkcast",
"Goodbye!",
"");
- } else if (compiler == Compiler.PROGUARD) {
+ } else if (compiler == Compiler.R8 || compiler == Compiler.PROGUARD) {
return StringUtils.joinLines("Hello!", "Unexpected outcome of checkcast", "Goodbye!", "");
} else if (compiler == Compiler.DX || compiler == Compiler.D8) {
if (ToolHelper.getDexVm().getVersion() == Version.V4_0_4
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
index 74f4321..5c6a305 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
@@ -207,9 +207,9 @@
assertThat(clazz, isPresent());
assertEquals(minify, clazz.isRenamed());
MethodSubject f1 = clazz.uniqueMethodWithName("f1");
- assertThat(f1, not(isPresent()));
+ assertThat(f1, isPresent());
MethodSubject f2 = clazz.uniqueMethodWithName("f2");
- assertThat(f2, not(isPresent()));
+ assertThat(f2, isPresent());
MethodSubject f3 = clazz.uniqueMethodWithName("f3");
assertThat(f3, not(isPresent()));
MethodSubject f4 = clazz.uniqueMethodWithName("f4");
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
new file mode 100644
index 0000000..8157f47
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -0,0 +1,199 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.transformers;
+
+import static org.objectweb.asm.Opcodes.ASM7;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.transformers.MethodTransformer.MethodContext;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+
+public class ClassFileTransformer {
+
+ /**
+ * Basic algorithm for transforming the content of a class file.
+ *
+ * <p>The provided transformers are nested in the order given: the first in the list will receive
+ * is call back first, if it forwards to 'super' then the seconds call back will be called, etc,
+ * until finally the writer will be called. If the writer is not called the effect is as if the
+ * callback was never called and its content will not be in the result.
+ */
+ public static byte[] transform(
+ byte[] bytes,
+ List<ClassTransformer> classTransformers,
+ List<MethodTransformer> methodTransformers) {
+ ClassReader reader = new ClassReader(bytes);
+ ClassWriter writer = new ClassWriter(reader, 0);
+ ClassVisitor subvisitor = new InnerMostClassTransformer(writer, methodTransformers);
+ for (int i = classTransformers.size() - 1; i >= 0; i--) {
+ classTransformers.get(i).setSubVisitor(subvisitor);
+ subvisitor = classTransformers.get(i);
+ }
+ reader.accept(subvisitor, 0);
+ return writer.toByteArray();
+ }
+
+ // Inner-most bride from the class transformation to the method transformers.
+ private static class InnerMostClassTransformer extends ClassVisitor {
+ ClassReference classReference;
+ final List<MethodTransformer> methodTransformers;
+
+ InnerMostClassTransformer(ClassWriter writer, List<MethodTransformer> methodTransformers) {
+ super(ASM7, writer);
+ this.methodTransformers = methodTransformers;
+ }
+
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ super.visit(version, access, name, signature, superName, interfaces);
+ classReference = Reference.classFromBinaryName(name);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String descriptor, String signature, String[] exceptions) {
+ MethodContext context = createMethodContext(access, name, descriptor);
+ MethodVisitor subvisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
+ for (int i = methodTransformers.size() - 1; i >= 0; i--) {
+ MethodTransformer transformer = methodTransformers.get(i);
+ transformer.setSubVisitor(subvisitor);
+ transformer.setContext(context);
+ subvisitor = transformer;
+ }
+ return subvisitor;
+ }
+
+ private MethodContext createMethodContext(int access, String name, String descriptor) {
+ // Maybe clean up this parsing of info as it is not very nice.
+ MethodSignature methodSignature = MethodSignature.fromSignature(name, descriptor);
+ MethodReference methodReference =
+ Reference.method(
+ classReference,
+ name,
+ Arrays.stream(methodSignature.parameters)
+ .map(DescriptorUtils::javaTypeToDescriptor)
+ .map(Reference::typeFromDescriptor)
+ .collect(Collectors.toList()),
+ methodSignature.type.equals("void")
+ ? null
+ : Reference.typeFromDescriptor(
+ DescriptorUtils.javaTypeToDescriptor(methodSignature.type)));
+ return new MethodContext(methodReference, access);
+ }
+ }
+
+ // Transformer utilities.
+
+ private final byte[] bytes;
+ private final List<ClassTransformer> classTransformers = new ArrayList<>();
+ private final List<MethodTransformer> methodTransformers = new ArrayList<>();
+
+ private ClassFileTransformer(byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+ public static ClassFileTransformer create(byte[] bytes) {
+ return new ClassFileTransformer(bytes);
+ }
+
+ public static ClassFileTransformer create(Class<?> clazz) throws IOException {
+ return create(ToolHelper.getClassAsBytes(clazz));
+ }
+
+ public byte[] transform() {
+ return ClassFileTransformer.transform(bytes, classTransformers, methodTransformers);
+ }
+
+ /** Base addition of a transformer on the class. */
+ public ClassFileTransformer addClassTransformer(ClassTransformer transformer) {
+ classTransformers.add(transformer);
+ return this;
+ }
+
+ /** Base addtion of a transformer on methods. */
+ public ClassFileTransformer addMethodTransformer(MethodTransformer transformer) {
+ methodTransformers.add(transformer);
+ return this;
+ }
+
+ /** Unconditionally replace the implements clause of a class. */
+ public ClassFileTransformer setImplements(Class<?>... interfaces) {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] ignoredInterfaces) {
+ super.visit(
+ version,
+ access,
+ name,
+ signature,
+ superName,
+ Arrays.stream(interfaces)
+ .map(clazz -> DescriptorUtils.getBinaryNameFromJavaType(clazz.getTypeName()))
+ .toArray(String[]::new));
+ }
+ });
+ }
+
+ /** Abstraction of the MethodVisitor.visitMethodInsn method with its continuation. */
+ @FunctionalInterface
+ public interface MethodInsnTransform {
+ void visitMethodInsn(
+ int opcode,
+ String owner,
+ String name,
+ String descriptor,
+ boolean isInterface,
+ MethodInsnTransformContinuation continuation);
+ }
+
+ /** Continuation for transforming a method. Will continue with the super visitor if called. */
+ @FunctionalInterface
+ public interface MethodInsnTransformContinuation {
+ void visitMethodInsn(
+ int opcode, String owner, String name, String descriptor, boolean isInterface);
+ }
+
+ public ClassFileTransformer transformMethodInsnInMethod(
+ String methodName, MethodInsnTransform transform) {
+ return addMethodTransformer(
+ new MethodTransformer() {
+ @Override
+ public void visitMethodInsn(
+ int opcode, String owner, String name, String descriptor, boolean isInterface) {
+ if (getContext().method.getMethodName().equals(methodName)) {
+ transform.visitMethodInsn(
+ opcode, owner, name, descriptor, isInterface, super::visitMethodInsn);
+ } else {
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ }
+ });
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassTransformer.java
new file mode 100644
index 0000000..885257e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/transformers/ClassTransformer.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.transformers;
+
+import static org.objectweb.asm.Opcodes.ASM7;
+
+import org.objectweb.asm.ClassVisitor;
+
+/**
+ * Class for transforming the content of a class.
+ *
+ * <p>This is just a simple wrapper on the ASM ClassVisitor interface.
+ */
+public class ClassTransformer extends ClassVisitor {
+ public ClassTransformer() {
+ super(ASM7, null);
+ }
+
+ // Package internals.
+
+ void setSubVisitor(ClassVisitor visitor) {
+ this.cv = visitor;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/transformers/MethodTransformer.java b/src/test/java/com/android/tools/r8/transformers/MethodTransformer.java
new file mode 100644
index 0000000..a5ffa82
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/transformers/MethodTransformer.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.transformers;
+
+import static org.objectweb.asm.Opcodes.ASM7;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * Class for transforming the content of a method.
+ *
+ * <p>This is just a simple wrapper on the ASM MethodVisitor interface with some added methods for
+ * obtaining context information.
+ */
+public class MethodTransformer extends MethodVisitor {
+
+ static class MethodContext {
+ public final MethodReference method;
+ public final int accessFlags;
+
+ public MethodContext(MethodReference method, int accessFlags) {
+ this.method = method;
+ this.accessFlags = accessFlags;
+ }
+ }
+
+ private MethodContext context;
+
+ public MethodTransformer() {
+ super(ASM7, null);
+ }
+
+ public ClassReference getHolder() {
+ return getContext().method.getHolderClass();
+ }
+
+ public MethodReference getMethod() {
+ return getContext().method;
+ }
+
+ // Package internals.
+
+ MethodContext getContext() {
+ return context;
+ }
+
+ void setSubVisitor(MethodVisitor visitor) {
+ this.mv = visitor;
+ }
+
+ void setContext(MethodContext context) {
+ this.context = context;
+ }
+}