Return-Path: Delivered-To: apmail-db-derby-commits-archive@www.apache.org Received: (qmail 6474 invoked from network); 31 Oct 2007 15:41:58 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 31 Oct 2007 15:41:58 -0000 Received: (qmail 11605 invoked by uid 500); 31 Oct 2007 15:41:45 -0000 Delivered-To: apmail-db-derby-commits-archive@db.apache.org Received: (qmail 11580 invoked by uid 500); 31 Oct 2007 15:41:45 -0000 Mailing-List: contact derby-commits-help@db.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: List-Post: Reply-To: "Derby Development" List-Id: Delivered-To: mailing list derby-commits@db.apache.org Received: (qmail 11569 invoked by uid 99); 31 Oct 2007 15:41:45 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 31 Oct 2007 08:41:45 -0700 X-ASF-Spam-Status: No, hits=-100.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 31 Oct 2007 15:41:56 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 60D6F1A9832; Wed, 31 Oct 2007 08:41:36 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r590719 - in /db/derby/code/branches/10.1/java/engine/org/apache/derby: iapi/services/classfile/ impl/services/bytecode/ Date: Wed, 31 Oct 2007 15:41:28 -0000 To: derby-commits@db.apache.org From: kmarsden@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20071031154136.60D6F1A9832@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: kmarsden Date: Wed Oct 31 08:41:27 2007 New Revision: 590719 URL: http://svn.apache.org/viewvc?rev=590719&view=rev Log: DERBY-766 (partial) Add code to the byte code compiler to enable it to analyze a block of existing generated byte code. Provide utility methods to allow other methods to be written to walk the byte code. Add an method to find the maximum stack depth of a piece of java code. Add sanity checks to double check the data used (OPCODE_ACTION) to walk the byte code and compare the calculated max stack depth with the existing generated value. This is progress towards providing the ability to split generated methods that exceed the JVM's limit into a number of smaller methods. This will replace the existing mechanisms which are reactive, non-optimal and only work in a limited number of situations. merge from trunk revision 376882 Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/CONSTANT_Index_info.java db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/CONSTANT_Utf8_info.java db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/ClassHolder.java db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/VMOpcode.java db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/CodeChunk.java Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/CONSTANT_Index_info.java URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/CONSTANT_Index_info.java?rev=590719&r1=590718&r2=590719&view=diff ============================================================================== --- db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/CONSTANT_Index_info.java (original) +++ db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/CONSTANT_Index_info.java Wed Oct 31 08:41:27 2007 @@ -39,7 +39,7 @@ Class Reference Constant Pool Entry - page 93 - Section 4.4.1 - One index */ -final class CONSTANT_Index_info extends ConstantPoolEntry { +public final class CONSTANT_Index_info extends ConstantPoolEntry { private int i1; private int i2; Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/CONSTANT_Utf8_info.java URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/CONSTANT_Utf8_info.java?rev=590719&r1=590718&r2=590719&view=diff ============================================================================== --- db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/CONSTANT_Utf8_info.java (original) +++ db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/CONSTANT_Utf8_info.java Wed Oct 31 08:41:27 2007 @@ -26,7 +26,7 @@ /** Constant Pool class - pages 92-99 */ /** Utf8- page 100 - Section 4.4.7 */ -final class CONSTANT_Utf8_info extends ConstantPoolEntry { +public final class CONSTANT_Utf8_info extends ConstantPoolEntry { private final String value; private int asString; private int asCode; Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/ClassHolder.java URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/ClassHolder.java?rev=590719&r1=590718&r2=590719&view=diff ============================================================================== --- db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/ClassHolder.java (original) +++ db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/ClassHolder.java Wed Oct 31 08:41:27 2007 @@ -546,7 +546,7 @@ ** Methods to convert indexes to constant pool entries and vice-versa. */ - ConstantPoolEntry getEntry(int index) { + public ConstantPoolEntry getEntry(int index) { return (ConstantPoolEntry) cptEntries.elementAt(index); } Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/VMOpcode.java URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/VMOpcode.java?rev=590719&r1=590718&r2=590719&view=diff ============================================================================== --- db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/VMOpcode.java (original) +++ db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/VMOpcode.java Wed Oct 31 08:41:27 2007 @@ -239,4 +239,23 @@ * See section 4.10 of JVM spec version 1. */ int MAX_CODE_LENGTH = 65535; + + /** + * Instruction length for IF (IFNULL, IFEQ) etc. + * Used in conditional handling. + */ + int IF_INS_LENGTH = 3; + + /** + * Instruction length for GOTO etc. + * Used in conditional handling. + */ + int GOTO_INS_LENGTH = 3; + + /** + * Instruction length for GOTO_W. + * Used in conditional handling. + */ + int GOTO_W_INS_LENGTH = 5; + } Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/CodeChunk.java URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/CodeChunk.java?rev=590719&r1=590718&r2=590719&view=diff ============================================================================== --- db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/CodeChunk.java (original) +++ db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/CodeChunk.java Wed Oct 31 08:41:27 2007 @@ -20,9 +20,12 @@ package org.apache.derby.impl.services.bytecode; +import org.apache.derby.iapi.services.classfile.CONSTANT_Index_info; +import org.apache.derby.iapi.services.classfile.CONSTANT_Utf8_info; import org.apache.derby.iapi.services.classfile.ClassFormatOutput; import org.apache.derby.iapi.services.classfile.ClassHolder; import org.apache.derby.iapi.services.classfile.ClassMember; +import org.apache.derby.iapi.services.classfile.VMDescriptor; import org.apache.derby.iapi.services.sanity.SanityManager; import org.apache.derby.iapi.services.classfile.VMOpcode; @@ -231,37 +234,339 @@ /* target = vm_reference */ { VMOpcode.NOP, BCExpr.vm_reference } } }; + + /** + * Constant used by OPCODE_ACTION to represent the + * common action of push one word, 1 byte + * for the instruction. + */ + private static final byte[] push1_1i = {1, 1}; + + /** + * Constant used by OPCODE_ACTION to represent the + * common action of push two words, 1 byte + * for the instruction. + */ + private static final byte[] push2_1i = {2, 1}; /** - * Add an instruction that has no opcodes. - * All instructions are 1 byte large. + * Constant used by OPCODE_ACTION to the opcode is + * not yet supported. + */ + private static final byte[] NS = {0, -1}; + + + /** + * Value for OPCODE_ACTION[opcode][0] to represent + * the number of words popped or pushed in variable. + */ + private static final byte VARIABLE_STACK = -128; + + /** + * Array that provides two pieces of information about + * each VM opcode. Each opcode has a two byte array. + *

