833 lines
30 KiB
Java
833 lines
30 KiB
Java
/*
|
|
* Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code 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
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package sun.tools.asm;
|
|
|
|
import sun.tools.java.*;
|
|
import java.util.Enumeration;
|
|
import java.io.IOException;
|
|
import java.io.DataOutputStream;
|
|
|
|
/**
|
|
* An Java instruction
|
|
*
|
|
* WARNING: The contents of this source file are not part of any
|
|
* supported API. Code that depends on them does so at its own risk:
|
|
* they are subject to change or removal without notice.
|
|
*/
|
|
public
|
|
class Instruction implements Constants {
|
|
long where;
|
|
int pc;
|
|
int opc;
|
|
Object value;
|
|
Instruction next;
|
|
//JCOV
|
|
boolean flagCondInverted; /* if true, the condition is reversed
|
|
relatively of source code */
|
|
boolean flagNoCovered = false; /* if true, the command will
|
|
ignored for coverage */
|
|
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public Instruction(long where, int opc, Object value, boolean flagCondInverted) {
|
|
this.where = where;
|
|
this.opc = opc;
|
|
this.value = value;
|
|
this.flagCondInverted = flagCondInverted;
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public Instruction(boolean flagNoCovered, long where, int opc, Object value) {
|
|
this.where = where;
|
|
this.opc = opc;
|
|
this.value = value;
|
|
this.flagNoCovered = flagNoCovered;
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public Instruction(long where, int opc, boolean flagNoCovered) {
|
|
this.where = where;
|
|
this.opc = opc;
|
|
this.flagNoCovered = flagNoCovered;
|
|
}
|
|
//end JCOV
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public Instruction(long where, int opc, Object value) {
|
|
this.where = where;
|
|
this.opc = opc;
|
|
this.value = value;
|
|
}
|
|
|
|
/**
|
|
* When deciding between a lookupswitch and a tableswitch, this
|
|
* value is used in determining how much size increase is
|
|
* acceptable.
|
|
*/
|
|
public static final double SWITCHRATIO;
|
|
|
|
static {
|
|
// Set SWITCHRATIO from the property javac.switchratio
|
|
// if it exists and is reasonable. Otherwise, set
|
|
// SWITCHRATIO to 1.5, meaning that we will accept a 1.5x
|
|
// blowup (for the instruction) to use a tableswitch instead
|
|
// of a lookupswitch.
|
|
double ratio = 1.5;
|
|
String valStr = System.getProperty("javac.switchratio");
|
|
if (valStr != null) {
|
|
try {
|
|
double temp = Double.valueOf(valStr).doubleValue();
|
|
if (!(Double.isNaN(temp) || temp < 0.0)) {
|
|
ratio = temp;
|
|
}
|
|
} catch (NumberFormatException ee) {}
|
|
}
|
|
SWITCHRATIO = ratio;
|
|
}
|
|
|
|
/**
|
|
* Accessor
|
|
*/
|
|
public int getOpcode() {
|
|
return pc;
|
|
}
|
|
|
|
public Object getValue() {
|
|
return value;
|
|
}
|
|
|
|
public void setValue(Object value) {
|
|
this.value = value;
|
|
}
|
|
|
|
|
|
/**
|
|
* Optimize
|
|
*/
|
|
void optimize(Environment env) {
|
|
switch (opc) {
|
|
case opc_istore: case opc_lstore: case opc_fstore:
|
|
case opc_dstore: case opc_astore:
|
|
// Don't keep the LocalVariable info around, unless we
|
|
// are actually going to generate a local variable table.
|
|
if ((value instanceof LocalVariable) && !env.debug_vars()) {
|
|
value = new Integer(((LocalVariable)value).slot);
|
|
}
|
|
break;
|
|
|
|
case opc_goto: {
|
|
Label lbl = (Label)value;
|
|
value = lbl = lbl.getDestination();
|
|
if (lbl == next) {
|
|
// goto to the next instruction, obsolete
|
|
opc = opc_dead;
|
|
break;
|
|
}
|
|
|
|
// We optimize
|
|
//
|
|
// goto Tag
|
|
// ...
|
|
// Tag:
|
|
// return
|
|
//
|
|
// except when we're generating debuggable code. When
|
|
// we're generating debuggable code, we leave it alone,
|
|
// in order to provide better stepping behavior. Consider
|
|
// a method the end of which looks like this:
|
|
//
|
|
// ...
|
|
// break;
|
|
// } // end of loop
|
|
// } // end of method
|
|
//
|
|
// If we optimize the goto away, we'll be left with a
|
|
// single instruction (return) and the need to ascribe that
|
|
// instruction to two source lines (the break statement and
|
|
// the method's right curly). Can't get there from here.
|
|
// Depending on which line-number ascription we choose, the
|
|
// stepping user will step directly from the break statement
|
|
// back into the caller of the method (case 1) or from the
|
|
// statement that precedes the break statement to the method's
|
|
// right curly (case 2). Similarly, he'll be able to set a
|
|
// breakpoint on the break statement (case 1) or the method's
|
|
// right curly (case 2), but not on both. Neither case 1 nor
|
|
// case 2 is desirable. .We want him to see both the break
|
|
// statement and the method's right curly when stepping,
|
|
// and we want him to be able to set a breakpoint on either or
|
|
// both. So we suppress the optimization when generating
|
|
// debuggable code.
|
|
// (Above notes from brucek@eng in JDK1.0.2, copied here
|
|
// by kelly.ohair@eng for JDK1.1)
|
|
//
|
|
// With the changes to allow -O and -g at the same time,
|
|
// I've changed the condition to be whether optimization is
|
|
// on instead of the debugging flag being off.
|
|
// - david.stoutamire@eng for 1.2
|
|
|
|
if (lbl.next != null && env.opt()) {
|
|
switch (lbl.next.opc) {
|
|
case opc_return: case opc_ireturn: case opc_lreturn:
|
|
case opc_freturn: case opc_dreturn: case opc_areturn:
|
|
// goto to return
|
|
opc = lbl.next.opc;
|
|
value = lbl.next.value;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case opc_ifeq: case opc_ifne: case opc_ifgt:
|
|
case opc_ifge: case opc_iflt: case opc_ifle:
|
|
case opc_ifnull: case opc_ifnonnull:
|
|
value = ((Label)value).getDestination();
|
|
if (value == next) {
|
|
// branch to next instruction, obsolete
|
|
opc = opc_pop;
|
|
break;
|
|
}
|
|
if ((next.opc == opc_goto) && (value == next.next)) {
|
|
// Conditional branch over goto, invert
|
|
// Note that you can't invert all conditions, condition
|
|
// results for float/double compares are not invertable.
|
|
switch (opc) {
|
|
case opc_ifeq: opc = opc_ifne; break;
|
|
case opc_ifne: opc = opc_ifeq; break;
|
|
case opc_iflt: opc = opc_ifge; break;
|
|
case opc_ifle: opc = opc_ifgt; break;
|
|
case opc_ifgt: opc = opc_ifle; break;
|
|
case opc_ifge: opc = opc_iflt; break;
|
|
case opc_ifnull: opc = opc_ifnonnull; break;
|
|
case opc_ifnonnull: opc = opc_ifnull; break;
|
|
}
|
|
//JCOV
|
|
flagCondInverted = !flagCondInverted;
|
|
//end JCOV
|
|
value = next.value;
|
|
next.opc = opc_dead;
|
|
}
|
|
break;
|
|
|
|
case opc_if_acmpeq: case opc_if_acmpne:
|
|
case opc_if_icmpeq: case opc_if_icmpne:
|
|
case opc_if_icmpgt: case opc_if_icmpge:
|
|
case opc_if_icmplt: case opc_if_icmple:
|
|
value = ((Label)value).getDestination();
|
|
if (value == next) {
|
|
// branch to next instruction, obsolete
|
|
opc = opc_pop2;
|
|
break;
|
|
}
|
|
if ((next.opc == opc_goto) && (value == next.next)) {
|
|
// Conditional branch over goto, invert
|
|
switch (opc) {
|
|
case opc_if_acmpeq: opc = opc_if_acmpne; break;
|
|
case opc_if_acmpne: opc = opc_if_acmpeq; break;
|
|
case opc_if_icmpeq: opc = opc_if_icmpne; break;
|
|
case opc_if_icmpne: opc = opc_if_icmpeq; break;
|
|
case opc_if_icmpgt: opc = opc_if_icmple; break;
|
|
case opc_if_icmpge: opc = opc_if_icmplt; break;
|
|
case opc_if_icmplt: opc = opc_if_icmpge; break;
|
|
case opc_if_icmple: opc = opc_if_icmpgt; break;
|
|
}
|
|
//JCOV
|
|
flagCondInverted = !flagCondInverted;
|
|
//end JCOV
|
|
value = next.value;
|
|
next.opc = opc_dead;
|
|
}
|
|
break;
|
|
|
|
case opc_tableswitch:
|
|
case opc_lookupswitch: {
|
|
SwitchData sw = (SwitchData)value;
|
|
sw.defaultLabel = sw.defaultLabel.getDestination();
|
|
for (Enumeration<Integer> e = sw.tab.keys() ; e.hasMoreElements() ; ) {
|
|
Integer k = e.nextElement();
|
|
Label lbl = sw.tab.get(k);
|
|
sw.tab.put(k, lbl.getDestination());
|
|
}
|
|
|
|
// Compute the approximate sizes of a tableswitch and a
|
|
// lookupswitch. Decide which one we want to generate.
|
|
|
|
long range = (long)sw.maxValue - (long)sw.minValue + 1;
|
|
long entries = sw.tab.size();
|
|
|
|
long tableSize = 4 + range;
|
|
long lookupSize = 3 + 2 * entries;
|
|
|
|
if (tableSize <= lookupSize * SWITCHRATIO) {
|
|
opc = opc_tableswitch;
|
|
} else {
|
|
opc = opc_lookupswitch;
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Collect constants into the constant table
|
|
*/
|
|
void collect(ConstantPool tab) {
|
|
switch (opc) {
|
|
case opc_istore: case opc_lstore: case opc_fstore:
|
|
case opc_dstore: case opc_astore:
|
|
if (value instanceof LocalVariable) {
|
|
MemberDefinition field = ((LocalVariable)value).field;
|
|
tab.put(field.getName().toString());
|
|
tab.put(field.getType().getTypeSignature());
|
|
}
|
|
return;
|
|
|
|
case opc_new: case opc_putfield:
|
|
case opc_putstatic: case opc_getfield:
|
|
case opc_getstatic: case opc_invokevirtual:
|
|
case opc_invokespecial: case opc_invokestatic:
|
|
case opc_invokeinterface: case opc_instanceof:
|
|
case opc_checkcast:
|
|
tab.put(value);
|
|
return;
|
|
|
|
case opc_anewarray:
|
|
tab.put(value);
|
|
return;
|
|
|
|
case opc_multianewarray:
|
|
tab.put(((ArrayData)value).type);
|
|
return;
|
|
|
|
case opc_ldc:
|
|
case opc_ldc_w:
|
|
if (value instanceof Integer) {
|
|
int v = ((Integer)value).intValue();
|
|
if ((v >= -1) && (v <= 5)) {
|
|
opc = opc_iconst_0 + v;
|
|
return;
|
|
} else if ((v >= -(1 << 7)) && (v < (1 << 7))) {
|
|
opc = opc_bipush;
|
|
return;
|
|
} else if ((v >= -(1 << 15)) && (v < (1 << 15))) {
|
|
opc = opc_sipush;
|
|
return;
|
|
}
|
|
} else if (value instanceof Float) {
|
|
float v = ((Float)value).floatValue();
|
|
if (v == 0) {
|
|
if (Float.floatToIntBits(v) == 0) {
|
|
opc = opc_fconst_0;
|
|
return;
|
|
}
|
|
} else if (v == 1) {
|
|
opc = opc_fconst_1;
|
|
return;
|
|
} else if (v == 2) {
|
|
opc = opc_fconst_2;
|
|
return;
|
|
}
|
|
}
|
|
tab.put(value);
|
|
return;
|
|
|
|
case opc_ldc2_w:
|
|
if (value instanceof Long) {
|
|
long v = ((Long)value).longValue();
|
|
if (v == 0) {
|
|
opc = opc_lconst_0;
|
|
return;
|
|
} else if (v == 1) {
|
|
opc = opc_lconst_1;
|
|
return;
|
|
}
|
|
} else if (value instanceof Double) {
|
|
double v = ((Double)value).doubleValue();
|
|
if (v == 0) {
|
|
if (Double.doubleToLongBits(v) == 0) {
|
|
opc = opc_dconst_0;
|
|
return;
|
|
}
|
|
} else if (v == 1) {
|
|
opc = opc_dconst_1;
|
|
return;
|
|
}
|
|
}
|
|
tab.put(value);
|
|
return;
|
|
|
|
case opc_try:
|
|
for (Enumeration<CatchData> e = ((TryData)value).catches.elements() ; e.hasMoreElements() ;) {
|
|
CatchData cd = e.nextElement();
|
|
if (cd.getType() != null) {
|
|
tab.put(cd.getType());
|
|
}
|
|
}
|
|
return;
|
|
|
|
case opc_nop:
|
|
if ((value != null) && (value instanceof ClassDeclaration))
|
|
tab.put(value);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Balance the stack
|
|
*/
|
|
int balance() {
|
|
switch (opc) {
|
|
case opc_dead: case opc_label: case opc_iinc:
|
|
case opc_arraylength: case opc_laload: case opc_daload:
|
|
case opc_nop: case opc_ineg: case opc_fneg:
|
|
case opc_lneg: case opc_dneg: case opc_i2f:
|
|
case opc_f2i: case opc_l2d: case opc_d2l:
|
|
case opc_i2b: case opc_i2c: case opc_i2s:
|
|
case opc_jsr: case opc_goto: case opc_jsr_w:
|
|
case opc_goto_w: case opc_return: case opc_ret:
|
|
case opc_instanceof: case opc_checkcast: case opc_newarray:
|
|
case opc_anewarray: case opc_try: case opc_swap:
|
|
return 0;
|
|
|
|
case opc_ldc: case opc_ldc_w: case opc_bipush:
|
|
case opc_sipush: case opc_aconst_null: case opc_iconst_m1:
|
|
case opc_iconst_0: case opc_iconst_1: case opc_iconst_2:
|
|
case opc_iconst_3: case opc_iconst_4: case opc_iconst_5:
|
|
case opc_fconst_0: case opc_fconst_1: case opc_fconst_2:
|
|
case opc_iload: case opc_fload: case opc_aload:
|
|
case opc_dup: case opc_dup_x1: case opc_dup_x2:
|
|
case opc_i2l: case opc_i2d: case opc_f2l:
|
|
case opc_f2d: case opc_new:
|
|
return 1;
|
|
|
|
case opc_lload: case opc_dload: case opc_dup2:
|
|
case opc_dup2_x1: case opc_dup2_x2: case opc_ldc2_w:
|
|
case opc_lconst_0: case opc_lconst_1: case opc_dconst_0:
|
|
case opc_dconst_1:
|
|
return 2;
|
|
|
|
case opc_istore: case opc_fstore: case opc_astore:
|
|
case opc_iaload: case opc_faload: case opc_aaload:
|
|
case opc_baload: case opc_caload: case opc_saload:
|
|
case opc_pop: case opc_iadd: case opc_fadd:
|
|
case opc_isub: case opc_fsub: case opc_imul:
|
|
case opc_fmul: case opc_idiv: case opc_fdiv:
|
|
case opc_irem: case opc_frem: case opc_ishl:
|
|
case opc_ishr: case opc_iushr: case opc_lshl:
|
|
case opc_lshr: case opc_lushr: case opc_iand:
|
|
case opc_ior: case opc_ixor: case opc_l2i:
|
|
case opc_l2f: case opc_d2i: case opc_d2f:
|
|
case opc_ifeq: case opc_ifne: case opc_iflt:
|
|
case opc_ifle: case opc_ifgt: case opc_ifge:
|
|
case opc_ifnull: case opc_ifnonnull: case opc_fcmpl:
|
|
case opc_fcmpg: case opc_ireturn: case opc_freturn:
|
|
case opc_areturn: case opc_tableswitch: case opc_lookupswitch:
|
|
case opc_athrow: case opc_monitorenter: case opc_monitorexit:
|
|
return -1;
|
|
|
|
case opc_lstore: case opc_dstore: case opc_pop2:
|
|
case opc_ladd: case opc_dadd: case opc_lsub:
|
|
case opc_dsub: case opc_lmul: case opc_dmul:
|
|
case opc_ldiv: case opc_ddiv: case opc_lrem:
|
|
case opc_drem: case opc_land: case opc_lor:
|
|
case opc_lxor: case opc_if_acmpeq: case opc_if_acmpne:
|
|
case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmplt:
|
|
case opc_if_icmple: case opc_if_icmpgt: case opc_if_icmpge:
|
|
case opc_lreturn: case opc_dreturn:
|
|
return -2;
|
|
|
|
case opc_iastore: case opc_fastore: case opc_aastore:
|
|
case opc_bastore: case opc_castore: case opc_sastore:
|
|
case opc_lcmp: case opc_dcmpl: case opc_dcmpg:
|
|
return -3;
|
|
|
|
case opc_lastore: case opc_dastore:
|
|
return -4;
|
|
|
|
case opc_multianewarray:
|
|
return 1 - ((ArrayData)value).nargs;
|
|
|
|
case opc_getfield:
|
|
return ((MemberDefinition)value).getType().stackSize() - 1;
|
|
|
|
case opc_putfield:
|
|
return -1 - ((MemberDefinition)value).getType().stackSize();
|
|
|
|
case opc_getstatic:
|
|
return ((MemberDefinition)value).getType().stackSize();
|
|
|
|
case opc_putstatic:
|
|
return -((MemberDefinition)value).getType().stackSize();
|
|
|
|
case opc_invokevirtual:
|
|
case opc_invokespecial:
|
|
case opc_invokeinterface:
|
|
return ((MemberDefinition)value).getType().getReturnType().stackSize() -
|
|
(((MemberDefinition)value).getType().stackSize() + 1);
|
|
|
|
case opc_invokestatic:
|
|
return ((MemberDefinition)value).getType().getReturnType().stackSize() -
|
|
(((MemberDefinition)value).getType().stackSize());
|
|
}
|
|
throw new CompilerError("invalid opcode: " + toString());
|
|
}
|
|
|
|
/**
|
|
* Return the size of the instruction
|
|
*/
|
|
int size(ConstantPool tab) {
|
|
switch (opc) {
|
|
case opc_try: case opc_label: case opc_dead:
|
|
return 0;
|
|
|
|
case opc_bipush: case opc_newarray:
|
|
return 2;
|
|
|
|
case opc_sipush: case opc_goto: case opc_jsr:
|
|
case opc_ifeq: case opc_ifne: case opc_ifgt:
|
|
case opc_ifge: case opc_iflt: case opc_ifle:
|
|
case opc_ifnull: case opc_ifnonnull: case opc_if_acmpeq:
|
|
case opc_if_acmpne: case opc_if_icmpeq: case opc_if_icmpne:
|
|
case opc_if_icmpgt: case opc_if_icmpge: case opc_if_icmplt:
|
|
case opc_if_icmple:
|
|
return 3;
|
|
|
|
case opc_ldc:
|
|
case opc_ldc_w:
|
|
if (tab.index(value) < 256) {
|
|
opc = opc_ldc;
|
|
return 2;
|
|
} else {
|
|
opc = opc_ldc_w;
|
|
return 3;
|
|
}
|
|
|
|
case opc_iload: case opc_lload: case opc_fload:
|
|
case opc_dload: case opc_aload: {
|
|
int v = ((Number)value).intValue();
|
|
if (v < 4) {
|
|
if (v < 0) {
|
|
throw new CompilerError("invalid slot: " + toString()
|
|
+ "\nThis error possibly resulted from poorly constructed class paths.");
|
|
}
|
|
opc = opc_iload_0 + (opc - opc_iload) * 4 + v;
|
|
return 1;
|
|
} else if (v <= 255) {
|
|
return 2;
|
|
} else {
|
|
opc += 256; // indicate wide variant
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
case opc_iinc: {
|
|
int register = ((int[])value)[0];
|
|
int increment = ((int[])value)[1];
|
|
if (register < 0) {
|
|
throw new CompilerError("invalid slot: " + toString());
|
|
}
|
|
if (register <= 255 && (((byte)increment) == increment)) {
|
|
return 3;
|
|
} else {
|
|
opc += 256; // indicate wide variant
|
|
return 6;
|
|
}
|
|
}
|
|
|
|
case opc_istore: case opc_lstore: case opc_fstore:
|
|
case opc_dstore: case opc_astore: {
|
|
int v = (value instanceof Number) ?
|
|
((Number)value).intValue() : ((LocalVariable)value).slot;
|
|
if (v < 4) {
|
|
if (v < 0) {
|
|
throw new CompilerError("invalid slot: " + toString());
|
|
}
|
|
opc = opc_istore_0 + (opc - opc_istore) * 4 + v;
|
|
return 1;
|
|
} else if (v <= 255) {
|
|
return 2;
|
|
} else {
|
|
opc += 256; // indicate wide variant
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
case opc_ret: {
|
|
int v = ((Number)value).intValue();
|
|
if (v <= 255) {
|
|
if (v < 0) {
|
|
throw new CompilerError("invalid slot: " + toString());
|
|
}
|
|
return 2;
|
|
} else {
|
|
opc += 256; // indicate wide variant
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
case opc_ldc2_w: case opc_new:
|
|
case opc_putstatic: case opc_getstatic:
|
|
case opc_putfield: case opc_getfield:
|
|
case opc_invokevirtual: case opc_invokespecial:
|
|
case opc_invokestatic: case opc_instanceof:
|
|
case opc_checkcast: case opc_anewarray:
|
|
return 3;
|
|
|
|
case opc_multianewarray:
|
|
return 4;
|
|
|
|
case opc_invokeinterface:
|
|
case opc_goto_w:
|
|
case opc_jsr_w:
|
|
return 5;
|
|
|
|
case opc_tableswitch: {
|
|
SwitchData sw = (SwitchData)value;
|
|
int n = 1;
|
|
for(; ((pc + n) % 4) != 0 ; n++);
|
|
return n + 16 + (sw.maxValue - sw.minValue) * 4;
|
|
}
|
|
|
|
case opc_lookupswitch: {
|
|
SwitchData sw = (SwitchData)value;
|
|
int n = 1;
|
|
for(; ((pc + n) % 4) != 0 ; n++);
|
|
return n + 8 + sw.tab.size() * 8;
|
|
}
|
|
|
|
case opc_nop:
|
|
if ((value != null) && !(value instanceof Integer))
|
|
return 2;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
// most opcodes are only 1 byte long
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Generate code
|
|
*/
|
|
@SuppressWarnings("fallthrough")
|
|
void write(DataOutputStream out, ConstantPool tab) throws IOException {
|
|
switch (opc) {
|
|
case opc_try: case opc_label: case opc_dead:
|
|
break;
|
|
|
|
case opc_bipush: case opc_newarray:
|
|
case opc_iload: case opc_lload: case opc_fload:
|
|
case opc_dload: case opc_aload: case opc_ret:
|
|
out.writeByte(opc);
|
|
out.writeByte(((Number)value).intValue());
|
|
break;
|
|
|
|
case opc_iload + 256: case opc_lload + 256:
|
|
case opc_fload + 256: case opc_dload + 256:
|
|
case opc_aload + 256: case opc_ret + 256:
|
|
out.writeByte(opc_wide);
|
|
out.writeByte(opc - 256);
|
|
out.writeShort(((Number)value).intValue());
|
|
break;
|
|
|
|
case opc_istore: case opc_lstore: case opc_fstore:
|
|
case opc_dstore: case opc_astore:
|
|
out.writeByte(opc);
|
|
out.writeByte((value instanceof Number) ?
|
|
((Number)value).intValue() : ((LocalVariable)value).slot);
|
|
break;
|
|
|
|
case opc_istore + 256: case opc_lstore + 256:
|
|
case opc_fstore + 256: case opc_dstore + 256:
|
|
case opc_astore + 256:
|
|
out.writeByte(opc_wide);
|
|
out.writeByte(opc - 256);
|
|
out.writeShort((value instanceof Number) ?
|
|
((Number)value).intValue() : ((LocalVariable)value).slot);
|
|
break;
|
|
|
|
case opc_sipush:
|
|
out.writeByte(opc);
|
|
out.writeShort(((Number)value).intValue());
|
|
break;
|
|
|
|
case opc_ldc:
|
|
out.writeByte(opc);
|
|
out.writeByte(tab.index(value));
|
|
break;
|
|
|
|
case opc_ldc_w: case opc_ldc2_w:
|
|
case opc_new: case opc_putstatic:
|
|
case opc_getstatic: case opc_putfield:
|
|
case opc_getfield: case opc_invokevirtual:
|
|
case opc_invokespecial: case opc_invokestatic:
|
|
case opc_instanceof: case opc_checkcast:
|
|
out.writeByte(opc);
|
|
out.writeShort(tab.index(value));
|
|
break;
|
|
|
|
case opc_iinc:
|
|
out.writeByte(opc);
|
|
out.writeByte(((int[])value)[0]); // register
|
|
out.writeByte(((int[])value)[1]); // increment
|
|
break;
|
|
|
|
case opc_iinc + 256:
|
|
out.writeByte(opc_wide);
|
|
out.writeByte(opc - 256);
|
|
out.writeShort(((int[])value)[0]); // register
|
|
out.writeShort(((int[])value)[1]); // increment
|
|
break;
|
|
|
|
case opc_anewarray:
|
|
out.writeByte(opc);
|
|
out.writeShort(tab.index(value));
|
|
break;
|
|
|
|
case opc_multianewarray:
|
|
out.writeByte(opc);
|
|
out.writeShort(tab.index(((ArrayData)value).type));
|
|
out.writeByte(((ArrayData)value).nargs);
|
|
break;
|
|
|
|
case opc_invokeinterface:
|
|
out.writeByte(opc);
|
|
out.writeShort(tab.index(value));
|
|
out.writeByte(((MemberDefinition)value).getType().stackSize() + 1);
|
|
out.writeByte(0);
|
|
break;
|
|
|
|
case opc_goto: case opc_jsr: case opc_ifeq:
|
|
case opc_ifne: case opc_ifgt: case opc_ifge:
|
|
case opc_iflt: case opc_ifle: case opc_ifnull:
|
|
case opc_ifnonnull: case opc_if_acmpeq: case opc_if_acmpne:
|
|
case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpgt:
|
|
case opc_if_icmpge: case opc_if_icmplt: case opc_if_icmple:
|
|
out.writeByte(opc);
|
|
out.writeShort(((Instruction)value).pc - pc);
|
|
break;
|
|
|
|
case opc_goto_w:
|
|
case opc_jsr_w:
|
|
out.writeByte(opc);
|
|
out.writeLong(((Instruction)value).pc - pc);
|
|
break;
|
|
|
|
case opc_tableswitch: {
|
|
SwitchData sw = (SwitchData)value;
|
|
out.writeByte(opc);
|
|
for(int n = 1 ; ((pc + n) % 4) != 0 ; n++) {
|
|
out.writeByte(0);
|
|
}
|
|
out.writeInt(sw.defaultLabel.pc - pc);
|
|
out.writeInt(sw.minValue);
|
|
out.writeInt(sw.maxValue);
|
|
for (int n = sw.minValue ; n <= sw.maxValue ; n++) {
|
|
Label lbl = sw.get(n);
|
|
int target_pc = (lbl != null) ? lbl.pc : sw.defaultLabel.pc;
|
|
out.writeInt(target_pc - pc);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case opc_lookupswitch: {
|
|
SwitchData sw = (SwitchData)value;
|
|
out.writeByte(opc);
|
|
int n = pc + 1;
|
|
for(; (n % 4) != 0 ; n++) {
|
|
out.writeByte(0);
|
|
}
|
|
out.writeInt(sw.defaultLabel.pc - pc);
|
|
out.writeInt(sw.tab.size());
|
|
for (Enumeration<Integer> e = sw.sortedKeys(); e.hasMoreElements() ; ) {
|
|
Integer v = e.nextElement();
|
|
out.writeInt(v.intValue());
|
|
out.writeInt(sw.get(v).pc - pc);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case opc_nop:
|
|
if (value != null) {
|
|
if (value instanceof Integer)
|
|
out.writeByte(((Integer)value).intValue());
|
|
else
|
|
out.writeShort(tab.index(value));
|
|
return;
|
|
}
|
|
// fall through
|
|
|
|
default:
|
|
out.writeByte(opc);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* toString
|
|
*/
|
|
public String toString() {
|
|
String prefix = (where >> WHEREOFFSETBITS) + ":\t";
|
|
switch (opc) {
|
|
case opc_try:
|
|
return prefix + "try " + ((TryData)value).getEndLabel().hashCode();
|
|
|
|
case opc_dead:
|
|
return prefix + "dead";
|
|
|
|
case opc_iinc: {
|
|
int register = ((int[])value)[0];
|
|
int increment = ((int[])value)[1];
|
|
return prefix + opcNames[opc] + " " + register + ", " + increment;
|
|
}
|
|
|
|
default:
|
|
if (value != null) {
|
|
if (value instanceof Label) {
|
|
return prefix + opcNames[opc] + " " + value.toString();
|
|
} else if (value instanceof Instruction) {
|
|
return prefix + opcNames[opc] + " " + value.hashCode();
|
|
} else if (value instanceof String) {
|
|
return prefix + opcNames[opc] + " \"" + value + "\"";
|
|
} else {
|
|
return prefix + opcNames[opc] + " " + value;
|
|
}
|
|
} else {
|
|
return prefix + opcNames[opc];
|
|
}
|
|
}
|
|
}
|
|
}
|