/*
 * $Id: ExpressionImage.java,v 1.10 1998/11/22 14:20:13 metlov Exp $
 *
 * This file is part of the Java Expressions Library (JEL).
 *   For more information about JEL visit :
 *    http://galaxy.fzu.cz/JEL/
 *
 * (c) 1998 by Konstantin Metlov(metlov@fzu.cz);
 *
 * JEL is Distributed under the terms of GNU General Public License.
 *    This code comes with ABSOLUTELY NO WARRANTY.
 *  For license details see COPYING file in this directory.
 */

package gnu.jel;

import gnu.jel.debug.Debug;
import gnu.jel.debug.Tester;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.util.Hashtable;
import java.util.Stack;
import java.util.Vector;

/**
 * This class is responsible for generating valid Java class files based on
 * a sequence of calls to it's methods (asm_XXXX family of methods). This
 * assembler tries to do more things than other java assemblers do. 
 * It keeps track of types in Java stack and automatically chooses which
 * java bytecodes should be generated to perform a given operation on
 * types in stack.
 * <P>Anyway, it is not general purpose Java assembler, it is specifically JEL
 * oriented. Currently this assembler does not provide direct access to Java
 * control transfer instructions.
 * <P>This class is designed to be the part of a bigger package. This means
 * it will silently generate wrong bytecodes. ;) BUT, if You compile it with
 * debugging ON (see <TT>boolean gnu.jel.debug.Debug.enabled</TT>) it should
 * warn You about all  possible wrong things it does. If there are more
 * assertions to be made, please submit Your patches to <TT>metlov@fzu.cz</TT>.
 * @see gnu.jel.debug.Debug#enabled
 * @author Konstantin L. Metlov (metlov@fzu.cz)
 */
public class ExpressionImage {
  
  /**
   * Denotes the PLUS binary operation.
   */
  public final static int BI_PL=0;

  /**
   * Denotes the MINUS binary operation.
   */
  public final static int BI_MI=1;

  /**
   * Denotes the MULTIPLY binary operation.
   */
  public final static int BI_MU=2;

  /**
   * Denotes the DIVIDE binary operation.
   */
  public final static int BI_DI=3;

  /**
   * Denotes the REMAINDER binary operation.
   */
  public final static int BI_RE=4;

  /**
   * Denotes the AND binary operation.
   */
  public final static int BI_AN=5;

  /**
   * Denotes the OR binary operation.
   */
  public final static int BI_OR=6;

  /**
   * Denotes the XOR binary operation.
   */
  public final static int BI_XO=7;

  /**
   * Denotes binary comparizon for equality
   */
  public final static int BI_EQ=8;

  /**
   * Denotes binary comparizon for inequality
   */
  public final static int BI_NE=9;

  /**
   * Denotes binary comparizon for "less"
   */
  public final static int BI_LT=10;

  /**
   * Denotes binary comparizon for "greater or equal"
   */
  public final static int BI_GE=11;
  
  /**
   * Denotes binary comparizon for "greater"
   */
  public final static int BI_GT=12;

  /**
   * Denotes binary comparizon for "less or equal"
   */
  public final static int BI_LE=13;

  /**
   * Denotes binary left shift
   */
  public final static int BI_LS=14;

  /**
   * Denotes binary right signed shift
   */
  public final static int BI_RSS=15;

  /**
   * Denotes binary right unsigned shift
   */
  public final static int BI_RUS=16;

  /**
   * Names of binary operations by ID in the readable form.
   */
  public final static String[] binaryNames={"add","substract","multiply",
					    "divide","remainder",
					    "bitwise and","bitwise or",
					    "bitwise xor","equal","not equal",
					    "less","greater or equal",
					    "greater","less or equal",
					    "left shift", "signed right shift",
					    "unsigned right shift"};
  /**
   * Symbols of binary operations by ID in the readable form.
   */
  public final static String[] binarySymbols={
    "+","-","*","/","%","&","|","^","==","!=","<",">=",
    ">","<=","<<",">>",">>>"};

  /**
   * Denotes logical conjunction operator
   */
  public final static int LOG_AN=0;

  /**
   * Denotes logical disjunction operator
   */
  public final static int LOG_OR=1;

  /**
   * Denotes logical complement operator
   */
  public final static int LOG_NO=2;
  
  /**
   * Names of logical operations by ID in the readable form.
   */
  public final static String[] logicalNames={"logical and","logical or",
					    "logical not"};

  /**
   * Symbols of logical operations by ID in the readable form.
   */
  public final static String[] logicalSymbols={"&&","||","!"};

  
  /**
   * Denotes the unary NEGATION operation.
   */
  public final static int UN_NE=0;

  /**
   * Denotes the unary bitwise complement operation.
   */
  public final static int UN_NO=1;
  
  /**
   * Names of unary operations by ID in the readable form.
   */
  public final static String[] unaryNames={"negate","bitwise complement"};

  /**
   * Symbols of unary operations by ID in the readable form.
   */
  public final static String[] unarySymbols={"-","~"};

  
  // NACHALO
  
