/******************************************************************************* * Copyright (c) 2002,2006 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package com.ibm.wala.shrikeBT; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Iterator; import com.ibm.wala.shrikeBT.ConstantInstruction.ClassToken; import com.ibm.wala.shrikeBT.IBinaryOpInstruction.Operator; import com.ibm.wala.shrikeBT.analysis.ClassHierarchyProvider; import com.ibm.wala.shrikeBT.analysis.Verifier; import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken; /** * This class generates Java bytecode from ShrikeBT Instructions. * * If there are too many instructions to fit into 64K bytecodes, then we break the method up, generating auxiliary methods called by * the main method. * * This class is abstract; there are subclasses for specific class file access toolkits. These toolkits are responsible for * providing ways to allocate constant pool entries. */ public abstract class Compiler implements Constants { // input final private boolean isConstructor; final private boolean isStatic; final private String classType; final private String signature; private final IInstruction[] instructions; final private ExceptionHandler[][] handlers; final private int[] instructionsToBytecodes; private static final int[] noRawHandlers = new int[0]; private ClassHierarchyProvider hierarchy; private ConstantPoolReader presetConstants; // working private int[] instructionsToOffsets; private BitSet branchTargets; private byte[][] stackWords; private byte[] code; // working on breaking up overlarge methods private int allocatedLocals; private BitSet[] liveLocals; private int[][] backEdges; private String[][] localTypes; private String[][] stackTypes; // output private int maxLocals; private int maxStack; private Output mainMethod; private ArrayList<Output> auxMethods; /** * Initialize a Compiler for the given method data. * * @param isStatic true iff the method is static * @param classType the JVM type of the class the method belongs to * @param signature the JVM signature of the method * @param instructions the ShrikeBT instructions * @param handlers the ShrikeBT exception handlers * @param instructionsToBytecodes the map from instructions to original bytecode offsets * @throws IllegalArgumentException if handlers is null * @throws IllegalArgumentException if instructions is null * @throws IllegalArgumentException if instructionsToBytecodes is null */ public Compiler(boolean isConstructor, boolean isStatic, String classType, String signature, IInstruction[] instructions, ExceptionHandler[][] handlers, int[] instructionsToBytecodes) { if (instructionsToBytecodes == null) { throw new IllegalArgumentException("instructionsToBytecodes is null"); } if (instructions == null) { throw new IllegalArgumentException("instructions is null"); } if (handlers == null) { throw new IllegalArgumentException("handlers is null"); } if (instructions.length != handlers.length) { throw new IllegalArgumentException("Instructions/handlers length mismatch"); } if (instructions.length != instructionsToBytecodes.length) { throw new IllegalArgumentException("Instructions/handlers length mismatch"); } this.isConstructor = isConstructor; this.isStatic = isStatic; this.classType = classType; this.signature = signature; this.instructions = instructions; this.handlers = handlers; this.instructionsToBytecodes = instructionsToBytecodes; } /** * Extract the data for the method to be compiled from the MethodData container. */ protected Compiler(MethodData info) { this(info.getName().equals("<init>"), info.getIsStatic(), info.getClassType(), info.getSignature(), info.getInstructions(), info.getHandlers(), info .getInstructionsToBytecodes()); } /** * @return the JVM type for the class this method belongs to */ final public String getClassType() { return classType; } /** * Notify the compiler that the constants appearing in the ConstantPoolReader cp will appear in the final class file. * * Instructions which were extracted from a class file with the same ConstantPoolReader can be written back much more efficiently * if the same constant pool indices are valid in the new class file. */ final public void setPresetConstants(ConstantPoolReader cp) { presetConstants = cp; } final public void setClassHierarchy(ClassHierarchyProvider h) { this.hierarchy = h; } protected abstract int allocateConstantPoolInteger(int v); protected abstract int allocateConstantPoolFloat(float v); protected abstract int allocateConstantPoolLong(long v); protected abstract int allocateConstantPoolDouble(double v); protected abstract int allocateConstantPoolString(String v); protected abstract int allocateConstantPoolClassType(String c); protected abstract int allocateConstantPoolMethodType(String c); protected abstract int allocateConstantPoolMethodHandle(ReferenceToken c); protected abstract int allocateConstantPoolField(String c, String name, String type); protected abstract int allocateConstantPoolMethod(String c, String name, String sig); protected abstract int allocateConstantPoolInterfaceMethod(String c, String name, String sig); protected abstract String createHelperMethod(boolean isStatic, String sig); private void collectInstructionInfo() { final BitSet s = new BitSet(instructions.length); final BitSet localsUsed = new BitSet(32); final BitSet localsWide = new BitSet(32); IInstruction.Visitor visitor = new IInstruction.Visitor() { private void visitTargets(IInstruction instr) { int[] ts = instr.getBranchTargets(); for (int k = 0; k < ts.length; k++) { s.set(ts[k]); } } @Override public void visitGoto(GotoInstruction instruction) { visitTargets(instruction); } @Override public void visitLocalStore(IStoreInstruction instruction) { localsUsed.set(instruction.getVarIndex()); String t = instruction.getType(); if (t.equals(TYPE_long) || t.equals(TYPE_double)) { localsWide.set(instruction.getVarIndex()); } } @Override public void visitConditionalBranch(IConditionalBranchInstruction instruction) { visitTargets(instruction); } @Override public void visitSwitch(SwitchInstruction instruction) { visitTargets(instruction); } }; for (int i = 0; i < instructions.length; i++) { instructions[i].visit(visitor); } String[] paramTypes = Util.getParamsTypes(isStatic ? null : TYPE_Object, signature); int index = 0; for (int i = 0; i < paramTypes.length; i++) { String t = paramTypes[i]; localsUsed.set(index); if (t.equals(TYPE_long) || t.equals(TYPE_double)) { localsWide.set(index); index += 2; } else { index++; } } ExceptionHandler[] lastHS = null; for (int i = 0; i < handlers.length; i++) { ExceptionHandler[] hs = handlers[i]; if (hs != lastHS) { for (int j = 0; j < hs.length; j++) { s.set(hs[j].handler); } lastHS = hs; } } this.branchTargets = s; int maxUsed = localsUsed.length(); if (maxUsed > 0 && localsWide.get(maxUsed - 1)) { maxUsed++; } this.maxLocals = maxUsed; } private void writeInt(int offset, int v) { code[offset] = (byte) (v >> 24); code[offset + 1] = (byte) (v >> 16); code[offset + 2] = (byte) (v >> 8); code[offset + 3] = (byte) v; } private void writeShort(int offset, int v) { code[offset] = (byte) (v >> 8); code[offset + 1] = (byte) v; } private void writeByte(int offset, int v) { code[offset] = (byte) v; } private boolean inBasicBlock(int i, int n) { if (i + n - 1 >= instructions.length) { return false; } for (int j = i + 1; j < i + n; j++) { if (branchTargets.get(j)) { return false; } if (!Arrays.equals(handlers[j], handlers[i])) { return false; } if (instructionsToBytecodes[j] != instructionsToBytecodes[i]) { return false; } } return true; } private void checkStackWordSize(byte[] stackWords, int stackLen) { if (stackLen * 2 > maxStack) { int words = 0; for (int i = 0; i < stackLen; i++) { words += stackWords[i]; } if (words > maxStack) { maxStack = words; } } } // private static int getStackDelta(Instruction instr) { // if (instr instanceof DupInstruction) { // return ((DupInstruction) instr).getSize(); // } else { // return (instr.getPushedWordSize() > 0 ? 1 : 0) - instr.getPoppedCount(); // } // } private void computeStackWordsAt(int i, int stackLen, byte[] stackWords, boolean[] visited) { while (!visited[i]) { IInstruction instr = instructions[i]; if (i > 0 && !instructions[i - 1].isFallThrough()) { byte[] newWords = new byte[stackLen]; System.arraycopy(stackWords, 0, newWords, 0, stackLen); this.stackWords[i] = newWords; } visited[i] = true; if (stackLen < instr.getPoppedCount()) { throw new IllegalArgumentException("Stack underflow in intermediate code, at offset " + i); } if (instr instanceof DupInstruction) { DupInstruction d = (DupInstruction) instr; int size = d.getSize(); int delta = d.getDelta(); System.arraycopy(stackWords, stackLen - size - delta, stackWords, stackLen - delta, delta + size); System.arraycopy(stackWords, stackLen, stackWords, stackLen - size - delta, size); stackLen += size; checkStackWordSize(stackWords, stackLen); } else if (instr instanceof SwapInstruction) { // we may have to emulate this using a dup[2]_x[1,2] // followed by a pop[2]. So update the maxStack to account for the // temporarily larger stack size int words = stackWords[stackLen - 1]; for (int j = 0; j < stackLen; j++) { words += stackWords[j]; } if (words > maxStack) { maxStack = words; } byte b = stackWords[stackLen - 2]; stackWords[stackLen - 2] = stackWords[stackLen - 1]; stackWords[stackLen - 1] = b; } else { stackLen -= instr.getPoppedCount(); byte w = instr.getPushedWordSize(); if (w > 0) { stackWords[stackLen] = w; stackLen++; checkStackWordSize(stackWords, stackLen); } } int[] bt = instr.getBranchTargets(); for (int j = 0; j < bt.length; j++) { int t = bt[j]; if (t < 0 || t >= visited.length) { throw new IllegalArgumentException("Branch target at offset " + i + " is out of bounds: " + t + " (max " + visited.length + ")"); } if (!visited[t]) { computeStackWordsAt(bt[j], stackLen, stackWords.clone(), visited); } } ExceptionHandler[] hs = handlers[i]; for (int j = 0; j < hs.length; j++) { int t = hs[j].handler; if (!visited[t]) { byte[] newWords = stackWords.clone(); newWords[0] = 1; computeStackWordsAt(t, 1, newWords, visited); } } if (!instr.isFallThrough()) { return; } i++; } } private void computeStackWords() { stackWords = new byte[instructions.length][]; maxStack = 0; computeStackWordsAt(0, 0, new byte[instructions.length * 2], new boolean[instructions.length]); } abstract class Patch { final int instrStart; final int instrOffset; final int targetLabel; Patch(int instrStart, int instrOffset, int targetLabel) { this.instrStart = instrStart; this.instrOffset = instrOffset; this.targetLabel = targetLabel; } abstract boolean apply(); } class ShortPatch extends Patch { ShortPatch(int instrStart, int instrOffset, int targetLabel) { super(instrStart, instrOffset, targetLabel); } @Override boolean apply() { int delta = instructionsToOffsets[targetLabel] - instrStart; if ((short) delta == delta) { writeShort(instrOffset, delta); return true; } else { return false; } } } class IntPatch extends Patch { IntPatch(int instrStart, int instrOffset, int targetLabel) { super(instrStart, instrOffset, targetLabel); } @Override boolean apply() { writeInt(instrOffset, instructionsToOffsets[targetLabel] - instrStart); return true; } } private void insertBranchOffsetInt(ArrayList<Patch> patches, int instrStart, int instrOffset, int targetLabel) { if (instructionsToOffsets[targetLabel] > 0 || targetLabel == 0) { writeInt(instrOffset, instructionsToOffsets[targetLabel] - instrStart); } else { patches.add(new IntPatch(instrStart, instrOffset, targetLabel)); } } private boolean applyPatches(ArrayList<Patch> patches) { for (Iterator<Patch> i = patches.iterator(); i.hasNext();) { Patch p = i.next(); if (!p.apply()) { return false; } } return true; } private static byte[] cachedBuf; private static synchronized byte[] makeCodeBuf() { if (cachedBuf != null) { byte[] result = cachedBuf; cachedBuf = null; return result; } else { return new byte[65535]; } } private static synchronized void releaseCodeBuf(byte[] buf) { cachedBuf = buf; } private boolean outputInstructions(int startInstruction, int endInstruction, int startOffset, boolean farBranches, byte[] initialStack) { instructionsToOffsets = new int[instructions.length]; code = makeCodeBuf(); ArrayList<Patch> patches = new ArrayList<Patch>(); int curOffset = startOffset; final int[] curOffsetRef = new int[1]; int stackLen = initialStack == null ? 0 : initialStack.length; final int[] stackLenRef = new int[1]; final byte[] stackWords = new byte[maxStack]; if (stackLen > 0) { System.arraycopy(initialStack, 0, stackWords, 0, stackLen); } final int[] instrRef = new int[1]; IInstruction.Visitor noOpcodeHandler = new IInstruction.Visitor() { @Override public void visitPop(PopInstruction instruction) { int count = instruction.getPoppedCount(); int offset = curOffsetRef[0]; int stackLen = stackLenRef[0]; while (count > 0) { code[offset] = (byte) (stackWords[stackLen - 1] == 1 ? OP_pop : OP_pop2); count--; stackLen--; offset++; } curOffsetRef[0] = offset; } @Override public void visitDup(DupInstruction instruction) { int size = instruction.getSize(); int delta = instruction.getDelta(); int offset = curOffsetRef[0]; int stackLen = stackLenRef[0]; int sizeWords = stackWords[stackLen - 1]; if (size == 2) { sizeWords += stackWords[stackLen - 2]; } int deltaWords = delta == 0 ? 0 : stackWords[stackLen - 1 - size]; if (delta == 2) { deltaWords += stackWords[stackLen - 1 - size - 1]; } if (sizeWords > 2 || deltaWords > 2) { throw new IllegalArgumentException("Invalid dup size"); } code[offset] = (byte) (OP_dup + (3 * (sizeWords - 1)) + deltaWords); offset++; curOffsetRef[0] = offset; } @Override public void visitSwap(SwapInstruction instruction) { int offset = curOffsetRef[0]; int stackLen = stackLenRef[0]; int topSize = stackWords[stackLen - 1]; int nextSize = stackWords[stackLen - 2]; if (topSize == 1 && nextSize == 1) { code[offset] = (byte) OP_swap; offset++; } else { code[offset] = (byte) (OP_dup + (3 * (topSize - 1)) + nextSize); code[offset + 1] = (byte) (topSize == 1 ? OP_pop : OP_pop2); offset += 2; } curOffsetRef[0] = offset; } }; for (int i = startInstruction; i < endInstruction; i++) { Instruction instr = (Instruction) instructions[i]; int opcode = instr.getOpcode(); int startI = i; instructionsToOffsets[i] = curOffset; if (opcode != -1) { boolean fallToConditional = false; code[curOffset] = (byte) opcode; curOffset++; switch (opcode) { case OP_iconst_0: if (inBasicBlock(i, 2) && instructions[i + 1] instanceof ConditionalBranchInstruction) { ConditionalBranchInstruction cbr = (ConditionalBranchInstruction) instructions[i + 1]; if (cbr.getType().equals(TYPE_int)) { code[curOffset - 1] = (byte) (cbr.getOperator().ordinal() + OP_ifeq); fallToConditional = true; i++; instr = (Instruction) instructions[i]; } } if (!fallToConditional) { break; } case OP_aconst_null: if (!fallToConditional && inBasicBlock(i, 2) && instructions[i + 1] instanceof ConditionalBranchInstruction) { ConditionalBranchInstruction cbr = (ConditionalBranchInstruction) instructions[i + 1]; if (cbr.getType().equals(TYPE_Object)) { code[curOffset - 1] = (byte) (cbr.getOperator().ordinal() + OP_ifnull); fallToConditional = true; i++; instr = (Instruction) instructions[i]; } } if (!fallToConditional) { break; } // by Xiangyu case OP_ifeq: case OP_ifge: case OP_ifgt: case OP_ifle: case OP_iflt: case OP_ifne: { int targetI = instr.getBranchTargets()[0]; boolean invert = false; int iStart = curOffset - 1; if (inBasicBlock(i, 2) && instr.getBranchTargets()[0] == i + 2 && instructions[i + 1] instanceof GotoInstruction) { invert = true; targetI = instructions[i + 1].getBranchTargets()[0]; i++; } if (targetI <= i) { int delta = instructionsToOffsets[targetI] - iStart; if ((short) delta != delta) { // emit "if_!XX TMP; goto_w L; TMP:" invert = !invert; writeShort(curOffset, 8); code[curOffset + 2] = (byte) OP_goto_w; writeInt(curOffset + 3, delta - 3); curOffset += 7; } else { writeShort(curOffset, (short) delta); curOffset += 2; } } else { Patch p; if (farBranches) { // emit "if_!XX TMP; goto_w L; TMP:" invert = !invert; writeShort(curOffset, 8); code[curOffset + 2] = (byte) OP_goto_w; p = new IntPatch(curOffset + 2, curOffset + 3, targetI); curOffset += 7; } else { p = new ShortPatch(iStart, curOffset, targetI); curOffset += 2; } patches.add(p); } if (invert) { code[iStart] = (byte) (((code[iStart] - OP_ifeq) ^ 1) + OP_ifeq); } break; } // by Xiangyu case OP_if_icmpeq: case OP_if_icmpge: case OP_if_icmpgt: case OP_if_icmple: case OP_if_icmplt: case OP_if_icmpne: case OP_if_acmpeq: case OP_if_acmpne: { int targetI = instr.getBranchTargets()[0]; boolean invert = false; int iStart = curOffset - 1; if (inBasicBlock(i, 2) && instr.getBranchTargets()[0] == i + 2 && instructions[i + 1] instanceof GotoInstruction) { invert = true; targetI = instructions[i + 1].getBranchTargets()[0]; i++; } if (targetI <= i) { int delta = instructionsToOffsets[targetI] - iStart; if ((short) delta != delta) { // emit "if_!XX TMP; goto_w L; TMP:" invert = !invert; writeShort(curOffset, 8); code[curOffset + 2] = (byte) OP_goto_w; writeInt(curOffset + 3, delta - 3); curOffset += 7; } else { writeShort(curOffset, (short) delta); curOffset += 2; } } else { Patch p; if (farBranches) { // emit "if_!XX TMP; goto_w L; TMP:" invert = !invert; writeShort(curOffset, 8); code[curOffset + 2] = (byte) OP_goto_w; p = new IntPatch(curOffset + 2, curOffset + 3, targetI); curOffset += 7; } else { p = new ShortPatch(iStart, curOffset, targetI); curOffset += 2; } patches.add(p); } if (invert) { code[iStart] = (byte) (((code[iStart] - OP_if_icmpeq) ^ 1) + OP_if_icmpeq); } break; } case OP_bipush: writeByte(curOffset, ((ConstantInstruction.ConstInt) instr).getIntValue()); curOffset++; break; case OP_sipush: writeShort(curOffset, ((ConstantInstruction.ConstInt) instr).getIntValue()); curOffset += 2; break; case OP_ldc_w: { int cpIndex; ConstantInstruction ci = (ConstantInstruction) instr; if (presetConstants != null && ci.getLazyConstantPool() == presetConstants) { cpIndex = ci.getCPIndex(); } else { String t = instr.getPushedType(null); if (t.equals(TYPE_int)) { cpIndex = allocateConstantPoolInteger(((ConstantInstruction.ConstInt) instr).getIntValue()); } else if (t.equals(TYPE_String)) { cpIndex = allocateConstantPoolString((String) ((ConstantInstruction.ConstString) instr).getValue()); } else if (t.equals(TYPE_Class)) { cpIndex = allocateConstantPoolClassType(((ClassToken) ((ConstantInstruction.ConstClass) instr).getValue()).getTypeName()); } else if (t.equals(TYPE_MethodType)) { cpIndex = allocateConstantPoolMethodType(((String) ((ConstantInstruction.ConstMethodType) instr).getValue())); } else if (t.equals(TYPE_MethodHandle)) { cpIndex = allocateConstantPoolMethodHandle(((ReferenceToken) ((ConstantInstruction.ConstMethodHandle) instr).getValue())); } else { cpIndex = allocateConstantPoolFloat(((ConstantInstruction.ConstFloat) instr).getFloatValue()); } } if (cpIndex < 256) { code[curOffset - 1] = (byte) OP_ldc; code[curOffset] = (byte) cpIndex; curOffset++; } else { writeShort(curOffset, cpIndex); curOffset += 2; } break; } case OP_ldc2_w: { int cpIndex; ConstantInstruction ci = (ConstantInstruction) instr; if (presetConstants != null && ci.getLazyConstantPool() == presetConstants) { cpIndex = ci.getCPIndex(); } else { String t = instr.getPushedType(null); if (t.equals(TYPE_long)) { cpIndex = allocateConstantPoolLong(((ConstantInstruction.ConstLong) instr).getLongValue()); } else { cpIndex = allocateConstantPoolDouble(((ConstantInstruction.ConstDouble) instr).getDoubleValue()); } } writeShort(curOffset, cpIndex); curOffset += 2; break; } case OP_iload_0: case OP_iload_1: case OP_iload_2: case OP_iload_3: case OP_iload: { if (inBasicBlock(i, 4)) { // try to generate an OP_iinc if (instructions[i + 1] instanceof ConstantInstruction.ConstInt && instructions[i + 2] instanceof BinaryOpInstruction && instructions[i + 3] instanceof StoreInstruction) { LoadInstruction i0 = (LoadInstruction) instr; ConstantInstruction.ConstInt i1 = (ConstantInstruction.ConstInt) instructions[i + 1]; BinaryOpInstruction i2 = (BinaryOpInstruction) instructions[i + 2]; StoreInstruction i3 = (StoreInstruction) instructions[i + 3]; int c = i1.getIntValue(); int v = i0.getVarIndex(); BinaryOpInstruction.Operator op = i2.getOperator(); if ((short) c == c && i3.getVarIndex() == v && (op == Operator.ADD || op == Operator.SUB) && i2.getType().equals(TYPE_int) && i3.getType().equals(TYPE_int)) { if (v < 256 && (byte) c == c) { code[curOffset - 1] = (byte) OP_iinc; writeByte(curOffset, v); writeByte(curOffset + 1, c); curOffset += 2; } else { code[curOffset - 1] = (byte) OP_wide; code[curOffset] = (byte) OP_iinc; writeShort(curOffset + 1, v); writeShort(curOffset + 3, c); curOffset += 5; } instructionsToOffsets[i + 1] = -1; instructionsToOffsets[i + 2] = -1; instructionsToOffsets[i + 3] = -1; i += 3; break; } } } if (opcode != OP_iload) { break; } } case OP_lload: case OP_fload: case OP_dload: case OP_aload: { int v = ((LoadInstruction) instr).getVarIndex(); if (v < 256) { writeByte(curOffset, v); curOffset++; } else { code[curOffset - 1] = (byte) OP_wide; code[curOffset] = (byte) opcode; writeShort(curOffset + 1, v); curOffset += 3; } break; } case OP_istore: case OP_lstore: case OP_fstore: case OP_dstore: case OP_astore: { int v = ((StoreInstruction) instr).getVarIndex(); if (v < 256) { writeByte(curOffset, v); curOffset++; } else { code[curOffset - 1] = (byte) OP_wide; code[curOffset] = (byte) opcode; writeShort(curOffset + 1, v); curOffset += 3; } break; } case OP_goto: { int targetI = instr.getBranchTargets()[0]; if (targetI <= i) { int delta = instructionsToOffsets[targetI] - (curOffset - 1); if ((short) delta != delta) { code[curOffset - 1] = (byte) OP_goto_w; writeInt(curOffset, delta); curOffset += 4; } else { writeShort(curOffset, (short) delta); curOffset += 2; } } else if (targetI == i + 1) { // ignore noop gotos curOffset--; } else { Patch p; if (farBranches) { code[curOffset - 1] = (byte) OP_goto_w; p = new IntPatch(curOffset - 1, curOffset, instr.getBranchTargets()[0]); curOffset += 4; } else { p = new ShortPatch(curOffset - 1, curOffset, instr.getBranchTargets()[0]); curOffset += 2; } patches.add(p); } break; } case OP_lookupswitch: { int start = curOffset - 1; SwitchInstruction sw = (SwitchInstruction) instr; int[] casesAndLabels = sw.getCasesAndLabels(); while ((curOffset & 3) != 0) { writeByte(curOffset, 0); curOffset++; } if (curOffset + 4 * casesAndLabels.length + 8 > code.length) { return false; } insertBranchOffsetInt(patches, start, curOffset, sw.getDefaultLabel()); writeInt(curOffset + 4, casesAndLabels.length / 2); curOffset += 8; for (int j = 0; j < casesAndLabels.length; j += 2) { writeInt(curOffset, casesAndLabels[j]); insertBranchOffsetInt(patches, start, curOffset + 4, casesAndLabels[j + 1]); curOffset += 8; } break; } case OP_tableswitch: { int start = curOffset - 1; SwitchInstruction sw = (SwitchInstruction) instr; int[] casesAndLabels = sw.getCasesAndLabels(); while ((curOffset & 3) != 0) { writeByte(curOffset, 0); curOffset++; } if (curOffset + 2 * casesAndLabels.length + 12 > code.length) { return false; } insertBranchOffsetInt(patches, start, curOffset, sw.getDefaultLabel()); writeInt(curOffset + 4, casesAndLabels[0]); writeInt(curOffset + 8, casesAndLabels[casesAndLabels.length - 2]); curOffset += 12; for (int j = 0; j < casesAndLabels.length; j += 2) { insertBranchOffsetInt(patches, start, curOffset, casesAndLabels[j + 1]); curOffset += 4; } break; } case OP_getfield: case OP_getstatic: { GetInstruction g = (GetInstruction) instr; int cpIndex; if (presetConstants != null && presetConstants == g.getLazyConstantPool()) { cpIndex = ((GetInstruction.Lazy) g).getCPIndex(); } else { cpIndex = allocateConstantPoolField(g.getClassType(), g.getFieldName(), g.getFieldType()); } writeShort(curOffset, cpIndex); curOffset += 2; break; } case OP_putfield: case OP_putstatic: { PutInstruction p = (PutInstruction) instr; int cpIndex; if (presetConstants != null && presetConstants == p.getLazyConstantPool()) { cpIndex = ((PutInstruction.Lazy) p).getCPIndex(); } else { cpIndex = allocateConstantPoolField(p.getClassType(), p.getFieldName(), p.getFieldType()); } writeShort(curOffset, cpIndex); curOffset += 2; break; } case OP_invokespecial: case OP_invokestatic: case OP_invokevirtual: { InvokeInstruction inv = (InvokeInstruction) instr; int cpIndex; if (presetConstants != null && presetConstants == inv.getLazyConstantPool()) { cpIndex = ((InvokeInstruction.Lazy) inv).getCPIndex(); } else { cpIndex = allocateConstantPoolMethod(inv.getClassType(), inv.getMethodName(), inv.getMethodSignature()); } writeShort(curOffset, cpIndex); curOffset += 2; break; } case OP_invokedynamic: { InvokeDynamicInstruction inv = (InvokeDynamicInstruction) instr; String sig = inv.getMethodSignature(); int cpIndex; if (presetConstants != null && presetConstants == inv.getLazyConstantPool()) { cpIndex = ((InvokeDynamicInstruction.Lazy) inv).getCPIndex(); } else { cpIndex = allocateConstantPoolInterfaceMethod(inv.getClassType(), inv.getMethodName(), sig); } writeShort(curOffset, cpIndex); code[curOffset + 2] = 0; code[curOffset + 3] = 0; curOffset += 4; break; } case OP_invokeinterface: { InvokeInstruction inv = (InvokeInstruction) instr; String sig = inv.getMethodSignature(); int cpIndex; if (presetConstants != null && presetConstants == inv.getLazyConstantPool()) { cpIndex = ((InvokeInstruction.Lazy) inv).getCPIndex(); } else { cpIndex = allocateConstantPoolInterfaceMethod(inv.getClassType(), inv.getMethodName(), sig); } writeShort(curOffset, cpIndex); code[curOffset + 2] = (byte) (Util.getParamsWordSize(sig) + 1); code[curOffset + 3] = 0; curOffset += 4; break; } case OP_new: writeShort(curOffset, allocateConstantPoolClassType(((NewInstruction) instr).getType())); curOffset += 2; break; case OP_newarray: code[curOffset] = indexedTypes_T[Util.getTypeIndex(((NewInstruction) instr).getType().substring(1))]; curOffset++; break; case OP_anewarray: writeShort(curOffset, allocateConstantPoolClassType(((NewInstruction) instr).getType().substring(1))); curOffset += 2; break; case OP_multianewarray: { NewInstruction n = (NewInstruction) instr; writeShort(curOffset, allocateConstantPoolClassType(n.getType())); code[curOffset + 2] = (byte) n.getArrayBoundsCount(); curOffset += 3; break; } case OP_checkcast: writeShort(curOffset, allocateConstantPoolClassType(((CheckCastInstruction) instr).getTypes()[0])); curOffset += 2; break; case OP_instanceof: writeShort(curOffset, allocateConstantPoolClassType(((InstanceofInstruction) instr).getType())); curOffset += 2; break; } } else { stackLenRef[0] = stackLen; curOffsetRef[0] = curOffset; instrRef[0] = i; instr.visit(noOpcodeHandler); curOffset = curOffsetRef[0]; i = instrRef[0]; } boolean haveStack = true; while (startI <= i) { instr = (Instruction) instructions[startI]; if (instr.isFallThrough() && haveStack) { if (stackLen < instr.getPoppedCount()) { throw new IllegalArgumentException("Stack underflow in intermediate code, at offset " + startI); } if (instr instanceof DupInstruction) { DupInstruction d = (DupInstruction) instr; int size = d.getSize(); int delta = d.getDelta(); System.arraycopy(stackWords, stackLen - size - delta, stackWords, stackLen - delta, delta + size); System.arraycopy(stackWords, stackLen, stackWords, stackLen - size - delta, size); stackLen += size; } else if (instr instanceof SwapInstruction) { byte b = stackWords[stackLen - 1]; stackWords[stackLen - 1] = stackWords[stackLen - 2]; stackWords[stackLen - 2] = b; } else { stackLen -= instr.getPoppedCount(); byte w = instr.getPushedWordSize(); if (w > 0) { stackWords[stackLen] = w; stackLen++; } } } else { // No stack, or the instruction doesn't fall through // try to grab the stack state at the start of the next instruction if (startI + 1 < endInstruction) { byte[] s = this.stackWords[startI + 1]; // if the next instruction doesn't have stack info (it's not // reachable), then just ignore it and remember that we don't have // stack info if (s == null) { haveStack = false; } else { stackLen = s.length; System.arraycopy(s, 0, stackWords, 0, stackLen); } } } startI++; } if (curOffset > code.length - 8) { return false; } if (!haveStack) { // skip forward through the unreachable instructions until we find // an instruction for which we know the stack state while (i + 1 < endInstruction) { byte[] s = this.stackWords[i + 1]; if (s != null) { stackLen = s.length; System.arraycopy(s, 0, stackWords, 0, stackLen); break; } i++; } } } if (applyPatches(patches)) { byte[] newCode = new byte[curOffset]; System.arraycopy(code, 0, newCode, 0, curOffset); releaseCodeBuf(code); code = newCode; } else { if (farBranches) { throw new Error("Failed to apply patches even with farBranches on"); } else { return outputInstructions(startInstruction, endInstruction, startOffset, true, initialStack); } } return true; } private int[] buildRawHandlers(int start, int end) { int[] handlerCounts = new int[end - start]; int maxCount = 0; for (int i = start; i < end; i++) { int len = handlers[i].length; handlerCounts[i - start] = len; if (len > maxCount) { maxCount = len; } } if (maxCount == 0) { return noRawHandlers; } else { ArrayList<int[]> rawHandlerList = new ArrayList<int[]>(); for (int i = maxCount; i > 0; i--) { for (int j = start; j < end; j++) { if (handlerCounts[j - start] == i) { int first = j; ExceptionHandler h = handlers[j][handlers[j].length - i]; do { handlerCounts[j - start]--; j++; } while (j < end && handlerCounts[j - start] == i && handlers[j][handlers[j].length - i].equals(h)); if (h.handler >= start && h.handler < end) { rawHandlerList.add(new int[] { instructionsToOffsets[first], j < end ? instructionsToOffsets[j] : code.length, instructionsToOffsets[h.handler], h.catchClass == null ? 0 : allocateConstantPoolClassType(h.catchClass) }); } j--; } } } int[] rawHandlers = new int[4 * rawHandlerList.size()]; int count = 0; for (Iterator<int[]> iter = rawHandlerList.iterator(); iter.hasNext();) { int[] element = iter.next(); System.arraycopy(element, 0, rawHandlers, count, 4); count += 4; } return rawHandlers; } } private int[] buildBytecodeMap(int start, int end) { int[] r = new int[code.length]; for (int i = 0; i < r.length; i++) { r[i] = -1; } for (int i = start; i < end; i++) { int off = instructionsToOffsets[i]; if (off >= 0) { r[off] = instructionsToBytecodes[i]; } } return r; } static class HelperPatch { final int start; final int length; final Instruction[] code; final ExceptionHandler[] handlers; HelperPatch(int start, int length, Instruction[] code, ExceptionHandler[] handlers) { this.start = start; this.length = length; this.code = code; this.handlers = handlers; } } private void addBackEdge(int from, int to) { int[] oldEdges = backEdges[from]; if (oldEdges == null) { backEdges[from] = new int[] { to }; } else if (oldEdges[oldEdges.length - 1] < 0) { int left = 1; int right = oldEdges.length - 1; while (true) { if (right - left < 2) { if (oldEdges[left] < 0) { break; } else { if (oldEdges[right] >= 0) throw new Error("Failed binary search"); left = right; break; } } else { int mid = (left + right) / 2; if (oldEdges[mid] < 0) { right = mid; } else { left = mid + 1; } } } oldEdges[left] = to; } else { int[] newEdges = new int[oldEdges.length * 2]; System.arraycopy(oldEdges, 0, newEdges, 0, oldEdges.length); newEdges[oldEdges.length] = to; for (int i = oldEdges.length + 1; i < newEdges.length; i++) { newEdges[i] = -1; } backEdges[from] = newEdges; } } private void addLiveVar(int instruction, int index) { while (true) { if (liveLocals[instruction].get(index)) { break; } IInstruction instr = instructions[instruction]; if (instr instanceof StoreInstruction && ((StoreInstruction) instr).getVarIndex() == index) { break; } liveLocals[instruction].set(index); int[] back = backEdges[instruction]; if (back != null) { for (int i = 0; i < back.length; i++) { addLiveVar(back[i], index); } } if (instruction > 0 && instructions[instruction - 1].isFallThrough()) { instruction--; } else { break; } } } private void makeLiveLocals() { liveLocals = new BitSet[instructions.length]; backEdges = new int[instructions.length][]; for (int i = 0; i < instructions.length; i++) { IInstruction instr = instructions[i]; int[] targets = instr.getBranchTargets(); for (int j = 0; j < targets.length; j++) { addBackEdge(targets[j], i); } ExceptionHandler[] hs = handlers[i]; for (int j = 0; j < hs.length; j++) { addBackEdge(hs[j].handler, i); } liveLocals[i] = new BitSet(); } for (int i = 0; i < backEdges.length; i++) { int[] back = backEdges[i]; if (back != null && back[back.length - 1] < 0) { int j = back.length; while (back[j - 1] < 0) { j--; } int[] newBack = new int[j]; System.arraycopy(back, 0, newBack, 0, newBack.length); backEdges[i] = newBack; } } for (int i = 0; i < instructions.length; i++) { IInstruction instr = instructions[i]; if (instr instanceof LoadInstruction) { addLiveVar(i, ((LoadInstruction) instr).getVarIndex()); } } } private String getAndCheckLocalType(int i, int l) { String[] lts = localTypes[i]; String t = TYPE_unknown; if (l < lts.length) { t = lts[l]; } if (t.equals(TYPE_null) || t.equals(TYPE_unknown)) { throw new IllegalArgumentException("Cannot split oversized method because local " + l + " is undefined at " + i); } return t; } private void allocateLocals(int count) { if (maxLocals < allocatedLocals + count * 2) { maxLocals = allocatedLocals + count * 2; } } private HelperPatch makeHelperPatch(int start, int len, int retVar, int unreadStack, int untouchedStack) { String retType = retVar >= 0 ? getAndCheckLocalType(start + len, retVar) : "V"; ArrayList<Instruction> callWrapper = new ArrayList<Instruction>(); int curStackLen = stackTypes[start].length; StringBuffer sigBuf = new StringBuffer(); sigBuf.append("("); // spill needed stack variables to allocated locals; allocateLocals(curStackLen - unreadStack); for (int i = curStackLen - 1; i >= unreadStack; i--) { if (i < untouchedStack) { callWrapper.add(DupInstruction.make(0)); } callWrapper.add(StoreInstruction.make(stackTypes[start][i], allocatedLocals + 2 * (i - unreadStack))); } // push needed locals BitSet liveVars = liveLocals[start]; for (int i = 0; i < liveVars.length(); i++) { if (liveVars.get(i)) { String t = getAndCheckLocalType(start, i); sigBuf.append(t); callWrapper.add(LoadInstruction.make(t, i)); if (Util.getWordSize(t) > 1) { i++; } } else { // dummy sigBuf.append("I"); callWrapper.add(ConstantInstruction.make(0)); } } // push stack variables for (int i = unreadStack; i < curStackLen; i++) { callWrapper.add(LoadInstruction.make(stackTypes[start][i], allocatedLocals + 2 * (i - unreadStack))); sigBuf.append(stackTypes[start][i]); if (Util.getWordSize(stackTypes[start][i]) == 2) { sigBuf.append("I"); callWrapper.add(ConstantInstruction.make(0)); } } sigBuf.append(")"); sigBuf.append(retType); String sig = sigBuf.toString(); String name = createHelperMethod(true, sig); callWrapper.add(InvokeInstruction.make(sig, classType, name, IInvokeInstruction.Dispatch.STATIC)); int savedMaxStack = maxStack; maxStack += curStackLen - unreadStack; int prefixLength = 4 * (curStackLen - unreadStack); byte[] initialStack = new byte[curStackLen - unreadStack]; for (int i = 0; i < initialStack.length; i++) { initialStack[i] = Util.getWordSize(stackTypes[start][unreadStack + i]); } if (!outputInstructions(start, start + len, prefixLength, false, initialStack)) { throw new Error("Helper function is overlarge"); } byte[] newCode = new byte[code.length + (retVar >= 0 ? 5 : 1)]; for (int i = 0; i < curStackLen - unreadStack; i++) { int local = allocatedLocals + i * 2; newCode[i * 4] = (byte) OP_wide; newCode[i * 4 + 1] = (byte) LoadInstruction.make(stackTypes[start][i + unreadStack], 500).getOpcode(); newCode[i * 4 + 2] = (byte) (local >> 8); newCode[i * 4 + 3] = (byte) local; } System.arraycopy(code, prefixLength, newCode, prefixLength, code.length - prefixLength); int suffixOffset = code.length; if (retVar >= 0) { newCode[suffixOffset] = (byte) OP_wide; newCode[suffixOffset + 1] = (byte) LoadInstruction.make(retType, 500).getOpcode(); newCode[suffixOffset + 2] = (byte) (retVar >> 8); newCode[suffixOffset + 3] = (byte) retVar; newCode[suffixOffset + 4] = (byte) ReturnInstruction.make(retType).getOpcode(); callWrapper.add(StoreInstruction.make(retType, retVar)); } else { newCode[suffixOffset] = (byte) ReturnInstruction.make(TYPE_void).getOpcode(); } if (callWrapper.size() > len) { return null; } int[] rawHandlers = buildRawHandlers(start, start + len); int[] bytecodeMap = buildBytecodeMap(start, start + len); auxMethods.add(new Output(name, sig, newCode, rawHandlers, bytecodeMap, maxLocals, maxStack, true, null)); maxStack = savedMaxStack; Instruction[] patch = new Instruction[callWrapper.size()]; callWrapper.toArray(patch); ExceptionHandler[] startHS = handlers[start]; ArrayList<ExceptionHandler> newHS = new ArrayList<ExceptionHandler>(); for (int i = 0; i < startHS.length; i++) { int t = startHS[i].handler; if (t < start || t >= start + len) { newHS.add(startHS[i]); } } ExceptionHandler[] patchHS = new ExceptionHandler[newHS.size()]; newHS.toArray(patchHS); return new HelperPatch(start, len, patch, patchHS); } private HelperPatch findBlock(int start, int len) { while (len > 100) { // make sure there is at most one entry int lastInvalid = start - 1; for (int i = start + 1; i < start + len; i++) { int[] back = backEdges[i]; boolean outsideBranch = false; for (int j = 0; back != null && j < back.length; j++) { if (back[j] < start || back[j] >= start + len) { outsideBranch = true; } } if (outsideBranch) { HelperPatch p = findBlock(lastInvalid + 1, i - lastInvalid - 1); if (p != null) { return p; } lastInvalid = i; } } if (lastInvalid >= start) { return null; } // make sure there is at most one exit (fall through at the end) if (!instructions[start + len - 1].isFallThrough()) { len--; continue; } lastInvalid = start - 1; for (int i = start; i < start + len; i++) { int[] targets = instructions[i].getBranchTargets(); boolean outsideBranch = false; if (instructions[i] instanceof ReturnInstruction) { outsideBranch = true; } for (int j = 0; j < targets.length; j++) { if (targets[j] < start || targets[j] >= start + len) { outsideBranch = true; } } if (outsideBranch) { HelperPatch p = findBlock(lastInvalid + 1, i - lastInvalid - 1); if (p != null) { return p; } lastInvalid = i; } } if (lastInvalid >= start) { return null; } lastInvalid = start - 1; for (int i = start; i < start + len; i++) { boolean out = false; ExceptionHandler[] hs = handlers[i]; for (int j = 0; j < hs.length; j++) { int h = hs[j].handler; if (h < start || h >= start + len) { out = true; } } int[] targets = instructions[i].getBranchTargets(); for (int j = 0; j < targets.length; j++) { int t = targets[j]; if (t < start || t >= start + len) { out = true; } } if (out) { HelperPatch p = findBlock(lastInvalid + 1, i - lastInvalid - 1); if (p != null) { return p; } lastInvalid = i; } } if (lastInvalid >= start) { return null; } if (stackTypes[start] == null) { while (stackTypes[start] == null && len > 0) { start++; len--; } continue; } // See how many stack elements at entry are still there, unchanged, at // exit int untouchedStack = Integer.MAX_VALUE; // See how many elements of that part of the stack are never even read int unreadStack = Integer.MAX_VALUE; for (int i = start; i < start + len; i++) { if (stackTypes[i] == null) { untouchedStack = 0; unreadStack = 0; break; } int lowWaterMark = stackTypes[i].length - instructions[i].getPoppedCount(); unreadStack = Math.min(unreadStack, lowWaterMark); if (instructions[i] instanceof DupInstruction) { // dup instructions don't actually pop off/change the element they // duplicate lowWaterMark += instructions[i].getPoppedCount(); } untouchedStack = Math.min(untouchedStack, lowWaterMark); } if (untouchedStack > unreadStack + 1 || (untouchedStack == unreadStack + 1 && untouchedStack < stackTypes[start].length)) { // we can only handle 1 read-but-untouched element start++; len--; continue; } // make sure we know the type of all the stack values that must be passed // in boolean unknownType = false; for (int i = unreadStack; i < untouchedStack; i++) { String t = stackTypes[start][i]; if (t == null || t.equals(TYPE_unknown) || t.equals(TYPE_null)) { unknownType = true; break; } } if (unknownType) { start++; len--; continue; } // make sure outgoing stack size is no more than the untouched stack size. if (stackTypes[start + len] == null || stackTypes[start + len].length > untouchedStack) { // This is a little conservative. We might be able to stop sooner with a // valid // extractable method, because changing 'len' might mean we have more // untouched stack elements. But we'll do this in a dumb way to avoid // being caught in some N^2 loop looking for extractable code. while (len > 0 && (stackTypes[start + len] == null || stackTypes[start + len].length > untouchedStack)) { len--; } continue; } // make sure at most one local is defined and live on exit BitSet liveAtEnd = liveLocals[start + len]; boolean multipleDefs = false; int localDefed = -1; int firstDef = -1; int secondDef = -1; for (int i = start; i < start + len; i++) { IInstruction instr = instructions[i]; if (instr instanceof StoreInstruction) { int l = ((StoreInstruction) instr).getVarIndex(); if (liveAtEnd.get(l) && l != localDefed) { if (localDefed < 0) { localDefed = l; firstDef = i; } else { multipleDefs = true; secondDef = i; break; } } } } if (multipleDefs) { HelperPatch p = findBlock(start, secondDef - start); if (p != null) { return p; } len = (start + len) - (firstDef + 1); start = firstDef + 1; continue; } // make sure that the same external handlers are used all the way through ExceptionHandler[] startHS = handlers[start]; int numOuts = 0; for (int j = 0; j < startHS.length; j++) { int t = startHS[j].handler; if (t < start || t >= start + len) { numOuts++; } } boolean mismatchedHandlers = false; int firstMismatch = -1; for (int i = start + 1; i < start + len; i++) { ExceptionHandler[] hs = handlers[i]; int matchingOuts = 0; for (int j = 0; j < hs.length; j++) { int t = hs[j].handler; if (t < start || t >= start + len) { boolean match = false; for (int k = 0; k < startHS.length; k++) { if (startHS[k].equals(hs[j])) { match = true; break; } } if (match) { matchingOuts++; } } } if (matchingOuts != numOuts) { firstMismatch = i; mismatchedHandlers = true; break; } } if (mismatchedHandlers) { HelperPatch p = findBlock(start, firstMismatch - start); if (p != null) { return p; } start = firstMismatch; continue; } // all conditions satisfied, extract the code try { HelperPatch p = makeHelperPatch(start, len, localDefed, unreadStack, untouchedStack); if (p == null) { // something went wrong. Probably the code to call the helper ended up // being // bigger than the original code we extracted! return null; } else { return p; } } catch (IllegalArgumentException ex) { return null; } } return null; } private void makeHelpers() { int offset = 0; ArrayList<HelperPatch> patches = new ArrayList<HelperPatch>(); while (offset + 5000 < instructions.length) { HelperPatch p = findBlock(offset, 5000); if (p != null) { patches.add(p); offset = p.start + p.length; } else { offset += 500; } } for (Iterator<HelperPatch> i = patches.iterator(); i.hasNext();) { HelperPatch p = i.next(); System.arraycopy(p.code, 0, instructions, p.start, p.code.length); for (int j = 0; j < p.length; j++) { int index = j + p.start; if (j < p.code.length) { instructions[index] = p.code[j]; } else { instructions[index] = PopInstruction.make(0); // nop } handlers[index] = p.handlers; instructionsToBytecodes[index] = -1; } } } private void makeTypes() { Verifier v = new Verifier(isConstructor, isStatic, classType, signature, instructions, handlers, instructionsToBytecodes, null); if (hierarchy != null) { v.setClassHierarchy(hierarchy); } try { v.computeTypes(); } catch (Verifier.FailureException ex) { throw new IllegalArgumentException("Cannot split oversized method because verification failed: " + ex.getMessage()); } localTypes = v.getLocalTypes(); stackTypes = v.getStackTypes(); } /** * Do the work of generating new bytecodes. * * In pathological cases this could throw an Error, when the code you passed in is too large to fit into a single JVM method and * Compiler can't find a way to break it up into helper methods. You probably won't encounter this unless you try to make it * happen :-). */ final public void compile() { collectInstructionInfo(); computeStackWords(); if (!outputInstructions(0, instructions.length, 0, false, null)) { allocatedLocals = maxLocals; makeLiveLocals(); makeTypes(); auxMethods = new ArrayList<Output>(); makeHelpers(); computeStackWords(); if (!outputInstructions(0, instructions.length, 0, false, null)) { throw new Error("Input code too large; consider breaking up your code"); } } mainMethod = new Output(null, null, code, buildRawHandlers(0, instructions.length), buildBytecodeMap(0, instructions.length), maxLocals, maxStack, isStatic, instructionsToOffsets); instructionsToOffsets = null; branchTargets = null; stackWords = null; code = null; } /** * Get the output bytecodes and other information for the method. */ final public Output getOutput() { return mainMethod; } /** * Get bytecodes and other information for any helper methods that are required to implement the main method. These helpers * represent code that could not be fit into the main method because of JVM method size constraints. */ final public Output[] getAuxiliaryMethods() { if (auxMethods == null) { return null; } else { Output[] r = new Output[auxMethods.size()]; auxMethods.toArray(r); return r; } } /** * This class represents a method generated by a Compiler. One input method to the Compiler can generate multiple Outputs (if the * input method is too big to be represented by a single method in the JVM, say if it requires more than 64K bytecodes). */ public final static class Output { final private byte[] code; final private int[] rawHandlers; final private int[] newBytecodesToOldBytecodes; final private String name; final private String signature; final private boolean isStatic; final private int maxLocals; final private int maxStack; final private int[] instructionsToOffsets; Output(String name, String signature, byte[] code, int[] rawHandlers, int[] newBytecodesToOldBytecodes, int maxLocals, int maxStack, boolean isStatic, int[] instructionsToOffsets) { this.code = code; this.name = name; this.signature = signature; this.rawHandlers = rawHandlers; this.newBytecodesToOldBytecodes = newBytecodesToOldBytecodes; this.isStatic = isStatic; this.maxLocals = maxLocals; this.maxStack = maxStack; this.instructionsToOffsets = instructionsToOffsets; } /** * @return the actual bytecodes */ public byte[] getCode() { return code; } public int[] getInstructionOffsets() { return instructionsToOffsets; } /** * @return the name of the method; either "null", if this code takes the place of the original method, or some string * representing the name of a helper method */ public String getMethodName() { return name; } /** * @return the method signature in JVM format */ public String getMethodSignature() { return signature; } /** * @return the access flags that should be used for this method, or 0 if this is the code for the original method */ public int getAccessFlags() { return name != null ? (ACC_PRIVATE | (isStatic ? ACC_STATIC : 0)) : 0; } /** * @return the raw exception handler table in JVM format */ public int[] getRawHandlers() { return rawHandlers; } /** * @return whether the method is static */ public boolean isStatic() { return isStatic; } /** * @return a map m such that the new bytecode instruction at offset i corresponds to the bytecode instruction at m[i] in the * original method */ public int[] getNewBytecodesToOldBytecodes() { return newBytecodesToOldBytecodes; } /** * @return the maximum stack size in words as required by the JVM */ public int getMaxStack() { return maxStack; } /** * @return the maximum local variable size in words as required by the JVM */ public int getMaxLocals() { return maxLocals; } } }