0001: // Copyright (c) 1997, 1998, 1999, 2001, 2003, 2004 Per M.A. Bothner.
0002: // This is free software; for terms and warranty disclaimer see ./COPYING.
0003:
0004: package gnu.bytecode;
0005:
0006: import java.io.*;
0007:
0008: /**
0009: * Represents the contents of a standard "Code" attribute.
0010: * <p>
0011: * Most of the actual methods that generate bytecode operation
0012: * are in this class (typically with names starting with <code>emit</code>),
0013: * though there are also some in <code>Method</code>.
0014: * <p>
0015: * Note that a <code>CodeAttr</code> is an <code>Attribute</code>
0016: * of a <code>Method</code>, and can in turn contain other
0017: * <code>Attribute</code>s, such as a <code>LineNumbersAttr</code>.
0018: *
0019: * @author Per Bothner
0020: */
0021:
0022: public class CodeAttr extends Attribute implements AttrContainer {
0023: Attribute attributes;
0024:
0025: public final Attribute getAttributes() {
0026: return attributes;
0027: }
0028:
0029: public final void setAttributes(Attribute attributes) {
0030: this .attributes = attributes;
0031: }
0032:
0033: LineNumbersAttr lines;
0034: public LocalVarsAttr locals;
0035:
0036: SourceDebugExtAttr sourceDbgExt;
0037:
0038: // In hindsight, maintaining stack_types is more hassle than it is worth.
0039: // Instead, better to just keep track of SP, which should catch most
0040: // stack errors, while being more general and less hassle. FIXME.
0041: Type[] stack_types;
0042:
0043: int SP; // Current stack size (in "words")
0044: private int max_stack;
0045: private int max_locals;
0046: /** Current active length of code array.
0047: * Note that processFixups may expand/contract the code array. */
0048: int PC;
0049: byte[] code;
0050:
0051: /* The exception handler table, as a vector of quadruples
0052: (start_pc, end_pc, handler_pc, catch_type).
0053: Only the first exception_table_length quadruples are defined. */
0054: short[] exception_table;
0055:
0056: /* The number of (defined) exception handlers (i.e. quadruples)
0057: in exception_table. */
0058: int exception_table_length;
0059:
0060: /** Not a fixup - a no-op. */
0061: static final int FIXUP_NONE = 0;
0062: /** The definition of a label. */
0063: static final int FIXUP_DEFINE = 1;
0064: /** The offset points to a tableswitch/lookupswitch - handle padding. */
0065: static final int FIXUP_SWITCH = 2;
0066: /** The offset contains a label relative to the previous FIXUP_SWITCH. */
0067: static final int FIXUP_CASE = 3;
0068: /** The offset points to a goto instruction.
0069: * This case up to FIXUP_TRANSFER2 must be contiguous
0070: * - see the jump-to-jump optimization in processFixups. */
0071: static final int FIXUP_GOTO = 4;
0072: /** The offset points to a jsr instruction. */
0073: static final int FIXUP_JSR = 5;
0074: /** The offset points to a conditional transfer (if_xxx) instruction. */
0075: static final int FIXUP_TRANSFER = 6;
0076: /** A FIXUP_GOTO_, FIXUP_JSR, or FIXUP_TRANSFER that uses a 2-byte offset. */
0077: static final int FIXUP_TRANSFER2 = 7;
0078: /** The offsets points to 3 bytes that should be deleted. */
0079: static final int FIXUP_DELETE3 = 8;
0080: /** The following instructions are moved to later in the code stream.
0081: * Instead the instructions starting at the fixup label are patched here.
0082: * (If the fixup label is null, we're done.)
0083: * This allows re-arranging code to avoid unneeded gotos.
0084: * The following instruction is the target of a later FIXUP_MOVE,
0085: * and we'll insert then when we get to it. */
0086: static final int FIXUP_MOVE = 9;
0087: /** The following instructions are moved to the end of the code stream.
0088: * Same as FIXUP_MOVE, but there is no explicit later FIXUP_MOVE that
0089: * refers to the following instructions. Created by beginFragment.
0090: * The fixup_offset points to the end of the fragment.
0091: * (The first processFixups patches these to FIXUP_MOVE.) */
0092: static final int FIXUP_MOVE_TO_END = 10;
0093: /** FIXUP_TRY with the following FIXUP_CATCH marks an exception handler.
0094: * The label is the start of the try clause;
0095: * the current offset marks the exception handler. */
0096: static final int FIXUP_TRY = 11;
0097: /** Second half of a FIXUP_TRY/FIXUP_CATCH pair.
0098: * The label is the ed of the try clause;
0099: * the current offset is the exception type as a constant pool index. */
0100: static final int FIXUP_CATCH = 12;
0101: /** With following FIXUP_LINE_NUMBER associates an offset with a line number.
0102: * The fixup_offset is the code location; the fixup_label is null. */
0103: static final int FIXUP_LINE_PC = 13;
0104: /** With preceding FIXUP_LINE_PC associates an offset with a line number.
0105: * The fixup_offset is the line number; the fixup_label is null. */
0106: static final int FIXUP_LINE_NUMBER = 14;
0107: int[] fixup_offsets;
0108: Label[] fixup_labels;
0109: int fixup_count;
0110:
0111: /** This causes a later processFixup to rearrange the code.
0112: * The code at target comes here, instead of the following instructions.
0113: * Fuctionally equivalent to: <code>goto target; here:</code>,
0114: * but implemented by code re-arranging. Therefore there should be
0115: * at some later point a <code>goto here; target:</code>.
0116: */
0117: public final void fixupChain(Label here, Label target) {
0118: fixupAdd(CodeAttr.FIXUP_MOVE, 0, target);
0119: here.define(this );
0120: }
0121:
0122: /** Add a fixup at this location.
0123: * @param kind one of the FIXUP_xxx codes.
0124: * @param label varies - typically the target of jump. */
0125: public final void fixupAdd(int kind, Label label) {
0126: fixupAdd(kind, PC, label);
0127: }
0128:
0129: final void fixupAdd(int kind, int offset, Label label) {
0130: int count = fixup_count;
0131: if (count == 0) {
0132: fixup_offsets = new int[30];
0133: fixup_labels = new Label[30];
0134: } else if (fixup_count == fixup_offsets.length) {
0135: int new_length = 2 * count;
0136: Label[] new_labels = new Label[new_length];
0137: System.arraycopy(fixup_labels, 0, new_labels, 0, count);
0138: fixup_labels = new_labels;
0139: int[] new_offsets = new int[new_length];
0140: System.arraycopy(fixup_offsets, 0, new_offsets, 0, count);
0141: fixup_offsets = new_offsets;
0142: }
0143: fixup_offsets[count] = (offset << 4) | kind;
0144: fixup_labels[count] = label;
0145: fixup_count = count + 1;
0146: }
0147:
0148: private final int fixupOffset(int index) {
0149: return fixup_offsets[index] >> 4;
0150: }
0151:
0152: private final int fixupKind(int index) {
0153: return fixup_offsets[index] & 15;
0154: }
0155:
0156: /** If true we get a line number entry for each instruction.
0157: * Normally false, but can be a convenient hack to allow instruction-level
0158: * stepping/debugging and stacktraces. In this case {@code LINE==PC}. */
0159: public static boolean instructionLineMode = false;
0160:
0161: /** The stack of currently active conditionals. */
0162: IfState if_stack;
0163:
0164: /** The stack of currently active try statements. */
0165: TryState try_stack;
0166:
0167: public final Method getMethod() {
0168: return (Method) getContainer();
0169: }
0170:
0171: public final int getPC() {
0172: return PC;
0173: }
0174:
0175: public final int getSP() {
0176: return SP;
0177: }
0178:
0179: public final ConstantPool getConstants() {
0180: return getMethod().classfile.constants;
0181: }
0182:
0183: /* True if we cannot fall through to bytes[PC] -
0184: the previous instruction was an uncondition control transfer. */
0185: private boolean unreachable_here;
0186:
0187: /** True if control could reach here. */
0188: public final boolean reachableHere() {
0189: return !unreachable_here;
0190: }
0191:
0192: public final void setReachable(boolean val) {
0193: unreachable_here = !val;
0194: }
0195:
0196: public final void setUnreachable() {
0197: unreachable_here = true;
0198: }
0199:
0200: /** Get the maximum number of words on the operand stack in this method. */
0201: public int getMaxStack() {
0202: return max_stack;
0203: }
0204:
0205: /** Get the maximum number of local variable words in this method. */
0206: public int getMaxLocals() {
0207: return max_locals;
0208: }
0209:
0210: /** Set the maximum number of words on the operand stack in this method. */
0211: public void setMaxStack(int n) {
0212: max_stack = n;
0213: }
0214:
0215: /** Set the maximum number of local variable words in this method. */
0216: public void setMaxLocals(int n) {
0217: max_locals = n;
0218: }
0219:
0220: /** Get the code (instruction bytes) of this method.
0221: * Does not make a copy. */
0222: public byte[] getCode() {
0223: return code;
0224: }
0225:
0226: /** Set the code (instruction bytes) of this method.
0227: * @param code the code bytes (which are not copied).
0228: * Implicitly calls setCodeLength(code.length). */
0229: public void setCode(byte[] code) {
0230: this .code = code;
0231: this .PC = code.length;
0232: }
0233:
0234: /** Set the length the the code (instruction bytes) of this method.
0235: * That is the number of current used bytes in getCode().
0236: * (Any remaing bytes provide for future growth.) */
0237: public void setCodeLength(int len) {
0238: PC = len;
0239: }
0240:
0241: /** Set the current lengthof the code (instruction bytes) of this method. */
0242: public int getCodeLength() {
0243: return PC;
0244: }
0245:
0246: public CodeAttr(Method meth) {
0247: super ("Code");
0248: addToFrontOf(meth);
0249: meth.code = this ;
0250: }
0251:
0252: public final void reserve(int bytes) {
0253: if (code == null)
0254: code = new byte[100 + bytes];
0255: else if (PC + bytes > code.length) {
0256: byte[] new_code = new byte[2 * code.length + bytes];
0257: System.arraycopy(code, 0, new_code, 0, PC);
0258: code = new_code;
0259: }
0260: }
0261:
0262: /** Get opcode that implements NOT (x OPCODE y). */
0263: byte invert_opcode(byte opcode) {
0264: if ((opcode >= 153 && opcode <= 166)
0265: || (opcode >= 198 && opcode <= 199))
0266: return (byte) (opcode ^ 1);
0267: throw new Error("unknown opcode to invert_opcode");
0268: }
0269:
0270: /**
0271: * Write an 8-bit byte to the current code-stream.
0272: * @param i the byte to write
0273: */
0274: public final void put1(int i) {
0275: code[PC++] = (byte) i;
0276: unreachable_here = false;
0277: }
0278:
0279: /**
0280: * Write a 16-bit short to the current code-stream
0281: * @param i the value to write
0282: */
0283: public final void put2(int i) {
0284: code[PC++] = (byte) (i >> 8);
0285: code[PC++] = (byte) (i);
0286: unreachable_here = false;
0287: }
0288:
0289: /**
0290: * Write a 32-bit int to the current code-stream
0291: * @param i the value to write
0292: */
0293: public final void put4(int i) {
0294: code[PC++] = (byte) (i >> 24);
0295: code[PC++] = (byte) (i >> 16);
0296: code[PC++] = (byte) (i >> 8);
0297:
0298: code[PC++] = (byte) (i);
0299: unreachable_here = false;
0300: }
0301:
0302: public final void putIndex2(CpoolEntry cnst) {
0303: put2(cnst.index);
0304: }
0305:
0306: public final void putLineNumber(String filename, int linenumber) {
0307: getMethod().classfile.setSourceFile(filename);
0308: putLineNumber(linenumber);
0309: }
0310:
0311: public final void putLineNumber(int linenumber) {
0312: if (sourceDbgExt != null)
0313: linenumber = sourceDbgExt.fixLine(linenumber);
0314: fixupAdd(FIXUP_LINE_PC, null);
0315: fixupAdd(FIXUP_LINE_NUMBER, linenumber, null);
0316: }
0317:
0318: public final void pushType(Type type) {
0319: if (type.size == 0)
0320: throw new Error("pushing void type onto stack");
0321: if (stack_types == null)
0322: stack_types = new Type[20];
0323: else if (SP + 1 >= stack_types.length) {
0324: Type[] new_array = new Type[2 * stack_types.length];
0325: System.arraycopy(stack_types, 0, new_array, 0, SP);
0326: stack_types = new_array;
0327: }
0328: if (type.size == 8)
0329: stack_types[SP++] = Type.void_type;
0330: stack_types[SP++] = type;
0331: if (SP > max_stack)
0332: max_stack = SP;
0333: }
0334:
0335: public final Type popType() {
0336: if (SP <= 0)
0337: throw new Error("popType called with empty stack "
0338: + getMethod());
0339: Type type = stack_types[--SP];
0340: if (type.size == 8)
0341: if (!popType().isVoid())
0342: throw new Error("missing void type on stack");
0343: return type;
0344: }
0345:
0346: public final Type topType() {
0347: return stack_types[SP - 1];
0348: }
0349:
0350: /** Compile code to pop values off the stack (and ignore them).
0351: * @param nvalues the number of values (not words) to pop
0352: */
0353: public void emitPop(int nvalues) {
0354: for (; nvalues > 0; --nvalues) {
0355: reserve(1);
0356: Type type = popType();
0357: if (type.size > 4)
0358: put1(88); // pop2
0359: else if (nvalues > 1) { // optimization: can we pop 2 4-byte words using a pop2
0360: Type type2 = popType();
0361: if (type2.size > 4) {
0362: put1(87); // pop
0363: reserve(1);
0364: }
0365: put1(88); // pop2
0366: --nvalues;
0367: } else
0368: put1(87); // pop
0369: }
0370: }
0371:
0372: /** Get a new Label for the current location.
0373: * Unlike Label.define, Does not change reachableHere().
0374: */
0375: public Label getLabel() {
0376: boolean unreachable = unreachable_here;
0377: Label label = new Label();
0378: label.define(this );
0379: unreachable_here = unreachable;
0380: return label;
0381: }
0382:
0383: public void emitSwap() {
0384: reserve(1);
0385: Type type1 = popType();
0386: Type type2 = popType();
0387:
0388: if (type1.size > 4 || type2.size > 4) {
0389: // There is no swap instruction in the JVM for this case.
0390: // Fall back to a more convoluted way.
0391: pushType(type2);
0392: pushType(type1);
0393: emitDupX();
0394: emitPop(1);
0395: } else {
0396: pushType(type1);
0397: put1(95); // swap
0398: pushType(type2);
0399: }
0400: }
0401:
0402: /** Emit code to duplicate the top element of the stack. */
0403: public void emitDup() {
0404: reserve(1);
0405:
0406: Type type = topType();
0407: put1(type.size <= 4 ? 89 : 92); // dup or dup2
0408: pushType(type);
0409: }
0410:
0411: /** Emit code to duplicate the top element of the stack
0412: and place the copy before the previous element. */
0413: public void emitDupX() {
0414: reserve(1);
0415:
0416: Type type = popType();
0417: Type skipedType = popType();
0418:
0419: if (skipedType.size <= 4)
0420: put1(type.size <= 4 ? 90 : 93); // dup_x1 or dup2_x1
0421: else
0422: put1(type.size <= 4 ? 91 : 94); // dup_x2 or dup2_x2
0423:
0424: pushType(type);
0425: pushType(skipedType);
0426: pushType(type);
0427: }
0428:
0429: /** Compile code to duplicate with offset.
0430: * @param size the size of the stack item to duplicate (1 or 2)
0431: * @param offset where to insert the result (must be 0, 1, or 2)
0432: * The new words get inserted at stack[SP-size-offset]
0433: */
0434: public void emitDup(int size, int offset) {
0435: if (size == 0)
0436: return;
0437: reserve(1);
0438: // copied1 and (optionally copied2) are the types of the duplicated words
0439: Type copied1 = popType();
0440: Type copied2 = null;
0441: if (size == 1) {
0442: if (copied1.size > 4)
0443: throw new Error("using dup for 2-word type");
0444: } else if (size != 2)
0445: throw new Error("invalid size to emitDup");
0446: else if (copied1.size <= 4) {
0447: copied2 = popType();
0448: if (copied2.size > 4)
0449: throw new Error("dup will cause invalid types on stack");
0450: }
0451:
0452: int kind;
0453: // These are the types of the words (in any) that are "skipped":
0454: Type skipped1 = null;
0455: Type skipped2 = null;
0456: if (offset == 0) {
0457: kind = size == 1 ? 89 : 92; // dup or dup2
0458: } else if (offset == 1) {
0459: kind = size == 1 ? 90 : 93; // dup_x1 or dup2_x1
0460: skipped1 = popType();
0461: if (skipped1.size > 4)
0462: throw new Error("dup will cause invalid types on stack");
0463: } else if (offset == 2) {
0464: kind = size == 1 ? 91 : 94; // dup_x2 or dup2_x2
0465: skipped1 = popType();
0466: if (skipped1.size <= 4) {
0467: skipped2 = popType();
0468: if (skipped2.size > 4)
0469: throw new Error(
0470: "dup will cause invalid types on stack");
0471: }
0472: } else
0473: throw new Error("emitDup: invalid offset");
0474:
0475: put1(kind);
0476: if (copied2 != null)
0477: pushType(copied2);
0478: pushType(copied1);
0479: if (skipped2 != null)
0480: pushType(skipped2);
0481: if (skipped1 != null)
0482: pushType(skipped1);
0483: if (copied2 != null)
0484: pushType(copied2);
0485: pushType(copied1);
0486: }
0487:
0488: /**
0489: * Compile code to duplicate the top 1 or 2 words.
0490: * @param size number of words to duplicate
0491: */
0492: public void emitDup(int size) {
0493: emitDup(size, 0);
0494: }
0495:
0496: public void emitDup(Type type) {
0497: emitDup(type.size > 4 ? 2 : 1, 0);
0498: }
0499:
0500: public void enterScope(Scope scope) {
0501: scope.setStartPC(this );
0502: locals.enterScope(scope);
0503: }
0504:
0505: public Scope pushScope() {
0506: Scope scope = new Scope();
0507: if (locals == null)
0508: locals = new LocalVarsAttr(getMethod());
0509: enterScope(scope);
0510: if (locals.parameter_scope == null)
0511: locals.parameter_scope = scope;
0512: return scope;
0513: }
0514:
0515: public Scope getCurrentScope() {
0516: return locals.current_scope;
0517: }
0518:
0519: public Scope popScope() {
0520: Scope scope = locals.current_scope;
0521: locals.current_scope = scope.parent;
0522: scope.freeLocals(this );
0523: scope.end = getLabel();
0524: return scope;
0525: }
0526:
0527: /** Get the index'th parameter. */
0528: public Variable getArg(int index) {
0529: return locals.parameter_scope.getVariable(index);
0530: }
0531:
0532: /**
0533: * Search by name for a Variable
0534: * @param name name to search for
0535: * @return the Variable, or null if not found (in any scope of this Method).
0536: */
0537: public Variable lookup(String name) {
0538: Scope scope = locals.current_scope;
0539: for (; scope != null; scope = scope.parent) {
0540: Variable var = scope.lookup(name);
0541: if (var != null)
0542: return var;
0543: }
0544: return null;
0545: }
0546:
0547: /** Add a new local variable (in the current scope).
0548: * @param type type of the new Variable.
0549: * @return the new Variable. */
0550: public Variable addLocal(Type type) {
0551: return locals.current_scope.addVariable(this , type, null);
0552: }
0553:
0554: /** Add a new local variable (in the current scope).
0555: * @param type type of the new Variable.
0556: * @param name name of the new Variable.
0557: * @return the new Variable. */
0558: public Variable addLocal(Type type, String name) {
0559: return locals.current_scope.addVariable(this , type, name);
0560: }
0561:
0562: /** Call addLocal for parameters (as implied by method type). */
0563: public void addParamLocals() {
0564: Method method = getMethod();
0565: if ((method.access_flags & Access.STATIC) == 0)
0566: addLocal(method.classfile).setParameter(true);
0567: int arg_count = method.arg_types.length;
0568: for (int i = 0; i < arg_count; i++)
0569: addLocal(method.arg_types[i]).setParameter(true);
0570: }
0571:
0572: public final void emitPushConstant(int val, Type type) {
0573: switch (type.getSignature().charAt(0)) {
0574: case 'B':
0575: case 'C':
0576: case 'I':
0577: case 'Z':
0578: case 'S':
0579: emitPushInt(val);
0580: break;
0581: case 'J':
0582: emitPushLong((long) val);
0583: break;
0584: case 'F':
0585: emitPushFloat((float) val);
0586: break;
0587: case 'D':
0588: emitPushDouble((double) val);
0589: break;
0590: default:
0591: throw new Error("bad type to emitPushConstant");
0592: }
0593: }
0594:
0595: /* Low-level method to pust a ConstantPool entry.
0596: * Does not do the appropriatre <code>pushType</code>. */
0597: public final void emitPushConstant(CpoolEntry cnst) {
0598: reserve(3);
0599: int index = cnst.index;
0600: if (cnst instanceof CpoolValue2) {
0601: put1(20); // ldc2_w
0602: put2(index);
0603: } else if (index < 256) {
0604: put1(18); // ldc
0605: put1(index);
0606: } else {
0607: put1(19); // ldc_w
0608: put2(index);
0609: }
0610: }
0611:
0612: public final void emitPushInt(int i) {
0613: reserve(3);
0614: if (i >= -1 && i <= 5)
0615: put1(i + 3); // iconst_m1 .. iconst_5
0616: else if (i >= -128 && i < 128) {
0617: put1(16); // bipush
0618: put1(i);
0619: } else if (i >= -32768 && i < 32768) {
0620: put1(17); // sipush
0621: put2(i);
0622: } else {
0623: emitPushConstant(getConstants().addInt(i));
0624: }
0625: pushType(Type.int_type);
0626: }
0627:
0628: public void emitPushLong(long i) {
0629: if (i == 0 || i == 1) {
0630: reserve(1);
0631: put1(9 + (int) i); // lconst_0 .. lconst_1
0632: } else if ((long) (int) i == i) {
0633: emitPushInt((int) i);
0634: reserve(1);
0635: popType();
0636: put1(133); // i2l
0637: } else {
0638: emitPushConstant(getConstants().addLong(i));
0639: }
0640: pushType(Type.long_type);
0641: }
0642:
0643: public void emitPushFloat(float x) {
0644: int xi = (int) x;
0645: if ((float) xi == x && xi >= -128 && xi < 128) {
0646: if (xi >= 0 && xi <= 2) {
0647: reserve(1);
0648: put1(11 + xi); // fconst_0 .. fconst_2
0649: if (xi == 0 && Float.floatToIntBits(x) != 0) // x == -0.0
0650: {
0651: reserve(1);
0652: put1(118); // fneg
0653: }
0654: } else {
0655: // Saves space in the constant pool
0656: // Probably faster, at least on modern CPUs.
0657: emitPushInt(xi);
0658: reserve(1);
0659: popType();
0660: put1(134); // i2f
0661: }
0662: } else {
0663: emitPushConstant(getConstants().addFloat(x));
0664: }
0665: pushType(Type.float_type);
0666: }
0667:
0668: public void emitPushDouble(double x) {
0669: int xi = (int) x;
0670: if ((double) xi == x && xi >= -128 && xi < 128) {
0671: if (xi == 0 || xi == 1) {
0672: reserve(1);
0673: put1(14 + xi); // dconst_0 or dconst_1
0674: if (xi == 0 && Double.doubleToLongBits(x) != 0L) // x == -0.0
0675: {
0676: reserve(1);
0677: put1(119); // dneg
0678: }
0679: } else {
0680: // Saves space in the constant pool
0681: // Probably faster, at least on modern CPUs.
0682: emitPushInt(xi);
0683: reserve(1);
0684: popType();
0685: put1(135); // i2d
0686: }
0687: } else {
0688: emitPushConstant(getConstants().addDouble(x));
0689: }
0690: pushType(Type.double_type);
0691: }
0692:
0693: /** Calculate how many CONSTANT_String constants we need for a string.
0694: * Each CONSTANT_String can be at most 0xFFFF bytes (as a UTF8 string).
0695: * Returns a String, where each char, coerced to an int, is the length
0696: * of a substring of the input that is at most 0xFFFF bytes.
0697: */
0698: public static final String calculateSplit(String str) {
0699: int strLength = str.length();
0700: StringBuffer sbuf = new StringBuffer(20);
0701: // Where the current segments starts, as an index in 'str':
0702: int segmentStart = 0;
0703: int byteLength = 0; // Length in bytes of current segment so far.
0704: for (int i = 0; i < strLength; i++) {
0705: char ch = str.charAt(i);
0706: int bytes = ch >= 0x0800 ? 3 : ch >= 0x0080 || ch == 0 ? 2
0707: : 1;
0708: if (byteLength + bytes > 0xFFFF) {
0709: sbuf.append((char) (i - segmentStart));
0710: segmentStart = i;
0711: byteLength = 0;
0712: }
0713: byteLength += bytes;
0714: }
0715: sbuf.append((char) (strLength - segmentStart));
0716: return sbuf.toString();
0717: }
0718:
0719: /** Emit code to push the value of a constant String.
0720: * Uses CONSTANT_String and CONSTANT_Utf8 constant pool entries as needed.
0721: * Can handle Strings whose UTF8 length is greates than 0xFFFF bytes
0722: * (the limit of a CONSTANT_Utf8) by generating String concatenation.
0723: */
0724: public final void emitPushString(String str) {
0725: if (str == null)
0726: emitPushNull();
0727: else {
0728: int length = str.length();
0729: String segments = calculateSplit(str);
0730: int numSegments = segments.length();
0731: if (numSegments <= 1)
0732: emitPushConstant(getConstants().addString(str));
0733: else {
0734: if (numSegments == 2) {
0735: int firstSegment = (int) segments.charAt(0);
0736: emitPushString(str.substring(0, firstSegment));
0737: emitPushString(str.substring(firstSegment));
0738: Method concatMethod = Type.string_type
0739: .getDeclaredMethod("concat", 1);
0740: emitInvokeVirtual(concatMethod);
0741: } else {
0742: ClassType sbufType = ClassType
0743: .make("java.lang.StringBuffer");
0744: emitNew(sbufType);
0745: emitDup(sbufType);
0746: emitPushInt(length);
0747: Type[] args1 = { Type.int_type };
0748: emitInvokeSpecial(sbufType.getDeclaredMethod(
0749: "<init>", args1));
0750: Type[] args2 = { Type.string_type };
0751: Method appendMethod = sbufType.getDeclaredMethod(
0752: "append", args2);
0753: int segStart = 0;
0754: for (int seg = 0; seg < numSegments; seg++) {
0755: emitDup(sbufType);
0756: int segEnd = segStart
0757: + (int) segments.charAt(seg);
0758: emitPushString(str.substring(segStart, segEnd));
0759: emitInvokeVirtual(appendMethod);
0760: segStart = segEnd;
0761: }
0762: emitInvokeVirtual(Type.toString_method);
0763: }
0764: if (str == str.intern())
0765: emitInvokeVirtual(Type.string_type
0766: .getDeclaredMethod("intern", 0));
0767: return;
0768: }
0769: pushType(Type.string_type);
0770: }
0771: }
0772:
0773: /** Push a class constant pool entry.
0774: * This is only supported by JDK 1.5 and later. */
0775: public final void emitPushClass(String name) {
0776: emitPushConstant(getConstants().addClass(name));
0777: pushType(Type.java_lang_Class_type);
0778: }
0779:
0780: public void emitPushNull() {
0781: reserve(1);
0782: put1(1); // aconst_null
0783: pushType(Type.pointer_type);
0784: }
0785:
0786: public final void emitPushThis() {
0787: reserve(1);
0788: put1(42); // aload_0
0789: pushType(getMethod().getDeclaringClass());
0790: }
0791:
0792: /** Emit code to push a constant primitive array.
0793: * @param value The array value that we want the emitted code to re-create.
0794: * @param arrayType The ArrayType that matches value.
0795: */
0796: public final void emitPushPrimArray(Object value,
0797: ArrayType arrayType) {
0798: Type elementType = arrayType.getComponentType();
0799: int len = java.lang.reflect.Array.getLength(value);
0800: emitPushInt(len);
0801: emitNewArray(elementType);
0802: char sig = elementType.getSignature().charAt(0);
0803: for (int i = 0; i < len; i++) {
0804: long ival = 0;
0805: float fval = 0;
0806: double dval = 0;
0807: switch (sig) {
0808: case 'J':
0809: ival = ((long[]) value)[i];
0810: if (ival == 0)
0811: continue;
0812: break;
0813: case 'I':
0814: ival = ((int[]) value)[i];
0815: if (ival == 0)
0816: continue;
0817: break;
0818: case 'S':
0819: ival = ((short[]) value)[i];
0820: if (ival == 0)
0821: continue;
0822: break;
0823: case 'C':
0824: ival = ((char[]) value)[i];
0825: if (ival == 0)
0826: continue;
0827: break;
0828: case 'B':
0829: ival = ((byte[]) value)[i];
0830: if (ival == 0)
0831: continue;
0832: break;
0833: case 'Z':
0834: ival = ((boolean[]) value)[i] ? 1 : 0;
0835: if (ival == 0)
0836: continue;
0837: break;
0838: case 'F':
0839: fval = ((float[]) value)[i];
0840: if (fval == 0.0)
0841: continue;
0842: break;
0843: case 'D':
0844: dval = ((double[]) value)[i];
0845: if (dval == 0.0)
0846: continue;
0847: break;
0848: }
0849: emitDup(arrayType);
0850: emitPushInt(i);
0851: switch (sig) {
0852: case 'Z':
0853: case 'C':
0854: case 'B':
0855: case 'S':
0856: case 'I':
0857: emitPushInt((int) ival);
0858: break;
0859: case 'J':
0860: emitPushLong(ival);
0861: break;
0862: case 'F':
0863: emitPushFloat(fval);
0864: break;
0865: case 'D':
0866: emitPushDouble(dval);
0867: break;
0868: }
0869: emitArrayStore(elementType);
0870: }
0871: }
0872:
0873: void emitNewArray(int type_code) {
0874: reserve(2);
0875: put1(188); // newarray
0876: put1(type_code);
0877: }
0878:
0879: public final void emitArrayLength() {
0880: if (!(popType() instanceof ArrayType))
0881: throw new Error("non-array type in emitArrayLength");
0882:
0883: reserve(1);
0884: put1(190); // arraylength
0885: pushType(Type.int_type);
0886: }
0887:
0888: /* Returns an integer in the range 0 (for 'int') through 4 (for object
0889: reference) to 7 (for 'short') which matches the pattern of how JVM
0890: opcodes typically depend on the operand type. */
0891:
0892: private int adjustTypedOp(char sig) {
0893: switch (sig) {
0894: case 'I':
0895: return 0; // int
0896: case 'J':
0897: return 1; // long
0898: case 'F':
0899: return 2; // float
0900: case 'D':
0901: return 3; // double
0902: default:
0903: return 4; // object
0904: case 'B':
0905: case 'Z':
0906: return 5; // byte or boolean
0907: case 'C':
0908: return 6; // char
0909: case 'S':
0910: return 7; // short
0911: }
0912: }
0913:
0914: private int adjustTypedOp(Type type) {
0915: return adjustTypedOp(type.getSignature().charAt(0));
0916: }
0917:
0918: private void emitTypedOp(int op, Type type) {
0919: reserve(1);
0920: put1(op + adjustTypedOp(type));
0921: }
0922:
0923: private void emitTypedOp(int op, char sig) {
0924: reserve(1);
0925: put1(op + adjustTypedOp(sig));
0926: }
0927:
0928: /** Store into an element of an array.
0929: * Must already have pushed the array reference, the index,
0930: * and the new value (in that order).
0931: * Stack: ..., array, index, value => ...
0932: */
0933: public void emitArrayStore(Type element_type) {
0934: popType(); // Pop new value
0935: popType(); // Pop index
0936: popType(); // Pop array reference
0937: emitTypedOp(79, element_type);
0938: }
0939:
0940: /** Load an element from an array.
0941: * Must already have pushed the array and the index (in that order):
0942: * Stack: ..., array, index => ..., value */
0943: public void emitArrayLoad(Type element_type) {
0944: popType(); // Pop index
0945: popType(); // Pop array reference
0946: emitTypedOp(46, element_type);
0947: pushType(element_type);
0948: }
0949:
0950: /**
0951: * Invoke new on a class type.
0952: * Does not call the constructor!
0953: * @param type the desired new object type
0954: */
0955: public void emitNew(ClassType type) {
0956: reserve(3);
0957: put1(187); // new
0958: putIndex2(getConstants().addClass(type));
0959: pushType(type);
0960: }
0961:
0962: /** Compile code to allocate a new array.
0963: * The size should have been already pushed on the stack.
0964: * @param element_type type of the array elements
0965: */
0966: public void emitNewArray(Type element_type, int dims) {
0967: if (popType().promote() != Type.int_type)
0968: throw new Error("non-int dim. spec. in emitNewArray");
0969:
0970: if (element_type instanceof PrimType) {
0971: int code;
0972: switch (element_type.getSignature().charAt(0)) {
0973: case 'B':
0974: code = 8;
0975: break;
0976: case 'S':
0977: code = 9;
0978: break;
0979: case 'I':
0980: code = 10;
0981: break;
0982: case 'J':
0983: code = 11;
0984: break;
0985: case 'F':
0986: code = 6;
0987: break;
0988: case 'D':
0989: code = 7;
0990: break;
0991: case 'Z':
0992: code = 4;
0993: break;
0994: case 'C':
0995: code = 5;
0996: break;
0997: default:
0998: throw new Error("bad PrimType in emitNewArray");
0999: }
1000: emitNewArray(code);
1001: } else if (element_type instanceof ObjectType) {
1002: reserve(3);
1003: put1(189); // anewarray
1004: putIndex2(getConstants()
1005: .addClass((ObjectType) element_type));
1006: } else if (element_type instanceof ArrayType) {
1007: reserve(4);
1008: put1(197); // multianewarray
1009: putIndex2(getConstants().addClass(
1010: new ArrayType(element_type)));
1011: if (dims < 1 || dims > 255)
1012: throw new Error("dims out of range in emitNewArray");
1013: put1(dims);
1014: while (--dims > 0)
1015: // first dim already popped
1016: if (popType().promote() != Type.int_type)
1017: throw new Error(
1018: "non-int dim. spec. in emitNewArray");
1019: } else
1020: throw new Error("unimplemented type in emitNewArray");
1021:
1022: pushType(new ArrayType(element_type));
1023: }
1024:
1025: public void emitNewArray(Type element_type) {
1026: emitNewArray(element_type, 1);
1027: }
1028:
1029: // We may want to deprecate this, because it depends on popType.
1030: private void emitBinop(int base_code) {
1031: Type type2 = popType().promote();
1032: Type type1_raw = popType();
1033: Type type1 = type1_raw.promote();
1034: if (type1 != type2 || !(type1 instanceof PrimType))
1035: throw new Error(
1036: "non-matching or bad types in binary operation");
1037: emitTypedOp(base_code, type1);
1038: pushType(type1_raw);
1039: }
1040:
1041: private void emitBinop(int base_code, char sig) {
1042: popType();
1043: popType();
1044: emitTypedOp(base_code, sig);
1045: pushType(Type.signatureToPrimitive(sig));
1046: }
1047:
1048: private void emitBinop(int base_code, Type type) {
1049: popType();
1050: popType();
1051: emitTypedOp(base_code, type);
1052: pushType(type);
1053: }
1054:
1055: // public final void emitIntAdd () { put1(96); popType();}
1056: // public final void emitLongAdd () { put1(97); popType();}
1057: // public final void emitFloatAdd () { put1(98); popType();}
1058: // public final void emitDoubleAdd () { put1(99); popType();}
1059:
1060: public final void emitAdd(char sig) {
1061: emitBinop(96, sig);
1062: }
1063:
1064: public final void emitAdd(PrimType type) {
1065: emitBinop(96, type);
1066: }
1067:
1068: /** @deprecated */
1069: public final void emitAdd() {
1070: emitBinop(96);
1071: }
1072:
1073: public final void emitSub(char sig) {
1074: emitBinop(100, sig);
1075: }
1076:
1077: public final void emitSub(PrimType type) {
1078: emitBinop(100, type);
1079: }
1080:
1081: /** @deprecated */
1082: public final void emitSub() {
1083: emitBinop(100);
1084: }
1085:
1086: public final void emitMul() {
1087: emitBinop(104);
1088: }
1089:
1090: public final void emitDiv() {
1091: emitBinop(108);
1092: }
1093:
1094: public final void emitRem() {
1095: emitBinop(112);
1096: }
1097:
1098: public final void emitAnd() {
1099: emitBinop(126);
1100: }
1101:
1102: public final void emitIOr() {
1103: emitBinop(128);
1104: }
1105:
1106: public final void emitXOr() {
1107: emitBinop(130);
1108: }
1109:
1110: public final void emitShl() {
1111: emitShift(120);
1112: }
1113:
1114: public final void emitShr() {
1115: emitShift(122);
1116: }
1117:
1118: public final void emitUshr() {
1119: emitShift(124);
1120: }
1121:
1122: private void emitShift(int base_code) {
1123: Type type2 = popType().promote();
1124: Type type1_raw = popType();
1125: Type type1 = type1_raw.promote();
1126:
1127: if (type1 != Type.int_type && type1 != Type.long_type)
1128: throw new Error(
1129: "the value shifted must be an int or a long");
1130:
1131: if (type2 != Type.int_type)
1132: throw new Error("the amount of shift must be an int");
1133:
1134: emitTypedOp(base_code, type1);
1135: pushType(type1_raw);
1136: }
1137:
1138: public final void emitNot(Type type) {
1139: emitPushConstant(1, type);
1140: emitAdd();
1141: emitPushConstant(1, type);
1142: emitAnd();
1143: }
1144:
1145: public void emitPrimop(int opcode, int arg_count, Type retType) {
1146: reserve(1);
1147: while (--arg_count >= 0)
1148: popType();
1149: put1(opcode);
1150: pushType(retType);
1151: }
1152:
1153: void emitMaybeWide(int opcode, int index) {
1154: if (index >= 256) {
1155: put1(196); // wide
1156: put1(opcode);
1157: put2(index);
1158: } else {
1159: put1(opcode);
1160: put1(index);
1161: }
1162: }
1163:
1164: /**
1165: * Compile code to push the contents of a local variable onto the statck.
1166: * @param var The variable whose contents we want to push.
1167: */
1168: public final void emitLoad(Variable var) {
1169: if (var.dead())
1170: throw new Error("attempting to push dead variable");
1171: int offset = var.offset;
1172: if (offset < 0 || !var.isSimple())
1173: throw new Error(
1174: "attempting to load from unassigned variable "
1175: + var + " simple:" + var.isSimple()
1176: + ", offset: " + offset);
1177: Type type = var.getType().promote();
1178: reserve(4);
1179: int kind = adjustTypedOp(type);
1180: if (offset <= 3)
1181: put1(26 + 4 * kind + offset); // [ilfda]load_[0123]
1182: else
1183: emitMaybeWide(21 + kind, offset); // [ilfda]load
1184: pushType(var.getType());
1185: }
1186:
1187: public void emitStore(Variable var) {
1188: if (var.dead())
1189: throw new Error("attempting to push dead variable");
1190: int offset = var.offset;
1191: if (offset < 0 || !var.isSimple())
1192: throw new Error("attempting to store in unassigned " + var
1193: + " simple:" + var.isSimple() + ", offset: "
1194: + offset);
1195: Type type = var.getType().promote();
1196: reserve(4);
1197: popType();
1198: int kind = adjustTypedOp(type);
1199: if (offset <= 3)
1200: put1(59 + 4 * kind + offset); // [ilfda]store_[0123]
1201: else
1202: emitMaybeWide(54 + kind, offset); // [ilfda]store
1203: }
1204:
1205: public void emitInc(Variable var, short inc) {
1206: if (var.dead())
1207: throw new Error("attempting to increment dead variable");
1208: int offset = var.offset;
1209: if (offset < 0 || !var.isSimple())
1210: throw new Error(
1211: "attempting to increment unassigned variable"
1212: + var.getName() + " simple:"
1213: + var.isSimple() + ", offset: " + offset);
1214: Type type = var.getType().promote();
1215: reserve(6);
1216: if (type != Type.int_type)
1217: throw new Error("attempting to increment non-int variable");
1218:
1219: boolean wide = offset > 255 || inc > 255 || inc < -256;
1220:
1221: if (wide) {
1222: put1(196); // wide
1223: put1(132); // iinc
1224: put2(offset);
1225: put2(inc);
1226: } else {
1227: put1(132); // iinc
1228: put1(offset);
1229: put1(inc);
1230: }
1231: }
1232:
1233: private final void emitFieldop(Field field, int opcode) {
1234: reserve(3);
1235: put1(opcode);
1236: putIndex2(getConstants().addFieldRef(field));
1237: }
1238:
1239: /** Compile code to get a static field value.
1240: * Stack: ... => ..., value */
1241:
1242: public final void emitGetStatic(Field field) {
1243: pushType(field.type);
1244: emitFieldop(field, 178); // getstatic
1245: }
1246:
1247: /** Compile code to get a non-static field value.
1248: * Stack: ..., objectref => ..., value */
1249:
1250: public final void emitGetField(Field field) {
1251: popType();
1252: pushType(field.type);
1253: emitFieldop(field, 180); // getfield
1254: }
1255:
1256: /** Compile code to put a static field value.
1257: * Stack: ..., value => ... */
1258:
1259: public final void emitPutStatic(Field field) {
1260: popType();
1261: emitFieldop(field, 179); // putstatic
1262: }
1263:
1264: /** Compile code to put a non-static field value.
1265: * Stack: ..., objectref, value => ... */
1266:
1267: public final void emitPutField(Field field) {
1268: popType();
1269: popType();
1270: emitFieldop(field, 181); // putfield
1271: }
1272:
1273: /** Comptes the number of stack words taken by a list of types. */
1274: private int words(Type[] types) {
1275: int res = 0;
1276: for (int i = types.length; --i >= 0;)
1277: if (types[i].size > 4)
1278: res += 2;
1279: else
1280: res++;
1281: return res;
1282: }
1283:
1284: public void emitInvokeMethod(Method method, int opcode) {
1285: reserve(opcode == 185 ? 5 : 3);
1286: int arg_count = method.arg_types.length;
1287: boolean is_invokestatic = opcode == 184;
1288: if (is_invokestatic != ((method.access_flags & Access.STATIC) != 0))
1289: throw new Error(
1290: "emitInvokeXxx static flag mis-match method.flags="
1291: + method.access_flags);
1292: if (!is_invokestatic)
1293: arg_count++;
1294: put1(opcode); // invokevirtual, invokespecial, or invokestatic
1295: putIndex2(getConstants().addMethodRef(method));
1296: if (opcode == 185) // invokeinterface
1297: {
1298: put1(words(method.arg_types) + 1); // 1 word for 'this'
1299: put1(0);
1300: }
1301: while (--arg_count >= 0)
1302: popType();
1303: if (method.return_type.size != 0)
1304: pushType(method.return_type);
1305: }
1306:
1307: public void emitInvoke(Method method) {
1308: int opcode;
1309: if ((method.access_flags & Access.STATIC) != 0)
1310: opcode = 184; // invokestatic
1311: else if (method.classfile.isInterface())
1312: opcode = 185; // invokeinterface
1313: else if ("<init>".equals(method.getName()))
1314: opcode = 183; // invokespecial
1315: else
1316: opcode = 182; // invokevirtual
1317: emitInvokeMethod(method, opcode);
1318: }
1319:
1320: /** Compile a virtual method call.
1321: * The stack contains the 'this' object, followed by the arguments in order.
1322: * @param method the method to invoke virtually
1323: */
1324: public void emitInvokeVirtual(Method method) {
1325: emitInvokeMethod(method, 182); // invokevirtual
1326: }
1327:
1328: public void emitInvokeSpecial(Method method) {
1329: emitInvokeMethod(method, 183); // invokespecial
1330: }
1331:
1332: /** Compile a static method call.
1333: * The stack contains the the arguments in order.
1334: * @param method the static method to invoke
1335: */
1336: public void emitInvokeStatic(Method method) {
1337: emitInvokeMethod(method, 184); // invokestatic
1338: }
1339:
1340: public void emitInvokeInterface(Method method) {
1341: emitInvokeMethod(method, 185); // invokeinterface
1342: }
1343:
1344: final void emitTransfer(Label label, int opcode) {
1345: fixupAdd(FIXUP_TRANSFER, label);
1346: put1(opcode);
1347: PC += 2;
1348: }
1349:
1350: /** Compile an unconditional branch (goto).
1351: * @param label target of the branch (must be in this method).
1352: */
1353: public final void emitGoto(Label label) {
1354: fixupAdd(FIXUP_GOTO, label);
1355: reserve(3);
1356: put1(167);
1357: PC += 2;
1358: setUnreachable();
1359: }
1360:
1361: public final void emitJsr(Label label) {
1362: fixupAdd(FIXUP_JSR, label);
1363: reserve(3);
1364: put1(168);
1365: PC += 2;
1366: }
1367:
1368: public final void emitGotoIfCompare1(Label label, int opcode) {
1369: popType();
1370: reserve(3);
1371: emitTransfer(label, opcode);
1372: }
1373:
1374: public final void emitGotoIfIntEqZero(Label label) {
1375: emitGotoIfCompare1(label, 153);
1376: }
1377:
1378: public final void emitGotoIfIntNeZero(Label label) {
1379: emitGotoIfCompare1(label, 154);
1380: }
1381:
1382: public final void emitGotoIfIntLtZero(Label label) {
1383: emitGotoIfCompare1(label, 155);
1384: }
1385:
1386: public final void emitGotoIfIntGeZero(Label label) {
1387: emitGotoIfCompare1(label, 156);
1388: }
1389:
1390: public final void emitGotoIfIntGtZero(Label label) {
1391: emitGotoIfCompare1(label, 157);
1392: }
1393:
1394: public final void emitGotoIfIntLeZero(Label label) {
1395: emitGotoIfCompare1(label, 158);
1396: }
1397:
1398: public final void emitGotoIfCompare2(Label label, int logop) {
1399: if (logop < 153 || logop > 158)
1400: throw new Error(
1401: "emitGotoIfCompare2: logop must be one of ifeq...ifle");
1402:
1403: Type type2 = popType().promote();
1404: Type type1 = popType().promote();
1405: reserve(4);
1406: char sig1 = type1.getSignature().charAt(0);
1407: char sig2 = type2.getSignature().charAt(0);
1408:
1409: boolean cmpg = (logop == 155 || logop == 158); // iflt,ifle
1410:
1411: if (sig1 == 'I' && sig2 == 'I')
1412: logop += 6; // iflt -> if_icmplt etc.
1413: else if (sig1 == 'J' && sig2 == 'J')
1414: put1(148); // lcmp
1415: else if (sig1 == 'F' && sig2 == 'F')
1416: put1(cmpg ? 149 : 150); // fcmpl/fcmpg
1417: else if (sig1 == 'D' && sig2 == 'D')
1418: put1(cmpg ? 151 : 152); // dcmpl/dcmpg
1419: else if ((sig1 == 'L' || sig1 == '[')
1420: && (sig2 == 'L' || sig2 == '[') && logop <= 154)
1421: logop += 12; // ifeq->if_acmpeq, ifne->if_acmpne
1422: else
1423: throw new Error("invalid types to emitGotoIfCompare2");
1424:
1425: emitTransfer(label, logop);
1426: }
1427:
1428: // binary comparisons
1429: /** @deprecated */
1430: public final void emitGotoIfEq(Label label, boolean invert) {
1431: emitGotoIfCompare2(label, invert ? 154 : 153);
1432: }
1433:
1434: /** Compile a conditional transfer if 2 top stack elements are equal. */
1435: public final void emitGotoIfEq(Label label) {
1436: emitGotoIfCompare2(label, 153);
1437: }
1438:
1439: /** Compile conditional transfer if 2 top stack elements are not equal. */
1440: public final void emitGotoIfNE(Label label) {
1441: emitGotoIfCompare2(label, 154);
1442: }
1443:
1444: public final void emitGotoIfLt(Label label) {
1445: emitGotoIfCompare2(label, 155);
1446: }
1447:
1448: public final void emitGotoIfGe(Label label) {
1449: emitGotoIfCompare2(label, 156);
1450: }
1451:
1452: public final void emitGotoIfGt(Label label) {
1453: emitGotoIfCompare2(label, 157);
1454: }
1455:
1456: public final void emitGotoIfLe(Label label) {
1457: emitGotoIfCompare2(label, 158);
1458: }
1459:
1460: /** Compile start of a conditional:
1461: * <tt>if (!(<var>x</var> opcode 0)) ...</tt>.
1462: * The value of <var>x</var> must already have been pushed. */
1463: public final void emitIfCompare1(int opcode) {
1464: IfState new_if = new IfState(this );
1465: if (popType().promote() != Type.int_type)
1466: throw new Error("non-int type to emitIfCompare1");
1467: reserve(3);
1468: emitTransfer(new_if.end_label, opcode);
1469: new_if.start_stack_size = SP;
1470: }
1471:
1472: /** Compile start of conditional: <tt>if (x != 0) ...</tt>.
1473: * Also use this if you have pushed a boolean value: if (b) ... */
1474: public final void emitIfIntNotZero() {
1475: emitIfCompare1(153); // ifeq
1476: }
1477:
1478: /** Compile start of conditional: <tt>if (x == 0) ...</tt>.
1479: * Also use this if you have pushed a boolean value: if (!b) ... */
1480: public final void emitIfIntEqZero() {
1481: emitIfCompare1(154); // ifne
1482: }
1483:
1484: /** Compile start of conditional: <tt>if (x <= 0)</tt>. */
1485: public final void emitIfIntLEqZero() {
1486: emitIfCompare1(157); // ifgt
1487: }
1488:
1489: /** Compile start of a conditional: <tt>if (!(x opcode null)) ...</tt>.
1490: * The value of <tt>x</tt> must already have been pushed and must be of
1491: * reference type. */
1492: public final void emitIfRefCompare1(int opcode) {
1493: IfState new_if = new IfState(this );
1494: if (!(popType() instanceof ObjectType))
1495: throw new Error("non-ref type to emitIfRefCompare1");
1496: reserve(3);
1497: emitTransfer(new_if.end_label, opcode);
1498: new_if.start_stack_size = SP;
1499: }
1500:
1501: /** Compile start of conditional: if (x != null) */
1502: public final void emitIfNotNull() {
1503: emitIfRefCompare1(198); // ifnull
1504: }
1505:
1506: /** Compile start of conditional: if (x == null) */
1507: public final void emitIfNull() {
1508: emitIfRefCompare1(199); // ifnonnull
1509: }
1510:
1511: /** Compile start of a conditional: if (!(x OPCODE y)) ...
1512: * The value of x and y must already have been pushed. */
1513: public final void emitIfIntCompare(int opcode) {
1514: IfState new_if = new IfState(this );
1515: popType();
1516: popType();
1517: reserve(3);
1518: emitTransfer(new_if.end_label, opcode);
1519: new_if.start_stack_size = SP;
1520: }
1521:
1522: /* Compile start of a conditional: if (x < y) ... */
1523: public final void emitIfIntLt() {
1524: emitIfIntCompare(162); // if_icmpge
1525: }
1526:
1527: /** Compile start of a conditional: if (x != y) ...
1528: * The values of x and y must already have been pushed. */
1529: public final void emitIfNEq() {
1530: IfState new_if = new IfState(this );
1531: emitGotoIfEq(new_if.end_label);
1532: new_if.start_stack_size = SP;
1533: }
1534:
1535: /** Compile start of a conditional: if (x == y) ...
1536: * The values of x and y must already have been pushed. */
1537: public final void emitIfEq() {
1538: IfState new_if = new IfState(this );
1539: emitGotoIfNE(new_if.end_label);
1540: new_if.start_stack_size = SP;
1541: }
1542:
1543: /** Compile start of a conditional: if (x < y) ...
1544: * The values of x and y must already have been pushed. */
1545: public final void emitIfLt() {
1546: IfState new_if = new IfState(this );
1547: emitGotoIfGe(new_if.end_label);
1548: new_if.start_stack_size = SP;
1549: }
1550:
1551: /** Compile start of a conditional: if (x >= y) ...
1552: * The values of x and y must already have been pushed. */
1553: public final void emitIfGe() {
1554: IfState new_if = new IfState(this );
1555: emitGotoIfLt(new_if.end_label);
1556: new_if.start_stack_size = SP;
1557: }
1558:
1559: /** Compile start of a conditional: if (x > y) ...
1560: * The values of x and y must already have been pushed. */
1561: public final void emitIfGt() {
1562: IfState new_if = new IfState(this );
1563: emitGotoIfLe(new_if.end_label);
1564: new_if.start_stack_size = SP;
1565: }
1566:
1567: /** Compile start of a conditional: if (x <= y) ...
1568: * The values of x and y must already have been pushed. */
1569: public final void emitIfLe() {
1570: IfState new_if = new IfState(this );
1571: emitGotoIfGt(new_if.end_label);
1572: new_if.start_stack_size = SP;
1573: }
1574:
1575: /** Emit a 'ret' instruction.
1576: * @param var the variable containing the return address */
1577: public void emitRet(Variable var) {
1578: int offset = var.offset;
1579: if (offset < 256) {
1580: reserve(2);
1581: put1(169); // ret
1582: put1(offset);
1583: } else {
1584: reserve(4);
1585: put1(196); // wide
1586: put1(169); // ret
1587: put2(offset);
1588: }
1589: }
1590:
1591: public final void emitThen() {
1592: if_stack.start_stack_size = SP;
1593: }
1594:
1595: public final void emitIfThen() {
1596: new IfState(this , null);
1597: }
1598:
1599: /** Compile start of else clause. */
1600: public final void emitElse() {
1601: Label else_label = if_stack.end_label;
1602: Label end_label = new Label(this );
1603: if_stack.end_label = end_label;
1604: if (reachableHere()) {
1605: int growth = SP - if_stack.start_stack_size;
1606: if_stack.stack_growth = growth;
1607: if (growth > 0) {
1608: if_stack.then_stacked_types = new Type[growth];
1609: System.arraycopy(stack_types,
1610: if_stack.start_stack_size,
1611: if_stack.then_stacked_types, 0, growth);
1612: } else
1613: if_stack.then_stacked_types = new Type[0]; // ???
1614: emitGoto(end_label);
1615: }
1616: while (SP > if_stack.start_stack_size)
1617: popType();
1618: SP = if_stack.start_stack_size;
1619: if (else_label != null)
1620: else_label.define(this );
1621: if_stack.doing_else = true;
1622: }
1623:
1624: /** Compile end of conditional. */
1625: public final void emitFi() {
1626: boolean make_unreachable = false;
1627: if (!if_stack.doing_else) { // There was no 'else' clause.
1628: if (reachableHere() && SP != if_stack.start_stack_size)
1629: throw new Error(
1630: "at PC "
1631: + PC
1632: + " then clause grows stack with no else clause");
1633: } else if (if_stack.then_stacked_types != null) {
1634: int then_clause_stack_size = if_stack.start_stack_size
1635: + if_stack.stack_growth;
1636: if (!reachableHere()) {
1637: if (if_stack.stack_growth > 0)
1638: System.arraycopy(if_stack.then_stacked_types, 0,
1639: stack_types, if_stack.start_stack_size,
1640: if_stack.stack_growth);
1641: SP = then_clause_stack_size;
1642: } else if (SP != then_clause_stack_size)
1643: throw new Error("at PC " + PC
1644: + ": SP at end of 'then' was "
1645: + then_clause_stack_size
1646: + " while SP at end of 'else' was " + SP);
1647: } else if (unreachable_here)
1648: make_unreachable = true;
1649:
1650: if (if_stack.end_label != null)
1651: if_stack.end_label.define(this );
1652: if (make_unreachable)
1653: setUnreachable();
1654: // Pop the if_stack.
1655: if_stack = if_stack.previous;
1656: }
1657:
1658: public final void emitConvert(Type from, Type to) {
1659: String to_sig = to.getSignature();
1660: String from_sig = from.getSignature();
1661: int op = -1;
1662: if (to_sig.length() == 1 || from_sig.length() == 1) {
1663: char to_sig0 = to_sig.charAt(0);
1664: char from_sig0 = from_sig.charAt(0);
1665: if (from_sig0 == to_sig0)
1666: return;
1667: if (from.size < 4)
1668: from_sig0 = 'I';
1669: if (to.size < 4) {
1670: emitConvert(from, Type.int_type);
1671: from_sig0 = 'I';
1672: }
1673: if (from_sig0 == to_sig0)
1674: return;
1675: switch (from_sig0) {
1676: case 'I':
1677: switch (to_sig0) {
1678: case 'B':
1679: op = 145;
1680: break; // i2b
1681: case 'C':
1682: op = 146;
1683: break; // i2c
1684: case 'S':
1685: op = 147;
1686: break; // i2s
1687: case 'J':
1688: op = 133;
1689: break; // i2l
1690: case 'F':
1691: op = 134;
1692: break; // i2f
1693: case 'D':
1694: op = 135;
1695: break; // i2d
1696: }
1697: break;
1698: case 'J':
1699: switch (to_sig0) {
1700: case 'I':
1701: op = 136;
1702: break; // l2i
1703: case 'F':
1704: op = 137;
1705: break; // l2f
1706: case 'D':
1707: op = 138;
1708: break; // l2d
1709: }
1710: break;
1711: case 'F':
1712: switch (to_sig0) {
1713: case 'I':
1714: op = 139;
1715: break; // f2i
1716: case 'J':
1717: op = 140;
1718: break; // f2l
1719: case 'D':
1720: op = 141;
1721: break; // f2d
1722: }
1723: break;
1724: case 'D':
1725: switch (to_sig0) {
1726: case 'I':
1727: op = 142;
1728: break; // d2i
1729: case 'J':
1730: op = 143;
1731: break; // d2l
1732: case 'F':
1733: op = 144;
1734: break; // d2f
1735: }
1736: break;
1737: }
1738: }
1739: if (op < 0)
1740: throw new Error("unsupported CodeAttr.emitConvert");
1741: reserve(1);
1742: popType();
1743: put1(op);
1744: pushType(to);
1745: }
1746:
1747: private void emitCheckcast(Type type, int opcode) {
1748: reserve(3);
1749: popType();
1750: put1(opcode);
1751: if (type instanceof ArrayType) {
1752: ArrayType atype = (ArrayType) type;
1753: CpoolUtf8 name = getConstants().addUtf8(atype.signature);
1754: putIndex2(getConstants().addClass(name));
1755: } else if (type instanceof ClassType) {
1756: putIndex2(getConstants().addClass((ClassType) type));
1757: } else
1758: throw new Error("unimplemented type " + type
1759: + " in emitCheckcast/emitInstanceof");
1760: }
1761:
1762: public static boolean castNeeded(Type top, Type required) {
1763: for (;;) {
1764: if (required instanceof ClassType
1765: && top instanceof ClassType
1766: && ((ClassType) top)
1767: .isSubclass((ClassType) required))
1768: return false;
1769: else if (required instanceof ArrayType
1770: && top instanceof ArrayType) {
1771: required = ((ArrayType) required).getComponentType();
1772: top = ((ArrayType) top).getComponentType();
1773: continue;
1774: }
1775: return true;
1776: }
1777: }
1778:
1779: public void emitCheckcast(Type type) {
1780: if (castNeeded(topType(), type)) {
1781: emitCheckcast(type, 192);
1782: pushType(type);
1783: }
1784: }
1785:
1786: public void emitInstanceof(Type type) {
1787: emitCheckcast(type, 193);
1788: pushType(Type.boolean_type);
1789: }
1790:
1791: public final void emitThrow() {
1792: popType();
1793: reserve(1);
1794: put1(191); // athrow
1795: setUnreachable();
1796: }
1797:
1798: public final void emitMonitorEnter() {
1799: popType();
1800: reserve(1);
1801: put1(194); // monitorenter
1802: }
1803:
1804: public final void emitMonitorExit() {
1805: popType();
1806: reserve(1);
1807: put1(195); // monitorexit
1808: }
1809:
1810: /** Call pending finalizer functions.
1811: * @param limit Only call finalizers more recent than this.
1812: */
1813: public void doPendingFinalizers(TryState limit) {
1814: TryState stack = try_stack;
1815:
1816: /* If a value is returned, it must be saved to a local variable,
1817: to prevent a verification error because of inconsistent stack sizes.
1818: */
1819: boolean saveResult = !getMethod().getReturnType().isVoid();
1820: Variable result = null;
1821:
1822: while (stack != limit) {
1823: if (stack.finally_subr != null // there is a finally block
1824: && stack.finally_ret_addr == null) // 'return' is not inside it
1825: {
1826: if (saveResult && result == null) {
1827: result = addLocal(topType());
1828: emitStore(result);
1829: }
1830: emitJsr(stack.finally_subr);
1831: }
1832:
1833: stack = stack.previous;
1834: }
1835:
1836: if (result != null)
1837: emitLoad(result);
1838: // We'd like to do freeLocal on the result Variable, but then we risk
1839: // it being re-used in a finalizer, which would trash its value. We
1840: // don't have any convenient way to to do that (the pending Scope
1841: // mechanism is over-kill), we for now we just leak the Variable.
1842: }
1843:
1844: /**
1845: * Compile a method return.
1846: * If inside a 'catch' clause, first call 'finally' clauses.
1847: * The return value (unless the return type is void) must be on the stack,
1848: * and have the correct type.
1849: */
1850: public final void emitReturn() {
1851: doPendingFinalizers(null);
1852: if (getMethod().getReturnType().size == 0) {
1853: reserve(1);
1854: put1(177); // return
1855: } else
1856: emitTypedOp(172, popType().promote());
1857: setUnreachable();
1858: }
1859:
1860: /** Add an exception handler. */
1861: public void addHandler(int start_pc, int end_pc, int handler_pc,
1862: int catch_type) {
1863: int index = 4 * exception_table_length;
1864: if (exception_table == null) {
1865: exception_table = new short[20];
1866: } else if (exception_table.length <= index) {
1867: short[] new_table = new short[2 * exception_table.length];
1868: System.arraycopy(exception_table, 0, new_table, 0, index);
1869: exception_table = new_table;
1870: }
1871: exception_table[index++] = (short) start_pc;
1872: exception_table[index++] = (short) end_pc;
1873: exception_table[index++] = (short) handler_pc;
1874: exception_table[index++] = (short) catch_type;
1875: exception_table_length++;
1876: }
1877:
1878: /** Add an exception handler. */
1879: public void addHandler(Label start_try, Label end_try,
1880: ClassType catch_type) {
1881: ConstantPool constants = getConstants();
1882: int catch_type_index;
1883: if (catch_type == null)
1884: catch_type_index = 0;
1885: else
1886: catch_type_index = constants.addClass(catch_type).index;
1887: fixupAdd(FIXUP_TRY, start_try);
1888: fixupAdd(FIXUP_CATCH, catch_type_index, end_try);
1889: }
1890:
1891: /** Beginning of code that has a cleanup handler.
1892: * This is similar to a try-finally, but the cleanup is only
1893: * done in the case of an exception. Alternatively, the try clause
1894: * has to manually do the cleanup with code duplication.
1895: * Equivalent to: <code>try <var>body</var> catch (Throwable ex) { <var>cleanup</var>; throw ex; }</code>
1896: * Call <code>emitWithCleanupStart</code> before the <code><var>body</var></code>.
1897: */
1898: public void emitWithCleanupStart() {
1899: int savedSP = SP;
1900: SP = 0;
1901: emitTryStart(false, null);
1902: SP = savedSP;
1903: }
1904:
1905: /** Called after a <code><var>body</var></code> that has a <code><var>cleanup</var></code> clause.
1906: * Followed by the <code><var>cleanup</var></code> code.
1907: */
1908: public void emitWithCleanupCatch(Variable catchVar) {
1909: emitTryEnd();
1910: Type[] savedTypes;
1911: if (SP > 0) {
1912: savedTypes = new Type[SP];
1913: System.arraycopy(stack_types, 0, savedTypes, 0, SP);
1914: SP = 0;
1915: } else
1916: savedTypes = null;
1917: try_stack.savedTypes = savedTypes;
1918:
1919: try_stack.saved_result = catchVar;
1920: int save_SP = SP;
1921: emitCatchStart(catchVar);
1922: }
1923:
1924: /** Called after generating a <code><var>cleanup</var></code> handler. */
1925:
1926: public void emitWithCleanupDone() {
1927: Variable catchVar = try_stack.saved_result;
1928: try_stack.saved_result = null;
1929: if (catchVar != null)
1930: emitLoad(catchVar);
1931: emitThrow();
1932: emitCatchEnd();
1933: Type[] savedTypes = try_stack.savedTypes;
1934: emitTryCatchEnd();
1935: if (savedTypes != null) {
1936: SP = savedTypes.length;
1937: if (SP >= stack_types.length)
1938: stack_types = savedTypes;
1939: else
1940: System.arraycopy(savedTypes, 0, stack_types, 0, SP);
1941: } else
1942: SP = 0;
1943: }
1944:
1945: public void emitTryStart(boolean has_finally, Type result_type) {
1946: if (result_type != null && result_type.isVoid())
1947: result_type = null;
1948: Variable[] savedStack = null;
1949: if (result_type != null || SP > 0)
1950: pushScope();
1951: if (SP > 0) {
1952: savedStack = new Variable[SP];
1953: int i = 0;
1954: while (SP > 0) {
1955: Variable var = addLocal(topType());
1956: emitStore(var);
1957: savedStack[i++] = var;
1958: }
1959: }
1960: TryState try_state = new TryState(this );
1961: try_state.savedStack = savedStack;
1962: if (result_type != null)
1963: try_state.saved_result = addLocal(result_type);
1964: if (has_finally)
1965: try_state.finally_subr = new Label();
1966: }
1967:
1968: public void emitTryEnd() {
1969: if (try_stack.end_label == null) {
1970: if (try_stack.saved_result != null && reachableHere())
1971: emitStore(try_stack.saved_result);
1972: try_stack.end_label = new Label();
1973: if (reachableHere()) {
1974: if (try_stack.finally_subr != null)
1975: emitJsr(try_stack.finally_subr);
1976: emitGoto(try_stack.end_label);
1977: }
1978: try_stack.end_try = getLabel();
1979: }
1980: }
1981:
1982: public void emitCatchStart(Variable var) {
1983: emitTryEnd();
1984: SP = 0;
1985: if (try_stack.try_type != null)
1986: emitCatchEnd();
1987: ClassType type = var == null ? null : (ClassType) var.getType();
1988: try_stack.try_type = type;
1989: addHandler(try_stack.start_try, try_stack.end_try, type);
1990: if (var != null) {
1991: pushType(type);
1992: emitStore(var);
1993: } else
1994: pushType(Type.throwable_type);
1995: }
1996:
1997: public void emitCatchEnd() {
1998: if (reachableHere()) {
1999: if (try_stack.saved_result != null)
2000: emitStore(try_stack.saved_result);
2001: if (try_stack.finally_subr != null)
2002: emitJsr(try_stack.finally_subr);
2003: emitGoto(try_stack.end_label);
2004: }
2005: try_stack.try_type = null;
2006: }
2007:
2008: public void emitFinallyStart() {
2009: emitTryEnd();
2010: if (try_stack.try_type != null)
2011: emitCatchEnd();
2012: SP = 0;
2013: try_stack.end_try = getLabel();
2014:
2015: pushScope();
2016: Type except_type = Type.pointer_type;
2017: Variable except = addLocal(except_type);
2018: emitCatchStart(null);
2019: emitStore(except);
2020: emitJsr(try_stack.finally_subr);
2021: emitLoad(except);
2022: emitThrow();
2023:
2024: try_stack.finally_subr.define(this );
2025: Type ret_addr_type = Type.pointer_type;
2026: try_stack.finally_ret_addr = addLocal(ret_addr_type);
2027: pushType(ret_addr_type);
2028: emitStore(try_stack.finally_ret_addr);
2029: }
2030:
2031: public void emitFinallyEnd() {
2032: emitRet(try_stack.finally_ret_addr);
2033: setUnreachable();
2034: popScope();
2035: try_stack.finally_subr = null;
2036: }
2037:
2038: public void emitTryCatchEnd() {
2039: if (try_stack.finally_subr != null)
2040: emitFinallyEnd();
2041: try_stack.end_label.define(this );
2042: Variable[] vars = try_stack.savedStack;
2043: if (vars != null) {
2044: for (int i = vars.length; --i >= 0;) {
2045: Variable v = vars[i];
2046: if (v != null) {
2047: emitLoad(v);
2048: }
2049: }
2050: }
2051: if (try_stack.saved_result != null)
2052: emitLoad(try_stack.saved_result);
2053: if (try_stack.saved_result != null || vars != null)
2054: popScope();
2055: try_stack = try_stack.previous;
2056: }
2057:
2058: public final TryState getCurrentTry() {
2059: return try_stack;
2060: }
2061:
2062: public final boolean isInTry() {
2063: // This also return true if we're in a catch clause, but that is
2064: // good enough for now.
2065: return try_stack != null;
2066: }
2067:
2068: /** Compile a tail-call to position 0 of the current procedure.
2069: * @param pop_args if true, copy argument registers (except this) from stack.
2070: * @param scope Scope whose start we jump back to. */
2071: public void emitTailCall(boolean pop_args, Scope scope) {
2072: if (pop_args) {
2073: Method meth = getMethod();
2074: int arg_slots = ((meth.access_flags & Access.STATIC) != 0) ? 0
2075: : 1;
2076: for (int i = meth.arg_types.length; --i >= 0;)
2077: arg_slots += meth.arg_types[i].size > 4 ? 2 : 1;
2078: for (int i = meth.arg_types.length; --i >= 0;) {
2079: arg_slots -= meth.arg_types[i].size > 4 ? 2 : 1;
2080: emitStore(locals.used[arg_slots]);
2081: }
2082: }
2083: emitGoto(scope.start);
2084: }
2085:
2086: public void processFixups() {
2087: if (fixup_count == 0)
2088: return;
2089:
2090: // For each label, set it to its maximum limit, assuming all
2091: // fixups causes the code the be expanded. We need a prepass
2092: // for this, since FIXUP_MOVEs can cause code to be reordered.
2093: // Also, convert each FIXUP_MOVE_TO_END to FIXUP_MOVE.
2094:
2095: int delta = 0;
2096: int instruction_tail = fixup_count;
2097: fixupAdd(CodeAttr.FIXUP_MOVE, 0, null);
2098:
2099: loop1: for (int i = 0;;) {
2100: int offset = fixup_offsets[i];
2101: int kind = offset & 15;
2102: offset >>= 4;
2103: Label label = fixup_labels[i];
2104: switch (kind) {
2105: case FIXUP_TRY:
2106: case FIXUP_LINE_PC:
2107: i++;
2108: case FIXUP_NONE:
2109: case FIXUP_CASE:
2110: case FIXUP_DELETE3:
2111: break;
2112: case FIXUP_DEFINE:
2113: label.position += delta;
2114: break;
2115: case FIXUP_SWITCH:
2116: delta += 3; // May need to add up to 3 padding bytes.
2117: break;
2118: case FIXUP_GOTO:
2119: // The first test fails in this case: GOTO L2; L1: L2: FIXME
2120: if (label.first_fixup == i + 1
2121: && fixupOffset(i + 1) == offset + 3) {
2122: // Optimize: GOTO L; L:
2123: fixup_offsets[i] = (offset << 4) | FIXUP_DELETE3;
2124: fixup_labels[i] = null;
2125: delta -= 3;
2126: break;
2127: }
2128: // ... else fall through ...
2129: case FIXUP_JSR:
2130: if (PC >= 0x8000)
2131: delta += 2; // May need to convert goto->goto_w, jsr->jsr_w.
2132: break;
2133: case FIXUP_TRANSFER:
2134: if (PC >= 0x8000)
2135: delta += 5; // May need to add a goto_w.
2136: break;
2137: case FIXUP_MOVE_TO_END:
2138: fixup_labels[instruction_tail] = fixup_labels[i + 1];
2139: instruction_tail = offset;
2140: // ... fall through ...
2141: case FIXUP_MOVE:
2142: int cur_pc = ((i + 1) >= fixup_count ? PC
2143: : fixupOffset(fixup_labels[i + 1].first_fixup));
2144: fixup_offsets[i] = (cur_pc << 4) | FIXUP_MOVE;
2145: if (label == null)
2146: break loop1;
2147: else {
2148: i = label.first_fixup;
2149: int next_pc = fixupOffset(i);
2150: delta = (cur_pc + delta) - next_pc;
2151: continue;
2152: }
2153: default:
2154: throw new Error("unexpected fixup");
2155: }
2156: i++;
2157: }
2158: // Next a loop to fix the position of each label, and calculate
2159: // the exact number of code bytes.
2160:
2161: // Number of bytes to be inserted or (if negative) removed, so far.
2162: int new_size = PC;
2163: // Current delta between final PC and offset in generate code array.
2164: delta = 0;
2165: loop2: for (int i = 0; i < fixup_count;) {
2166: int offset = fixup_offsets[i];
2167: int kind = offset & 15;
2168: Label label = fixup_labels[i];
2169: if (label != null && label.position < 0)
2170: throw new Error("undefined label " + label);
2171: while (label != null
2172: && kind >= FIXUP_GOTO
2173: && kind <= FIXUP_TRANSFER2
2174: && label.first_fixup + 1 < fixup_count
2175: && (fixup_offsets[label.first_fixup + 1] == ((fixup_offsets[label.first_fixup] & 15) | FIXUP_GOTO))) {
2176: // Optimize JUMP L; ... L: GOTO X
2177: // (where the JUMP is any GOTO or other transfer)
2178: // by changing the JUMP L to JUMP X.
2179: label = fixup_labels[label.first_fixup + 1];
2180: fixup_labels[i] = label;
2181: }
2182: offset = offset >> 4;
2183: switch (kind) {
2184: case FIXUP_TRY:
2185: case FIXUP_LINE_PC:
2186: i++;
2187: case FIXUP_NONE:
2188: case FIXUP_CASE:
2189: break;
2190: case FIXUP_DELETE3:
2191: delta -= 3;
2192: new_size -= 3;
2193: break;
2194: case FIXUP_DEFINE:
2195: label.position = offset + delta;
2196: break;
2197: case FIXUP_SWITCH:
2198: int padding = 3 - (offset + delta) & 3;
2199: delta += padding;
2200: new_size += padding;
2201: break;
2202: case FIXUP_GOTO:
2203: case FIXUP_JSR:
2204: case FIXUP_TRANSFER:
2205: int rel = label.position - (offset + delta);
2206: if ((short) rel == rel) {
2207: fixup_offsets[i] = (offset << 4) | FIXUP_TRANSFER2;
2208: } else {
2209: delta += kind == FIXUP_TRANSFER ? 5 : 2; // need goto_w
2210: new_size += kind == FIXUP_TRANSFER ? 5 : 2; // need goto_w
2211: }
2212: break;
2213: case FIXUP_MOVE:
2214: if (label == null)
2215: break loop2;
2216: else {
2217: i = label.first_fixup;
2218: int next_pc = fixupOffset(i);
2219: delta = (offset + delta) - next_pc;
2220: continue;
2221: }
2222: default:
2223: throw new Error("unexpected fixup");
2224: }
2225: i++;
2226: }
2227:
2228: byte[] new_code = new byte[new_size];
2229: int new_pc = 0;
2230: int next_fixup_index = 0;
2231: int next_fixup_offset = fixupOffset(0);
2232: loop3: for (int old_pc = 0;;) {
2233: if (old_pc < next_fixup_offset) {
2234: new_code[new_pc++] = code[old_pc++];
2235: } else {
2236: int kind = fixup_offsets[next_fixup_index] & 15;
2237: Label label = fixup_labels[next_fixup_index];
2238: switch (kind) {
2239: case FIXUP_NONE:
2240: case FIXUP_DEFINE:
2241: break;
2242: case FIXUP_DELETE3:
2243: old_pc += 3;
2244: break;
2245: case FIXUP_TRANSFER2:
2246: delta = label.position - new_pc;
2247: new_code[new_pc++] = code[old_pc];
2248: new_code[new_pc++] = (byte) (delta >> 8);
2249: new_code[new_pc++] = (byte) (delta & 0xFF);
2250: old_pc += 3;
2251: break;
2252: case FIXUP_GOTO:
2253: case FIXUP_JSR:
2254: case FIXUP_TRANSFER:
2255: delta = label.position - new_pc;
2256: byte opcode = code[old_pc];
2257: if (kind == FIXUP_TRANSFER) {
2258: // convert: IF_xxx L to IF_NOT_xxx Lt; GOTO L; Lt:
2259: opcode = invert_opcode(opcode);
2260: new_code[new_pc++] = opcode;
2261: new_code[new_pc++] = 0;
2262: new_code[new_pc++] = 8; // 8 byte offset to Lt.
2263: opcode = (byte) 200; // goto_w
2264: } else {
2265: // Change goto to goto_w; jsr to jsr_w:
2266: opcode = (byte) (opcode + (200 - 167));
2267: }
2268: new_code[new_pc++] = opcode;
2269: new_code[new_pc++] = (byte) (delta >> 24);
2270: new_code[new_pc++] = (byte) (delta >> 16);
2271: new_code[new_pc++] = (byte) (delta >> 8);
2272: new_code[new_pc++] = (byte) (delta & 0xFF);
2273: old_pc += 3;
2274: break;
2275: case FIXUP_SWITCH:
2276: int padding = 3 - new_pc & 3;
2277: int switch_start = new_pc;
2278: new_code[new_pc++] = code[old_pc++];
2279: while (--padding >= 0)
2280: new_code[new_pc++] = 0;
2281: while (next_fixup_index < fixup_count
2282: && fixupKind(next_fixup_index + 1) == FIXUP_CASE) {
2283: next_fixup_index++;
2284: int offset = fixupOffset(next_fixup_index);
2285: while (old_pc < offset)
2286: new_code[new_pc++] = code[old_pc++];
2287: delta = (fixup_labels[next_fixup_index].position - switch_start);
2288: new_code[new_pc++] = (byte) (delta >> 24);
2289: new_code[new_pc++] = (byte) (delta >> 16);
2290: new_code[new_pc++] = (byte) (delta >> 8);
2291: new_code[new_pc++] = (byte) (delta & 0xFF);
2292: old_pc += 4;
2293: }
2294: break;
2295: case FIXUP_TRY:
2296: addHandler(
2297: fixup_labels[next_fixup_index].position,
2298: fixup_labels[next_fixup_index + 1].position,
2299: new_pc, fixupOffset(next_fixup_index + 1));
2300: next_fixup_index++;
2301: break;
2302: case FIXUP_LINE_PC:
2303: if (lines == null)
2304: lines = new LineNumbersAttr(this );
2305: next_fixup_index++;
2306: lines.put(fixupOffset(next_fixup_index), new_pc);
2307: break;
2308: case FIXUP_MOVE:
2309: if (label == null)
2310: break loop3;
2311: else {
2312: next_fixup_index = label.first_fixup;
2313: old_pc = fixupOffset(next_fixup_index);
2314: next_fixup_offset = old_pc;
2315: if (label.position != new_pc)
2316: throw new Error("bad pc");
2317: continue;
2318: }
2319: default:
2320: throw new Error("unexpected fixup");
2321: }
2322: next_fixup_index++;
2323: next_fixup_offset = fixupOffset(next_fixup_index);
2324: }
2325: }
2326: if (new_size != new_pc)
2327: throw new Error("PC confusion new_pc:" + new_pc
2328: + " new_size:" + new_size);
2329: PC = new_size;
2330: code = new_code;
2331: fixup_count = 0;
2332: fixup_labels = null;
2333: fixup_offsets = null;
2334: }
2335:
2336: public void assignConstants(ClassType cl) {
2337: if (locals != null && locals.container == null
2338: && !locals.isEmpty())
2339: locals.addToFrontOf(this );
2340: processFixups();
2341: if (instructionLineMode) {
2342: // A kludge to low-level debugging:
2343: // Define a "line number" for each instrction.
2344: if (lines == null)
2345: lines = new LineNumbersAttr(this );
2346: lines.linenumber_count = 0;
2347: int codeLen = getCodeLength();
2348: for (int i = 0; i < codeLen; i++)
2349: lines.put(i, i);
2350: }
2351: super .assignConstants(cl);
2352: Attribute.assignConstants(this , cl);
2353: }
2354:
2355: public final int getLength() {
2356: return 12 + getCodeLength() + 8 * exception_table_length
2357: + Attribute.getLengthAll(this );
2358: }
2359:
2360: public void write(DataOutputStream dstr) throws java.io.IOException {
2361: dstr.writeShort(max_stack);
2362: dstr.writeShort(max_locals);
2363: dstr.writeInt(PC);
2364: dstr.write(code, 0, PC);
2365:
2366: dstr.writeShort(exception_table_length);
2367: int count = exception_table_length;
2368: for (int i = 0; --count >= 0; i += 4) {
2369: dstr.writeShort(exception_table[i]);
2370: dstr.writeShort(exception_table[i + 1]);
2371: dstr.writeShort(exception_table[i + 2]);
2372: dstr.writeShort(exception_table[i + 3]);
2373: }
2374:
2375: Attribute.writeAll(this , dstr);
2376: }
2377:
2378: public void print(ClassTypeWriter dst) {
2379: dst.print("Attribute \"");
2380: dst.print(getName());
2381: dst.print("\", length:");
2382: dst.print(getLength());
2383: dst.print(", max_stack:");
2384: dst.print(max_stack);
2385: dst.print(", max_locals:");
2386: dst.print(max_locals);
2387: dst.print(", code_length:");
2388: int length = getCodeLength();
2389: dst.println(length);
2390: disAssemble(dst, 0, length);
2391: if (exception_table_length > 0) {
2392: dst.print("Exceptions (count: ");
2393: dst.print(exception_table_length);
2394: dst.println("):");
2395: int count = exception_table_length;
2396: for (int i = 0; --count >= 0; i += 4) {
2397: dst.print(" start: ");
2398: dst.print(exception_table[i] & 0xffff);
2399: dst.print(", end: ");
2400: dst.print(exception_table[i + 1] & 0xffff);
2401: dst.print(", handler: ");
2402: dst.print(exception_table[i + 2] & 0xffff);
2403: dst.print(", type: ");
2404: int catch_type_index = exception_table[i + 3] & 0xffff;
2405: if (catch_type_index == 0)
2406: dst.print("0 /* finally */");
2407: else {
2408: dst.printOptionalIndex(catch_type_index);
2409: dst.printConstantTersely(catch_type_index,
2410: ConstantPool.CLASS);
2411: }
2412: dst.println();
2413: }
2414: }
2415: dst.printAttributes(this );
2416: }
2417:
2418: /* DEBUGGING:
2419: public void disAssembleWithFixups (ClassTypeWriter dst)
2420: {
2421: if (fixup_count == 0)
2422: {
2423: disAssemble(dst, 0, PC);
2424: return;
2425: }
2426: int prev_pc = 0;
2427: for (int i = 0; i < fixup_count; )
2428: {
2429: int offset = fixup_offsets[i];
2430: int kind = offset & 15;
2431: Label label = fixup_labels[i];
2432: offset = offset >> 4;
2433: int pc = offset;
2434: if (kind == FIXUP_MOVE || kind == FIXUP_MOVE_TO_END)
2435: pc = (i+1 >= fixup_count) ? PC : fixup_offsets[i+1] >> 4;
2436: disAssemble(dst, prev_pc, offset);
2437: prev_pc = pc;
2438: dst.print("fixup#"); dst.print(i);
2439: dst.print(" @"); dst.print(offset);
2440: switch (kind)
2441: {
2442: case FIXUP_NONE:
2443: dst.println(" NONE");
2444: break;
2445: case FIXUP_DEFINE:
2446: dst.print(" DEFINE ");
2447: dst.println(label);
2448: break;
2449: case FIXUP_SWITCH:
2450: dst.println(" SWITCH");
2451: break;
2452: case FIXUP_CASE:
2453: dst.println(" CASE");
2454: break;
2455: case FIXUP_GOTO:
2456: dst.print(" GOTO ");
2457: dst.println(label);
2458: break;
2459: case FIXUP_TRANSFER:
2460: dst.print(" TRANSFER ");
2461: dst.println(label);
2462: break;
2463: case FIXUP_TRANSFER2:
2464: dst.print(" TRANSFER2 ");
2465: dst.println(label);
2466: break;
2467: case FIXUP_DELETE3:
2468: dst.println(" DELETE3");
2469: break;
2470: case FIXUP_MOVE:
2471: dst.print(" MOVE ");
2472: dst.println(label);
2473: break;
2474: case FIXUP_MOVE_TO_END:
2475: dst.print(" MOVE_TO_END ");
2476: dst.println(label);
2477: break;
2478: case FIXUP_TRY:
2479: dst.print(" TRY start: ");
2480: dst.println(label);
2481: i++;
2482: dst.print(" - end: ");
2483: dst.print(fixup_labels[i]);
2484: dst.print(" type: ");
2485: dst.println(fixup_offsets[i] >> 4);
2486: break;
2487: case FIXUP_LINE_PC:
2488: dst.print(" LINE ");
2489: i++;
2490: dst.println(fixup_offsets[i] >> 4);
2491: break;
2492: default:
2493: dst.println(" kind:"+fixupKind(i)+" offset:"+fixupOffset(i)+" "+fixup_labels[i]);
2494: }
2495: i++;
2496: }
2497: }
2498: */
2499:
2500: public void disAssemble(ClassTypeWriter dst, int start, int limit) {
2501: boolean wide = false;
2502: for (int i = start; i < limit;) {
2503: int oldpc = i++;
2504: int op = code[oldpc] & 0xff;
2505: String str = Integer.toString(oldpc);
2506: int printConstant = 0;
2507: int j = str.length();
2508: while (++j <= 3)
2509: dst.print(' ');
2510: dst.print(str);
2511: dst.print(": ");
2512: // We do a rough binary partition of the opcodes.
2513: if (op < 120) {
2514: if (op < 87) {
2515: if (op < 3)
2516: print("nop;aconst_null;iconst_m1;", op, dst);
2517: else if (op < 9) {
2518: dst.print("iconst_");
2519: dst.print(op - 3);
2520: } else if (op < 16) // op >= 9 [lconst_0] && op <= 15 [dconst_1]
2521: {
2522: char typ;
2523: if (op < 11) {
2524: typ = 'l';
2525: op -= 9;
2526: } else if (op < 14) {
2527: typ = 'f';
2528: op -= 11;
2529: } else {
2530: typ = 'd';
2531: op -= 14;
2532: }
2533: dst.print(typ);
2534: dst.print("const_");
2535: dst.print(op);
2536: } else if (op < 21) {
2537: if (op < 18) // op >= 16 [bipush] && op <= 17 [sipush]
2538: {
2539: print("bipush ;sipush ;", op - 16, dst);
2540: int constant;
2541: if (op == 16)
2542: constant = code[i++];
2543: else {
2544: constant = (short) readUnsignedShort(i);
2545: i += 2;
2546: }
2547: dst.print(constant);
2548: } else // op >= 18 [ldc] && op <= 20 [ldc2_w]
2549: {
2550: printConstant = op == 18 ? 1 : 2;
2551: print("ldc;ldc_w;ldc2_w;", op - 18, dst);
2552: }
2553: } else // op >= 21 && op < 87
2554: {
2555: String load_or_store;
2556: if (op < 54) {
2557: load_or_store = "load";
2558: } else {
2559: load_or_store = "store";
2560: op -= (54 - 21);
2561: }
2562: int index; // -2 if array op; -1 if index follows
2563: if (op < 26) {
2564: index = -1;
2565: op -= 21;
2566: } else if (op < 46) {
2567: op -= 26;
2568: index = op % 4;
2569: op >>= 2;
2570: } else {
2571: index = -2;
2572: op -= 46;
2573: }
2574: dst.print("ilfdabcs".charAt(op));
2575: if (index == -2)
2576: dst.write('a');
2577: dst.print(load_or_store);
2578: if (index >= 0) {
2579: dst.write('_');
2580: dst.print(index);
2581: } else if (index == -1) {
2582: if (wide) {
2583: index = readUnsignedShort(i);
2584: i += 2;
2585: } else {
2586: index = code[i] & 0xff;
2587: i++;
2588: }
2589: wide = false;
2590: dst.print(' ');
2591: dst.print(index);
2592: }
2593: }
2594: } else // op >= 87 && op < 120
2595: {
2596: if (op < 96)
2597: print(
2598: "pop;pop2;dup;dup_x1;dup_x2;dup2;dup2_x1;dup2_x2;swap;",
2599: op - 87, dst);
2600: else // op >= 96 [iadd] && op <= 119 [dneg]
2601: {
2602: dst.print("ilfda".charAt((op - 96) % 4));
2603: print("add;sub;mul;div;rem;neg;",
2604: (op - 96) >> 2, dst);
2605: }
2606: }
2607: } else // op >= 120
2608: {
2609: if (op < 170) {
2610: if (op < 132) // op >= 120 [ishl] && op <= 131 [lxor]
2611: {
2612: dst.print((op & 1) == 0 ? 'i' : 'l');
2613: print("shl;shr;ushr;and;or;xor;",
2614: (op - 120) >> 1, dst);
2615: } else if (op == 132) // iinc
2616: {
2617: int var_index;
2618: int constant;
2619: dst.print("iinc");
2620: if (!wide) {
2621: var_index = 0xff & code[i++];
2622: constant = code[i++];
2623: } else {
2624: var_index = readUnsignedShort(i);
2625: i += 2;
2626: constant = (short) readUnsignedShort(i);
2627: i += 2;
2628: wide = false;
2629: }
2630: dst.print(' ');
2631: dst.print(var_index);
2632: dst.print(' ');
2633: dst.print(constant);
2634: } else if (op < 148) // op >= 133 [i2l] && op <= 147 [i2s]
2635: {
2636: dst.print("ilfdi".charAt((op - 133) / 3));
2637: dst.print('2');
2638: dst.print("lfdifdildilfbcs".charAt(op - 133));
2639: } else if (op < 153) // op >= 148 [lcmp] && op <= 152 [dcmpg]
2640: print("lcmp;fcmpl;fcmpg;dcmpl;dcmpg;",
2641: op - 148, dst);
2642: else if (op < 169) {
2643: if (op < 159) {
2644: dst.print("if");
2645: print("eq;ne;lt;ge;gt;le;", op - 153, dst);
2646: } else if (op < 167) {
2647: if (op < 165) {
2648: dst.print("if_icmp");
2649: } else {
2650: dst.print("if_acmp");
2651: op -= 165 - 159;
2652: }
2653: print("eq;ne;lt;ge;gt;le;", op - 159, dst);
2654: } else
2655: print("goto;jsr;", op - 167, dst);
2656: int delta = (short) readUnsignedShort(i);
2657: i += 2;
2658: dst.print(' ');
2659: dst.print(oldpc + delta);
2660: } else {
2661: int index;
2662: dst.print("ret ");
2663: if (wide) {
2664: index = readUnsignedShort(i);
2665: index += 2;
2666: } else {
2667: index = code[i] & 0xff;
2668: i++;
2669: }
2670: wide = false;
2671: dst.print(index);
2672: }
2673: } else {
2674: if (op < 172) // [tableswitch] or [lookupswitch]
2675: {
2676: if (fixup_count == 0)
2677: i = (i + 3) & ~3; // skip 0-3 byte padding.
2678: int code_offset = readInt(i);
2679: i += 4;
2680: if (op == 170) {
2681: dst.print("tableswitch");
2682: int low = readInt(i);
2683: i += 4;
2684: int high = readInt(i);
2685: i += 4;
2686: dst.print(" low: ");
2687: dst.print(low);
2688: dst.print(" high: ");
2689: dst.print(high);
2690: dst.print(" default: ");
2691: dst.print(oldpc + code_offset);
2692: for (; low <= high; low++) {
2693: code_offset = readInt(i);
2694: i += 4;
2695: dst.println();
2696: dst.print(" ");
2697: dst.print(low);
2698: dst.print(": ");
2699: dst.print(oldpc + code_offset);
2700: }
2701: } else {
2702: dst.print("lookupswitch");
2703: int npairs = readInt(i);
2704: i += 4;
2705: dst.print(" npairs: ");
2706: dst.print(npairs);
2707: dst.print(" default: ");
2708: dst.print(oldpc + code_offset);
2709: while (--npairs >= 0) {
2710: int match = readInt(i);
2711: i += 4;
2712: code_offset = readInt(i);
2713: i += 4;
2714: dst.println();
2715: dst.print(" ");
2716: dst.print(match);
2717: dst.print(": ");
2718: dst.print(oldpc + code_offset);
2719: }
2720: }
2721: } else if (op < 178) // op >= 172 [ireturn] && op <= 177 [return]
2722: {
2723: if (op < 177)
2724: dst.print("ilfda".charAt(op - 172));
2725: dst.print("return");
2726: } else if (op < 182) // op >= 178 [getstatic] && op <= 181 [putfield]
2727: {
2728: print("getstatic;putstatic;getfield;putfield;",
2729: op - 178, dst);
2730: printConstant = 2;
2731: } else if (op < 185) // op >= 182 && op <= 185 [invoke*]
2732: {
2733: dst.print("invoke");
2734: print("virtual;special;static;", op - 182, dst);
2735: printConstant = 2;
2736: } else if (op == 185) // invokeinterface
2737: {
2738: dst.print("invokeinterface (");
2739: int index = readUnsignedShort(i);
2740: i += 2;
2741: int args = 0xff & code[i];
2742: i += 2;
2743: dst.print(args + " args)");
2744: dst.printConstantOperand(index);
2745: } else if (op < 196) {
2746: print(
2747: "186;new;newarray;anewarray;arraylength;athrow;checkcast;instanceof;monitorenter;monitorexit;",
2748: op - 186, dst);
2749: if (op == 187 || op == 189 || op == 192
2750: || op == 193)
2751: printConstant = 2;
2752: else if (op == 188) // newarray
2753: {
2754: int type = code[i++];
2755: dst.print(' ');
2756: if (type >= 4 && type <= 11)
2757: print(
2758: "boolean;char;float;double;byte;short;int;long;",
2759: type - 4, dst);
2760: else
2761: dst.print(type);
2762: }
2763:
2764: } else if (op == 196) // wide
2765: {
2766: dst.print("wide");
2767: wide = true;
2768: } else if (op == 197) {
2769: dst.print("multianewarray");
2770: int index = readUnsignedShort(i);
2771: i += 2;
2772: dst.printConstantOperand(index);
2773: int dims = 0xff & code[i++];
2774: dst.print(' ');
2775: dst.print(dims);
2776: } else if (op < 200) {
2777: print("ifnull;ifnonnull;", op - 198, dst);
2778: int delta = (short) readUnsignedShort(i);
2779: i += 2;
2780: dst.print(' ');
2781: dst.print(oldpc + delta);
2782: } else if (op < 202) {
2783: print("goto_w;jsr_w;", op - 200, dst);
2784: int delta = readInt(i);
2785: i += 4;
2786: dst.print(' ');
2787: dst.print(oldpc + delta);
2788: } else
2789: dst.print(op);
2790: }
2791: }
2792: if (printConstant > 0) {
2793: int index;
2794: if (printConstant == 1)
2795: index = 0xff & code[i++];
2796: else {
2797: index = readUnsignedShort(i);
2798: i += 2;
2799: }
2800: dst.printConstantOperand(index);
2801: }
2802: dst.println();
2803: }
2804: }
2805:
2806: private int readUnsignedShort(int offset) {
2807: return ((0xff & code[offset]) << 8) | (0xff & code[offset + 1]);
2808: }
2809:
2810: private int readInt(int offset) {
2811: return (readUnsignedShort(offset) << 16)
2812: | readUnsignedShort(offset + 2);
2813: }
2814:
2815: /*
2816: public saveStack (ClassType into)
2817: {
2818: Field[] flds = new Field[SP];
2819: while (SP > 0)
2820: {
2821: Field fld = ?;
2822: emitStore(fld);
2823: flds[SP...]
2824: }
2825: }
2826: */
2827:
2828: /* Print the i'th ';'-delimited substring of str on dst. */
2829: private void print(String str, int i, PrintWriter dst) {
2830: int last = 0;
2831: int pos = -1;
2832: for (; i >= 0; i--) {
2833: last = ++pos;
2834: pos = str.indexOf(';', last);
2835: }
2836: dst.write(str, last, pos - last);
2837: }
2838:
2839: /** Return an object encapsulating the type state of the JVM stack. */
2840: public Type[] saveStackTypeState(boolean clear) {
2841: if (SP == 0)
2842: return null;
2843: Type[] typeState = new Type[SP];
2844: System.arraycopy(stack_types, 0, typeState, 0, SP);
2845: if (clear)
2846: SP = 0;
2847: return typeState;
2848: }
2849:
2850: /** Restore a type state as saved by saveStackTypeState. */
2851: public void restoreStackTypeState(Type[] save) {
2852: if (save == null)
2853: SP = 0;
2854: else {
2855: SP = save.length;
2856: System.arraycopy(save, 0, stack_types, 0, SP);
2857: }
2858: }
2859:
2860: public int beginFragment(Label start, Label after) {
2861: int i = fixup_count;
2862: fixupAdd(FIXUP_MOVE_TO_END, after);
2863: start.define(this );
2864: return i;
2865: }
2866:
2867: /** End a fragment.
2868: * @param cookie the return value from the previous beginFragment.
2869: */
2870: public void endFragment(int cookie) {
2871: fixup_offsets[cookie] = (fixup_count << 4) | FIXUP_MOVE_TO_END;
2872: Label after = fixup_labels[cookie];
2873: fixupAdd(FIXUP_MOVE, 0, null);
2874: after.define(this);
2875: }
2876: }
|