📄 codeattr.java
字号:
// Copyright (c) 1997, 1998, 1999, 2001, 2003, 2004 Per M.A. Bothner.// This is free software; for terms and warranty disclaimer see ./COPYING.package gnu.bytecodecvssnap;import java.io.*;/** * Represents the contents of a standard "Code" attribute. * <p> * Most of the actual methods that generate bytecode operation * are in this class (typically with names starting with <code>emit</code>), * though there are also some in <code>Method</code>. * <p> * Note that a <code>CodeAttr</code> is an <code>Attribute</code> * of a <code>Method</code>, and can in turn contain other * <code>Attribute</code>s, such as a <code>LineNumbersAttr</code>. * * @author Per Bothner */public class CodeAttr extends Attribute implements AttrContainer{ Attribute attributes; public final Attribute getAttributes () { return attributes; } public final void setAttributes (Attribute attributes) { this.attributes = attributes; } LineNumbersAttr lines; public LocalVarsAttr locals; SourceDebugExtAttr sourceDbgExt; // In hindsight, maintaining stack_types is more hassle than it is worth. // Instead, better to just keep track of SP, which should catch most // stack errors, while being more general and less hassle. FIXME. Type[] stack_types; int SP; // Current stack size (in "words") private int max_stack; private int max_locals; /** Current active length of code array. * Note that processFixups may expand/contract the code array. */ int PC; byte[] code; /* The exception handler table, as a vector of quadruples (start_pc, end_pc, handler_pc, catch_type). Only the first exception_table_length quadruples are defined. */ short[] exception_table; /* The number of (defined) exception handlers (i.e. quadruples) in exception_table. */ int exception_table_length; /** Not a fixup - a no-op. */ static final int FIXUP_NONE = 0; /** The definition of a label. */ static final int FIXUP_DEFINE = 1; /** The offset points to a tableswitch/lookupswitch - handle padding. */ static final int FIXUP_SWITCH = 2; /** The offset contains a label relative to the previous FIXUP_SWITCH. */ static final int FIXUP_CASE = 3; /** The offset points to a goto instruction. * This case up to FIXUP_TRANSFER2 must be contiguous * - see the jump-to-jump optimization in processFixups. */ static final int FIXUP_GOTO = 4; /** The offset points to a jsr instruction. */ static final int FIXUP_JSR = 5; /** The offset points to a conditional transfer (if_xxx) instruction. */ static final int FIXUP_TRANSFER = 6; /** A FIXUP_GOTO_, FIXUP_JSR, or FIXUP_TRANSFER that uses a 2-byte offset. */ static final int FIXUP_TRANSFER2 = 7; /** The offsets points to 3 bytes that should be deleted. */ static final int FIXUP_DELETE3 = 8; /** The following instructions are moved to later in the code stream. * Instead the instructions starting at the fixup label are patched here. * (If the fixup label is null, we're done.) * This allows re-arranging code to avoid unneeded gotos. * The following instruction is the target of a later FIXUP_MOVE, * and we'll insert then when we get to it. */ static final int FIXUP_MOVE = 9; /** The following instructions are moved to the end of the code stream. * Same as FIXUP_MOVE, but there is no explicit later FIXUP_MOVE that * refers to the following instructions. Created by beginFragment. * The fixup_offset points to the end of the fragment. * (The first processFixups patches these to FIXUP_MOVE.) */ static final int FIXUP_MOVE_TO_END = 10; /** FIXUP_TRY with the following FIXUP_CATCH marks an exception handler. * The label is the start of the try clause; * the current offset marks the exception handler. */ static final int FIXUP_TRY = 11; /** Second half of a FIXUP_TRY/FIXUP_CATCH pair. * The label is the ed of the try clause; * the current offset is the exception type as a constant pool index. */ static final int FIXUP_CATCH = 12; /** With following FIXUP_LINE_NUMBER associates an offset with a line number. * The fixup_offset is the code location; the fixup_label is null. */ static final int FIXUP_LINE_PC = 13; /** With preceding FIXUP_LINE_PC associates an offset with a line number. * The fixup_offset is the line number; the fixup_label is null. */ static final int FIXUP_LINE_NUMBER = 14; int[] fixup_offsets; Label[] fixup_labels; int fixup_count; /** This causes a later processFixup to rearrange the code. * The codet a target comes here, nstead of the following instructions. */ public final void fixupChain (Label here, Label target) { fixupAdd(CodeAttr.FIXUP_MOVE, 0, target); here.define(this); } /** Add a fixup at this location. * @param kind one of the FIXUP_xxx codes. * @param label varies - typically the target of jump. */ public final void fixupAdd (int kind, Label label) { fixupAdd(kind, PC, label); } final void fixupAdd (int kind, int offset, Label label) { int count = fixup_count; if (count == 0) { fixup_offsets = new int[30]; fixup_labels = new Label[30]; } else if (fixup_count == fixup_offsets.length) { int new_length = 2 * count; Label[] new_labels = new Label[new_length]; System.arraycopy (fixup_labels, 0, new_labels, 0, count); fixup_labels = new_labels; int[] new_offsets = new int[new_length]; System.arraycopy (fixup_offsets, 0, new_offsets, 0, count); fixup_offsets = new_offsets; } fixup_offsets[count] = (offset << 4) | kind; fixup_labels[count] = label; fixup_count = count + 1; } private final int fixupOffset(int index) { return fixup_offsets[index] >> 4; } private final int fixupKind(int index) { return fixup_offsets[index] & 15; } /** The stack of currently active conditionals. */ IfState if_stack; /** The stack of currently active try statements. */ TryState try_stack; public final Method getMethod() { return (Method) getContainer(); } public final int getPC() { return PC; } public final int getSP() { return SP; } public final ConstantPool getConstants () { return getMethod().classfile.constants; } /* True if we cannot fall through to bytes[PC] - the previous instruction was an uncondition control transfer. */ private boolean unreachable_here; /** True if control could reach here. */ public final boolean reachableHere () { return !unreachable_here; } public final void setReachable(boolean val) { unreachable_here = !val; } public final void setUnreachable() { unreachable_here = true; } /** Get the maximum number of words on the operand stack in this method. */ public int getMaxStack() { return max_stack; } /** Get the maximum number of local variable words in this method. */ public int getMaxLocals() { return max_locals; } /** Set the maximum number of words on the operand stack in this method. */ public void setMaxStack(int n) { max_stack = n; } /** Set the maximum number of local variable words in this method. */ public void setMaxLocals(int n) { max_locals = n; } /** Get the code (instruction bytes) of this method. * Does not make a copy. */ public byte[] getCode() { return code; } /** Set the code (instruction bytes) of this method. * @param code the code bytes (which are not copied). * Implicitly calls setCodeLength(code.length). */ public void setCode(byte[] code) { this.code = code; this.PC = code.length; } /** Set the length the the code (instruction bytes) of this method. * That is the number of current used bytes in getCode(). * (Any remaing bytes provide for future growth.) */ public void setCodeLength(int len) { PC = len;} /** Set the current lengthof the code (instruction bytes) of this method. */ public int getCodeLength() { return PC; } public CodeAttr (Method meth) { super ("Code"); addToFrontOf(meth); meth.code = this; } public final void reserve (int bytes) { if (code == null) code = new byte[100+bytes]; else if (PC + bytes > code.length) { byte[] new_code = new byte[2 * code.length + bytes]; System.arraycopy (code, 0, new_code, 0, PC); code = new_code; } } /** Get opcode that implements NOT (x OPCODE y). */ byte invert_opcode (byte opcode) { if ((opcode >= 153 && opcode <= 166) || (opcode >= 198 && opcode <= 199)) return (byte) (opcode ^ 1); throw new Error("unknown opcode to invert_opcode"); } /** * Write an 8-bit byte to the current code-stream. * @param i the byte to write */ public final void put1(int i) { code[PC++] = (byte) i; unreachable_here = false; } /** * Write a 16-bit short to the current code-stream * @param i the value to write */ public final void put2(int i) { code[PC++] = (byte) (i >> 8); code[PC++] = (byte) (i); unreachable_here = false; } /** * Write a 32-bit int to the current code-stream * @param i the value to write */ public final void put4(int i) { code[PC++] = (byte) (i >> 24); code[PC++] = (byte) (i >> 16); code[PC++] = (byte) (i >> 8); code[PC++] = (byte) (i); unreachable_here = false; } public final void putIndex2 (CpoolEntry cnst) { put2(cnst.index); } public final void putLineNumber (String filename, int linenumber) { getMethod().classfile.setSourceFile(filename); putLineNumber(linenumber); } public final void putLineNumber (int linenumber) { if (sourceDbgExt != null) linenumber = sourceDbgExt.fixLine(linenumber); fixupAdd(FIXUP_LINE_PC, null); fixupAdd(FIXUP_LINE_NUMBER, linenumber, null); } public final void pushType(Type type) { if (type.size == 0) throw new Error ("pushing void type onto stack"); if (stack_types == null) stack_types = new Type[20]; else if (SP + 1 >= stack_types.length) { Type[] new_array = new Type[2 * stack_types.length]; System.arraycopy (stack_types, 0, new_array, 0, SP); stack_types = new_array; } if (type.size == 8) stack_types[SP++] = Type.void_type; stack_types[SP++] = type; if (SP > max_stack) max_stack = SP; } public final Type popType () { if (SP <= 0) throw new Error("popType called with empty stack "+getMethod()); Type type = stack_types[--SP]; if (type.size == 8) if (! popType().isVoid()) throw new Error("missing void type on stack"); return type; } public final Type topType () { return stack_types[SP - 1]; } /** Compile code to pop values off the stack (and ignore them). * @param nvalues the number of values (not words) to pop */ public void emitPop (int nvalues) { for ( ; nvalues > 0; --nvalues) { reserve(1); Type type = popType(); if (type.size > 4) put1(88); // pop2 else if (nvalues > 1) { // optimization: can we pop 2 4-byte words using a pop2 Type type2 = popType(); if (type2.size > 4) { put1(87); // pop reserve(1); } put1(88); // pop2 --nvalues; } else put1(87); // pop } } /** Get a new Label for the current location. * Unlike Label.define, Does not change reachableHere(). */ public Label getLabel () { boolean unreachable = unreachable_here; Label label = new Label(); label.define(this); unreachable_here = unreachable; return label; } public void emitSwap () { reserve(1); Type type1 = popType(); Type type2 = popType(); if (type1.size > 4 || type2.size > 4) { // There is no swap instruction in the JVM for this case. // Fall back to a more convoluted way. pushType(type2); pushType(type1); emitDupX(); emitPop(1); } else { pushType(type1); put1(95); // swap pushType(type2); } } /** Emit code to duplicate the top element of the stack. */ public void emitDup () { reserve(1); Type type = topType(); put1 (type.size <= 4 ? 89 : 92); // dup or dup2 pushType (type); } /** Emit code to duplicate the top element of the stack and place the copy before the previous element. */ public void emitDupX () { reserve(1); Type type = popType(); Type skipedType = popType(); if (skipedType.size <= 4) put1 (type.size <= 4 ? 90 : 93); // dup_x1 or dup2_x1 else put1 (type.size <= 4 ? 91 : 94); // dup_x2 or dup2_x2 pushType (type); pushType (skipedType); pushType (type); } /** Compile code to duplicate with offset. * @param size the size of the stack item to duplicate (1 or 2) * @param offset where to insert the result (must be 0, 1, or 2) * The new words get inserted at stack[SP-size-offset] */ public void emitDup (int size, int offset) { if (size == 0) return; reserve(1); // copied1 and (optionally copied2) are the types of the duplicated words Type copied1 = popType (); Type copied2 = null; if (size == 1) { if (copied1.size > 4) throw new Error ("using dup for 2-word type"); } else if (size != 2) throw new Error ("invalid size to emitDup"); else if (copied1.size <= 4) { copied2 = popType(); if (copied2.size > 4) throw new Error ("dup will cause invalid types on stack"); } int kind; // These are the types of the words (in any) that are "skipped": Type skipped1 = null; Type skipped2 = null; if (offset == 0) { kind = size == 1 ? 89 : 92; // dup or dup2 } else if (offset == 1) { kind = size == 1 ? 90 : 93; // dup_x1 or dup2_x1 skipped1 = popType (); if (skipped1.size > 4) throw new Error ("dup will cause invalid types on stack"); } else if (offset == 2) { kind = size == 1 ? 91 : 94; // dup_x2 or dup2_x2 skipped1 = popType(); if (skipped1.size <= 4) { skipped2 = popType(); if (skipped2.size > 4) throw new Error ("dup will cause invalid types on stack"); } } else throw new Error ("emitDup: invalid offset"); put1(kind); if (copied2 != null) pushType(copied2); pushType(copied1); if (skipped2 != null) pushType(skipped2); if (skipped1 != null) pushType(skipped1); if (copied2 != null) pushType(copied2); pushType(copied1); } /** * Compile code to duplicate the top 1 or 2 words. * @param size number of words to duplicate */ public void emitDup (int size) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -