WALA/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/Compiler.java

1869 lines
59 KiB
Java

/*******************************************************************************
* 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<>();
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<>();
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<>();
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<>();
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<>();
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<>();
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;
}
}
}