--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.services.bytecode.CodeChunk\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.services.bytecode;\r
+\r
+import org.apache.derby.iapi.services.classfile.CONSTANT_Index_info;\r
+import org.apache.derby.iapi.services.classfile.CONSTANT_Utf8_info;\r
+import org.apache.derby.iapi.services.classfile.ClassFormatOutput;\r
+import org.apache.derby.iapi.services.classfile.ClassHolder;\r
+import org.apache.derby.iapi.services.classfile.ClassMember;\r
+import org.apache.derby.iapi.services.classfile.VMDescriptor;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+import org.apache.derby.iapi.services.io.ArrayOutputStream;\r
+\r
+import java.io.IOException;\r
+import java.lang.reflect.Modifier;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * This class represents a chunk of code in a CodeAttribute.\r
+ * Typically, a CodeAttribute represents the code in a method.\r
+ * If there is a try/catch block, each catch block will get its\r
+ * own code chunk. This allows the catch blocks to all be put at\r
+ * the end of the generated code for a method, which eliminates\r
+ * the need to generate a jump around each catch block, which\r
+ * would be a forward reference.\r
+ */\r
+final class CodeChunk {\r
+ \r
+ /**\r
+ * Starting point of the byte code stream in the underlying stream/array.\r
+ */\r
+ private static final int CODE_OFFSET = 8;\r
+ \r
+ // The use of ILOAD for the non-integer types is correct.\r
+ // We have to assume that the appropriate checks/conversions\r
+ // are defined on math operation results to ensure that\r
+ // the type is preserved when/as needed.\r
+ static final short[] LOAD_VARIABLE = {\r
+ VMOpcode.ILOAD, /* vm_byte */\r
+ VMOpcode.ILOAD, /* vm_short */\r
+ VMOpcode.ILOAD, /* vm_int */\r
+ VMOpcode.LLOAD, /* vm_long */\r
+ VMOpcode.FLOAD, /* vm_float */\r
+ VMOpcode.DLOAD, /* vm_double */\r
+ VMOpcode.ILOAD, /* vm_char */\r
+ VMOpcode.ALOAD /* vm_reference */\r
+ };\r
+\r
+ static final short[] LOAD_VARIABLE_FAST = {\r
+ VMOpcode.ILOAD_0, /* vm_byte */\r
+ VMOpcode.ILOAD_0, /* vm_short */\r
+ VMOpcode.ILOAD_0, /* vm_int */\r
+ VMOpcode.LLOAD_0, /* vm_long */\r
+ VMOpcode.FLOAD_0, /* vm_float */\r
+ VMOpcode.DLOAD_0, /* vm_double */\r
+ VMOpcode.ILOAD_0, /* vm_char */\r
+ VMOpcode.ALOAD_0 /* vm_reference */\r
+ };\r
+\r
+ // The ISTOREs for non-int types are how things work.\r
+ // It assumes that the appropriate casts are done\r
+ // on operations on non-ints to ensure that the values\r
+ // remain in the valid ranges.\r
+ static final short[] STORE_VARIABLE = {\r
+ VMOpcode.ISTORE, /* vm_byte */\r
+ VMOpcode.ISTORE, /* vm_short */\r
+ VMOpcode.ISTORE, /* vm_int */\r
+ VMOpcode.LSTORE, /* vm_long */\r
+ VMOpcode.FSTORE, /* vm_float */\r
+ VMOpcode.DSTORE, /* vm_double */\r
+ VMOpcode.ISTORE, /* vm_char */\r
+ VMOpcode.ASTORE /* vm_reference */\r
+ };\r
+\r
+ static final short[] STORE_VARIABLE_FAST = {\r
+ VMOpcode.ISTORE_0, /* vm_byte */\r
+ VMOpcode.ISTORE_0, /* vm_short */\r
+ VMOpcode.ISTORE_0, /* vm_int */\r
+ VMOpcode.LSTORE_0, /* vm_long */\r
+ VMOpcode.FSTORE_0, /* vm_float */\r
+ VMOpcode.DSTORE_0, /* vm_double */\r
+ VMOpcode.ISTORE_0, /* vm_char */\r
+ VMOpcode.ASTORE_0 /* vm_reference */\r
+ };\r
+\r
+ static final short ARRAY_ACCESS[] = {\r
+ VMOpcode.BALOAD, /* vm_byte */\r
+ VMOpcode.SALOAD, /* vm_short */\r
+ VMOpcode.IALOAD, /* vm_int */\r
+ VMOpcode.LALOAD, /* vm_long */\r
+ VMOpcode.FALOAD, /* vm_float */\r
+ VMOpcode.DALOAD, /* vm_double */\r
+ VMOpcode.CALOAD, /* vm_char */\r
+ VMOpcode.AALOAD /* vm_reference */\r
+ };\r
+ static final short ARRAY_STORE[] = {\r
+ VMOpcode.BASTORE, /* vm_byte */\r
+ VMOpcode.SASTORE, /* vm_short */\r
+ VMOpcode.IASTORE, /* vm_int */\r
+ VMOpcode.LASTORE, /* vm_long */\r
+ VMOpcode.FASTORE, /* vm_float */\r
+ VMOpcode.DASTORE, /* vm_double */\r
+ VMOpcode.CASTORE, /* vm_char */\r
+ VMOpcode.AASTORE /* vm_reference */\r
+ }; \r
+ static final short[] RETURN_OPCODE = {\r
+ VMOpcode.IRETURN, /* 0 = byte */\r
+ VMOpcode.IRETURN, /* 1 = short */\r
+ VMOpcode.IRETURN, /* 2 = int */\r
+ VMOpcode.LRETURN, /* 3 = long */\r
+ VMOpcode.FRETURN, /* 4 = float */\r
+ VMOpcode.DRETURN, /* 5 = double */\r
+ VMOpcode.IRETURN, /* 6 = char */\r
+ VMOpcode.ARETURN /* 7 = reference */\r
+ };\r
+\r
+ // the first dimension is the current vmTypeId\r
+ // the second dimension is the target vmTypeId\r
+ //\r
+ // the cells of the entry at [current,target] are:\r
+ // 0: operation\r
+ // 1: result type of operation\r
+ // if entry[1] = target, we are done. otherwise,\r
+ // you have to continue with entry[1] as the new current\r
+ // after generating the opcode listed (don't generate if it is NOP).\r
+ // if entry[0] = BAD, we can\r
+ \r
+ static final short CAST_CONVERSION_INFO[][][] = {\r
+ /* current = vm_byte */\r
+ {\r
+ /* target = vm_byte */ { VMOpcode.NOP, BCExpr.vm_byte },\r
+ /* target = vm_short */ { VMOpcode.NOP, BCExpr.vm_short },\r
+ /* target = vm_int */ { VMOpcode.NOP, BCExpr.vm_int },\r
+ /* target = vm_long */ { VMOpcode.NOP, BCExpr.vm_int },\r
+ /* target = vm_float */ { VMOpcode.NOP, BCExpr.vm_int },\r
+ /* target = vm_double */ { VMOpcode.NOP, BCExpr.vm_int },\r
+ /* target = vm_char */ { VMOpcode.NOP, BCExpr.vm_char }, \r
+ /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }\r
+ },\r
+ /* current = vm_short */\r
+ {\r
+ /* target = vm_byte */ { VMOpcode.NOP, BCExpr.vm_byte },\r
+ /* target = vm_short */ { VMOpcode.NOP, BCExpr.vm_short },\r
+ /* target = vm_int */ { VMOpcode.NOP, BCExpr.vm_int },\r
+ /* target = vm_long */ { VMOpcode.NOP, BCExpr.vm_int },\r
+ /* target = vm_float */ { VMOpcode.NOP, BCExpr.vm_int },\r
+ /* target = vm_double */ { VMOpcode.NOP, BCExpr.vm_int },\r
+ /* target = vm_char */ { VMOpcode.NOP, BCExpr.vm_char }, \r
+ /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }\r
+ },\r
+ /* current = vm_int */\r
+ {\r
+ /* target = vm_byte */ { VMOpcode.I2B, BCExpr.vm_byte },\r
+ /* target = vm_short */ { VMOpcode.I2S, BCExpr.vm_short },\r
+ /* target = vm_int */ { VMOpcode.NOP, BCExpr.vm_int },\r
+ /* target = vm_long */ { VMOpcode.I2L, BCExpr.vm_long },\r
+ /* target = vm_float */ { VMOpcode.I2F, BCExpr.vm_float },\r
+ /* target = vm_double */ { VMOpcode.I2D, BCExpr.vm_double },\r
+ /* target = vm_char */ { VMOpcode.I2B, BCExpr.vm_char }, \r
+ /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }\r
+ },\r
+ /* current = vm_long */\r
+ {\r
+ /* target = vm_byte */ { VMOpcode.L2I, BCExpr.vm_int },\r
+ /* target = vm_short */ { VMOpcode.L2I, BCExpr.vm_int },\r
+ /* target = vm_int */ { VMOpcode.L2I, BCExpr.vm_int },\r
+ /* target = vm_long */ { VMOpcode.NOP, BCExpr.vm_long },\r
+ /* target = vm_float */ { VMOpcode.L2F, BCExpr.vm_float },\r
+ /* target = vm_double */ { VMOpcode.L2D, BCExpr.vm_double },\r
+ /* target = vm_char */ { VMOpcode.L2I, BCExpr.vm_int }, \r
+ /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }\r
+ },\r
+ /* current = vm_float */\r
+ {\r
+ /* target = vm_byte */ { VMOpcode.F2I, BCExpr.vm_int },\r
+ /* target = vm_short */ { VMOpcode.F2I, BCExpr.vm_int },\r
+ /* target = vm_int */ { VMOpcode.F2I, BCExpr.vm_int },\r
+ /* target = vm_long */ { VMOpcode.F2L, BCExpr.vm_long },\r
+ /* target = vm_float */ { VMOpcode.NOP, BCExpr.vm_float },\r
+ /* target = vm_double */ { VMOpcode.F2D, BCExpr.vm_double },\r
+ /* target = vm_char */ { VMOpcode.F2I, BCExpr.vm_int }, \r
+ /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }\r
+ },\r
+ /* current = vm_double */\r
+ {\r
+ /* target = vm_byte */ { VMOpcode.D2I, BCExpr.vm_int },\r
+ /* target = vm_short */ { VMOpcode.D2I, BCExpr.vm_int },\r
+ /* target = vm_int */ { VMOpcode.D2I, BCExpr.vm_int },\r
+ /* target = vm_long */ { VMOpcode.D2L, BCExpr.vm_long },\r
+ /* target = vm_float */ { VMOpcode.D2F, BCExpr.vm_float },\r
+ /* target = vm_double */ { VMOpcode.NOP, BCExpr.vm_double },\r
+ /* target = vm_char */ { VMOpcode.D2I, BCExpr.vm_int }, \r
+ /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }\r
+ },\r
+ /* current = vm_char */\r
+ {\r
+ /* target = vm_byte */ { VMOpcode.NOP, BCExpr.vm_byte },\r
+ /* target = vm_short */ { VMOpcode.NOP, BCExpr.vm_short },\r
+ /* target = vm_int */ { VMOpcode.NOP, BCExpr.vm_int },\r
+ /* target = vm_long */ { VMOpcode.NOP, BCExpr.vm_int },\r
+ /* target = vm_float */ { VMOpcode.NOP, BCExpr.vm_int },\r
+ /* target = vm_double */ { VMOpcode.NOP, BCExpr.vm_int },\r
+ /* target = vm_char */ { VMOpcode.NOP, BCExpr.vm_char },\r
+ /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }\r
+ },\r
+ /* current = vm_reference */\r
+ {\r
+ /* target = vm_byte */ { VMOpcode.BAD, BCExpr.vm_byte },\r
+ /* target = vm_short */ { VMOpcode.BAD, BCExpr.vm_short },\r
+ /* target = vm_int */ { VMOpcode.BAD, BCExpr.vm_int },\r
+ /* target = vm_long */ { VMOpcode.BAD, BCExpr.vm_long },\r
+ /* target = vm_float */ { VMOpcode.BAD, BCExpr.vm_float },\r
+ /* target = vm_double */ { VMOpcode.BAD, BCExpr.vm_double },\r
+ /* target = vm_char */ { VMOpcode.BAD, BCExpr.vm_char },\r
+ /* target = vm_reference */ { VMOpcode.NOP, BCExpr.vm_reference }\r
+ }\r
+ };\r
+ \r
+ /**\r
+ * Constant used by OPCODE_ACTION to represent the\r
+ * common action of push one word, 1 byte\r
+ * for the instruction.\r
+ */\r
+ private static final byte[] push1_1i = {1, 1};\r
+\r
+ /**\r
+ * Constant used by OPCODE_ACTION to represent the\r
+ * common action of push two words, 1 byte\r
+ * for the instruction.\r
+ */\r
+ private static final byte[] push2_1i = {2, 1}; \r
+ /**\r
+ * Constant used by OPCODE_ACTION to the opcode is\r
+ * not yet supported.\r
+ */\r
+ private static final byte[] NS = {0, -1};\r
+ \r
+ \r
+ /**\r
+ * Value for OPCODE_ACTION[opcode][0] to represent\r
+ * the number of words popped or pushed in variable.\r
+ */\r
+ private static final byte VARIABLE_STACK = -128;\r
+ \r
+ /**\r
+ * Array that provides two pieces of information about\r
+ * each VM opcode. Each opcode has a two byte array.\r
+ * <P>\r
+ * The first element in the array [0] is the number of\r
+ * stack words (double/long count as two) pushed by the opcode.\r
+ * Will be negative if the opcode pops values.\r
+ * \r
+ * <P>\r
+ * The second element in the array [1] is the number of bytes\r
+ * in the instruction stream that this opcode's instruction\r
+ * takes up, including the opocode.\r
+ */\r
+ private static final byte[][] OPCODE_ACTION =\r
+ {\r
+ \r
+ /* NOP 0 */ { 0, 1 },\r
+ \r
+ /* ACONST_NULL 1 */ push1_1i,\r
+ /* ICONST_M1 2 */ push1_1i,\r
+ /* ICONST_0 3 */ push1_1i,\r
+ /* ICONST_1 4 */ push1_1i,\r
+ /* ICONST_2 5 */ push1_1i,\r
+ /* ICONST_3 6 */ push1_1i,\r
+ /* ICONST_4 7 */ push1_1i,\r
+ /* ICONST_5 8 */ push1_1i,\r
+ /* LCONST_0 9 */ push2_1i,\r
+ /* LCONST_1 10 */ push2_1i,\r
+ /* FCONST_0 11 */ push1_1i,\r
+ /* FCONST_1 12 */ push1_1i,\r
+ /* FCONST_2 13 */ push1_1i,\r
+ /* DCONST_0 14 */ push2_1i,\r
+ /* DCONST_1 15 */ push2_1i,\r
+ \r
+ /* BIPUSH 16 */ {1, 2},\r
+ /* SIPUSH 17 */ {1, 3},\r
+ /* LDC 18 */ {1, 2},\r
+ /* LDC_W 19 */ {1, 3},\r
+ /* LDC2_W 20 */ {2, 3},\r
+ \r
+ /* ILOAD 21 */ { 1, 2 },\r
+ /* LLOAD 22 */ { 2, 2 },\r
+ /* FLOAD 23 */ { 1, 2 },\r
+ /* DLOAD 24 */ { 2, 2 },\r
+ /* ALOAD 25 */ { 1, 2 },\r
+ /* ILOAD_0 26 */ push1_1i,\r
+ /* ILOAD_1 27 */ push1_1i,\r
+ /* ILOAD_2 28 */ push1_1i,\r
+ /* ILOAD_3 29 */ push1_1i,\r
+ /* LLOAD_0 30 */ push2_1i,\r
+ /* LLOAD_1 31 */ push2_1i,\r
+ /* LLOAD_2 32 */ push2_1i,\r
+ /* LLOAD_3 33 */ push2_1i,\r
+ /* FLOAD_0 34 */ push1_1i,\r
+ /* FLOAD_1 35 */ push1_1i,\r
+ /* FLOAD_2 36 */ push1_1i,\r
+ /* FLOAD_3 37 */ push1_1i,\r
+ /* DLOAD_0 38 */ push2_1i,\r
+ /* DLOAD_1 39 */ push2_1i,\r
+ /* DLOAD_2 40 */ push2_1i,\r
+ /* DLOAD_3 41 */ push2_1i,\r
+ /* ALOAD_0 42 */ push1_1i,\r
+ /* ALOAD_1 43 */ push1_1i,\r
+ /* ALOAD_2 44 */ push1_1i,\r
+ /* ALOAD_3 45 */ push1_1i,\r
+ /* IALOAD 46 */ { -1, 1 },\r
+ /* LALOAD 47 */ { 0, 1 },\r
+ /* FALOAD 48 */ { -1, 1 },\r
+ /* DALOAD 49 */ { 0, 1 },\r
+ /* AALOAD 50 */ { -1, 1 },\r
+ /* BALOAD 51 */ { -1, 1 },\r
+ /* CALOAD 52 */ { -1, 1 },\r
+ \r
+ /* SALOAD 53 */ { -1, 1 },\r
+ /* ISTORE 54 */ { -1, 2 },\r
+ /* LSTORE 55 */ { -2, 2 },\r
+ /* FSTORE 56 */ { -1, 2 },\r
+ /* DSTORE 57 */ { -2, 2 },\r
+ /* ASTORE 58 */ { -1, 2 },\r
+ /* ISTORE_0 59 */ { -1, 1 },\r
+ /* ISTORE_1 60 */ { -1, 1 },\r
+ /* ISTORE_2 61 */ { -1, 1 },\r
+ /* ISTORE_3 62 */ { -1, 1 },\r
+ /* LSTORE_0 63 */ { -2, 1 },\r
+ /* LSTORE_1 64 */ { -2, 1 },\r
+ /* LSTORE_2 65 */ { -2, 1 },\r
+ /* LSTORE_3 66 */ { -2, 1 },\r
+ /* FSTORE_0 67 */ { -1, 1 },\r
+ /* FSTORE_1 68 */ { -1, 1 },\r
+ /* FSTORE_2 69 */ { -1, 1 },\r
+ /* FSTORE_3 70 */ { -1, 1 },\r
+ /* DSTORE_0 71 */ { -2, 1 },\r
+ /* DSTORE_1 72 */ { -2, 1 },\r
+ /* DSTORE_2 73 */ { -2, 1 },\r
+ /* DSTORE_3 74 */ { -2, 1 },\r
+ /* ASTORE_0 75 */ { -1, 1 },\r
+ /* ASTORE_1 76 */ { -1, 1 },\r
+ /* ASTORE_2 77 */ { -1, 1 },\r
+ /* ASTORE_3 78 */ { -1, 1 },\r
+ /* IASTORE 79 */ { -3, 1 },\r
+ /* LASTORE 80 */ { -4, 1 },\r
+ /* FASTORE 81 */ { -3, 1 },\r
+ /* DASTORE 82 */ { -4, 1 },\r
+ /* AASTORE 83 */ { -3, 1 },\r
+ /* BASTORE 84 */ { -3, 1 },\r
+ /* CASTORE 85 */ { -3, 1 },\r
+ /* SASTORE 86 */ { -3, 1 },\r
+ \r
+ /* POP 87 */ { -1, 1 },\r
+ /* POP2 88 */ { -2, 1 },\r
+ /* DUP 89 */ push1_1i,\r
+ /* DUP_X1 90 */ push1_1i,\r
+ /* DUP_X2 91 */ push1_1i,\r
+ /* DUP2 92 */ push2_1i,\r
+ /* DUP2_X1 93 */ push2_1i,\r
+ /* DUP2_X2 94 */ push2_1i,\r
+ /* SWAP 95 */ { 0, 1 },\r
+ \r
+ /* IADD 96 */ NS,\r
+ /* LADD 97 */ NS,\r
+ /* FADD 98 */ { -1, 1 },\r
+ /* DADD 99 */ { -2, 1 },\r
+ /* ISUB 100 */ NS,\r
+ /* LSUB 101 */ NS,\r
+ /* FSUB 102 */ { -1, 1 },\r
+ /* DSUB 103 */ { -2, 1 },\r
+ /* IMUL 104 */ NS,\r
+ /* LMUL 105 */ NS,\r
+ /* FMUL 106 */ { -1, 1 },\r
+ /* DMUL 107 */ { -2, 1 },\r
+ /* IDIV 108 */ NS,\r
+ /* LDIV 109 */ NS,\r
+ /* FDIV 110 */ { -1, 1 },\r
+ /* DDIV 111 */ { -2, 1 },\r
+ /* IREM 112 */ { -1, 1 },\r
+ /* LREM 113 */ { -2, 1 },\r
+ /* FREM 114 */ { -1, 1 },\r
+ /* DREM 115 */ { -2, 1 },\r
+ /* INEG 116 */ { 0, 1 },\r
+ /* LNEG 117 */ { 0, 1 },\r
+ /* FNEG 118 */ { 0, 1 },\r
+ /* DNEG 119 */ { 0, 1 },\r
+ /* ISHL 120 */ { -1, 1 },\r
+ /* LSHL 121 */ NS,\r
+ /* ISHR 122 */ NS,\r
+ /* LSHR 123 */ NS,\r
+ /* IUSHR 124 */ NS,\r
+ /* LUSHR 125 */ NS,\r
+ \r
+ /* IAND 126 */ { -1, 1 },\r
+ /* LAND 127 */ NS,\r
+ /* IOR 128 */ { -1, 1 },\r
+ /* LOR 129 */ NS,\r
+ /* IXOR 130 */ NS,\r
+ /* LXOR 131 */ NS,\r
+ /* IINC 132 */ NS,\r
+ \r
+ /* I2L 133 */ push1_1i,\r
+ /* I2F 134 */ { 0, 1 },\r
+ /* I2D 135 */ push1_1i,\r
+ /* L2I 136 */ { -1, 1 },\r
+ /* L2F 137 */ { -1, 1 },\r
+ /* L2D 138 */ { 0, 1 },\r
+ /* F2I 139 */ { 0, 1 },\r
+ /* F2L 140 */ push2_1i,\r
+ /* F2D 141 */ push1_1i,\r
+ /* D2I 142 */ { -1, 1 },\r
+ /* D2L 143 */ { 0, 1 },\r
+ /* D2F 144 */ { -1, 1 },\r
+ /* I2B 145 */ { 0, 1 },\r
+ /* I2C 146 */ { 0, 1 },\r
+ /* I2S 147 */ { 0, 1 },\r
+ \r
+ /* LCMP 148 */ NS,\r
+ /* FCMPL 149 */ { -1, 1 },\r
+ /* FCMPG 150 */ { -1, 1 },\r
+ /* DCMPL 151 */ { -3, 1 },\r
+ /* DCMPG 152 */ { -3, 1 },\r
+ /* IFEQ 153 */ { -1, VMOpcode.IF_INS_LENGTH },\r
+ /* IFNE 154 */ { -1, VMOpcode.IF_INS_LENGTH },\r
+ /* IFLT 155 */ { -1, VMOpcode.IF_INS_LENGTH },\r
+ /* IFGE 156 */ { -1, VMOpcode.IF_INS_LENGTH },\r
+ /* IFGT 157 */ { -1, VMOpcode.IF_INS_LENGTH },\r
+ /* IFLE 158 */ { -1, VMOpcode.IF_INS_LENGTH },\r
+ /* IF_ICMPEQ 159 */ NS,\r
+ /* IF_ICMPNE 160 */ NS,\r
+ /* IF_ICMPLT 161 */ NS,\r
+ /* IF_ICMPGE 162 */ NS,\r
+ /* IF_ICMPGT 163 */ NS,\r
+ /* IF_ICMPLE 164 */ NS,\r
+ /* IF_ACMPEQ 165 */ NS,\r
+ /* IF_ACMPNE 166 */ NS,\r
+ /* GOTO 167 */ { 0, VMOpcode.GOTO_INS_LENGTH },\r
+ /* JSR 168 */ NS,\r
+ /* RET 169 */ NS,\r
+ /* TABLESWITCH 170 */ NS,\r
+ /* LOOKUPSWITCH 171 */NS,\r
+ \r
+ /* IRETURN 172 */ { -1, 1 }, // strictly speaking all words on the stack are popped.\r
+ /* LRETURN 173 */ { -2, 1 }, // strictly speaking all words on the stack are popped.\r
+ /* FRETURN 174 */ { -1, 1 }, // strictly speaking all words on the stack are popped.\r
+ /* DRETURN 175 */ { -2, 1 }, // strictly speaking all words on the stack are popped.\r
+ /* ARETURN 176 */ { -1, 1 }, // strictly speaking all words on the stack are popped.\r
+ /* RETURN 177 */ { 0, 1 }, // strictly speaking all words on the stack are popped.\r
+\r
+ /* GETSTATIC 178 */ {VARIABLE_STACK, 3 },\r
+ /* PUTSTATIC 179 */ {VARIABLE_STACK, 3 },\r
+ /* GETFIELD 180 */ {VARIABLE_STACK, 3 },\r
+ /* PUTFIELD 181 */ {VARIABLE_STACK, 3 },\r
+ /* INVOKEVIRTUAL 182 */ {VARIABLE_STACK, 3 },\r
+ /* INVOKESPECIAL 183 */ {VARIABLE_STACK, 3 },\r
+ /* INVOKESTATIC 184 */ {VARIABLE_STACK, 3 },\r
+ /* INVOKEINTERFACE 185 */ {VARIABLE_STACK, 5 },\r
+ \r
+ /* XXXUNUSEDXXX 186 */ NS,\r
+\r
+ /* NEW 187 */ { 1, 3 },\r
+ /* NEWARRAY 188 */ { 0, 2 },\r
+ /* ANEWARRAY 189 */ { 0, 3 },\r
+ /* ARRAYLENGTH 190 */ { 0, 1 },\r
+ /* ATHROW 191 */ NS,\r
+ /* CHECKCAST 192 */ { 0, 3},\r
+ /* INSTANCEOF 193 */ { 0, 3 },\r
+ /* MONITORENTER 194 */ NS,\r
+ /* MONITOREXIT 195 */ NS,\r
+ /* WIDE 196 */ NS,\r
+ /* MULTIANEWARRAY 197 */ NS,\r
+ /* IFNULL 198 */ { -1, VMOpcode.IF_INS_LENGTH },\r
+ /* IFNONNULL 199 */ { -1, VMOpcode.IF_INS_LENGTH },\r
+ /* GOTO_W 200 */ {0, VMOpcode.GOTO_W_INS_LENGTH },\r
+ /* JSR_W 201 */ NS,\r
+ /* BREAKPOINT 202 */ NS,\r
+ \r
+ };\r
+ \r
+ /**\r
+ * Assume an IOException means some limit of the class file\r
+ * format was hit\r
+ * \r
+ */\r
+ private void limitHit(IOException ioe)\r
+ {\r
+ cb.addLimitExceeded(ioe.toString());\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Add an instruction that has no operand.\r
+ * All opcodes are 1 byte large.\r
+ */\r
+ void addInstr(short opcode) {\r
+ try {\r
+ cout.putU1(opcode);\r
+ } catch (IOException ioe) {\r
+ limitHit(ioe);\r
+ }\r
+\r
+ if (SanityManager.DEBUG) { \r
+ if (OPCODE_ACTION[opcode][1] != 1)\r
+ SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" +\r
+ " writing 1 byte - set as " + OPCODE_ACTION[opcode][1]); \r
+ }\r
+ }\r
+\r
+ /**\r
+ * Add an instruction that has a 16 bit operand.\r
+ */\r
+ void addInstrU2(short opcode, int operand) {\r
+ \r
+ try {\r
+ cout.putU1(opcode);\r
+ cout.putU2(operand);\r
+ } catch (IOException ioe) {\r
+ limitHit(ioe);\r
+ }\r
+\r
+ if (SanityManager.DEBUG) { \r
+ if (OPCODE_ACTION[opcode][1] != 3)\r
+ SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" +\r
+ " writing 3 bytes - set as " + OPCODE_ACTION[opcode][1]); \r
+ }\r
+ }\r
+\r
+ /**\r
+ * Add an instruction that has a 32 bit operand.\r
+ */\r
+ void addInstrU4(short opcode, int operand) {\r
+ try {\r
+ cout.putU1(opcode);\r
+ cout.putU4(operand);\r
+ } catch (IOException ioe) {\r
+ limitHit(ioe);\r
+ }\r
+ if (SanityManager.DEBUG) { \r
+ if (OPCODE_ACTION[opcode][1] != 5)\r
+ SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" +\r
+ " writing 5 bytes - set as " + OPCODE_ACTION[opcode][1]); \r
+ }\r
+ }\r
+\r
+ \r
+ /**\r
+ * Add an instruction that has an 8 bit operand.\r
+ */\r
+ void addInstrU1(short opcode, int operand) {\r
+ try {\r
+ cout.putU1(opcode);\r
+ cout.putU1(operand);\r
+ } catch (IOException ioe) {\r
+ limitHit(ioe);\r
+ }\r
+\r
+ // Only debug code from here.\r
+ if (SanityManager.DEBUG) {\r
+ \r
+ if (OPCODE_ACTION[opcode][1] != 2)\r
+ SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" +\r
+ " writing 2 bytes - set as " + OPCODE_ACTION[opcode][1]);\r
+ \r
+ }\r
+ }\r
+\r
+ /**\r
+ * This takes an instruction that has a narrow\r
+ * and a wide form for CPE access, and\r
+ * generates accordingly the right one.\r
+ * We assume the narrow instruction is what\r
+ * we were given, and that the wide form is\r
+ * the next possible instruction.\r
+ */\r
+ void addInstrCPE(short opcode, int cpeNum) {\r
+ if (cpeNum < 256) {\r
+ addInstrU1(opcode, cpeNum);\r
+ }\r
+ else {\r
+ addInstrU2((short) (opcode+1), cpeNum);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This takes an instruction that can be wrapped in\r
+ * a wide for large variable #s and does so.\r
+ */\r
+ void addInstrWide(short opcode, int varNum) {\r
+ if (varNum < 256) {\r
+ addInstrU1(opcode, varNum);\r
+ }\r
+ else {\r
+ addInstr(VMOpcode.WIDE);\r
+ addInstrU2(opcode, varNum);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * For adding an instruction with 3 operands, a U2 and two U1's.\r
+ * So far, this is used by VMOpcode.INVOKEINTERFACE.\r
+ */\r
+ void addInstrU2U1U1(short opcode, int operand1, short operand2,\r
+ short operand3) {\r
+ try {\r
+ cout.putU1(opcode);\r
+ cout.putU2(operand1);\r
+ cout.putU1(operand2);\r
+ cout.putU1(operand3);\r
+ } catch (IOException ioe) {\r
+ limitHit(ioe);\r
+ }\r
+ if (SanityManager.DEBUG) { \r
+ if (OPCODE_ACTION[opcode][1] != 5)\r
+ SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" +\r
+ " writing 5 bytes - set as " + OPCODE_ACTION[opcode][1]); \r
+ }\r
+ }\r
+\r
+ /** Get the current program counter */\r
+ int getPC() {\r
+ return cout.size() + pcDelta;\r
+ }\r
+ \r
+ /**\r
+ * Return the complete instruction length for the\r
+ * passed in opcode. This will include the space for\r
+ * the opcode and its operand.\r
+ */\r
+ private static int instructionLength(short opcode)\r
+ {\r
+ int instructionLength = OPCODE_ACTION[opcode][1];\r
+ \r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (instructionLength < 0)\r
+ SanityManager.THROWASSERT("Opcode without instruction length " + opcode);\r
+ }\r
+ \r
+ return instructionLength;\r
+ }\r
+ \r
+ /**\r
+ * The delta between cout.size() and the pc.\r
+ * For an initial code chunk this is -8 (CODE_OFFSET)\r
+ * since 8 bytes are written.\r
+ * For a nested CodeChunk return by insertCodeSpace the delta\r
+ * corresponds to the original starting pc.\r
+ * @see #insertCodeSpace\r
+ */\r
+ private final int pcDelta;\r
+ \r
+ /**\r
+ * The class we are generating code for, used to indicate that\r
+ * some limit was hit during code generation.\r
+ */\r
+ final BCClass cb;\r
+\r
+ CodeChunk(BCClass cb) {\r
+ this.cb = cb;\r
+ cout = new ClassFormatOutput();\r
+ try {\r
+ cout.putU2(0); // max_stack, placeholder for now\r
+ cout.putU2(0); // max_locals, placeholder for now\r
+ cout.putU4(0); // code_length, placeholder 4 now\r
+ } catch (IOException ioe) {\r
+ limitHit(ioe);\r
+ }\r
+ pcDelta = - CodeChunk.CODE_OFFSET;\r
+ }\r
+ \r
+ /**\r
+ * Return a CodeChunk that has limited visibility into\r
+ * this CodeChunk. Used when a caller needs to insert instructions\r
+ * into an existing stream.\r
+ * @param pc\r
+ * @param byteCount\r
+ */\r
+ private CodeChunk(CodeChunk main, int pc, int byteCount)\r
+ {\r
+ this.cb = main.cb;\r
+ ArrayOutputStream aos =\r
+ new ArrayOutputStream(main.cout.getData());\r
+ \r
+ try {\r
+ aos.setPosition(CODE_OFFSET + pc);\r
+ aos.setLimit(byteCount);\r
+ } catch (IOException e) {\r
+ limitHit(e);\r
+ }\r
+ \r
+ cout = new ClassFormatOutput(aos);\r
+ pcDelta = pc;\r
+ }\r
+\r
+ private final ClassFormatOutput cout;\r
+\r
+ /**\r
+ * now that we have codeBytes, fix the lengths fields in it\r
+ * to reflect what was stored.\r
+ * Limits checked here are from these sections of the JVM spec.\r
+ * <UL>\r
+ * <LI> 4.7.3 The Code Attribute\r
+ * <LI> 4.10 Limitations of the Java Virtual Machine \r
+ * </UL>\r
+ */\r
+ private void fixLengths(BCMethod mb, int maxStack, int maxLocals, int codeLength) {\r
+\r
+ byte[] codeBytes = cout.getData();\r
+\r
+ // max_stack is in bytes 0-1\r
+ if (mb != null && maxStack > 65535)\r
+ cb.addLimitExceeded(mb, "max_stack", 65535, maxStack);\r
+ \r
+ codeBytes[0] = (byte)(maxStack >> 8 );\r
+ codeBytes[1] = (byte)(maxStack );\r
+\r
+ // max_locals is in bytes 2-3\r
+ if (mb != null && maxLocals > 65535)\r
+ cb.addLimitExceeded(mb, "max_locals", 65535, maxLocals);\r
+ codeBytes[2] = (byte)(maxLocals >> 8 );\r
+ codeBytes[3] = (byte)(maxLocals );\r
+\r
+ // code_length is in bytes 4-7\r
+ if (mb != null && codeLength > VMOpcode.MAX_CODE_LENGTH)\r
+ cb.addLimitExceeded(mb, "code_length",\r
+ VMOpcode.MAX_CODE_LENGTH, codeLength);\r
+ codeBytes[4] = (byte)(codeLength >> 24 );\r
+ codeBytes[5] = (byte)(codeLength >> 16 );\r
+ codeBytes[6] = (byte)(codeLength >> 8 );\r
+ codeBytes[7] = (byte)(codeLength ); \r
+ }\r
+\r
+ /**\r
+ * wrap up the entry and stuff it in the class,\r
+ * now that it holds all of the instructions and\r
+ * the exception table.\r
+ */\r
+ void complete(BCMethod mb, ClassHolder ch,\r
+ ClassMember method, int maxStack, int maxLocals) {\r
+\r
+ int codeLength = getPC();\r
+\r
+ ClassFormatOutput out = cout;\r
+ \r
+ try {\r
+\r
+ out.putU2(0); // exception_table_length\r
+\r
+ if (SanityManager.DEBUG) {\r
+ if (SanityManager.DEBUG_ON("ClassLineNumbers")) {\r
+ // Add a single attribute - LineNumberTable\r
+ // This add fake line numbers that are the pc offset in the method.\r
+ out.putU2(1); // attributes_count\r
+\r
+ int cpiUTF = ch.addUtf8("LineNumberTable");\r
+\r
+ out.putU2(cpiUTF);\r
+ out.putU4((codeLength * 4) + 2);\r
+ out.putU2(codeLength);\r
+ for (int i = 0; i < codeLength; i++) {\r
+ out.putU2(i);\r
+ out.putU2(i);\r
+ }\r
+ } else {\r
+ out.putU2(0); // attributes_count\r
+ }\r
+\r
+ } else {\r
+ out.putU2(0); // attributes_count\r
+ // attributes is empty, a 0-element array.\r
+ }\r
+ } catch (IOException ioe) {\r
+ limitHit(ioe);\r
+ }\r
+\r
+ fixLengths(mb, maxStack, maxLocals, codeLength);\r
+ method.addAttribute("Code", out);\r
+ \r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // Only validate if the class file format is valid.\r
+ // Ok code length and guaranteed no errors building the class.\r
+ if ((codeLength <= VMOpcode.MAX_CODE_LENGTH)\r
+ && (mb != null && mb.cb.limitMsg == null))\r
+ { \r
+ // Validate the alternate way to calculate the\r
+ // max stack agrees with the dynamic as the code\r
+ // is built way.\r
+ int walkedMaxStack = findMaxStack(ch, 0, codeLength);\r
+ if (walkedMaxStack != maxStack)\r
+ {\r
+ SanityManager.THROWASSERT("MAX STACK MISMATCH!! " +\r
+ maxStack + " <> " + walkedMaxStack);\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+ /**\r
+ * Return the opcode at the given pc.\r
+ */\r
+ short getOpcode(int pc)\r
+ {\r
+ return (short) (cout.getData()[CODE_OFFSET + pc] & 0xff);\r
+ }\r
+ \r
+ /**\r
+ * Get the unsigned short value for the opcode at the program\r
+ * counter pc.\r
+ */\r
+ private int getU2(int pc)\r
+ {\r
+ byte[] codeBytes = cout.getData();\r
+ \r
+ int u2p = CODE_OFFSET + pc + 1;\r
+ \r
+ return ((codeBytes[u2p] & 0xff) << 8) | (codeBytes[u2p+1] & 0xff);\r
+ }\r
+\r
+ /**\r
+ * Get the unsigned 32 bit value for the opcode at the program\r
+ * counter pc.\r
+ */\r
+ private int getU4(int pc)\r
+ {\r
+ byte[] codeBytes = cout.getData();\r
+ \r
+ int u4p = CODE_OFFSET + pc + 1;\r
+ \r
+ return (((codeBytes[u4p] & 0xff) << 24) |\r
+ ((codeBytes[u4p+1] & 0xff) << 16) |\r
+ ((codeBytes[u4p+2] & 0xff) << 8) |\r
+ ((codeBytes[u4p+3] & 0xff)));\r
+ } \r
+ /**\r
+ * Insert room for byteCount bytes after the instruction at pc\r
+ * and prepare to replace the instruction at pc. The instruction\r
+ * at pc is not modified by this call, space is allocated after it.\r
+ * The newly inserted space will be filled with NOP instructions.\r
+ * \r
+ * Returns a CodeChunk positioned at pc and available to write\r
+ * instructions upto (byteCode + length(existing instruction at pc) bytes.\r
+ * \r
+ * This chunk is left correctly positioned at the end of the code\r
+ * stream, ready to accept more code. Its pc will have increased by\r
+ * additionalBytes.\r
+ * \r
+ * It is the responsibility of the caller to patch up any\r
+ * branches or gotos.\r
+ * \r
+ * @param pc\r
+ * @param additionalBytes\r
+ */\r
+ CodeChunk insertCodeSpace(int pc, int additionalBytes)\r
+ {\r
+ short existingOpcode = getOpcode(pc);\r
+\r
+ int lengthOfExistingInstruction\r
+ = instructionLength(existingOpcode);\r
+ \r
+ \r
+ if (additionalBytes > 0)\r
+ {\r
+ // Size of the current code after this pc.\r
+ int sizeToMove = (getPC() - pc) - lengthOfExistingInstruction;\r
+\r
+ // Increase the code by the number of bytes to be\r
+ // inserted. These NOPs will be overwritten by the\r
+ // moved code by the System.arraycopy below.\r
+ // It's assumed that the number of inserted bytes\r
+ // is small, one or two instructions worth, so it\r
+ // won't be a performance issue.\r
+ for (int i = 0; i < additionalBytes; i++)\r
+ addInstr(VMOpcode.NOP);\r
+ \r
+ // Must get codeBytes here as the array might have re-sized.\r
+ byte[] codeBytes = cout.getData();\r
+ \r
+ int byteOffset = CODE_OFFSET + pc + lengthOfExistingInstruction;\r
+ \r
+ \r
+ // Shift the existing code stream down\r
+ System.arraycopy(\r
+ codeBytes, byteOffset,\r
+ codeBytes, byteOffset + additionalBytes,\r
+ sizeToMove);\r
+ \r
+ // Place NOPs in the space just freed by the move.\r
+ // This is not required, it ias assumed the caller\r
+ // will overwrite all the bytes they requested, but\r
+ // to be safe fill in with NOPs rather than leaving code\r
+ // that could break the verifier.\r
+ Arrays.fill(codeBytes, byteOffset, byteOffset + additionalBytes,\r
+ (byte) VMOpcode.NOP);\r
+ }\r
+ \r
+ // The caller must overwrite the original instruction\r
+ // at pc, thus increase the range of the limit stream\r
+ // created to include those bytes.\r
+ additionalBytes += lengthOfExistingInstruction;\r
+ \r
+ // Now the caller needs to fill in the instructions\r
+ // that make up the modified byteCount bytes of bytecode stream.\r
+ // Return a CodeChunk that can be used for this and\r
+ // is limited to only those bytes.\r
+ // The pc of the original code chunk is left unchanged.\r
+ \r
+ return new CodeChunk(this, pc, additionalBytes);\r
+ \r
+ }\r
+ \r
+ /*\r
+ * * Methods related to splitting the byte code chunks into sections that\r
+ * fit in the JVM's limits for a single method.\r
+ */\r
+\r
+ /**\r
+ * For a block of byte code starting at program counter pc for codeLength\r
+ * bytes return the maximum stack value, assuming a initial stack depth of\r
+ * zero.\r
+ */\r
+ private int findMaxStack(ClassHolder ch, int pc, int codeLength) {\r
+\r
+ final int endPc = pc + codeLength;\r
+ int stack = 0;\r
+ int maxStack = 0;\r
+\r
+ for (; pc < endPc;) {\r
+\r
+ short opcode = getOpcode(pc);\r
+ \r
+ \r
+ int stackDelta = stackWordDelta(ch, pc, opcode);\r
+\r
+ stack += stackDelta;\r
+ if (stack > maxStack)\r
+ maxStack = stack;\r
+ \r
+ int[] cond_pcs = findConditionalPCs(pc, opcode);\r
+ if (cond_pcs != null) { \r
+ // an else block exists.\r
+ if (cond_pcs[3] != -1) {\r
+ int blockMaxStack = findMaxStack(ch, cond_pcs[1],\r
+ cond_pcs[2]);\r
+ if ((stack + blockMaxStack) > maxStack)\r
+ maxStack = stack + blockMaxStack;\r
+\r
+ pc = cond_pcs[3];\r
+ continue;\r
+ }\r
+ }\r
+\r
+ pc += instructionLength(opcode);\r
+ }\r
+\r
+ return maxStack;\r
+ }\r
+\r
+ /**\r
+ * Return the number of stack words pushed (positive) or popped (negative)\r
+ * by this instruction.\r
+ */\r
+ private int stackWordDelta(ClassHolder ch, int pc, short opcode) {\r
+ if (SanityManager.DEBUG) {\r
+ // this validates the OPCODE_ACTION entry\r
+ instructionLength(opcode);\r
+ }\r
+\r
+ int stackDelta = OPCODE_ACTION[opcode][0];\r
+ if (stackDelta == VARIABLE_STACK) {\r
+ stackDelta = getVariableStackDelta(ch, pc, opcode);\r
+ }\r
+\r
+ return stackDelta;\r
+ }\r
+\r
+ /**\r
+ * Get the type descriptor in the virtual machine format for the type\r
+ * defined by the constant pool index for the instruction at pc.\r
+ */\r
+ private String getTypeDescriptor(ClassHolder ch, int pc) {\r
+ int cpi = getU2(pc);\r
+\r
+\r
+ // Field reference or method reference\r
+ CONSTANT_Index_info cii = (CONSTANT_Index_info) ch.getEntry(cpi);\r
+\r
+ // NameAndType reference\r
+ int nameAndType = cii.getI2();\r
+ cii = (CONSTANT_Index_info) ch.getEntry(nameAndType);\r
+\r
+ // UTF8 descriptor\r
+ int descriptor = cii.getI2();\r
+ CONSTANT_Utf8_info type = (CONSTANT_Utf8_info) ch.getEntry(descriptor);\r
+\r
+ String vmDescriptor = type.toString();\r
+\r
+ return vmDescriptor;\r
+ }\r
+\r
+ /**\r
+ * Get the word count for a type descriptor in the format of the virual\r
+ * machine. For a method this returns the the word count for the return\r
+ * type.\r
+ */\r
+ private static int getDescriptorWordCount(String vmDescriptor) {\r
+ \r
+ int width;\r
+ if (VMDescriptor.DOUBLE.equals(vmDescriptor))\r
+ width = 2;\r
+ else if (VMDescriptor.LONG.equals(vmDescriptor))\r
+ width = 2;\r
+ else if (vmDescriptor.charAt(0) == VMDescriptor.C_METHOD) {\r
+ switch (vmDescriptor.charAt(vmDescriptor.length() - 1)) {\r
+ case VMDescriptor.C_DOUBLE:\r
+ case VMDescriptor.C_LONG:\r
+ width = 2;\r
+ break;\r
+ case VMDescriptor.C_VOID:\r
+ width = 0;\r
+ break;\r
+ default:\r
+ width = 1;\r
+ break;\r
+ }\r
+ } else\r
+ width = 1;\r
+\r
+ return width;\r
+ }\r
+\r
+ /**\r
+ * Get the number of words pushed (positive) or popped (negative) by this\r
+ * instruction. The instruction is a get/put field or a method call, thus\r
+ * the size of the words is defined by the field or method being access.\r
+ */\r
+ private int getVariableStackDelta(ClassHolder ch, int pc, int opcode) {\r
+ String vmDescriptor = getTypeDescriptor(ch, pc);\r
+ int width = CodeChunk.getDescriptorWordCount(vmDescriptor);\r
+\r
+ int stackDelta = 0;\r
+ // Stack delta depends on context.\r
+ switch (opcode) {\r
+ case VMOpcode.GETSTATIC:\r
+ stackDelta = width;\r
+ break;\r
+\r
+ case VMOpcode.GETFIELD:\r
+ stackDelta = width - 1; // one for popped object ref\r
+ break;\r
+\r
+ case VMOpcode.PUTSTATIC:\r
+ stackDelta = -width;\r
+ break;\r
+\r
+ case VMOpcode.PUTFIELD:\r
+ stackDelta = -width - 1; // one for pop object ref\r
+ break;\r
+\r
+ case VMOpcode.INVOKEVIRTUAL:\r
+ case VMOpcode.INVOKESPECIAL:\r
+ stackDelta = -1; // for instance reference for method call.\r
+ case VMOpcode.INVOKESTATIC:\r
+ stackDelta += (width - CodeChunk.parameterWordCount(vmDescriptor));\r
+ // System.out.println("invoked non-interface " + stackDelta);\r
+ break;\r
+\r
+ case VMOpcode.INVOKEINTERFACE:\r
+ // third byte contains the number of arguments to be popped\r
+ stackDelta = width - getOpcode(pc + 3);\r
+ // System.out.println("invoked interface " + stackDelta);\r
+ break;\r
+ default:\r
+ System.out.println("WHO IS THIS ");\r
+ break;\r
+\r
+ }\r
+\r
+ return stackDelta;\r
+ }\r
+\r
+ /**\r
+ * Calculate the number of stack words in the arguments pushed for this\r
+ * method descriptor.\r
+ */\r
+ private static int parameterWordCount(String methodDescriptor) {\r
+ int wordCount = 0;\r
+ for (int i = 1;; i++) {\r
+ switch (methodDescriptor.charAt(i)) {\r
+ case VMDescriptor.C_ENDMETHOD:\r
+ return wordCount;\r
+ case VMDescriptor.C_DOUBLE:\r
+ case VMDescriptor.C_LONG:\r
+ wordCount += 2;\r
+ break;\r
+ case VMDescriptor.C_ARRAY:\r
+ // skip while there are array symbols.\r
+ do {\r
+ i++;\r
+ } while (methodDescriptor.charAt(i) == VMDescriptor.C_ARRAY);\r
+ if (methodDescriptor.charAt(i) != VMDescriptor.C_CLASS) {\r
+ // an array is a reference, even an array of doubles.\r
+ wordCount += 1;\r
+ break;\r
+ }\r
+\r
+ // fall through to skip the Lclassname; after the array.\r
+\r
+ case VMDescriptor.C_CLASS:\r
+ // skip until ;\r
+ do {\r
+ i++;\r
+ } while (methodDescriptor.charAt(i) != VMDescriptor.C_ENDCLASS);\r
+ wordCount += 1;\r
+ break;\r
+ default:\r
+ wordCount += 1;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Find the limits of a conditional block starting at the instruction with\r
+ * the given opcode at the program counter pc.\r
+ * <P>\r
+ * Returns a six element integer array of program counters and lengths.\r
+ * <code. [0] - program counter of the IF opcode (passed in as pc) [1] -\r
+ * program counter of the start of the then block [2] - length of the then\r
+ * block [3] - program counter of the else block, -1 if no else block\r
+ * exists. [4] - length of of the else block, -1 if no else block exists.\r
+ * [5] - program counter of the common end point. </code>\r
+ * \r
+ * Looks for and handles conditionals that are written by the Conditional\r
+ * class.\r
+ * \r
+ * @return Null if the opcode is not the start of a conditional otherwise\r
+ * the array of values.\r
+ */\r
+ private int[] findConditionalPCs(int pc, short opcode) {\r
+ switch (opcode) {\r
+ default:\r
+ return null;\r
+ case VMOpcode.IFNONNULL:\r
+ case VMOpcode.IFNULL:\r
+ case VMOpcode.IFEQ:\r
+ case VMOpcode.IFNE:\r
+ break;\r
+ }\r
+\r
+ int then_pc;\r
+ int else_pc;\r
+ int if_off = getU2(pc);\r
+\r
+ if ((if_off == 8)\r
+ && (getOpcode(pc + VMOpcode.IF_INS_LENGTH) == VMOpcode.GOTO_W)) {\r
+ // 32 bit branch\r
+ then_pc = pc + VMOpcode.IF_INS_LENGTH + VMOpcode.GOTO_W_INS_LENGTH;\r
+\r
+ // Get else PC from the 32 bit offset within the GOTO_W\r
+ // instruction remembering to add it to the pc of that\r
+ // instruction, not the original IF.\r
+ else_pc = pc + VMOpcode.IF_INS_LENGTH\r
+ + getU4(pc + VMOpcode.IF_INS_LENGTH);\r
+\r
+ } else {\r
+ then_pc = pc + VMOpcode.IF_INS_LENGTH;\r
+ else_pc = pc + if_off;\r
+ }\r
+\r
+ // Need to look for the goto or goto_w at the\r
+ // end of the then block. There might not be\r
+ // one for the case when there is no else block.\r
+ // In that case the then block will just run into\r
+ // what we currently think the else pc.\r
+\r
+ int end_pc = -1;\r
+ for (int tpc = then_pc; tpc < else_pc;) {\r
+ short opc = getOpcode(tpc);\r
+\r
+ // need to handle conditionals\r
+ int[] innerCond = findConditionalPCs(tpc, opc);\r
+ if (innerCond != null) {\r
+ // skip to the end of this conditional\r
+ tpc = innerCond[5]; // end_pc\r
+ continue;\r
+ }\r
+\r
+ if (opc == VMOpcode.GOTO) {\r
+ // not at the end of the then block\r
+ // so not our goto. Shouldn't see this\r
+ // with the current code due to the\r
+ // skipping of the conditional above.\r
+ // But safe defensive programming.\r
+ if (tpc != (else_pc - VMOpcode.GOTO_INS_LENGTH))\r
+ continue;\r
+\r
+ end_pc = tpc + getU2(tpc);\r
+ break;\r
+ } else if (opc == VMOpcode.GOTO_W) {\r
+ // not at the end of the then block\r
+ // so not our goto. SHouldn't see this\r
+ // with the current code due to the\r
+ // skipping of the conditional above.\r
+ // But safe defensive programming.\r
+ if (tpc != (else_pc - VMOpcode.GOTO_W_INS_LENGTH))\r
+ continue;\r
+\r
+ end_pc = tpc + getU4(tpc);\r
+ break;\r
+\r
+ }\r
+ tpc += instructionLength(opc);\r
+ }\r
+\r
+ int else_len;\r
+ int then_len;\r
+ if (end_pc == -1) {\r
+ // no else block;\r
+ end_pc = else_pc;\r
+ else_pc = -1;\r
+\r
+ then_len = end_pc - then_pc;\r
+ else_len = -1;\r
+ } else {\r
+ then_len = else_pc - then_pc;\r
+ else_len = end_pc - else_pc;\r
+ }\r
+\r
+ int[] ret = new int[6];\r
+\r
+ ret[0] = pc;\r
+ ret[1] = then_pc;\r
+ ret[2] = then_len;\r
+ ret[3] = else_pc;\r
+ ret[4] = else_len;\r
+ ret[5] = end_pc;\r
+\r
+ return ret;\r
+ }\r
+ \r
+ /**\r
+ * Attempt to split the current method by pushing a chunk of\r
+ * its code into a sub-method. The starting point of the split\r
+ * (split_pc) must correspond to a stack depth of zero. It is the\r
+ * reponsibility of the caller to ensure this.\r
+ * Split is only made if there exists a chunk of code starting at\r
+ * pc=split_pc, whose stack depth upon termination is zero.\r
+ * The method will try to split a code section greater than\r
+ * optimalMinLength but may split earlier if no such block exists.\r
+ * <P>\r
+ * The method is aimed at splitting methods that contain\r
+ * many independent statements.\r
+ * <P>\r
+ * If a split is possible this method will perform the split and\r
+ * create a void sub method, and move the code into the sub-method\r
+ * and setup this method to call the sub-method before continuing.\r
+ * This method's max stack and current pc will be correctly set\r
+ * as though the method had just been created.\r
+ * \r
+ * @param mb Method for this chunk.\r
+ * @param ch Class definition\r
+ * @param optimalMinLength minimum length required for split\r
+ */\r
+ final int splitZeroStack(BCMethod mb, ClassHolder ch, final int split_pc,\r
+ final int optimalMinLength) {\r
+ \r
+ int splitMinLength = splitMinLength(mb);\r
+ \r
+ int stack = 0;\r
+\r
+ // maximum possible split seen that is less than\r
+ // the minimum.\r
+ int possibleSplitLength = -1;\r
+\r
+ // do not split until at least this point (inclusive)\r
+ // used to ensure no split occurs in the middle of\r
+ // a conditional.\r
+ int outerConditionalEnd_pc = -1;\r
+\r
+ int end_pc = getPC(); // pc will be positioned at the end.\r
+ for (int pc = split_pc; pc < end_pc;) {\r
+\r
+ short opcode = getOpcode(pc);\r
+\r
+ int stackDelta = stackWordDelta(ch, pc, opcode);\r
+\r
+ stack += stackDelta;\r
+\r
+ // Cannot split a conditional but need to calculate\r
+ // the stack depth at the end of the conditional.\r
+ // Each path through the conditional will have the\r
+ // same stack depth.\r
+ int[] cond_pcs = findConditionalPCs(pc, opcode);\r
+ if (cond_pcs != null) {\r
+ // an else block exists, skip the then block.\r
+ if (cond_pcs[3] != -1) {\r
+ pc = cond_pcs[3];\r
+ continue;\r
+ }\r
+\r
+ if (SanityManager.DEBUG) {\r
+ if (outerConditionalEnd_pc != -1) {\r
+ if (cond_pcs[5] >= outerConditionalEnd_pc)\r
+ SanityManager.THROWASSERT("NESTED CONDITIONALS!");\r
+ }\r
+ }\r
+\r
+ if (outerConditionalEnd_pc == -1) {\r
+ outerConditionalEnd_pc = cond_pcs[5];\r
+ }\r
+ }\r
+\r
+ pc += instructionLength(opcode);\r
+\r
+ // Don't split in the middle of a conditional\r
+ if (outerConditionalEnd_pc != -1) {\r
+ if (pc > outerConditionalEnd_pc) {\r
+ // passed the outermost conditional\r
+ outerConditionalEnd_pc = -1;\r
+ }\r
+ continue;\r
+ }\r
+\r
+ if (stack != 0)\r
+ continue;\r
+\r
+ int splitLength = pc - split_pc;\r
+\r
+ if (splitLength < optimalMinLength) {\r
+ // record we do have a possible split.\r
+ possibleSplitLength = splitLength;\r
+ continue;\r
+ }\r
+\r
+ // no point splitting to a method bigger\r
+ // than the VM can handle. Save one for\r
+ // return instruction.\r
+ if (splitLength > BCMethod.CODE_SPLIT_LENGTH - 1) {\r
+ splitLength = -1;\r
+ }\r
+ else if (CodeChunk.isReturn(opcode))\r
+ {\r
+ // Don't handle a return in the middle of\r
+ // an instruction stream. Don't think this\r
+ // is generated, but be safe. \r
+ splitLength = -1;\r
+ }\r
+ \r
+ // if splitLenth was set to -1 above then there\r
+ // is no possible split at this instruction.\r
+ if (splitLength == -1)\r
+ {\r
+ // no earlier split at all\r
+ if (possibleSplitLength == -1)\r
+ return -1;\r
+ \r
+ // Decide if the earlier possible split is\r
+ // worth it.\r
+ if (possibleSplitLength <= splitMinLength)\r
+ return -1;\r
+\r
+ // OK go with the earlier split\r
+ splitLength = possibleSplitLength;\r
+\r
+ }\r
+\r
+ // Yes, we can split this big method into a smaller method!!\r
+\r
+ BCMethod subMethod = startSubMethod(mb, "void", split_pc,\r
+ splitLength);\r
+\r
+ return splitCodeIntoSubMethod(mb, ch, subMethod,\r
+ split_pc, splitLength);\r
+ }\r
+ return -1;\r
+ }\r
+\r
+ /**\r
+ * Start a sub method that we will split the portion of our current code to,\r
+ * starting from start_pc and including codeLength bytes of code.\r
+ * \r
+ * Return a BCMethod obtained from BCMethod.getNewSubMethod with the passed\r
+ * in return type and same parameters as mb if the code block to be moved\r
+ * uses parameters.\r
+ */\r
+ private BCMethod startSubMethod(BCMethod mb, String returnType,\r
+ int split_pc, int blockLength) {\r
+\r
+ boolean needParameters = usesParameters(mb, split_pc, blockLength);\r
+\r
+ return mb.getNewSubMethod(returnType, needParameters);\r
+ }\r
+\r
+ /**\r
+ * Does a section of code use parameters.\r
+ * Any load, exception ALOAD_0 in an instance method, is\r
+ * seen as using parameters, as this complete byte code\r
+ * implementation does not use local variables.\r
+ * \r
+ */\r
+ private boolean usesParameters(BCMethod mb, int pc, int codeLength) {\r
+\r
+ // does the method even have parameters?\r
+ if (mb.parameters == null)\r
+ return false;\r
+\r
+ boolean isStatic = (mb.myEntry.getModifier() & Modifier.STATIC) != 0;\r
+\r
+ int endPc = pc + codeLength;\r
+\r
+ for (; pc < endPc;) {\r
+ short opcode = getOpcode(pc);\r
+ switch (opcode) {\r
+ case VMOpcode.ILOAD_0:\r
+ case VMOpcode.LLOAD_0:\r
+ case VMOpcode.FLOAD_0:\r
+ case VMOpcode.DLOAD_0:\r
+ return true;\r
+\r
+ case VMOpcode.ALOAD_0:\r
+ if (isStatic)\r
+ return true;\r
+ break;\r
+\r
+ case VMOpcode.ILOAD_1:\r
+ case VMOpcode.LLOAD_1:\r
+ case VMOpcode.FLOAD_1:\r
+ case VMOpcode.DLOAD_1:\r
+ case VMOpcode.ALOAD_1:\r
+ return true;\r
+\r
+ case VMOpcode.ILOAD_2:\r
+ case VMOpcode.LLOAD_2:\r
+ case VMOpcode.FLOAD_2:\r
+ case VMOpcode.DLOAD_2:\r
+ case VMOpcode.ALOAD_2:\r
+ return true;\r
+\r
+ case VMOpcode.ILOAD_3:\r
+ case VMOpcode.LLOAD_3:\r
+ case VMOpcode.FLOAD_3:\r
+ case VMOpcode.DLOAD_3:\r
+ case VMOpcode.ALOAD_3:\r
+ return true;\r
+\r
+ case VMOpcode.ILOAD:\r
+ case VMOpcode.LLOAD:\r
+ case VMOpcode.FLOAD:\r
+ case VMOpcode.DLOAD:\r
+ case VMOpcode.ALOAD:\r
+ return true;\r
+ default:\r
+ break;\r
+\r
+ }\r
+ pc += instructionLength(opcode);\r
+ }\r
+ return false;\r
+ }\r
+ /**\r
+ * Split a block of code from this method into a sub-method\r
+ * and call it.\r
+ * \r
+ * Returns the pc of this method just after the call\r
+ * to the sub-method.\r
+ \r
+ * @param mb My method\r
+ * @param ch My class\r
+ * @param subMethod Sub-method code was pushed into\r
+ * @param split_pc Program counter the split started at\r
+ * @param splitLength Length of code split\r
+ */\r
+ private int splitCodeIntoSubMethod(BCMethod mb, ClassHolder ch,\r
+ BCMethod subMethod, final int split_pc, final int splitLength) {\r
+ CodeChunk subChunk = subMethod.myCode;\r
+\r
+ byte[] codeBytes = cout.getData();\r
+\r
+ // the code to be moved into the sub method\r
+ // as a block. This will correctly increase the\r
+ // program counter.\r
+ try {\r
+ subChunk.cout.write(codeBytes, CODE_OFFSET + split_pc, splitLength);\r
+ } catch (IOException ioe) {\r
+ limitHit(ioe);\r
+ }\r
+\r
+ // Just cause the sub-method to return,\r
+ // fix up its maxStack and then complete it.\r
+ if (subMethod.myReturnType.equals("void"))\r
+ subChunk.addInstr(VMOpcode.RETURN);\r
+ else\r
+ subChunk.addInstr(VMOpcode.ARETURN);\r
+ \r
+ // Finding the max stack requires the class format to\r
+ // still be valid. If we have blown the number of constant\r
+ // pool entries then we can no longer guarantee that indexes\r
+ // into the constant pool in the code stream are valid.\r
+ if (cb.limitMsg != null)\r
+ return -1;\r
+ \r
+ subMethod.maxStack = subChunk.findMaxStack(ch, 0, subChunk.getPC());\r
+ subMethod.complete();\r
+\r
+ return removePushedCode(mb, ch, subMethod, split_pc, splitLength);\r
+ }\r
+\r
+ /**\r
+ * Remove a block of code from this method that was pushed into a sub-method\r
+ * and call the sub-method.\r
+ * \r
+ * Returns the pc of this method just after the call to the sub-method.\r
+ * \r
+ * @param mb\r
+ * My method\r
+ * @param ch\r
+ * My class\r
+ * @param subMethod\r
+ * Sub-method code was pushed into\r
+ * @param split_pc\r
+ * Program counter the split started at\r
+ * @param splitLength\r
+ * Length of code split\r
+ */\r
+ private int removePushedCode(BCMethod mb, ClassHolder ch,\r
+ BCMethod subMethod, final int split_pc, final int splitLength) {\r
+ // now need to fix up this method, create\r
+ // a new CodeChunk just to be clearer than\r
+ // trying to modify this chunk directly.\r
+ \r
+ // total length of the code for this method before split\r
+ final int codeLength = getPC();\r
+ \r
+ CodeChunk replaceChunk = new CodeChunk(mb.cb);\r
+ mb.myCode = replaceChunk;\r
+ mb.maxStack = 0;\r
+\r
+ byte[] codeBytes = cout.getData();\r
+ \r
+ // write any existing code before the split point\r
+ // into the replacement chunk.\r
+ if (split_pc != 0) {\r
+ try {\r
+ replaceChunk.cout.write(codeBytes, CODE_OFFSET, split_pc);\r
+ } catch (IOException ioe) {\r
+ limitHit(ioe);\r
+ }\r
+ }\r
+\r
+ // Call the sub method, will write into replaceChunk.\r
+ mb.callSubMethod(subMethod);\r
+\r
+ int postSplit_pc = replaceChunk.getPC();\r
+\r
+ // Write the code remaining in this method into the replacement chunk\r
+\r
+ int remainingCodePC = split_pc + splitLength;\r
+ int remainingCodeLength = codeLength - splitLength - split_pc;\r
+ \r
+ try {\r
+ replaceChunk.cout.write(codeBytes, CODE_OFFSET + remainingCodePC,\r
+ remainingCodeLength);\r
+ } catch (IOException ioe) {\r
+ limitHit(ioe);\r
+ }\r
+\r
+ // Finding the max stack requires the class format to\r
+ // still be valid. If we have blown the number of constant\r
+ // pool entries then we can no longer guarantee that indexes\r
+ // into the constant pool in the code stream are valid.\r
+ if (cb.limitMsg != null)\r
+ return -1;\r
+ \r
+ mb.maxStack = replaceChunk.findMaxStack(ch, 0, replaceChunk.getPC());\r
+\r
+ return postSplit_pc;\r
+ }\r
+ \r
+ /**\r
+ * Split an expression out of a large method into its own\r
+ * sub-method.\r
+ * <P>\r
+ * Method call expressions are of the form:\r
+ * <UL>\r
+ * <LI> expr.method(args) -- instance method call\r
+ * <LI> method(args) -- static method call\r
+ * </UL>\r
+ * Two special cases of instance method calls will be handled\r
+ * by the first incarnation of splitExpressionOut. \r
+ * three categories:\r
+ * <UL>\r
+ * <LI>this.method(args)\r
+ * <LI>this.getter().method(args)\r
+ * </UL>\r
+ * These calls are choosen as they are easier sub-cases\r
+ * and map to the code generated for SQL statements.\r
+ * Future coders can expand the method to cover more cases.\r
+ * <P>\r
+ * This method will split out such expressions in sub-methods\r
+ * and replace the original code with a call to that submethod.\r
+ * <UL>\r
+ * <LI>this.method(args) ->> this.sub1([parameters])\r
+ * <LI>this.getter().method(args) ->> this.sub1([parameters])\r
+ * </UL>\r
+ * The assumption is of course that the call to the sub-method\r
+ * is much smaller than the code it replaces.\r
+ * <P>\r
+ * Looking at the byte code for such calls they would look like\r
+ * (for an example three argument method):\r
+ * <code>\r
+ * this arg1 arg2 arg3 INVOKE // this.method(args)\r
+ * this INVOKE arg1 arg2 arg3 INVOKE // this.getter().metod(args)\r
+ * </code>\r
+ * The bytecode for the arguments can be arbitary long and\r
+ * consist of expressions, typical Derby code for generated\r
+ * queries is deeply nested method calls.\r
+ * <BR>\r
+ * If none of the arguments requred the parameters passed into\r
+ * the method, then in both cases the replacement bytecode\r
+ * would look like:\r
+ * <code>\r
+ * this.sub1();\r
+ * </code>\r
+ * Parameter handling is just as in the method splitZeroStack().\r
+ * <P>\r
+ * Because the VM is a stack machine the original byte code\r
+ * sequences are self contained. The stack at the start of\r
+ * is sequence is N and at the end (after the method call) will\r
+ * be:\r
+ * <UL>\r
+ * <LI> N - void method\r
+ * <LI> N + 1 - method returning a single word\r
+ * <LI> N + 2 - method returning a double word (java long or double)\r
+ * </UL>\r
+ * This code will handle the N+1 where the word is a reference,\r
+ * the typical case for generated code.\r
+ * <BR>\r
+ * The code is self contained because in general the byte code\r
+ * for the arguments will push and pop values but never drop\r
+ * below the stack value at the start of the byte code sequence.\r
+ * E.g. in the examples the stack before the first arg will be\r
+ * N+1 (the objectref for the method call) and at the end of the\r
+ * byte code for arg1 will be N+2 or N+3 depending on if arg1 is\r
+ * a single or double word argument. During the execution of\r
+ * the byte code the stack may have had many arguments pushed\r
+ * and popped, but will never have dropped below N+1. Thus the\r
+ * code for arg1 is independent of the stack's previous values\r
+ * and is self contained. This self-containment then extends to\r
+ * all the arguements, the method call itself and pushing the\r
+ * objectref for the method call, thus the complete\r
+ * sequence is self-contained.\r
+ * <BR>\r
+ * The self-containment breaks in a few cases, take the simple\r
+ * method call this.method(3), the byte code for this could be:\r
+ * <code>\r
+ * push3 this swap invoke\r
+ * </code>\r
+ * In this case the byte code for arg1 (swap) is not self-contained\r
+ * and relies on earlier stack values.\r
+ * <P>\r
+ * How to identify "self-contained blocks of code".\r
+ * <BR>\r
+ * We walk through the byte code and maintain a history of\r
+ * the program counter that indicates the start of the\r
+ * independent sequence each stack word depends on.\r
+ * Thus for a ALOAD_0 instruction which pushes 'this' the\r
+ * dependent pc is that of the this. If a DUP instruction followed\r
+ * then the top-word is now dependent on the previous word (this)\r
+ * and thus the dependence of it is equal to the dependence of\r
+ * the previous word. This information is kept in earliestIndepPC\r
+ * array as we process the instruction stream.\r
+ * <BR>\r
+ * When a INVOKE instruction is seen for an instance method\r
+ * that returns a single or double word, the dependence of\r
+ * the returned value is the dependence of the word in the\r
+ * stack that is the objectref for the call. This complete\r
+ * sequence from the pc the objectref depended on to the\r
+ * INVOKE instruction is then a self contained sequence\r
+ * and can be split into a sub-method.\r
+\r
+ * <BR>\r
+ * If the block is self-contained then it can be split, following\r
+ * similar logic to splitZeroStack().\r
+ * \r
+ * <P>\r
+ * WORK IN PROGRESS - Incremental development\r
+ * <BR>\r
+ * Currently walks the method maintaining the\r
+ * earliestIndepPC array and identifies potential blocks\r
+ * to splt, performs splits as required.\r
+ * Called by BCMethod but commented out in submitted code.\r
+ * Tested with local changes from calls in BCMethod.\r
+ * Splits generally work, though largeCodeGen shows\r
+ * a problem that will be fixed before the code in\r
+ * enabled for real.\r
+ * \r
+ */\r
+\r
+ final int splitExpressionOut(final BCMethod mb, final ClassHolder ch,\r
+ final int optimalMinLength,\r
+ final int maxStack)\r
+ {\r
+ // Save the best block we have seen for splitting out.\r
+ int bestSplitPC = -1;\r
+ int bestSplitBlockLength = -1;\r
+ String bestSplitRT = null; \r
+ \r
+ int splitMinLength = splitMinLength(mb);\r
+ \r
+ // Program counter of the earliest instruction\r
+ // that the word in the current active stack\r
+ // at the given depth depends on.\r
+ //\r
+ // Some examples, N is the stack depth *after*\r
+ // the instruction.\r
+ // E.g. \r
+ // ALOAD_0 - pushes this, is an instruction that\r
+ // pushes an independent value, so the current\r
+ // stack word depends on the pc of current instruction.\r
+ // earliestIndepPC[N] = pc (that pushed the value).\r
+ //\r
+ // DUP - duplicates the top word, so the duplicated\r
+ // top word will depend on the same pc as the word\r
+ // it was duplicated from.\r
+ // I.e. earliestIndepPC[N]\r
+ // = earliestIndepPC[N-1];\r
+ //\r
+ // instance method call returning single word value.\r
+ // The top word will depend on the same pc as the\r
+ // objectref for the method call, which was at the\r
+ // same depth in this case.\r
+ // earliestIndepPC[N] unchanged\r
+ //\r
+ // at any time earliestIndepPC is only valid\r
+ // from 1 to N where N is the depth of the stack.\r
+ int[] earliestIndepPC = new int[maxStack+1];\r
+ \r
+ int stack = 0;\r
+ \r
+ //TODO: this conditional handling is copied from\r
+ //the splitZeroStack code, need to check to see\r
+ // how it fits with the expression logic.\r
+ // do not split until at least this point (inclusive)\r
+ // used to ensure no split occurs in the middle of\r
+ // a conditional.\r
+ int outerConditionalEnd_pc = -1; \r
+\r
+ int end_pc = getPC();\r
+ \r
+ for (int pc = 0; pc < end_pc;) {\r
+\r
+ short opcode = getOpcode(pc);\r
+ \r
+ int stackDelta = stackWordDelta(ch, pc, opcode);\r
+ \r
+ stack += stackDelta;\r
+ \r
+ // Cannot split a conditional but need to calculate\r
+ // the stack depth at the end of the conditional.\r
+ // Each path through the conditional will have the\r
+ // same stack depth.\r
+ int[] cond_pcs = findConditionalPCs(pc, opcode);\r
+ if (cond_pcs != null) {\r
+ \r
+ // TODO: This conditional handling was copied\r
+ // from splitZeroStack, haven't looked in detail\r
+ // to see how a conditional should be handled\r
+ // with an expression split. So for the time\r
+ // being just bail.\r
+ if (true)\r
+ return -1;\r
+\r
+ // an else block exists, skip the then block.\r
+ if (cond_pcs[3] != -1) {\r
+ pc = cond_pcs[3];\r
+ continue;\r
+ }\r
+ \r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (outerConditionalEnd_pc != -1)\r
+ {\r
+ if (cond_pcs[5] >= outerConditionalEnd_pc)\r
+ SanityManager.THROWASSERT("NESTED CONDITIONALS!");\r
+ }\r
+ }\r
+\r
+ if (outerConditionalEnd_pc == -1)\r
+ {\r
+ outerConditionalEnd_pc = cond_pcs[5];\r
+ }\r
+ }\r
+ \r
+ pc += instructionLength(opcode);\r
+ \r
+ // Don't split in the middle of a conditional\r
+ if (outerConditionalEnd_pc != -1) {\r
+ if (pc > outerConditionalEnd_pc) {\r
+ // passed the outermost conditional\r
+ outerConditionalEnd_pc = -1;\r
+ }\r
+ continue;\r
+ }\r
+ \r
+ int opcode_pc = pc - instructionLength(opcode);\r
+ switch (opcode)\r
+ {\r
+ // Any instruction we don't have any information\r
+ // on, we simply clear all evidence of independent\r
+ // starting points, and start again.\r
+ default:\r
+ Arrays.fill(earliestIndepPC,\r
+ 0, stack + 1, -1);\r
+ break;\r
+ \r
+ // Independent instructions do not change the stack depth\r
+ // and the independence of the top word picks up\r
+ // the independence of the previous word at the same\r
+ // position. Ie. no change!\r
+ case VMOpcode.ARRAYLENGTH:\r
+ case VMOpcode.NOP:\r
+ case VMOpcode.CHECKCAST:\r
+ case VMOpcode.D2L:\r
+ case VMOpcode.DNEG:\r
+ case VMOpcode.F2I:\r
+ break;\r
+ \r
+ // Independent instructions that push one word\r
+ case VMOpcode.ALOAD_0:\r
+ case VMOpcode.ALOAD_1:\r
+ case VMOpcode.ALOAD_2:\r
+ case VMOpcode.ALOAD_3:\r
+ case VMOpcode.ALOAD:\r
+ case VMOpcode.ACONST_NULL:\r
+ case VMOpcode.BIPUSH:\r
+ case VMOpcode.FCONST_0:\r
+ case VMOpcode.FCONST_1:\r
+ case VMOpcode.FCONST_2:\r
+ case VMOpcode.FLOAD:\r
+ case VMOpcode.ICONST_0:\r
+ case VMOpcode.ICONST_1:\r
+ case VMOpcode.ICONST_2:\r
+ case VMOpcode.ICONST_3:\r
+ case VMOpcode.ICONST_4:\r
+ case VMOpcode.ICONST_5:\r
+ case VMOpcode.ICONST_M1:\r
+ case VMOpcode.LDC:\r
+ case VMOpcode.LDC_W:\r
+ case VMOpcode.SIPUSH:\r
+ earliestIndepPC[stack] = opcode_pc;\r
+ break;\r
+ \r
+ // Independent instructions that push two words\r
+ case VMOpcode.DCONST_0:\r
+ case VMOpcode.DCONST_1:\r
+ case VMOpcode.LCONST_0:\r
+ case VMOpcode.LCONST_1:\r
+ case VMOpcode.LDC2_W:\r
+ case VMOpcode.LLOAD:\r
+ case VMOpcode.LLOAD_0:\r
+ case VMOpcode.LLOAD_1:\r
+ case VMOpcode.LLOAD_2:\r
+ case VMOpcode.LLOAD_3:\r
+ earliestIndepPC[stack - 1] = \r
+ earliestIndepPC[stack] = opcode_pc;\r
+ break;\r
+ \r
+ // nothing to do for pop, obviously no\r
+ // code will be dependent on the popped words.\r
+ case VMOpcode.POP:\r
+ case VMOpcode.POP2:\r
+ break;\r
+ \r
+ case VMOpcode.SWAP:\r
+ earliestIndepPC[stack] = earliestIndepPC[stack -1];\r
+ break;\r
+ \r
+ // push a value that depends on the previous value\r
+ case VMOpcode.I2L:\r
+ earliestIndepPC[stack] = earliestIndepPC[stack -1];\r
+ break;\r
+ \r
+ case VMOpcode.GETFIELD:\r
+ {\r
+ String vmDescriptor = getTypeDescriptor(ch, opcode_pc);\r
+ int width = CodeChunk.getDescriptorWordCount(vmDescriptor);\r
+ if (width == 2)\r
+ earliestIndepPC[stack] = earliestIndepPC[stack -1];\r
+ \r
+ break;\r
+ }\r
+\r
+ case VMOpcode.INVOKEINTERFACE:\r
+ case VMOpcode.INVOKEVIRTUAL:\r
+ {\r
+ // ...,objectref[,word]*\r
+ // \r
+ // => ...\r
+ // => ...,word\r
+ // => ...,word1,word2\r
+ \r
+ // Width of the value returned by the method call.\r
+ String vmDescriptor = getTypeDescriptor(ch, opcode_pc);\r
+ int width = CodeChunk.getDescriptorWordCount(vmDescriptor);\r
+ \r
+ // Independence of this block is the independence\r
+ // of the objectref that invokved the method.\r
+ int selfContainedBlockStart;\r
+ if (width == 0)\r
+ {\r
+ // objectref was at one more than the current depth\r
+ // no plan to split here though, as we are only\r
+ // splitting methods that return a reference.\r
+ selfContainedBlockStart = -1;\r
+ // earliestIndepPC[stack + 1];\r
+ }\r
+ else if (width == 1)\r
+ { \r
+ // stack is unchanged, objectref was at\r
+ // the current stack depth\r
+ selfContainedBlockStart = earliestIndepPC[stack];\r
+ }\r
+ else\r
+ {\r
+ // width == 2, objectref was one below the\r
+ // current stack depth.\r
+ // no plan to split here though, as we are only\r
+ // splitting methods that return a reference.\r
+ selfContainedBlockStart = -1;\r
+ \r
+ // top two words depend on the objectref\r
+ // which was at the same depth of the first word\r
+ // of the 64 bit value.\r
+ earliestIndepPC[stack] =\r
+ earliestIndepPC[stack - 1];\r
+ }\r
+ \r
+ if (selfContainedBlockStart != -1)\r
+ {\r
+ int blockLength = pc - selfContainedBlockStart;\r
+ \r
+ if (blockLength <= splitMinLength)\r
+ {\r
+ // No point splitting, too small\r
+ }\r
+ else if (blockLength > (VMOpcode.MAX_CODE_LENGTH - 1))\r
+ {\r
+ // too big to split into a single method\r
+ // (one for the return opcode)\r
+ }\r
+ else\r
+ {\r
+ // Only split for a method that returns\r
+ // an class reference.\r
+ int me = vmDescriptor.lastIndexOf(')');\r
+ \r
+ if (vmDescriptor.charAt(me+1) == 'L')\r
+ {\r
+ String rt = vmDescriptor.substring(me + 2,\r
+ vmDescriptor.length() - 1);\r
+ \r
+ // convert to external format.\r
+ rt = rt.replace('/', '.');\r
+ \r
+ if (blockLength >= optimalMinLength)\r
+ {\r
+ // Split now!\r
+ BCMethod subMethod = startSubMethod(mb,\r
+ rt, selfContainedBlockStart,\r
+ blockLength);\r
+ \r
+ return splitCodeIntoSubMethod(mb, ch, subMethod,\r
+ selfContainedBlockStart, blockLength); \r
+ } \r
+ else if (blockLength > bestSplitBlockLength)\r
+ {\r
+ // Save it, may split at this point\r
+ // if nothing better seen.\r
+ bestSplitPC = selfContainedBlockStart;\r
+ bestSplitBlockLength = blockLength;\r
+ bestSplitRT = rt;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ } \r
+ \r
+ }\r
+ \r
+\r
+ if (bestSplitBlockLength != -1) {\r
+ BCMethod subMethod = startSubMethod(mb,\r
+ bestSplitRT, bestSplitPC,\r
+ bestSplitBlockLength);\r
+ \r
+ return splitCodeIntoSubMethod(mb, ch, subMethod,\r
+ bestSplitPC, bestSplitBlockLength); \r
+ }\r
+ \r
+ return -1;\r
+ }\r
+ \r
+ /**\r
+ * See if the opcode is a return instruction.\r
+ * @param opcode opcode to be checked\r
+ * @return true for is a return instruction, false otherwise.\r
+ */\r
+ private static boolean isReturn(short opcode)\r
+ {\r
+ switch (opcode)\r
+ {\r
+ case VMOpcode.RETURN:\r
+ case VMOpcode.ARETURN:\r
+ case VMOpcode.IRETURN:\r
+ case VMOpcode.FRETURN:\r
+ case VMOpcode.DRETURN:\r
+ case VMOpcode.LRETURN:\r
+ return true;\r
+ default:\r
+ return false;\r
+ } \r
+ }\r
+ \r
+ /**\r
+ * Minimum split length for a sub-method. If the number of\r
+ * instructions to call the sub-method exceeds the length\r
+ * of the sub-method, then there's no point splitting.\r
+ * The number of bytes in the code stream to call\r
+ * a generated sub-method can take is based upon the number of method args.\r
+ * A method can have maximum of 255 words of arguments (section 4.10 JVM spec)\r
+ * which in the worst case would be 254 (one-word) parameters\r
+ * and this. For a sub-method the arguments will come from the\r
+ * parameters to the method, i.e. ALOAD, ILOAD etc.\r
+ * <BR>\r
+ * This leads to this number of instructions.\r
+ * <UL>\r
+ * <LI> 4 - 'this' and first 3 parameters have single byte instructions\r
+ * <LI> (N-4)*2 - Remaining parameters have two byte instructions\r
+ * <LI> 3 for the invoke instruction.\r
+ * </UL>\r
+ */\r
+ private static int splitMinLength(BCMethod mb) {\r
+ int min = 1 + 3; // For ALOAD_0 (this) and invoke instruction\r
+ \r
+ if (mb.parameters != null) {\r
+ int paramCount = mb.parameters.length;\r
+ \r
+ min += paramCount;\r
+ \r
+ if (paramCount > 3)\r
+ min += (paramCount - 3);\r
+ }\r
+ \r
+ return min;\r
+ }\r
+ /*\r
+ final int splitNonZeroStack(BCMethod mb, ClassHolder ch,\r
+ final int codeLength, final int optimalMinLength,\r
+ int maxStack) {\r
+ \r
+ // program counter for the instruction that\r
+ // made the stack reach the given stack depth.\r
+ int[] stack_pcs = new int[maxStack+1];\r
+ Arrays.fill(stack_pcs, -1);\r
+ \r
+ int stack = 0;\r
+ \r
+ // maximum possible split seen that is less than\r
+ // the minimum.\r
+ int possibleSplitLength = -1;\r
+ \r
+ System.out.println("NZ SPLIT + " + mb.getName());\r
+\r
+ // do not split until at least this point (inclusive)\r
+ // used to ensure no split occurs in the middle of\r
+ // a conditional.\r
+ int outerConditionalEnd_pc = -1;\r
+\r
+ int end_pc = 0 + codeLength;\r
+ for (int pc = 0; pc < end_pc;) {\r
+\r
+ short opcode = getOpcode(pc);\r
+\r
+ int stackDelta = stackWordDelta(ch, pc, opcode);\r
+ \r
+ stack += stackDelta;\r
+ \r
+ // Cannot split a conditional but need to calculate\r
+ // the stack depth at the end of the conditional.\r
+ // Each path through the conditional will have the\r
+ // same stack depth.\r
+ int[] cond_pcs = findConditionalPCs(pc, opcode);\r
+ if (cond_pcs != null) {\r
+ // an else block exists, skip the then block.\r
+ if (cond_pcs[3] != -1) {\r
+ pc = cond_pcs[3];\r
+ continue;\r
+ }\r
+ \r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (outerConditionalEnd_pc != -1)\r
+ {\r
+ if (cond_pcs[5] >= outerConditionalEnd_pc)\r
+ SanityManager.THROWASSERT("NESTED CONDITIONALS!");\r
+ }\r
+ }\r
+\r
+ if (outerConditionalEnd_pc == -1)\r
+ {\r
+ outerConditionalEnd_pc = cond_pcs[5];\r
+ }\r
+ }\r
+ \r
+ pc += instructionLength(opcode);\r
+ \r
+ // Don't split in the middle of a conditional\r
+ if (outerConditionalEnd_pc != -1) {\r
+ if (pc > outerConditionalEnd_pc) {\r
+ // passed the outermost conditional\r
+ outerConditionalEnd_pc = -1;\r
+ }\r
+ continue;\r
+ }\r
+ \r
+ if (stackDelta == 0)\r
+ continue;\r
+\r
+ // Only split when the stack is having items popped\r
+ if (stackDelta > 0)\r
+ {\r
+ // pushing double word, clear out a\r
+ if (stackDelta == 2)\r
+ stack_pcs[stack - 1] = pc;\r
+ stack_pcs[stack] = pc;\r
+ continue;\r
+ }\r
+ \r
+ int opcode_pc = pc - instructionLength(opcode);\r
+ \r
+ // Look for specific opcodes that have the capability\r
+ // of having a significant amount of code in a self\r
+ // contained block.\r
+ switch (opcode)\r
+ {\r
+ // this.method(A) construct\r
+ // ... -- stack N\r
+ // push this -- stack N+1\r
+ // push args -- stack N+1+A\r
+ // call method -- stack N+R (R=0,1,2)\r
+ //\r
+ // stackDelta = (N+R) - (N+1+A) = R-(1+A)\r
+ // stack = N+R\r
+ // Need to determine N+1\r
+ // \r
+ // \r
+ //\r
+ // this.a(<i2>, <i2>, <i3>)\r
+ // returning int\r
+ //\r
+ // stackDelta = -3 (this & 3 args popped, ret pushed)\r
+ // initial depth N = 10\r
+ // pc - stack\r
+ // 100 ... - stack 10\r
+ // 101 push this - stack 11\r
+ // 109 push i1 - stack 12\r
+ // 125 push i2 - stack 13\r
+ // 156 push i3 - stack 14\r
+ // 157 call - stack 11\r
+ // \r
+ // need stack_pcs[11] = stack_pcs[11 + -3]\r
+ //\r
+ // ref.method(args).method(args) ... method(args)\r
+ // \r
+ case VMOpcode.INVOKEINTERFACE:\r
+ case VMOpcode.INVOKESPECIAL:\r
+ case VMOpcode.INVOKEVIRTUAL:\r
+ {\r
+ String vmDescriptor = getTypeDescriptor(ch, opcode_pc);\r
+ int r = CodeChunk.getDescriptorWordCount(vmDescriptor);\r
+ \r
+ // PC of the opcode that pushed the reference for\r
+ // this method call.\r
+ int ref_pc = stack_pcs[stack - r + 1];\r
+ if (getOpcode(ref_pc) == VMOpcode.ALOAD_0) {\r
+ System.out.println("POSS SPLIT " + (pc - ref_pc) + " @ " + ref_pc);\r
+ }\r
+ break;\r
+ }\r
+ case VMOpcode.INVOKESTATIC:\r
+ String vmDescriptor = getTypeDescriptor(ch, opcode_pc);\r
+ int r = CodeChunk.getDescriptorWordCount(vmDescriptor);\r
+ int p1_pc = stack_pcs[stack - r + 1];\r
+ System.out.println("POSS STATIC SPLIT " + (pc - p1_pc) + " @ " + p1_pc);\r
+ \r
+ }\r
+ stack_pcs[stack] = opcode_pc;\r
+ }\r
+ return -1;\r
+ }*/\r
+}\r