/*

   Derby - Class org.apache.derby.impl.services.bytecode.CodeChunk

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to you under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package org.apache.derby.impl.services.bytecode;

import org.apache.derby.iapi.services.classfile.CONSTANT_Index_info;
import org.apache.derby.iapi.services.classfile.CONSTANT_Utf8_info;
import org.apache.derby.iapi.services.classfile.ClassFormatOutput;
import org.apache.derby.iapi.services.classfile.ClassHolder;
import org.apache.derby.iapi.services.classfile.ClassMember;
import org.apache.derby.iapi.services.classfile.VMDescriptor;

import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.services.classfile.VMOpcode;
import org.apache.derby.iapi.services.io.ArrayOutputStream;

import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.Arrays;

/**
 * This class represents a chunk of code in a CodeAttribute.
 * Typically, a CodeAttribute represents the code in a method.
 * If there is a try/catch block, each catch block will get its
 * own code chunk.  This allows the catch blocks to all be put at
 * the end of the generated code for a method, which eliminates
 * the need to generate a jump around each catch block, which
 * would be a forward reference.
 */
final class CodeChunk {
	
	/**
	 * Starting point of the byte code stream in the underlying stream/array.
	 */
	private static final int CODE_OFFSET = 8;
		
	// The use of ILOAD for the non-integer types is correct.
	// We have to assume that the appropriate checks/conversions
	// are defined on math operation results to ensure that
	// the type is preserved when/as needed.
	static final short[] LOAD_VARIABLE = {
		VMOpcode.ILOAD,	/* vm_byte */
		VMOpcode.ILOAD,	/* vm_short */
		VMOpcode.ILOAD,	/* vm_int */
		VMOpcode.LLOAD,	/* vm_long */
		VMOpcode.FLOAD,	/* vm_float */
		VMOpcode.DLOAD,	/* vm_double */
		VMOpcode.ILOAD,	/* vm_char */
		VMOpcode.ALOAD	/* vm_reference */
	};

	static final short[] LOAD_VARIABLE_FAST = {
		VMOpcode.ILOAD_0,	/* vm_byte */
		VMOpcode.ILOAD_0,	/* vm_short */
		VMOpcode.ILOAD_0,	/* vm_int */
		VMOpcode.LLOAD_0,	/* vm_long */
		VMOpcode.FLOAD_0,	/* vm_float */
		VMOpcode.DLOAD_0,	/* vm_double */
		VMOpcode.ILOAD_0,	/* vm_char */
		VMOpcode.ALOAD_0	/* vm_reference */
	};

	// The ISTOREs for non-int types are how things work.
	// It assumes that the appropriate casts are done
	// on operations on non-ints to ensure that the values
	// remain in the valid ranges.
	static final short[] STORE_VARIABLE = {
		VMOpcode.ISTORE,	/* vm_byte */
		VMOpcode.ISTORE,	/* vm_short */
		VMOpcode.ISTORE,	/* vm_int */
		VMOpcode.LSTORE,	/* vm_long */
		VMOpcode.FSTORE,	/* vm_float */
		VMOpcode.DSTORE,	/* vm_double */
		VMOpcode.ISTORE,	/* vm_char */
		VMOpcode.ASTORE	/* vm_reference */
	};

	static final short[] STORE_VARIABLE_FAST = {
		VMOpcode.ISTORE_0,	/* vm_byte */
		VMOpcode.ISTORE_0,	/* vm_short */
		VMOpcode.ISTORE_0,	/* vm_int */
		VMOpcode.LSTORE_0,	/* vm_long */
		VMOpcode.FSTORE_0,	/* vm_float */
		VMOpcode.DSTORE_0,	/* vm_double */
		VMOpcode.ISTORE_0,	/* vm_char */
		VMOpcode.ASTORE_0	/* vm_reference */
	};

	static final short ARRAY_ACCESS[] = {
		VMOpcode.BALOAD,	/* vm_byte */
		VMOpcode.SALOAD,	/* vm_short */
		VMOpcode.IALOAD,	/* vm_int */
		VMOpcode.LALOAD,	/* vm_long */
		VMOpcode.FALOAD,	/* vm_float */
		VMOpcode.DALOAD,	/* vm_double */
		VMOpcode.CALOAD,	/* vm_char */
		VMOpcode.AALOAD	/* vm_reference */
	};
	static final short ARRAY_STORE[] = {
		VMOpcode.BASTORE,	/* vm_byte */
		VMOpcode.SASTORE,	/* vm_short */
		VMOpcode.IASTORE,	/* vm_int */
		VMOpcode.LASTORE,	/* vm_long */
		VMOpcode.FASTORE,	/* vm_float */
		VMOpcode.DASTORE,	/* vm_double */
		VMOpcode.CASTORE,	/* vm_char */
		VMOpcode.AASTORE	/* vm_reference */
	};	
	static final short[] RETURN_OPCODE = {
		VMOpcode.IRETURN,  /* 0 = byte      */
		VMOpcode.IRETURN,  /* 1 = short     */
		VMOpcode.IRETURN,  /* 2 = int       */
		VMOpcode.LRETURN,  /* 3 = long      */
		VMOpcode.FRETURN,  /* 4 = float     */
		VMOpcode.DRETURN,  /* 5 = double    */
		VMOpcode.IRETURN,  /* 6 = char      */
		VMOpcode.ARETURN   /* 7 = reference */
		};

	// the first dimension is the current vmTypeId
	// the second dimension is the target vmTypeId
	//
	// the cells of the entry at [current,target] are:
	// 0: operation
	// 1: result type of operation
	// if entry[1] = target, we are done. otherwise,
	// you have to continue with entry[1] as the new current
	// after generating the opcode listed (don't generate if it is NOP).
	// if entry[0] = BAD, we can
	
