Version 1.5.58
Store parameter names info in DexEncodedMethod
CL: https://r8-review.googlesource.com/c/r8/+/39840
Cherry-pick: Add test for unsorted local variable table with -keepparameternames
CL: https://r8-review.googlesource.com/c/r8/+/39781
Cherry-pick: Only use locals starting on the first label for -keepparameternames
CL: https://r8-review.googlesource.com/c/r8/+/39863
Bug: 132549918
Bug: 136697156
Change-Id: I056706d8fb6ca4b1fa1b0f890fc45494c54901c6
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index f8c0fef..f12fbc7 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.5.57";
+ public static final String LABEL = "1.5.58";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index d2f8e2d..96da5d2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -60,11 +60,14 @@
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.IntPredicate;
@@ -111,7 +114,9 @@
public static final DexEncodedMethod[] EMPTY_ARRAY = {};
public static final DexEncodedMethod SENTINEL =
- new DexEncodedMethod(null, null, null, null, null);
+ new DexEncodedMethod(null, null, null, ParameterAnnotationsList.empty(), null);
+ public static final Int2ReferenceMap<DebugLocalInfo> NO_PARAMETER_INFO =
+ new Int2ReferenceArrayMap<>(0);
public final DexMethod method;
public final MethodAccessFlags accessFlags;
@@ -128,6 +133,8 @@
private OptionalBool isLibraryMethodOverride = OptionalBool.unknown();
+ private Int2ReferenceMap<DebugLocalInfo> parameterInfo = NO_PARAMETER_INFO;
+
// This flag indicates the current instance is no longer up-to-date as another instance was
// created based on this. Any further (public) operations on this instance will raise an error
// to catch potential bugs due to the inconsistency (e.g., http://b/111893131)
@@ -413,6 +420,19 @@
setCode(builder.build());
}
+ public void setParameterInfo(Int2ReferenceMap<DebugLocalInfo> parameterInfo) {
+ assert this.parameterInfo == NO_PARAMETER_INFO;
+ this.parameterInfo = parameterInfo;
+ }
+
+ public boolean hasParameterInfo() {
+ return parameterInfo != NO_PARAMETER_INFO;
+ }
+
+ public Map<Integer, DebugLocalInfo> getParameterInfo() {
+ return parameterInfo;
+ }
+
@Override
public String toString() {
checkIfObsolete();
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 38b5e1a..0271be9 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -24,11 +24,11 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OffOrAuto;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.BitSet;
import java.util.Iterator;
-import java.util.Map;
import java.util.function.BiFunction;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
@@ -154,14 +154,14 @@
triggerDelayedParsingIfNeccessary();
if (!keepLocals(encodedMethod, appView.options())) {
// If the locals are not kept, we might still need a bit of locals information to satisfy
- // -keepparameternames for R8.
- Map<Integer, DebugLocalInfo> parameterInfo = collectParameterInfo(encodedMethod, appView);
- // We strip locals here because we will not be able to recover from invalid info.
- if (canStripLocals(encodedMethod, appView)) {
- node.localVariables.clear();
+ // -keepparameternames for R8. As locals are stripped after collection the parameter names
+ // this information can only be retrieved the first time IR is build for a method, so stick
+ // to the information if already present.
+ if (!encodedMethod.hasParameterInfo()) {
+ encodedMethod.setParameterInfo(collectParameterInfo(encodedMethod, appView));
}
- return internalBuild(
- context, encodedMethod, appView, generator, callerPosition, parameterInfo);
+ node.localVariables.clear();
+ return internalBuild(context, encodedMethod, appView, generator, callerPosition);
} else {
return internalBuildWithLocals(context, encodedMethod, appView, generator, callerPosition);
}
@@ -174,13 +174,11 @@
ValueNumberGenerator generator,
Position callerPosition) {
try {
- return internalBuild(
- context, encodedMethod, appView, generator, callerPosition, IRCode.NO_PARAMETER_INFO);
+ return internalBuild(context, encodedMethod, appView, generator, callerPosition);
} catch (InvalidDebugInfoException e) {
appView.options().warningInvalidDebugInfo(encodedMethod, origin, e);
node.localVariables.clear();
- return internalBuild(
- context, encodedMethod, appView, generator, callerPosition, IRCode.NO_PARAMETER_INFO);
+ return internalBuild(context, encodedMethod, appView, generator, callerPosition);
}
}
@@ -194,17 +192,28 @@
return false;
}
- private Map<Integer, DebugLocalInfo> collectParameterInfo(
+ private Int2ReferenceMap<DebugLocalInfo> collectParameterInfo(
DexEncodedMethod encodedMethod, AppView<?> appView) {
+ LabelNode firstLabel = null;
+ for (Iterator<AbstractInsnNode> it = getNode().instructions.iterator(); it.hasNext(); ) {
+ AbstractInsnNode insn = it.next();
+ if (insn.getType() == AbstractInsnNode.LABEL) {
+ firstLabel = (LabelNode) insn;
+ break;
+ }
+ }
+ if (firstLabel == null) {
+ return DexEncodedMethod.NO_PARAMETER_INFO;
+ }
if (!appView.options().hasProguardConfiguration()
|| !appView.options().getProguardConfiguration().isKeepParameterNames()) {
- return IRCode.NO_PARAMETER_INFO;
+ return DexEncodedMethod.NO_PARAMETER_INFO;
}
// The enqueuer might build IR to trace reflective behaviour. At that point liveness is not
// known, so be conservative with collection parameter name information.
if (appView.appInfo().hasLiveness()
&& !appView.appInfo().withLiveness().isPinned(encodedMethod.method)) {
- return IRCode.NO_PARAMETER_INFO;
+ return DexEncodedMethod.NO_PARAMETER_INFO;
}
// Collect the local slots used for parameters.
BitSet localSlotsForParameters = new BitSet(0);
@@ -220,11 +229,10 @@
// assuming that that does actually describe the parameter (name, type and possibly
// signature).
DexItemFactory factory = appView.options().itemFactory;
- Map<Integer, DebugLocalInfo> parameterInfo =
+ Int2ReferenceMap<DebugLocalInfo> parameterInfo =
new Int2ReferenceArrayMap<>(localSlotsForParameters.cardinality());
- for (Object o : node.localVariables) {
- LocalVariableNode node = (LocalVariableNode) o;
- if (node.index < nextLocalSlotsForParameters
+ for (LocalVariableNode node : node.localVariables) {
+ if (node.start == firstLabel
&& localSlotsForParameters.get(node.index)
&& !parameterInfo.containsKey(node.index)) {
parameterInfo.put(
@@ -238,31 +246,13 @@
return parameterInfo;
}
- private boolean canStripLocals(DexEncodedMethod encodedMethod, AppView<?> appView) {
- // If not keeping parameter names the locals can always be stripped.
- if (!appView.options().hasProguardConfiguration()
- || !appView.options().getProguardConfiguration().isKeepParameterNames()) {
- return true;
- }
- // The enqueuer might build IR to trace reflective behaviour. At that point liveness is not
- // known, so locals cannot be stripped as IR will built again in the IR converter.
- if (appView.appInfo().hasLiveness()
- && !appView.appInfo().withLiveness().isPinned(encodedMethod.method)) {
- return true;
- }
- return false;
- }
-
private IRCode internalBuild(
DexEncodedMethod context,
DexEncodedMethod encodedMethod,
AppView<?> appView,
ValueNumberGenerator generator,
- Position callerPosition,
- Map<Integer, DebugLocalInfo> parameterInfo) {
- assert node.localVariables.isEmpty()
- || keepLocals(encodedMethod, appView.options())
- || !canStripLocals(encodedMethod, appView);
+ Position callerPosition) {
+ assert node.localVariables.isEmpty() || keepLocals(encodedMethod, appView.options());
JarSourceCode source =
new JarSourceCode(
method.holder,
@@ -271,7 +261,7 @@
appView.graphLense().getOriginalMethodSignature(encodedMethod.method),
callerPosition);
IRBuilder builder = new IRBuilder(encodedMethod, appView, source, origin, generator);
- return builder.build(context, parameterInfo);
+ return builder.build(context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index f427047..ea328b0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -16,7 +16,6 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
@@ -85,8 +84,6 @@
// 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;
- public static final Map<Integer, DebugLocalInfo> NO_PARAMETER_INFO =
- new Int2ReferenceArrayMap<>(0);
public final DexEncodedMethod method;
@@ -110,8 +107,6 @@
public final Origin origin;
- public final Map<Integer, DebugLocalInfo> parameterInfo;
-
public IRCode(
InternalOptions options,
DexEncodedMethod method,
@@ -120,8 +115,7 @@
boolean hasDebugPositions,
boolean hasMonitorInstruction,
boolean hasConstString,
- Origin origin,
- Map<Integer, DebugLocalInfo> parameterInfo) {
+ Origin origin) {
assert options != null;
this.options = options;
this.method = method;
@@ -131,10 +125,8 @@
this.hasMonitorInstruction = hasMonitorInstruction;
this.hasConstString = hasConstString;
this.origin = origin;
- this.parameterInfo = parameterInfo;
// TODO(zerny): Remove or update this property now that all instructions have positions.
allThrowingInstructionsHavePositions = computeAllThrowingInstructionsHavePositions();
- assert parameterInfo != null;
}
public void copyMetadataFromInlinee(IRCode inlinee) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index ffe8806..657032a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -311,7 +311,7 @@
assert !block.exit().isReturn() || stackHeightTracker.isEmpty();
if (firstBlock) {
- addParameterNamesIfRequired(block, code.parameterInfo);
+ addParameterNamesIfRequired(block);
firstBlock = false;
}
@@ -631,15 +631,19 @@
}
}
- private void addParameterNamesIfRequired(
- BasicBlock block, Map<Integer, DebugLocalInfo> parameterInfo) {
+ private void addParameterNamesIfRequired(BasicBlock block) {
// Don't add this information if the code already have full debug information.
if (appView.options().debug) {
return;
}
- if (code.parameterInfo != IRCode.NO_PARAMETER_INFO) {
- for (Map.Entry<Integer, DebugLocalInfo> entries : parameterInfo.entrySet()) {
+ if (appView.appInfo().hasLiveness()
+ && !appView.appInfo().withLiveness().isPinned(method.method)) {
+ return;
+ }
+
+ if (method.hasParameterInfo()) {
+ for (Map.Entry<Integer, DebugLocalInfo> entries : method.getParameterInfo().entrySet()) {
LocalVariableInfo localVariableInfo =
new LocalVariableInfo(entries.getKey(), entries.getValue(), getLabel(block));
CfLabel endLabel = ensureLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 6d9aa3b..cbddf03 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -496,19 +496,6 @@
* @return The list of basic blocks. First block is the main entry.
*/
public IRCode build(DexEncodedMethod context) {
- return build(context, IRCode.NO_PARAMETER_INFO);
- }
-
- /**
- * Build the high-level IR in SSA form.
- *
- * @param context Under what context this IRCode is built. Either the current method or caller.
- * @param parameterInfo Parameter information to include in the output. Pass <code>
- * IRCode.NO_PARAMETER_INFO</code> if this is not relevant. This information is only used if
- * the generated code does not contain any debug information.
- * @return The list of basic blocks. First block is the main entry.
- */
- public IRCode build(DexEncodedMethod context, Map<Integer, DebugLocalInfo> parameterInfo) {
assert source != null;
source.setUp();
@@ -609,8 +596,7 @@
hasDebugPositions,
hasMonitorInstruction,
hasConstString,
- origin,
- parameterInfo);
+ origin);
// Verify critical edges are split so we have a place to insert phi moves if necessary.
assert ir.verifySplitCriticalEdges();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
index cd8d101..97cb2b2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
@@ -141,8 +141,7 @@
false,
false,
false,
- Origin.unknown(),
- IRCode.NO_PARAMETER_INFO);
+ Origin.unknown());
PeepholeOptimizer.optimize(code, new MockLinearScanRegisterAllocator(appView, code));
// Check that all four constant number instructions remain.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
index 413efce..48a8246 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
@@ -82,8 +82,7 @@
false,
false,
false,
- Origin.unknown(),
- IRCode.NO_PARAMETER_INFO);
+ Origin.unknown());
CodeRewriter.collapseTrivialGotos(null, code);
assertTrue(code.entryBlock().isTrivialGoto());
assertTrue(blocks.contains(block0));
@@ -170,8 +169,7 @@
false,
false,
false,
- Origin.unknown(),
- IRCode.NO_PARAMETER_INFO);
+ Origin.unknown());
CodeRewriter.collapseTrivialGotos(null, code);
assertTrue(block0.getInstructions().get(1).isIf());
assertEquals(block1, block0.getInstructions().get(1).asIf().fallthroughBlock());
diff --git a/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesUnsortedLocalVariablesTableTest.java b/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesUnsortedLocalVariablesTableTest.java
new file mode 100644
index 0000000..d858322
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesUnsortedLocalVariablesTableTest.java
@@ -0,0 +1,215 @@
+// 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.shaking.keepparameternames;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.LocalVariableTable;
+import com.android.tools.r8.utils.codeinspector.LocalVariableTable.LocalVariableTableEntry;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class KeepParameterNamesUnsortedLocalVariablesTableTest extends TestBase implements Opcodes {
+
+ private final TestParameters parameters;
+ private final boolean keepParameterNames;
+
+ @Parameterized.Parameters(name = "{0}, keepparameternames {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(getTestParameters().withCfRuntimes().build(), BooleanUtils.values());
+ }
+
+ public KeepParameterNamesUnsortedLocalVariablesTableTest(
+ TestParameters parameters, boolean keepParameterNames) {
+ this.parameters = parameters;
+ this.keepParameterNames = keepParameterNames;
+ }
+
+ private void checkLocalVariable(
+ LocalVariableTableEntry localVariable,
+ int index,
+ String name,
+ TypeSubject type,
+ String signature) {
+ assertEquals(index, localVariable.index);
+ assertEquals(name, localVariable.name);
+ assertEquals(type, localVariable.type);
+ assertEquals(signature, localVariable.signature);
+ }
+
+ private void checkLocalVariableTable(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz("Api");
+ assertThat(classSubject, isPresent());
+
+ MethodSubject method = classSubject.uniqueMethodWithName("api1");
+ assertThat(method, isPresent());
+
+ assertEquals(keepParameterNames, method.hasLocalVariableTable());
+ if (keepParameterNames) {
+ LocalVariableTable localVariableTable = method.getLocalVariableTable();
+ assertEquals(3, localVariableTable.size());
+ checkLocalVariable(
+ localVariableTable.get(0),
+ 0,
+ "this",
+ classSubject.asFoundClassSubject().asTypeSybject(),
+ null);
+ checkLocalVariable(
+ localVariableTable.get(1), 1, "parameter1", inspector.getTypeSubject("int"), null);
+ checkLocalVariable(
+ localVariableTable.get(2), 2, "parameter2", inspector.getTypeSubject("int"), null);
+ } else {
+ method.getLocalVariableTable().isEmpty();
+ }
+ }
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput = StringUtils.lines("In Api.api1 6", "Result 6");
+ testForR8(parameters.getBackend())
+ .addInnerClasses(KeepParameterNamesUnsortedLocalVariablesTableTest.class)
+ .addProgramClassFileData(dumpApi())
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules("-keep class Api { api*(...); }")
+ .apply(this::configureKeepParameterNames)
+ .compile()
+ .inspect(this::checkLocalVariableTable)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ private void configureKeepParameterNames(TestShrinkerBuilder builder) {
+ if (keepParameterNames) {
+ builder.addKeepRules("-keepparameternames");
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) throws Exception {
+ Class<?> api = Class.forName("Api");
+ Method api1 = api.getMethod("api1", int.class, int.class);
+ System.out.println("Result " + api1.invoke(api.getConstructor().newInstance(), 2, 3));
+ }
+ }
+
+ /*
+
+ Dump below is a modified version of this java code:
+
+ public class Api {
+ public int api1(int parameter1, int parameter2) {
+ int x = parameter1 * parameter2;
+ System.out.println("In Api.api1 " + x);
+ return x;
+ }
+ }
+
+ Modifications:
+ LabelX introduced to stop parameter1 and parameter2.
+ The local variable x stored into slot 1 from LabelX (using parameter1's slot)
+ The order of the local variable table changed to have x before parameter1.
+ */
+
+ public static byte[] dumpApi() throws Exception {
+
+ ClassWriter classWriter = new ClassWriter(0);
+ MethodVisitor methodVisitor;
+
+ classWriter.visit(V1_8, ACC_SUPER | ACC_PUBLIC, "Api", null, "java/lang/Object", null);
+
+ classWriter.visitSource("Api.java", null);
+
+ {
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(184, label0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ methodVisitor.visitInsn(RETURN);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLocalVariable("this", "LApi;", null, label0, label1, 0);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "api1", "(II)I", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(186, label0);
+ methodVisitor.visitVarInsn(ILOAD, 1);
+ methodVisitor.visitVarInsn(ILOAD, 2);
+ methodVisitor.visitInsn(IMUL);
+ Label labelX = new Label();
+ methodVisitor.visitLabel(labelX);
+ methodVisitor.visitVarInsn(ISTORE, 1);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLineNumber(187, label1);
+ methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
+ methodVisitor.visitLdcInsn("In Api.api1 ");
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/StringBuilder",
+ "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ false);
+ methodVisitor.visitVarInsn(ILOAD, 1);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/StringBuilder",
+ "append",
+ "(I)Ljava/lang/StringBuilder;",
+ false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+ Label label2 = new Label();
+ methodVisitor.visitLabel(label2);
+ methodVisitor.visitLineNumber(188, label2);
+ methodVisitor.visitVarInsn(ILOAD, 1);
+ methodVisitor.visitInsn(IRETURN);
+ Label label3 = new Label();
+ methodVisitor.visitLabel(label3);
+ methodVisitor.visitLocalVariable("this", "LApi;", null, label0, label3, 0);
+ methodVisitor.visitLocalVariable("x", "I", null, labelX, label3, 1);
+ methodVisitor.visitLocalVariable("parameter1", "I", null, label0, labelX, 1);
+ methodVisitor.visitLocalVariable("parameter2", "I", null, label0, labelX, 2);
+ methodVisitor.visitMaxs(3, 3);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+}