  private static final byte[] prologue={
    (byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE, // magic CAFE BABE !
    0x00, 0x03,             // minor
    0x00, 0x2D              // major
  };
  
  private int poolEntries=1; // Number of entries in the constant pool
  // Starts from 1  
  
  // <<Here the first CP entry of type UTF8, containing the name of the class,
  // should be written.
  
  private static final byte[] cp_middle={ // initial entries in CP
    /*02*/ 0x07, 0x00, 0x01, // Class name is in CP1 (name of generated class)
    /*03*/ 0x01, 0x00, 0x1A, // UTF 26 bytes "gnu/jel/CompiledExpression"
	   0x67, 0x6E, 0x75, 0x2F, 0x6A, 0x65, 0x6C, 0x2F, 0x43, 0x6F, 0x6D,
	   0x70, 0x69, 0x6C, 0x65, 0x64, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73,
	   0x73, 0x69, 0x6F, 0x6E,
    /*04*/ 0x07, 0x00, 0x03, // Class name is in CP3 (superclass)
    /*05*/ 0x01, 0x00, 0x01, 0x65, // UTF 1 byte "e"
    /*06*/ 0x01, 0x00, 0x13, // UTF 19 bytes "[Ljava/lang/Object;"
	   0x5B, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67,
	   0x2F, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x3B,
    /*07*/ 0x01, 0x00, 0x06, // UTF 6 bytes "<init>"
	   0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E,
    /*08*/ 0x01, 0x00, 0x16, // UTF 22 bytes "([Ljava/lang/Object;)V"
	   0x28, 0x5B, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E,
	   0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x3B, 0x29, 0x56,
    /*09*/ 0x01, 0x00, 0x0A, // UTF 10 bytes "Exceptions"
	   0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73,
    /*10*/ 0x01, 0x00, 0x04, // UTF 4 bytes "Code"
	   0x43, 0x6F, 0x64, 0x65,
    /*11*/ 0x01, 0x00, 0x03, // UTF 3 bytes "()V"
	   0x28, 0x29, 0x56,
    /*12*/ 0x0C, 0x00, 0x07, 0x00, 0x0B, // N&T, N CP7, T CP11   (<init>)
    /*13*/ 0x0A, 0x00, 0x04, 0x00, 0x0C, // Mref, C CP4,N&T CP12 (super.<init>)
    /*14*/ 0x0C, 0x00, 0x05, 0x00, 0x06, // N&T, N CP5, T CP6    (Object[] e;)
    /*15*/ 0x09, 0x00, 0x02, 0x00, 0x0E, // Fref, C CP2, N&T CP14(this.e)
    /*16*/ 0x01, 0x00, 0x07, // UTF 7 bytes "getType"
	   0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65,
    /*17*/ 0x01, 0x00, 0x03, // UTF 3 bytes "()I"
	   0x28, 0x29, 0x49,
    /*18*/ 0x01, 0x00, 0x13, // UTF 19 bytes "java/lang/Throwable"
	   0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F,
	   0x54, 0x68, 0x72, 0x6F, 0x77, 0x61, 0x62, 0x6C, 0x65,
    /*19*/ 0x07, 0x00, 0x12,  // Class name in CP19 ("java/lang/Throwable");
    /*20*/ // DON'T FORGET TO CHANGE CONSTRUCTOR IF YOU ADD SOMETHING !!!
	  
  };
  
  private static final byte[] intermezzo1={ 
    0x00, 0x21,             // Access: PUBLIC, AC_SUPER
    0x00, 0x02,             // This class info is CP2
    0x00, 0x04,             // Super class info is CP4
    0x00, 0x00,             // no interfaces at all
    0x00, 0x01,             // have one field
    /* */  0x00, 0x02,        // PRIVATE
    /* */  0x00, 0x05,        // Name UTF is CP5, (non descriptive? who cares?)
    /* */  0x00, 0x06,        // Descriptor UTF is CP6
    /* */  0x00, 0x00,        // No attributes ( just a field)
    0x00, 0x03,             // have three methods
    /*1*/  0x00, 0x01,        // PUBLIC
    /* */  0x00, 0x07,        // Name UTF is CP7, ("<init>")
    /* */  0x00, 0x08,        // Descriptor UTF is CP8
    /* */  0x00, 0x02,        // two attributes, CODE and exceptions
    /*   */  0x00, 0x09,         //-- attribute name is CP9 ("Exceptions")
    /*     */  0x00, 0x00, 0x00, 0x02, // length is 2 bytes (following)
    /*     */  0x00, 0x00,             // No Checked Exceptions Thrown
    /*   */  0x00, 0x0A,         //-- attribute name is CP10 ("Code")
    /*     */  0x00, 0x00, 0x00, 0x16, // length is 22 bytes
    /*     */  0x00, 0x02,             // stack size is 2
    /*     */  0x00, 0x02,             // locals number is 2
    /*         */  0x00, 0x00, 0x00, 0x0A, // the code is 10 bytes long
    /*       00*/  0x2a,                  // aload_0 
    /*       01*/  (byte)0xB7,0x00,0x0D,  // invokespecial super.<init>
    /*       04*/  0x2a,                  // aload_0 
    /*       05*/  0x2b,                  // aload_1
    /*       06*/  (byte)0xB5,0x00,0x0F,  // putfield this.e
    /*       09*/  (byte)0xB1,            // return void
    /*    */   0x00, 0x00,             // no exception table
    /*    */   0x00, 0x00,             // no code attributes (don't DEBUG ;)
    /*2*/  0x00, 0x01,        // PUBLIC
    /* */  0x00, 0x10,        // Name UTF is CP16, ("getType")
    /* */  0x00, 0x11,        // Descriptor UTF is CP17 "()I"
    /* */  0x00, 0x02,        // two attributes, CODE and exceptions
    /*   */  0x00, 0x09,         //-- attribute name is CP9 ("Exceptions")
    /*     */  0x00, 0x00, 0x00, 0x02, // length is 2 bytes (following)
    /*     */  0x00, 0x00,             // No Checked Exceptions Thrown
    /*   */  0x00, 0x0A,         //-- attribute name is CP10 ("Code")
    /*     */  0x00, 0x00, 0x00, 0x0F, // length is 15 bytes
    /*     */  0x00, 0x01,             // stack size is 2
    /*     */  0x00, 0x01,             // locals number is 2
    /*         */  0x00, 0x00, 0x00, 0x03, // the code is 3 bytes long
    /*       00*/  0x10,                   // bipush
  };
  private byte thetype=99; // The actual type of the expression
  
  private static final byte[] intermezzo2={
    /*       02*/  (byte)0xAC,             // ireturn
    /*    */   0x00, 0x00,             // no exception table
    /*    */   0x00, 0x00,             // no code attributes (don't DEBUG ;)
    /*3*/  0x00, 0x01,        // PUBLIC (THE MAIN METHOD,WHICH DOES EVALUATION)
  };
  short nameIdx=9999;      // Index of the name in CP
  short signIdx=9999;      // Index of descriptor in CP

  private static final byte[] intermezzo3={
    /* */ 0x00, 0x02,        // two attributes, Exceptions and Code
    /*   */ 0x00, 0x09,         // -- attribute name in CP9 ("Exceptions")
    /*     */ 0x00, 0x00, 0x00, 0x04,   // length is 4 bytes (following)
    /*     */ 0x00, 0x01,               // 1 checked exception thrown
    /*     */ 0x00, 0x13,               // Class in CP19, (java.lang.Throwable)
    /*   */  0x00, 0x0A         // -- attribute name is CP10 ("Code")
  };

                    // SHOULD FOLLOW :
                    // u4 length_of_attribute = length_of_code+12
  int max_stack=2;  // u2 stack size
  int max_locals=2; // u2 num locals    //1 this,1 param
                                        // I J J F D D L = +7 = 11;
                    // u4 length_of_code
                    // u1[length_of_code]={THE CODE ITSELF}
  
  private static final byte[] konetc={
    /*    */   0x00, 0x00,             // no exception table
    /*    */   0x00, 0x00,             // no code attributes (don't DEBUG ;)
    /* */ 0x00, 0x00         // No Class File attributes
  };
  
  // FINITA

  /**
   * Classes of the primitive types by ID
   */
  public final static Class[] 
  primitiveTypes={Boolean.TYPE, Byte.TYPE   , Character.TYPE,
		    Short.TYPE  , Integer.TYPE, Long.TYPE,
		    Float.TYPE  , Double.TYPE, Void.TYPE };
  
  private final static char[] primitiveCodes= {'Z','B','C','S',
					       'I','J','F','D','V'};
  
  /**
   * Names of the primitive types by ID in readable form.
   */
  public final static String[] primitiveTypeNames = {
    "boolean","byte","char" ,"short" ,
    "int"    ,"long","float","double"
  };

  // Various useful things for working with StringBuffer-s
  private static java.lang.Class sb_class=null;
  private static java.lang.Class tsb_class=null;
  private static java.lang.Class string_class=null;
  private static java.lang.reflect.Constructor sb_constructor=null;
  private static java.lang.reflect.Method[]
    sb_append_primitive=new java.lang.reflect.Method[primitiveTypes.length-1];
  private static java.lang.reflect.Method sb_append_String=null;
  private static java.lang.reflect.Method sb_append_Object=null;
  private static java.lang.reflect.Method sb_toString=null;
  
  // Prepare StringBuffer append methods
  static {
    try {
      sb_class=Class.forName("java.lang.StringBuffer");
      tsb_class=Class.forName("gnu.jel.TempStringBuffer");
      string_class=Class.forName("java.lang.String");
      
      Class[] meth_par=new Class[0];
      sb_toString=sb_class.getMethod("toString",meth_par);

      meth_par=new Class[1];
      meth_par[0]=string_class;
      sb_constructor=sb_class.getConstructor(meth_par);
      sb_append_String=sb_class.getMethod("append",meth_par);
      meth_par[0]=Class.forName("java.lang.Object");
      sb_append_Object=sb_class.getMethod("append",meth_par);

      for(int i=0;i<primitiveTypes.length-1;i++) {
	// byte is int, short is int
	meth_par[0]=primitiveTypes[i==1?4:i==3?4:i];
	sb_append_primitive[i]=sb_class.getMethod("append",meth_par);
      };
    } catch (NoSuchMethodException e1) {
      if (Debug.enabled)
	Debug.println("Get Yourself a real JAVA.");
      // Can't be in java
    } catch (ClassNotFoundException e2) {
      if (Debug.enabled)
	Debug.println("Get Yourself a real JAVA.");
      // Can't be in java
    };
  };

  private final static byte[] returns={
    (byte)0xAC, (byte)0xAC, (byte)0xAC, (byte)0xAC,
    (byte)0xAC, (byte)0xAD, (byte)0xAE, (byte)0xAF,
  };

  // All conversion tables ignore the type void completely. The object
  // type is represented by primitiveindex == primitiveTypes.length
  
  // Primitive type conversions
  
  // Possible widening conversions
  private final static byte[] cvt_wide= {
    (byte)0x80,0x40,0x60,0x50,0x78,0x5C,0x5E,0x5F
  };

  // Widening conversions decoded:
  //      Z     B     C     S     I     J     F     D   <--- OTKUDA
  //  --------------------------------------------------
  //      1     0     0     0     0     0     0     0  |  Z  
  //      0     1     0     0     0     0     0     0  |  B
  //      0     1     1     0     0     0     0     0  |  C
  //      0     1     0     1     0     0     0     0  |  S
  //      0     1     1     1     1     0     0     0  |  I
  //      0     1     0     1     1     1     0     0  |  J
  //      0     1     0     1     1     1     1     0  |  F
  //      0     1     0     1     1     1     1     1  |  D
  //  --------------------------------------------------


  //  OTKUDA                            Z   B   C   S   I   J   F   D 
  private final static  int[][] cvt1={{000,255,255,255,255,255,255,255}, // Z
				      {145,000,145,145,145,136,139,142}, // B
				      {146,146,000,146,146,136,139,142}, // C
				      {147,147,147,000,147,136,139,142}, // S
   				      {000,000,000,000,000,136,139,142}, // I
				      {133,133,133,133,133,000,140,143}, // J
				      {134,134,134,134,134,137,000,144}, // F
				      {135,135,135,135,135,138,141,000}};// D

  //  OTKUDA                            Z   B   C   S   I   J   F   D 
  private final static  int[][] cvt2={{000,000,000,000,000,000,000,000}, // Z
				      {000,000,000,000,000,145,145,145}, // B
				      {000,000,000,000,000,146,146,146}, // C
				      {000,000,000,000,000,147,147,147}, // S
   				      {000,000,000,000,000,000,000,000}, // I
				      {000,000,000,000,000,000,000,000}, // J
				      {000,000,000,000,000,000,000,000}, // F
				      {000,000,000,000,000,000,000,000}};// D
  
  private final static byte[] stkoccup={ 1,  1,  1,  1,  1,  2,  1,  2};
  
  // Shortcut load opcodes for int type
  // index is value+1; allowed values from -1 to 5.
  // other values should be loaded from CP.
  private final static int[] load_ints=      {   2, 3, 4,   5,   6,   7,   8};
  private final static int[] load_long_ints1={   2, 9,10,   5,   6,   7,   8};
  private final static int[] load_long_ints2={0x85, 0, 0,0x85,0x85,0x85,0x85};


  private final static
    int[][] binary_promotions={
  //                             Z   B   C   S   I   J   F   D 
                               {000,255,255,255,255,255,255,255}, // Z
			       {255,004,004,004,004,005,006,007}, // B
			       {255,004,004,004,004,005,006,007}, // C
			       {255,004,004,004,004,005,006,007}, // S
			       {255,004,004,004,004,005,006,007}, // I
			       {255,005,005,005,005,005,006,007}, // J
			       {255,006,006,006,006,006,006,007}, // F
			       {255,007,007,007,007,007,007,007}};// D
  
  private final static 
  //                             Z   B   C   S   I   J   F   D 
      int[] unary_promotions=  {255,004,004,004,004,005,006,007};


  private final 
    static  int[][][] ops={
                       // First opcode (255 if op is not allowed)
  //                          Z   B   C   S   I   J   F   D 
                           {{255, 96,255, 96, 96, 97, 98, 99},  // PL
			    {255,100,255,100,100,101,102,103},  // MI
			    {255,104,255,104,104,105,106,107},  // MU
			    {255,108,255,108,108,109,110,111},  // DI
			    {255,112,255,112,112,113,114,115},  // RE
			    {126,126,255,126,126,127,255,255},  // AN
			    {128,128,255,128,128,129,255,255},  // OR
			    {130,130,255,130,130,131,255,255},  // XO
			    {  0,  0,  0,  0,  0,148,150,152},  // EQ
			    {  0,  0,  0,  0,  0,148,150,152},  // NE
			    {  0,  0,  0,  0,  0,148,150,152},  // LT
			    {  0,  0,  0,  0,  0,148,149,151},  // GE
			    {  0,  0,  0,  0,  0,148,149,151},  // GT
			    {  0,  0,  0,  0,  0,148,150,152},  // LE
			    {120,120,120,120,120,121,255,255},  // LS
			    {122,122,122,122,122,123,255,255},  // RSS
			    {124,124,124,124,124,125,255,255}}  // RUS
                           };

  // tells if the binary promotion should be performed for given op
  // if false the separate unary promotion of operands should be performed
  // in case of unary promotion (shift operators) result is of the type
  // of the first operand.
  private static final boolean[] is_promotion_binary=
  // PL    MI    MU    DI    RE    AN    OR    XO    EQ    NE    LT    GE
  { true, true, true, true, true, true, true, true, true, true, true, true,
  // GT    LE    LS   RSS   RUS
    true, true,false,false,false};
  
  // tells if the binary operation generated unclosed jump,
  // unclosed jump also means that both operands are removed from stack,
  // no result is loaded
  private static final int[][] openjumps={
  //                          Z   B   C   S   I   J   F   D 
                            {  0,  0,  0,  0,  0,  0,  0,  0},  // PL
			    {  0,  0,  0,  0,  0,  0,  0,  0},  // MI
			    {  0,  0,  0,  0,  0,  0,  0,  0},  // MU
			    {  0,  0,  0,  0,  0,  0,  0,  0},  // DI
			    {  0,  0,  0,  0,  0,  0,  0,  0},  // RE
			    {  0,  0,  0,  0,  0,  0,  0,  0},  // AN
			    {  0,  0,  0,  0,  0,  0,  0,  0},  // OR
			    {  0,  0,  0,  0,  0,  0,  0,  0},  // XO
			    {159,159,159,159,159,153,153,153},  // EQ
			    {160,160,160,160,160,154,154,154},  // NE
			    {161,161,161,161,161,155,155,155},  // LT
			    {162,162,162,162,162,156,156,156},  // GE
			    {163,163,163,163,163,157,157,157},  // GT
			    {164,164,164,164,164,158,158,158},	// LE
			    {  0,  0,  0,  0,  0,  0,  0,  0},  // LS
			    {  0,  0,  0,  0,  0,  0,  0,  0},  // RSS
			    {  0,  0,  0,  0,  0,  0,  0,  0}}; // RUS
    
  private final static byte invert_jump_bytecode(int jmp) {
    if (Debug.enabled)
      Debug.assert((jmp>=153) && (jmp<=164),
		   "Attempt to invert non jump bytecode ("+jmp+")");
    return (byte)(((jmp-1) ^ 0x0001)+1);
  };

  /**
   * Checks if the binary numeric promotion is required for the operation.
   * <P>Used to check whether given binary operation requires binary or
   * unary numeric promotion (see JLS 5.6.1) of it's operands.
   * @param binary_op is one of BI_XXX constants.
   * @return true if binary numeric promotion is required false if unary
   *         numeric promotion is required.
   */
  public static final boolean isPromotionBinary(int binary_op) {
    return is_promotion_binary[binary_op];
  };

  
  
  //                                   Z   B   C   S   I   J   F   D 
  private final static  int[] norm = {000,145,146,147,000,000,000,000};


  private final 
    static int[][][] una ={
                        // First opcode (255 if not allowed)
  //                          Z   B   C   S   I   J   F   D 
                           {{116,116,255,116,116,117,118,119},  // NE
                            {255,  2,  2,  2,  2,  2,255,255}}, // NO
			// Second opcode (0 if not needed)
  //                          Z   B   C   S   I   J   F   D 
			   {{  0,  0,  0,  0,  0,  0,  0,  0},  // NE
			    {  0,130,130,130,130,133,  0,  0}}, // NO
		        // Third opcode (0 if not needed)
  //                          Z   B   C   S   I   J   F   D 
			   {{  0,  0,  0,  0,  0,  0,  0,  0},  // NE
			    {  0,  0,  0,  0,  0,131,  0,  0}}  // NO
                           };

  private final
      static int[][] una_excess_stack = {
  //      Z   B   C   S   I   J   F   D 
	{  0,  0,  0,  0,  0,  0,  0,  0},  // NE
	{  0,  1,  1,  1,  1,  2,  0,  0}, // NO
      };

  //                                   Z   B   C   S   I   J   F   D   L
  private final static byte[] stor = { 54, 54, 54, 54, 54, 55, 56, 57, 58};
  
  private final static byte[] load = { 21, 21, 21, 21, 21, 22, 23, 24, 25};

  private final static byte[] sladdr={  4,  4,  4,  4,  4,  5,  7,  8, 10};
    
  private final static byte CONSTANT_Class = 7;
  private final static byte CONSTANT_Fieldref = 9;
  private final static byte CONSTANT_Methodref = 10;
  private final static byte CONSTANT_InterfaceMethodref = 11;
  private final static byte CONSTANT_String = 8;
  private final static byte CONSTANT_Integer = 3;
  private final static byte CONSTANT_Float = 4;
  private final static byte CONSTANT_Long = 5;
  private final static byte CONSTANT_Double = 6;
  private final static byte CONSTANT_NameAndType = 12;
  private final static byte CONSTANT_Utf8 = 1;
  
  private ByteArrayOutputStream constPool;
  private DataOutputStream constPoolData;

  // Constant pool hashing :
  // In next hashtables keys are Objects, values their CP indices
  // UTFs require special handling since their keys can be the same as
  // ones of strings
  private Hashtable Items=new Hashtable();
  private Hashtable UTFs=new Hashtable();
  
  private PatchableByteArrayOutputStream methodText;
  private DataOutputStream methodTextData;

  private boolean classFinished=false;
  private int currSSW=0; // Current stack size in words
  private Vector objectPool=new Vector(); // pool of constants of type Object
  private Stack typesStk = new Stack(); // Types in Java stack
  int jump_progress = 0; // jump is currently being generated(its code is here)
  
  // Arrays of Params types for functions
  private Stack functionParams = new Stack();
  
  // integer arrays describing the progress of accumulation.
  // {methodref_CP, numparams, currparam, lastparam_stkpos, isstatic}
  private Stack functionINTS = new Stack();

  // Return types of functions
  private Stack functionRet = new Stack();

  // Conditionals 
  private IntegerStack jumps0=new IntegerStack();
  private IntegerStack jumps1=new IntegerStack();
  private IntegerStack jumps =new IntegerStack(); // unconditional jumps
  private IntegerStack blocks0=new IntegerStack();
  private IntegerStack blocks1=new IntegerStack();
  
  // Holds sizes of Java stack after branch (also reused for checking)
  private IntegerStack branchStack=new IntegerStack(); 
  
  private boolean invert_next_jump=false;

  /**
   * Constructs and initializes empty expression image.
   * <P> Immediately after the construction of the expression a number of
   * code generating methods can be issued:
   * <PRE>
   * asm_load_primitive(...)
   * asm_binary_param(..)
   * ...
   * </PRE>
   * <P> The code generation should be finished by one of the return methods:
   * <PRE>
   * asm_return();
   * asm_throw_return();
   * </PRE>
   * <P>Before any return method is called, the code generation is assumed 
   * to be in progress and methods, attempting to get class representation 
   * (getImage) or instantiate the class (getExpression) will fail assertion.
   * <P>After the code generation is finished and one of return methods is 
   * called methods, attempting to modify the class (i.e. 
   * <TT>asm_load_primitive(...)</TT>...) will fail. This is done to ensure 
   * integrity of generated classes.
   * <P> Constants of the type object are allowed in the generated expressions.
   * (If You don't know in java the constants are residing in the constant
   * pool and it is impossible to store constants of general Object type
   * other than java.lang.String there. This assembler overcomes this
   * limitation and allows to use other _NON-MUTABLE_ objects as constants.
   * More information is provided in the description of 
   * <TT>asm_load_object</TT>
   * method.
   * @see gnu.jel.ExpressionImage#asm_load_object
   * @see gnu.jel.ExpressionImage#asm_load_primitive
   * @see gnu.jel.ExpressionImage#asm_binary
   * @see gnu.jel.ExpressionImage#asm_convert
   * @see gnu.jel.ExpressionImage#asm_func_call
   * @see gnu.jel.ExpressionImage#asm_logical_binary
   * @see gnu.jel.ExpressionImage#asm_logical_unblock_not
   * @see gnu.jel.ExpressionImage#asm_unary
   * @see gnu.jel.ExpressionImage#asm_return
   * @see gnu.jel.ExpressionImage#asm_throw_return
   */
  public ExpressionImage() {
    constPool=new ByteArrayOutputStream();
    constPoolData=new DataOutputStream(constPool);
    methodText=new PatchableByteArrayOutputStream();
    methodTextData=new DataOutputStream(methodText);
    
    classFinished=false;
    
    // NOTE that classname is written now by ExpressionBits class.
    poolEntries++;
    
    try {    
      constPoolData.write(cp_middle);  // The rest of CP is already prepared
      poolEntries+=18; // Number of entries in the rest
    } catch (java.io.IOException e) {
      if (Debug.enabled) Debug.reportThrowable(e);
    };
    
  };

  private final void checkAlter() {
    if (Debug.enabled) 
      Debug.assert(!classFinished,"Attempt to modify finished class.");
  };

  /**
   * Used to get the index of the given UTF8 string in the Constant Pool.
   * <P> If the specified string was not found in the pool -- it is added.
   * @param str the string to look for ( to add )
   * @return index of the string in the Constant Pool.
   */
  private int getUTFIndex(String str) {
    // Check if it is in the pool already
    Integer index=(Integer)UTFs.get(str);
    if (index==null) {    // add UTF to the pool
      index=new Integer(poolEntries++);
      try {
	checkAlter();
	constPoolData.write(CONSTANT_Utf8);
	constPoolData.writeUTF(str);
      } catch (java.io.IOException e) {
	if (Debug.enabled) Debug.reportThrowable(e);
      };
      UTFs.put(str,index);
    };
    return index.intValue();
  };
  
  /**
   * Used to get the index of the given Long constant in the Constant Pool.
   * <P> If the specified Long was not found in the pool it is added.
   *
   * @param val the Long to look for ( to add )
   * @return index of the Long in the Constant Pool.
   */
  private int getLongIndex(Long val) {
    // Check if it is in the pool already
    Integer index=(Integer)Items.get(val);
    if (index==null) {    // add Long to the pool
      index=new Integer(poolEntries++);
      try {
	checkAlter();
	constPoolData.write(CONSTANT_Long);
	constPoolData.writeLong(val.longValue());
	poolEntries++; // Long occupies two entries in CP, weird !!!
      } catch (java.io.IOException e) {
	if (Debug.enabled) Debug.reportThrowable(e);
      };
      Items.put(val,index);
    };
    return index.intValue();
  };

  /**
   * Used to get the index of the given Integer constant in the Constant Pool.
   * <P> If the specified Integer was not found in the pool it is added.
   *
   * @param val the Integer to look for ( to add )
   * @return index of the Integer in the Constant Pool.
   */
  private int getIntIndex(Integer val) {
    // Check if it is in the pool already
    Integer index=(Integer)Items.get(val);
    if (index==null) {    // add Integer to the pool
      index=new Integer(poolEntries++);
      try {
	checkAlter();
	constPoolData.write(CONSTANT_Integer);
	constPoolData.writeInt(val.intValue());
      } catch (java.io.IOException e) {
	if (Debug.enabled) Debug.reportThrowable(e);
      };
      Items.put(val,index);
    };
    return index.intValue();
  };

  private int getFloatIndex(Float val) {
    // Check if it is in the pool already
    Integer index=(Integer)Items.get(val);
    if (index==null) {    // add Float to the pool
      index=new Integer(poolEntries++);
      try {
	checkAlter();
	constPoolData.write(CONSTANT_Float);
	constPoolData.writeFloat(val.floatValue());
      } catch (java.io.IOException e) {
	if (Debug.enabled) Debug.reportThrowable(e);
      };
      Items.put(val,index);
    };
    return index.intValue();
  };


  /**
   * Used to get the index of the given Double constant in the Constant Pool.
   * <P> If the specified Double was not found in the pool it is added.
   *
   * @param val the Double to look for ( to add )
   * @return index of the Double in the Constant Pool.
   */
  private int getDoubleIndex(Double val) {
    // Check if it is in the pool already
    Integer index=(Integer)Items.get(val);
    if (index==null) {    // add Double to the pool
      index=new Integer(poolEntries++);
      try {
	checkAlter();
	constPoolData.write(CONSTANT_Double);
	constPoolData.writeDouble(val.doubleValue());
	poolEntries++; // Double occupies two entries in CP, weird !!!
      } catch (java.io.IOException e) {
	if (Debug.enabled) Debug.reportThrowable(e);
      };
      Items.put(val,index);
    };
    return index.intValue();
  };

  /**
   * Used to get the index of the given String constant in the Constant Pool.
   * <P> If the specified String was not found in the pool it is added.
   *
   * @param str the String to look for ( to add )
   * @return index of the String in the Constant Pool.
   */
  private int getStringIndex(String str) {
    // Check if it is in the pool already
    Integer index=(Integer)Items.get(str);
    if (index==null) {    // add String to the pool
      int UTFIndex=getUTFIndex(str); // write string , if needed
      
      index=new Integer(poolEntries++);
      try {
	checkAlter();
	constPoolData.write(CONSTANT_String);
	constPoolData.writeShort(UTFIndex);
      } catch (java.io.IOException e) {
	if (Debug.enabled) Debug.reportThrowable(e);
      };
      
      Items.put(str,index);
    };
    return index.intValue();
  };

  /**
   * Used to get the index of the given Class info in the Constant Pool.
   * <P> If the specified Class info was not found in the pool it is added.
   *
   * @param the_class the Class object of the class to look for ( to add )
   * @return index of the Class info in the Constant Pool.
   */
  private int getClassIndex(Class the_class) {
    // Check if it is in the pool already
    Integer index=(Integer)Items.get(the_class);
    if (index==null) {    // add Class to the pool
      String fqcn=the_class.getName();
      String histNameStr=toHistoricalForm(fqcn);
      
      int UTFIndex=getUTFIndex(histNameStr); // write string , if needed
      
      index=new Integer(poolEntries++);
      writeClassInfo(UTFIndex);
      Items.put(the_class,index);
    };
    return index.intValue();
  };

  private void writeClassInfo(int nameInd) {
    try {
      checkAlter();
      constPoolData.write(CONSTANT_Class);
      constPoolData.writeShort(nameInd);
    } catch (java.io.IOException e) {
      if (Debug.enabled) Debug.reportThrowable(e);
    };
  };

  /*
   * Used to get the index of the given Method reference in the Constant Pool.
   * <P> If the specified Method reference is not yet present in the CP 
   * it is added (along with corresponding NameAndType record).
   *
   * @param the_method Method object to look for ( to add )
   * @return index of the Method Reference in the Constant Pool.
   */
  private int getMethodIndex(java.lang.reflect.Method the_method) {
    // Check if it is in the pool already
    Integer index=(Integer)Items.get(the_method);
    if (index==null) {    // add Method to the pool
      index=new Integer(writeMethodRef(the_method.getName(),
				       getSignature(the_method),
				       the_method.getDeclaringClass()));
      Items.put(the_method,index);
    };
    return index.intValue();
  };

  /*
   * Used to get the index of the given constructor reference in the 
   * Constant Pool.
   * <P> If the specified Constructor reference is not yet present in the CP 
   * it is added (along with corresponding NameAndType record).
   *
   * @param constructor Constructor object to look for ( to add )
   * @return index of the Constructor Reference in the Constant Pool.
   */
  private int getMethodIndex(java.lang.reflect.Constructor constructor) {
    // Check if it is in the pool already
    Integer index=(Integer)Items.get(constructor);
    if (index==null) {    // add Method to the pool
      index=new Integer(writeMethodRef("<init>",
				       getSignature(constructor),
				       constructor.getDeclaringClass()));
      Items.put(constructor,index);
    };
    return index.intValue();
  };


  private int writeMethodRef(String name,String signature,
			     Class declaringClass) {
    int name_ind=getUTFIndex(name);
    int sign_ind=getUTFIndex(signature);
    int cls_ind=getClassIndex(declaringClass);
    
    // Create Name and Type record
    int nat_ind=poolEntries++;
    try {
      checkAlter();
      constPoolData.write(CONSTANT_NameAndType);
      constPoolData.writeShort(name_ind);
      constPoolData.writeShort(sign_ind);
    } catch (java.io.IOException e) {
      if (Debug.enabled) Debug.reportThrowable(e);
    };
    
    int index=poolEntries++;
      
    // Create Methodref or InterfaceMethodref
    int mtd_type=CONSTANT_Methodref;
    if (declaringClass.isInterface()) 
      mtd_type=CONSTANT_InterfaceMethodref;
    
    try {
      checkAlter();
      constPoolData.write(mtd_type);
      constPoolData.writeShort(cls_ind);
      constPoolData.writeShort(nat_ind);
    } catch (java.io.IOException e) {
      if (Debug.enabled) Debug.reportThrowable(e);
    };
    
    return index;
  };
  
  /**
   * Computes signature of the given method.
   * <P> The signature of the method(Method descriptor) is the string and 
   * it's format is described in the paragraph 4.3.3 of the Java VM
   * specification (ISBN 0-201-63451-1).
   * <P> This utility method can be used outside of the JEL package
   * it does not involve any JEL specific assumptions and should follow
   * JVM Specification precisely.
   * @param m is the method to compute the sugnature of.
   * @return the method sugnature.
   */
  public static String getSignature(java.lang.reflect.Method m) {
    StringBuffer signature=new StringBuffer();
    appendParametersSignature(signature,m.getParameterTypes());
    signature.append(getSignature(m.getReturnType()));
    return signature.toString();
  };

  /**
   * Computes signature of the given constructor.
   * @param m is the method to compute the sugnature of.
   * @return the method sugnature.
   * @see gnu.jel.ExpressionImage#getSignature
   */
  public static String getSignature(java.lang.reflect.Constructor c) {
    StringBuffer signature=new StringBuffer();
    appendParametersSignature(signature,c.getParameterTypes());
    signature.append('V');
    return signature.toString();
  };


  private static void appendParametersSignature(StringBuffer signature,
						  Class[] parameters) {
    signature.append('(');
    for(int i=0;i<parameters.length;i++) 
      signature.append(getSignature(parameters[i]));
    signature.append(')');
  };
  
  /**
   * Computes the signature of the given class.
   * <P> The signature of the class (Field descriptor) is the string and 
   * it's format is described in the paragraph 4.3.2 of the Java VM 
   * specification (ISBN 0-201-63451-1).
   * <P>The same can be done using <TT>java.lang.Class.getName()</TT> by 
   * converting it's result into the "historical form".
   * <P> This utility method can be used outside of the JEL package
   * it does not involve any JEL specific assumptions and should follow
   * JVM Specification precisely.
   * @param cls is the class to compute the sgnature of. Can be primitive or
   *            array type.
   * @return the class signature.
   */
  public static String getSignature(Class cls) {
    if (cls.isPrimitive()) 
      return String.valueOf(primitiveCodes[primitiveID(cls)]);
    if (cls.isArray()) return '['+getSignature(cls.getComponentType());
    // Just a class
    return 'L'+toHistoricalForm(cls.getName())+';';
  };

  private static String toHistoricalForm(String className) {
    StringBuffer histName=new StringBuffer(className);
    int namelen=className.length();
    for(int i=className.indexOf('.');(i>0) && (i<namelen);
	i=className.indexOf('.',i+1)) histName.setCharAt(i,'/');
    return histName.toString();
  };

  /**
   * Returns compiled expression represented by ExpressionBits object.
   * <P> This function should be called after the code generation had 
   * finished.
   * <P> The only case You'll want to get <TT>gnu.jel.ExpressionBits</TT>
   * class instead of instantiated and ready to run 
   * <TT>gnu.jel.CompiledExpression</TT> subclass is when You intend to write
   * expression to a persistent storage and run it in other JVM session.
   * @return ready to be serialized ExpressionBits object.
   */
  public ExpressionBits getBits() {
    if (Debug.enabled)
      Debug.assert(classFinished,"Attempt to get the code of unfinished"+
		   " class.");
    try {
      ByteArrayOutputStream image=new ByteArrayOutputStream();
      DataOutputStream imageData=new DataOutputStream(image);
      
      imageData.write(prologue);
      imageData.writeShort(poolEntries);
      
      byte[] beforeName=image.toByteArray();
      
      image.reset();
      constPool.writeTo(imageData);
      imageData.write(intermezzo1);   // start getType
      imageData.write(thetype);       // put the actual type in
      imageData.write(intermezzo2);   // start evaluate_XXX
      imageData.writeShort(nameIdx);  
      imageData.writeShort(signIdx);
      imageData.write(intermezzo3);   // come to evaluate code
      int code_len=methodText.size();
      imageData.writeInt(code_len+12);
      imageData.writeShort(max_stack);
      imageData.writeShort(max_locals);
      imageData.writeInt(code_len);
      methodText.writeTo(imageData);
      imageData.write(konetc);
      
      byte[] afterName=image.toByteArray();

      Object[] objectConstants=new Object[objectPool.size()];
      objectPool.copyInto(objectConstants);      

      return new ExpressionBits(beforeName,afterName,objectConstants);
      
    } catch (java.io.IOException e) {
      if (Debug.enabled) Debug.reportThrowable(e);
      return null;
    };

  };

  /**
   * Used to get the binary image of the class.
   * <P> This function returns bytecode of generated expression in a Java
   * classfile format. It is for debugging purposes only. Use ExpressionBits 
   * if You want to store expression into a stream (file).
   * @return a binary class representation.
   */
  public byte[] getImage() {
    return getBits().getImage();
  };
  
  /**
   * Constructs a new instance of this expression.
   * <P>This function returns ready to run instance of 
   * <TT>gnu.jel.CompiledExpression</TT> subclass.
   * @return a CompiledExpression instance or null if there was a error.
   */
  public CompiledExpression getExpression() {
    return getBits().getExpression();
  };

  private final void ensureStack() {
    if (currSSW>max_stack) max_stack=currSSW;
  };

  private final void codeOP(int op) {
    try {
      methodTextData.write(op);
    } catch (java.io.IOException e) {
      // Can't be
    };
  };

  private final void codeINDEX(int ind) {
    try {
      methodTextData.writeShort(ind);
    } catch (java.io.IOException e) {
      // Can't be
    };
  };
  
  static final int primitiveID(Class c) {
    if (Debug.enabled) 
      Debug.assert(c.isPrimitive(),
		   "PrimitiveID should be used for primitive types only.");
    int i;
    for(i=0;(i<primitiveTypes.length) && (primitiveTypes[i]!=c);i++);
    if (Debug.enabled)
      Debug.assert(i<primitiveTypes.length,
		   "You didn't put _ALL_ primitive types"+
		   " into primitiveTypes array.");
    return i;
  };

  private static final int stackSpace(Class c) {
    if (c.isPrimitive()) return stkoccup[primitiveID(c)];
    return 1;
  };

  // ------------------------- ASSEMBLER ------------------------------
  /**
   * Generates code to load given object constant into Java stack.
   * <P> Constants of type object, other than instances of
   * <TT>java.lang.String</TT> are not directly supported by Java Virtual
   * Machine. This means there is no way to reinstantiate those object
   * based on the information in the Java class file alone.
   * <P> To overcome this limitation an array of such objects is created and
   * only integer index into this "object constants" array is compiled into a
   * class file. The drawback is that the generated class file can not be run
   * without proper object constants array supplied. This means bytecode alone
   * does not represent compiled program anymore.
   * <P> To simplify storage of expressions an ExpressionBits class is 
   * introduced which holds both bytecode and object constants, allowing to
   * easily store them together in a stream.
   * <P> CONCLUSION : if Your class uses this function for objects other than
   * Strings You _must_ use ExpressionBits to store compiled bytecode.
   * 
   * @param o is the object to load.
   * @see gnu.jel.ExpressionBits
   */
  public void asm_load_object(Object o) {
    if (o==null) {
      typesStk.push(null);
      codeOP(0x01);    //|   aconst_null
      currSSW+=1; ensureStack();
    } else if (o instanceof String) {
      typesStk.push(string_class);
      int index=getStringIndex((String)o);
      if (index<255) {
	codeOP(0x12);                            //|   ldc
	codeOP(index);                           //|     String CP index 1 byte
      } else {
	codeOP(0x13);                            //|   ldc_w
	codeINDEX(index);                        //|     String CP index
      };
      currSSW+=1; ensureStack();
    } else {
      // Compute the index into the object pool
      int opi=objectPool.indexOf(o);
      if (opi==-1) {
	objectPool.addElement(o);
	opi=objectPool.size()-1;
      };
      typesStk.push(o.getClass());
      
      currSSW+=2; ensureStack();
      // Generate loading from OP array
      codeOP(0x2a);        //|   aload_0         ; this
      codeOP(0xb4);        //|   getfield
      codeINDEX(15);       //|     00, 0F        ; CP15 FieldRef this.e.
      
      if (opi<255) { // if index is small
	codeOP(0x10);      //|   bipush
	codeOP(opi);       //|     <opi>
      } else {       // if index is large
	int opii=getIntIndex(new Integer(opi));
	codeOP(0x13);      //|   ldc_w
	codeINDEX(opii);   //|     <opii_1><opii_2>
      };
      
      codeOP(0x32);        //|   aaload          ; Object constant loaded
    
      int clsind=getClassIndex(o.getClass());
      codeOP(0xc0);       //|   checkcast          ; runtime cast
      codeINDEX(clsind);  //|     <ind_1><ind_2>  
      
      currSSW--; // temporary stack word is out
    };
    
  };

  /**
   * Generates code to load given constant of a primitive type.
   * <P>For example, to load <TT>double</TT> constant <TT>1.0D</TT> into
   * the Java stack one has to call : <BR>
   * <TT>asm_load_primitive(new Double(1.0))</TT>.
   * @param o is the value of a constant of primitive type, wrapped into
   * corresponding reflection object.
   */
  public void asm_load_primitive(Object o) {
    checkAlter();

    int short_opcode1=0;
    int short_opcode2=0;
    int CP_index=-1;
    int typeid=-1;
    
    if (o instanceof Double) {
      Double dou=(Double)o;
      double dv=dou.doubleValue();
      if (dv==0.0) short_opcode1=0xE;      //| dconst_0
      else if (dv==1.0) short_opcode1=0xF; //| dconst_1
      else CP_index=getDoubleIndex(dou);
      typeid=7;
    } else if (o instanceof Long) {
      Long lo=(Long)o;
      long lv=lo.longValue();
      if ((lv>=-1) && (lv<=5)) {
	short_opcode1=load_long_ints1[(int)lv+1];
	short_opcode2=load_long_ints2[(int)lv+1];
      }	else CP_index=getLongIndex(lo);
      typeid=5;
    } else if (o instanceof Float) {
      Float fo=(Float)o;
      float fv=fo.floatValue();
      if (fv==0.0) short_opcode1=0xB;           //| fconst_0
      else if (fv==1.0) short_opcode1=0xC;      //| fconst_1
      else if (fv==2.0) short_opcode1=0xD;      //| fconst_2
      CP_index=getFloatIndex(fo);
      typeid=6;
    } else if ((o instanceof Integer) || (o instanceof Byte) || 
	       (o instanceof Short) || (o instanceof Character)) {
      typeid=4;
      if (o instanceof Short)          typeid=3;
      else if (o instanceof Character) typeid=2;
      else if (o instanceof Byte)      typeid=1;
      
      int iv;
      if (typeid!=2)
	iv=((Number)o).intValue();
      else
	iv=(int)((Character)o).charValue();
      
      if ((iv>=-1) && (iv<=5)) {
	short_opcode1=load_ints[iv+1];
      } else {
	if (typeid==4) 
	  CP_index=getIntIndex((Integer)o);
	else
	  CP_index=getIntIndex(new Integer(iv));
      };
    } else if (o instanceof Boolean) {
      typeid=0;
      if (((Boolean)o).booleanValue()) 
	short_opcode1=4;
      else
	short_opcode1=3;
    } else { // Object
      if (Debug.enabled) 
	Debug.println("Attempt to load object as a primitive type detected.");
    };
    
    int stack_occupation=stkoccup[typeid];

    if (short_opcode1==0) {
      if (Debug.enabled)
	Debug.assert((stack_occupation==1)||(stack_occupation==2),
		     "Primitive types must occupy 1 or 2 words in"+
		     " java stack.");
      int opload=0x13;                      //| ldc_w
      if (stack_occupation==2) opload=0x14; //| ldc2_w
      
      // attempt short CP ref
      if ((opload==0x13) && (CP_index<255)) {
	codeOP(opload-1);
	codeOP(CP_index);
      } else {  // OK OK making wide
	codeOP(opload);
	codeINDEX(CP_index);
      };
    } else {
      codeOP(short_opcode1);
      if (short_opcode2!=0) codeOP(short_opcode2);
    };
    
    currSSW+=stack_occupation; ensureStack();
    typesStk.push(primitiveTypes[typeid]);
  };

  /**
   * Tests is this assembler can generate code to convert from from one type to another even with possible loss of the information.
   * <P> Both widening and narrowing conversions are supported by the 
   * assembler, this function reports all supported conversions.
   * <P> There are thoughts about support wrapping/unwrapping conversions 
   * (i.e. double <--> java.lang.Double) as described in the Java Reflection 
   * Specification. Currently these are not supported.
   * 
   * @param t1 is the type You want to convert from.
   * @param t2 is the type You want to convert to.
   * @return true is the corresponding conversion is supported.
   */
  public static boolean canConvert(Class t1,Class t2) {
    boolean pt1=t1.isPrimitive();
    boolean pt2=t2.isPrimitive();
    
    if (pt2 && pt1) {
      int it1=primitiveID(t1);
      int it2=primitiveID(t2);
      return (cvt1[it2][it1]!=255);
    };
    
    if (pt2 ^  pt1) return false; // Can't convert yet between PT and OBJ.
    
    return t2.isAssignableFrom(t1);
  };

  /**
   * Tests is this assembler can generate code to convert from 
   * from one type to another without loss of the information.
   * <P> Both widening and narrowing conversions are supported by the 
   * assembler itself, this function reports widening conversions only.
   * 
   * @param t1 is the type You want to convert from.
   * @param t2 is the type You want to convert to.
   * @return true is the corresponding widening conversion is supported.
   */
  public static boolean canConvertByWidening(Class t1,Class t2) {
    boolean pt1=t1.isPrimitive();
    boolean pt2=t2.isPrimitive();
    
    if (pt2 && pt1) {
      int it1=primitiveID(t1);
      int it2=primitiveID(t2);
      return (cvt_wide[it2] & (0x80 >> it1)) >0 ;
    };
    if (pt2 ^  pt1) return false; // Can't convert yet between PT and OBJ.
    return t2.isAssignableFrom(t1);
  };

  /**
   * Converts current top of the java stack to the given class type.
   * @param type is the type to convert to, can be primitive.
   * @return true if conversion was made, false if types incompatible.
   */
  public boolean asm_convert(Class type) {

    Class typeNOW=(Class) typesStk.peek();
    
    if (type==typeNOW) return true;

    boolean ptn=typeNOW.isPrimitive();
    boolean pt=type.isPrimitive();
    
    if (ptn && pt) {
      int tni=primitiveID(typeNOW);
      int ti=primitiveID(type);

      int op1= cvt1[ti][tni]; 
      int op2= cvt2[ti][tni];
      //Debug.println("Converting "+tni+" to "+ti+" opcode="+op1);
      //Debug.assert(false,"see stack");
      if (op1==255) {
	if (Debug.enabled) 
	  Debug.assert(false,"Incompatible types in asm_convert");
	return false;  
      };
      
      currSSW=currSSW+stkoccup[ti]-stkoccup[tni]; ensureStack();
      if (op1!=0) codeOP(op1);
      if (op2!=0) codeOP(op2);
      typesStk.pop();
      typesStk.push(type);
      return true;
    };
    
    if (ptn ^ pt) {
      if (Debug.enabled)
	Debug.println("Can't convert between pimitive and object types in"+
		      " this version.");
      return false;
    };

    typesStk.pop();
    typesStk.push(type);
   
    return true; // classes can only be downcasted ( no (Class)var syntax yet)
    
  };

  /**
   * Performs binary numeric promotion of types.
   * <P> Promotion is done according to the Java Language Specification
   * (paragraph 5.6.2). Both parameters should be Java primitive types.
   * @param c1  first type
   * @param c2  second type
   * @return Class (primitive type reflection), representing 
   * the binary promoted type, null if can't promote.
   */
  public static Class getBinaryPromoted(Class c1, Class c2) {
    
    if (Debug.enabled)
      Debug.assert(c1.isPrimitive() && c2.isPrimitive(),
		   "Both types for the binary promotion should be primitive.");
    int ic1=primitiveID(c1);
    int ic2=primitiveID(c2);
    int idx=binary_promotions[ic1][ic2];
    if (idx==255) return null;
    return primitiveTypes[idx];
  };

  /**
   * Performs unary numeric promotion of types.
   * <P> Promotion is done according to the Java Language Specification
   * (paragraph 5.6.1). Both parameters should be Java primitive numeric types.
   * @param c  the type to promote
   * @return Class (primitive type reflection), representing 
   * unary promoted type, null if can't promote.
   */
  public static Class getUnaryPromoted(Class c) {
    if (Debug.enabled)
      Debug.assert(c.isPrimitive(),
		   "Both types for the binary promotion should be primitive.");

    int ic=unary_promotions[primitiveID(c)];
    if (ic==255) return null;
    return primitiveTypes[ic];
  };

  static int getUnaryPromoted(int cid) {
    return unary_promotions[cid];
  };

  /**
   * Used to test if this assembler can generate given unary operation.
   * @param is the code of operation (UN_NE).
   * @param type is the type of the operand.
   * @return if the code for such operation can be generated.
   */
  public static boolean canGenerateUnary(int op, Class type) {
    if (!type.isPrimitive()) return false;
    return (una[0][op][primitiveID(type)]!=255);
  };
  


  /**
   * Generates code to perform given unary operation on the value in stack.
   * <P> Unary operations can be performed on the primitive
   * Java types only.
   * <P> If operation can not be supported for the given type
   * this method returns false and generates nothing.
   * @param o type of operation to perform, one of NE
   */
  public void asm_unary(int o) {
    ensure_value();
    Class a=(Class) typesStk.peek();

    if (!a.isPrimitive()) {
      if (Debug.enabled)
	Debug.println("Unary OPs are supported on primitive types only");
      return;
    };
    
    int ia=primitiveID(a);

    if (una[0][o][ia]==255) {
      if (Debug.enabled)
	Debug.println("The unary operation requested ("+o+") is not supported"+
		      " for type in stack ("+ia+").");
      return;
    };

    for(int i=0;i<una.length;i++) {
      int opc=una[i][o][ia];
      if (opc>0) codeOP(opc);
    };
    
    // note possible extra item in stack
    int excess_stack=una_excess_stack[o][ia];
    currSSW+=excess_stack;
    ensureStack();
    currSSW-=excess_stack;
    
    // note unary promotion (does not change the stack occupation)
    typesStk.pop();
    typesStk.push(primitiveTypes[unary_promotions[ia]]);
  };

  /**
   * Used to test if this  assembler can generate given binary operation.
   * @param is the code of operation (BI_PL,BI_MI,BI_MU ...).
   * @param t1 is the type of the first operand.
   * @param t2 is the type of the second operand.
   * @return if the code for such operation can be generated.
   */
  public static boolean canGenerateBinary(int op, Class t1, Class t2) {
    // string concatenation (is first is string or tsb the other side can
    // be of any type)
    if ((op==BI_PL) && ((t1==string_class) || (t1==tsb_class)))
      return true;
    
    // operations on primitives
    if (!(t1.isPrimitive() && t2.isPrimitive())) return false;
    int it1=primitiveID(t1);
    int it2=primitiveID(t2);
    
    if (is_promotion_binary[op]) {
      int promoted_type=binary_promotions[it1][it2];
      if (promoted_type==255) return false; // incompatible types
      return (ops[0][op][promoted_type]!=255);
    };

    // otherwise it is a shift operator (both operands should be 
    // integral types)
    return (it1>=1) && (it1<=5) && (it2>=1) && (it2<=5);
  };

  private void normalize_tsb() {
    Class top=(Class)typesStk.peek();
    if (top!=tsb_class) return;
    codeOP(182); //| invokevirtual
    codeINDEX(getMethodIndex(sb_toString));
    typesStk.pop();
    typesStk.push(string_class);
  };

  /**
   * Generates code to perform given binary operation.
   * <P> Method canGenerateBinary(..) should be used to determine
   * if the binary operation can be generated for particular types of data.
   * <P> The pattern to generate binary operations with this code generator
   * is following :
   * <PRE>
   * 1. Calculate the first operand.
   * 2. asm_binary_param( BI_XXX )
   * 3. Calculate the second operand.
   * 4. asm_binary( BI_XXX )
   * </PRE>
   * <P> If operation can not be supported due to incompatible types of
   * operands this method returns false and generates nothing.
   *
   * @param o type of operation to perform, one of BI_XXX
   * @see gnu.jel.ExpressionImage#canGenerateBinary
   * @see gnu.jel.ExpressionImage#asm_binary_param
   */
  public void asm_binary(int o) {
    ensure_value();

    // peek two types on stack
    Class a2=(Class)typesStk.peek();
    Object tmp=typesStk.pop();
    Class a1=(Class)typesStk.peek();
    typesStk.push(tmp);

    if ((o==BI_PL) && (a1==tsb_class)) { // String concatenation
      codeOP(182); //| invokevirtual
      if (a2.isPrimitive())
	codeINDEX(getMethodIndex(sb_append_primitive[primitiveID(a2)]));
      else if (a2==string_class)
	codeINDEX(getMethodIndex(sb_append_String));
      else 
	codeINDEX(getMethodIndex(sb_append_Object));

      typesStk.pop();  currSSW-=stackSpace(a2);
      // StringBuffer remains because it was duplicated before
      
      return;
    };
    
    // Primitive types binary op
    if ((!a1.isPrimitive()) || (!a2.isPrimitive())) {
      if (Debug.enabled)
	Debug.println("Binary OPs are supported on primitive types only");
      return;
    };

    int ia1=primitiveID(a1);
    int ia2=primitiveID(a2);
    int restype=ia1;
    
    if (is_promotion_binary[o] && (ia1 != ia2)) {
      if (Debug.enabled)
	Debug.println("Types are not the same ("+ia1+"!="+ia2+
		      "), for operations with binary promotions "+
		      "conversion should be done prior to OP.");
      return;
    };
    

    if (!is_promotion_binary[o]) { 
      // make conversion of the second operand of binary ops with
      // unary promotions (only for shift commands);
      restype=unary_promotions[ia1];
      
      // the second operand of shifts is always of type int
      asm_convert(Integer.TYPE);
      // note the possible change of the type and the stack occupation
      a2=(Class)typesStk.peek();
      ia2=primitiveID(a2);
    };

    if (ops[0][o][restype]==255) {
      if (Debug.enabled)
	Debug.println("The binary operation requested ("+o+") is not "+
		      "supported for types in stack ("+ia1+") .");
      return;
    };

    // Adjust types in stack (throw arguments)
    typesStk.pop();  currSSW-=stkoccup[ia2]; 
    typesStk.pop();  currSSW-=stkoccup[ia1];

    if (Debug.enabled)
      Debug.assert(jump_progress==0,"Attempt to generate binary operation"+
		   " without finishing the jump") ;

    // gen. code
    for(int i=0;i<ops.length;i++) {
      int opc=ops[i][o][restype];
      if (opc>0) codeOP(opc);
    };

    // The jump command is not written to the stream at this point.
    jump_progress=openjumps[o][restype]; 
    if (jump_progress==0) { // jumps do not load anything to the stack
      typesStk.push(primitiveTypes[restype]); currSSW+=stkoccup[restype];
    };
    // stack occupation should always decrease, no ensurestack is needed
  };


  /**
   * Starts generation of code for the method call.
   * <P> Method can be static or virtual. See the descrition of
   * <TT>asm_func_call</TT> for more details.
   * @param f is the method to call.
   * @param id is the number of the function in the array of the 
   *    instances of objects implementing the function (for virtual
   *    methods only).
   * @see gnu.jel.ExpressionImage#asm_func_call
   */
  public void asm_func_start(java.lang.reflect.Method f,int id) {
    Class[] params=f.getParameterTypes();
    functionParams.push(params);
    boolean isStatic=java.lang.reflect.Modifier.isStatic(f.getModifiers());
    int[] descr={getMethodIndex(f),(isStatic?1:0),params.length,0,currSSW};
    functionINTS.push(descr);
    functionRet.push(f.getReturnType());
    if (!isStatic) { // load Object reference to stack
      checkAlter();
      currSSW+=2; ensureStack();
      codeOP(0x2b);     //|  aload_1   ; load dynamic lib reference

      if (id<255) { // if index is small
	codeOP(0x10);     //|   bipush
	codeOP(id);       //|     <opi>
      } else {       // if index is large
	int idi=getIntIndex(new Integer(id));
	codeOP(0x13);     //|   ldc_w
	codeINDEX(idi);  //|     <opii_1><opii_2>
      };
      
      codeOP(0x32);      //|  aaload    ; load dynamic module reference

      int cls_CP=getClassIndex(f.getDeclaringClass());
      codeOP(0xc0);      //|  checkcast  ; cast to the right class
      codeINDEX(cls_CP); //|   <ind_1><ind_2>
      currSSW--;
      descr[4]=currSSW;
    };
    asm_logical_block();
  };

  /**
   * Denotes that the next parameter for the current function is now in stack.
   * <P>Actually, this method generates code to convert the value currently 
   * on top of Java stack to the next formal parameter type
   * of a current function.
   * @return false if types are incompatible.
   * @see gnu.jel.ExpressionImage#asm_func_start
   */
  public void asm_func_param() {
    if (Debug.enabled)
      Debug.assert(!functionParams.empty(),
		   "No function call being generated.");

    ensure_value();
    asm_logical_unblock();
    normalize_tsb();

    Class[] params= (Class[])functionParams.peek();
    int[] descr=(int[])functionINTS.peek();
    int currpar=descr[3];
    if (Debug.enabled)
      Debug.assert(currpar<descr[2],
		   "Too many parameters for function");
    
    boolean converted=asm_convert(params[currpar]);
    
    if (!converted) {
      if (Debug.enabled)
	Debug.println("Can't convert to the required formal parameter type.");
      return;
    };
    
    if (Debug.enabled)
      Debug.assert(currSSW==descr[4]+stackSpace(params[currpar]),
		   "Stack mismatch. Something was left in the stack in "+
		   "between of function formal parameters.");
    descr[4]=currSSW;
    descr[3]++; // next parameter.
    asm_logical_block();
  };
  
  /**
   * Finishes generation of call to a function.
   * <P> Call to this method should be followed zero or more asm_func_param()
   * calls which would transform parameters on top of the stack to be 
   * compatible with the function input (each additioal call will process
   * the next formal parameter).
   * <P>The pattern to generate the function call is the following
   * <PRE>
   * 1. asm_func_start(...);
   * 2. Calculate the first parameter.
   * 3. asm_func_param();
   * 4. Calculate the second parameter.
   * 5. asm_func_param();
   *   .... so on ...
   * 6  Calculate the last parameter.
   * 7. asm_func_param();
   * 8. asm_func_call();
   * </PRE>
   * <P> Correctness of <TT>id</TT> parameter can not be checked by the code 
   * generator !!! Be careful, runtime error will be generated if it's
   * incorrect.
   * @see gnu.jel.ExpressionImage#asm_func_param
   * @see gnu.jel.ExpressionImage#asm_func_start
   */
  public void asm_func_call() {
    asm_logical_unblock();
    if (Debug.enabled)
      Debug.assert(!functionParams.empty(),
		   "No function call being generated.");
    Class[] params= (Class[])functionParams.pop();
    int[] descr=(int[])functionINTS.pop();
    Class retType=(Class)functionRet.pop();
    int currpar=descr[3];

    if (currpar!=descr[2]) {
      if (Debug.enabled)
	Debug.println("Wrong number of parameters supplied to a function.");
      return;
    };

    boolean isStatic=(descr[1]==1);
    int totstk=(isStatic?0:1);
    
    checkAlter();
    if (isStatic) {
      codeOP(0xb8);       //| invokestatic
    } else {
      codeOP(0xb6);       //| invokevirtual
    };
    codeINDEX(descr[0]);  //|   <ind_1><ind_2>
    for(int i=0;i<params.length;i++) {
      totstk+=stackSpace(params[i]);
      typesStk.pop();
    };
    currSSW-=totstk;
          
    typesStk.push(retType);
    currSSW+=stackSpace(retType); ensureStack();
  };

  /**
   * Starts generation of code for conditional. 
   *
   * <P>Starts the branch, 
   * corresponding to the case when previous logical was "true".
   * expression.
   * <P> The pattern to generate an "if" operator or ?: conditional is 
   * the following :
   * <PRE>
   * 1. Calculate condition 
   * 2. asm_branch_start_true()
   * 3. Generate code for the "true" branch
   * 4. asm_branch_start_false()
   * 5. Generate code for the "false" branch
   * 6. asm_branch_end()
   * </PRE>
   * <P> Note that the code for "true" branch should be generated BEFORE
   * the code for the current branch. This limitation may be removed in a
   *  future.
   */
  public void asm_branch_start_true() {
    ensure_jump(); // true jump first
    
    if (!invert_next_jump)
      jump_progress=invert_jump_bytecode(jump_progress);
    invert_next_jump=false;
    codeOP(jump_progress);
    jump_progress=0;
    mkLabel(jumps0); 
    landLabels(jumps1,blocks1);
    blockLabels(jumps0,blocks0);
    // Size of typesStk before first branch
    branchStack.push(typesStk.size());
  };

  /**
   * Continues generation of code for conditional.
   * @see gnu.jel.ExpressionImage#asm_branch_start_true
   */
  public void asm_branch_start_false() {
    ensure_value();
    unblockLabels(jumps0,blocks0);

    int beforeStk=branchStack.pop();
    
    if (Debug.enabled)
      branchStack.push(currSSW);

    // remove the result of the previous branch, the other branch must
    // put the same thing back into the types stack
    while (beforeStk<typesStk.size()) {
      currSSW-=stackSpace((Class)typesStk.pop());       
    };
    
    codeOP(167);   //|   goto
    mkLabel(jumps);
    landLabels(jumps0,blocks0);
  };

  /**
   * Finishes generation of code for conditional.
   * @see gnu.jel.ExpressionImage#asm_branch_start_true
   */
  public void asm_branch_end() {
    ensure_value();
    if (Debug.enabled)
      Debug.assert(branchStack.pop()==currSSW,
		   "Stack mismatch when compiling conditional");
    landLabel(jumps);
  };


  private final void mkLabel(IntegerStack jumps) {
    int currpos=methodText.size();
    jumps.push(currpos);
    try {
      methodTextData.writeShort(0); // placeholder for the backpatched address
    } catch (java.io.IOException e) {
      // Can't be
    };
  };

  private final void landLabel(IntegerStack jumps) {
    int currpos=methodText.size();
    int addrpos=jumps.pop();
    
    // in the next command -1 because displacement is counted from the
    // jump opcode
    methodText.patchAddress(addrpos,currpos-addrpos+1); 
  };
  
  private final void landLabels(IntegerStack jumps,IntegerStack blocks) {
    int blocked_at=0;
    if (blocks.size()>0) blocked_at=blocks.peek();
    while (jumps.size()>blocked_at) landLabel(jumps);
  };

  private final void blockLabels(IntegerStack jumps,IntegerStack blocks) {
    blocks.push(jumps.size());
  };
    
  private final void unblockLabels(IntegerStack jumps,IntegerStack blocks) {
    //  if (Debug.enabled) {
    //  int blockpos=0;
    //  if (!blocks.isEmpty()) blockpos=((Integer)blocks.peek()).intValue();
    //  Debug.assert(jumps.size()==blockpos,
    //		   "Some labels were not landed");
    //};
    blocks.pop_throw();
  };

  /**
   * Denotes that the first parameter for the given binary OP is now in stack.
   * @param op type of operation to perform, one of BI_XXX
   * @see gnu.jel.ExpressionImage#asm_binary
   */
  public void asm_binary_param(int opc) {
    ensure_value();
    
    // String concatenation
    if ((opc==BI_PL) && (typesStk.peek()==string_class)) {
      // Convert String to StringBuffer
	codeOP(187);                               //| new
	codeINDEX(getClassIndex(sb_class));        //|     StringBuffer
	codeOP(90);                                //| dup_x1
	codeOP(90);                                //| dup_x1
	codeOP(87);                                //| pop
	codeOP(183);                               //| invokespecial
	codeINDEX(getMethodIndex(sb_constructor)); //|     StringBuffer.<init>
	typesStk.pop(); typesStk.push(tsb_class);
	currSSW+=3; ensureStack(); currSSW-=3;
    }; // wrong types will be checked by binaryop
  };

  /**
   * Denotes the first parameter for the given logical binary OP is 
   * now in stack.
   * @param opc is the code of the logical binary operation (LOG_AN,LOG_OR)
   * @see gnu.jel.ExpressionImage#asm_logical_binary
   */
  public void asm_logical_binary_param(int opc) {
    switch(opc) {
    case LOG_AN: asm_logical_binary_param_and(); break;
    case LOG_OR: asm_logical_binary_param_or(); break;
    default:
      if (Debug.enabled)
	Debug.println("Wrong operation code ("+opc+") for logical operation.");
    };
  };

  /**
   * Generates code to perform given logical binary operation.
   * <P> The logical binary operations are either logical and (LOG_AN) or
   * logical or (LOG_OR).
   * <P> The following pattern should be followed in order to evaluate 
   * logical expressions :
   * <PRE>
   * 1. Calculate the first operand (should be boolean).
   * 2. asm_logical_binary_param( <LOG_AN> | <LOG_OR> )
   * 3. Calculate the second operand (should be boolean).
   * 4. asm_logical_binary( <LOG_AN> | <LOG_OR> )
   * </PRE>
   * @param opc is the code of the logical binary operation (LOG_AN,LOG_OR)
   */
  public void asm_logical_binary(int opc) {
    switch(opc) {
    case LOG_AN: asm_logical_binary_and(); break;
    case LOG_OR: asm_logical_binary_or(); break;
    default:
      if (Debug.enabled)
	Debug.println("Wrong operation code ("+opc+") for logical operation.");
    };
  };
  
  private final void asm_logical_binary_param_and() {
    logical_param(true,jumps0,jumps1,blocks0,blocks1);
  };

  private final void asm_logical_binary_param_or() {
    logical_param(false,jumps1,jumps0,blocks1,blocks0);
  };

  private void logical_param(boolean and,IntegerStack jumps0,
			     IntegerStack jumps1,IntegerStack blocks0,
			     IntegerStack blocks1) {
    ensure_jump();
    if (invert_next_jump ^ and)
      jump_progress=invert_jump_bytecode(jump_progress);
    invert_next_jump=false;
    codeOP(jump_progress);
    jump_progress=0;
    mkLabel(jumps0);
    landLabels(jumps1,blocks1);
    blockLabels(jumps0,blocks0);
  };

  private final void asm_logical_binary_and() {
    unblockLabels(jumps0,blocks0);
  };

  private final void asm_logical_binary_or() {
    unblockLabels(jumps1,blocks1);
  };

  /**
   * This function starts a group of logical subexpressions.
   * @see gnu.jel.ExpressionImage#asm_logical_unblock_not
   */  
  public void asm_logical_block() {
    blockLabels(jumps0,blocks0);
    blockLabels(jumps1,blocks1);
  };

  private void asm_logical_unblock() {
    unblockLabels(jumps0,blocks0);
    unblockLabels(jumps1,blocks1);
  };

  /**
   * This function finishes a group of logical subexpressions with inversion.
   * <P>For example, to calculate "!(..<I>something</I>..)" it is
   *  needed to call :
   * <PRE>
   * asm_logical_block()
   * ....
   * calculate <I>something</I> manipulating with other methods of this code generator
   * ....
   * asm_logical_unblock_not();
   * </PRE>
   * <P>After the above block the state of this codegenerator will correspond
   * to the boolean result of the logical not operator on <I>something</I>.
   */
  public void asm_logical_unblock_not() {
    ensure_jump();
    IntegerStack.swap(jumps1,blocks1.pop(),jumps0,blocks0.pop());
    invert_next_jump=!invert_next_jump;
  };
  
  // Makes sure that currently jump is in progress
  // as with binary comparizon ops jump in progress is always
  // a jump to "true" label.
  private final void ensure_jump() {
    if (jump_progress!=0) return;
    Class a1=(Class)typesStk.pop();
    if (Debug.enabled)
      Debug.assert(a1==Boolean.TYPE,"Only booleans can generate jumps in "+
		   "ensurejump()");
    currSSW-=stkoccup[0]; // pop boolean out of stack
    jump_progress=157; //|
  };
  
  // ensures that the boolean is now in java stack and all unblocked 
  // labels were landed
  // this should be called every time the boolean value is used :
  // before giving it as a parameter to a function; before returning the 
  // boolean value.
  private final void ensure_value() {
    if ((jump_progress==0) && (noPendingJumps())) return;
    // (typesStk.peek()!=Boolean.TYPE) &&
    //  Debug.println("actively ensuring value.");
    asm_branch_start_true();
    asm_load_primitive(Boolean.TRUE);
    asm_branch_start_false();
    asm_load_primitive(Boolean.FALSE);
    asm_branch_end();
  };

  private final boolean noPendingJumps() {
    int blocked0=0;
    if (blocks0.size()>0) blocked0=blocks0.peek();
    
    int blocked1=0;
    if (blocks1.size()>0) blocked1=blocks1.peek();

    return (jumps0.size()==blocked0) && (jumps1.size()==blocked1);
  };

  /**
   * Finishes construction of expression by generating code to return a value.
   * <P> Code to return to a caller a quantity on top of the Java stack is
   * generated.
   */
  public void asm_return() {
    checkAlter();
    ensure_value();
    normalize_tsb();

    if (Debug.enabled)
      Debug.assert((jumps0.size()==0) && (blocks0.size()==0) &&
		   (jumps1.size()==0) && (blocks1.size()==0) &&
		   (jumps.size()==0),"Some labels were not landed and/or "+
		   "blocks removed.");

    Class typeout=null;
    boolean returnvoid=false;
    try { typeout=(Class) typesStk.peek(); } 
    catch (java.util.EmptyStackException e) {
      // Nothing in stack is the same as void in stack and is returned as null
      asm_load_object(null);
      returnvoid=true;
      //if (Debug.enabled) Debug.reportThrowable(e);
    };

    // Find out the result type
    int prid=primitiveTypes.length; // void = 8
    byte instruction=(byte) 0xB0;         // return instruction : areturn
    if (!returnvoid) {
      prid++; // Object =9;
      if ((typeout!=null) && (typeout.isPrimitive())) {
	prid=primitiveID(typeout);
	instruction=returns[prid];
      };
    };

    StringBuffer mname=new StringBuffer("evaluate");
    StringBuffer signature=new StringBuffer("([Ljava/lang/Object;)");
    // Form signature and name
    if (prid<primitiveTypes.length) {
      mname.append('_'); mname.append(primitiveTypeNames[prid]);
      signature.append(primitiveCodes[prid]);
    } else {
      signature.append("Ljava/lang/Object;");
    };
    
    // put name & signature into cp
    nameIdx=(short)getUTFIndex(mname.toString());
    signIdx=(short)getUTFIndex(signature.toString());
    
    // Mark type
    thetype=(byte)prid;
    
    // Generate return instruction
    typesStk.pop();
    
    if (Debug.enabled) 
      Debug.assert(typesStk.size()==0,"Stack is not empty when returning.");
    
    if (Debug.enabled)
      Debug.assert((prid>7) || (currSSW==stkoccup[prid]),
		   "Words left in stack = "+currSSW+"should be 1.");
    
    codeOP(instruction);              //|  XXXreturn
    classFinished=true;
  };
  
  /**
   * Finishes construction of expression by generating code to throw exception.
   * <P> Object on top of the Java stack (which should be instance of
   * <TT>java.lang.Throwable</TT>) is thrown.
   */
  public void asm_throw_return() {
    checkAlter();
    Class typeout=null;
    try { typeout=(Class) typesStk.pop(); } 
    catch (java.util.EmptyStackException e) {
      if (Debug.enabled) Debug.reportThrowable(e);
    };
    if (Debug.enabled) { // Check cast
      try { 
	Class thr=Class.forName("java.lang.Throwable");
	Debug.assert(thr.isAssignableFrom(typeout),
	       "Attempt to throw non throwable.");
      } catch (ClassNotFoundException e) {
      };
    };
    codeOP(0xbf);               //|  athrow
    classFinished=true;
  };

  // ------------------------- TESTSUITE -------------------------------

  /**
   * Performs unitary test of the code generator.
   * @param args ignored.
   */
  public static void main(String[] args) {
    Tester t=new Tester(System.out);
    test(t);
    t.summarize();
  };

  static void dumpImage(ExpressionImage ei) {
    if (Debug.enabled) {
      try {
	java.io.FileOutputStream fos=
	  new java.io.FileOutputStream("dump.class");
	fos.write(ei.getImage());
	fos.close();
      } catch (Exception e) {
	Debug.println("Can't dump generated class file.");
      };
    };
  };

  private static boolean transmitPrimitive(Object obj,Object tobe) 
       throws Throwable {
    if (Debug.enabled) {
      ExpressionImage ei=null;
      Object res=null;
      try {
	ei=new ExpressionImage();
	ei.asm_load_primitive(obj);
	ei.asm_return();
	CompiledExpression cex=ei.getExpression();
	res=cex.evaluate(null);
      } catch (Throwable e) {
	dumpImage(ei);
	throw e;
      };
      boolean success=res.equals(tobe);
      if (!success) {
	Debug.println("Transmit of \""+obj.getClass().getName()+"\" failed.");
	Debug.println("Expected \""+tobe.toString()+"\",  encountered \""+
		      res.toString()+"\".");
	dumpImage(ei);
      };
      return success;
    } else return true;
  };

  /**
   * Performs unitary test of the code generator.
   * <p> Used if all package is being tested and not just codegen.
   * @param t Tester to report test results.
   */
  public static void test(Tester t) {
    if (Debug.enabled) {
      // Utility classes tests
      {
	t.startTest("toHistoricalForm(\"java.lang.String\")");
	t.compare(toHistoricalForm("java.lang.String"),
		  "java/lang/String");

	t.startTest("getSignature((\"a string\").getClass())");
	t.compare(getSignature(("a string").getClass()),
		  "Ljava/lang/String;");
	
	t.startTest("getSignature((new int[10]).getClass())");
	t.compare(getSignature((new int[10]).getClass()),
		  "[I");
	
	t.startTest("getSignature((new Object[10]).getClass())");
	t.compare(getSignature((new Object[10]).getClass()),
		  "[Ljava/lang/Object;");

      };

      // Constant pool filling test
      {
	ExpressionImage ei=new ExpressionImage();
	
	t.startTest("Add UTF twice");
	String s1="some string";
	int si=ei.getUTFIndex(s1);
	int sii=ei.getUTFIndex(s1);
	t.compare(sii,si);

	t.startTest("Checking the index of added UTF");
	t.compare(si,20);
	
	t.startTest("Add Long twice");
	int li=ei.getLongIndex(new Long(15));
	int lii=ei.getLongIndex(new Long(15));
	t.compare(lii,li);

	t.startTest("Add Integer twice");
	int ii=ei.getIntIndex(new Integer(15));
	int iii=ei.getIntIndex(new Integer(15));
	t.compare(iii,ii);

	t.startTest("Add Float twice");
	int ff=ei.getFloatIndex(new Float(15.0f));
	int fff=ei.getFloatIndex(new Float(15.0f));
	t.compare(fff,ff);

	t.startTest("Add Double twice");
	int di=ei.getDoubleIndex(new Double(15.0));
	int dii=ei.getDoubleIndex(new Double(15.0));
	t.compare(dii,di);

	t.startTest("Add a new String twice");
	String s2="some other string";
	int s2i=ei.getStringIndex(s2);
	int s2ii=ei.getStringIndex(s2);
	t.compare(s2ii,s2i);

	t.startTest("Add a string with existing UTF twice");
	int s3i=ei.getStringIndex(s1);
	int s3ii=ei.getStringIndex(s1);
	t.compare(s3ii,s3i);
	
	t.startTest("Add a class twice.");
	int ci=ei.getClassIndex(ei.getClass());
	int cii=ei.getClassIndex(ei.getClass());
	t.compare(cii,ci);
	
	t.startTest("Add a method twice.");
	Class[] params=new Class[1];
	java.lang.reflect.Method a_method;
	try {
	  params[0]=Class.forName("gnu.jel.debug.Tester");
	  a_method=ei.getClass().getMethod("test",params);
	  int mi=ei.getMethodIndex(a_method);
	  int mii=ei.getMethodIndex(a_method);
	  t.compare(mii,mi);
	} catch (Exception e) {
	  t.testFail();
	  Debug.reportThrowable(e);
	};

	t.startTest("Counting added entries ");
	t.compare(ei.poolEntries,14+2+19+1);
        // 14(entries added), 2(longs/doubles), 19(in cp_start), 1(zeroth) 

	t.startTest("Loading and executing via getExpression");
	try {
	  boolean ok=true;
	  ei.asm_load_object(null);
	  ei.asm_return();
	  CompiledExpression cell=ei.getExpression();
	  ok = ok && (cell.evaluate(null)==null);
	  if (!ok) dumpImage(ei);
	  if (ok) t.testOK(); else t.testFail();
	} catch (java.lang.Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};

	t.startTest("Check transmission of Object constants");
	try {
	  ExpressionImage e=new ExpressionImage();
	  e.asm_load_object(e);
	  e.asm_return();
	  CompiledExpression cell=e.getExpression();
	  boolean ok = (cell.evaluate(null)==e);
	  if (!ok) dumpImage(e);
	  if (ok) t.testOK(); else t.testFail();
	} catch (java.lang.Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};

	t.startTest("Check load/return of primitives (Z,B,C,S,I,J,F,D)");
	try {
 if      (!transmitPrimitive(new Long(0L),new Long(0L))) t.testFail();
 else if (!transmitPrimitive(new Long(1L),new Long(1L))) t.testFail();
 else if (!transmitPrimitive(new Long(-1L),new Long(-1L))) t.testFail();
 else if (!transmitPrimitive(new Long(2L),new Long(2L))) t.testFail();
 else if (!transmitPrimitive(new Long(3L),new Long(3L))) t.testFail();
 else if (!transmitPrimitive(new Long(5L),new Long(5L))) t.testFail();
 else if (!transmitPrimitive(new Long(6L),new Long(6L))) t.testFail();
 else if (!transmitPrimitive(new Double(0.0d),new Double(0.0d))) t.testFail();
 else if (!transmitPrimitive(new Double(1.0d),new Double(1.0d))) t.testFail();
 else if (!transmitPrimitive(new Double(2.0d),new Double(2.0d))) t.testFail();
 else if (!transmitPrimitive(new Double(3.0d),new Double(3.0d))) t.testFail();
 else if (!transmitPrimitive(new Integer(0),new Integer(0))) t.testFail();
 else if (!transmitPrimitive(new Integer(1),new Integer(1))) t.testFail();
 else if (!transmitPrimitive(new Integer(-1),new Integer(-1))) t.testFail();
 else if (!transmitPrimitive(new Integer(2),new Integer(2))) t.testFail();
 else if (!transmitPrimitive(new Integer(3),new Integer(3))) t.testFail();
 else if (!transmitPrimitive(new Integer(4),new Integer(4))) t.testFail();
 else if (!transmitPrimitive(new Integer(5),new Integer(5))) t.testFail();
 else if (!transmitPrimitive(new Integer(6),new Integer(6))) t.testFail();
 else if (!transmitPrimitive(new Byte((byte)1),new Byte((byte)1))) 
   t.testFail();
 else if (!transmitPrimitive(new Character(' '),new Character(' '))) 
   t.testFail();
 else if (!transmitPrimitive(new Short((short)-3),new Short((short)-3)))
   t.testFail();
 else if (!transmitPrimitive(Boolean.TRUE,Boolean.TRUE)) t.testFail();
 else if (!transmitPrimitive(Boolean.FALSE,Boolean.FALSE)) t.testFail();
 else if (!transmitPrimitive(new Float(1.0f),new Float(1.0f))) t.testFail();
 else if (!transmitPrimitive(new Float(0.0f),new Float(0.0f))) t.testFail();
 else if (!transmitPrimitive(new Float(2.0f),new Float(2.0f))) t.testFail();
 else if (!transmitPrimitive(new Float(3.0f),new Float(3.0f))) t.testFail();
 else t.testOK();
	} catch (java.lang.Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};
	
	
	t.startTest("Evaluating 2L*2L=4L");
	try {
	  ExpressionImage e=new ExpressionImage();
	  e.asm_load_primitive(new Long(2L));
	  e.asm_binary_param(BI_MU);
	  e.asm_load_primitive(new Long(2L));
	  e.asm_binary(BI_MU);
	  e.asm_return();
	  CompiledExpression cell=e.getExpression();
	  if (((Long)cell.evaluate(null)).longValue()==4) 
	    t.testOK();
	  else
	    t.testFail();
	} catch (java.lang.Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};

	t.startTest("Evaluating 2D*2D=4D");
	try {
	  ExpressionImage e=new ExpressionImage();
	  e.asm_load_primitive(new Double(2D));
	  e.asm_binary_param(BI_MU);
	  e.asm_load_primitive(new Double(2D));
	  e.asm_binary(BI_MU);
	  e.asm_return();
	  CompiledExpression cell=e.getExpression();
	  if (((Double)cell.evaluate(null)).doubleValue()==4.0) 
	    t.testOK();
	  else
	    t.testFail();
	} catch (java.lang.Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};

	CompiledExpression cell_store=null;
	
	t.startTest("Evaluating Math.sin(2L+2L-4L+Math.round(1.2F))="+
		    "Math.sin(1.0)");
	try {
	  ExpressionImage e=new ExpressionImage();
	  Class math=Class.forName("java.lang.Math");
	  Class[] prs=new Class[1];
	  prs[0]=Double.TYPE;
	  java.lang.reflect.Method sinus=math.getDeclaredMethod("sin",prs);
	  prs[0]=Float.TYPE;
	  java.lang.reflect.Method round=math.getDeclaredMethod("round",prs);

	  e.asm_func_start(sinus,0);
	  e.asm_load_primitive(new Long(2L));
	  e.asm_binary_param(BI_PL);
	  e.asm_load_primitive(new Long(2L));
	  e.asm_binary(BI_PL);
	  e.asm_binary_param(BI_MI);
	  e.asm_load_primitive(new Long(4L));
	  e.asm_binary(BI_MI);
	  e.asm_binary_param(BI_PL);
	  e.asm_func_start(round,0);
	  e.asm_load_primitive(new Double(1.2D));
	  e.asm_func_param();
	  e.asm_func_call();
	  e.asm_convert(Long.TYPE);
	  e.asm_binary(BI_PL);
	  e.asm_func_param();
	  e.asm_func_call();
	  e.asm_return();
	  CompiledExpression cell=e.getExpression();
	  cell_store=cell;  // save for next test
	  double result=((Double)cell.evaluate(null)).doubleValue();
	  //	  System.out.println(result);
	  if (Math.abs(result-Math.sin(1.0))<0.001) 
	    t.testOK();
	  else
	    t.testFail();
	 
	} catch (java.lang.Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};
	
	t.startTest("Evaluating last expression 100000 times");
	try {
	  for(int i=0;i<100000;i++) cell_store.evaluate(null);
	  
	  cell_store=null; // free memory
	  
	  t.testOK();
	} catch (Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};
	
	t.startTest("Evaluating (true && false)|| "+
		    "!(false && true)||false=true");
	try {
	  ExpressionImage e=new ExpressionImage();
	  e.asm_load_primitive(Boolean.TRUE);
	  e.asm_logical_binary_param_and();
	  e.asm_load_primitive(Boolean.FALSE);
	  e.asm_logical_binary_and();
	  e.asm_logical_binary_param_or();
	  e.asm_logical_block();
	  e.asm_load_primitive(Boolean.FALSE);
	  e.asm_logical_binary_param_and();
	  e.asm_load_primitive(Boolean.TRUE);
	  e.asm_logical_binary_and();
	  e.asm_logical_unblock_not();
	  e.asm_logical_binary_or();
	  e.asm_logical_binary_param_or();
	  e.asm_load_primitive(Boolean.FALSE);
	  e.asm_logical_binary_or();
	  e.asm_return();
	  
	  
	  CompiledExpression cell=e.getExpression();
	  
	  boolean result=((Boolean)cell.evaluate(null)).booleanValue();

	  if (result) t.testOK(); else t.testFail();

	} catch (Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};

	// Various typed comparizons
	testComparizons(new Byte((byte)1),new Byte((byte)2),"bytes",t);
	testComparizons(new Byte((byte)2),new Byte((byte)1),"bytes",t);
	testComparizons(new Byte((byte)1),new Byte((byte)1),"bytes",t);
	testComparizons(new Short((short)1),new Short((short)2),"shorts",t);
	testComparizons(new Short((short)2),new Short((short)1),"shorts",t);
	testComparizons(new Short((short)1),new Short((short)1),"shorts",t);
	testComparizons(new Character('a'),new Character('b'),"characters",t);
	testComparizons(new Character('b'),new Character('a'),"characters",t);
	testComparizons(new Character('a'),new Character('a'),"characters",t);
	testComparizons(new Integer(1),new Integer(2),"integers",t);
	testComparizons(new Integer(2),new Integer(1),"integers",t);
	testComparizons(new Integer(1),new Integer(1),"integers",t);
	testComparizons(new Long(1),new Long(2),"long integers",t);
	testComparizons(new Long(2),new Long(1),"long integers",t);
	testComparizons(new Long(1),new Long(1),"long integers",t);
	testComparizons(new Float(1),new Float(2),"floats",t);
	testComparizons(new Float(2),new Float(1),"floats",t);
	testComparizons(new Float(1),new Float(1),"floats",t);
	testComparizons(new Double(1),new Double(2),"doubles",t);
	testComparizons(new Double(2),new Double(1),"doubles",t);
	testComparizons(new Double(1),new Double(1),"doubles",t);
	
	t.startTest("Evaluating ((1+2>2)==true)=true ");
	try {
	  ExpressionImage e=new ExpressionImage();
	  e.asm_load_primitive(new Integer(1));
	  e.asm_binary_param(BI_PL);
	  e.asm_load_primitive(new Integer(2));
	  e.asm_binary(BI_PL);
	  e.asm_binary_param(BI_GT);
	  e.asm_load_primitive(new Integer(2));
	  e.asm_binary(BI_GT);
	  e.asm_binary_param(BI_EQ);
	  e.asm_load_primitive(Boolean.TRUE);
	  e.asm_binary(BI_EQ);
	  e.asm_return();

	  CompiledExpression cell=e.getExpression();
	  
	  boolean result=((Boolean)cell.evaluate(null)).booleanValue();

	  if (result) t.testOK(); else t.testFail();

	} catch (Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};

	t.startTest("Evaluating (true?(true?true:false):false)=true ");
	try {
	  ExpressionImage e=new ExpressionImage();
	  
	  e.asm_load_primitive(Boolean.TRUE);
	  e.asm_branch_start_true();
	  e.asm_load_primitive(Boolean.TRUE);
	  e.asm_branch_start_true();
	  e.asm_load_primitive(Boolean.TRUE);
	  e.asm_branch_start_false();
	  e.asm_load_primitive(Boolean.FALSE);
	  e.asm_branch_end();
	  e.asm_branch_start_false();
	  e.asm_load_primitive(Boolean.FALSE);
	  e.asm_branch_end();
	  e.asm_return();
	  
	  CompiledExpression cell=e.getExpression();
	  
	  boolean result=((Boolean)cell.evaluate(null)).booleanValue();

	  if (result) t.testOK(); else t.testFail();

	} catch (Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};

	t.startTest("Evaluating (1==1)&&(max(~(1<=2?1:0),~(2>=3:1:0))==-1) =true");
	try {
	  ExpressionImage e=new ExpressionImage();

	  Class math=Class.forName("java.lang.Math");
	  Class[] prs=new Class[2];
	  prs[0]=Integer.TYPE;
	  prs[1]=Integer.TYPE;
	  java.lang.reflect.Method maximum=math.getDeclaredMethod("max",prs);
	  
	  e.asm_load_primitive(new Integer(1));
	  e.asm_binary_param(BI_EQ);
          e.asm_load_primitive(new Integer(1));
	  e.asm_binary(BI_EQ);

	  e.asm_logical_binary_param_and();

	  e.asm_func_start(maximum,0);
	  e.asm_load_primitive(new Integer(1));
	  e.asm_binary_param(BI_LE);
	  e.asm_load_primitive(new Integer(2));
	  e.asm_binary(BI_LE);
	  e.asm_branch_start_true();
	  e.asm_load_primitive(new Integer(1));
	  e.asm_branch_start_false();
	  e.asm_load_primitive(new Integer(0));
	  e.asm_branch_end();

	  e.asm_unary(UN_NO);
	  e.asm_func_param();

	  e.asm_load_primitive(new Integer(2));
	  e.asm_binary_param(BI_GE);
	  e.asm_load_primitive(new Integer(3));
	  e.asm_binary(BI_GE);
	  e.asm_branch_start_true();
	  e.asm_load_primitive(new Integer(1));
	  e.asm_branch_start_false();
	  e.asm_load_primitive(new Integer(0));
	  e.asm_branch_end();

	  e.asm_unary(UN_NO);
	  e.asm_func_param();
	  e.asm_func_call();

	  e.asm_binary_param(BI_EQ);

	  e.asm_load_primitive(new Integer(-1));
	  e.asm_binary(BI_EQ);
	  e.asm_logical_binary_and();
	  e.asm_return();

	  CompiledExpression cell=e.getExpression();

	  boolean result=((Boolean)cell.evaluate(null)).booleanValue();	  
	  
	  if (result) t.testOK(); else t.testFail();

	} catch (Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};

	t.startTest("Evaluating \"aa\"+\"bb\"+\"cc\" = \"aabbcc\"");
	try {
	  ExpressionImage e=new ExpressionImage();
	  
	  e.asm_load_object("aa");
	  e.asm_binary_param(BI_PL);
	  e.asm_load_object("bb");
	  e.asm_binary(BI_PL);
	  e.asm_binary_param(BI_PL);
	  e.asm_load_object("cc");
	  e.asm_binary(BI_PL);
	  e.asm_return();

	  CompiledExpression cell=e.getExpression();

	  boolean result=cell.evaluate(null).equals("aabbcc");	  
	  
	  if (result) t.testOK(); else t.testFail();

	} catch (Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};

	t.startTest("Evaluating \"str\"+true+2B+'!'+3I+4L+5.0F = \"strtrue2!345.0\"");
	try {
	  ExpressionImage e=new ExpressionImage();

	  e.asm_load_object("str");
	  e.asm_binary_param(BI_PL);
	  e.asm_load_primitive(Boolean.TRUE);
	  e.asm_binary(BI_PL);
	  e.asm_binary_param(BI_PL);
	  e.asm_load_primitive(new Byte((byte)2));
	  e.asm_binary(BI_PL);
	  e.asm_binary_param(BI_PL);
	  e.asm_load_primitive(new Character('!'));
	  e.asm_binary(BI_PL);
	  e.asm_binary_param(BI_PL);
	  e.asm_load_primitive(new Integer(3));
	  e.asm_binary(BI_PL);
	  e.asm_binary_param(BI_PL);
	  e.asm_load_primitive(new Long(4L));
	  e.asm_binary(BI_PL);
	  e.asm_binary_param(BI_PL);
	  e.asm_load_primitive(new Float(5.0F));
	  e.asm_binary(BI_PL);
	  e.asm_return();	  

	  CompiledExpression cell=e.getExpression();
	  
	  boolean result=cell.evaluate(null).equals("strtrue2!345.0");	  
	  
	  if (result) t.testOK(); else t.testFail();

	} catch (Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};

	t.startTest("Evaluating \"str\"+6.0D+new Double(7.0D) = \"str6.07.0\"");
	try {
	  ExpressionImage e=new ExpressionImage();

	  e.asm_load_object("str");
	  e.asm_binary_param(BI_PL);
	  e.asm_load_primitive(new Double(6.0D));
	  e.asm_binary(BI_PL);
	  e.asm_binary_param(BI_PL);
	  e.asm_load_object(new Double(7.0D));
	  e.asm_binary(BI_PL);
	  e.asm_return();	  

	  CompiledExpression cell=e.getExpression();
	  
	  boolean result=cell.evaluate(null).equals("str6.07.0");
	  
	  if (result) t.testOK(); else t.testFail();

	} catch (Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};

      };
    };
  };
  
  private static void testComparizons(Object v1,Object v2,String type,
				     Tester t) {
    if (Debug.enabled) {
      int op=BI_EQ;
      String sgn[]={"==","!=","<",">=",">","<="};
      while (op<=BI_GT) {
	t.startTest("Evaluating "+type+" "+v1+sgn[op-BI_EQ]+v2+" || "+
		    v1+sgn[op-BI_EQ+1]+v2+" = true");
	try {
	  ExpressionImage e=new ExpressionImage();
	  e.asm_load_primitive(v1);
	  e.asm_binary_param(op);
	  e.asm_load_primitive(v2);
	  e.asm_binary(op);
	  e.asm_logical_binary_param_or();
	  e.asm_load_primitive(v1);
	  e.asm_binary_param(op+1);
	  e.asm_load_primitive(v2);
	  e.asm_binary(op+1);
	  e.asm_logical_binary_or();
	  e.asm_return();
	  
	  CompiledExpression cell=e.getExpression();
	  
	  boolean result=((Boolean)cell.evaluate(null)).booleanValue();
	  
	  if (result) t.testOK(); else t.testFail();
	  
	} catch (Throwable e) {
	  Debug.reportThrowable(e);
	  t.testFail();
	};
	op+=2;
      };
    };
  };
  
};

//  -------------- PatchableByteArrayOutputStream -------------------------- 
class PatchableByteArrayOutputStream extends java.io.ByteArrayOutputStream {
  public void patchAddress(int pos,int address) {
    if (Debug.enabled)
      Debug.assert(address<=65535,"Jump destination is too far.");
    buf[pos  ]=(byte)((address >>> 8) & 0xFF);
    buf[pos+1]=(byte)((address >>> 0) & 0xFF);
  };
};


// This is to denote StringBuffer used by string concatenation operation
class TempStringBuffer {
};

// This is to avoid new objects allocation when dealing with jumps
class IntegerStack {
  private int[] data;
  private int count=0;
  