+ * The first element in the array [0] is the number of + * stack words (double/long count as two) pushed by the opcode. + * Will be negative if the opcode pops values. + * + *

+ * The second element in the array [1] is the number of bytes + * in the instruction stream that this opcode's instruction + * takes up, including the opocode. + */ + private static final byte[][] OPCODE_ACTION = + { + + /* NOP 0 */ { 0, 1 }, + + /* ACONST_NULL 1 */ push1_1i, + /* ICONST_M1 2 */ push1_1i, + /* ICONST_0 3 */ push1_1i, + /* ICONST_1 4 */ push1_1i, + /* ICONST_2 5 */ push1_1i, + /* ICONST_3 6 */ push1_1i, + /* ICONST_4 7 */ push1_1i, + /* ICONST_5 8 */ push1_1i, + /* LCONST_0 9 */ push2_1i, + /* LCONST_1 10 */ push2_1i, + /* FCONST_0 11 */ push1_1i, + /* FCONST_1 12 */ push1_1i, + /* FCONST_2 13 */ push1_1i, + /* DCONST_0 14 */ push2_1i, + /* DCONST_1 15 */ push2_1i, + + /* BIPUSH 16 */ {1, 2}, + /* SIPUSH 17 */ {1, 3}, + /* LDC 18 */ {1, 2}, + /* LDC_W 19 */ {1, 3}, + /* LDC2_W 20 */ {2, 3}, + + /* ILOAD 21 */ { 1, 2 }, + /* LLOAD 22 */ { 2, 2 }, + /* FLOAD 23 */ { 1, 2 }, + /* DLOAD 24 */ { 2, 2 }, + /* ALOAD 25 */ { 1, 2 }, + /* ILOAD_0 26 */ push1_1i, + /* ILOAD_1 27 */ push1_1i, + /* ILOAD_2 28 */ push1_1i, + /* ILOAD_3 29 */ push1_1i, + /* LLOAD_0 30 */ push2_1i, + /* LLOAD_1 31 */ push2_1i, + /* LLOAD_2 32 */ push2_1i, + /* LLOAD_3 33 */ push2_1i, + /* FLOAD_0 34 */ push1_1i, + /* FLOAD_1 35 */ push1_1i, + /* FLOAD_2 36 */ push1_1i, + /* FLOAD_3 37 */ push1_1i, + /* DLOAD_0 38 */ push2_1i, + /* DLOAD_1 39 */ push2_1i, + /* DLOAD_2 40 */ push2_1i, + /* DLOAD_3 41 */ push2_1i, + /* ALOAD_0 42 */ push1_1i, + /* ALOAD_1 43 */ push1_1i, + /* ALOAD_2 44 */ push1_1i, + /* ALOAD_3 45 */ push1_1i, + /* IALOAD 46 */ { -1, 1 }, + /* LALOAD 47 */ { 0, 1 }, + /* FALOAD 48 */ { -1, 1 }, + /* DALOAD 49 */ { 0, 1 }, + /* AALOAD 50 */ { -1, 1 }, + /* BALOAD 51 */ { -1, 1 }, + /* CALOAD 52 */ { -1, 1 }, + + /* SALOAD 53 */ { -1, 1 }, + /* ISTORE 54 */ { -1, 2 }, + /* LSTORE 55 */ { -2, 2 }, + /* FSTORE 56 */ { -1, 2 }, + /* DSTORE 57 */ { -2, 2 }, + /* ASTORE 58 */ { -1, 2 }, + /* ISTORE_0 59 */ { -1, 1 }, + /* ISTORE_1 60 */ { -1, 1 }, + /* ISTORE_2 61 */ { -1, 1 }, + /* ISTORE_3 62 */ { -1, 1 }, + /* LSTORE_0 63 */ { -2, 1 }, + /* LSTORE_1 64 */ { -2, 1 }, + /* LSTORE_2 65 */ { -2, 1 }, + /* LSTORE_3 66 */ { -2, 1 }, + /* FSTORE_0 67 */ { -1, 1 }, + /* FSTORE_1 68 */ { -1, 1 }, + /* FSTORE_2 69 */ { -1, 1 }, + /* FSTORE_3 70 */ { -1, 1 }, + /* DSTORE_0 71 */ { -2, 1 }, + /* DSTORE_1 72 */ { -2, 1 }, + /* DSTORE_2 73 */ { -2, 1 }, + /* DSTORE_3 74 */ { -2, 1 }, + /* ASTORE_0 75 */ { -1, 1 }, + /* ASTORE_1 76 */ { -1, 1 }, + /* ASTORE_2 77 */ { -1, 1 }, + /* ASTORE_3 78 */ { -1, 1 }, + /* IASTORE 79 */ { -3, 1 }, + /* LASTORE 80 */ { -4, 1 }, + /* FASTORE 81 */ { -3, 1 }, + /* DASTORE 82 */ { -4, 1 }, + /* AASTORE 83 */ { -3, 1 }, + /* BASTORE 84 */ { -3, 1 }, + /* CASTORE 85 */ { -3, 1 }, + /* SASTORE 86 */ { -3, 1 }, + + /* POP 87 */ { -1, 1 }, + /* POP2 88 */ { -2, 1 }, + /* DUP 89 */ push1_1i, + /* DUP_X1 90 */ push1_1i, + /* DUP_X2 91 */ push1_1i, + /* DUP2 92 */ push2_1i, + /* DUP2_X1 93 */ push2_1i, + /* DUP2_X2 94 */ push2_1i, + /* SWAP 95 */ { 0, 1 }, + + /* IADD 96 */ NS, + /* LADD 97 */ NS, + /* FADD 98 */ { -1, 1 }, + /* DADD 99 */ { -2, 1 }, + /* ISUB 100 */ NS, + /* LSUB 101 */ NS, + /* FSUB 102 */ { -1, 1 }, + /* DSUB 103 */ { -2, 1 }, + /* IMUL 104 */ NS, + /* LMUL 105 */ NS, + /* FMUL 106 */ { -1, 1 }, + /* DMUL 107 */ { -2, 1 }, + /* IDIV 108 */ NS, + /* LDIV 109 */ NS, + /* FDIV 110 */ { -1, 1 }, + /* DDIV 111 */ { -2, 1 }, + /* IREM 112 */ { -1, 1 }, + /* LREM 113 */ { -2, 1 }, + /* FREM 114 */ { -1, 1 }, + /* DREM 115 */ { -2, 1 }, + /* INEG 116 */ { 0, 1 }, + /* LNEG 117 */ { 0, 1 }, + /* FNEG 118 */ { 0, 1 }, + /* DNEG 119 */ { 0, 1 }, + /* ISHL 120 */ { -1, 1 }, + /* LSHL 121 */ NS, + /* ISHR 122 */ NS, + /* LSHR 123 */ NS, + /* IUSHR 124 */ NS, + /* LUSHR 125 */ NS, + + /* IAND 126 */ { -1, 1 }, + /* LAND 127 */ NS, + /* IOR 128 */ { -1, 1 }, + /* LOR 129 */ NS, + /* IXOR 130 */ NS, + /* LXOR 131 */ NS, + /* IINC 132 */ NS, + + /* I2L 133 */ push1_1i, + /* I2F 134 */ { 0, 1 }, + /* I2D 135 */ push1_1i, + /* L2I 136 */ { -1, 1 }, + /* L2F 137 */ { -1, 1 }, + /* L2D 138 */ { 0, 1 }, + /* F2I 139 */ { 0, 1 }, + /* F2L 140 */ push2_1i, + /* F2D 141 */ push1_1i, + /* D2I 142 */ { -1, 1 }, + /* D2L 143 */ { 0, 1 }, + /* D2F 144 */ { -1, 1 }, + /* I2B 145 */ { 0, 1 }, + /* I2C 146 */ { 0, 1 }, + /* I2S 147 */ { 0, 1 }, + + /* LCMP 148 */ NS, + /* FCMPL 149 */ { -1, 1 }, + /* FCMPG 150 */ { -1, 1 }, + /* DCMPL 151 */ { -3, 1 }, + /* DCMPG 152 */ { -3, 1 }, + /* IFEQ 153 */ { -1, VMOpcode.IF_INS_LENGTH }, + /* IFNE 154 */ { -1, VMOpcode.IF_INS_LENGTH }, + /* IFLT 155 */ { -1, VMOpcode.IF_INS_LENGTH }, + /* IFGE 156 */ { -1, VMOpcode.IF_INS_LENGTH }, + /* IFGT 157 */ { -1, VMOpcode.IF_INS_LENGTH }, + /* IFLE 158 */ { -1, VMOpcode.IF_INS_LENGTH }, + /* IF_ICMPEQ 159 */ NS, + /* IF_ICMPNE 160 */ NS, + /* IF_ICMPLT 161 */ NS, + /* IF_ICMPGE 162 */ NS, + /* IF_ICMPGT 163 */ NS, + /* IF_ICMPLE 164 */ NS, + /* IF_ACMPEQ 165 */ NS, + /* IF_ACMPNE 166 */ NS, + /* GOTO 167 */ { 0, VMOpcode.GOTO_INS_LENGTH }, + /* JSR 168 */ NS, + /* RET 169 */ NS, + /* TABLESWITCH 170 */ NS, + /* LOOKUPSWITCH 171 */NS, + + /* IRETURN 172 */ { -1, 1 }, // strictly speaking all words on the stack are popped. + /* LRETURN 173 */ { -2, 1 }, // strictly speaking all words on the stack are popped. + /* FRETURN 174 */ { -1, 1 }, // strictly speaking all words on the stack are popped. + /* DRETURN 175 */ { -2, 1 }, // strictly speaking all words on the stack are popped. + /* ARETURN 176 */ { -1, 1 }, // strictly speaking all words on the stack are popped. + /* RETURN 177 */ { 0, 1 }, // strictly speaking all words on the stack are popped. + + /* GETSTATIC 178 */ {VARIABLE_STACK, 3 }, + /* PUTSTATIC 179 */ {VARIABLE_STACK, 3 }, + /* GETFIELD 180 */ {VARIABLE_STACK, 3 }, + /* PUTFIELD 181 */ {VARIABLE_STACK, 3 }, + /* INVOKEVIRTUAL 182 */ {VARIABLE_STACK, 3 }, + /* INVOKESPECIAL 183 */ {VARIABLE_STACK, 3 }, + /* INVOKESTATIC 184 */ {VARIABLE_STACK, 3 }, + /* INVOKEINTERFACE 185 */ {VARIABLE_STACK, 5 }, + + /* XXXUNUSEDXXX 186 */ NS, + + /* NEW 187 */ { 1, 3 }, + /* NEWARRAY 188 */ { 0, 2 }, + /* ANEWARRAY 189 */ { 0, 3 }, + /* ARRAYLENGTH 190 */ { 0, 1 }, + /* ATHROW 191 */ NS, + /* CHECKCAST 192 */ { 0, 3}, + /* INSTANCEOF 193 */ { 0, 3 }, + /* MONITORENTER 194 */ NS, + /* MONITOREXIT 195 */ NS, + /* WIDE 196 */ NS, + /* MULTIANEWARRAY 197 */ NS, + /* IFNULL 198 */ { -1, VMOpcode.IF_INS_LENGTH }, + /* IFNONNULL 199 */ { -1, VMOpcode.IF_INS_LENGTH }, + /* GOTO_W 200 */ {0, VMOpcode.GOTO_W_INS_LENGTH }, + /* JSR_W 201 */ NS, + /* BREAKPOINT 202 */ NS, + + }; + + + + /** + * Add an instruction that has no operand. + * All opcodes are 1 byte large. */ void addInstr(short opcode) { try { cout.putU1(opcode); } catch (IOException ioe) { } + + if (SanityManager.DEBUG) { + if (OPCODE_ACTION[opcode][1] != 1) + SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" + + " writing 1 byte - set as " + OPCODE_ACTION[opcode][1]); + } } + /** + * Add an instruction that has a 16 bit operand. + */ void addInstrU2(short opcode, int operand) { try { cout.putU1(opcode); cout.putU2(operand); } catch (IOException ioe) { } + + if (SanityManager.DEBUG) { + if (OPCODE_ACTION[opcode][1] != 3) + SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" + + " writing 3 bytes - set as " + OPCODE_ACTION[opcode][1]); + } } - void addInstrU4(short opcode, int operand) { + + /** + * Add an instruction that has a 32 bit operand. + */ + void addInstrU4(short opcode, int operand) { try { cout.putU1(opcode); cout.putU4(operand); } catch (IOException ioe) { } + if (SanityManager.DEBUG) { + if (OPCODE_ACTION[opcode][1] != 5) + SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" + + " writing 5 bytes - set as " + OPCODE_ACTION[opcode][1]); + } } - void addInstrU1(short opcode, int operand) { + + + /** + * Add an instruction that has an 8 bit operand. + */ + void addInstrU1(short opcode, int operand) { try { cout.putU1(opcode); cout.putU1(operand); } catch (IOException ioe) { } + + // Only debug code from here. + if (SanityManager.DEBUG) { + + if (OPCODE_ACTION[opcode][1] != 2) + SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" + + " writing 2 bytes - set as " + OPCODE_ACTION[opcode][1]); + + } } /** @@ -273,38 +578,25 @@ * the next possible instruction. */ void addInstrCPE(short opcode, int cpeNum) { - try { - // REMIND: used 256 and 1 as magic numbers... if (cpeNum < 256) { - cout.putU1(opcode); - cout.putU1(cpeNum); + addInstrU1(opcode, cpeNum); } else { - cout.putU1((short) (opcode+1)); - cout.putU2(cpeNum); - } - } catch (IOException ioe) { + addInstrU2((short) (opcode+1), cpeNum); } } /** * This takes an instruction that can be wrapped in * a wide for large variable #s and does so. - * REVISIT: could hide this in addInstrU2? */ void addInstrWide(short opcode, int varNum) { - try { - // REMIND: used 256 as magic number... if (varNum < 256) { - cout.putU1(opcode); - cout.putU1(varNum); + addInstrU1(opcode, varNum); } else { - cout.putU1(VMOpcode.WIDE); - cout.putU1(opcode); - cout.putU2(varNum); - } - } catch (IOException ioe) { + addInstr(VMOpcode.WIDE); + addInstrU2(opcode, varNum); } } @@ -321,14 +613,37 @@ cout.putU1(operand3); } catch (IOException ioe) { } + if (SanityManager.DEBUG) { + if (OPCODE_ACTION[opcode][1] != 5) + SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" + + " writing 5 bytes - set as " + OPCODE_ACTION[opcode][1]); + } } /** Get the current program counter */ - public int getPC() { + int getPC() { return cout.size() + pcDelta; } /** + * Return the complete instruction length for the + * passed in opcode. This will include the space for + * the opcode and its operand. + */ + private static int instructionLength(short opcode) + { + int instructionLength = OPCODE_ACTION[opcode][1]; + + if (SanityManager.DEBUG) + { + if (instructionLength < 0) + SanityManager.THROWASSERT("Opcode without instruction length " + opcode); + } + + return instructionLength; + } + + /** * The delta between cout.size() and the pc. * For an initial code chunk this is -8 (CODE_OFFSET) * since 8 bytes are written. @@ -407,7 +722,7 @@ codeBytes[4] = (byte)(codeLength >> 24 ); codeBytes[5] = (byte)(codeLength >> 16 ); codeBytes[6] = (byte)(codeLength >> 8 ); - codeBytes[7] = (byte)(codeLength ); + codeBytes[7] = (byte)(codeLength ); } /** @@ -421,7 +736,7 @@ int codeLength = getPC(); ClassFormatOutput out = cout; - + try { out.putU2(0); // exception_table_length @@ -454,8 +769,24 @@ fixLengths(mb, maxStack, maxLocals, codeLength); method.addAttribute("Code", out); + + if (SanityManager.DEBUG) + { + if (codeLength <= VMOpcode.MAX_CODE_LENGTH) + { + // Validate the alternate way to calculate the + // max stack agrees with the dynamic as the code + // is built way. + int walkedMaxStack = findMaxStack(ch, 0, codeLength); + if (walkedMaxStack != maxStack) + { + SanityManager.THROWASSERT("MAX STACK MISMATCH!! " + + maxStack + " <> " + walkedMaxStack); + } + } + } + } - /** * Return the opcode at the given pc. */ @@ -465,6 +796,34 @@ } /** + * Get the unsigned short value for the opcode at the program + * counter pc. + */ + private int getU2(int pc) + { + byte[] codeBytes = cout.getData(); + + int u2p = CODE_OFFSET + pc + 1; + + return ((codeBytes[u2p] & 0xff) << 8) | (codeBytes[u2p+1] & 0xff); + } + + /** + * Get the unsigned 32 bit value for the opcode at the program + * counter pc. + */ + private int getU4(int pc) + { + byte[] codeBytes = cout.getData(); + + int u4p = CODE_OFFSET + pc + 1; + + return (((codeBytes[u4p] & 0xff) << 24) | + ((codeBytes[u4p+1] & 0xff) << 16) | + ((codeBytes[u4p+2] & 0xff) << 8) | + ((codeBytes[u4p+3] & 0xff))); + } + /** * Insert room for byteCount bytes after the instruction at pc * and prepare to replace the instruction at pc. The instruction * at pc is not modified by this call, space is allocated after it. @@ -487,12 +846,10 @@ CodeChunk insertCodeSpace(int pc, int additionalBytes) { short existingOpcode = getOpcode(pc); - int lengthOfExistingInstruction; - if (existingOpcode == VMOpcode.GOTO_W) - lengthOfExistingInstruction = 5; - else - lengthOfExistingInstruction = 3; - + + int lengthOfExistingInstruction + = instructionLength(existingOpcode); + if (additionalBytes > 0) { @@ -515,8 +872,6 @@ // Shift the existing code stream down - // pc + 3 - // Pc + 3 + 5 System.arraycopy( codeBytes, byteOffset, codeBytes, byteOffset + additionalBytes, @@ -545,4 +900,333 @@ return new CodeChunk(this, pc, additionalBytes); } + + /* + * * Methods related to splitting the byte code chunks into sections that + * fit in the JVM's limits for a single method. + */ + + /** + * For a block of byte code starting at program counter pc for codeLength + * bytes return the maximum stack value, assuming a initial stack depth of + * zero. + */ + private int findMaxStack(ClassHolder ch, int pc, int codeLength) { + + int endPc = pc + codeLength; + int stack = 0; + int maxStack = 0; + + for (; pc < endPc;) { + + short opcode = getOpcode(pc); + + int stackDelta = stackWordDelta(ch, pc, opcode); + + stack += stackDelta; + if (stack > maxStack) + maxStack = stack; + + int[] cond_pcs = findConditionalPCs(pc, opcode); + if (cond_pcs != null) { + // an else block exists. + if (cond_pcs[3] != -1) { + int blockMaxStack = findMaxStack(ch, cond_pcs[1], + cond_pcs[2]); + if ((stack + blockMaxStack) > maxStack) + maxStack = stack + blockMaxStack; + + pc = cond_pcs[3]; + continue; + } + } + + pc += instructionLength(opcode); + } + + return maxStack; + } + + /** + * Return the number of stack words pushed (positive) or popped (negative) + * by this instruction. + */ + private int stackWordDelta(ClassHolder ch, int pc, short opcode) { + if (SanityManager.DEBUG) { + // this validates the OPCODE_ACTION entry + instructionLength(opcode); + } + + int stackDelta = OPCODE_ACTION[opcode][0]; + if (stackDelta == VARIABLE_STACK) { + stackDelta = getVariableStackDelta(ch, pc, opcode); + } + + return stackDelta; + } + + /** + * Get the type descriptor in the virtual machine format for the type + * defined by the constant pool index for the instruction at pc. + */ + private String getTypeDescriptor(ClassHolder ch, int pc) { + int cpi = getU2(pc); + + // Field reference or method reference + CONSTANT_Index_info cii = (CONSTANT_Index_info) ch.getEntry(cpi); + + // NameAndType reference + int nameAndType = cii.getI2(); + cii = (CONSTANT_Index_info) ch.getEntry(nameAndType); + + // UTF8 descriptor + int descriptor = cii.getI2(); + CONSTANT_Utf8_info type = (CONSTANT_Utf8_info) ch.getEntry(descriptor); + + String vmDescriptor = type.toString(); + + return vmDescriptor; + } + + /** + * Get the word count for a type descriptor in the format of the virual + * machine. For a method this returns the the word count for the return + * type. + */ + private static int getDescriptorWordCount(String vmDescriptor) { + // System.out.println("vmDescriptor" + vmDescriptor); + + int width; + if (VMDescriptor.DOUBLE.equals(vmDescriptor)) + width = 2; + else if (VMDescriptor.LONG.equals(vmDescriptor)) + width = 2; + else if (vmDescriptor.charAt(0) == VMDescriptor.C_METHOD) { + switch (vmDescriptor.charAt(vmDescriptor.length() - 1)) { + case VMDescriptor.C_DOUBLE: + case VMDescriptor.C_LONG: + width = 2; + break; + case VMDescriptor.C_VOID: + width = 0; + break; + default: + width = 1; + break; + } + } else + width = 1; + + return width; + } + + /** + * Get the number of words pushed (positive) or popped (negative) by this + * instruction. The instruction is a get/put field or a method call, thus + * the size of the words is defined by the field or method being access. + */ + private int getVariableStackDelta(ClassHolder ch, int pc, int opcode) { + String vmDescriptor = getTypeDescriptor(ch, pc); + int width = CodeChunk.getDescriptorWordCount(vmDescriptor); + + int stackDelta = 0; + // Stack delta depends on context. + switch (opcode) { + case VMOpcode.GETSTATIC: + stackDelta = width; + break; + + case VMOpcode.GETFIELD: + stackDelta = width - 1; // one for popped object ref + break; + + case VMOpcode.PUTSTATIC: + stackDelta = -width; + break; + + case VMOpcode.PUTFIELD: + stackDelta = -width - 1; // one for pop object ref + break; + + case VMOpcode.INVOKEVIRTUAL: + case VMOpcode.INVOKESPECIAL: + stackDelta = -1; // for instance reference for method call. + case VMOpcode.INVOKESTATIC: + stackDelta += (width - CodeChunk.parameterWordCount(vmDescriptor)); + // System.out.println("invoked non-interface " + stackDelta); + break; + + case VMOpcode.INVOKEINTERFACE: + // third byte contains the number of arguments to be popped + stackDelta = width - getOpcode(pc + 3); + // System.out.println("invoked interface " + stackDelta); + break; + default: + System.out.println("WHO IS THIS "); + break; + + } + + return stackDelta; + } + + /** + * Calculate the number of stack words in the arguments pushed for this + * method descriptor. + */ + private static int parameterWordCount(String methodDescriptor) { + int wordCount = 0; + for (int i = 1;; i++) { + switch (methodDescriptor.charAt(i)) { + case VMDescriptor.C_ENDMETHOD: + return wordCount; + case VMDescriptor.C_DOUBLE: + case VMDescriptor.C_LONG: + wordCount += 2; + break; + case VMDescriptor.C_ARRAY: + // skip while there are array symbols. + do { + i++; + } while (methodDescriptor.charAt(i) == VMDescriptor.C_ARRAY); + if (methodDescriptor.charAt(i) != VMDescriptor.C_CLASS) { + // an array is a reference, even an array of doubles. + wordCount += 1; + break; + } + + // fall through to skip the Lclassname; after the array. + + case VMDescriptor.C_CLASS: + // skip until ; + do { + i++; + } while (methodDescriptor.charAt(i) != VMDescriptor.C_ENDCLASS); + wordCount += 1; + break; + default: + wordCount += 1; + break; + } + } + } + + /** + * Find the limits of a conditional block starting at the instruction with + * the given opcode at the program counter pc. + *

+ * Returns a six element integer array of program counters and lengths. + * + * + * Looks for and handles conditionals that are written by the Conditional + * class. + * + * @return Null if the opcode is not the start of a conditional otherwise + * the array of values. + */ + private int[] findConditionalPCs(int pc, short opcode) { + switch (opcode) { + default: + return null; + case VMOpcode.IFNONNULL: + case VMOpcode.IFNULL: + case VMOpcode.IFEQ: + case VMOpcode.IFNE: + break; + } + + int then_pc; + int else_pc; + int if_off = getU2(pc); + + if ((if_off == 8) + && (getOpcode(pc + VMOpcode.IF_INS_LENGTH) == VMOpcode.GOTO_W)) { + // 32 bit branch + then_pc = pc + VMOpcode.IF_INS_LENGTH + VMOpcode.GOTO_W_INS_LENGTH; + + // Get else PC from the 32 bit offset within the GOTO_W + // instruction remembering to add it to the pc of that + // instruction, not the original IF. + else_pc = pc + VMOpcode.IF_INS_LENGTH + + getU4(pc + VMOpcode.IF_INS_LENGTH); + + } else { + then_pc = pc + VMOpcode.IF_INS_LENGTH; + else_pc = pc + if_off; + } + + // Need to look for the goto or goto_w at the + // end of the then block. There might not be + // one for the case when there is no else block. + // In that case the then block will just run into + // what we currently think the else pc. + + int end_pc = -1; + for (int tpc = then_pc; tpc < else_pc;) { + short opc = getOpcode(tpc); + + // need to handle conditionals + int[] innerCond = findConditionalPCs(tpc, opc); + if (innerCond != null) { + // skip to the end of this conditional + tpc = innerCond[5]; // end_pc + continue; + } + + if (opc == VMOpcode.GOTO) { + // not at the end of the then block + // so not our goto. Shouldn't see this + // with the current code due to the + // skipping of the conditional above. + // But safe defensive programming. + if (tpc != (else_pc - VMOpcode.GOTO_INS_LENGTH)) + continue; + + end_pc = tpc + getU2(tpc); + break; + } else if (opc == VMOpcode.GOTO_W) { + // not at the end of the then block + // so not our goto. SHouldn't see this + // with the current code due to the + // skipping of the conditional above. + // But safe defensive programming. + if (tpc != (else_pc - VMOpcode.GOTO_W_INS_LENGTH)) + continue; + + end_pc = tpc + getU4(tpc); + break; + + } + tpc += instructionLength(opc); + } + + int else_len; + int then_len; + if (end_pc == -1) { + // no else block; + end_pc = else_pc; + else_pc = -1; + + then_len = end_pc - then_pc; + else_len = -1; + } else { + then_len = else_pc - then_pc; + else_len = end_pc - else_pc; + } + + int[] ret = new int[6]; + + ret[0] = pc; + ret[1] = then_pc; + ret[2] = then_len; + ret[3] = else_pc; + ret[4] = else_len; + ret[5] = end_pc; + + return ret; + } }