	static final short CAST_CONVERSION_INFO[][][] = {
		/* current = vm_byte */
		{
		/* target = vm_byte      */ { VMOpcode.NOP, BCExpr.vm_byte },
		/* target = vm_short     */ { VMOpcode.NOP, BCExpr.vm_short },
		/* target = vm_int       */ { VMOpcode.NOP, BCExpr.vm_int },
		/* target = vm_long      */ { VMOpcode.NOP, BCExpr.vm_int },
		/* target = vm_float     */ { VMOpcode.NOP, BCExpr.vm_int },
		/* target = vm_double    */ { VMOpcode.NOP, BCExpr.vm_int },
		/* target = vm_char      */ { VMOpcode.NOP, BCExpr.vm_char }, 
		/* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }
		},
		/* current = vm_short */
		{
		/* target = vm_byte      */ { VMOpcode.NOP, BCExpr.vm_byte },
		/* target = vm_short     */ { VMOpcode.NOP, BCExpr.vm_short },
		/* target = vm_int       */ { VMOpcode.NOP, BCExpr.vm_int },
		/* target = vm_long      */ { VMOpcode.NOP, BCExpr.vm_int },
		/* target = vm_float     */ { VMOpcode.NOP, BCExpr.vm_int },
		/* target = vm_double    */ { VMOpcode.NOP, BCExpr.vm_int },
		/* target = vm_char      */ { VMOpcode.NOP, BCExpr.vm_char }, 
		/* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }
		},
		/* current = vm_int */
		{
		/* target = vm_byte      */ { VMOpcode.I2B, BCExpr.vm_byte },
		/* target = vm_short     */ { VMOpcode.I2S, BCExpr.vm_short },
		/* target = vm_int       */ { VMOpcode.NOP, BCExpr.vm_int },
		/* target = vm_long      */ { VMOpcode.I2L, BCExpr.vm_long },
		/* target = vm_float     */ { VMOpcode.I2F, BCExpr.vm_float },
		/* target = vm_double    */ { VMOpcode.I2D, BCExpr.vm_double },
		/* target = vm_char      */ { VMOpcode.I2B, BCExpr.vm_char }, 
		/* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }
		},
		/* current = vm_long */
		{
		/* target = vm_byte      */ { VMOpcode.L2I, BCExpr.vm_int },
		/* target = vm_short     */ { VMOpcode.L2I, BCExpr.vm_int },
		/* target = vm_int       */ { VMOpcode.L2I, BCExpr.vm_int },
		/* target = vm_long      */ { VMOpcode.NOP, BCExpr.vm_long },
		/* target = vm_float     */ { VMOpcode.L2F, BCExpr.vm_float },
		/* target = vm_double    */ { VMOpcode.L2D, BCExpr.vm_double },
		/* target = vm_char      */ { VMOpcode.L2I, BCExpr.vm_int }, 
		/* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }
		},
		/* current = vm_float */
		{
		/* target = vm_byte      */ { VMOpcode.F2I, BCExpr.vm_int },
		/* target = vm_short     */ { VMOpcode.F2I, BCExpr.vm_int },
		/* target = vm_int       */ { VMOpcode.F2I, BCExpr.vm_int },
		/* target = vm_long      */ { VMOpcode.F2L, BCExpr.vm_long },
		/* target = vm_float     */ { VMOpcode.NOP, BCExpr.vm_float },
		/* target = vm_double    */ { VMOpcode.F2D, BCExpr.vm_double },
		/* target = vm_char      */ { VMOpcode.F2I, BCExpr.vm_int }, 
		/* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }
		},
		/* current = vm_double */
		{
		/* target = vm_byte      */ { VMOpcode.D2I, BCExpr.vm_int },
		/* target = vm_short     */ { VMOpcode.D2I, BCExpr.vm_int },
		/* target = vm_int       */ { VMOpcode.D2I, BCExpr.vm_int },
		/* target = vm_long      */ { VMOpcode.D2L, BCExpr.vm_long },
		/* target = vm_float     */ { VMOpcode.D2F, BCExpr.vm_float },
		/* target = vm_double    */ { VMOpcode.NOP, BCExpr.vm_double },
		/* target = vm_char      */ { VMOpcode.D2I, BCExpr.vm_int }, 
		/* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }
		},
		/* current = vm_char */
		{
		/* target = vm_byte      */ { VMOpcode.NOP, BCExpr.vm_byte },
		/* target = vm_short     */ { VMOpcode.NOP, BCExpr.vm_short },
		/* target = vm_int       */ { VMOpcode.NOP, BCExpr.vm_int },
		/* target = vm_long      */ { VMOpcode.NOP, BCExpr.vm_int },
		/* target = vm_float     */ { VMOpcode.NOP, BCExpr.vm_int },
		/* target = vm_double    */ { VMOpcode.NOP, BCExpr.vm_int },
		/* target = vm_char      */ { VMOpcode.NOP, BCExpr.vm_char },
		/* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference }
		},
		/* current = vm_reference */
		{
		/* target = vm_byte      */ { VMOpcode.BAD, BCExpr.vm_byte },
		/* target = vm_short     */ { VMOpcode.BAD, BCExpr.vm_short },
		/* target = vm_int       */ { VMOpcode.BAD, BCExpr.vm_int },
		/* target = vm_long      */ { VMOpcode.BAD, BCExpr.vm_long },
		/* target = vm_float     */ { VMOpcode.BAD, BCExpr.vm_float },
		/* target = vm_double    */ { VMOpcode.BAD, BCExpr.vm_double },
		/* target = vm_char      */ { VMOpcode.BAD, BCExpr.vm_char },
		/* target = vm_reference */ { VMOpcode.NOP, BCExpr.vm_reference }
		}
	};
	
	/**
	 * Constant used by OPCODE_ACTION to represent the
	 * common action of push one word, 1 byte
	 * for the instruction.
	 */
	private static final byte[] push1_1i = {1, 1};

	/**
	 * Constant used by OPCODE_ACTION to represent the
	 * common action of push two words, 1 byte
	 * for the instruction.
	 */
	private static final byte[] push2_1i = {2, 1};	
	/**
	 * Constant used by OPCODE_ACTION to the opcode is
	 * not yet supported.
	 */
	private static final byte[] NS = {0, -1};
	
	
	/**
	 * Value for OPCODE_ACTION[opcode][0] to represent
	 * the number of words popped or pushed in variable.
	 */
	private static final byte VARIABLE_STACK = -128;
	
	/**
	 * Array that provides two pieces of information about
	 * each VM opcode. Each opcode has a two byte array.
	 * <P>
	 * The first element in the array [0] is the number of
	 * stack words (double/long count as two) pushed by the opcode.
	 * Will be negative if the opcode pops values.
	 * 
	 * <P>
	 * The second element in the array [1] is the number of bytes
	 * in the instruction stream that this opcode's instruction
	 * takes up, including the opocode.
	 */
	private static final byte[][] OPCODE_ACTION =
	{
	
    /* NOP  0 */           { 0, 1 },
    
    /* ACONST_NULL  1 */  push1_1i,
    /* ICONST_M1  2 */    push1_1i,
    /* ICONST_0  3 */     push1_1i,
    /* ICONST_1  4 */     push1_1i,
    /* ICONST_2  5 */     push1_1i,
    /* ICONST_3  6 */     push1_1i,
    /* ICONST_4  7 */     push1_1i,
    /* ICONST_5  8 */     push1_1i,
    /* LCONST_0  9 */     push2_1i,
    /* LCONST_1  10 */    push2_1i,
    /* FCONST_0  11 */    push1_1i,
    /* FCONST_1  12 */    push1_1i,
    /* FCONST_2  13 */    push1_1i,
    /* DCONST_0  14 */    push2_1i,
    /* DCONST_1  15 */    push2_1i,
    
    /* BIPUSH  16 */     {1, 2},
    /* SIPUSH  17 */     {1, 3},
    /* LDC  18 */        {1, 2},
    /* LDC_W  19 */      {1, 3},
    /* LDC2_W  20 */     {2, 3},
    
    /* ILOAD  21 */     { 1, 2 },
    /* LLOAD  22 */     { 2, 2 },
    /* FLOAD  23 */     { 1, 2 },
    /* DLOAD  24 */     { 2, 2 },
    /* ALOAD  25 */     { 1, 2 },
    /* ILOAD_0  26 */   push1_1i,
    /* ILOAD_1  27 */   push1_1i,
    /* ILOAD_2  28 */   push1_1i,
    /* ILOAD_3  29 */   push1_1i,
    /* LLOAD_0  30 */   push2_1i,
    /* LLOAD_1  31 */   push2_1i,
    /* LLOAD_2  32 */   push2_1i,
    /* LLOAD_3  33 */   push2_1i,
    /* FLOAD_0  34 */   push1_1i,
    /* FLOAD_1  35 */   push1_1i,
    /* FLOAD_2  36 */   push1_1i,
    /* FLOAD_3  37 */   push1_1i,
    /* DLOAD_0  38 */   push2_1i,
    /* DLOAD_1  39 */   push2_1i,
    /* DLOAD_2  40 */   push2_1i,
    /* DLOAD_3  41 */   push2_1i,
    /* ALOAD_0  42 */   push1_1i,
    /* ALOAD_1  43 */   push1_1i,
    /* ALOAD_2  44 */   push1_1i,
    /* ALOAD_3  45 */   push1_1i,
    /* IALOAD  46 */    { -1, 1 },
    /* LALOAD  47 */    { 0, 1 },
    /* FALOAD  48 */    { -1, 1 },
    /* DALOAD  49 */    { 0, 1 },
    /* AALOAD  50 */    { -1, 1 },
    /* BALOAD  51 */    { -1, 1 },
    /* CALOAD  52 */    { -1, 1 },
    
    /* SALOAD  53 */       { -1, 1 },
    /* ISTORE  54 */       { -1, 2 },
    /* LSTORE  55 */       { -2, 2 },
    /* FSTORE  56 */       { -1, 2 },
    /* DSTORE  57 */       { -2, 2 },
    /* ASTORE  58 */       { -1, 2 },
    /* ISTORE_0  59 */     { -1, 1 },
    /* ISTORE_1  60 */     { -1, 1 },
    /* ISTORE_2  61 */     { -1, 1 },
    /* ISTORE_3  62 */     { -1, 1 },
    /* LSTORE_0  63 */     { -2, 1 },
    /* LSTORE_1  64 */     { -2, 1 },
    /* LSTORE_2  65 */     { -2, 1 },
    /* LSTORE_3  66 */     { -2, 1 },
    /* FSTORE_0  67 */     { -1, 1 },
    /* FSTORE_1  68 */     { -1, 1 },
    /* FSTORE_2  69 */     { -1, 1 },
    /* FSTORE_3  70 */     { -1, 1 },
    /* DSTORE_0  71 */     { -2, 1 },
    /* DSTORE_1  72 */     { -2, 1 },
    /* DSTORE_2  73 */     { -2, 1 },
    /* DSTORE_3  74 */     { -2, 1 },
    /* ASTORE_0  75 */     { -1, 1 },
    /* ASTORE_1  76 */     { -1, 1 },
    /* ASTORE_2  77 */     { -1, 1 },
    /* ASTORE_3  78 */     { -1, 1 },
    /* IASTORE  79 */      { -3, 1 },
    /* LASTORE  80 */      { -4, 1 },
    /* FASTORE  81 */      { -3, 1 },
    /* DASTORE  82 */      { -4, 1 },
    /* AASTORE  83 */      { -3, 1 },
    /* BASTORE  84 */      { -3, 1 },
    /* CASTORE  85 */      { -3, 1 },
    /* SASTORE  86 */      { -3, 1 },
    
    /* POP  87 */      { -1, 1 },
    /* POP2  88 */     { -2, 1 },
    /* DUP  89 */      push1_1i,
    /* DUP_X1  90 */   push1_1i,
    /* DUP_X2  91 */   push1_1i,
    /* DUP2  92 */     push2_1i,
    /* DUP2_X1  93 */  push2_1i,
    /* DUP2_X2  94 */  push2_1i,
    /* SWAP  95 */     { 0, 1 },
    
    /* IADD  96 */     NS,
    /* LADD  97 */     NS,
    /* FADD  98 */     { -1, 1 },
    /* DADD  99 */     { -2, 1 },
    /* ISUB  100 */     NS,
    /* LSUB  101 */     NS,
    /* FSUB  102 */     { -1, 1 },
    /* DSUB  103 */     { -2, 1 },
    /* IMUL  104 */     NS,
    /* LMUL  105 */     NS,
    /* FMUL  106 */     { -1, 1 },
    /* DMUL  107 */     { -2, 1 },
    /* IDIV  108 */     NS,
    /* LDIV  109 */     NS,
    /* FDIV  110 */     { -1, 1 },
    /* DDIV  111 */     { -2, 1 },
    /* IREM  112 */     { -1, 1 },
    /* LREM  113 */     { -2, 1 },
    /* FREM  114 */     { -1, 1 },
    /* DREM  115 */     { -2, 1 },
    /* INEG  116 */     { 0, 1 },
    /* LNEG  117 */     { 0, 1 },
    /* FNEG  118 */     { 0, 1 },
    /* DNEG  119 */     { 0, 1 },
    /* ISHL  120 */     { -1, 1 },
    /* LSHL  121 */     NS,
    /* ISHR  122 */     NS,
    /* LSHR  123 */     NS,
    /* IUSHR  124 */     NS,
    /* LUSHR  125 */     NS,
    
    /* IAND  126 */     { -1, 1 },
    /* LAND  127 */     NS,
    /* IOR  128 */      { -1, 1 },
    /* LOR  129 */      NS,
    /* IXOR  130 */     NS,
    /* LXOR  131 */     NS,
    /* IINC  132 */     NS,
    
    /* I2L  133 */     push1_1i,
    /* I2F  134 */     { 0, 1 },
    /* I2D  135 */     push1_1i,
    /* L2I  136 */     { -1, 1 },
    /* L2F  137 */     { -1, 1 },
    /* L2D  138 */     { 0, 1 },
    /* F2I  139 */     { 0, 1 },
    /* F2L  140 */     push2_1i,
    /* F2D  141 */     push1_1i,
    /* D2I  142 */     { -1, 1 },
    /* D2L  143 */     { 0, 1 },
    /* D2F  144 */     { -1, 1 },
    /* I2B  145 */     { 0, 1 },
    /* I2C  146 */     { 0, 1 },
    /* I2S  147 */     { 0, 1 },
    
    /* LCMP  148 */        NS,
    /* FCMPL  149 */       { -1, 1 },
    /* FCMPG  150 */       { -1, 1 },
    /* DCMPL  151 */       { -3, 1 },
    /* DCMPG  152 */       { -3, 1 },
    /* IFEQ  153 */        { -1, VMOpcode.IF_INS_LENGTH },
    /* IFNE  154 */        { -1, VMOpcode.IF_INS_LENGTH },
    /* IFLT  155 */        { -1, VMOpcode.IF_INS_LENGTH },
    /* IFGE  156 */        { -1, VMOpcode.IF_INS_LENGTH },
    /* IFGT  157 */        { -1, VMOpcode.IF_INS_LENGTH },
    /* IFLE  158 */        { -1, VMOpcode.IF_INS_LENGTH },
    /* IF_ICMPEQ  159 */   NS,
    /* IF_ICMPNE  160 */   NS,
    /* IF_ICMPLT  161 */   NS,
    /* IF_ICMPGE  162 */   NS,
    /* IF_ICMPGT  163 */   NS,
    /* IF_ICMPLE  164 */   NS,
    /* IF_ACMPEQ  165 */   NS,
    /* IF_ACMPNE  166 */   NS,
    /* GOTO  167 */        { 0, VMOpcode.GOTO_INS_LENGTH },
    /* JSR  168 */         NS,
    /* RET  169 */         NS,
    /* TABLESWITCH  170 */ NS,
    /* LOOKUPSWITCH  171 */NS,
    
    /* IRETURN  172 */     { -1, 1 }, // strictly speaking all words on the stack are popped.
    /* LRETURN  173 */     { -2, 1 }, // strictly speaking all words on the stack are popped.
    /* FRETURN  174 */     { -1, 1 }, // strictly speaking all words on the stack are popped.
    /* DRETURN  175 */     { -2, 1 }, // strictly speaking all words on the stack are popped.
    /* ARETURN  176 */     { -1, 1 }, // strictly speaking all words on the stack are popped.
    /* RETURN  177 */      { 0, 1 }, // strictly speaking all words on the stack are popped.

    /* GETSTATIC  178 */           {VARIABLE_STACK, 3 },
    /* PUTSTATIC  179 */           {VARIABLE_STACK, 3 },
    /* GETFIELD  180 */            {VARIABLE_STACK, 3 },
    /* PUTFIELD  181 */            {VARIABLE_STACK, 3 },
    /* INVOKEVIRTUAL  182 */       {VARIABLE_STACK, 3 },
    /* INVOKESPECIAL  183 */       {VARIABLE_STACK, 3 },
    /* INVOKESTATIC  184 */        {VARIABLE_STACK, 3 },
    /* INVOKEINTERFACE  185 */     {VARIABLE_STACK, 5 },
    
    /* XXXUNUSEDXXX  186 */        NS,

    /* NEW  187 */                 { 1, 3 },
    /* NEWARRAY  188 */            { 0, 2 },
    /* ANEWARRAY  189 */           { 0, 3 },
    /* ARRAYLENGTH  190 */         { 0, 1 },
    /* ATHROW  191 */              NS,
    /* CHECKCAST  192 */           { 0, 3},
    /* INSTANCEOF  193 */          { 0, 3 },
    /* MONITORENTER  194 */        NS,
    /* MONITOREXIT  195 */         NS,
    /* WIDE  196 */                NS,
    /* MULTIANEWARRAY  197 */      NS,
    /* IFNULL  198 */              { -1, VMOpcode.IF_INS_LENGTH },
    /* IFNONNULL  199 */           { -1, VMOpcode.IF_INS_LENGTH },
    /* GOTO_W  200 */              {0, VMOpcode.GOTO_W_INS_LENGTH },
    /* JSR_W  201 */               NS,
    /* BREAKPOINT  202 */          NS,
	
	};
	
    /**
     * Assume an IOException means some limit of the class file
     * format was hit
     * 
     */
    private void limitHit(IOException ioe)
    {
        cb.addLimitExceeded(ioe.toString());
    }
	
	
	/**
	 * Add an instruction that has no operand.
	 * All opcodes are 1 byte large.
	 */
	void addInstr(short opcode) {
		try {
		cout.putU1(opcode);
		} catch (IOException ioe) {
            limitHit(ioe);
		}

		if (SanityManager.DEBUG) {			
			if (OPCODE_ACTION[opcode][1] != 1)
				SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" +
						" writing 1 byte - set as " + OPCODE_ACTION[opcode][1]);		
		}
	}

	/**
	 * Add an instruction that has a 16 bit operand.
	 */
	void addInstrU2(short opcode, int operand) {
        
		try {
		cout.putU1(opcode);
		cout.putU2(operand);
		} catch (IOException ioe) {
            limitHit(ioe);
		}

		if (SanityManager.DEBUG) {			
			if (OPCODE_ACTION[opcode][1] != 3)
				SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" +
						" writing 3 bytes - set as " + OPCODE_ACTION[opcode][1]);		
		}
	}

	/**
	 * Add an instruction that has a 32 bit operand.
	 */
     void addInstrU4(short opcode, int operand) {
		try {
		cout.putU1(opcode);
		cout.putU4(operand);
		} catch (IOException ioe) {
            limitHit(ioe);
		}
		if (SanityManager.DEBUG) {			
			if (OPCODE_ACTION[opcode][1] != 5)
				SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" +
						" writing 5 bytes - set as " + OPCODE_ACTION[opcode][1]);		
		}
	}

     
 	/**
 	 * Add an instruction that has an 8 bit operand.
 	 */
     void addInstrU1(short opcode, int operand) {
		try {
		cout.putU1(opcode);
		cout.putU1(operand);
		} catch (IOException ioe) {
            limitHit(ioe);
		}

		// Only debug code from here.
		if (SanityManager.DEBUG) {
			
			if (OPCODE_ACTION[opcode][1] != 2)
				SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" +
						" writing 2 bytes - set as " + OPCODE_ACTION[opcode][1]);
		
		}
	}

	/**
	 * This takes an instruction that has a narrow
	 * and a wide form for CPE access, and
	 * generates accordingly the right one.
	 * We assume the narrow instruction is what
	 * we were given, and that the wide form is
	 * the next possible instruction.
	 */
	void addInstrCPE(short opcode, int cpeNum) {
		if (cpeNum < 256) {
			addInstrU1(opcode, cpeNum);
		}
		else {
			addInstrU2((short) (opcode+1), cpeNum);
		}
	}

	/**
	 * This takes an instruction that can be wrapped in
	 * a wide for large variable #s and does so.
	 */
	void addInstrWide(short opcode, int varNum) {
		if (varNum < 256) {
			addInstrU1(opcode, varNum);
		}
		else {
			addInstr(VMOpcode.WIDE);
			addInstrU2(opcode, varNum);
		}
	}

	/**
	 * For adding an instruction with 3 operands, a U2 and two U1's.
	 * So far, this is used by VMOpcode.INVOKEINTERFACE.
	 */
	void addInstrU2U1U1(short opcode, int operand1, short operand2,
		short operand3) {
		try {
		cout.putU1(opcode);
		cout.putU2(operand1);
		cout.putU1(operand2);
		cout.putU1(operand3);
		} catch (IOException ioe) {
            limitHit(ioe);
		}
		if (SanityManager.DEBUG) {			
			if (OPCODE_ACTION[opcode][1] != 5)
				SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" +
						" writing 5 bytes - set as " + OPCODE_ACTION[opcode][1]);		
		}
	}

	/** Get the current program counter */
	int getPC() {
		return cout.size() + pcDelta;
	}
	
	/**
	 * Return the complete instruction length for the
	 * passed in opcode. This will include the space for
	 * the opcode and its operand.
	 */
	private static int instructionLength(short opcode)
	{
		int instructionLength = OPCODE_ACTION[opcode][1];
		
		if (SanityManager.DEBUG)
		{
			if (instructionLength < 0)
				SanityManager.THROWASSERT("Opcode without instruction length " + opcode);
		}
		
		return instructionLength;
	}
	
	/**
	 * The delta between cout.size() and the pc.
	 * For an initial code chunk this is -8 (CODE_OFFSET)
	 * since 8 bytes are written.
	 * For a nested CodeChunk return by insertCodeSpace the delta
	 * corresponds to the original starting pc.
	 * @see #insertCodeSpace
	 */
	private final int pcDelta;
    
    /**
     * The class we are generating code for, used to indicate that
     * some limit was hit during code generation.
     */
    final BCClass       cb;

	CodeChunk(BCClass cb) {
        this.cb = cb;
        cout = new ClassFormatOutput();
		try {
			cout.putU2(0); // max_stack, placeholder for now
			cout.putU2(0); // max_locals, placeholder for now
			cout.putU4(0); // code_length, placeholder 4 now
		} catch (IOException ioe) {
            limitHit(ioe);
		}
		pcDelta = - CodeChunk.CODE_OFFSET;
	}
	
	/**
	 * Return a CodeChunk that has limited visibility into
	 * this CodeChunk. Used when a caller needs to insert instructions
	 * into an existing stream.
	 * @param pc
	 * @param byteCount
	 */
	private CodeChunk(CodeChunk main, int pc, int byteCount)
	{
        this.cb = main.cb;
		ArrayOutputStream aos =
			new ArrayOutputStream(main.cout.getData());
		
		try {
			aos.setPosition(CODE_OFFSET + pc);
			aos.setLimit(byteCount);
		} catch (IOException e) {
            limitHit(e);
		}
		
		cout = new ClassFormatOutput(aos);
		pcDelta = pc;
	}

	private final ClassFormatOutput cout;

	/**
	 * now that we have codeBytes, fix the lengths fields in it
	 * to reflect what was stored.
	 * Limits checked here are from these sections of the JVM spec.
	 * <UL>
	 * <LI> 4.7.3 The Code Attribute
	 * <LI> 4.10 Limitations of the Java Virtual Machine 
	 * </UL>
	 */
	private void fixLengths(BCMethod mb, int maxStack, int maxLocals, int codeLength) {

		byte[] codeBytes = cout.getData();

		// max_stack is in bytes 0-1
		if (mb != null && maxStack > 65535)
			cb.addLimitExceeded(mb, "max_stack", 65535, maxStack);
			
		codeBytes[0] = (byte)(maxStack >> 8 );
		codeBytes[1] = (byte)(maxStack );

		// max_locals is in bytes 2-3
		if (mb != null && maxLocals > 65535)
			cb.addLimitExceeded(mb, "max_locals", 65535, maxLocals);
		codeBytes[2] = (byte)(maxLocals >> 8 );
		codeBytes[3] = (byte)(maxLocals );

		// code_length is in bytes 4-7
		if (mb != null && codeLength > VMOpcode.MAX_CODE_LENGTH)
			cb.addLimitExceeded(mb, "code_length",
					VMOpcode.MAX_CODE_LENGTH, codeLength);
		codeBytes[4] = (byte)(codeLength >> 24 );
		codeBytes[5] = (byte)(codeLength >> 16 );
		codeBytes[6] = (byte)(codeLength >> 8 );
		codeBytes[7] = (byte)(codeLength );			
	}

	/**
	 * wrap up the entry and stuff it in the class,
	 * now that it holds all of the instructions and
	 * the exception table.
	 */
	void complete(BCMethod mb, ClassHolder ch,
			ClassMember method, int maxStack, int maxLocals) {

        int codeLength =  getPC();

		ClassFormatOutput out = cout;
		
		try {

			out.putU2(0); // exception_table_length

			if (SanityManager.DEBUG) {
			  if (SanityManager.DEBUG_ON("ClassLineNumbers")) {
				// Add a single attribute - LineNumberTable
				// This add fake line numbers that are the pc offset in the method.
				out.putU2(1); // attributes_count

				int cpiUTF = ch.addUtf8("LineNumberTable");

				out.putU2(cpiUTF);
				out.putU4((codeLength * 4) + 2);
				out.putU2(codeLength);
				for (int i = 0; i < codeLength; i++) {
					out.putU2(i);
					out.putU2(i);
				}
			  } else {
				  out.putU2(0); // attributes_count
			  }

			} else {
				out.putU2(0); // attributes_count
				// attributes is empty, a 0-element array.
			}
		} catch (IOException ioe) {
            limitHit(ioe);
		}

		fixLengths(mb, maxStack, maxLocals, codeLength);
		method.addAttribute("Code", out);
		
		if (SanityManager.DEBUG)
		{
            // Only validate if the class file format is valid.
            // Ok code length and guaranteed no errors building the class.
            if ((codeLength <= VMOpcode.MAX_CODE_LENGTH)
                && (mb != null && mb.cb.limitMsg == null))
            {              
				// Validate the alternate way to calculate the
				// max stack agrees with the dynamic as the code
				// is built way.
				int walkedMaxStack = findMaxStack(ch, 0, codeLength);
				if (walkedMaxStack != maxStack)
				{
					SanityManager.THROWASSERT("MAX STACK MISMATCH!! " +
							maxStack + " <> " + walkedMaxStack);
				}
			}
		}

	}
	/**
	 * Return the opcode at the given pc.
	 */
	short getOpcode(int pc)
	{
		return (short) (cout.getData()[CODE_OFFSET + pc] & 0xff);
	}
	
	/**
	 * Get the unsigned short value for the opcode at the program
	 * counter pc.
	 */
	private int getU2(int pc)
	{
		byte[] codeBytes = cout.getData();
		
		int u2p = CODE_OFFSET + pc + 1;
        		
		return ((codeBytes[u2p] & 0xff) << 8) | (codeBytes[u2p+1] & 0xff);
	}

	/**
	 * Get the unsigned 32 bit value for the opcode at the program
	 * counter pc.
	 */
	private int getU4(int pc)
	{
		byte[] codeBytes = cout.getData();
		
		int u4p = CODE_OFFSET + pc + 1;
		
		return (((codeBytes[u4p] & 0xff) << 24) |
		        ((codeBytes[u4p+1] & 0xff) << 16) |
		        ((codeBytes[u4p+2] & 0xff) << 8) |
		        ((codeBytes[u4p+3] & 0xff)));
	}	
	/**
	 * Insert room for byteCount bytes after the instruction at pc
	 * and prepare to replace the instruction at pc. The instruction
	 * at pc is not modified by this call, space is allocated after it.
	 * The newly inserted space will be filled with NOP instructions.
	 * 
	 * Returns a CodeChunk positioned at pc and available to write
	 * instructions upto (byteCode + length(existing instruction at pc) bytes.
	 * 
	 * This chunk is left correctly positioned at the end of the code
	 * stream, ready to accept more code. Its pc will have increased by
	 * additionalBytes.
	 * 
	 * It is the responsibility of the caller to patch up any
	 * branches or gotos.
	 * 
	 * @param pc
	 * @param additionalBytes
	 */
	CodeChunk insertCodeSpace(int pc, int additionalBytes)
	{
		short existingOpcode = getOpcode(pc);

		int lengthOfExistingInstruction
		    = instructionLength(existingOpcode);
			
		
		if (additionalBytes > 0)
		{
			// Size of the current code after this pc.
			int sizeToMove = (getPC() - pc) - lengthOfExistingInstruction;

			// Increase the code by the number of bytes to be
			// inserted. These NOPs will be overwritten by the
			// moved code by the System.arraycopy below.
			// It's assumed that the number of inserted bytes
			// is small, one or two instructions worth, so it
			// won't be a performance issue.
			for (int i = 0; i < additionalBytes; i++)
				addInstr(VMOpcode.NOP);
		
			// Must get codeBytes here as the array might have re-sized.
			byte[] codeBytes = cout.getData();
			
			int byteOffset = CODE_OFFSET + pc + lengthOfExistingInstruction;
					
			
			// Shift the existing code stream down
			System.arraycopy(
					codeBytes, byteOffset,
					codeBytes, byteOffset + additionalBytes,
					sizeToMove);
			
			// Place NOPs in the space just freed by the move.
			// This is not required, it ias assumed the caller
			// will overwrite all the bytes they requested, but
			// to be safe fill in with NOPs rather than leaving code
			// that could break the verifier.
			Arrays.fill(codeBytes, byteOffset, byteOffset + additionalBytes,
					(byte) VMOpcode.NOP);
		}
		
		// The caller must overwrite the original instruction
		// at pc, thus increase the range of the limit stream
		// created to include those bytes.
		additionalBytes += lengthOfExistingInstruction;
		
		// Now the caller needs to fill in the instructions
		// that make up the modified byteCount bytes of bytecode stream.
		// Return a CodeChunk that can be used for this and
		// is limited to only those bytes.
		// The pc of the original code chunk is left unchanged.
		
		return new CodeChunk(this, pc, additionalBytes);
						
	}
	
	/*
     * * Methods related to splitting the byte code chunks into sections that
     * fit in the JVM's limits for a single method.
     */

    /**
     * For a block of byte code starting at program counter pc for codeLength
     * bytes return the maximum stack value, assuming a initial stack depth of
     * zero.
     */
    private int findMaxStack(ClassHolder ch, int pc, int codeLength) {

        final int endPc = pc + codeLength;
        int stack = 0;
        int maxStack = 0;

        for (; pc < endPc;) {

            short opcode = getOpcode(pc);
            
 
            int stackDelta = stackWordDelta(ch, pc, opcode);

            stack += stackDelta;
            if (stack > maxStack)
                maxStack = stack;
 
            int[] cond_pcs = findConditionalPCs(pc, opcode);
            if (cond_pcs != null) {                 
                // an else block exists.
                if (cond_pcs[3] != -1) {
                    int blockMaxStack = findMaxStack(ch, cond_pcs[1],
                            cond_pcs[2]);
                    if ((stack + blockMaxStack) > maxStack)
                        maxStack = stack + blockMaxStack;

                    pc = cond_pcs[3];
                    continue;
                }
            }

            pc += instructionLength(opcode);
        }

        return maxStack;
    }

    /**
     * Return the number of stack words pushed (positive) or popped (negative)
     * by this instruction.
     */
    private int stackWordDelta(ClassHolder ch, int pc, short opcode) {
        if (SanityManager.DEBUG) {
            // this validates the OPCODE_ACTION entry
            instructionLength(opcode);
        }

        int stackDelta = OPCODE_ACTION[opcode][0];
        if (stackDelta == VARIABLE_STACK) {
            stackDelta = getVariableStackDelta(ch, pc, opcode);
        }

        return stackDelta;
    }

    /**
     * Get the type descriptor in the virtual machine format for the type
     * defined by the constant pool index for the instruction at pc.
     */
    private String getTypeDescriptor(ClassHolder ch, int pc) {
        int cpi = getU2(pc);


        // Field reference or method reference
        CONSTANT_Index_info cii = (CONSTANT_Index_info) ch.getEntry(cpi);

        // NameAndType reference
        int nameAndType = cii.getI2();
        cii = (CONSTANT_Index_info) ch.getEntry(nameAndType);

        // UTF8 descriptor
        int descriptor = cii.getI2();
        CONSTANT_Utf8_info type = (CONSTANT_Utf8_info) ch.getEntry(descriptor);

        String vmDescriptor = type.toString();

        return vmDescriptor;
    }

    /**
     * Get the word count for a type descriptor in the format of the virual
     * machine. For a method this returns the the word count for the return
     * type.
     */
    private static int getDescriptorWordCount(String vmDescriptor) {
 
        int width;
        if (VMDescriptor.DOUBLE.equals(vmDescriptor))
            width = 2;
        else if (VMDescriptor.LONG.equals(vmDescriptor))
            width = 2;
        else if (vmDescriptor.charAt(0) == VMDescriptor.C_METHOD) {
            switch (vmDescriptor.charAt(vmDescriptor.length() - 1)) {
            case VMDescriptor.C_DOUBLE:
            case VMDescriptor.C_LONG:
                width = 2;
                break;
            case VMDescriptor.C_VOID:
                width = 0;
                break;
            default:
                width = 1;
                break;
            }
        } else
            width = 1;

        return width;
    }

    /**
     * Get the number of words pushed (positive) or popped (negative) by this
     * instruction. The instruction is a get/put field or a method call, thus
     * the size of the words is defined by the field or method being access.
     */
    private int getVariableStackDelta(ClassHolder ch, int pc, int opcode) {
        String vmDescriptor = getTypeDescriptor(ch, pc);
        int width = CodeChunk.getDescriptorWordCount(vmDescriptor);

        int stackDelta = 0;
        // Stack delta depends on context.
        switch (opcode) {
        case VMOpcode.GETSTATIC:
            stackDelta = width;
            break;

        case VMOpcode.GETFIELD:
            stackDelta = width - 1; // one for popped object ref
            break;

        case VMOpcode.PUTSTATIC:
            stackDelta = -width;
            break;

        case VMOpcode.PUTFIELD:
            stackDelta = -width - 1; // one for pop object ref
            break;

        case VMOpcode.INVOKEVIRTUAL:
        case VMOpcode.INVOKESPECIAL:
            stackDelta = -1; // for instance reference for method call.
        case VMOpcode.INVOKESTATIC:
            stackDelta += (width - CodeChunk.parameterWordCount(vmDescriptor));
            // System.out.println("invoked non-interface " + stackDelta);
            break;

        case VMOpcode.INVOKEINTERFACE:
            // third byte contains the number of arguments to be popped
            stackDelta = width - getOpcode(pc + 3);
            // System.out.println("invoked interface " + stackDelta);
            break;
        default:
            System.out.println("WHO IS THIS ");
            break;

        }

        return stackDelta;
    }

    /**
     * Calculate the number of stack words in the arguments pushed for this
     * method descriptor.
     */
    private static int parameterWordCount(String methodDescriptor) {
        int wordCount = 0;
        for (int i = 1;; i++) {
            switch (methodDescriptor.charAt(i)) {
            case VMDescriptor.C_ENDMETHOD:
                return wordCount;
            case VMDescriptor.C_DOUBLE:
            case VMDescriptor.C_LONG:
                wordCount += 2;
                break;
            case VMDescriptor.C_ARRAY:
                // skip while there are array symbols.
                do {
                    i++;
                } while (methodDescriptor.charAt(i) == VMDescriptor.C_ARRAY);
                if (methodDescriptor.charAt(i) != VMDescriptor.C_CLASS) {
                    // an array is a reference, even an array of doubles.
                    wordCount += 1;
                    break;
                }

            // fall through to skip the Lclassname; after the array.

            case VMDescriptor.C_CLASS:
                // skip until ;
                do {
                    i++;
                } while (methodDescriptor.charAt(i) != VMDescriptor.C_ENDCLASS);
                wordCount += 1;
                break;
            default:
                wordCount += 1;
                break;
            }
        }
    }

    /**
     * Find the limits of a conditional block starting at the instruction with
     * the given opcode at the program counter pc.
     * <P>
     * Returns a six element integer array of program counters and lengths.
     * <code> [0] - program counter of the IF opcode (passed in as pc) [1] -
     * program counter of the start of the then block [2] - length of the then
     * block [3] - program counter of the else block, -1 if no else block
     * exists. [4] - length of of the else block, -1 if no else block exists.
     * [5] - program counter of the common end point. </code>
     * 
     * Looks for and handles conditionals that are written by the Conditional
     * class.
     * 
     * @return Null if the opcode is not the start of a conditional otherwise
     *         the array of values.
     */
    private int[] findConditionalPCs(int pc, short opcode) {
        switch (opcode) {
        default:
            return null;
        case VMOpcode.IFNONNULL:
        case VMOpcode.IFNULL:
        case VMOpcode.IFEQ:
        case VMOpcode.IFNE:
            break;
        }

        int then_pc;
        int else_pc;
        int if_off = getU2(pc);

        if ((if_off == 8)
                && (getOpcode(pc + VMOpcode.IF_INS_LENGTH) == VMOpcode.GOTO_W)) {
            // 32 bit branch
            then_pc = pc + VMOpcode.IF_INS_LENGTH + VMOpcode.GOTO_W_INS_LENGTH;

            // Get else PC from the 32 bit offset within the GOTO_W
            // instruction remembering to add it to the pc of that
            // instruction, not the original IF.
            else_pc = pc + VMOpcode.IF_INS_LENGTH
                    + getU4(pc + VMOpcode.IF_INS_LENGTH);

        } else {
            then_pc = pc + VMOpcode.IF_INS_LENGTH;
            else_pc = pc + if_off;
        }

        // Need to look for the goto or goto_w at the
        // end of the then block. There might not be
        // one for the case when there is no else block.
        // In that case the then block will just run into
        // what we currently think the else pc.

        int end_pc = -1;
        for (int tpc = then_pc; tpc < else_pc;) {
            short opc = getOpcode(tpc);

            // need to handle conditionals
            int[] innerCond = findConditionalPCs(tpc, opc);
            if (innerCond != null) {
                // skip to the end of this conditional
                tpc = innerCond[5]; // end_pc
                continue;
            }

            if (opc == VMOpcode.GOTO) {
                // not at the end of the then block
                // so not our goto. Shouldn't see this
                // with the current code due to the
                // skipping of the conditional above.
                // But safe defensive programming.
                if (tpc != (else_pc - VMOpcode.GOTO_INS_LENGTH))
                    continue;

                end_pc = tpc + getU2(tpc);
                break;
            } else if (opc == VMOpcode.GOTO_W) {
                // not at the end of the then block
                // so not our goto. SHouldn't see this
                // with the current code due to the
                // skipping of the conditional above.
                // But safe defensive programming.
                if (tpc != (else_pc - VMOpcode.GOTO_W_INS_LENGTH))
                    continue;

                end_pc = tpc + getU4(tpc);
                break;

            }
            tpc += instructionLength(opc);
        }

        int else_len;
        int then_len;
        if (end_pc == -1) {
            // no else block;
            end_pc = else_pc;
            else_pc = -1;

            then_len = end_pc - then_pc;
            else_len = -1;
        } else {
            then_len = else_pc - then_pc;
            else_len = end_pc - else_pc;
        }

        int[] ret = new int[6];

        ret[0] = pc;
        ret[1] = then_pc;
        ret[2] = then_len;
        ret[3] = else_pc;
        ret[4] = else_len;
        ret[5] = end_pc;

        return ret;
    }
    
    /**
     * Attempt to split the current method by pushing a chunk of
     * its code into a sub-method. The starting point of the split
     * (split_pc) must correspond to a stack depth of zero. It is the
     * reponsibility of the caller to ensure this.
     * Split is only made if there exists a chunk of code starting at
     * pc=split_pc, whose stack depth upon termination is zero.
     * The method will try to split a code section greater than
     * optimalMinLength but may split earlier if no such block exists.
     * <P>
     * The method is aimed at splitting methods that contain
     * many independent statements.
     * <P>
     * If a split is possible this method will perform the split and
     * create a void sub method, and move the code into the sub-method
     * and setup this method to call the sub-method before continuing.
     * This method's max stack and current pc will be correctly set
     * as though the method had just been created.
     * 
     * @param mb Method for this chunk.
     * @param ch Class definition
     * @param optimalMinLength minimum length required for split
     */
    final int splitZeroStack(BCMethod mb, ClassHolder ch, final int split_pc,
            final int optimalMinLength) {
        
        int splitMinLength = splitMinLength(mb);
        
        int stack = 0;

        // maximum possible split seen that is less than
        // the minimum.
        int possibleSplitLength = -1;

        // do not split until at least this point (inclusive)
        // used to ensure no split occurs in the middle of
        // a conditional.
        int outerConditionalEnd_pc = -1;

        int end_pc = getPC(); // pc will be positioned at the end.
        for (int pc = split_pc; pc < end_pc;) {

            short opcode = getOpcode(pc);

            int stackDelta = stackWordDelta(ch, pc, opcode);

            stack += stackDelta;

            // Cannot split a conditional but need to calculate
            // the stack depth at the end of the conditional.
            // Each path through the conditional will have the
            // same stack depth.
            int[] cond_pcs = findConditionalPCs(pc, opcode);
            if (cond_pcs != null) {
                // an else block exists, skip the then block.
                if (cond_pcs[3] != -1) {
                    pc = cond_pcs[3];
                    continue;
                }

                if (SanityManager.DEBUG) {
                    if (outerConditionalEnd_pc != -1) {
                        if (cond_pcs[5] >= outerConditionalEnd_pc)
                            SanityManager.THROWASSERT("NESTED CONDITIONALS!");
                    }
                }

                if (outerConditionalEnd_pc == -1) {
                    outerConditionalEnd_pc = cond_pcs[5];
                }
            }

            pc += instructionLength(opcode);

            // Don't split in the middle of a conditional
            if (outerConditionalEnd_pc != -1) {
                if (pc > outerConditionalEnd_pc) {
                    // passed the outermost conditional
                    outerConditionalEnd_pc = -1;
                }
                continue;
            }

            if (stack != 0)
                continue;

            int splitLength = pc - split_pc;

            if (splitLength < optimalMinLength) {
                // record we do have a possible split.
                possibleSplitLength = splitLength;
                continue;
            }

            // no point splitting to a method bigger
            // than the VM can handle. Save one for
            // return instruction.
            if (splitLength > BCMethod.CODE_SPLIT_LENGTH - 1) {
                splitLength = -1;
            }
            else if (CodeChunk.isReturn(opcode))
            {
                // Don't handle a return in the middle of
                // an instruction stream. Don't think this
                // is generated, but be safe.           
                splitLength = -1;
            }
            
            // if splitLenth was set to -1 above then there
            // is no possible split at this instruction.
            if (splitLength == -1)
            {
                // no earlier split at all
                if (possibleSplitLength == -1)
                    return -1;
 
                // Decide if the earlier possible split is
                // worth it.
                if (possibleSplitLength <= splitMinLength)
                    return -1;

                // OK go with the earlier split
                splitLength = possibleSplitLength;

            }

            // Yes, we can split this big method into a smaller method!!

            BCMethod subMethod = startSubMethod(mb, "void", split_pc,
                    splitLength);

            return splitCodeIntoSubMethod(mb, ch, subMethod,
                    split_pc, splitLength);
        }
        return -1;
    }

    /**
     * Start a sub method that we will split the portion of our current code to,
     * starting from start_pc and including codeLength bytes of code.
     * 
     * Return a BCMethod obtained from BCMethod.getNewSubMethod with the passed
     * in return type and same parameters as mb if the code block to be moved
     * uses parameters.
     */
    private BCMethod startSubMethod(BCMethod mb, String returnType,
            int split_pc, int blockLength) {

        boolean needParameters = usesParameters(mb, split_pc, blockLength);

        return mb.getNewSubMethod(returnType, needParameters);
    }

    /**
     * Does a section of code use parameters.
     * Any load, exception ALOAD_0 in an instance method, is
     * seen as using parameters, as this complete byte code
     * implementation does not use local variables.
     * 
     */
    private boolean usesParameters(BCMethod mb, int pc, int codeLength) {

        // does the method even have parameters?
        if (mb.parameters == null)
            return false;

        boolean isStatic = (mb.myEntry.getModifier() & Modifier.STATIC) != 0;

        int endPc = pc + codeLength;

        for (; pc < endPc;) {
            short opcode = getOpcode(pc);
            switch (opcode) {
            case VMOpcode.ILOAD_0:
            case VMOpcode.LLOAD_0:
            case VMOpcode.FLOAD_0:
            case VMOpcode.DLOAD_0:
                return true;

            case VMOpcode.ALOAD_0:
                if (isStatic)
                    return true;
                break;

            case VMOpcode.ILOAD_1:
            case VMOpcode.LLOAD_1:
            case VMOpcode.FLOAD_1:
            case VMOpcode.DLOAD_1:
            case VMOpcode.ALOAD_1:
                return true;

            case VMOpcode.ILOAD_2:
            case VMOpcode.LLOAD_2:
            case VMOpcode.FLOAD_2:
            case VMOpcode.DLOAD_2:
            case VMOpcode.ALOAD_2:
                return true;

            case VMOpcode.ILOAD_3:
            case VMOpcode.LLOAD_3:
            case VMOpcode.FLOAD_3:
            case VMOpcode.DLOAD_3:
            case VMOpcode.ALOAD_3:
                return true;

            case VMOpcode.ILOAD:
            case VMOpcode.LLOAD:
            case VMOpcode.FLOAD:
            case VMOpcode.DLOAD:
            case VMOpcode.ALOAD:
                return true;
            default:
                break;

            }
            pc += instructionLength(opcode);
        }
        return false;
    }
    /**
     * Split a block of code from this method into a sub-method
     * and call it.
     * 
     * Returns the pc of this method just after the call
     * to the sub-method.
     
     * @param mb My method
     * @param ch My class
     * @param subMethod Sub-method code was pushed into
     * @param split_pc Program counter the split started at
     * @param splitLength Length of code split
     */
    private int splitCodeIntoSubMethod(BCMethod mb, ClassHolder ch,
            BCMethod subMethod, final int split_pc, final int splitLength) {
        CodeChunk subChunk = subMethod.myCode;

        byte[] codeBytes = cout.getData();

        // the code to be moved into the sub method
        // as a block. This will correctly increase the
        // program counter.
        try {
            subChunk.cout.write(codeBytes, CODE_OFFSET + split_pc, splitLength);
        } catch (IOException ioe) {
            limitHit(ioe);
        }

        // Just cause the sub-method to return,
        // fix up its maxStack and then complete it.
        if (subMethod.myReturnType.equals("void"))
            subChunk.addInstr(VMOpcode.RETURN);
        else
           subChunk.addInstr(VMOpcode.ARETURN);
        
        // Finding the max stack requires the class format to
        // still be valid. If we have blown the number of constant
        // pool entries then we can no longer guarantee that indexes
        // into the constant pool in the code stream are valid.
        if (cb.limitMsg != null)
            return -1;
        
        subMethod.maxStack = subChunk.findMaxStack(ch, 0, subChunk.getPC());
        subMethod.complete();

        return removePushedCode(mb, ch, subMethod, split_pc, splitLength);
    }

    /**
     * Remove a block of code from this method that was pushed into a sub-method
     * and call the sub-method.
     * 
     * Returns the pc of this method just after the call to the sub-method.
     * 
     * @param mb
     *            My method
     * @param ch
     *            My class
     * @param subMethod
     *            Sub-method code was pushed into
     * @param split_pc
     *            Program counter the split started at
     * @param splitLength
     *            Length of code split
     */
    private int removePushedCode(BCMethod mb, ClassHolder ch,
            BCMethod subMethod, final int split_pc, final int splitLength) {
        // now need to fix up this method, create
        // a new CodeChunk just to be clearer than
        // trying to modify this chunk directly.
        
        // total length of the code for this method before split
        final int codeLength = getPC();
        
        CodeChunk replaceChunk = new CodeChunk(mb.cb);
        mb.myCode = replaceChunk;
        mb.maxStack = 0;

        byte[] codeBytes = cout.getData();
        
        // write any existing code before the split point
        // into the replacement chunk.
        if (split_pc != 0) {
            try {
                replaceChunk.cout.write(codeBytes, CODE_OFFSET, split_pc);
            } catch (IOException ioe) {
                limitHit(ioe);
            }
        }

        // Call the sub method, will write into replaceChunk.
        mb.callSubMethod(subMethod);

        int postSplit_pc = replaceChunk.getPC();

        // Write the code remaining in this method into the replacement chunk

        int remainingCodePC = split_pc + splitLength;
        int remainingCodeLength = codeLength - splitLength - split_pc;
        
       try {
            replaceChunk.cout.write(codeBytes, CODE_OFFSET + remainingCodePC,
                    remainingCodeLength);
        } catch (IOException ioe) {
            limitHit(ioe);
        }

        // Finding the max stack requires the class format to
        // still be valid. If we have blown the number of constant
        // pool entries then we can no longer guarantee that indexes
        // into the constant pool in the code stream are valid.
        if (cb.limitMsg != null)
            return -1;
        
        mb.maxStack = replaceChunk.findMaxStack(ch, 0, replaceChunk.getPC());

        return postSplit_pc;
    }
    
    /**
     * Split an expression out of a large method into its own
     * sub-method.
     * <P>
     * Method call expressions are of the form:
     * <UL>
     * <LI> expr.method(args) -- instance method call
     * <LI> method(args) -- static method call
     * </UL>
     * Two special cases of instance method calls will be handled
     * by the first incarnation of splitExpressionOut. 
     * three categories:
     * <UL>
     * <LI>this.method(args)
     * <LI>this.getter().method(args)
     * </UL>
     * These calls are choosen as they are easier sub-cases
     * and map to the code generated for SQL statements.
     * Future coders can expand the method to cover more cases.
     * <P>
     * This method will split out such expressions in sub-methods
     * and replace the original code with a call to that submethod.
     * <UL>
     * <LI>this.method(args) -&gt;&gt; this.sub1([parameters])
     * <LI>this.getter().method(args) -&gt;&gt; this.sub1([parameters])
     * </UL>
     * The assumption is of course that the call to the sub-method
     * is much smaller than the code it replaces.
     * <P>
     * Looking at the byte code for such calls they would look like
     * (for an example three argument method):
     * <code>
     * this arg1 arg2 arg3 INVOKE // this.method(args)
     * this INVOKE arg1 arg2 arg3 INVOKE // this.getter().metod(args)
     * </code>
     * The bytecode for the arguments can be arbitary long and
     * consist of expressions, typical Derby code for generated
     * queries is deeply nested method calls.
     * <BR>
     * If none of the arguments requred the parameters passed into
     * the method, then in both cases the replacement bytecode
     * would look like:
     * <code>
     * this.sub1();
     * </code>
     * Parameter handling is just as in the method splitZeroStack().
     * <P>
     * Because the VM is a stack machine the original byte code
     * sequences are self contained. The stack at the start of
     * is sequence is N and at the end (after the method call) will
     * be:
     * <UL>
     * <LI> N - void method
     * <LI> N + 1 - method returning a single word
     * <LI> N + 2 - method returning a double word (java long or double)
     * </UL>
     * This code will handle the N+1 where the word is a reference,
     * the typical case for generated code.
     * <BR>
     * The code is self contained because in general the byte code
     * for the arguments will push and pop values but never drop
     * below the stack value at the start of the byte code sequence.
     * E.g. in the examples the stack before the first arg will be
     * N+1 (the objectref for the method call) and at the end of the
     * byte code for arg1 will be N+2 or N+3 depending on if arg1 is
     * a single or double word argument. During the execution of
     * the byte code the stack may have had many arguments pushed
     * and popped, but will never have dropped below N+1. Thus the
     * code for arg1 is independent of the stack's previous values
     * and is self contained. This self-containment then extends to
     * all the arguements, the method call itself and pushing the
     * objectref for the method call, thus the complete
     * sequence is self-contained.
     * <BR>
     * The self-containment breaks in a few cases, take the simple
     * method call this.method(3), the byte code for this could be:
     * <code>
     * push3 this swap invoke
     * </code>
     * In this case the byte code for arg1 (swap) is not self-contained
     * and relies on earlier stack values.
     * <P>
     * How to identify "self-contained blocks of code".
     * <BR>
     * We walk through the byte code and maintain a history of
     * the program counter that indicates the start of the
     * independent sequence each stack word depends on.
     * Thus for a ALOAD_0 instruction which pushes 'this' the
     * dependent pc is that of the this. If a DUP instruction followed
     * then the top-word is now dependent on the previous word (this)
     * and thus the dependence of it is equal to the dependence of
     * the previous word. This information is kept in earliestIndepPC
     * array as we process the instruction stream.
     * <BR>
     * When a INVOKE instruction is seen for an instance method
     * that returns a single or double word, the dependence of
     * the returned value is the dependence of the word in the
     * stack that is the objectref for the call. This complete
     * sequence from the pc the objectref depended on to the
     * INVOKE instruction is then a self contained sequence
     * and can be split into a sub-method.

     * <BR>
     * If the block is self-contained then it can be split, following
     * similar logic to splitZeroStack().
     *  
     *  <P>
     *  WORK IN PROGRESS - Incremental development
     *  <BR>
     *  Currently walks the method maintaining the
     *  earliestIndepPC array and identifies potential blocks
     *  to splt, performs splits as required.
     *  Called by BCMethod but commented out in submitted code.
     *  Tested with local changes from calls in BCMethod.
     *  Splits generally work, though largeCodeGen shows
     *  a problem that will be fixed before the code in
     *  enabled for real.
     *  
      */

    final int splitExpressionOut(final BCMethod mb, final ClassHolder ch,
            final int optimalMinLength,
            final int maxStack)
    {
        // Save the best block we have seen for splitting out.
        int bestSplitPC = -1;
        int bestSplitBlockLength = -1;
        String bestSplitRT = null;  
        
        int splitMinLength = splitMinLength(mb);
        
        // Program counter of the earliest instruction
        // that the word in the current active stack
        // at the given depth depends on.
        //
        // Some examples, N is the stack depth *after*
        // the instruction.
        // E.g. 
        // ALOAD_0 - pushes this, is an instruction that
        // pushes an independent value, so the current
        // stack word depends on the pc of current instruction.
        // earliestIndepPC[N] = pc (that pushed the value).
        //
        // DUP - duplicates the top word, so the duplicated
        // top word will depend on the same pc as the word
        // it was duplicated from.
        // I.e. earliestIndepPC[N]
        //            = earliestIndepPC[N-1];
        //
        // instance method call returning single word value.
        // The top word will depend on the same pc as the
        // objectref for the method call, which was at the
        // same depth in this case.
        // earliestIndepPC[N] unchanged
        //
        // at any time earliestIndepPC is only valid
        // from 1 to N where N is the depth of the stack.
       int[] earliestIndepPC = new int[maxStack+1];
       
        int stack = 0;
               
        //TODO: this conditional handling is copied from
        //the splitZeroStack code, need to check to see
        // how it fits with the expression logic.
        // do not split until at least this point (inclusive)
        // used to ensure no split occurs in the middle of
        // a conditional.
        int outerConditionalEnd_pc = -1;  

        int end_pc = getPC();
        
        for (int pc = 0; pc < end_pc;) {

            short opcode = getOpcode(pc);
            
            int stackDelta = stackWordDelta(ch, pc, opcode);
            
            stack += stackDelta;
            
            // Cannot split a conditional but need to calculate
            // the stack depth at the end of the conditional.
            // Each path through the conditional will have the
            // same stack depth.
            int[] cond_pcs = findConditionalPCs(pc, opcode);
            if (cond_pcs != null) {
                
                // TODO: This conditional handling was copied
                // from splitZeroStack, haven't looked in detail
                // to see how a conditional should be handled
                // with an expression split. So for the time
                // being just bail.
                if (true)
                    return -1;

                // an else block exists, skip the then block.
                if (cond_pcs[3] != -1) {
                    pc = cond_pcs[3];
                    continue;
                }
                
                if (SanityManager.DEBUG)
                {
                    if (outerConditionalEnd_pc != -1)
                    {
                        if (cond_pcs[5] >= outerConditionalEnd_pc)
                            SanityManager.THROWASSERT("NESTED CONDITIONALS!");
                    }
                }

                if (outerConditionalEnd_pc == -1)
                {
                    outerConditionalEnd_pc = cond_pcs[5];
                }
            }
                       
            pc += instructionLength(opcode);
            
            // Don't split in the middle of a conditional
            if (outerConditionalEnd_pc != -1) {
                if (pc > outerConditionalEnd_pc) {
                    // passed the outermost conditional
                    outerConditionalEnd_pc = -1;
                }
                continue;
            }
            
            int opcode_pc = pc - instructionLength(opcode);
            switch (opcode)
            {
            // Any instruction we don't have any information
            // on, we simply clear all evidence of independent
            // starting points, and start again.
            default:
                Arrays.fill(earliestIndepPC,
                        0, stack + 1, -1);
                break;
            
            // Independent instructions do not change the stack depth
            // and the independence of the top word picks up
            // the independence of the previous word at the same
            // position. Ie. no change!
            case VMOpcode.ARRAYLENGTH:
            case VMOpcode.NOP:
            case VMOpcode.CHECKCAST:
            case VMOpcode.D2L:
            case VMOpcode.DNEG:
            case VMOpcode.F2I:
            	break;
            	
            // Independent instructions that push one word
            case VMOpcode.ALOAD_0:
            case VMOpcode.ALOAD_1:
            case VMOpcode.ALOAD_2:
            case VMOpcode.ALOAD_3:
            case VMOpcode.ALOAD:
            case VMOpcode.ACONST_NULL:
            case VMOpcode.BIPUSH:
            case VMOpcode.FCONST_0:
            case VMOpcode.FCONST_1:
            case VMOpcode.FCONST_2:
            case VMOpcode.FLOAD:
            case VMOpcode.ICONST_0:
            case VMOpcode.ICONST_1:
            case VMOpcode.ICONST_2:
            case VMOpcode.ICONST_3:
            case VMOpcode.ICONST_4:
            case VMOpcode.ICONST_5:
            case VMOpcode.ICONST_M1:
            case VMOpcode.LDC:
            case VMOpcode.LDC_W:
            case VMOpcode.SIPUSH:
            	earliestIndepPC[stack] = opcode_pc;
            	break;
            
            // Independent instructions that push two words
            case VMOpcode.DCONST_0:
            case VMOpcode.DCONST_1:
            case VMOpcode.LCONST_0:
            case VMOpcode.LCONST_1:
            case VMOpcode.LDC2_W:
            case VMOpcode.LLOAD:
            case VMOpcode.LLOAD_0:
            case VMOpcode.LLOAD_1:
            case VMOpcode.LLOAD_2:
            case VMOpcode.LLOAD_3:
                earliestIndepPC[stack - 1] = 
                    earliestIndepPC[stack] = opcode_pc;
                break;
                
            // nothing to do for pop, obviously no
            // code will be dependent on the popped words.
            case VMOpcode.POP:
            case VMOpcode.POP2:
                break;
                
            case VMOpcode.SWAP:
                earliestIndepPC[stack] = earliestIndepPC[stack -1];
                break;
            
            // push a value that depends on the previous value
            case VMOpcode.I2L:
                earliestIndepPC[stack] = earliestIndepPC[stack -1];
                break;
                
            case VMOpcode.GETFIELD:
            {
                String vmDescriptor = getTypeDescriptor(ch, opcode_pc);
                int width = CodeChunk.getDescriptorWordCount(vmDescriptor);
                if (width == 2)
                    earliestIndepPC[stack] = earliestIndepPC[stack -1];
                    
                break;
            }

            case VMOpcode.INVOKEINTERFACE:
            case VMOpcode.INVOKEVIRTUAL:
            {
            	//   ...,objectref[,word]*
            	//   
            	// => ...
            	// => ...,word
            	// => ...,word1,word2
            	
                // Width of the value returned by the method call.
                String vmDescriptor = getTypeDescriptor(ch, opcode_pc);
                int width = CodeChunk.getDescriptorWordCount(vmDescriptor);
                
                // Independence of this block is the independence
                // of the objectref that invokved the method.
                int selfContainedBlockStart;
                if (width == 0)
                {
                    // objectref was at one more than the current depth
                    // no plan to split here though, as we are only
                    // splitting methods that return a reference.
                    selfContainedBlockStart = -1;
                        // earliestIndepPC[stack + 1];
                }
                else if (width == 1)
                {     
                    // stack is unchanged, objectref was at
                    // the current stack depth
                    selfContainedBlockStart = earliestIndepPC[stack];
               }
                else
                {
                    // width == 2, objectref was one below the
                    // current stack depth.
                    // no plan to split here though, as we are only
                    // splitting methods that return a reference.
                    selfContainedBlockStart = -1;
                        
                    // top two words depend on the objectref
                    // which was at the same depth of the first word
                    // of the 64 bit value.
                    earliestIndepPC[stack] =
                        earliestIndepPC[stack - 1];
                 }
                
                if (selfContainedBlockStart != -1)
                {
                    int blockLength = pc - selfContainedBlockStart;
                    
                    if (blockLength <= splitMinLength)
                    {
                        // No point splitting, too small
                    }
                    else if (blockLength > (VMOpcode.MAX_CODE_LENGTH - 1))
                    {
                        // too big to split into a single method
                        // (one for the return opcode)
                    }
                    else
                    {
                        // Only split for a method that returns
                        // an class reference.
                        int me = vmDescriptor.lastIndexOf(')');
                    
                        if (vmDescriptor.charAt(me+1) == 'L')
                        {
                            String rt = vmDescriptor.substring(me + 2,
                                    vmDescriptor.length() - 1);
                            
                            // convert to external format.
                            rt = rt.replace('/', '.');
                            
                            if (blockLength >= optimalMinLength)
                            {
                                // Split now!
                                BCMethod subMethod = startSubMethod(mb,
                                        rt, selfContainedBlockStart,
                                        blockLength);
    
                                return splitCodeIntoSubMethod(mb, ch, subMethod,
                                        selfContainedBlockStart, blockLength);                             
                            }                       
                            else if (blockLength > bestSplitBlockLength)
                            {
                                // Save it, may split at this point
                                // if nothing better seen.
                                bestSplitPC = selfContainedBlockStart;
                                bestSplitBlockLength = blockLength;
                                bestSplitRT = rt;
                            }
                        }
                    }
                }
            	break;
              }
            }   
            
       }
        

        if (bestSplitBlockLength != -1) {
            BCMethod subMethod = startSubMethod(mb,
                    bestSplitRT, bestSplitPC,
                    bestSplitBlockLength);
    
            return splitCodeIntoSubMethod(mb, ch, subMethod,
                    bestSplitPC, bestSplitBlockLength);  
        }
               
        return -1;
    }
    
    /**
     * See if the opcode is a return instruction.
     * @param opcode opcode to be checked
     * @return true for is a return instruction, false otherwise.
     */
    private static boolean isReturn(short opcode)
    {
        switch (opcode)
        {
        case VMOpcode.RETURN:
        case VMOpcode.ARETURN:
        case VMOpcode.IRETURN:
        case VMOpcode.FRETURN:
        case VMOpcode.DRETURN:
        case VMOpcode.LRETURN:
            return true;
         default:
            return false;
        }        
    }
    
    /**
     * Minimum split length for a sub-method. If the number of
     * instructions to call the sub-method exceeds the length
     * of the sub-method, then there's no point splitting.
     * The number of bytes in the code stream to call
     * a generated sub-method can take is based upon the number of method args.
     * A method can have maximum of 255 words of arguments (section 4.10 JVM spec)
     * which in the worst case would be 254 (one-word) parameters
     * and this. For a sub-method the arguments will come from the
     * parameters to the method, i.e. ALOAD, ILOAD etc.
     * <BR>
     * This leads to this number of instructions.
     * <UL>
     * <LI> 4 - 'this' and first 3 parameters have single byte instructions
     * <LI> (N-4)*2 - Remaining parameters have two byte instructions
     * <LI> 3 for the invoke instruction.
     * </UL>
     */
    private static int splitMinLength(BCMethod mb) {
        int min = 1 + 3; // For ALOAD_0 (this) and invoke instruction
        
        if (mb.parameters != null) {
            int paramCount = mb.parameters.length;
            
            min += paramCount;
            
            if (paramCount > 3)
                min += (paramCount - 3);
        }
        
        return min;
    }
    /*
    final int splitNonZeroStack(BCMethod mb, ClassHolder ch,
            final int codeLength, final int optimalMinLength,
            int maxStack) {
        
        // program counter for the instruction that
        // made the stack reach the given stack depth.
        int[] stack_pcs = new int[maxStack+1];
        Arrays.fill(stack_pcs, -1);
        
        int stack = 0;
        
        // maximum possible split seen that is less than
        // the minimum.
        int possibleSplitLength = -1;
        
        System.out.println("NZ SPLIT + " + mb.getName());

        // do not split until at least this point (inclusive)
        // used to ensure no split occurs in the middle of
        // a conditional.
        int outerConditionalEnd_pc = -1;

        int end_pc = 0 + codeLength;
        for (int pc = 0; pc < end_pc;) {

            short opcode = getOpcode(pc);

            int stackDelta = stackWordDelta(ch, pc, opcode);
            
            stack += stackDelta;
            
            // Cannot split a conditional but need to calculate
            // the stack depth at the end of the conditional.
            // Each path through the conditional will have the
            // same stack depth.
            int[] cond_pcs = findConditionalPCs(pc, opcode);
            if (cond_pcs != null) {
                // an else block exists, skip the then block.
                if (cond_pcs[3] != -1) {
                    pc = cond_pcs[3];
                    continue;
                }
                
                if (SanityManager.DEBUG)
                {
                    if (outerConditionalEnd_pc != -1)
                    {
                        if (cond_pcs[5] >= outerConditionalEnd_pc)
                            SanityManager.THROWASSERT("NESTED CONDITIONALS!");
                    }
                }

                if (outerConditionalEnd_pc == -1)
                {
                    outerConditionalEnd_pc = cond_pcs[5];
                }
            }
                       
            pc += instructionLength(opcode);
            
            // Don't split in the middle of a conditional
            if (outerConditionalEnd_pc != -1) {
                if (pc > outerConditionalEnd_pc) {
                    // passed the outermost conditional
                    outerConditionalEnd_pc = -1;
                }
                continue;
            }
            
            if (stackDelta == 0)
                continue;

            // Only split when the stack is having items popped
            if (stackDelta > 0)
            {
                // pushing double word, clear out a
                if (stackDelta == 2)
                    stack_pcs[stack - 1] = pc;
                stack_pcs[stack] = pc;
                continue;
            }
            
            int opcode_pc = pc - instructionLength(opcode);
            
            // Look for specific opcodes that have the capability
            // of having a significant amount of code in a self
            // contained block.
            switch (opcode)
            {
            // this.method(A) construct
            //  ...         -- stack N
            //  push this -- stack N+1
            //  push args -- stack N+1+A
            //  call method -- stack N+R (R=0,1,2)
            //
            //  stackDelta = (N+R) - (N+1+A) = R-(1+A)
            //  stack = N+R
            //  Need to determine N+1
            //  
            //  
            //
            //  this.a(<i2>, <i2>, <i3>)
            //  returning int
            //
            //  stackDelta = -3 (this & 3 args popped, ret pushed)
            //  initial depth N = 10
            //  pc        - stack
            //  100 ...       - stack 10
            //  101 push this - stack 11
            //  109 push i1   - stack 12
            //  125 push i2   - stack 13
            //  156 push i3   - stack 14
            //  157 call      - stack 11
            //  
            //  need stack_pcs[11] = stack_pcs[11 + -3]
            //
            // ref.method(args).method(args) ... method(args)
            // 
            case VMOpcode.INVOKEINTERFACE:
            case VMOpcode.INVOKESPECIAL:
            case VMOpcode.INVOKEVIRTUAL:
            {
                String vmDescriptor = getTypeDescriptor(ch, opcode_pc);
                int r = CodeChunk.getDescriptorWordCount(vmDescriptor);
             
                // PC of the opcode that pushed the reference for
                // this method call.
                int ref_pc = stack_pcs[stack - r + 1];
               if (getOpcode(ref_pc) == VMOpcode.ALOAD_0) {
                    System.out.println("POSS SPLIT " + (pc - ref_pc) + " @ " + ref_pc);
                }
               break;
            }
            case VMOpcode.INVOKESTATIC:
                String vmDescriptor = getTypeDescriptor(ch, opcode_pc);
                int r = CodeChunk.getDescriptorWordCount(vmDescriptor);
                int p1_pc = stack_pcs[stack - r + 1];
                System.out.println("POSS STATIC SPLIT " + (pc - p1_pc) + " @ " + p1_pc);
                
            }
            stack_pcs[stack] = opcode_pc;
        }
        return -1;
    }*/
}
