You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1010 lines
28 KiB
1010 lines
28 KiB
/* BasicBlockWriter Copyright (C) 1999-2002 Jochen Hoenicke.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; see the file COPYING.LESSER. If not, write to
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
package net.sf.jode.bytecode;
|
|
|
|
import java.io.DataOutputStream;
|
|
import java.io.IOException;
|
|
import java.util.BitSet;
|
|
import java.util.Stack;
|
|
///#def COLLECTIONS java.util
|
|
import java.util.ArrayList;
|
|
///#enddef
|
|
|
|
/**
|
|
* This is a helper class, that contains the method to write basic
|
|
* blocks to a class file.
|
|
*/
|
|
class BasicBlockWriter implements Opcodes {
|
|
|
|
private class LVTEntry {
|
|
int startAddr, endAddr;
|
|
Instruction start, end;
|
|
LocalVariableInfo lvi;
|
|
}
|
|
|
|
BasicBlocks bb;
|
|
int[] blockAddr;
|
|
int[][] instrLength;
|
|
|
|
int lntCount;
|
|
short[] lnt;
|
|
LVTEntry[] lvt;
|
|
|
|
boolean retAtEnd;
|
|
int lastRetAddr;
|
|
|
|
BitSet isRet;
|
|
BitSet isWide;
|
|
BitSet isWideCond;
|
|
|
|
public BasicBlockWriter(BasicBlocks bb, GrowableConstantPool gcp) {
|
|
this.bb = bb;
|
|
init(gcp);
|
|
prepare(gcp);
|
|
}
|
|
|
|
public void buildNewLVT() {
|
|
Block startBlock = bb.getStartBlock();
|
|
Block[] blocks = bb.getBlocks();
|
|
if (startBlock == null)
|
|
return;
|
|
|
|
/* We begin with the first Instruction and follow program flow.
|
|
* We remember which locals are life at start of each block
|
|
* in atStart.
|
|
*/
|
|
LocalVariableInfo[][] atStart =
|
|
new LocalVariableInfo[blocks.length][];
|
|
int startBlockNr = startBlock.getBlockNr();
|
|
atStart[startBlockNr] = new LocalVariableInfo[bb.getMaxLocals()];
|
|
for (int i=0; i < bb.getParamCount(); i++) {
|
|
LocalVariableInfo lvi = bb.getParamInfo(i);
|
|
atStart[startBlockNr][i] = lvi.getName() != null ? lvi : null;
|
|
}
|
|
|
|
/* We currently ignore the jsr/ret issue. Should be okay,
|
|
* though, since it can only generate a bit too much local
|
|
* information. */
|
|
Stack todo = new Stack();
|
|
todo.push(startBlock);
|
|
while (!todo.isEmpty()) {
|
|
Block block = (Block) todo.pop();
|
|
int blockNr = block.getBlockNr();
|
|
LocalVariableInfo[] life
|
|
= (LocalVariableInfo[]) atStart[blockNr].clone();
|
|
Instruction[] instrs = block.getInstructions();
|
|
for (int i = 0; i < instrs.length; i++) {
|
|
if (instrs[i].hasLocal()) {
|
|
LocalVariableInfo lvi = instrs[i].getLocalInfo();
|
|
int slot = lvi.getSlot();
|
|
life[slot] = lvi.getName() != null ? lvi : null;
|
|
}
|
|
}
|
|
Block[] succs = block.getSuccs();
|
|
for (int j = 0; j < succs.length; j++) {
|
|
if (succs[j] == null)
|
|
continue;
|
|
int succNr = succs[j].getBlockNr();
|
|
if (atStart[succNr] == null) {
|
|
atStart[succNr] = (LocalVariableInfo[]) life.clone();
|
|
todo.push(succs[j]);
|
|
} else {
|
|
boolean changed = false;
|
|
for (int k = 0; k < life.length; k++) {
|
|
if (atStart[succNr][k] != life[k]
|
|
&& atStart[succNr][k] != null) {
|
|
atStart[succNr][k] = null;
|
|
changed = true;
|
|
}
|
|
}
|
|
if (changed && !todo.contains(succs[j]))
|
|
todo.push(succs[j]);
|
|
}
|
|
}
|
|
Handler[] handlers = block.getHandlers();
|
|
for (int j = 0; j < handlers.length; j++) {
|
|
int succNr = handlers[j].getCatcher().getBlockNr();
|
|
if (atStart[succNr] == null) {
|
|
atStart[succNr] = (LocalVariableInfo[]) life.clone();
|
|
todo.push(handlers[j].getCatcher());
|
|
} else {
|
|
boolean changed = false;
|
|
for (int k = 0; k < life.length; k++) {
|
|
if (atStart[succNr][k] != life[k]
|
|
&& atStart[succNr][k] != null) {
|
|
atStart[succNr][k] = null;
|
|
changed = true;
|
|
}
|
|
}
|
|
if (changed && !todo.contains(handlers[j].getCatcher()))
|
|
todo.push(handlers[j].getCatcher());
|
|
}
|
|
}
|
|
}
|
|
|
|
ArrayList lvtEntries = new ArrayList();
|
|
|
|
LVTEntry[] current = new LVTEntry[bb.getMaxLocals()];
|
|
for (int slot=0; slot < bb.getParamCount(); slot++) {
|
|
LocalVariableInfo lvi = bb.getParamInfo(slot);
|
|
if (lvi.getName() != null) {
|
|
current[slot] = new LVTEntry();
|
|
current[slot].startAddr = 0;
|
|
current[slot].lvi = lvi;
|
|
System.err.println("lvi at init,"+slot+": "+lvi);
|
|
}
|
|
}
|
|
|
|
for (int i=0; i < blocks.length; i++) {
|
|
if (atStart[i] == null)
|
|
// ignore unreachable blocks:
|
|
continue;
|
|
|
|
Block block = blocks[i];
|
|
int addr = blockAddr[i];
|
|
for (int slot = 0; slot < current.length; slot++) {
|
|
if (current[slot] != null
|
|
&& current[slot].lvi != atStart[i][slot]) {
|
|
current[slot].endAddr = addr;
|
|
lvtEntries.add(current[slot]);
|
|
current[slot] = null;
|
|
}
|
|
if (current[slot] == null && atStart[i][slot] != null) {
|
|
current[slot] = new LVTEntry();
|
|
current[slot].startAddr = addr;
|
|
current[slot].lvi = atStart[i][slot];
|
|
System.err.println("lvi at "+i+","+slot+": "+current[slot].lvi);
|
|
}
|
|
}
|
|
|
|
Instruction[] instrs = block.getInstructions();
|
|
for (int k = 0; k < instrs.length; k++) {
|
|
Instruction instr = instrs[k];
|
|
if (instr.hasLocal()) {
|
|
LocalVariableInfo lvi = instr.getLocalInfo();
|
|
int slot = lvi.getSlot();
|
|
if (current[slot] != null
|
|
&& current[slot].lvi != lvi) {
|
|
current[slot].endAddr = addr;
|
|
lvtEntries.add(current[slot]);
|
|
current[slot] = null;
|
|
}
|
|
if (current[slot] == null
|
|
&& lvi.getName() != null) {
|
|
current[slot] = new LVTEntry();
|
|
current[slot].startAddr = addr;
|
|
current[slot].lvi = lvi;
|
|
System.err.println("lvi at "+i+","+k+","+slot+": "+current[slot].lvi);
|
|
}
|
|
}
|
|
addr += instrLength[i][k];
|
|
}
|
|
}
|
|
|
|
for (int slot = 0; slot < current.length; slot++) {
|
|
if (current[slot] != null) {
|
|
current[slot].endAddr = blockAddr[blockAddr.length - 1];
|
|
lvtEntries.add(current[slot]);
|
|
current[slot] = null;
|
|
}
|
|
}
|
|
if (lvtEntries.size() > 0)
|
|
lvt = (LVTEntry[]) lvtEntries.toArray
|
|
(new LVTEntry[lvtEntries.size()]);
|
|
}
|
|
|
|
public void init(GrowableConstantPool gcp) {
|
|
Block[] blocks = bb.getBlocks();
|
|
blockAddr = new int[blocks.length + 1];
|
|
instrLength = new int[blocks.length + 1][];
|
|
|
|
int[] gotos = new int[blocks.length + 1];
|
|
int[] conds = new int[blocks.length + 1];
|
|
|
|
boolean needRet = false;
|
|
boolean hasRet = false;
|
|
isRet = new BitSet();
|
|
BitSet isJsr = new BitSet();
|
|
|
|
int addr = 0;
|
|
Block startBlock = bb.getStartBlock();
|
|
if (startBlock == null) {
|
|
addr++;
|
|
isRet.set(0);
|
|
hasRet = true;
|
|
gotos[0] = -1;
|
|
} else if (startBlock != blocks[0]) {
|
|
/* reserve 3 byte for a goto at the beginning */
|
|
addr += 3;
|
|
gotos[0] = startBlock.getBlockNr();
|
|
}
|
|
|
|
for (int i = 0; i < blocks.length; i++) {
|
|
boolean hasDefaultSucc = true;
|
|
blockAddr[i] = addr;
|
|
Instruction[] instrs = blocks[i].getInstructions();
|
|
instrLength[i] = new int[instrs.length];
|
|
Block[] succs = blocks[i].getSuccs();
|
|
for (int j = 0; j < instrs.length; j++) {
|
|
Instruction instr = instrs[j];
|
|
if (instr.hasLineNr())
|
|
lntCount++;
|
|
|
|
conds[i+1] = -2;
|
|
int opcode = instr.getOpcode();
|
|
int length;
|
|
switch_opc:
|
|
switch (opcode) {
|
|
case opc_ldc:
|
|
case opc_ldc2_w: {
|
|
Object constant = instr.getConstant();
|
|
if (constant == null) {
|
|
length = 1;
|
|
break switch_opc;
|
|
}
|
|
for (int k = 1; k < constants.length; k++) {
|
|
if (constant.equals(constants[k])) {
|
|
length = 1;
|
|
break switch_opc;
|
|
}
|
|
}
|
|
if (opcode == opc_ldc2_w) {
|
|
gcp.putLongConstant(constant);
|
|
length = 3;
|
|
break switch_opc;
|
|
}
|
|
if (constant instanceof Integer) {
|
|
int value = ((Integer) constant).intValue();
|
|
if (value >= Byte.MIN_VALUE
|
|
&& value <= Byte.MAX_VALUE) {
|
|
length = 2;
|
|
break switch_opc;
|
|
} else if (value >= Short.MIN_VALUE
|
|
&& value <= Short.MAX_VALUE) {
|
|
length = 3;
|
|
break switch_opc;
|
|
}
|
|
}
|
|
if (gcp.putConstant(constant) < 256) {
|
|
length = 2;
|
|
} else {
|
|
length = 3;
|
|
}
|
|
break;
|
|
}
|
|
case opc_iinc: {
|
|
int slot = instr.getLocalSlot();
|
|
int increment = instr.getIncrement();
|
|
if (slot < 256
|
|
&& increment >= Byte.MIN_VALUE
|
|
&& increment <= Byte.MAX_VALUE)
|
|
length = 3;
|
|
else
|
|
length = 6;
|
|
break;
|
|
}
|
|
case opc_iload: case opc_lload:
|
|
case opc_fload: case opc_dload: case opc_aload:
|
|
case opc_istore: case opc_lstore:
|
|
case opc_fstore: case opc_dstore: case opc_astore:
|
|
if (instr.getLocalSlot() < 4) {
|
|
length = 1;
|
|
break;
|
|
}
|
|
if (instr.getLocalSlot() < 256)
|
|
length = 2;
|
|
else
|
|
length = 4;
|
|
break;
|
|
case opc_ret: {
|
|
if (instr.getLocalSlot() < 256)
|
|
length = 2;
|
|
else
|
|
length = 4;
|
|
hasDefaultSucc = false;
|
|
break;
|
|
}
|
|
case opc_lookupswitch: {
|
|
length = (~addr) & 3; /* padding */
|
|
int[] values = instr.getValues();
|
|
int npairs = values.length;
|
|
for (int k=0; k< succs.length; k++) {
|
|
if (succs[k] == null)
|
|
needRet = true;
|
|
}
|
|
if (npairs > 0
|
|
&& 4 + 4 * (values[npairs-1] - values[0] + 1)
|
|
<= 8 * npairs) {
|
|
// Use a table switch
|
|
length += 13 + 4 * (values[npairs-1] - values[0] + 1);
|
|
} else {
|
|
// Use a lookup switch
|
|
length += 9 + 8 * npairs;
|
|
}
|
|
hasDefaultSucc = false;
|
|
break;
|
|
}
|
|
case opc_jsr:
|
|
conds[i+1] = succs[0].getBlockNr();
|
|
length = 3;
|
|
isJsr.set(i+1);
|
|
break;
|
|
case opc_ifeq: case opc_ifne:
|
|
case opc_iflt: case opc_ifge:
|
|
case opc_ifgt: case opc_ifle:
|
|
case opc_if_icmpeq: case opc_if_icmpne:
|
|
case opc_if_icmplt: case opc_if_icmpge:
|
|
case opc_if_icmpgt: case opc_if_icmple:
|
|
case opc_if_acmpeq: case opc_if_acmpne:
|
|
case opc_ifnull: case opc_ifnonnull:
|
|
if (succs[0] == null) {
|
|
needRet = true;
|
|
conds[i+1] = -1;
|
|
} else
|
|
conds[i+1] = succs[0].getBlockNr();
|
|
length = 3;
|
|
break;
|
|
case opc_multianewarray: {
|
|
if (instr.getDimensions() == 1) {
|
|
String clazz = instr.getClazzType().substring(1);
|
|
if (newArrayTypes.indexOf(clazz.charAt(0)) != -1) {
|
|
length = 2;
|
|
} else {
|
|
gcp.putClassType(clazz);
|
|
length = 3;
|
|
}
|
|
} else {
|
|
gcp.putClassType(instr.getClazzType());
|
|
length = 4;
|
|
}
|
|
break;
|
|
}
|
|
case opc_getstatic:
|
|
case opc_getfield:
|
|
case opc_putstatic:
|
|
case opc_putfield:
|
|
gcp.putRef(ConstantPool.FIELDREF, instr.getReference());
|
|
length = 3;
|
|
break;
|
|
case opc_invokespecial:
|
|
case opc_invokestatic:
|
|
case opc_invokevirtual:
|
|
gcp.putRef(ConstantPool.METHODREF, instr.getReference());
|
|
length = 3;
|
|
break;
|
|
case opc_invokeinterface:
|
|
gcp.putRef(ConstantPool.INTERFACEMETHODREF,
|
|
instr.getReference());
|
|
length = 5;
|
|
break;
|
|
case opc_new:
|
|
case opc_checkcast:
|
|
case opc_instanceof:
|
|
gcp.putClassType(instr.getClazzType());
|
|
length = 3;
|
|
break;
|
|
case opc_ireturn: case opc_lreturn:
|
|
case opc_freturn: case opc_dreturn: case opc_areturn:
|
|
case opc_return:
|
|
case opc_athrow:
|
|
length = 1;
|
|
hasDefaultSucc = false;
|
|
break;
|
|
case opc_nop:
|
|
case opc_iaload: case opc_laload: case opc_faload:
|
|
case opc_daload: case opc_aaload:
|
|
case opc_baload: case opc_caload: case opc_saload:
|
|
case opc_iastore: case opc_lastore: case opc_fastore:
|
|
case opc_dastore: case opc_aastore:
|
|
case opc_bastore: case opc_castore: case opc_sastore:
|
|
case opc_pop: case opc_pop2:
|
|
case opc_dup: case opc_dup_x1: case opc_dup_x2:
|
|
case opc_dup2: case opc_dup2_x1: case opc_dup2_x2:
|
|
case opc_swap:
|
|
case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd:
|
|
case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub:
|
|
case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul:
|
|
case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv:
|
|
case opc_irem: case opc_lrem: case opc_frem: case opc_drem:
|
|
case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg:
|
|
case opc_ishl: case opc_lshl:
|
|
case opc_ishr: case opc_lshr:
|
|
case opc_iushr: case opc_lushr:
|
|
case opc_iand: case opc_land:
|
|
case opc_ior: case opc_lor:
|
|
case opc_ixor: case opc_lxor:
|
|
case opc_i2l: case opc_i2f: case opc_i2d:
|
|
case opc_l2i: case opc_l2f: case opc_l2d:
|
|
case opc_f2i: case opc_f2l: case opc_f2d:
|
|
case opc_d2i: case opc_d2l: case opc_d2f:
|
|
case opc_i2b: case opc_i2c: case opc_i2s:
|
|
case opc_lcmp: case opc_fcmpl: case opc_fcmpg:
|
|
case opc_dcmpl: case opc_dcmpg:
|
|
case opc_arraylength:
|
|
case opc_monitorenter: case opc_monitorexit:
|
|
length = 1;
|
|
break;
|
|
default:
|
|
throw new IllegalStateException("Invalid opcode "+opcode);
|
|
}
|
|
instrLength[i][j] = length;
|
|
addr += length;
|
|
}
|
|
if (hasDefaultSucc) {
|
|
Block defaultSucc = succs[succs.length-1];
|
|
if (defaultSucc == null) {
|
|
// This is a return
|
|
gotos[i+1] = -1;
|
|
isRet.set(i+1);
|
|
lastRetAddr = addr;
|
|
hasRet = true;
|
|
addr++;
|
|
} else if (defaultSucc.getBlockNr() == i + 1) {
|
|
// no need for any jump
|
|
gotos[i+1] = succs[succs.length-1].getBlockNr();
|
|
} else {
|
|
// Reserve space for a normal goto.
|
|
gotos[i+1] = succs[succs.length-1].getBlockNr();
|
|
addr += 3;
|
|
}
|
|
} else {
|
|
// No goto needed for this block
|
|
gotos[i+1] = -2;
|
|
}
|
|
}
|
|
if (needRet && !hasRet) {
|
|
retAtEnd = true;
|
|
lastRetAddr = addr;
|
|
addr++;
|
|
}
|
|
blockAddr[blocks.length] = addr;
|
|
|
|
isWide = new BitSet();
|
|
isWideCond = new BitSet();
|
|
// Now check for wide goto/jsr/if, but only if method is big enough
|
|
boolean changed = addr > Short.MAX_VALUE;
|
|
while (changed) {
|
|
changed = false;
|
|
for (int i = 0; i < gotos.length; i++) {
|
|
int gotoNr = gotos[i];
|
|
int condNr = conds[i];
|
|
if (!isWideCond.get(i) && condNr != -2) {
|
|
int from = blockAddr[i] - 3;
|
|
if (gotoNr != i + 1)
|
|
from -= isRet.get(i) ? 1 : isWide.get(i) ? 5 : 3;
|
|
int dist = Integer.MAX_VALUE;
|
|
if (condNr == -1) {
|
|
if (retAtEnd) {
|
|
dist = blockAddr[blockAddr.length-1] - 1 - from;
|
|
} else {
|
|
for (int j = 0; j < gotos.length; j++) {
|
|
if (isRet.get(j)) {
|
|
dist = blockAddr[j] - 1 - from;
|
|
if (dist >= Short.MIN_VALUE
|
|
&& dist <= Short.MAX_VALUE)
|
|
break;
|
|
}
|
|
}
|
|
if (dist == Integer.MAX_VALUE)
|
|
throw new InternalError();
|
|
}
|
|
} else {
|
|
dist = blockAddr[condNr] - from;
|
|
}
|
|
|
|
if (dist < Short.MIN_VALUE || dist > Short.MAX_VALUE) {
|
|
/* We must do the a wide cond:
|
|
* if_!xxx L
|
|
* goto_w condNr
|
|
* L:goto gotoNr
|
|
*/
|
|
isWideCond.set(i);
|
|
int diff = isJsr.get(i) ? 2 : condNr == -1 ? 1 : 5;
|
|
instrLength[i][instrLength[i].length-1] += diff;
|
|
for (int j = i; j < blockAddr.length; j++)
|
|
blockAddr[j] += diff;
|
|
changed = true;
|
|
}
|
|
}
|
|
if (!isWide.get(i) && gotoNr >= 0) {
|
|
int dist = blockAddr[gotoNr] - blockAddr[i] + 3;
|
|
if (dist < Short.MIN_VALUE || dist > Short.MAX_VALUE) {
|
|
/* wide goto, correct addresses */
|
|
isWide.set(i);
|
|
for (int j = i; j < blockAddr.length; j++)
|
|
blockAddr[j] += 2;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
buildNewLVT();
|
|
}
|
|
|
|
public int getSize() {
|
|
/* maxStack: 2
|
|
* maxLocals: 2
|
|
* code: 4 + codeLength
|
|
* exc count: 2
|
|
* exceptions: n * 8
|
|
* attributes:
|
|
* lvt_name: 2
|
|
* lvt_length: 4
|
|
* lvt_count: 2
|
|
* lvt_entries: n * 10
|
|
* attributes:
|
|
* lnt_name: 2
|
|
* lnt_length: 4
|
|
* lnt_count: 2
|
|
* lnt_entries: n * 4
|
|
*/
|
|
int attrsize = 0;
|
|
if (lvt != null)
|
|
attrsize += 8 + lvt.length * 10;
|
|
if (lntCount > 0)
|
|
attrsize += 8 + lntCount * 4;
|
|
return 10
|
|
+ blockAddr[blockAddr.length - 1]
|
|
+ bb.getExceptionHandlers().length * 8
|
|
+ attrsize;
|
|
}
|
|
|
|
protected int getAttributeCount() {
|
|
int count = 0;
|
|
if (lvt != null)
|
|
count++;
|
|
if (lntCount > 0)
|
|
count++;
|
|
return count;
|
|
}
|
|
|
|
public void prepare(GrowableConstantPool gcp) {
|
|
Handler[] handlers = bb.getExceptionHandlers();
|
|
for (int i = 0; i< handlers.length; i++) {
|
|
if (handlers[i].type != null)
|
|
gcp.putClassName(handlers[i].type);
|
|
}
|
|
if (lvt != null) {
|
|
gcp.putUTF8("LocalVariableTable");
|
|
int count = lvt.length;
|
|
for (int i=0; i < count; i++) {
|
|
System.err.println("lvt: "+lvt[i].lvi);
|
|
gcp.putUTF8(lvt[i].lvi.getName());
|
|
gcp.putUTF8(lvt[i].lvi.getType());
|
|
}
|
|
}
|
|
if (lntCount > 0)
|
|
gcp.putUTF8("LineNumberTable");
|
|
}
|
|
|
|
public void writeAttributes(GrowableConstantPool gcp,
|
|
DataOutputStream output)
|
|
throws IOException {
|
|
if (lvt != null) {
|
|
output.writeShort(gcp.putUTF8("LocalVariableTable"));
|
|
int count = lvt.length;
|
|
int length = 2 + 10 * count;
|
|
output.writeInt(length);
|
|
output.writeShort(count);
|
|
for (int i=0; i < count; i++) {
|
|
output.writeShort(lvt[i].startAddr);
|
|
output.writeShort(lvt[i].endAddr);
|
|
output.writeShort(gcp.putUTF8(lvt[i].lvi.getName()));
|
|
output.writeShort(gcp.putUTF8(lvt[i].lvi.getType()));
|
|
output.writeShort(lvt[i].lvi.getSlot());
|
|
}
|
|
}
|
|
if (lntCount > 0) {
|
|
output.writeShort(gcp.putUTF8("LineNumberTable"));
|
|
int length = 2 + 4 * lntCount;
|
|
output.writeInt(length);
|
|
output.writeShort(lntCount);
|
|
for (int i = 0; i < lntCount; i++) {
|
|
output.writeShort(lnt[2*i]);
|
|
output.writeShort(lnt[2*i+1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void write(GrowableConstantPool gcp,
|
|
DataOutputStream output) throws IOException {
|
|
output.writeShort(bb.getMaxStack());
|
|
output.writeShort(bb.getMaxLocals());
|
|
Block[] blocks = bb.getBlocks();
|
|
if (blockAddr[blockAddr.length - 1] > 65535)
|
|
throw new ClassFormatError("Method too long");
|
|
output.writeInt(blockAddr[blockAddr.length-1]);
|
|
lnt = new short[lntCount*2];
|
|
|
|
int addr = 0;
|
|
Block startBlock = bb.getStartBlock();
|
|
if (isRet.get(0)) {
|
|
output.writeByte(opc_return);
|
|
addr ++;
|
|
} else if (isWide.get(0)) {
|
|
output.writeByte(opc_goto_w);
|
|
output.writeInt(blockAddr[startBlock.getBlockNr()]);
|
|
addr += 5;
|
|
} else if (startBlock != blocks[0]) {
|
|
output.writeByte(opc_goto);
|
|
output.writeShort(blockAddr[startBlock.getBlockNr()]);
|
|
addr += 3;
|
|
}
|
|
int lntPtr = 0;
|
|
|
|
for (int i = 0; i< blocks.length; i++) {
|
|
boolean hasDefaultSucc = true;
|
|
Block[] succs = blocks[i].getSuccs();
|
|
if (addr != blockAddr[i])
|
|
throw new InternalError("Address calculation broken for "+i+": "+blockAddr[i]+"!="+addr+"!");
|
|
Instruction[] instructions = blocks[i].getInstructions();
|
|
int size = instructions.length;
|
|
for (int j = 0; j < size; j++) {
|
|
Instruction instr = instructions[j];
|
|
if (instr.hasLineNr()) {
|
|
lnt[lntPtr++] = (short) addr;
|
|
lnt[lntPtr++] = (short) instr.getLineNr();
|
|
}
|
|
int opcode = instr.getOpcode();
|
|
switch_opc:
|
|
switch (opcode) {
|
|
case opc_iload: case opc_lload:
|
|
case opc_fload: case opc_dload: case opc_aload:
|
|
case opc_istore: case opc_lstore:
|
|
case opc_fstore: case opc_dstore: case opc_astore: {
|
|
int slot = instr.getLocalSlot();
|
|
if (slot < 4) {
|
|
if (opcode < opc_istore)
|
|
output.writeByte(opc_iload_0
|
|
+ 4*(opcode-opc_iload)
|
|
+ slot);
|
|
else
|
|
output.writeByte(opc_istore_0
|
|
+ 4*(opcode-opc_istore)
|
|
+ slot);
|
|
} else if (slot < 256) {
|
|
output.writeByte(opcode);
|
|
output.writeByte(slot);
|
|
} else {
|
|
output.writeByte(opc_wide);
|
|
output.writeByte(opcode);
|
|
output.writeShort(slot);
|
|
}
|
|
break;
|
|
}
|
|
case opc_ret: {
|
|
int slot = instr.getLocalSlot();
|
|
if (slot < 256) {
|
|
output.writeByte(opcode);
|
|
output.writeByte(slot);
|
|
} else {
|
|
output.writeByte(opc_wide);
|
|
output.writeByte(opcode);
|
|
output.writeShort(slot);
|
|
}
|
|
hasDefaultSucc = false;
|
|
break;
|
|
}
|
|
case opc_ldc:
|
|
case opc_ldc2_w: {
|
|
Object constant = instr.getConstant();
|
|
if (constant == null) {
|
|
output.writeByte(opc_aconst_null);
|
|
break switch_opc;
|
|
}
|
|
for (int k = 1; k < constants.length; k++) {
|
|
if (constant.equals(constants[k])) {
|
|
output.writeByte(opc_aconst_null + k);
|
|
break switch_opc;
|
|
}
|
|
}
|
|
if (opcode == opc_ldc2_w) {
|
|
output.writeByte(opcode);
|
|
output.writeShort(gcp.putLongConstant(constant));
|
|
} else {
|
|
if (constant instanceof Integer) {
|
|
int value = ((Integer) constant).intValue();
|
|
if (value >= Byte.MIN_VALUE
|
|
&& value <= Byte.MAX_VALUE) {
|
|
|
|
output.writeByte(opc_bipush);
|
|
output.writeByte(((Integer)constant)
|
|
.intValue());
|
|
break switch_opc;
|
|
} else if (value >= Short.MIN_VALUE
|
|
&& value <= Short.MAX_VALUE) {
|
|
output.writeByte(opc_sipush);
|
|
output.writeShort(((Integer)constant)
|
|
.intValue());
|
|
break switch_opc;
|
|
}
|
|
}
|
|
if (instrLength[i][j] == 2) {
|
|
output.writeByte(opc_ldc);
|
|
output.writeByte(gcp.putConstant(constant));
|
|
} else {
|
|
output.writeByte(opc_ldc_w);
|
|
output.writeShort(gcp.putConstant(constant));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case opc_iinc: {
|
|
int slot = instr.getLocalSlot();
|
|
int incr = instr.getIncrement();
|
|
if (instrLength[i][j] == 3) {
|
|
output.writeByte(opcode);
|
|
output.writeByte(slot);
|
|
output.writeByte(incr);
|
|
} else {
|
|
output.writeByte(opc_wide);
|
|
output.writeByte(opcode);
|
|
output.writeShort(slot);
|
|
output.writeShort(incr);
|
|
}
|
|
break;
|
|
}
|
|
case opc_jsr: {
|
|
int dist = blockAddr[succs[0].getBlockNr()] - addr;
|
|
if (isWideCond.get(i+1)) {
|
|
/* wide jsr */
|
|
output.writeByte(opc_jsr_w);
|
|
output.writeInt(dist);
|
|
} else {
|
|
/* wide jsr */
|
|
output.writeByte(opc_jsr);
|
|
output.writeShort(dist);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case opc_ifeq: case opc_ifne:
|
|
case opc_iflt: case opc_ifge:
|
|
case opc_ifgt: case opc_ifle:
|
|
case opc_if_icmpeq: case opc_if_icmpne:
|
|
case opc_if_icmplt: case opc_if_icmpge:
|
|
case opc_if_icmpgt: case opc_if_icmple:
|
|
case opc_if_acmpeq: case opc_if_acmpne:
|
|
case opc_ifnull: case opc_ifnonnull: {
|
|
Block dest = succs[0];
|
|
if (isWideCond.get(i+1)) {
|
|
/* swap condition */
|
|
if (opcode >= opc_ifnull)
|
|
opcode = opcode ^ 1;
|
|
else
|
|
opcode = 1 + ((opcode - 1) ^ 1);
|
|
output.writeByte(opcode);
|
|
if (dest == null) {
|
|
output.writeShort(4);
|
|
output.writeByte(opc_ret);
|
|
} else {
|
|
output.writeShort(8);
|
|
output.writeByte(opc_goto_w);
|
|
int dist = blockAddr[dest.getBlockNr()] - addr;
|
|
output.writeInt(dist);
|
|
}
|
|
} else {
|
|
int dist;
|
|
if (dest == null) {
|
|
if (retAtEnd) {
|
|
dist = blockAddr[blocks.length] - 1 - addr;
|
|
} else {
|
|
for (int k = 0; ; k++) {
|
|
if (isRet.get(k)) {
|
|
dist = blockAddr[k] - 1 - addr;
|
|
if (dist >= Short.MIN_VALUE
|
|
&& dist <= Short.MAX_VALUE)
|
|
break;
|
|
}
|
|
if (k == blocks.length)
|
|
throw new InternalError();
|
|
}
|
|
}
|
|
} else {
|
|
dist = blockAddr[dest.getBlockNr()] - addr;
|
|
}
|
|
output.writeByte(opcode);
|
|
output.writeShort(dist);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case opc_lookupswitch: {
|
|
int align = 3-(addr % 4);
|
|
int[] values = instr.getValues();
|
|
int npairs = values.length;
|
|
Block defBlock = succs[npairs];
|
|
int defAddr = defBlock == null ? lastRetAddr
|
|
: blockAddr[defBlock.getBlockNr()];
|
|
|
|
if (npairs > 0) {
|
|
int tablesize = values[npairs-1] - values[0] + 1;
|
|
if (4 + tablesize * 4 <= 8 * npairs) {
|
|
// Use a table switch
|
|
output.writeByte(opc_tableswitch);
|
|
output.write(new byte[align]);
|
|
/* def */
|
|
output.writeInt(defAddr - addr);
|
|
/* low */
|
|
output.writeInt(values[0]);
|
|
/* high */
|
|
output.writeInt(values[npairs-1]);
|
|
int pos = values[0];
|
|
for (int k = 0; k < npairs; k++) {
|
|
while (pos++ < values[k])
|
|
output.writeInt(defAddr - addr);
|
|
int dest = succs[k] == null ? lastRetAddr
|
|
: blockAddr[succs[k].getBlockNr()];
|
|
output.writeInt(dest - addr);
|
|
}
|
|
hasDefaultSucc = false;
|
|
break;
|
|
}
|
|
}
|
|
// Use a lookup switch
|
|
output.writeByte(opc_lookupswitch);
|
|
output.write(new byte[align]);
|
|
/* def */
|
|
output.writeInt(defAddr - addr);
|
|
output.writeInt(npairs);
|
|
for (int k = 0; k < npairs; k++) {
|
|
output.writeInt(values[k]);
|
|
int dest = succs[k] == null ? lastRetAddr
|
|
: blockAddr[succs[k].getBlockNr()];
|
|
output.writeInt(dest - addr);
|
|
}
|
|
hasDefaultSucc = false;
|
|
break;
|
|
}
|
|
|
|
case opc_getstatic:
|
|
case opc_getfield:
|
|
case opc_putstatic:
|
|
case opc_putfield:
|
|
output.writeByte(opcode);
|
|
output.writeShort(gcp.putRef(ConstantPool.FIELDREF,
|
|
instr.getReference()));
|
|
break;
|
|
|
|
case opc_invokespecial:
|
|
case opc_invokestatic:
|
|
case opc_invokeinterface:
|
|
case opc_invokevirtual: {
|
|
Reference ref = instr.getReference();
|
|
output.writeByte(opcode);
|
|
if (opcode == opc_invokeinterface) {
|
|
output.writeShort
|
|
(gcp.putRef(ConstantPool.INTERFACEMETHODREF, ref));
|
|
output.writeByte
|
|
(TypeSignature
|
|
.getParameterSize(ref.getType()) + 1);
|
|
output.writeByte(0);
|
|
} else
|
|
output.writeShort(gcp.putRef(ConstantPool.METHODREF, ref));
|
|
break;
|
|
}
|
|
case opc_new:
|
|
case opc_checkcast:
|
|
case opc_instanceof:
|
|
output.writeByte(opcode);
|
|
output.writeShort(gcp.putClassType(instr.getClazzType()));
|
|
break;
|
|
case opc_multianewarray:
|
|
if (instr.getDimensions() == 1) {
|
|
String clazz = instr.getClazzType().substring(1);
|
|
int index = newArrayTypes.indexOf(clazz.charAt(0));
|
|
if (index != -1) {
|
|
output.writeByte(opc_newarray);
|
|
output.writeByte(index + 4);
|
|
} else {
|
|
output.writeByte(opc_anewarray);
|
|
output.writeShort(gcp.putClassType(clazz));
|
|
}
|
|
} else {
|
|
output.writeByte(opcode);
|
|
output.writeShort
|
|
(gcp.putClassType(instr.getClazzType()));
|
|
output.writeByte(instr.getDimensions());
|
|
}
|
|
break;
|
|
|
|
case opc_ireturn: case opc_lreturn:
|
|
case opc_freturn: case opc_dreturn: case opc_areturn:
|
|
case opc_athrow: case opc_return:
|
|
output.writeByte(opcode);
|
|
hasDefaultSucc = false;
|
|
break;
|
|
|
|
case opc_nop:
|
|
case opc_iaload: case opc_laload: case opc_faload:
|
|
case opc_daload: case opc_aaload:
|
|
case opc_baload: case opc_caload: case opc_saload:
|
|
case opc_iastore: case opc_lastore: case opc_fastore:
|
|
case opc_dastore: case opc_aastore:
|
|
case opc_bastore: case opc_castore: case opc_sastore:
|
|
case opc_pop: case opc_pop2:
|
|
case opc_dup: case opc_dup_x1: case opc_dup_x2:
|
|
case opc_dup2: case opc_dup2_x1: case opc_dup2_x2:
|
|
case opc_swap:
|
|
case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd:
|
|
case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub:
|
|
case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul:
|
|
case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv:
|
|
case opc_irem: case opc_lrem: case opc_frem: case opc_drem:
|
|
case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg:
|
|
case opc_ishl: case opc_lshl:
|
|
case opc_ishr: case opc_lshr:
|
|
case opc_iushr: case opc_lushr:
|
|
case opc_iand: case opc_land:
|
|
case opc_ior: case opc_lor:
|
|
case opc_ixor: case opc_lxor:
|
|
case opc_i2l: case opc_i2f: case opc_i2d:
|
|
case opc_l2i: case opc_l2f: case opc_l2d:
|
|
case opc_f2i: case opc_f2l: case opc_f2d:
|
|
case opc_d2i: case opc_d2l: case opc_d2f:
|
|
case opc_i2b: case opc_i2c: case opc_i2s:
|
|
case opc_lcmp: case opc_fcmpl: case opc_fcmpg:
|
|
case opc_dcmpl: case opc_dcmpg:
|
|
case opc_arraylength:
|
|
case opc_monitorenter: case opc_monitorexit:
|
|
output.writeByte(opcode);
|
|
break;
|
|
default:
|
|
throw new ClassFormatException("Invalid opcode "+opcode);
|
|
}
|
|
addr += instrLength[i][j];
|
|
}
|
|
if (hasDefaultSucc) {
|
|
// Check which type of goto we should use at end of this block.
|
|
Block defaultSucc = succs[succs.length - 1];
|
|
if (isRet.get(i+1)) {
|
|
output.writeByte(opc_return);
|
|
addr++;
|
|
} else if (isWide.get(i+1)) {
|
|
output.writeByte(opc_goto_w);
|
|
output.writeInt(blockAddr[defaultSucc.getBlockNr()]
|
|
- addr);
|
|
addr+=5;
|
|
} else if (defaultSucc.getBlockNr() != i+1) {
|
|
output.writeByte(opc_goto);
|
|
output.writeShort(blockAddr[defaultSucc.getBlockNr()]
|
|
- addr);
|
|
addr+=3;
|
|
}
|
|
}
|
|
}
|
|
if (retAtEnd) {
|
|
output.writeByte(opc_return);
|
|
addr++;
|
|
}
|
|
if (addr != blockAddr[blocks.length])
|
|
throw new InternalError("Address calculation broken!");
|
|
|
|
Handler[] handlers = bb.getExceptionHandlers();
|
|
output.writeShort(handlers.length);
|
|
for (int i = 0; i< handlers.length; i++) {
|
|
output.writeShort(blockAddr[handlers[i].start.getBlockNr()]);
|
|
output.writeShort(blockAddr[handlers[i].end.getBlockNr()+1]);
|
|
output.writeShort(blockAddr[handlers[i].catcher.getBlockNr()]);
|
|
output.writeShort((handlers[i].type == null) ? 0
|
|
: gcp.putClassName(handlers[i].type));
|
|
}
|
|
}
|
|
}
|
|
|