0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.services.bytecode.CodeChunk
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.services.bytecode;
0023:
0024: import org.apache.derby.iapi.services.classfile.CONSTANT_Index_info;
0025: import org.apache.derby.iapi.services.classfile.CONSTANT_Utf8_info;
0026: import org.apache.derby.iapi.services.classfile.ClassFormatOutput;
0027: import org.apache.derby.iapi.services.classfile.ClassHolder;
0028: import org.apache.derby.iapi.services.classfile.ClassMember;
0029: import org.apache.derby.iapi.services.classfile.VMDescriptor;
0030:
0031: import org.apache.derby.iapi.services.sanity.SanityManager;
0032: import org.apache.derby.iapi.services.classfile.VMOpcode;
0033: import org.apache.derby.iapi.services.io.ArrayOutputStream;
0034:
0035: import java.io.IOException;
0036: import java.lang.reflect.Modifier;
0037: import java.util.Arrays;
0038:
0039: /**
0040: * This class represents a chunk of code in a CodeAttribute.
0041: * Typically, a CodeAttribute represents the code in a method.
0042: * If there is a try/catch block, each catch block will get its
0043: * own code chunk. This allows the catch blocks to all be put at
0044: * the end of the generated code for a method, which eliminates
0045: * the need to generate a jump around each catch block, which
0046: * would be a forward reference.
0047: */
0048: final class CodeChunk {
0049:
0050: /**
0051: * Starting point of the byte code stream in the underlying stream/array.
0052: */
0053: private static final int CODE_OFFSET = 8;
0054:
0055: // The use of ILOAD for the non-integer types is correct.
0056: // We have to assume that the appropriate checks/conversions
0057: // are defined on math operation results to ensure that
0058: // the type is preserved when/as needed.
0059: static final short[] LOAD_VARIABLE = { VMOpcode.ILOAD, /* vm_byte */
0060: VMOpcode.ILOAD, /* vm_short */
0061: VMOpcode.ILOAD, /* vm_int */
0062: VMOpcode.LLOAD, /* vm_long */
0063: VMOpcode.FLOAD, /* vm_float */
0064: VMOpcode.DLOAD, /* vm_double */
0065: VMOpcode.ILOAD, /* vm_char */
0066: VMOpcode.ALOAD /* vm_reference */
0067: };
0068:
0069: static final short[] LOAD_VARIABLE_FAST = { VMOpcode.ILOAD_0, /* vm_byte */
0070: VMOpcode.ILOAD_0, /* vm_short */
0071: VMOpcode.ILOAD_0, /* vm_int */
0072: VMOpcode.LLOAD_0, /* vm_long */
0073: VMOpcode.FLOAD_0, /* vm_float */
0074: VMOpcode.DLOAD_0, /* vm_double */
0075: VMOpcode.ILOAD_0, /* vm_char */
0076: VMOpcode.ALOAD_0 /* vm_reference */
0077: };
0078:
0079: // The ISTOREs for non-int types are how things work.
0080: // It assumes that the appropriate casts are done
0081: // on operations on non-ints to ensure that the values
0082: // remain in the valid ranges.
0083: static final short[] STORE_VARIABLE = { VMOpcode.ISTORE, /* vm_byte */
0084: VMOpcode.ISTORE, /* vm_short */
0085: VMOpcode.ISTORE, /* vm_int */
0086: VMOpcode.LSTORE, /* vm_long */
0087: VMOpcode.FSTORE, /* vm_float */
0088: VMOpcode.DSTORE, /* vm_double */
0089: VMOpcode.ISTORE, /* vm_char */
0090: VMOpcode.ASTORE /* vm_reference */
0091: };
0092:
0093: static final short[] STORE_VARIABLE_FAST = { VMOpcode.ISTORE_0, /* vm_byte */
0094: VMOpcode.ISTORE_0, /* vm_short */
0095: VMOpcode.ISTORE_0, /* vm_int */
0096: VMOpcode.LSTORE_0, /* vm_long */
0097: VMOpcode.FSTORE_0, /* vm_float */
0098: VMOpcode.DSTORE_0, /* vm_double */
0099: VMOpcode.ISTORE_0, /* vm_char */
0100: VMOpcode.ASTORE_0 /* vm_reference */
0101: };
0102:
0103: static final short ARRAY_ACCESS[] = { VMOpcode.BALOAD, /* vm_byte */
0104: VMOpcode.SALOAD, /* vm_short */
0105: VMOpcode.IALOAD, /* vm_int */
0106: VMOpcode.LALOAD, /* vm_long */
0107: VMOpcode.FALOAD, /* vm_float */
0108: VMOpcode.DALOAD, /* vm_double */
0109: VMOpcode.CALOAD, /* vm_char */
0110: VMOpcode.AALOAD /* vm_reference */
0111: };
0112: static final short ARRAY_STORE[] = { VMOpcode.BASTORE, /* vm_byte */
0113: VMOpcode.SASTORE, /* vm_short */
0114: VMOpcode.IASTORE, /* vm_int */
0115: VMOpcode.LASTORE, /* vm_long */
0116: VMOpcode.FASTORE, /* vm_float */
0117: VMOpcode.DASTORE, /* vm_double */
0118: VMOpcode.CASTORE, /* vm_char */
0119: VMOpcode.AASTORE /* vm_reference */
0120: };
0121: static final short[] RETURN_OPCODE = { VMOpcode.IRETURN, /* 0 = byte */
0122: VMOpcode.IRETURN, /* 1 = short */
0123: VMOpcode.IRETURN, /* 2 = int */
0124: VMOpcode.LRETURN, /* 3 = long */
0125: VMOpcode.FRETURN, /* 4 = float */
0126: VMOpcode.DRETURN, /* 5 = double */
0127: VMOpcode.IRETURN, /* 6 = char */
0128: VMOpcode.ARETURN /* 7 = reference */
0129: };
0130:
0131: // the first dimension is the current vmTypeId
0132: // the second dimension is the target vmTypeId
0133: //
0134: // the cells of the entry at [current,target] are:
0135: // 0: operation
0136: // 1: result type of operation
0137: // if entry[1] = target, we are done. otherwise,
0138: // you have to continue with entry[1] as the new current
0139: // after generating the opcode listed (don't generate if it is NOP).
0140: // if entry[0] = BAD, we can
0141:
0142: static final short CAST_CONVERSION_INFO[][][] = {
0143: /* current = vm_byte */
0144: {
0145: /* target = vm_byte */{ VMOpcode.NOP,
0146: BCExpr.vm_byte },
0147: /* target = vm_short */{ VMOpcode.NOP,
0148: BCExpr.vm_short },
0149: /* target = vm_int */{ VMOpcode.NOP,
0150: BCExpr.vm_int },
0151: /* target = vm_long */{ VMOpcode.NOP,
0152: BCExpr.vm_int },
0153: /* target = vm_float */{ VMOpcode.NOP,
0154: BCExpr.vm_int },
0155: /* target = vm_double */{ VMOpcode.NOP,
0156: BCExpr.vm_int },
0157: /* target = vm_char */{ VMOpcode.NOP,
0158: BCExpr.vm_char },
0159: /* target = vm_reference */{ VMOpcode.BAD,
0160: BCExpr.vm_reference } },
0161: /* current = vm_short */
0162: {
0163: /* target = vm_byte */{ VMOpcode.NOP,
0164: BCExpr.vm_byte },
0165: /* target = vm_short */{ VMOpcode.NOP,
0166: BCExpr.vm_short },
0167: /* target = vm_int */{ VMOpcode.NOP,
0168: BCExpr.vm_int },
0169: /* target = vm_long */{ VMOpcode.NOP,
0170: BCExpr.vm_int },
0171: /* target = vm_float */{ VMOpcode.NOP,
0172: BCExpr.vm_int },
0173: /* target = vm_double */{ VMOpcode.NOP,
0174: BCExpr.vm_int },
0175: /* target = vm_char */{ VMOpcode.NOP,
0176: BCExpr.vm_char },
0177: /* target = vm_reference */{ VMOpcode.BAD,
0178: BCExpr.vm_reference } },
0179: /* current = vm_int */
0180: {
0181: /* target = vm_byte */{ VMOpcode.I2B,
0182: BCExpr.vm_byte },
0183: /* target = vm_short */{ VMOpcode.I2S,
0184: BCExpr.vm_short },
0185: /* target = vm_int */{ VMOpcode.NOP,
0186: BCExpr.vm_int },
0187: /* target = vm_long */{ VMOpcode.I2L,
0188: BCExpr.vm_long },
0189: /* target = vm_float */{ VMOpcode.I2F,
0190: BCExpr.vm_float },
0191: /* target = vm_double */{ VMOpcode.I2D,
0192: BCExpr.vm_double },
0193: /* target = vm_char */{ VMOpcode.I2B,
0194: BCExpr.vm_char },
0195: /* target = vm_reference */{ VMOpcode.BAD,
0196: BCExpr.vm_reference } },
0197: /* current = vm_long */
0198: {
0199: /* target = vm_byte */{ VMOpcode.L2I,
0200: BCExpr.vm_int },
0201: /* target = vm_short */{ VMOpcode.L2I,
0202: BCExpr.vm_int },
0203: /* target = vm_int */{ VMOpcode.L2I,
0204: BCExpr.vm_int },
0205: /* target = vm_long */{ VMOpcode.NOP,
0206: BCExpr.vm_long },
0207: /* target = vm_float */{ VMOpcode.L2F,
0208: BCExpr.vm_float },
0209: /* target = vm_double */{ VMOpcode.L2D,
0210: BCExpr.vm_double },
0211: /* target = vm_char */{ VMOpcode.L2I,
0212: BCExpr.vm_int },
0213: /* target = vm_reference */{ VMOpcode.BAD,
0214: BCExpr.vm_reference } },
0215: /* current = vm_float */
0216: {
0217: /* target = vm_byte */{ VMOpcode.F2I,
0218: BCExpr.vm_int },
0219: /* target = vm_short */{ VMOpcode.F2I,
0220: BCExpr.vm_int },
0221: /* target = vm_int */{ VMOpcode.F2I,
0222: BCExpr.vm_int },
0223: /* target = vm_long */{ VMOpcode.F2L,
0224: BCExpr.vm_long },
0225: /* target = vm_float */{ VMOpcode.NOP,
0226: BCExpr.vm_float },
0227: /* target = vm_double */{ VMOpcode.F2D,
0228: BCExpr.vm_double },
0229: /* target = vm_char */{ VMOpcode.F2I,
0230: BCExpr.vm_int },
0231: /* target = vm_reference */{ VMOpcode.BAD,
0232: BCExpr.vm_reference } },
0233: /* current = vm_double */
0234: {
0235: /* target = vm_byte */{ VMOpcode.D2I,
0236: BCExpr.vm_int },
0237: /* target = vm_short */{ VMOpcode.D2I,
0238: BCExpr.vm_int },
0239: /* target = vm_int */{ VMOpcode.D2I,
0240: BCExpr.vm_int },
0241: /* target = vm_long */{ VMOpcode.D2L,
0242: BCExpr.vm_long },
0243: /* target = vm_float */{ VMOpcode.D2F,
0244: BCExpr.vm_float },
0245: /* target = vm_double */{ VMOpcode.NOP,
0246: BCExpr.vm_double },
0247: /* target = vm_char */{ VMOpcode.D2I,
0248: BCExpr.vm_int },
0249: /* target = vm_reference */{ VMOpcode.BAD,
0250: BCExpr.vm_reference } },
0251: /* current = vm_char */
0252: {
0253: /* target = vm_byte */{ VMOpcode.NOP,
0254: BCExpr.vm_byte },
0255: /* target = vm_short */{ VMOpcode.NOP,
0256: BCExpr.vm_short },
0257: /* target = vm_int */{ VMOpcode.NOP,
0258: BCExpr.vm_int },
0259: /* target = vm_long */{ VMOpcode.NOP,
0260: BCExpr.vm_int },
0261: /* target = vm_float */{ VMOpcode.NOP,
0262: BCExpr.vm_int },
0263: /* target = vm_double */{ VMOpcode.NOP,
0264: BCExpr.vm_int },
0265: /* target = vm_char */{ VMOpcode.NOP,
0266: BCExpr.vm_char },
0267: /* target = vm_reference */{ VMOpcode.BAD,
0268: BCExpr.vm_reference } },
0269: /* current = vm_reference */
0270: {
0271: /* target = vm_byte */{ VMOpcode.BAD,
0272: BCExpr.vm_byte },
0273: /* target = vm_short */{ VMOpcode.BAD,
0274: BCExpr.vm_short },
0275: /* target = vm_int */{ VMOpcode.BAD,
0276: BCExpr.vm_int },
0277: /* target = vm_long */{ VMOpcode.BAD,
0278: BCExpr.vm_long },
0279: /* target = vm_float */{ VMOpcode.BAD,
0280: BCExpr.vm_float },
0281: /* target = vm_double */{ VMOpcode.BAD,
0282: BCExpr.vm_double },
0283: /* target = vm_char */{ VMOpcode.BAD,
0284: BCExpr.vm_char },
0285: /* target = vm_reference */{ VMOpcode.NOP,
0286: BCExpr.vm_reference } } };
0287:
0288: /**
0289: * Constant used by OPCODE_ACTION to represent the
0290: * common action of push one word, 1 byte
0291: * for the instruction.
0292: */
0293: private static final byte[] push1_1i = { 1, 1 };
0294:
0295: /**
0296: * Constant used by OPCODE_ACTION to represent the
0297: * common action of push two words, 1 byte
0298: * for the instruction.
0299: */
0300: private static final byte[] push2_1i = { 2, 1 };
0301: /**
0302: * Constant used by OPCODE_ACTION to the opcode is
0303: * not yet supported.
0304: */
0305: private static final byte[] NS = { 0, -1 };
0306:
0307: /**
0308: * Value for OPCODE_ACTION[opcode][0] to represent
0309: * the number of words popped or pushed in variable.
0310: */
0311: private static final byte VARIABLE_STACK = -128;
0312:
0313: /**
0314: * Array that provides two pieces of information about
0315: * each VM opcode. Each opcode has a two byte array.
0316: * <P>
0317: * The first element in the array [0] is the number of
0318: * stack words (double/long count as two) pushed by the opcode.
0319: * Will be negative if the opcode pops values.
0320: *
0321: * <P>
0322: * The second element in the array [1] is the number of bytes
0323: * in the instruction stream that this opcode's instruction
0324: * takes up, including the opocode.
0325: */
0326: private static final byte[][] OPCODE_ACTION = {
0327:
0328: /* NOP 0 */{ 0, 1 },
0329:
0330: /* ACONST_NULL 1 */push1_1i,
0331: /* ICONST_M1 2 */push1_1i,
0332: /* ICONST_0 3 */push1_1i,
0333: /* ICONST_1 4 */push1_1i,
0334: /* ICONST_2 5 */push1_1i,
0335: /* ICONST_3 6 */push1_1i,
0336: /* ICONST_4 7 */push1_1i,
0337: /* ICONST_5 8 */push1_1i,
0338: /* LCONST_0 9 */push2_1i,
0339: /* LCONST_1 10 */push2_1i,
0340: /* FCONST_0 11 */push1_1i,
0341: /* FCONST_1 12 */push1_1i,
0342: /* FCONST_2 13 */push1_1i,
0343: /* DCONST_0 14 */push2_1i,
0344: /* DCONST_1 15 */push2_1i,
0345:
0346: /* BIPUSH 16 */{ 1, 2 },
0347: /* SIPUSH 17 */{ 1, 3 },
0348: /* LDC 18 */{ 1, 2 },
0349: /* LDC_W 19 */{ 1, 3 },
0350: /* LDC2_W 20 */{ 2, 3 },
0351:
0352: /* ILOAD 21 */{ 1, 2 },
0353: /* LLOAD 22 */{ 2, 2 },
0354: /* FLOAD 23 */{ 1, 2 },
0355: /* DLOAD 24 */{ 2, 2 },
0356: /* ALOAD 25 */{ 1, 2 },
0357: /* ILOAD_0 26 */push1_1i,
0358: /* ILOAD_1 27 */push1_1i,
0359: /* ILOAD_2 28 */push1_1i,
0360: /* ILOAD_3 29 */push1_1i,
0361: /* LLOAD_0 30 */push2_1i,
0362: /* LLOAD_1 31 */push2_1i,
0363: /* LLOAD_2 32 */push2_1i,
0364: /* LLOAD_3 33 */push2_1i,
0365: /* FLOAD_0 34 */push1_1i,
0366: /* FLOAD_1 35 */push1_1i,
0367: /* FLOAD_2 36 */push1_1i,
0368: /* FLOAD_3 37 */push1_1i,
0369: /* DLOAD_0 38 */push2_1i,
0370: /* DLOAD_1 39 */push2_1i,
0371: /* DLOAD_2 40 */push2_1i,
0372: /* DLOAD_3 41 */push2_1i,
0373: /* ALOAD_0 42 */push1_1i,
0374: /* ALOAD_1 43 */push1_1i,
0375: /* ALOAD_2 44 */push1_1i,
0376: /* ALOAD_3 45 */push1_1i,
0377: /* IALOAD 46 */{ -1, 1 },
0378: /* LALOAD 47 */{ 0, 1 },
0379: /* FALOAD 48 */{ -1, 1 },
0380: /* DALOAD 49 */{ 0, 1 },
0381: /* AALOAD 50 */{ -1, 1 },
0382: /* BALOAD 51 */{ -1, 1 },
0383: /* CALOAD 52 */{ -1, 1 },
0384:
0385: /* SALOAD 53 */{ -1, 1 },
0386: /* ISTORE 54 */{ -1, 2 },
0387: /* LSTORE 55 */{ -2, 2 },
0388: /* FSTORE 56 */{ -1, 2 },
0389: /* DSTORE 57 */{ -2, 2 },
0390: /* ASTORE 58 */{ -1, 2 },
0391: /* ISTORE_0 59 */{ -1, 1 },
0392: /* ISTORE_1 60 */{ -1, 1 },
0393: /* ISTORE_2 61 */{ -1, 1 },
0394: /* ISTORE_3 62 */{ -1, 1 },
0395: /* LSTORE_0 63 */{ -2, 1 },
0396: /* LSTORE_1 64 */{ -2, 1 },
0397: /* LSTORE_2 65 */{ -2, 1 },
0398: /* LSTORE_3 66 */{ -2, 1 },
0399: /* FSTORE_0 67 */{ -1, 1 },
0400: /* FSTORE_1 68 */{ -1, 1 },
0401: /* FSTORE_2 69 */{ -1, 1 },
0402: /* FSTORE_3 70 */{ -1, 1 },
0403: /* DSTORE_0 71 */{ -2, 1 },
0404: /* DSTORE_1 72 */{ -2, 1 },
0405: /* DSTORE_2 73 */{ -2, 1 },
0406: /* DSTORE_3 74 */{ -2, 1 },
0407: /* ASTORE_0 75 */{ -1, 1 },
0408: /* ASTORE_1 76 */{ -1, 1 },
0409: /* ASTORE_2 77 */{ -1, 1 },
0410: /* ASTORE_3 78 */{ -1, 1 },
0411: /* IASTORE 79 */{ -3, 1 },
0412: /* LASTORE 80 */{ -4, 1 },
0413: /* FASTORE 81 */{ -3, 1 },
0414: /* DASTORE 82 */{ -4, 1 },
0415: /* AASTORE 83 */{ -3, 1 },
0416: /* BASTORE 84 */{ -3, 1 },
0417: /* CASTORE 85 */{ -3, 1 },
0418: /* SASTORE 86 */{ -3, 1 },
0419:
0420: /* POP 87 */{ -1, 1 },
0421: /* POP2 88 */{ -2, 1 },
0422: /* DUP 89 */push1_1i,
0423: /* DUP_X1 90 */push1_1i,
0424: /* DUP_X2 91 */push1_1i,
0425: /* DUP2 92 */push2_1i,
0426: /* DUP2_X1 93 */push2_1i,
0427: /* DUP2_X2 94 */push2_1i,
0428: /* SWAP 95 */{ 0, 1 },
0429:
0430: /* IADD 96 */NS,
0431: /* LADD 97 */NS,
0432: /* FADD 98 */{ -1, 1 },
0433: /* DADD 99 */{ -2, 1 },
0434: /* ISUB 100 */NS,
0435: /* LSUB 101 */NS,
0436: /* FSUB 102 */{ -1, 1 },
0437: /* DSUB 103 */{ -2, 1 },
0438: /* IMUL 104 */NS,
0439: /* LMUL 105 */NS,
0440: /* FMUL 106 */{ -1, 1 },
0441: /* DMUL 107 */{ -2, 1 },
0442: /* IDIV 108 */NS,
0443: /* LDIV 109 */NS,
0444: /* FDIV 110 */{ -1, 1 },
0445: /* DDIV 111 */{ -2, 1 },
0446: /* IREM 112 */{ -1, 1 },
0447: /* LREM 113 */{ -2, 1 },
0448: /* FREM 114 */{ -1, 1 },
0449: /* DREM 115 */{ -2, 1 },
0450: /* INEG 116 */{ 0, 1 },
0451: /* LNEG 117 */{ 0, 1 },
0452: /* FNEG 118 */{ 0, 1 },
0453: /* DNEG 119 */{ 0, 1 },
0454: /* ISHL 120 */{ -1, 1 },
0455: /* LSHL 121 */NS,
0456: /* ISHR 122 */NS,
0457: /* LSHR 123 */NS,
0458: /* IUSHR 124 */NS,
0459: /* LUSHR 125 */NS,
0460:
0461: /* IAND 126 */{ -1, 1 },
0462: /* LAND 127 */NS,
0463: /* IOR 128 */{ -1, 1 },
0464: /* LOR 129 */NS,
0465: /* IXOR 130 */NS,
0466: /* LXOR 131 */NS,
0467: /* IINC 132 */NS,
0468:
0469: /* I2L 133 */push1_1i,
0470: /* I2F 134 */{ 0, 1 },
0471: /* I2D 135 */push1_1i,
0472: /* L2I 136 */{ -1, 1 },
0473: /* L2F 137 */{ -1, 1 },
0474: /* L2D 138 */{ 0, 1 },
0475: /* F2I 139 */{ 0, 1 },
0476: /* F2L 140 */push2_1i,
0477: /* F2D 141 */push1_1i,
0478: /* D2I 142 */{ -1, 1 },
0479: /* D2L 143 */{ 0, 1 },
0480: /* D2F 144 */{ -1, 1 },
0481: /* I2B 145 */{ 0, 1 },
0482: /* I2C 146 */{ 0, 1 },
0483: /* I2S 147 */{ 0, 1 },
0484:
0485: /* LCMP 148 */NS,
0486: /* FCMPL 149 */{ -1, 1 },
0487: /* FCMPG 150 */{ -1, 1 },
0488: /* DCMPL 151 */{ -3, 1 },
0489: /* DCMPG 152 */{ -3, 1 },
0490: /* IFEQ 153 */{ -1, VMOpcode.IF_INS_LENGTH },
0491: /* IFNE 154 */{ -1, VMOpcode.IF_INS_LENGTH },
0492: /* IFLT 155 */{ -1, VMOpcode.IF_INS_LENGTH },
0493: /* IFGE 156 */{ -1, VMOpcode.IF_INS_LENGTH },
0494: /* IFGT 157 */{ -1, VMOpcode.IF_INS_LENGTH },
0495: /* IFLE 158 */{ -1, VMOpcode.IF_INS_LENGTH },
0496: /* IF_ICMPEQ 159 */NS,
0497: /* IF_ICMPNE 160 */NS,
0498: /* IF_ICMPLT 161 */NS,
0499: /* IF_ICMPGE 162 */NS,
0500: /* IF_ICMPGT 163 */NS,
0501: /* IF_ICMPLE 164 */NS,
0502: /* IF_ACMPEQ 165 */NS,
0503: /* IF_ACMPNE 166 */NS,
0504: /* GOTO 167 */{ 0, VMOpcode.GOTO_INS_LENGTH },
0505: /* JSR 168 */NS,
0506: /* RET 169 */NS,
0507: /* TABLESWITCH 170 */NS,
0508: /* LOOKUPSWITCH 171 */NS,
0509:
0510: /* IRETURN 172 */{ -1, 1 }, // strictly speaking all words on the stack are popped.
0511: /* LRETURN 173 */{ -2, 1 }, // strictly speaking all words on the stack are popped.
0512: /* FRETURN 174 */{ -1, 1 }, // strictly speaking all words on the stack are popped.
0513: /* DRETURN 175 */{ -2, 1 }, // strictly speaking all words on the stack are popped.
0514: /* ARETURN 176 */{ -1, 1 }, // strictly speaking all words on the stack are popped.
0515: /* RETURN 177 */{ 0, 1 }, // strictly speaking all words on the stack are popped.
0516:
0517: /* GETSTATIC 178 */{ VARIABLE_STACK, 3 },
0518: /* PUTSTATIC 179 */{ VARIABLE_STACK, 3 },
0519: /* GETFIELD 180 */{ VARIABLE_STACK, 3 },
0520: /* PUTFIELD 181 */{ VARIABLE_STACK, 3 },
0521: /* INVOKEVIRTUAL 182 */{ VARIABLE_STACK, 3 },
0522: /* INVOKESPECIAL 183 */{ VARIABLE_STACK, 3 },
0523: /* INVOKESTATIC 184 */{ VARIABLE_STACK, 3 },
0524: /* INVOKEINTERFACE 185 */{ VARIABLE_STACK, 5 },
0525:
0526: /* XXXUNUSEDXXX 186 */NS,
0527:
0528: /* NEW 187 */{ 1, 3 },
0529: /* NEWARRAY 188 */{ 0, 2 },
0530: /* ANEWARRAY 189 */{ 0, 3 },
0531: /* ARRAYLENGTH 190 */{ 0, 1 },
0532: /* ATHROW 191 */NS,
0533: /* CHECKCAST 192 */{ 0, 3 },
0534: /* INSTANCEOF 193 */{ 0, 3 },
0535: /* MONITORENTER 194 */NS,
0536: /* MONITOREXIT 195 */NS,
0537: /* WIDE 196 */NS,
0538: /* MULTIANEWARRAY 197 */NS,
0539: /* IFNULL 198 */{ -1, VMOpcode.IF_INS_LENGTH },
0540: /* IFNONNULL 199 */{ -1, VMOpcode.IF_INS_LENGTH },
0541: /* GOTO_W 200 */{ 0, VMOpcode.GOTO_W_INS_LENGTH },
0542: /* JSR_W 201 */NS,
0543: /* BREAKPOINT 202 */NS,
0544:
0545: };
0546:
0547: /**
0548: * Assume an IOException means some limit of the class file
0549: * format was hit
0550: *
0551: */
0552: private void limitHit(IOException ioe) {
0553: cb.addLimitExceeded(ioe.toString());
0554: }
0555:
0556: /**
0557: * Add an instruction that has no operand.
0558: * All opcodes are 1 byte large.
0559: */
0560: void addInstr(short opcode) {
0561: try {
0562: cout.putU1(opcode);
0563: } catch (IOException ioe) {
0564: limitHit(ioe);
0565: }
0566:
0567: if (SanityManager.DEBUG) {
0568: if (OPCODE_ACTION[opcode][1] != 1)
0569: SanityManager.THROWASSERT("Opcode " + opcode
0570: + " incorrect entry in OPCODE_ACTION -"
0571: + " writing 1 byte - set as "
0572: + OPCODE_ACTION[opcode][1]);
0573: }
0574: }
0575:
0576: /**
0577: * Add an instruction that has a 16 bit operand.
0578: */
0579: void addInstrU2(short opcode, int operand) {
0580:
0581: try {
0582: cout.putU1(opcode);
0583: cout.putU2(operand);
0584: } catch (IOException ioe) {
0585: limitHit(ioe);
0586: }
0587:
0588: if (SanityManager.DEBUG) {
0589: if (OPCODE_ACTION[opcode][1] != 3)
0590: SanityManager.THROWASSERT("Opcode " + opcode
0591: + " incorrect entry in OPCODE_ACTION -"
0592: + " writing 3 bytes - set as "
0593: + OPCODE_ACTION[opcode][1]);
0594: }
0595: }
0596:
0597: /**
0598: * Add an instruction that has a 32 bit operand.
0599: */
0600: void addInstrU4(short opcode, int operand) {
0601: try {
0602: cout.putU1(opcode);
0603: cout.putU4(operand);
0604: } catch (IOException ioe) {
0605: limitHit(ioe);
0606: }
0607: if (SanityManager.DEBUG) {
0608: if (OPCODE_ACTION[opcode][1] != 5)
0609: SanityManager.THROWASSERT("Opcode " + opcode
0610: + " incorrect entry in OPCODE_ACTION -"
0611: + " writing 5 bytes - set as "
0612: + OPCODE_ACTION[opcode][1]);
0613: }
0614: }
0615:
0616: /**
0617: * Add an instruction that has an 8 bit operand.
0618: */
0619: void addInstrU1(short opcode, int operand) {
0620: try {
0621: cout.putU1(opcode);
0622: cout.putU1(operand);
0623: } catch (IOException ioe) {
0624: limitHit(ioe);
0625: }
0626:
0627: // Only debug code from here.
0628: if (SanityManager.DEBUG) {
0629:
0630: if (OPCODE_ACTION[opcode][1] != 2)
0631: SanityManager.THROWASSERT("Opcode " + opcode
0632: + " incorrect entry in OPCODE_ACTION -"
0633: + " writing 2 bytes - set as "
0634: + OPCODE_ACTION[opcode][1]);
0635:
0636: }
0637: }
0638:
0639: /**
0640: * This takes an instruction that has a narrow
0641: * and a wide form for CPE access, and
0642: * generates accordingly the right one.
0643: * We assume the narrow instruction is what
0644: * we were given, and that the wide form is
0645: * the next possible instruction.
0646: */
0647: void addInstrCPE(short opcode, int cpeNum) {
0648: if (cpeNum < 256) {
0649: addInstrU1(opcode, cpeNum);
0650: } else {
0651: addInstrU2((short) (opcode + 1), cpeNum);
0652: }
0653: }
0654:
0655: /**
0656: * This takes an instruction that can be wrapped in
0657: * a wide for large variable #s and does so.
0658: */
0659: void addInstrWide(short opcode, int varNum) {
0660: if (varNum < 256) {
0661: addInstrU1(opcode, varNum);
0662: } else {
0663: addInstr(VMOpcode.WIDE);
0664: addInstrU2(opcode, varNum);
0665: }
0666: }
0667:
0668: /**
0669: * For adding an instruction with 3 operands, a U2 and two U1's.
0670: * So far, this is used by VMOpcode.INVOKEINTERFACE.
0671: */
0672: void addInstrU2U1U1(short opcode, int operand1, short operand2,
0673: short operand3) {
0674: try {
0675: cout.putU1(opcode);
0676: cout.putU2(operand1);
0677: cout.putU1(operand2);
0678: cout.putU1(operand3);
0679: } catch (IOException ioe) {
0680: limitHit(ioe);
0681: }
0682: if (SanityManager.DEBUG) {
0683: if (OPCODE_ACTION[opcode][1] != 5)
0684: SanityManager.THROWASSERT("Opcode " + opcode
0685: + " incorrect entry in OPCODE_ACTION -"
0686: + " writing 5 bytes - set as "
0687: + OPCODE_ACTION[opcode][1]);
0688: }
0689: }
0690:
0691: /** Get the current program counter */
0692: int getPC() {
0693: return cout.size() + pcDelta;
0694: }
0695:
0696: /**
0697: * Return the complete instruction length for the
0698: * passed in opcode. This will include the space for
0699: * the opcode and its operand.
0700: */
0701: private static int instructionLength(short opcode) {
0702: int instructionLength = OPCODE_ACTION[opcode][1];
0703:
0704: if (SanityManager.DEBUG) {
0705: if (instructionLength < 0)
0706: SanityManager
0707: .THROWASSERT("Opcode without instruction length "
0708: + opcode);
0709: }
0710:
0711: return instructionLength;
0712: }
0713:
0714: /**
0715: * The delta between cout.size() and the pc.
0716: * For an initial code chunk this is -8 (CODE_OFFSET)
0717: * since 8 bytes are written.
0718: * For a nested CodeChunk return by insertCodeSpace the delta
0719: * corresponds to the original starting pc.
0720: * @see #insertCodeSpace
0721: */
0722: private final int pcDelta;
0723:
0724: /**
0725: * The class we are generating code for, used to indicate that
0726: * some limit was hit during code generation.
0727: */
0728: final BCClass cb;
0729:
0730: CodeChunk(BCClass cb) {
0731: this .cb = cb;
0732: cout = new ClassFormatOutput();
0733: try {
0734: cout.putU2(0); // max_stack, placeholder for now
0735: cout.putU2(0); // max_locals, placeholder for now
0736: cout.putU4(0); // code_length, placeholder 4 now
0737: } catch (IOException ioe) {
0738: limitHit(ioe);
0739: }
0740: pcDelta = -CodeChunk.CODE_OFFSET;
0741: }
0742:
0743: /**
0744: * Return a CodeChunk that has limited visibility into
0745: * this CodeChunk. Used when a caller needs to insert instructions
0746: * into an existing stream.
0747: * @param pc
0748: * @param byteCount
0749: */
0750: private CodeChunk(CodeChunk main, int pc, int byteCount) {
0751: this .cb = main.cb;
0752: ArrayOutputStream aos = new ArrayOutputStream(main.cout
0753: .getData());
0754:
0755: try {
0756: aos.setPosition(CODE_OFFSET + pc);
0757: aos.setLimit(byteCount);
0758: } catch (IOException e) {
0759: limitHit(e);
0760: }
0761:
0762: cout = new ClassFormatOutput(aos);
0763: pcDelta = pc;
0764: }
0765:
0766: private final ClassFormatOutput cout;
0767:
0768: /**
0769: * now that we have codeBytes, fix the lengths fields in it
0770: * to reflect what was stored.
0771: * Limits checked here are from these sections of the JVM spec.
0772: * <UL>
0773: * <LI> 4.7.3 The Code Attribute
0774: * <LI> 4.10 Limitations of the Java Virtual Machine
0775: * </UL>
0776: */
0777: private void fixLengths(BCMethod mb, int maxStack, int maxLocals,
0778: int codeLength) {
0779:
0780: byte[] codeBytes = cout.getData();
0781:
0782: // max_stack is in bytes 0-1
0783: if (mb != null && maxStack > 65535)
0784: cb.addLimitExceeded(mb, "max_stack", 65535, maxStack);
0785:
0786: codeBytes[0] = (byte) (maxStack >> 8);
0787: codeBytes[1] = (byte) (maxStack);
0788:
0789: // max_locals is in bytes 2-3
0790: if (mb != null && maxLocals > 65535)
0791: cb.addLimitExceeded(mb, "max_locals", 65535, maxLocals);
0792: codeBytes[2] = (byte) (maxLocals >> 8);
0793: codeBytes[3] = (byte) (maxLocals);
0794:
0795: // code_length is in bytes 4-7
0796: if (mb != null && codeLength > VMOpcode.MAX_CODE_LENGTH)
0797: cb.addLimitExceeded(mb, "code_length",
0798: VMOpcode.MAX_CODE_LENGTH, codeLength);
0799: codeBytes[4] = (byte) (codeLength >> 24);
0800: codeBytes[5] = (byte) (codeLength >> 16);
0801: codeBytes[6] = (byte) (codeLength >> 8);
0802: codeBytes[7] = (byte) (codeLength);
0803: }
0804:
0805: /**
0806: * wrap up the entry and stuff it in the class,
0807: * now that it holds all of the instructions and
0808: * the exception table.
0809: */
0810: void complete(BCMethod mb, ClassHolder ch, ClassMember method,
0811: int maxStack, int maxLocals) {
0812:
0813: int codeLength = getPC();
0814:
0815: ClassFormatOutput out = cout;
0816:
0817: try {
0818:
0819: out.putU2(0); // exception_table_length
0820:
0821: if (SanityManager.DEBUG) {
0822: if (SanityManager.DEBUG_ON("ClassLineNumbers")) {
0823: // Add a single attribute - LineNumberTable
0824: // This add fake line numbers that are the pc offset in the method.
0825: out.putU2(1); // attributes_count
0826:
0827: int cpiUTF = ch.addUtf8("LineNumberTable");
0828:
0829: out.putU2(cpiUTF);
0830: out.putU4((codeLength * 4) + 2);
0831: out.putU2(codeLength);
0832: for (int i = 0; i < codeLength; i++) {
0833: out.putU2(i);
0834: out.putU2(i);
0835: }
0836: } else {
0837: out.putU2(0); // attributes_count
0838: }
0839:
0840: } else {
0841: out.putU2(0); // attributes_count
0842: // attributes is empty, a 0-element array.
0843: }
0844: } catch (IOException ioe) {
0845: limitHit(ioe);
0846: }
0847:
0848: fixLengths(mb, maxStack, maxLocals, codeLength);
0849: method.addAttribute("Code", out);
0850:
0851: if (SanityManager.DEBUG) {
0852: // Only validate if the class file format is valid.
0853: // Ok code length and guaranteed no errors building the class.
0854: if ((codeLength <= VMOpcode.MAX_CODE_LENGTH)
0855: && (mb != null && mb.cb.limitMsg == null)) {
0856: // Validate the alternate way to calculate the
0857: // max stack agrees with the dynamic as the code
0858: // is built way.
0859: int walkedMaxStack = findMaxStack(ch, 0, codeLength);
0860: if (walkedMaxStack != maxStack) {
0861: SanityManager.THROWASSERT("MAX STACK MISMATCH!! "
0862: + maxStack + " <> " + walkedMaxStack);
0863: }
0864: }
0865: }
0866:
0867: }
0868:
0869: /**
0870: * Return the opcode at the given pc.
0871: */
0872: short getOpcode(int pc) {
0873: return (short) (cout.getData()[CODE_OFFSET + pc] & 0xff);
0874: }
0875:
0876: /**
0877: * Get the unsigned short value for the opcode at the program
0878: * counter pc.
0879: */
0880: private int getU2(int pc) {
0881: byte[] codeBytes = cout.getData();
0882:
0883: int u2p = CODE_OFFSET + pc + 1;
0884:
0885: return ((codeBytes[u2p] & 0xff) << 8)
0886: | (codeBytes[u2p + 1] & 0xff);
0887: }
0888:
0889: /**
0890: * Get the unsigned 32 bit value for the opcode at the program
0891: * counter pc.
0892: */
0893: private int getU4(int pc) {
0894: byte[] codeBytes = cout.getData();
0895:
0896: int u4p = CODE_OFFSET + pc + 1;
0897:
0898: return (((codeBytes[u4p] & 0xff) << 24)
0899: | ((codeBytes[u4p + 1] & 0xff) << 16)
0900: | ((codeBytes[u4p + 2] & 0xff) << 8) | ((codeBytes[u4p + 3] & 0xff)));
0901: }
0902:
0903: /**
0904: * Insert room for byteCount bytes after the instruction at pc
0905: * and prepare to replace the instruction at pc. The instruction
0906: * at pc is not modified by this call, space is allocated after it.
0907: * The newly inserted space will be filled with NOP instructions.
0908: *
0909: * Returns a CodeChunk positioned at pc and available to write
0910: * instructions upto (byteCode + length(existing instruction at pc) bytes.
0911: *
0912: * This chunk is left correctly positioned at the end of the code
0913: * stream, ready to accept more code. Its pc will have increased by
0914: * additionalBytes.
0915: *
0916: * It is the responsibility of the caller to patch up any
0917: * branches or gotos.
0918: *
0919: * @param pc
0920: * @param additionalBytes
0921: */
0922: CodeChunk insertCodeSpace(int pc, int additionalBytes) {
0923: short existingOpcode = getOpcode(pc);
0924:
0925: int lengthOfExistingInstruction = instructionLength(existingOpcode);
0926:
0927: if (additionalBytes > 0) {
0928: // Size of the current code after this pc.
0929: int sizeToMove = (getPC() - pc)
0930: - lengthOfExistingInstruction;
0931:
0932: // Increase the code by the number of bytes to be
0933: // inserted. These NOPs will be overwritten by the
0934: // moved code by the System.arraycopy below.
0935: // It's assumed that the number of inserted bytes
0936: // is small, one or two instructions worth, so it
0937: // won't be a performance issue.
0938: for (int i = 0; i < additionalBytes; i++)
0939: addInstr(VMOpcode.NOP);
0940:
0941: // Must get codeBytes here as the array might have re-sized.
0942: byte[] codeBytes = cout.getData();
0943:
0944: int byteOffset = CODE_OFFSET + pc
0945: + lengthOfExistingInstruction;
0946:
0947: // Shift the existing code stream down
0948: System.arraycopy(codeBytes, byteOffset, codeBytes,
0949: byteOffset + additionalBytes, sizeToMove);
0950:
0951: // Place NOPs in the space just freed by the move.
0952: // This is not required, it ias assumed the caller
0953: // will overwrite all the bytes they requested, but
0954: // to be safe fill in with NOPs rather than leaving code
0955: // that could break the verifier.
0956: Arrays.fill(codeBytes, byteOffset, byteOffset
0957: + additionalBytes, (byte) VMOpcode.NOP);
0958: }
0959:
0960: // The caller must overwrite the original instruction
0961: // at pc, thus increase the range of the limit stream
0962: // created to include those bytes.
0963: additionalBytes += lengthOfExistingInstruction;
0964:
0965: // Now the caller needs to fill in the instructions
0966: // that make up the modified byteCount bytes of bytecode stream.
0967: // Return a CodeChunk that can be used for this and
0968: // is limited to only those bytes.
0969: // The pc of the original code chunk is left unchanged.
0970:
0971: return new CodeChunk(this , pc, additionalBytes);
0972:
0973: }
0974:
0975: /*
0976: * * Methods related to splitting the byte code chunks into sections that
0977: * fit in the JVM's limits for a single method.
0978: */
0979:
0980: /**
0981: * For a block of byte code starting at program counter pc for codeLength
0982: * bytes return the maximum stack value, assuming a initial stack depth of
0983: * zero.
0984: */
0985: private int findMaxStack(ClassHolder ch, int pc, int codeLength) {
0986:
0987: final int endPc = pc + codeLength;
0988: int stack = 0;
0989: int maxStack = 0;
0990:
0991: for (; pc < endPc;) {
0992:
0993: short opcode = getOpcode(pc);
0994:
0995: int stackDelta = stackWordDelta(ch, pc, opcode);
0996:
0997: stack += stackDelta;
0998: if (stack > maxStack)
0999: maxStack = stack;
1000:
1001: int[] cond_pcs = findConditionalPCs(pc, opcode);
1002: if (cond_pcs != null) {
1003: // an else block exists.
1004: if (cond_pcs[3] != -1) {
1005: int blockMaxStack = findMaxStack(ch, cond_pcs[1],
1006: cond_pcs[2]);
1007: if ((stack + blockMaxStack) > maxStack)
1008: maxStack = stack + blockMaxStack;
1009:
1010: pc = cond_pcs[3];
1011: continue;
1012: }
1013: }
1014:
1015: pc += instructionLength(opcode);
1016: }
1017:
1018: return maxStack;
1019: }
1020:
1021: /**
1022: * Return the number of stack words pushed (positive) or popped (negative)
1023: * by this instruction.
1024: */
1025: private int stackWordDelta(ClassHolder ch, int pc, short opcode) {
1026: if (SanityManager.DEBUG) {
1027: // this validates the OPCODE_ACTION entry
1028: instructionLength(opcode);
1029: }
1030:
1031: int stackDelta = OPCODE_ACTION[opcode][0];
1032: if (stackDelta == VARIABLE_STACK) {
1033: stackDelta = getVariableStackDelta(ch, pc, opcode);
1034: }
1035:
1036: return stackDelta;
1037: }
1038:
1039: /**
1040: * Get the type descriptor in the virtual machine format for the type
1041: * defined by the constant pool index for the instruction at pc.
1042: */
1043: private String getTypeDescriptor(ClassHolder ch, int pc) {
1044: int cpi = getU2(pc);
1045:
1046: // Field reference or method reference
1047: CONSTANT_Index_info cii = (CONSTANT_Index_info) ch
1048: .getEntry(cpi);
1049:
1050: // NameAndType reference
1051: int nameAndType = cii.getI2();
1052: cii = (CONSTANT_Index_info) ch.getEntry(nameAndType);
1053:
1054: // UTF8 descriptor
1055: int descriptor = cii.getI2();
1056: CONSTANT_Utf8_info type = (CONSTANT_Utf8_info) ch
1057: .getEntry(descriptor);
1058:
1059: String vmDescriptor = type.toString();
1060:
1061: return vmDescriptor;
1062: }
1063:
1064: /**
1065: * Get the word count for a type descriptor in the format of the virual
1066: * machine. For a method this returns the the word count for the return
1067: * type.
1068: */
1069: private static int getDescriptorWordCount(String vmDescriptor) {
1070:
1071: int width;
1072: if (VMDescriptor.DOUBLE.equals(vmDescriptor))
1073: width = 2;
1074: else if (VMDescriptor.LONG.equals(vmDescriptor))
1075: width = 2;
1076: else if (vmDescriptor.charAt(0) == VMDescriptor.C_METHOD) {
1077: switch (vmDescriptor.charAt(vmDescriptor.length() - 1)) {
1078: case VMDescriptor.C_DOUBLE:
1079: case VMDescriptor.C_LONG:
1080: width = 2;
1081: break;
1082: case VMDescriptor.C_VOID:
1083: width = 0;
1084: break;
1085: default:
1086: width = 1;
1087: break;
1088: }
1089: } else
1090: width = 1;
1091:
1092: return width;
1093: }
1094:
1095: /**
1096: * Get the number of words pushed (positive) or popped (negative) by this
1097: * instruction. The instruction is a get/put field or a method call, thus
1098: * the size of the words is defined by the field or method being access.
1099: */
1100: private int getVariableStackDelta(ClassHolder ch, int pc, int opcode) {
1101: String vmDescriptor = getTypeDescriptor(ch, pc);
1102: int width = CodeChunk.getDescriptorWordCount(vmDescriptor);
1103:
1104: int stackDelta = 0;
1105: // Stack delta depends on context.
1106: switch (opcode) {
1107: case VMOpcode.GETSTATIC:
1108: stackDelta = width;
1109: break;
1110:
1111: case VMOpcode.GETFIELD:
1112: stackDelta = width - 1; // one for popped object ref
1113: break;
1114:
1115: case VMOpcode.PUTSTATIC:
1116: stackDelta = -width;
1117: break;
1118:
1119: case VMOpcode.PUTFIELD:
1120: stackDelta = -width - 1; // one for pop object ref
1121: break;
1122:
1123: case VMOpcode.INVOKEVIRTUAL:
1124: case VMOpcode.INVOKESPECIAL:
1125: stackDelta = -1; // for instance reference for method call.
1126: case VMOpcode.INVOKESTATIC:
1127: stackDelta += (width - CodeChunk
1128: .parameterWordCount(vmDescriptor));
1129: // System.out.println("invoked non-interface " + stackDelta);
1130: break;
1131:
1132: case VMOpcode.INVOKEINTERFACE:
1133: // third byte contains the number of arguments to be popped
1134: stackDelta = width - getOpcode(pc + 3);
1135: // System.out.println("invoked interface " + stackDelta);
1136: break;
1137: default:
1138: System.out.println("WHO IS THIS ");
1139: break;
1140:
1141: }
1142:
1143: return stackDelta;
1144: }
1145:
1146: /**
1147: * Calculate the number of stack words in the arguments pushed for this
1148: * method descriptor.
1149: */
1150: private static int parameterWordCount(String methodDescriptor) {
1151: int wordCount = 0;
1152: for (int i = 1;; i++) {
1153: switch (methodDescriptor.charAt(i)) {
1154: case VMDescriptor.C_ENDMETHOD:
1155: return wordCount;
1156: case VMDescriptor.C_DOUBLE:
1157: case VMDescriptor.C_LONG:
1158: wordCount += 2;
1159: break;
1160: case VMDescriptor.C_ARRAY:
1161: // skip while there are array symbols.
1162: do {
1163: i++;
1164: } while (methodDescriptor.charAt(i) == VMDescriptor.C_ARRAY);
1165: if (methodDescriptor.charAt(i) != VMDescriptor.C_CLASS) {
1166: // an array is a reference, even an array of doubles.
1167: wordCount += 1;
1168: break;
1169: }
1170:
1171: // fall through to skip the Lclassname; after the array.
1172:
1173: case VMDescriptor.C_CLASS:
1174: // skip until ;
1175: do {
1176: i++;
1177: } while (methodDescriptor.charAt(i) != VMDescriptor.C_ENDCLASS);
1178: wordCount += 1;
1179: break;
1180: default:
1181: wordCount += 1;
1182: break;
1183: }
1184: }
1185: }
1186:
1187: /**
1188: * Find the limits of a conditional block starting at the instruction with
1189: * the given opcode at the program counter pc.
1190: * <P>
1191: * Returns a six element integer array of program counters and lengths.
1192: * <code. [0] - program counter of the IF opcode (passed in as pc) [1] -
1193: * program counter of the start of the then block [2] - length of the then
1194: * block [3] - program counter of the else block, -1 if no else block
1195: * exists. [4] - length of of the else block, -1 if no else block exists.
1196: * [5] - program counter of the common end point. </code>
1197: *
1198: * Looks for and handles conditionals that are written by the Conditional
1199: * class.
1200: *
1201: * @return Null if the opcode is not the start of a conditional otherwise
1202: * the array of values.
1203: */
1204: private int[] findConditionalPCs(int pc, short opcode) {
1205: switch (opcode) {
1206: default:
1207: return null;
1208: case VMOpcode.IFNONNULL:
1209: case VMOpcode.IFNULL:
1210: case VMOpcode.IFEQ:
1211: case VMOpcode.IFNE:
1212: break;
1213: }
1214:
1215: int then_pc;
1216: int else_pc;
1217: int if_off = getU2(pc);
1218:
1219: if ((if_off == 8)
1220: && (getOpcode(pc + VMOpcode.IF_INS_LENGTH) == VMOpcode.GOTO_W)) {
1221: // 32 bit branch
1222: then_pc = pc + VMOpcode.IF_INS_LENGTH
1223: + VMOpcode.GOTO_W_INS_LENGTH;
1224:
1225: // Get else PC from the 32 bit offset within the GOTO_W
1226: // instruction remembering to add it to the pc of that
1227: // instruction, not the original IF.
1228: else_pc = pc + VMOpcode.IF_INS_LENGTH
1229: + getU4(pc + VMOpcode.IF_INS_LENGTH);
1230:
1231: } else {
1232: then_pc = pc + VMOpcode.IF_INS_LENGTH;
1233: else_pc = pc + if_off;
1234: }
1235:
1236: // Need to look for the goto or goto_w at the
1237: // end of the then block. There might not be
1238: // one for the case when there is no else block.
1239: // In that case the then block will just run into
1240: // what we currently think the else pc.
1241:
1242: int end_pc = -1;
1243: for (int tpc = then_pc; tpc < else_pc;) {
1244: short opc = getOpcode(tpc);
1245:
1246: // need to handle conditionals
1247: int[] innerCond = findConditionalPCs(tpc, opc);
1248: if (innerCond != null) {
1249: // skip to the end of this conditional
1250: tpc = innerCond[5]; // end_pc
1251: continue;
1252: }
1253:
1254: if (opc == VMOpcode.GOTO) {
1255: // not at the end of the then block
1256: // so not our goto. Shouldn't see this
1257: // with the current code due to the
1258: // skipping of the conditional above.
1259: // But safe defensive programming.
1260: if (tpc != (else_pc - VMOpcode.GOTO_INS_LENGTH))
1261: continue;
1262:
1263: end_pc = tpc + getU2(tpc);
1264: break;
1265: } else if (opc == VMOpcode.GOTO_W) {
1266: // not at the end of the then block
1267: // so not our goto. SHouldn't see this
1268: // with the current code due to the
1269: // skipping of the conditional above.
1270: // But safe defensive programming.
1271: if (tpc != (else_pc - VMOpcode.GOTO_W_INS_LENGTH))
1272: continue;
1273:
1274: end_pc = tpc + getU4(tpc);
1275: break;
1276:
1277: }
1278: tpc += instructionLength(opc);
1279: }
1280:
1281: int else_len;
1282: int then_len;
1283: if (end_pc == -1) {
1284: // no else block;
1285: end_pc = else_pc;
1286: else_pc = -1;
1287:
1288: then_len = end_pc - then_pc;
1289: else_len = -1;
1290: } else {
1291: then_len = else_pc - then_pc;
1292: else_len = end_pc - else_pc;
1293: }
1294:
1295: int[] ret = new int[6];
1296:
1297: ret[0] = pc;
1298: ret[1] = then_pc;
1299: ret[2] = then_len;
1300: ret[3] = else_pc;
1301: ret[4] = else_len;
1302: ret[5] = end_pc;
1303:
1304: return ret;
1305: }
1306:
1307: /**
1308: * Attempt to split the current method by pushing a chunk of
1309: * its code into a sub-method. The starting point of the split
1310: * (split_pc) must correspond to a stack depth of zero. It is the
1311: * reponsibility of the caller to ensure this.
1312: * Split is only made if there exists a chunk of code starting at
1313: * pc=split_pc, whose stack depth upon termination is zero.
1314: * The method will try to split a code section greater than
1315: * optimalMinLength but may split earlier if no such block exists.
1316: * <P>
1317: * The method is aimed at splitting methods that contain
1318: * many independent statements.
1319: * <P>
1320: * If a split is possible this method will perform the split and
1321: * create a void sub method, and move the code into the sub-method
1322: * and setup this method to call the sub-method before continuing.
1323: * This method's max stack and current pc will be correctly set
1324: * as though the method had just been created.
1325: *
1326: * @param mb Method for this chunk.
1327: * @param ch Class definition
1328: * @param optimalMinLength minimum length required for split
1329: */
1330: final int splitZeroStack(BCMethod mb, ClassHolder ch,
1331: final int split_pc, final int optimalMinLength) {
1332:
1333: int splitMinLength = splitMinLength(mb);
1334:
1335: int stack = 0;
1336:
1337: // maximum possible split seen that is less than
1338: // the minimum.
1339: int possibleSplitLength = -1;
1340:
1341: // do not split until at least this point (inclusive)
1342: // used to ensure no split occurs in the middle of
1343: // a conditional.
1344: int outerConditionalEnd_pc = -1;
1345:
1346: int end_pc = getPC(); // pc will be positioned at the end.
1347: for (int pc = split_pc; pc < end_pc;) {
1348:
1349: short opcode = getOpcode(pc);
1350:
1351: int stackDelta = stackWordDelta(ch, pc, opcode);
1352:
1353: stack += stackDelta;
1354:
1355: // Cannot split a conditional but need to calculate
1356: // the stack depth at the end of the conditional.
1357: // Each path through the conditional will have the
1358: // same stack depth.
1359: int[] cond_pcs = findConditionalPCs(pc, opcode);
1360: if (cond_pcs != null) {
1361: // an else block exists, skip the then block.
1362: if (cond_pcs[3] != -1) {
1363: pc = cond_pcs[3];
1364: continue;
1365: }
1366:
1367: if (SanityManager.DEBUG) {
1368: if (outerConditionalEnd_pc != -1) {
1369: if (cond_pcs[5] >= outerConditionalEnd_pc)
1370: SanityManager
1371: .THROWASSERT("NESTED CONDITIONALS!");
1372: }
1373: }
1374:
1375: if (outerConditionalEnd_pc == -1) {
1376: outerConditionalEnd_pc = cond_pcs[5];
1377: }
1378: }
1379:
1380: pc += instructionLength(opcode);
1381:
1382: // Don't split in the middle of a conditional
1383: if (outerConditionalEnd_pc != -1) {
1384: if (pc > outerConditionalEnd_pc) {
1385: // passed the outermost conditional
1386: outerConditionalEnd_pc = -1;
1387: }
1388: continue;
1389: }
1390:
1391: if (stack != 0)
1392: continue;
1393:
1394: int splitLength = pc - split_pc;
1395:
1396: if (splitLength < optimalMinLength) {
1397: // record we do have a possible split.
1398: possibleSplitLength = splitLength;
1399: continue;
1400: }
1401:
1402: // no point splitting to a method bigger
1403: // than the VM can handle. Save one for
1404: // return instruction.
1405: if (splitLength > BCMethod.CODE_SPLIT_LENGTH - 1) {
1406: splitLength = -1;
1407: } else if (CodeChunk.isReturn(opcode)) {
1408: // Don't handle a return in the middle of
1409: // an instruction stream. Don't think this
1410: // is generated, but be safe.
1411: splitLength = -1;
1412: }
1413:
1414: // if splitLenth was set to -1 above then there
1415: // is no possible split at this instruction.
1416: if (splitLength == -1) {
1417: // no earlier split at all
1418: if (possibleSplitLength == -1)
1419: return -1;
1420:
1421: // Decide if the earlier possible split is
1422: // worth it.
1423: if (possibleSplitLength <= splitMinLength)
1424: return -1;
1425:
1426: // OK go with the earlier split
1427: splitLength = possibleSplitLength;
1428:
1429: }
1430:
1431: // Yes, we can split this big method into a smaller method!!
1432:
1433: BCMethod subMethod = startSubMethod(mb, "void", split_pc,
1434: splitLength);
1435:
1436: return splitCodeIntoSubMethod(mb, ch, subMethod, split_pc,
1437: splitLength);
1438: }
1439: return -1;
1440: }
1441:
1442: /**
1443: * Start a sub method that we will split the portion of our current code to,
1444: * starting from start_pc and including codeLength bytes of code.
1445: *
1446: * Return a BCMethod obtained from BCMethod.getNewSubMethod with the passed
1447: * in return type and same parameters as mb if the code block to be moved
1448: * uses parameters.
1449: */
1450: private BCMethod startSubMethod(BCMethod mb, String returnType,
1451: int split_pc, int blockLength) {
1452:
1453: boolean needParameters = usesParameters(mb, split_pc,
1454: blockLength);
1455:
1456: return mb.getNewSubMethod(returnType, needParameters);
1457: }
1458:
1459: /**
1460: * Does a section of code use parameters.
1461: * Any load, exception ALOAD_0 in an instance method, is
1462: * seen as using parameters, as this complete byte code
1463: * implementation does not use local variables.
1464: *
1465: */
1466: private boolean usesParameters(BCMethod mb, int pc, int codeLength) {
1467:
1468: // does the method even have parameters?
1469: if (mb.parameters == null)
1470: return false;
1471:
1472: boolean isStatic = (mb.myEntry.getModifier() & Modifier.STATIC) != 0;
1473:
1474: int endPc = pc + codeLength;
1475:
1476: for (; pc < endPc;) {
1477: short opcode = getOpcode(pc);
1478: switch (opcode) {
1479: case VMOpcode.ILOAD_0:
1480: case VMOpcode.LLOAD_0:
1481: case VMOpcode.FLOAD_0:
1482: case VMOpcode.DLOAD_0:
1483: return true;
1484:
1485: case VMOpcode.ALOAD_0:
1486: if (isStatic)
1487: return true;
1488: break;
1489:
1490: case VMOpcode.ILOAD_1:
1491: case VMOpcode.LLOAD_1:
1492: case VMOpcode.FLOAD_1:
1493: case VMOpcode.DLOAD_1:
1494: case VMOpcode.ALOAD_1:
1495: return true;
1496:
1497: case VMOpcode.ILOAD_2:
1498: case VMOpcode.LLOAD_2:
1499: case VMOpcode.FLOAD_2:
1500: case VMOpcode.DLOAD_2:
1501: case VMOpcode.ALOAD_2:
1502: return true;
1503:
1504: case VMOpcode.ILOAD_3:
1505: case VMOpcode.LLOAD_3:
1506: case VMOpcode.FLOAD_3:
1507: case VMOpcode.DLOAD_3:
1508: case VMOpcode.ALOAD_3:
1509: return true;
1510:
1511: case VMOpcode.ILOAD:
1512: case VMOpcode.LLOAD:
1513: case VMOpcode.FLOAD:
1514: case VMOpcode.DLOAD:
1515: case VMOpcode.ALOAD:
1516: return true;
1517: default:
1518: break;
1519:
1520: }
1521: pc += instructionLength(opcode);
1522: }
1523: return false;
1524: }
1525:
1526: /**
1527: * Split a block of code from this method into a sub-method
1528: * and call it.
1529: *
1530: * Returns the pc of this method just after the call
1531: * to the sub-method.
1532:
1533: * @param mb My method
1534: * @param ch My class
1535: * @param subMethod Sub-method code was pushed into
1536: * @param split_pc Program counter the split started at
1537: * @param splitLength Length of code split
1538: */
1539: private int splitCodeIntoSubMethod(BCMethod mb, ClassHolder ch,
1540: BCMethod subMethod, final int split_pc,
1541: final int splitLength) {
1542: CodeChunk subChunk = subMethod.myCode;
1543:
1544: byte[] codeBytes = cout.getData();
1545:
1546: // the code to be moved into the sub method
1547: // as a block. This will correctly increase the
1548: // program counter.
1549: try {
1550: subChunk.cout.write(codeBytes, CODE_OFFSET + split_pc,
1551: splitLength);
1552: } catch (IOException ioe) {
1553: limitHit(ioe);
1554: }
1555:
1556: // Just cause the sub-method to return,
1557: // fix up its maxStack and then complete it.
1558: if (subMethod.myReturnType.equals("void"))
1559: subChunk.addInstr(VMOpcode.RETURN);
1560: else
1561: subChunk.addInstr(VMOpcode.ARETURN);
1562:
1563: // Finding the max stack requires the class format to
1564: // still be valid. If we have blown the number of constant
1565: // pool entries then we can no longer guarantee that indexes
1566: // into the constant pool in the code stream are valid.
1567: if (cb.limitMsg != null)
1568: return -1;
1569:
1570: subMethod.maxStack = subChunk.findMaxStack(ch, 0, subChunk
1571: .getPC());
1572: subMethod.complete();
1573:
1574: return removePushedCode(mb, ch, subMethod, split_pc,
1575: splitLength);
1576: }
1577:
1578: /**
1579: * Remove a block of code from this method that was pushed into a sub-method
1580: * and call the sub-method.
1581: *
1582: * Returns the pc of this method just after the call to the sub-method.
1583: *
1584: * @param mb
1585: * My method
1586: * @param ch
1587: * My class
1588: * @param subMethod
1589: * Sub-method code was pushed into
1590: * @param split_pc
1591: * Program counter the split started at
1592: * @param splitLength
1593: * Length of code split
1594: */
1595: private int removePushedCode(BCMethod mb, ClassHolder ch,
1596: BCMethod subMethod, final int split_pc,
1597: final int splitLength) {
1598: // now need to fix up this method, create
1599: // a new CodeChunk just to be clearer than
1600: // trying to modify this chunk directly.
1601:
1602: // total length of the code for this method before split
1603: final int codeLength = getPC();
1604:
1605: CodeChunk replaceChunk = new CodeChunk(mb.cb);
1606: mb.myCode = replaceChunk;
1607: mb.maxStack = 0;
1608:
1609: byte[] codeBytes = cout.getData();
1610:
1611: // write any existing code before the split point
1612: // into the replacement chunk.
1613: if (split_pc != 0) {
1614: try {
1615: replaceChunk.cout.write(codeBytes, CODE_OFFSET,
1616: split_pc);
1617: } catch (IOException ioe) {
1618: limitHit(ioe);
1619: }
1620: }
1621:
1622: // Call the sub method, will write into replaceChunk.
1623: mb.callSubMethod(subMethod);
1624:
1625: int postSplit_pc = replaceChunk.getPC();
1626:
1627: // Write the code remaining in this method into the replacement chunk
1628:
1629: int remainingCodePC = split_pc + splitLength;
1630: int remainingCodeLength = codeLength - splitLength - split_pc;
1631:
1632: try {
1633: replaceChunk.cout.write(codeBytes, CODE_OFFSET
1634: + remainingCodePC, remainingCodeLength);
1635: } catch (IOException ioe) {
1636: limitHit(ioe);
1637: }
1638:
1639: // Finding the max stack requires the class format to
1640: // still be valid. If we have blown the number of constant
1641: // pool entries then we can no longer guarantee that indexes
1642: // into the constant pool in the code stream are valid.
1643: if (cb.limitMsg != null)
1644: return -1;
1645:
1646: mb.maxStack = replaceChunk.findMaxStack(ch, 0, replaceChunk
1647: .getPC());
1648:
1649: return postSplit_pc;
1650: }
1651:
1652: /**
1653: * Split an expression out of a large method into its own
1654: * sub-method.
1655: * <P>
1656: * Method call expressions are of the form:
1657: * <UL>
1658: * <LI> expr.method(args) -- instance method call
1659: * <LI> method(args) -- static method call
1660: * </UL>
1661: * Two special cases of instance method calls will be handled
1662: * by the first incarnation of splitExpressionOut.
1663: * three categories:
1664: * <UL>
1665: * <LI>this.method(args)
1666: * <LI>this.getter().method(args)
1667: * </UL>
1668: * These calls are choosen as they are easier sub-cases
1669: * and map to the code generated for SQL statements.
1670: * Future coders can expand the method to cover more cases.
1671: * <P>
1672: * This method will split out such expressions in sub-methods
1673: * and replace the original code with a call to that submethod.
1674: * <UL>
1675: * <LI>this.method(args) ->> this.sub1([parameters])
1676: * <LI>this.getter().method(args) ->> this.sub1([parameters])
1677: * </UL>
1678: * The assumption is of course that the call to the sub-method
1679: * is much smaller than the code it replaces.
1680: * <P>
1681: * Looking at the byte code for such calls they would look like
1682: * (for an example three argument method):
1683: * <code>
1684: * this arg1 arg2 arg3 INVOKE // this.method(args)
1685: * this INVOKE arg1 arg2 arg3 INVOKE // this.getter().metod(args)
1686: * </code>
1687: * The bytecode for the arguments can be arbitary long and
1688: * consist of expressions, typical Derby code for generated
1689: * queries is deeply nested method calls.
1690: * <BR>
1691: * If none of the arguments requred the parameters passed into
1692: * the method, then in both cases the replacement bytecode
1693: * would look like:
1694: * <code>
1695: * this.sub1();
1696: * </code>
1697: * Parameter handling is just as in the method splitZeroStack().
1698: * <P>
1699: * Because the VM is a stack machine the original byte code
1700: * sequences are self contained. The stack at the start of
1701: * is sequence is N and at the end (after the method call) will
1702: * be:
1703: * <UL>
1704: * <LI> N - void method
1705: * <LI> N + 1 - method returning a single word
1706: * <LI> N + 2 - method returning a double word (java long or double)
1707: * </UL>
1708: * This code will handle the N+1 where the word is a reference,
1709: * the typical case for generated code.
1710: * <BR>
1711: * The code is self contained because in general the byte code
1712: * for the arguments will push and pop values but never drop
1713: * below the stack value at the start of the byte code sequence.
1714: * E.g. in the examples the stack before the first arg will be
1715: * N+1 (the objectref for the method call) and at the end of the
1716: * byte code for arg1 will be N+2 or N+3 depending on if arg1 is
1717: * a single or double word argument. During the execution of
1718: * the byte code the stack may have had many arguments pushed
1719: * and popped, but will never have dropped below N+1. Thus the
1720: * code for arg1 is independent of the stack's previous values
1721: * and is self contained. This self-containment then extends to
1722: * all the arguements, the method call itself and pushing the
1723: * objectref for the method call, thus the complete
1724: * sequence is self-contained.
1725: * <BR>
1726: * The self-containment breaks in a few cases, take the simple
1727: * method call this.method(3), the byte code for this could be:
1728: * <code>
1729: * push3 this swap invoke
1730: * </code>
1731: * In this case the byte code for arg1 (swap) is not self-contained
1732: * and relies on earlier stack values.
1733: * <P>
1734: * How to identify "self-contained blocks of code".
1735: * <BR>
1736: * We walk through the byte code and maintain a history of
1737: * the program counter that indicates the start of the
1738: * independent sequence each stack word depends on.
1739: * Thus for a ALOAD_0 instruction which pushes 'this' the
1740: * dependent pc is that of the this. If a DUP instruction followed
1741: * then the top-word is now dependent on the previous word (this)
1742: * and thus the dependence of it is equal to the dependence of
1743: * the previous word. This information is kept in earliestIndepPC
1744: * array as we process the instruction stream.
1745: * <BR>
1746: * When a INVOKE instruction is seen for an instance method
1747: * that returns a single or double word, the dependence of
1748: * the returned value is the dependence of the word in the
1749: * stack that is the objectref for the call. This complete
1750: * sequence from the pc the objectref depended on to the
1751: * INVOKE instruction is then a self contained sequence
1752: * and can be split into a sub-method.
1753:
1754: * <BR>
1755: * If the block is self-contained then it can be split, following
1756: * similar logic to splitZeroStack().
1757: *
1758: * <P>
1759: * WORK IN PROGRESS - Incremental development
1760: * <BR>
1761: * Currently walks the method maintaining the
1762: * earliestIndepPC array and identifies potential blocks
1763: * to splt, performs splits as required.
1764: * Called by BCMethod but commented out in submitted code.
1765: * Tested with local changes from calls in BCMethod.
1766: * Splits generally work, though largeCodeGen shows
1767: * a problem that will be fixed before the code in
1768: * enabled for real.
1769: *
1770: */
1771:
1772: final int splitExpressionOut(final BCMethod mb,
1773: final ClassHolder ch, final int optimalMinLength,
1774: final int maxStack) {
1775: // Save the best block we have seen for splitting out.
1776: int bestSplitPC = -1;
1777: int bestSplitBlockLength = -1;
1778: String bestSplitRT = null;
1779:
1780: int splitMinLength = splitMinLength(mb);
1781:
1782: // Program counter of the earliest instruction
1783: // that the word in the current active stack
1784: // at the given depth depends on.
1785: //
1786: // Some examples, N is the stack depth *after*
1787: // the instruction.
1788: // E.g.
1789: // ALOAD_0 - pushes this, is an instruction that
1790: // pushes an independent value, so the current
1791: // stack word depends on the pc of current instruction.
1792: // earliestIndepPC[N] = pc (that pushed the value).
1793: //
1794: // DUP - duplicates the top word, so the duplicated
1795: // top word will depend on the same pc as the word
1796: // it was duplicated from.
1797: // I.e. earliestIndepPC[N]
1798: // = earliestIndepPC[N-1];
1799: //
1800: // instance method call returning single word value.
1801: // The top word will depend on the same pc as the
1802: // objectref for the method call, which was at the
1803: // same depth in this case.
1804: // earliestIndepPC[N] unchanged
1805: //
1806: // at any time earliestIndepPC is only valid
1807: // from 1 to N where N is the depth of the stack.
1808: int[] earliestIndepPC = new int[maxStack + 1];
1809:
1810: int stack = 0;
1811:
1812: //TODO: this conditional handling is copied from
1813: //the splitZeroStack code, need to check to see
1814: // how it fits with the expression logic.
1815: // do not split until at least this point (inclusive)
1816: // used to ensure no split occurs in the middle of
1817: // a conditional.
1818: int outerConditionalEnd_pc = -1;
1819:
1820: int end_pc = getPC();
1821:
1822: for (int pc = 0; pc < end_pc;) {
1823:
1824: short opcode = getOpcode(pc);
1825:
1826: int stackDelta = stackWordDelta(ch, pc, opcode);
1827:
1828: stack += stackDelta;
1829:
1830: // Cannot split a conditional but need to calculate
1831: // the stack depth at the end of the conditional.
1832: // Each path through the conditional will have the
1833: // same stack depth.
1834: int[] cond_pcs = findConditionalPCs(pc, opcode);
1835: if (cond_pcs != null) {
1836:
1837: // TODO: This conditional handling was copied
1838: // from splitZeroStack, haven't looked in detail
1839: // to see how a conditional should be handled
1840: // with an expression split. So for the time
1841: // being just bail.
1842: if (true)
1843: return -1;
1844:
1845: // an else block exists, skip the then block.
1846: if (cond_pcs[3] != -1) {
1847: pc = cond_pcs[3];
1848: continue;
1849: }
1850:
1851: if (SanityManager.DEBUG) {
1852: if (outerConditionalEnd_pc != -1) {
1853: if (cond_pcs[5] >= outerConditionalEnd_pc)
1854: SanityManager
1855: .THROWASSERT("NESTED CONDITIONALS!");
1856: }
1857: }
1858:
1859: if (outerConditionalEnd_pc == -1) {
1860: outerConditionalEnd_pc = cond_pcs[5];
1861: }
1862: }
1863:
1864: pc += instructionLength(opcode);
1865:
1866: // Don't split in the middle of a conditional
1867: if (outerConditionalEnd_pc != -1) {
1868: if (pc > outerConditionalEnd_pc) {
1869: // passed the outermost conditional
1870: outerConditionalEnd_pc = -1;
1871: }
1872: continue;
1873: }
1874:
1875: int opcode_pc = pc - instructionLength(opcode);
1876: switch (opcode) {
1877: // Any instruction we don't have any information
1878: // on, we simply clear all evidence of independent
1879: // starting points, and start again.
1880: default:
1881: Arrays.fill(earliestIndepPC, 0, stack + 1, -1);
1882: break;
1883:
1884: // Independent instructions do not change the stack depth
1885: // and the independence of the top word picks up
1886: // the independence of the previous word at the same
1887: // position. Ie. no change!
1888: case VMOpcode.ARRAYLENGTH:
1889: case VMOpcode.NOP:
1890: case VMOpcode.CHECKCAST:
1891: case VMOpcode.D2L:
1892: case VMOpcode.DNEG:
1893: case VMOpcode.F2I:
1894: break;
1895:
1896: // Independent instructions that push one word
1897: case VMOpcode.ALOAD_0:
1898: case VMOpcode.ALOAD_1:
1899: case VMOpcode.ALOAD_2:
1900: case VMOpcode.ALOAD_3:
1901: case VMOpcode.ALOAD:
1902: case VMOpcode.ACONST_NULL:
1903: case VMOpcode.BIPUSH:
1904: case VMOpcode.FCONST_0:
1905: case VMOpcode.FCONST_1:
1906: case VMOpcode.FCONST_2:
1907: case VMOpcode.FLOAD:
1908: case VMOpcode.ICONST_0:
1909: case VMOpcode.ICONST_1:
1910: case VMOpcode.ICONST_2:
1911: case VMOpcode.ICONST_3:
1912: case VMOpcode.ICONST_4:
1913: case VMOpcode.ICONST_5:
1914: case VMOpcode.ICONST_M1:
1915: case VMOpcode.LDC:
1916: case VMOpcode.LDC_W:
1917: case VMOpcode.SIPUSH:
1918: earliestIndepPC[stack] = opcode_pc;
1919: break;
1920:
1921: // Independent instructions that push two words
1922: case VMOpcode.DCONST_0:
1923: case VMOpcode.DCONST_1:
1924: case VMOpcode.LCONST_0:
1925: case VMOpcode.LCONST_1:
1926: case VMOpcode.LDC2_W:
1927: case VMOpcode.LLOAD:
1928: case VMOpcode.LLOAD_0:
1929: case VMOpcode.LLOAD_1:
1930: case VMOpcode.LLOAD_2:
1931: case VMOpcode.LLOAD_3:
1932: earliestIndepPC[stack - 1] = earliestIndepPC[stack] = opcode_pc;
1933: break;
1934:
1935: // nothing to do for pop, obviously no
1936: // code will be dependent on the popped words.
1937: case VMOpcode.POP:
1938: case VMOpcode.POP2:
1939: break;
1940:
1941: case VMOpcode.SWAP:
1942: earliestIndepPC[stack] = earliestIndepPC[stack - 1];
1943: break;
1944:
1945: // push a value that depends on the previous value
1946: case VMOpcode.I2L:
1947: earliestIndepPC[stack] = earliestIndepPC[stack - 1];
1948: break;
1949:
1950: case VMOpcode.GETFIELD: {
1951: String vmDescriptor = getTypeDescriptor(ch, opcode_pc);
1952: int width = CodeChunk
1953: .getDescriptorWordCount(vmDescriptor);
1954: if (width == 2)
1955: earliestIndepPC[stack] = earliestIndepPC[stack - 1];
1956:
1957: break;
1958: }
1959:
1960: case VMOpcode.INVOKEINTERFACE:
1961: case VMOpcode.INVOKEVIRTUAL: {
1962: // ...,objectref[,word]*
1963: //
1964: // => ...
1965: // => ...,word
1966: // => ...,word1,word2
1967:
1968: // Width of the value returned by the method call.
1969: String vmDescriptor = getTypeDescriptor(ch, opcode_pc);
1970: int width = CodeChunk
1971: .getDescriptorWordCount(vmDescriptor);
1972:
1973: // Independence of this block is the independence
1974: // of the objectref that invokved the method.
1975: int selfContainedBlockStart;
1976: if (width == 0) {
1977: // objectref was at one more than the current depth
1978: // no plan to split here though, as we are only
1979: // splitting methods that return a reference.
1980: selfContainedBlockStart = -1;
1981: // earliestIndepPC[stack + 1];
1982: } else if (width == 1) {
1983: // stack is unchanged, objectref was at
1984: // the current stack depth
1985: selfContainedBlockStart = earliestIndepPC[stack];
1986: } else {
1987: // width == 2, objectref was one below the
1988: // current stack depth.
1989: // no plan to split here though, as we are only
1990: // splitting methods that return a reference.
1991: selfContainedBlockStart = -1;
1992:
1993: // top two words depend on the objectref
1994: // which was at the same depth of the first word
1995: // of the 64 bit value.
1996: earliestIndepPC[stack] = earliestIndepPC[stack - 1];
1997: }
1998:
1999: if (selfContainedBlockStart != -1) {
2000: int blockLength = pc - selfContainedBlockStart;
2001:
2002: if (blockLength <= splitMinLength) {
2003: // No point splitting, too small
2004: } else if (blockLength > (VMOpcode.MAX_CODE_LENGTH - 1)) {
2005: // too big to split into a single method
2006: // (one for the return opcode)
2007: } else {
2008: // Only split for a method that returns
2009: // an class reference.
2010: int me = vmDescriptor.lastIndexOf(')');
2011:
2012: if (vmDescriptor.charAt(me + 1) == 'L') {
2013: String rt = vmDescriptor.substring(me + 2,
2014: vmDescriptor.length() - 1);
2015:
2016: // convert to external format.
2017: rt = rt.replace('/', '.');
2018:
2019: if (blockLength >= optimalMinLength) {
2020: // Split now!
2021: BCMethod subMethod = startSubMethod(mb,
2022: rt, selfContainedBlockStart,
2023: blockLength);
2024:
2025: return splitCodeIntoSubMethod(mb, ch,
2026: subMethod,
2027: selfContainedBlockStart,
2028: blockLength);
2029: } else if (blockLength > bestSplitBlockLength) {
2030: // Save it, may split at this point
2031: // if nothing better seen.
2032: bestSplitPC = selfContainedBlockStart;
2033: bestSplitBlockLength = blockLength;
2034: bestSplitRT = rt;
2035: }
2036: }
2037: }
2038: }
2039: break;
2040: }
2041: }
2042:
2043: }
2044:
2045: if (bestSplitBlockLength != -1) {
2046: BCMethod subMethod = startSubMethod(mb, bestSplitRT,
2047: bestSplitPC, bestSplitBlockLength);
2048:
2049: return splitCodeIntoSubMethod(mb, ch, subMethod,
2050: bestSplitPC, bestSplitBlockLength);
2051: }
2052:
2053: return -1;
2054: }
2055:
2056: /**
2057: * See if the opcode is a return instruction.
2058: * @param opcode opcode to be checked
2059: * @return true for is a return instruction, false otherwise.
2060: */
2061: private static boolean isReturn(short opcode) {
2062: switch (opcode) {
2063: case VMOpcode.RETURN:
2064: case VMOpcode.ARETURN:
2065: case VMOpcode.IRETURN:
2066: case VMOpcode.FRETURN:
2067: case VMOpcode.DRETURN:
2068: case VMOpcode.LRETURN:
2069: return true;
2070: default:
2071: return false;
2072: }
2073: }
2074:
2075: /**
2076: * Minimum split length for a sub-method. If the number of
2077: * instructions to call the sub-method exceeds the length
2078: * of the sub-method, then there's no point splitting.
2079: * The number of bytes in the code stream to call
2080: * a generated sub-method can take is based upon the number of method args.
2081: * A method can have maximum of 255 words of arguments (section 4.10 JVM spec)
2082: * which in the worst case would be 254 (one-word) parameters
2083: * and this. For a sub-method the arguments will come from the
2084: * parameters to the method, i.e. ALOAD, ILOAD etc.
2085: * <BR>
2086: * This leads to this number of instructions.
2087: * <UL>
2088: * <LI> 4 - 'this' and first 3 parameters have single byte instructions
2089: * <LI> (N-4)*2 - Remaining parameters have two byte instructions
2090: * <LI> 3 for the invoke instruction.
2091: * </UL>
2092: */
2093: private static int splitMinLength(BCMethod mb) {
2094: int min = 1 + 3; // For ALOAD_0 (this) and invoke instruction
2095:
2096: if (mb.parameters != null) {
2097: int paramCount = mb.parameters.length;
2098:
2099: min += paramCount;
2100:
2101: if (paramCount > 3)
2102: min += (paramCount - 3);
2103: }
2104:
2105: return min;
2106: }
2107: /*
2108: final int splitNonZeroStack(BCMethod mb, ClassHolder ch,
2109: final int codeLength, final int optimalMinLength,
2110: int maxStack) {
2111:
2112: // program counter for the instruction that
2113: // made the stack reach the given stack depth.
2114: int[] stack_pcs = new int[maxStack+1];
2115: Arrays.fill(stack_pcs, -1);
2116:
2117: int stack = 0;
2118:
2119: // maximum possible split seen that is less than
2120: // the minimum.
2121: int possibleSplitLength = -1;
2122:
2123: System.out.println("NZ SPLIT + " + mb.getName());
2124:
2125: // do not split until at least this point (inclusive)
2126: // used to ensure no split occurs in the middle of
2127: // a conditional.
2128: int outerConditionalEnd_pc = -1;
2129:
2130: int end_pc = 0 + codeLength;
2131: for (int pc = 0; pc < end_pc;) {
2132:
2133: short opcode = getOpcode(pc);
2134:
2135: int stackDelta = stackWordDelta(ch, pc, opcode);
2136:
2137: stack += stackDelta;
2138:
2139: // Cannot split a conditional but need to calculate
2140: // the stack depth at the end of the conditional.
2141: // Each path through the conditional will have the
2142: // same stack depth.
2143: int[] cond_pcs = findConditionalPCs(pc, opcode);
2144: if (cond_pcs != null) {
2145: // an else block exists, skip the then block.
2146: if (cond_pcs[3] != -1) {
2147: pc = cond_pcs[3];
2148: continue;
2149: }
2150:
2151: if (SanityManager.DEBUG)
2152: {
2153: if (outerConditionalEnd_pc != -1)
2154: {
2155: if (cond_pcs[5] >= outerConditionalEnd_pc)
2156: SanityManager.THROWASSERT("NESTED CONDITIONALS!");
2157: }
2158: }
2159:
2160: if (outerConditionalEnd_pc == -1)
2161: {
2162: outerConditionalEnd_pc = cond_pcs[5];
2163: }
2164: }
2165:
2166: pc += instructionLength(opcode);
2167:
2168: // Don't split in the middle of a conditional
2169: if (outerConditionalEnd_pc != -1) {
2170: if (pc > outerConditionalEnd_pc) {
2171: // passed the outermost conditional
2172: outerConditionalEnd_pc = -1;
2173: }
2174: continue;
2175: }
2176:
2177: if (stackDelta == 0)
2178: continue;
2179:
2180: // Only split when the stack is having items popped
2181: if (stackDelta > 0)
2182: {
2183: // pushing double word, clear out a
2184: if (stackDelta == 2)
2185: stack_pcs[stack - 1] = pc;
2186: stack_pcs[stack] = pc;
2187: continue;
2188: }
2189:
2190: int opcode_pc = pc - instructionLength(opcode);
2191:
2192: // Look for specific opcodes that have the capability
2193: // of having a significant amount of code in a self
2194: // contained block.
2195: switch (opcode)
2196: {
2197: // this.method(A) construct
2198: // ... -- stack N
2199: // push this -- stack N+1
2200: // push args -- stack N+1+A
2201: // call method -- stack N+R (R=0,1,2)
2202: //
2203: // stackDelta = (N+R) - (N+1+A) = R-(1+A)
2204: // stack = N+R
2205: // Need to determine N+1
2206: //
2207: //
2208: //
2209: // this.a(<i2>, <i2>, <i3>)
2210: // returning int
2211: //
2212: // stackDelta = -3 (this & 3 args popped, ret pushed)
2213: // initial depth N = 10
2214: // pc - stack
2215: // 100 ... - stack 10
2216: // 101 push this - stack 11
2217: // 109 push i1 - stack 12
2218: // 125 push i2 - stack 13
2219: // 156 push i3 - stack 14
2220: // 157 call - stack 11
2221: //
2222: // need stack_pcs[11] = stack_pcs[11 + -3]
2223: //
2224: // ref.method(args).method(args) ... method(args)
2225: //
2226: case VMOpcode.INVOKEINTERFACE:
2227: case VMOpcode.INVOKESPECIAL:
2228: case VMOpcode.INVOKEVIRTUAL:
2229: {
2230: String vmDescriptor = getTypeDescriptor(ch, opcode_pc);
2231: int r = CodeChunk.getDescriptorWordCount(vmDescriptor);
2232:
2233: // PC of the opcode that pushed the reference for
2234: // this method call.
2235: int ref_pc = stack_pcs[stack - r + 1];
2236: if (getOpcode(ref_pc) == VMOpcode.ALOAD_0) {
2237: System.out.println("POSS SPLIT " + (pc - ref_pc) + " @ " + ref_pc);
2238: }
2239: break;
2240: }
2241: case VMOpcode.INVOKESTATIC:
2242: String vmDescriptor = getTypeDescriptor(ch, opcode_pc);
2243: int r = CodeChunk.getDescriptorWordCount(vmDescriptor);
2244: int p1_pc = stack_pcs[stack - r + 1];
2245: System.out.println("POSS STATIC SPLIT " + (pc - p1_pc) + " @ " + p1_pc);
2246:
2247: }
2248: stack_pcs[stack] = opcode_pc;
2249: }
2250: return -1;
2251: }*/
2252: }
|