📄 codeattr.java
字号:
emitDup(size, 0); } public void emitDup (Type type) { emitDup(type.size > 4 ? 2 : 1, 0); } public void enterScope (Scope scope) { scope.setStartPC(this); locals.enterScope(scope); } public Scope pushScope () { Scope scope = new Scope (); if (locals == null) locals = new LocalVarsAttr(getMethod()); enterScope(scope); if (locals.parameter_scope == null) locals.parameter_scope = scope; return scope; } public Scope getCurrentScope() { return locals.current_scope; } public Scope popScope () { Scope scope = locals.current_scope; locals.current_scope = scope.parent; scope.freeLocals(this); scope.end = getLabel(); return scope; } /** Get the index'th parameter. */ public Variable getArg (int index) { return locals.parameter_scope.getVariable(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). */ public 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. */ public Variable addLocal (Type type, String name) { return locals.current_scope.addVariable (this, type, name); } /** Call addLocal for parameters (as implied by method type). */ public void addParamLocals() { Method method = getMethod(); if ((method.access_flags & Access.STATIC) == 0) addLocal(method.classfile).setParameter(true); int arg_count = method.arg_types.length; for (int i = 0; i < arg_count; i++) addLocal(method.arg_types[i]).setParameter(true); } 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"); } } /* Low-level method to pust a ConstantPool entry. * Does not do the appropriatre <code>pushType</code>. */ public final void emitPushConstant (CpoolEntry cnst) { reserve(3); int index = cnst.index; if (cnst instanceof CpoolValue2) { put1 (20); // ldc2_w put2 (index); } else if (index < 256) { put1(18); // ldc put1(index); } else { put1(19); // ldc_w 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 if (xi == 0 && Float.floatToIntBits(x) != 0) // x == -0.0 { reserve(1); put1(118); // fneg } } 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 if (xi == 0 && Double.doubleToLongBits(x) != 0L) // x == -0.0 { reserve(1); put1(119); // dneg } } 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); } /** Calculate how many CONSTANT_String constants we need for a string. * Each CONSTANT_String can be at most 0xFFFF bytes (as a UTF8 string). * Returns a String, where each char, coerced to an int, is the length * of a substring of the input that is at most 0xFFFF bytes. */ public final String calculateSplit (String str) { int strLength = str.length(); StringBuffer sbuf = new StringBuffer(20); // Where the current segments starts, as an index in 'str': int segmentStart = 0; int byteLength = 0; // Length in bytes of current segment so far. for (int i = 0; i < strLength; i++) { char ch = str.charAt(i); int bytes = ch >= 0x0800 ? 3 : ch >= 0x0080 || ch == 0 ? 2 : 1; if (byteLength + bytes > 0xFFFF) { sbuf.append((char) (i - segmentStart)); segmentStart = i; byteLength = 0; } byteLength += bytes; } sbuf.append((char) (strLength - segmentStart)); return sbuf.toString(); } /** Emit code to push the value of a constant String. * Uses CONSTANT_String and CONSTANT_Utf8 constant pool entries as needed. * Can handle Strings whose UTF8 length is greates than 0xFFFF bytes * (the limit of a CONSTANT_Utf8) by generating String concatenation. */ public final void emitPushString (String str) { if (str == null) emitPushNull(); else { int length = str.length(); String segments = calculateSplit(str); int numSegments = segments.length(); if (numSegments <= 1) emitPushConstant(getConstants().addString(str)); else { if (numSegments == 2) { int firstSegment = (int) segments.charAt(0); emitPushString(str.substring(0, firstSegment)); emitPushString(str.substring(firstSegment)); Method concatMethod = Type.string_type.getDeclaredMethod("concat", 1); emitInvokeVirtual(concatMethod); } else { ClassType sbufType = ClassType.make("java.lang.StringBuffer"); emitNew(sbufType); emitDup(sbufType); emitPushInt(length); Type[] args1 = { Type.int_type }; emitInvokeSpecial(sbufType.getDeclaredMethod("<init>", args1)); Type[] args2 = { Type.string_type }; Method appendMethod = sbufType.getDeclaredMethod("append", args2); int segStart = 0; for (int seg = 0; seg < numSegments; seg++) { emitDup(sbufType); int segEnd = segStart + (int) segments.charAt(seg); emitPushString(str.substring(segStart, segEnd)); emitInvokeVirtual(appendMethod); segStart = segEnd; } emitInvokeVirtual(Type.toString_method); } if (str == str.intern()) emitInvokeVirtual(Type.string_type.getDeclaredMethod("intern", 0)); return; } pushType(Type.string_type); } } /** Push a class constant pool entry. * This is only supported by JDK 1.5 and later. */ public final void emitPushClass (String name) { emitPushConstant(getConstants().addClass(name)); pushType(Type.java_lang_Class_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()); } /** Emit code to push a constant primitive array. * @param value The array value that we want the emitted code to re-create. * @param arrayType The ArrayType that matches value. */ public final void emitPushPrimArray(Object value, ArrayType arrayType) { Type elementType = arrayType.getComponentType(); int len = java.lang.reflect.Array.getLength(value); emitPushInt(len); emitNewArray(elementType); char sig = elementType.getSignature().charAt(0); for (int i = 0; i < len; i++) { long ival = 0; float fval = 0; double dval = 0; switch (sig) { case 'J': ival = ((long[]) value)[i]; if (ival == 0) continue; break; case 'I': ival = ((int[]) value)[i]; if (ival == 0) continue; break; case 'S': ival = ((short[]) value)[i]; if (ival == 0) continue; break; case 'C': ival = ((char[]) value)[i]; if (ival == 0) continue; break; case 'B': ival = ((byte[]) value)[i]; if (ival == 0) continue; break; case 'Z': ival = ((boolean[]) value)[i] ? 1 : 0; if (ival == 0) continue; break; case 'F': fval = ((float[]) value)[i]; if (fval == 0.0) continue; break; case 'D': dval = ((double[]) value)[i]; if (dval == 0.0) continue; break; } emitDup(arrayType); emitPushInt(i); switch (sig) { case 'Z': case 'C': case 'B': case 'S': case 'I': emitPushInt((int) ival); break; case 'J': emitPushLong(ival); break; case 'F': emitPushFloat(fval); break; case 'D': emitPushDouble(dval); break; } emitArrayStore(elementType); } } void emitNewArray (int type_code) { reserve(2); put1(188); // newarray put1(type_code); } public final void emitArrayLength () { if (! (popType() instanceof ArrayType)) throw new Error( "non-array type in emitArrayLength" ); reserve(1); put1(190); // arraylength pushType(Type.int_type); } /* Returns an integer in the range 0 (for 'int') through 4 (for object reference) to 7 (for 'short') which matches the pattern of how JVM opcodes typically depend on the operand type. */ private int adjustTypedOp (char sig) { switch (sig) { 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 int adjustTypedOp (Type type) { return adjustTypedOp(type.getSignature().charAt(0)); } private void emitTypedOp (int op, Type type) { reserve(1); put1(op + adjustTypedOp(type)); } private void emitTypedOp (int op, char sig) { reserve(1); put1(op + adjustTypedOp(sig)); } /** 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 + -