  public IntegerStack(int initCapacity) {
    data=new int[initCapacity];
  };

  public IntegerStack() {
    this(30);
  };
  
  public final void push(int what) {
    if (count>=data.length) increaseCapacity(count+1);
    data[count++]=what;
  };
  
  public final int peek() {
    return data[count-1];
  };
  
  public final int pop() {
    return data[--count];
  };

  public final int size() {
    return count;
  };

  public final void pop_throw() {
    count--;
    if (Debug.enabled)
      Debug.assert(count>=0,"Stack is empty when removing top.");
  };

  // Swaps values above given limits in two stacks
  public static void swap(IntegerStack one,int oneLim,
		   IntegerStack other,int otherLim) {
    int copyFromOne=one.count-oneLim;
    int copyFromOther=other.count-otherLim;
    boolean cf_one=copyFromOne>0;
    boolean cf_other=copyFromOther>0;
    if ((cf_one) || (cf_other)) {
      int nSizeOne=oneLim+copyFromOther;
      int nSizeOther=otherLim+copyFromOne;
      
      // ensure capacities
      if (nSizeOne>one.data.length) one.increaseCapacity(nSizeOne);
      if (nSizeOther>other.data.length) other.increaseCapacity(nSizeOther);
      
      int[] temp=null;
      if (cf_one) {
	temp=new int[copyFromOne];
	System.arraycopy(one.data,oneLim,temp,0,copyFromOne);
      };
      if (cf_other)
	System.arraycopy(other.data,otherLim,one.data,oneLim,copyFromOther);

      if (cf_one)
	System.arraycopy(temp,0,other.data,otherLim,copyFromOne);
      
      one.count=nSizeOne;
      other.count=nSizeOther;
    };
  };

  private synchronized void increaseCapacity(int minCapacity) {
    int[] old_data=data;
    int oldSize=data.length;
    int newSize=oldSize*2;
    if (newSize<minCapacity) newSize=minCapacity;
    data=new int[newSize];
    System.arraycopy(old_data,0,data,0,count);
  };
};
