📄 codeattr.java
字号:
// Copyright (c) 1997 Per M.A. Bothner.// This is free software; for terms and warranty disclaimer see ./COPYING.package gnu.bytecode;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; LocalVarsAttr locals; private Type[] stack_types; int SP; // Current stack size (in "words") private int max_stack; private int max_locals; int PC; // readPC (which is <= PC) is a bound on locations that have been // saved into labels or otherwise externally seen. // Hence, we cannot re-arrange code upto readPC, but we can // rearrange code between readPC and PC. int readPC; 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 quadrules are defined. */ short[] exception_table; /* The number of (defined) exception handlers (i.e. quadruples) in exception_table. */ int exception_table_length; /* A chain of labels. Unsorted, except that the Label with the lowest element in fixups must be the first one. */ Label labels; /** 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 ConstantPool getConstants () { return getMethod().classfile.constants; } /* True if we cannot fall through to bytes[PC] - the previous instruction was an uncondition control transfer. */ boolean unreachable_here; /** True if control could reach here. */ public boolean reachableHere () { return !unreachable_here; } /** 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; readPC = PC; } /** 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; readPC = len;} /** Set the current lengthof the code (instruction bytes) of this method. */ public int getCodeLength() { readPC = PC; return PC; } public CodeAttr (Method meth) { super ("Code"); setContainer(meth); setNext(meth.getAttributes()); meth.setAttributes(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; } while (labels != null && labels.fixups != null) { int oldest_fixup = labels.fixups[0]; int threshold = unreachable_here ? 30000 : 32000; if (PC + bytes - oldest_fixup > threshold) labels.emit_spring (this); else break; } } /** * 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 (int linenumber) { if (lines == null) lines = new LineNumbersAttr(this); readPC = PC; lines.put(linenumber, PC); } public final void pushType(Type type) { if (type == Type.void_type) 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"); Type type = stack_types[--SP]; if (type.size == 8) if (popType () != Type.void_type) 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 } } public void emitSwap () { reserve(1); Type type1 = popType(); Type type2 = popType(); if (type1.size > 4 || type2.size > 4) throw new Error ("emitSwap: not allowed for long or double"); pushType(type1); put1(95); // swap pushType(type2); } /** 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) { emitDup(size, 0); } public void emitDup (Type type) { emitDup(type.size > 4 ? 2 : 1, 0); } public void enterScope (Scope scope) { locals.enterScope(scope); } public Scope pushScope () { Scope scope = new Scope (); scope.start_pc = PC; readPC = PC; if (locals == null) locals = new LocalVarsAttr(this); locals.enterScope(scope); if (locals.parameter_scope == null) locals.parameter_scope= scope; return scope; } public Scope popScope () { Scope scope = locals.current_scope; locals.current_scope = scope.parent; scope.end_pc = PC; readPC = PC; for (Variable var = scope.vars; var != null; var = var.next) { if (var.isSimple () && ! var.dead ()) var.freeLocal(this); } return scope; } /** Get the index'th parameter. */ public Variable getArg (int index) { return locals.parameter_scope.find_var (index); } /** * Search by name for a Variable * @param name name to search for * @return the Variable, or null if not found (in any scope of this Method). */ Variable lookup (String name) { Scope scope = locals.current_scope; for (; scope != null; scope = scope.parent) { Variable var = scope.lookup (name); if (var != null) return var; } return null; } /** Add a new local variable (in the current scope). * @param type type of the new Variable. * @return the new Variable. */ public Variable addLocal (Type type) { return locals.current_scope.addVariable(this, type, null); } /** Add a new local variable (in the current scope). * @param type type of the new Variable. * @param name name of the new Variable. * @return the new Variable. */ Variable addLocal (Type type, String name) { return locals.current_scope.addVariable (this, type, name); } public final void emitPushConstant(int val, Type type) { switch (type.getSignature().charAt(0)) { case 'B': case 'C': case 'I': case 'Z': case 'S': emitPushInt(val); break; case 'J': emitPushLong((long)val); break; case 'F': emitPushFloat((float)val); break; case 'D': emitPushDouble((double)val); break; default: throw new Error("bad type to emitPushConstant"); } } public final void emitPushConstant (CpoolEntry cnst) { reserve(3); int index = cnst.index; if (cnst instanceof CpoolValue2) { put1 (20); // ldc2w put2 (index); } else if (index < 256) { put1(18); // ldc1 put1(index); } else { put1(19); // ldc2 put2(index); } } public final void emitPushInt(int i) { reserve(3); if (i >= -1 && i <= 5) put1(i + 3); // iconst_m1 .. iconst_5 else if (i >= -128 && i < 128) { put1(16); // bipush put1(i); } else if (i >= -32768 && i < 32768) { put1(17); // sipush put2(i); } else { emitPushConstant(getConstants().addInt(i)); } pushType(Type.int_type); } public void emitPushLong (long i) { if (i == 0 || i == 1) { reserve(1); put1 (9 + (int) i); // lconst_0 .. lconst_1 } else if ((long) (int) i == i) { emitPushInt ((int) i); reserve(1); popType(); put1 (133); // i2l } else { emitPushConstant(getConstants().addLong(i)); } pushType(Type.long_type); } public void emitPushFloat (float x) { int xi = (int) x; if ((float) xi == x && xi >= -128 && xi < 128) { if (xi >= 0 && xi <= 2) { reserve(1); put1(11 + xi); // fconst_0 .. fconst_2 } else { // Saves space in the constant pool // Probably faster, at least on modern CPUs. emitPushInt (xi); reserve(1); popType(); put1 (134); // i2f } } else { emitPushConstant(getConstants().addFloat(x)); } pushType(Type.float_type); } public void emitPushDouble (double x) { int xi = (int) x; if ((double) xi == x && xi >= -128 && xi < 128) { if (xi == 0 || xi == 1) { reserve(1); put1(14+xi); // dconst_0 or dconst_1 } else { // Saves space in the constant pool // Probably faster, at least on modern CPUs. emitPushInt (xi); reserve(1); popType(); put1 (135); // i2d } } else { emitPushConstant(getConstants().addDouble(x)); } pushType(Type.double_type); } public final void emitPushString (String str) { emitPushConstant(getConstants().addString(str)); pushType(Type.string_type); } public void emitPushNull () { reserve(1); put1(1); // aconst_null pushType(Type.pointer_type); } public final void emitPushThis() { reserve(1); put1(42); // aload_0 pushType(getMethod().getDeclaringClass()); } void emitNewArray (int type_code) { reserve(2); put1(188); // newarray put1(type_code); } public final void emitArrayLength () { reserve(1); put1(190); // arraylength pushType(Type.int_type); } private int adjustTypedOp (Type type) { switch (type.getSignature().charAt(0)) { case 'I': return 0; // int case 'J': return 1; // long case 'F': return 2; // float case 'D': return 3; // double default: return 4; // object case 'B': case 'Z': return 5; // byte or boolean case 'C': return 6; // char case 'S': return 7; // short } } private void emitTypedOp (int op, Type type) { reserve(1); put1(op + adjustTypedOp(type)); } /** Store into an element of an array. * Must already have pushed the array reference, the index, * and the new value (in that order). * Stack: ..., array, index, value => ... */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -