db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kmars...@apache.org
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 GMT
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.
+	 * <P>
+	 * 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.
+	 * 
+	 * <P>
+	 * 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.
+     * <P>
+     * Returns a six element integer array of program counters and lengths.
+     * <code. [0] - program counter of the IF opcode (passed in as pc) [1] -
+     * program counter of the start of the then block [2] - length of the then
+     * block [3] - program counter of the else block, -1 if no else block
+     * exists. [4] - length of of the else block, -1 if no else block exists.
+     * [5] - program counter of the common end point. </code>
+     * 
+     * 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;
+    }
 }



Mime
View raw message