view src/main/gov/nasa/jpf/jvm/ClassFile.java @ 28:7be90179bb3b

Provided support for double colon operator used for lamabda expressions. Fixed a bug related to generating names for funcation object classes (by supporting double colon operator, a new stragety needed to generate unique names for function objects. To achive that the bootstrap ids are incorporated into names). Finally modified the method that retrieves the SAM from functional interfaces.
author nastaran <nastaran.shafiei@gmail.com>
date Thu, 25 Jun 2015 13:20:50 -0700
parents 61d41facf527
children
line wrap: on
line source

/*
 * Copyright (C) 2014, United States Government, as represented by the
 * Administrator of the National Aeronautics and Space Administration.
 * All rights reserved.
 *
 * The Java Pathfinder core (jpf-core) platform is licensed under the
 * Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 *        http://www.apache.org/licenses/LICENSE-2.0. 
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */

package gov.nasa.jpf.jvm;

import gov.nasa.jpf.jvm.JVMByteCodeReader;
import gov.nasa.jpf.vm.ClassParseException;
import gov.nasa.jpf.JPFException;
import gov.nasa.jpf.util.BailOut;
import gov.nasa.jpf.util.BinaryClassSource;

import java.io.File;

/**
 * class to read and dissect Java classfile contents (as specified by the Java VM
 * spec  http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#16628
 */
public class ClassFile extends BinaryClassSource {

  public static final int CONSTANT_UTF8 = 1;
  public static final int CONSTANT_INTEGER = 3;
  public static final int CONSTANT_FLOAT = 4;
  public static final int CONSTANT_LONG = 5;
  public static final int CONSTANT_DOUBLE = 6;
  public static final int CONSTANT_CLASS = 7;
  public static final int CONSTANT_STRING = 8;
  public static final int FIELD_REF = 9;
  public static final int METHOD_REF = 10;
  public static final int INTERFACE_METHOD_REF = 11;
  public static final int NAME_AND_TYPE = 12;
  public static final int METHOD_HANDLE = 15;
  public static final int METHOD_TYPE = 16;
  public static final int INVOKE_DYNAMIC = 18;

  public static final int REF_GETFIELD = 1;
  public static final int REF_GETSTATIC = 2;
  public static final int REF_PUTFIELD = 3;
  public static final int REF_PUTSTATIC = 4;
  public static final int REF_INVOKEVIRTUAL = 5;
  public static final int REF_INVOKESTATIC = 6;
  public static final int REF_INVOKESPECIAL = 7;
  public static final int REF_NEW_INVOKESPECIAL = 8;
  public static final int REF_INVOKEINTERFACE = 9;

  // used to store types in cpValue[]
  public static enum CpInfo {
    Unused_0,                 // 0
    ConstantUtf8,             // 1
    Unused_2,                 // 2
    ConstantInteger,          // 3
    ConstantFloat,            // 4
    ConstantLong,             // 5
    ConstantDouble,           // 6
    ConstantClass,            // 7
    ConstantString,           // 8
    FieldRef,                 // 9
    MethodRef,                // 10
    InterfaceMethodRef,       // 11
    NameAndType,              // 12
    Unused_13,
    Unused_14,
    MethodHandle,             // 15
    MethodType,               // 16
    Unused_17,
    InvokeDynamic             // 18
  }

  // <2do> this is going away
  String requestedTypeName; // the type name that caused this classfile to be loaded

  // the const pool
  int[] cpPos;     // cpPos[i] holds data start index for cp_entry i (0 is unused)
  Object[] cpValue; // cpValue[i] hold the String/Integer/Float/Double associated with corresponding cp_entries
  
  //--- ctors
  public ClassFile (byte[] data, int offset){
    super(data,offset);
  }

  public ClassFile (byte[] data){
    super(data,0);
  }

  public ClassFile (String typeName, byte[] data){
    super(data,0);
    
    this.requestedTypeName = typeName;
  }
  
  public ClassFile (String typeName, byte[] data, int offset){
    super(data, offset);
    
    this.requestedTypeName = typeName;
  }

  public ClassFile (File file) throws ClassParseException {
    super(file);
  }

  public ClassFile (String pathName)  throws ClassParseException {
    super( new File(pathName));
  }



  
  /**
   * set classfile data.  This is mainly provided to allow
   * on-the-fly classfile instrumentation with 3rd party libraries
   * 
   * BEWARE - like getData(), this method can cause parsing to fail if the
   * provided data does not conform to the VM specs. In particular, this
   * method should ONLY be called before executing parse(ClassFileReader) and
   * will otherwise throw a JPFException
   */
  public void setData(byte[] newData){
    if (cpPos != null){
      throw new JPFException("concurrent modification of ClassFile data");
    }
    
    data = newData;
  }
  
  /**
   * return the typename this classfile gets loaded for
   * <2do> this is going away
   */
  public String getRequestedTypeName(){
    return requestedTypeName;
  }


  //--- general attributes
  public static final String SYNTHETIC_ATTR = "Synthetic";
  public static final String DEPRECATED_ATTR = "Deprecated";
  public static final String SIGNATURE_ATTR = "Signature";
  public static final String RUNTIME_INVISIBLE_ANNOTATIONS_ATTR = "RuntimeInvisibleAnnotations";
  public static final String RUNTIME_VISIBLE_ANNOTATIONS_ATTR = "RuntimeVisibleAnnotations";
  public static final String RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR = "RuntimeVisibleTypeAnnotations";

  //--- standard field attributes
  public static final String CONST_VALUE_ATTR = "ConstantValue";

  protected final static String[] stdFieldAttrs = {
    CONST_VALUE_ATTR, SYNTHETIC_ATTR, DEPRECATED_ATTR, SIGNATURE_ATTR,
    RUNTIME_INVISIBLE_ANNOTATIONS_ATTR, RUNTIME_VISIBLE_ANNOTATIONS_ATTR, RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR };


  //--- standard method attributes
  public static final String CODE_ATTR = "Code";
  public static final String EXCEPTIONS_ATTR = "Exceptions";
  public static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS_ATTR = "RuntimeInvisibleParameterAnnotations";
  public static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS_ATTR = "RuntimeVisibleParameterAnnotations";
  public static final String ANNOTATIONDEFAULT_ATTR = "AnnotationDefault";

  protected final static String[] stdMethodAttrs = { 
    CODE_ATTR, EXCEPTIONS_ATTR, SYNTHETIC_ATTR, DEPRECATED_ATTR, SIGNATURE_ATTR,
    RUNTIME_INVISIBLE_ANNOTATIONS_ATTR, RUNTIME_VISIBLE_ANNOTATIONS_ATTR,
    RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS_ATTR,
    RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS_ATTR,
    RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR,
    ANNOTATIONDEFAULT_ATTR
  };


  //--- standard code attributes
  public static final String LINE_NUMBER_TABLE_ATTR = "LineNumberTable";
  public static final String LOCAL_VAR_TABLE_ATTR = "LocalVariableTable";

  protected final static String[] stdCodeAttrs = { LINE_NUMBER_TABLE_ATTR, LOCAL_VAR_TABLE_ATTR, RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR };


  //--- standard class attributes
  public static final String  SOURCE_FILE_ATTR = "SourceFile";
  public static final String  INNER_CLASSES_ATTR = "InnerClasses";
  public static final String  ENCLOSING_METHOD_ATTR = "EnclosingMethod";
  public static final String  BOOTSTRAP_METHOD_ATTR = "BootstrapMethods";
  
  protected final static String[] stdClassAttrs = {
    SOURCE_FILE_ATTR, DEPRECATED_ATTR, INNER_CLASSES_ATTR, DEPRECATED_ATTR, SIGNATURE_ATTR,
    RUNTIME_INVISIBLE_ANNOTATIONS_ATTR, RUNTIME_VISIBLE_ANNOTATIONS_ATTR, RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR,
    ENCLOSING_METHOD_ATTR, BOOTSTRAP_METHOD_ATTR };


  protected String internStdAttrName(int cpIdx, String name, String[] stdNames){
    for (int i=0; i<stdNames.length; i++){
      if (stdNames[i] == name) return name;
    }
    for (int i=0; i<stdNames.length; i++){
      String stdName = stdNames[i];
      if (stdName.equals(name)){
        cpValue[cpIdx] = stdName;
        return stdName;
      }
    }
    return name;
  }


  //--- constpool access

  //--- the primitive info cpValue
  public String utf8At(int utf8InfoIdx){
    //assert data[cpPos[utf8InfoIdx]] == 1 : "not a utf8_info tag";
    return (String) cpValue[utf8InfoIdx];
  }

  public int intAt(int intInfoIdx){
    //assert data[cpPos[intInfoIdx]] == 3 : "not a int_info tag";
    return (Integer) cpValue[intInfoIdx];
  }

  public float floatAt(int floatInfoIdx){
    //assert data[cpPos[floatInfoIdx]] == 4 : "not a float_info tag";
    return (Float) cpValue[floatInfoIdx];
  }

  public long longAt(int longInfoIdx){
    //assert data[cpPos[longInfoIdx]] == 5 : "not a long_info tag";
    return (Long) cpValue[longInfoIdx];
  }

  public double doubleAt(int doubleInfoIdx){
    //assert data[cpPos[doubleInfoIdx]] == 6 : "not a double_info tag";
    return (Double) cpValue[doubleInfoIdx];
  }

  //--- those two are delegated but resolved
  public String classNameAt(int classInfoIdx){
    //assert data[cpPos[classInfoIdx]] == 7 : "not a Class_info tag";
    return (String) cpValue[classInfoIdx];
  }

  public String stringAt(int stringInfoIdx){
    //assert data[cpPos[stringInfoIdx]] == 8 : "not a String_info tag";
    return (String) cpValue[stringInfoIdx];
  }

  //--- composite infos

  // the generic ones (if we don't care what kind of reference type this is)
  public String refClassNameAt(int cpIdx){
    return (String) cpValue[ u2(cpPos[cpIdx]+1)];
  }
  public String refNameAt(int cpIdx){
    return utf8At( u2( cpPos[ u2(cpPos[cpIdx]+3)]+1));
  }
  public String refDescriptorAt(int cpIdx){
    return utf8At( u2( cpPos[ u2(cpPos[cpIdx]+3)]+3));
  }

  public int mhRefTypeAt (int methodHandleInfoIdx){
    return u1(cpPos[methodHandleInfoIdx]+1);
  }
  public int mhMethodRefIndexAt  (int methodHandleInfoIdx){
    return u2(cpPos[methodHandleInfoIdx]+2);
  }
  
  // those could check ref types
  public String fieldClassNameAt(int fieldRefInfoIdx){
    //assert data[cpPos[fieldRefInfoIdx]] == 9 : "not a Fieldref_info tag";
    return (String) cpValue[ u2(cpPos[fieldRefInfoIdx]+1)];
  }
  public String fieldNameAt(int fieldRefInfoIdx){
    return utf8At( u2( cpPos[ u2(cpPos[fieldRefInfoIdx]+3)]+1));
  }
  public String fieldDescriptorAt(int fieldRefInfoIdx){
    return utf8At( u2( cpPos[ u2(cpPos[fieldRefInfoIdx]+3)]+3));
  }

  public String methodClassNameAt(int methodRefInfoIdx){
    return (String) cpValue[ u2(cpPos[methodRefInfoIdx]+1)];
  }
  public String methodNameAt(int methodRefInfoIdx){
    return utf8At( u2( cpPos[ u2(cpPos[methodRefInfoIdx]+3)]+1));
  }
  public String methodDescriptorAt(int methodRefInfoIdx){
    return utf8At( u2( cpPos[ u2(cpPos[methodRefInfoIdx]+3)]+3));
  }

  public String methodTypeDescriptorAt (int methodTypeInfoIdx){
    return utf8At( u2(cpPos[methodTypeInfoIdx]+1));
  }
  
  public String interfaceMethodClassNameAt(int ifcMethodRefInfoIdx){
    return (String) cpValue[ u2(cpPos[ifcMethodRefInfoIdx]+1)];
  }
  public String interfaceMethodNameAt(int ifcMethodRefInfoIdx){
    return utf8At( u2( cpPos[ u2(cpPos[ifcMethodRefInfoIdx]+3)]+1));
  }
  public String interfaceMethodDescriptorAt(int ifcMethodRefInfoIdx){
    return utf8At( u2( cpPos[ u2(cpPos[ifcMethodRefInfoIdx]+3)]+3));
  }
  
  public int bootstrapMethodIndex (int cpInvokeDynamicIndex){
    return u2(cpPos[cpInvokeDynamicIndex]+1);
  }
  public String samMethodNameAt(int cpInvokeDynamicIndex) {
    return utf8At( u2( cpPos[ u2(cpPos[cpInvokeDynamicIndex]+3)]+1)); 
  }
  public String callSiteDescriptor(int cpInvokeDynamicIndex) {
    return utf8At( u2( cpPos[ u2(cpPos[cpInvokeDynamicIndex]+3)]+3)); 
  }
  
  public String getRefTypeName (int refCode){
    switch (refCode){
      case REF_GETFIELD:      return "getfield";
      case REF_GETSTATIC:     return "getstatic";
      case REF_PUTFIELD:      return "putfield";
      case REF_PUTSTATIC:     return "putstatic";
      case REF_INVOKEVIRTUAL: return "invokevirtual";
      case REF_INVOKESTATIC:  return "invokestatic";
      case REF_INVOKESPECIAL: return "invokespecial";
      case REF_NEW_INVOKESPECIAL: return "new-invokespecial";
      case REF_INVOKEINTERFACE: return "invokeinterface";
      default:
        return "<unknown>";
    }
  }
  
  public String getTypeName (int typeCode){
    switch(typeCode){
      case 4: return "boolean";
      case 5: return "char";
      case 6: return "float";
      case 7: return "double";
      case 8: return "byte";
      case 9: return "short";
      case 10: return "int";
      case 11: return "long";
      default:
        return "<unknown>";
    }
  }

  @Override
  public int getPos(){
    return pos;
  }

  public int getPc(){
    return pc;
  }

  //--- traverse/analyze the const pool (this is rather exotic)

  public int getNumberOfCpEntries(){
    return cpValue.length;
  }

  public Object getCpValue (int i){
    return cpValue[i];
  }

  public int getCpTag (int i){
    return data[cpPos[i]];
  }

  /**
   * the result can be used as input for u2(dataIndex)
   *
   * NOTE - this returns -1 for the dreaded unused extra entries associated
   * with ConstantDouble and ConstantLong
   */
  public int getDataPosOfCpEntry (int i){
    return cpPos[i];
  }

  //--- standard attributes

  public Object getConstValueAttribute(int dataPos){
    int cpIdx = u2(dataPos);
    Object v = cpValue[cpIdx];
    return v;
  }

  public String getSourceFileAttribute(int dataPos){
    // SourceFile_attribute { u2 attr_name_idx; u4 attr_length; u2 sourcefile_idx<utf8>; }

    int cpIdx = u2(dataPos + 6);
    Object v = cpValue[cpIdx];
    return (String)v;
  }

  
  //--- low level readers

  public final int u1(int dataIdx){
    return data[dataIdx] & 0xff;
  }

  public final int u2(int dataIdx){
    return ((data[dataIdx]&0xff) << 8) | (data[dataIdx+1]&0xff);
  }

  public final int i1(int dataIdx) {
    return data[dataIdx++];
  }

  public final int i2(int dataIdx) {
    int idx = dataIdx;
    return (data[idx++] << 8) | (data[idx]&0xff);
  }

  public final int readU2(){
    int idx = pos;
    pos += 2;
    return ((data[idx++]&0xff) << 8) | (data[idx]&0xff);
  }

  public final int readI2() {
    int idx = pos;
    pos += 2;
    return (data[idx++] << 8) | (data[idx]&0xff);
  }

  public final int readI4(){
    int idx = pos;
    pos += 4;
    byte[] data = this.data;

    return (data[idx++] <<24) | ((data[idx++]&0xff) << 16) | ((data[idx++]&0xff) << 8) | (data[idx]&0xff);
  }

  
  //--- reader notifications
  private void setClass(ClassFileReader reader, String clsName, String superClsName, int flags, int cpCount) throws ClassParseException {
    int p = pos;
    reader.setClass( this, clsName, superClsName, flags, cpCount);
    pos = p;
  }

  private void setInterfaceCount(ClassFileReader reader, int ifcCount){
    int p = pos;
    reader.setInterfaceCount( this, ifcCount);
    pos = p;
  }
  private void setInterface(ClassFileReader reader, int ifcIndex, String ifcName){
    int p = pos;
    reader.setInterface( this, ifcIndex, ifcName);
    pos = p;
  }
  private void setInterfacesDone(ClassFileReader reader){
    int p = pos;
    reader.setInterfacesDone( this);
    pos = p;
  }


  private void setFieldCount(ClassFileReader reader, int fieldCount){
    int p = pos;
    reader.setFieldCount( this, fieldCount);
    pos = p;

  }
  private void setField(ClassFileReader reader, int fieldIndex, int accessFlags, String name, String descriptor){
    int p = pos;
    reader.setField( this, fieldIndex, accessFlags, name, descriptor);
    pos = p;
  }
  private void setFieldAttributeCount(ClassFileReader reader, int fieldIndex, int attrCount){
    int p = pos;
    reader.setFieldAttributeCount( this, fieldIndex, attrCount);
    pos = p;
  }
  private void setFieldAttribute(ClassFileReader reader, int fieldIndex, int attrIndex, String name, int attrLength){
    int p = pos + attrLength;
    reader.setFieldAttribute( this, fieldIndex, attrIndex, name, attrLength);
    pos = p;
  }
  private void setFieldAttributesDone(ClassFileReader reader, int fieldIndex){
    int p = pos;
    reader.setFieldAttributesDone( this, fieldIndex);
    pos = p;
  }
  private void setFieldDone(ClassFileReader reader, int fieldIndex){
    int p = pos;
    reader.setFieldDone( this, fieldIndex);
    pos = p;
  }
  private void setFieldsDone(ClassFileReader reader){
    int p = pos;
    reader.setFieldsDone( this);
    pos = p;
  }
  private void setConstantValue(ClassFileReader reader, Object tag, Object value){
    int p = pos;
    reader.setConstantValue( this, tag, value);
    pos = p;
  }

  private void setMethodCount(ClassFileReader reader, int methodCount){
    int p = pos;
    reader.setMethodCount( this, methodCount);
    pos = p;
  }
  private void setMethod(ClassFileReader reader, int methodIndex, int accessFlags, String name, String descriptor){
    int p = pos;
    reader.setMethod( this, methodIndex, accessFlags, name, descriptor);
    pos = p;
  }
  private void setMethodAttributeCount(ClassFileReader reader, int methodIndex, int attrCount){
    int p = pos;
    reader.setMethodAttributeCount( this, methodIndex, attrCount);
    pos = p;
  }
  private void setMethodAttribute(ClassFileReader reader, int methodIndex, int attrIndex, String name, int attrLength){
    int p = pos + attrLength;
    reader.setMethodAttribute( this, methodIndex, attrIndex, name, attrLength);
    pos = p;
  }
  private void setMethodAttributesDone(ClassFileReader reader, int methodIndex){
    int p = pos;
    reader.setMethodAttributesDone( this, methodIndex);
    pos = p;
  }
  private void setMethodDone(ClassFileReader reader, int methodIndex){
    int p = pos;
    reader.setMethodDone( this, methodIndex);
    pos = p;
  }
  private void setMethodsDone(ClassFileReader reader){
    int p = pos;
    reader.setMethodsDone( this);
    pos = p;
  }
  private void setExceptionCount(ClassFileReader reader, Object tag, int exceptionCount){
    int p = pos;
    reader.setExceptionCount( this, tag, exceptionCount);
    pos = p;
  }
  private void setExceptionsDone(ClassFileReader reader, Object tag){
    int p = pos;
    reader.setExceptionsDone( this, tag);
    pos = p;
  }
  private void setException(ClassFileReader reader, Object tag, int exceptionIndex, String exceptionType){
    int p = pos;
    reader.setException( this, tag, exceptionIndex, exceptionType);
    pos = p;
  }
  private void setCode(ClassFileReader reader, Object tag, int maxStack, int maxLocals, int codeLength){
    int p = pos + codeLength;
    reader.setCode( this, tag, maxStack, maxLocals, codeLength);
    pos = p;
  }
  private void setExceptionTableCount(ClassFileReader reader, Object tag, int exceptionTableCount){
    int p = pos;
    reader.setExceptionHandlerTableCount( this, tag, exceptionTableCount);
    pos = p;
  }
  private void setExceptionTableEntry(ClassFileReader reader, Object tag, int exceptionIndex,
          int startPc, int endPc, int handlerPc, String catchType){
    int p = pos;
    reader.setExceptionHandler( this, tag, exceptionIndex, startPc, endPc, handlerPc, catchType);
    pos = p;
  }
  private void setExceptionTableDone(ClassFileReader reader, Object tag){
    int p = pos;
    reader.setExceptionHandlerTableDone( this, tag);
    pos = p;
  }

  private void setCodeAttributeCount(ClassFileReader reader, Object tag, int attrCount){
    int p = pos;
    reader.setCodeAttributeCount( this, tag, attrCount);
    pos = p;
  }
  private void setCodeAttribute(ClassFileReader reader, Object tag, int attrIndex, String name, int attrLength){
    int p = pos + attrLength;
    reader.setCodeAttribute( this, tag, attrIndex, name, attrLength);
    pos = p;
  }
  private void setCodeAttributesDone(ClassFileReader reader, Object tag){
    int p = pos;
    reader.setCodeAttributesDone( this, tag);
    pos = p;
  }
          
  private void setLineNumberTableCount(ClassFileReader reader, Object tag, int lineNumberCount){
    int p = pos;
    reader.setLineNumberTableCount( this, tag, lineNumberCount);
    pos = p;
  }
  private void setLineNumber(ClassFileReader reader, Object tag, int lineIndex, int lineNumber, int startPc){
    int p = pos;
    reader.setLineNumber( this, tag, lineIndex, lineNumber, startPc);
    pos = p;
  }
  private void setLineNumberTableDone(ClassFileReader reader, Object tag){
    int p = pos;
    reader.setLineNumberTableDone( this, tag);
    pos = p;
  }

  private void setLocalVarTableCount(ClassFileReader reader, Object tag, int localVarCount){
    int p = pos;
    reader.setLocalVarTableCount( this, tag, localVarCount);
    pos = p;
  }
  private void setLocalVar(ClassFileReader reader, Object tag, int localVarIndex, String varName, String descriptor,
                      int scopeStartPc, int scopeEndPc, int slotIndex){
    int p = pos;
    reader.setLocalVar( this, tag, localVarIndex, varName, descriptor, scopeStartPc, scopeEndPc, slotIndex);
    pos = p;
  }
  private void setLocalVarTableDone(ClassFileReader reader, Object tag){
    int p = pos;
    reader.setLocalVarTableDone( this, tag);
    pos = p;
  }


  private void setClassAttributeCount(ClassFileReader reader, int attrCount){
    int p = pos;
    reader.setClassAttributeCount( this, attrCount);
    pos = p;
  }
  private void setClassAttribute(ClassFileReader reader, int attrIndex, String name, int attrLength){
    int p = pos + attrLength;
    reader.setClassAttribute( this, attrIndex, name, attrLength);
    pos = p;
  }
  private void setClassAttributesDone(ClassFileReader reader){
    int p = pos;
    reader.setClassAttributesDone(this);
    pos = p;
  }

  private void setSourceFile(ClassFileReader reader, Object tag, String pathName){
    int p = pos;
    reader.setSourceFile( this, tag, pathName);
    pos = p;
  }
  
  private void setBootstrapMethodCount (ClassFileReader reader, Object tag, int bootstrapMethodCount){
    int p = pos;
    reader.setBootstrapMethodCount( this, tag, bootstrapMethodCount);
    pos = p;    
  }
  private void setBootstrapMethod (ClassFileReader reader, Object tag, int idx, 
                                   int refKind, String cls, String mth, String descriptor, int[] cpArgs){
    int p = pos;
    reader.setBootstrapMethod( this, tag, idx, refKind, cls, mth, descriptor, cpArgs);
    pos = p;    
  }
  private void setBootstrapMethodsDone (ClassFileReader reader, Object tag){
    int p = pos;
    reader.setBootstrapMethodsDone( this, tag);
    pos = p;    
  }
  
  private void setInnerClassCount(ClassFileReader reader, Object tag, int innerClsCount){
    int p = pos;
    reader.setInnerClassCount( this, tag, innerClsCount);
    pos = p;
  }
  private void setInnerClass(ClassFileReader reader, Object tag, int innerClsIndex, String outerName, String innerName,
          String innerSimpleName, int accessFlags){
    int p = pos;
    reader.setInnerClass( this, tag, innerClsIndex, outerName, innerName, innerSimpleName, accessFlags);
    pos = p;
  }
  private void setEnclosingMethod(ClassFileReader reader, Object tag, String enclosingClass, String enclosedMethod, String descriptor){
    int p = pos;
	  reader.setEnclosingMethod( this, tag, enclosingClass, enclosedMethod, descriptor);
	  pos = p;
  }
  private void setInnerClassesDone(ClassFileReader reader, Object tag){
    int p = pos;
    reader.setInnerClassesDone(this, tag);
    pos = p;
  }

  private void setAnnotationCount(ClassFileReader reader, Object tag, int annotationCount){
    int p = pos;
    reader.setAnnotationCount( this, tag, annotationCount);
    pos = p;
  }
  private void setAnnotation(ClassFileReader reader, Object tag, int annotationIndex, String annotationType){
    int p = pos;
    reader.setAnnotation( this, tag, annotationIndex, annotationType);
    pos = p;
  }
  private void setAnnotationsDone(ClassFileReader reader, Object tag){
    int p = pos;
    reader.setAnnotationsDone(this, tag);
    pos = p;
  }

  private void setTypeAnnotationCount(ClassFileReader reader, Object tag, int annotationCount){
    int p = pos;
    reader.setTypeAnnotationCount( this, tag, annotationCount);
    pos = p;
  }
  private void setTypeAnnotationsDone(ClassFileReader reader, Object tag){
    int p = pos;
    reader.setTypeAnnotationsDone(this, tag);
    pos = p;
  }

  
  private void setAnnotationValueCount(ClassFileReader reader, Object tag, int annotationIndex, int nValuePairs){
    int p = pos;
    reader.setAnnotationValueCount( this, tag, annotationIndex, nValuePairs);
    pos = p;
  }
  private void setPrimitiveAnnotationValue(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex,
          String elementName, int arrayIndex, Object val){
    int p = pos;
    reader.setPrimitiveAnnotationValue( this, tag, annotationIndex, valueIndex, elementName, arrayIndex, val);
    pos = p;
  }
  private void setStringAnnotationValue(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex,
          String elementName, int arrayIndex, String s){
    int p = pos;
    reader.setStringAnnotationValue( this, tag, annotationIndex, valueIndex, elementName, arrayIndex, s);
    pos = p;
  }
  private void setClassAnnotationValue(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex,
          String elementName, int arrayIndex, String typeName){
    int p = pos;
    reader.setClassAnnotationValue( this, tag, annotationIndex, valueIndex, elementName, arrayIndex, typeName);
    pos = p;
  }
  private void setEnumAnnotationValue(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex,
          String elementName, int arrayIndex, String enumType, String enumValue){
    int p = pos;
    reader.setEnumAnnotationValue( this, tag, annotationIndex, valueIndex, elementName, arrayIndex, enumType, enumValue);
    pos = p;
  }

  private void setAnnotationValueElementCount(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex,
          String elementName, int elementCount){
    int p = pos;
    reader.setAnnotationValueElementCount(this, tag, annotationIndex, valueIndex, elementName, elementCount);
    pos = p;
  }
  private void setAnnotationValueElementsDone(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex,
          String elementName){
    int p = pos;
    reader.setAnnotationValueElementsDone(this, tag, annotationIndex, valueIndex, elementName);
    pos = p;
  }

  public void setAnnotationValuesDone(ClassFileReader reader, Object tag, int annotationIndex){
    int p = pos;
    reader.setAnnotationValuesDone(this, tag, annotationIndex);
    pos = p;
  }

  private void setParameterCount(ClassFileReader reader, Object tag, int parameterCount){
    int p = pos;
    reader.setParameterCount(this, tag, parameterCount);
    pos = p;
  }
  private void setParameterAnnotationCount(ClassFileReader reader, Object tag, int paramIndex, int annotationCount){
    int p = pos;
    reader.setParameterAnnotationCount(this, tag, paramIndex, annotationCount);
    pos = p;
  }
  private void setParameterAnnotation(ClassFileReader reader, Object tag, int annotationIndex, String annotationType){
    int p = pos;
    reader.setParameterAnnotation( this, tag, annotationIndex, annotationType);
    pos = p;
  }
  private void setParameterAnnotationsDone(ClassFileReader reader, Object tag, int paramIndex){
    int p = pos;
    reader.setParameterAnnotationsDone(this, tag, paramIndex);
    pos = p;
  }
  private void setParametersDone(ClassFileReader reader, Object tag){
    int p = pos;
    reader.setParametersDone(this, tag);
    pos = p;
  }

  public void setSignature(ClassFileReader reader, Object tag, String signature){
    int p = pos;
    reader.setSignature(this, tag, signature);
    pos = p;
  }

  //--- parsing

  /**
   * this is the main parsing routine that uses the ClassFileReader interface
   * to tell clients about the classfile contents
   *
   * ClassFile structure: http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#74353
   *   u4 magic;  // 0xcafebabe
   *   u2 minor_version;
   *   u2 major_version;
   *
   *   u2 constant_pool_count;
   *   cp_entry constant_pool[constant_pool_count-1];
   *   u2 access_flags;
   *
   *   u2 this_class;
   *   u2 super_class;
   *
   *   u2 interfaces_count;
   *   u2 interfaces[interfaces_count];
   *
   *   u2 fields_count;
   *   field_info fields[fields_count];
   *
   *   u2 methods_count;
   *   method_info methods[methods_count];
   *
   *   u2 attributes_count;
   *   attribute_info attributes[attributes_count];
   */
  public void parse( ClassFileReader reader)  throws ClassParseException {
    int cpIdx;

    try {
      // yeah, cafebabe
      int magic = readI4();
      if (magic != 0xCAFEBABE) {
        error("wrong magic: " + Integer.toHexString(magic));
      }

      // we don't do much with the version numbers yet
      int minor = readU2();
      int major = readU2();

      // get the const pool
      int cpCount = readU2();
      cpPos = new int[cpCount];
      cpValue = new Object[cpCount];
      parseCp(cpCount);

      // the class essentials
      int accessFlags = readU2();

      cpIdx = readU2();
      String clsName = (String) cpValue[cpIdx];

      cpIdx = readU2();
      String superClsName = (String) cpValue[cpIdx];


      setClass(reader, clsName, superClsName, accessFlags, cpCount);

      // interfaces
      int ifcCount = readU2();
      parseInterfaces(reader, ifcCount);

      // fields
      int fieldCount = readU2();
      parseFields(reader, fieldCount);

      // methods
      int methodCount = readU2();
      parseMethods(reader, methodCount);

      // class attributes
      int classAttrCount = readU2();
      parseClassAttributes(reader, classAttrCount);

    } catch (BailOut x){
      // nothing, just a control exception to shortcut the classfile parsing
    }
  }


  //--- constpool parsing

  public static String readModifiedUTF8String( byte[] data, int pos, int len) throws ClassParseException {
    
    int n = 0; // the number of chars in buf
    char[] buf = new char[len]; // it can't be more, but it can be less chars
    
    // \u0001 - \u007f             : single byte chars:  0xxxxxxx
    // \u0000 and \u0080 - \u07ff  : double byte chars:  110xxxxx, 10xxxxxx
    // \u0800 - \uffff             : tripple byte chars: 1110xxxx, 10xxxxxx, 10xxxxxx
    
    int max = pos+len;
    for (int i=pos; i<max; i++){
      int c = data[i] & 0xff;
      if ((c & 0x80) == 0){ // single byte char  0xxxxxxx
        buf[n++] = (char)c;
        
      } else {
        if ((c & 0x40) != 0){      // 11xxxxxx
          
          // for the sake of efficiency, we don't check for the trailing zero bit in the marker,
          // we just mask it out
          if ((c & 0x20) == 0) {   // 110xxxxx - double byte char
            buf[n++] = (char) (((c & 0x1f) << 6) | (data[++i] & 0x3f));
            
          } else {                 // 1110xxxx - tripple byte char
            buf[n++] = (char) (((c & 0x0f) << 12) | ((data[++i] & 0x3f) << 6) | (data[++i] & 0x3f));
          }
          
        } else {
          throw new ClassParseException("malformed modified UTF-8 input: ");
        }
      }
    }
    
    return new String(buf, 0, n);
  }

  
  // the protected methods are called automatically, the public parse..Attr() methods
  // are called optionally from the corresponding ClassFileReader.set..Attribute() method.
  // Note that these calls have to provide the ClassFileReader as an argument because
  // we might actually switch to another reader (e.g. MethodInfos for parseCodeAttr)

  protected void parseCp(int cpCount)  throws ClassParseException {
    int j = pos;

    byte[] data = this.data;
    int[] dataIdx = this.cpPos;
    Object[] values = this.cpValue;

    //--- first pass: store data index values and convert non-delegating constant values
    // cp_entry[0] is traditionally unused
    for (int i=1; i<cpCount; i++) {
      switch (data[j]){
        case 0:
          error("illegal constpool tag");

        case CONSTANT_UTF8:  // utf8_info { u1 tag; u2 length; u1 bytes[length]; }
          dataIdx[i] = j++;
          int len = ((data[j++]&0xff) <<8) | (data[j++]&0xff);

          String s = readModifiedUTF8String( data, j, len);
          values[i] = s;

          j += len;
          break;

        case 2:
          error("illegal constpool tag");

        case CONSTANT_INTEGER:  // Integer_info { u1 tag; u4 bytes; }
          dataIdx[i] = j++;

          int iVal = (data[j++]&0xff)<<24 | (data[j++]&0xff)<<16 | (data[j++]&0xff)<<8 | (data[j++]&0xff);
          values[i] = new Integer(iVal);
          break;

        case CONSTANT_FLOAT:  // Float_info  { u1 tag; u4 bytes; }
          dataIdx[i] = j++;

          int iBits = (data[j++]&0xff)<<24 | (data[j++]&0xff)<<16 | (data[j++]&0xff)<<8 | (data[j++]&0xff);
          float fVal = Float.intBitsToFloat(iBits);
          values[i] = new Float(fVal);
          break;

        case CONSTANT_LONG:  // Long_info { u1 tag; u4 high_bytes; u4 low_bytes; }
          dataIdx[i] = j++;
          long lVal =  (data[j++]&0xffL)<<56 | (data[j++]&0xffL)<<48 | (data[j++]&0xffL)<<40 | (data[j++]&0xffL)<<32
                    | (data[j++]&0xffL)<<24 | (data[j++]&0xffL)<<16 | (data[j++]&0xffL)<<8 | (data[j++]&0xffL);
          values[i] = new Long(lVal);

          dataIdx[++i] = -1;  // 8 byte cpValue occupy 2 index slots
          break;

        case CONSTANT_DOUBLE:  // Double_info  { u1 tag; u4 high_bytes; u4 low_bytes; }
          dataIdx[i] = j++;

          long lBits = (data[j++]&0xffL)<<56 | (data[j++]&0xffL)<<48 | (data[j++]&0xffL)<<40 | (data[j++]&0xffL)<<32
                    | (data[j++]&0xffL)<<24 | (data[j++]&0xffL)<<16 | (data[j++]&0xffL)<<8 | (data[j++]&0xffL);
          double dVal = Double.longBitsToDouble(lBits);
          values[i] = new Double(dVal);

          dataIdx[++i] = -1;  // 8 byte cpValue occupy 2 index slots
          break;

        case CONSTANT_CLASS:  // Class_info { u1 tag; u2 name_index<utf8>; }
          dataIdx[i] = j;
          values[i] = CpInfo.ConstantClass;

          j += 3;
          break;

        case CONSTANT_STRING:  // String_info { u1 tag; u2 string_index<utf8>; }
          dataIdx[i] = j;
          values[i] = CpInfo.ConstantString;

          j += 3;
          break;

        case FIELD_REF:  // Fieldref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
          dataIdx[i] = j;
          values[i] = CpInfo.FieldRef;
          j += 5;
          break;

        case METHOD_REF: // Methodref_info  { u1 tag; u2 class_index; u2 name_and_type_index; }
          dataIdx[i] = j;
          values[i] = CpInfo.MethodRef;
          j += 5;
          break;

        case INTERFACE_METHOD_REF: // InterfaceMethodref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
          dataIdx[i] = j;
          values[i] = CpInfo.InterfaceMethodRef;
          j += 5;
          break;

        case NAME_AND_TYPE: // NameAndType_info { u1 tag; u2 name_index<utf8>; u2 descriptor_index<utf8>; }
          dataIdx[i] = j;
          values[i] = CpInfo.NameAndType;

          j += 5;
          break;

        //--- the Java 8 ones
          
        case METHOD_HANDLE: // MethodHandle_info { u1 tag; u1 reference_kind; u2 reference_index<mthref>; }
          dataIdx[i] = j;
          values[i] = CpInfo.MethodHandle;
          j += 4;
          break;
          
        case METHOD_TYPE:  // MethodType_info { u1 tag;  u2 descriptor_index<utf8>; }
          dataIdx[i] = j;
          values[i] = CpInfo.MethodType;
          j += 3;
          break;

        case INVOKE_DYNAMIC: //  InvokeDynamic_info { u1 tag; u2 bootstrap_method_attr_index; u2 name_and_type_index; }
          dataIdx[i] = j;
          values[i] = CpInfo.InvokeDynamic;
          j += 5;
          break;
          
        default:
          error("illegal constpool tag: " + data[j]);
      }
    }

    pos = j;

    //--- second pass: store values of delegating constant values
    for (int i=1; i<cpCount; i++){
      Object v = cpValue[i];

      // we store string and class constants as their utf8 string values
      if (v == CpInfo.ConstantClass || v == CpInfo.ConstantString){
         cpValue[i] = cpValue[u2(cpPos[i]+1)];
      }
    }
  }

  protected void parseInterfaces(ClassFileReader reader, int ifcCount){

    setInterfaceCount(reader, ifcCount);

    for (int i=0; i<ifcCount; i++){
      int cpIdx = readU2();
      setInterface(reader, i, classNameAt(cpIdx));
    }

    setInterfacesDone(reader);
  }

  //--- fields
  protected void parseFields(ClassFileReader reader, int fieldCount) {

    setFieldCount(reader, fieldCount);

    for (int i=0; i<fieldCount; i++){
      int accessFlags = readU2();

      int cpIdx = readU2();
      String name = utf8At(cpIdx);

      cpIdx = readU2();
      String descriptor = utf8At(cpIdx);

      setField(reader, i, accessFlags, name, descriptor);

      int attrCount = readU2();
      parseFieldAttributes(reader, i, attrCount);

      setFieldDone(reader, i);
    }

    setFieldsDone(reader);
  }

  protected void parseFieldAttributes(ClassFileReader reader, int fieldIdx, int attrCount){
    setFieldAttributeCount(reader, fieldIdx, attrCount);

    for (int i=0; i<attrCount; i++){
      int cpIdx = readU2();
      String name = utf8At(cpIdx);

      name = internStdAttrName(cpIdx, name, stdFieldAttrs);

      int attrLength = readI4(); // actually U4, but we don't support 2GB attributes
      setFieldAttribute(reader, fieldIdx, i, name, attrLength);
    }

    setFieldAttributesDone(reader, fieldIdx);
  }

  /**
   * optionally called by reader to obtain a ConstantValue field attribute
   * 
   *   ConstantValue {u2 attrName<utf8>; u4 attrLength; u2 constIndex<class|string|int|float|long|double> }
   * 
   * pos is at constIndex
   */
  public void parseConstValueAttr(ClassFileReader reader, Object tag){
    int cpIdx = readU2();
    setConstantValue(reader, tag, cpValue[cpIdx]);
  }


  //--- methods
  protected void parseMethods(ClassFileReader reader, int methodCount) {

    setMethodCount(reader, methodCount);

    for (int i=0; i<methodCount; i++){
      int accessFlags = readU2();

      int cpIdx = readU2();
      String name = utf8At(cpIdx);

      cpIdx = readU2();
      String descriptor = utf8At(cpIdx);

      setMethod(reader, i, accessFlags, name, descriptor);

      int attrCount = readU2();
      parseMethodAttributes(reader, i, attrCount);

      setMethodDone(reader, i);
    }

    setMethodsDone(reader);
  }

  protected void parseMethodAttributes(ClassFileReader reader, int methodIdx, int attrCount){
    setMethodAttributeCount(reader, methodIdx, attrCount);

    for (int i=0; i<attrCount; i++){
      int cpIdx = readU2();
      String name = utf8At(cpIdx);

      name = internStdAttrName(cpIdx, name, stdMethodAttrs);

      int attrLength = readI4(); // actually U4, but we don't support 2GB attributes
      setMethodAttribute(reader, methodIdx, i, name, attrLength);
    }

    setMethodAttributesDone(reader, methodIdx);
  }

  public void parseExceptionAttr (ClassFileReader reader, Object tag){
    int exceptionCount = readU2();
    setExceptionCount(reader, tag, exceptionCount);

    for (int i=0; i<exceptionCount; i++){
      int cpIdx = readU2();
      String exceptionType = classNameAt(cpIdx);
      setException(reader, tag, i, exceptionType);
    }

    setExceptionsDone(reader, tag);
  }

  /**
   * (optionally) called by reader from within the setMethodAttribute() notification
   * This means we have recursive notification since this is a variable length
   * attribute that has variable length attributes
   *
   * Code_attribute { u2 attr_name_index<utf8>; u4 attr_length;
   *                  u2 max_stack; u2 max_locals;
   *                  u4 code_length; u1 code[code_length];
   *                  u2 exception_table_length;
   *                  { u2 start_pc; u2 end_pc; u2  handler_pc; u2  catch_type<class_entry>;
   *                  }	exception_table[exception_table_length];
   *                  u2 attributes_count;
   *                  attribute_info attributes[attributes_count];  }
   *
   * pos is at max_stack
   */
  public void parseCodeAttr (ClassFileReader reader, Object tag){
    int maxStack = readU2();
    int maxLocals = readU2();
    int codeLength = readI4();  // no code length > 2GB supported
    int codeStartPos = pos;

    setCode(reader, tag, maxStack, maxLocals, codeLength);

    int exceptionCount = readU2();
    setExceptionTableCount(reader, tag, exceptionCount);

    for (int i = 0; i < exceptionCount; i++) {
      int startPc = readU2();
      int endPc = readU2();
      int handlerPc = readU2();

      int cpIdx = readU2();
      String catchType = (String) cpValue[cpIdx]; // a Constant_class

      setExceptionTableEntry(reader, tag, i, startPc, endPc, handlerPc, catchType);
    }
    setExceptionTableDone(reader, tag);

    int attrCount = readU2();
    parseCodeAttrAttributes(reader, tag, attrCount);
  }


  protected void parseCodeAttrAttributes(ClassFileReader reader, Object tag, int attrCount){

    setCodeAttributeCount(reader, tag, attrCount);

    for (int i=0; i<attrCount; i++){
      int cpIdx = readU2();
      String name = utf8At(cpIdx);

      name = internStdAttrName(cpIdx, name, stdCodeAttrs);

      int attrLength = readI4(); // actually U4, but we don't support 2GB attributes
      setCodeAttribute(reader, tag, i, name, attrLength);
    }

    setCodeAttributesDone(reader, tag);
  }

  /**
   * optionally called from ClassFileReader.setCodeAttribute() to parse LineNumberTables
   *   LineNumberTable { u2 attrName; u4 attrLength;
   *                     u2 lineCount;
   *                     { u2 startPc; u2 lineNumber; } [lineCount] };
   * pos is at lineCount
   */
  public void parseLineNumberTableAttr(ClassFileReader reader, Object tag){
    int lineCount = readU2();
    setLineNumberTableCount(reader, tag, lineCount);
    
    for (int i=0; i<lineCount; i++){
      int startPc = readU2();
      int lineNumber = readU2();
      setLineNumber(reader, tag, i, lineNumber, startPc);
    }

    setLineNumberTableDone(reader, tag);
  }

  
  /**
   * optionally called from ClassFileReader.setCodeAttribute() to parse LocalVarTables
   *   LocalVarTableTable { u2 attrName; u4 attrLength;
   *                        u2 localVarCount;
   *                        { u2 startPc; u2 lineNumber; } [lineCount] };
   * pos is at localVarCount
   */
  public void parseLocalVarTableAttr(ClassFileReader reader, Object tag){
    int localVarCount = readU2();
    setLocalVarTableCount(reader, tag, localVarCount);
    
    for (int i=0; i<localVarCount; i++){
      int startPc = readU2();
      int length = readU2();
      int cpIdx = readU2();
      String varName = (String) cpValue[cpIdx];
      cpIdx = readU2();
      String descriptor = (String)  cpValue[cpIdx];
      int slotIndex = readU2();
      
      setLocalVar(reader, tag, i, varName, descriptor, startPc, startPc+length-1, slotIndex );
    }

    setLocalVarTableDone(reader, tag);
  }

  //--- class
  protected void parseClassAttributes(ClassFileReader reader, int attrCount){

    setClassAttributeCount(reader, attrCount);

    for (int i=0; i<attrCount; i++){
      int cpIdx = readU2();
      String name = utf8At(cpIdx);

      name = internStdAttrName(cpIdx, name, stdClassAttrs);

      int attrLength = readI4(); // actually U4, but we don't support 2GB attributes
      setClassAttribute(reader, i, name, attrLength);
    }

    setClassAttributesDone(reader);
  }


  /**
   * (optionally) called by ClassFileReader from within setClassAttribute() notification
   *
   * InnerClass { u2 nameIdx<utf8>; u4 length; u2 sourceFile<utf8>; }
   */
  public void parseSourceFileAttr(ClassFileReader reader, Object tag){
    int cpIdx = readU2();
    String pathName = utf8At(cpIdx);
    setSourceFile(reader, tag, pathName);
  }

  /**
   * (optionally) called by ClassFileReader from within setClassAttribute() notification
   *
   * InnerClass { 
   *    u2 nameIdx<utf8>; 
   *    u4 length;
   *    u2 classCount;
   *    { u2 innerCls<cls>;
   *      u2 outerCls<cls>;
   *      u2 innerName<utf8>; 
   *      u2 innerAccessFlags;
   *    } classes[classCount] }
   * }
   * 
   * pos is at classCount
   */
  public void parseInnerClassesAttr(ClassFileReader reader, Object tag){
    int innerClsCount = readU2();    
    setInnerClassCount(reader, tag, innerClsCount);

    for (int i = 0; i < innerClsCount; i++) {
      int cpIdx = readU2();
      String innerClsName = (cpIdx != 0) ? (String) cpValue[cpIdx] : null;
      cpIdx = readU2();
      String outerClsName = (cpIdx != 0) ? (String) cpValue[cpIdx] : null;
      cpIdx = readU2();
      String innerSimpleName = (cpIdx != 0) ? (String) cpValue[cpIdx] : null;
      int accessFlags = readU2();

      setInnerClass(reader, tag, i, outerClsName, innerClsName, innerSimpleName, accessFlags);
    }

    setInnerClassesDone(reader, tag);
  }
  
  /**
   * EnclosingMethod_attribute {
   *   u2 attribute_name_index;
   *   u4 attribute_length;
   *   u2 class_index     -> Class_info { u1 tag; u2 name_index->utf8 }
   *   u2 method_index    -> NameAndType_info { u1 tag; u2 name_index->utf8; u2 descriptor_index->utf8 }
   * }
   */
  public void parseEnclosingMethodAttr(ClassFileReader reader, Object tag){
    String enclosedMethod = null;
    String descriptor = null;
    
    int cpIdx = readU2(); // start of Class_info
    String enclosingClass =  nameAt(cpIdx);
    
    cpIdx = readU2(); // start of NameAndType_info
    
    // check if this is inside a method - we also get EnclosingMethod_infos for
    // classes that are not immediately enclosed
    if (cpIdx != 0){
      enclosedMethod = nameAt(cpIdx);    
      descriptor = descriptorAt(cpIdx);
    }
    
    setEnclosingMethod(reader, tag, enclosingClass, enclosedMethod, descriptor);
  }
  
  /**
   * BootstrapMethods_attribute {
   *     u2 attribute_name_index;
   *     u4 attribute_length;
   *     u2 num_bootstrap_methods;
   *     {   u2 bootstrap_method_ref; -> MethodHandle
   *         u2 num_bootstrap_arguments;
   *         u2 bootstrap_arguments[num_bootstrap_arguments];
   *     } bootstrap_methods[num_bootstrap_methods];
   * }
   * 
   * pos is at num_bootstrap_methods
  */
  public void parseBootstrapMethodAttr (ClassFileReader reader, Object tag){
    int nBootstrapMethods = readU2();
    
    setBootstrapMethodCount(reader, tag, nBootstrapMethods);
    
    for (int i=0; i<nBootstrapMethods; i++){
      int cpMhIdx = readU2();
      int nArgs = readU2();
      int[] bmArgs = new int[nArgs];
      for (int j=0; j<nArgs; j++){
        bmArgs[j] = readU2();
      }
      
      // kind of this method handle
      int refKind = mhRefTypeAt(cpMhIdx);
      
      // CONSTANT_Methodref_info structure
      int mrefIdx = mhMethodRefIndexAt(cpMhIdx);
      
      String clsName = methodClassNameAt(mrefIdx);
      String mthName = methodNameAt(mrefIdx);
      String descriptor = methodDescriptorAt(mrefIdx);
      
      setBootstrapMethod(reader, tag, i, refKind, clsName, mthName, descriptor, bmArgs);
    }
    
    setBootstrapMethodsDone( reader, tag);
  }
  
  String nameAt(int nameTypeInfoIdx) {
    return utf8At(u2(cpPos[nameTypeInfoIdx] + 1));
  }
  
  String descriptorAt (int nameTypeInfoIdx){
    return utf8At( u2( cpPos[nameTypeInfoIdx]+3));
  }

// those are as per http://java.sun.com/docs/books/jvms/second_edition/ClassFileFormat-Java5.pdf

  /*
   *   element_value {
   *     u1 tag;
   *     union {
   *       u2 const_value_index;
   *       { u2 type_name_index; u2 const_name_index; } enum_const_value;
   *       u2 class_info_index;
   *       annotation annotation_value;
   *       { u2 num_values; element_value values[num_values]; } array_value;
   *     } value;
   *   }
   *   valid tags are primitve type codes B,C,D,F,I,J,S,Z
   *   plus:   's'=String, 'e'=enum, 'c'=class, '@'=annotation, '['=array
   */
  void parseAnnotationValue(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex, String elementName, int arrayIndex){
    int cpIdx;
    Object val;

    int t = readUByte();
    switch (t){
      case 'Z':
        // booleans have to be treated differently since there is no CONSTANT_Boolean, i.e. values are
        // stored as CONSTANT_Integer in the constpool, i.e. the cpValue doesn't have the right type
        cpIdx = readU2();
        val = cpValue[cpIdx];
        val = Boolean.valueOf((Integer)val == 1);
        setPrimitiveAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, val);
        break;        

      case 'B':
        cpIdx = readU2();
        val = cpValue[cpIdx];
        val = Byte.valueOf(((Integer)val).byteValue());
        setPrimitiveAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, val);
        break;
        
      case 'C':
        cpIdx = readU2();
        val = cpValue[cpIdx];
        val = Character.valueOf((char)((Integer)val).shortValue());
        setPrimitiveAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, val);
        break;
        
      case 'S':
        cpIdx = readU2();
        val = cpValue[cpIdx];
        val = Short.valueOf(((Integer)val).shortValue());
        setPrimitiveAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, val);
        break;

      case 'I':
      case 'F':
      case 'D':
      case 'J':
        cpIdx = readU2();
        val = cpValue[cpIdx];
        setPrimitiveAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, val);
        break;

      case 's':
        cpIdx = readU2();
        String s = (String) cpValue[cpIdx];
        setStringAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, s);
        break;

      case 'e':
        cpIdx = readU2();
        String enumTypeName = (String)cpValue[cpIdx];
        cpIdx = readU2();
        String enumConstName = (String)cpValue[cpIdx];
        setEnumAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, enumTypeName, enumConstName);
        break;

      case 'c':
        cpIdx = readU2();
        String className = (String)cpValue[cpIdx];
        setClassAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, className);
        break;

      case '@':
        parseAnnotation(reader, tag, 0, false);  // getting recursive here
        break;

      case '[':
        int arrayLen = readU2();
        setAnnotationValueElementCount(reader, tag, annotationIndex, valueIndex, elementName, arrayLen);
        for (int i=0; i<arrayLen; i++){
          parseAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, i);
        }
        setAnnotationValueElementsDone(reader, tag, annotationIndex, valueIndex, elementName);
        break;
    }
  }

  /*
   *   annotation {
   *     u2 type_index;
   *     u2 num_element_value_pairs;
   *     {
   *       u2 element_name_index;
   *       element_value value;
   *     } element_value_pairs[num_element_value_pairs]
   *   }
   */
  void parseAnnotation (ClassFileReader reader, Object tag, int annotationIndex, boolean isParameterAnnotation){
    int cpIdx = readU2();
    String annotationType = (String)cpValue[cpIdx];

    if (isParameterAnnotation){
      setParameterAnnotation(reader, tag, annotationIndex, annotationType);
    } else {
      setAnnotation(reader, tag, annotationIndex, annotationType);
    }

    parseAnnotationValues(reader, tag, annotationIndex);
  }

  void parseAnnotationValues (ClassFileReader reader, Object tag, int annotationIndex){
    int nValuePairs = readU2();
    setAnnotationValueCount(reader, tag, annotationIndex, nValuePairs);

    for (int i=0; i<nValuePairs; i++){
      int cpIdx = readU2();
      String elementName = (String)cpValue[cpIdx];
      parseAnnotationValue(reader, tag, annotationIndex, i, elementName, -1);
    }

    setAnnotationValuesDone(reader, tag, annotationIndex);
  }
  
  /*
   * class, field, method annotation attributes (only one per target)
   *
   *  Runtime[In]VisibleAnnotations_attribute {
   *     u2 attribute_name_index;
   *     u4 attribute_length;
   *     u2 num_annotations;        << pos
   *     annotation annotations[num_annotations];
   *   }
   */
  public void parseAnnotationsAttr (ClassFileReader reader, Object tag){
    int numAnnotations = readU2();
    setAnnotationCount(reader, tag, numAnnotations);

    for (int i=0; i<numAnnotations; i++){
      parseAnnotation(reader, tag, i, false);
    }

    setAnnotationsDone(reader, tag);
  }

  
  // JSR 308 type annotation target types
  public static final int CLASS_TYPE_PARAMETER                 = 0x00;
  public static final int METHOD_TYPE_PARAMETER                = 0x01;
  public static final int CLASS_EXTENDS                        = 0x10;
  public static final int CLASS_TYPE_PARAMETER_BOUND           = 0x11;
  public static final int METHOD_TYPE_PARAMETER_BOUND          = 0x12;
  public static final int FIELD                                = 0x13;
  public static final int METHOD_RETURN                        = 0x14;
  public static final int METHOD_RECEIVER                      = 0x15;
  public static final int METHOD_FORMAL_PARAMETER              = 0x16;
  public static final int THROWS                               = 0x17;
  public static final int LOCAL_VARIABLE                       = 0x40;
  public static final int RESOURCE_VARIABLE                    = 0x41;
  public static final int EXCEPTION_PARAMETER                  = 0x42;
  public static final int INSTANCEOF                           = 0x43;
  public static final int NEW                                  = 0x44;
  public static final int CONSTRUCTOR_REFERENCE                = 0x45;
  public static final int METHOD_REFERENCE                     = 0x46;
  public static final int CAST                                 = 0x47;
  public static final int CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48;
  public static final int METHOD_INVOCATION_TYPE_ARGUMENT      = 0x49;
  public static final int CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT  = 0x4a;
  public static final int METHOD_REFERENCE_TYPE_ARGUMENT       = 0x4b;  
  
  public static String getTargetTypeName (int targetType){
    switch (targetType){
      case CLASS_TYPE_PARAMETER: return "class type parameter";
      case METHOD_TYPE_PARAMETER: return "method type parameter";
      case CLASS_EXTENDS: return "super class";
      case CLASS_TYPE_PARAMETER_BOUND: return "class type parameter bound";
      case METHOD_TYPE_PARAMETER_BOUND: return "method type parameter bound";
      case FIELD: return "field";
      case METHOD_RETURN: return "method return";
      case METHOD_RECEIVER: return "method receiver";
      case METHOD_FORMAL_PARAMETER: return "method formal parameter";
      case THROWS: return "throws";
      case LOCAL_VARIABLE: return "local variable";
      case RESOURCE_VARIABLE: return "resource variable";
      case EXCEPTION_PARAMETER: return "exception parameter";
      case INSTANCEOF: return "instanceof";
      case NEW: return "new";
      case CONSTRUCTOR_REFERENCE: return "ctor reference";
      case METHOD_REFERENCE: return "method reference";
      case CAST: return "case";
      case METHOD_INVOCATION_TYPE_ARGUMENT: return "method invocation type argument";
      case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: return "ctor reference type argument";
      case METHOD_REFERENCE_TYPE_ARGUMENT: return "method reference type argument";
      default:
        return "<unknown target type 0x" + Integer.toHexString(targetType);
    }
  }
  
  public static String getTypePathEncoding (short[] typePath){
    if (typePath == null){
      return "()";
    }
    
    StringBuffer sb = new StringBuffer();
    for (int i=0; i<typePath.length;i++){
      int e = typePath[i];
      sb.append('(');
      sb.append( Integer.toString((e>>8) & 0xff));
      sb.append( Integer.toString(e & 0xff));
      sb.append(')');
    }
    
    return sb.toString();
  }
  
  public static String getScopeEncoding (long[] scopeEntries){
    StringBuffer sb = new StringBuffer();
    for (int i=0; i<scopeEntries.length;i++){
      long e = scopeEntries[i];
      int slotIndex = (int)(e & 0xffff);
      int length = (int)((e >> 16) & 0xffff);
      int startPc = (int)((e >> 32) & 0xffff);
      
      if (i>0){
        sb.append(',');
      }
      
      sb.append('[');
      sb.append( Integer.toString(startPc));
      sb.append("..");
      sb.append( Integer.toString(startPc + length-1));
      sb.append("]#");
      sb.append(slotIndex);
    }
    
    return sb.toString();
  }
  
  // JSR 308 type annotation, which adds 3 fields to the old annotation structure
  //
  //  type_annotation {
  //      u1 target_type;        // targeted program element (sec 3.2)
  //      union {                // ?? this is probably packed - variable size unions make no sense
  //          type_parameter_target;
  //          supertype_target;
  //          type_parameter_bound_target;
  //          empty_target;
  //          method_formal_parameter_target;
  //          throws_target;
  //          localvar_target;
  //          catch_target;
  //          offset_target;
  //          type_argument_target;
  //      } target_info;         // targeted program element (sec 3.3)
  //
  //      type_path target_path; // encoding of annotation position in compound type (array, generic, etc., sec 3.4)
  //
  //                             // standard annotation fields
  //      u2 type_index;         // the annotation type
  //      u2 num_element_value_pairs;
  //      {
  //          u2 element_name_index;
  //          element_value value;
  //      } element_value_pairs[num_element_value_pairs];
  //  }
  //
  //  struct type_path {
  //    u1              path_length;
  //    type_path_entry path[path_length];
  //  }
  //
  //  struct type_path_entry {
  //    u1 type_path_kind;
  //        // 0: deeper in array type
  //        // 1: deeper in nested type
  //        // 2: bound of wildcard typearg
  //        // 3: type argument of parameterized type
  //    u1 type_argument_index;
  //        // 0, if type_path_kind == 0,1,2
  //        // 0-based index of type arg in parameterized type if type_path_kind i== 3
  //  }
  
  int getTargetInfoSize (int targetType){
    int len = 3; // max static length are xx_TYPE_ARGUMENTs
    if (targetType == LOCAL_VARIABLE || targetType == RESOURCE_VARIABLE){
      len = Math.max( len, u2(pos) * 6); // three u2 values per entry
    }
    
    return len;
  }

  int getTypePathSize (short[] typePath){
    int typePathSize = 1;
    if (typePath != null) {
      typePathSize += typePath.length * 2;
    }
    return typePathSize;
  }
  
  
  short[] readTypePath (){
    short[] typePath = null;
    
    int pathLength = readUByte();
    if (pathLength > 0){
      typePath = new short[pathLength];
      for (int i=0; i<pathLength; i++){
        int pathKind = (short)readUByte();
        int argIdx = (short)readUByte();
        typePath[i]= (short)((pathKind << 8) | argIdx);
      }
    }
    
    return typePath;
  }

  String readAnnotationType (){
    int cpIdx = readU2();
    String annotationType = (String)cpValue[cpIdx];
    return annotationType;
  }

  void setTypeAnnotation (ClassFileReader reader, Object tag, int annotationIndex) {
    int targetType = readUByte();
    
    switch (targetType){
      case CLASS_TYPE_PARAMETER:
      case METHOD_TYPE_PARAMETER: {
        // type_parameter_target { u1 type_parameter_index; }
        int typeParamIdx = readUByte();
        reader.setTypeParameterAnnotation( this, tag, annotationIndex, targetType, typeParamIdx, readTypePath(), readAnnotationType());
        break;
      } 
      case CLASS_EXTENDS: {
        // supertype_target { u2 supertype_index; }
        int superTypeIdx = readU2();
        reader.setSuperTypeAnnotation( this, tag, annotationIndex, targetType, superTypeIdx, readTypePath(), readAnnotationType());
        break;
      }
      case CLASS_TYPE_PARAMETER_BOUND:
      case METHOD_TYPE_PARAMETER_BOUND: {
        // type_parameter_bound_target { u1 type_parameter_index; u1 bound_index; }
        int typeParamIdx = readUByte();
        int boundIdx = readUByte();
        reader.setTypeParameterBoundAnnotation(this, tag, annotationIndex, targetType, typeParamIdx, boundIdx, readTypePath(), readAnnotationType());
        break;
      }
      case METHOD_RETURN:
      case METHOD_RECEIVER:
      case FIELD:
        // empty_target {}
        reader.setTypeAnnotation( this, tag, annotationIndex, targetType, readTypePath(), readAnnotationType());
        break;
        
      case METHOD_FORMAL_PARAMETER: {
        // method_formal_parameter_target { u1 method_formal_parameter_index; }
        int formalParamIdx = readUByte();
        reader.setFormalParameterAnnotation( this, tag, annotationIndex, targetType, formalParamIdx, readTypePath(), readAnnotationType());
        break;
      }
      case THROWS: {
        // throws_target { u2 throws_type_index; }
        int throwsTypeIdx = readU2();
        reader.setThrowsAnnotation( this, tag, annotationIndex, targetType, throwsTypeIdx, readTypePath(), readAnnotationType());        
        break;
      } 
      case LOCAL_VARIABLE:
      case RESOURCE_VARIABLE: {
        // this can't just refer to a LocalVarInfo since those depend on debug compile options
        //
        //  localvar_target {
        //      u2 table_length;  // number of entries, not bytes
        //      {
        //          u2 start_pc;
        //          u2 length; // bytecode offset length
        //          u2 index;  // local var idx
        //      } table[table_length];
        //  }
        int tableLength = readU2();
        long[] scopeEntries = new long[tableLength];
        for (int i=0; i<tableLength; i++){
          int startPc = readU2();
          int length = readU2();
          int slotIdx = readU2();
          scopeEntries[i] = ((long)startPc << 32) | ((long)length << 16) | slotIdx;
        }
        reader.setVariableAnnotation( this, tag, annotationIndex, targetType, scopeEntries, readTypePath(), readAnnotationType());
        break;
      }
      case EXCEPTION_PARAMETER: {
        // catch_target { u2 exception_table_index; }
        int exceptionIdx = readU2();
        reader.setExceptionParameterAnnotation( this, tag, annotationIndex, targetType, exceptionIdx, readTypePath(), readAnnotationType());        
        break;
      }
      case INSTANCEOF:
      case METHOD_REFERENCE:
      case CONSTRUCTOR_REFERENCE:
      case NEW: {
        // offset_target { u2 offset; }   // insn offset within bytecode
        int offset = readU2();
        reader.setBytecodeAnnotation(this, tag, annotationIndex, targetType, offset, readTypePath(), readAnnotationType());
        break;
      }
      case CAST:
      case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
      case METHOD_INVOCATION_TYPE_ARGUMENT:
      case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
      case METHOD_REFERENCE_TYPE_ARGUMENT: {
        //  type_argument_target {
        //      u2 offset;
        //      u1 type_argument_index;
        //  }
        int offset = readU2();
        int typeArgIdx = readUByte();
        reader.setBytecodeTypeParameterAnnotation(this, tag, annotationIndex, targetType, offset, typeArgIdx, readTypePath(), readAnnotationType());
        break;
      }
      
      default:
        // <2do - report this to the reader
        throw new RuntimeException("unknown type annotation target: 0x" + Integer.toHexString(targetType));
    }
  }

  
  void parseTypeAnnotation (ClassFileReader reader, Object tag, int annotationIndex) {
   
    // this does the respective setXTypeAnnotation() reader callback
    //dumpData(pos, 16);
    setTypeAnnotation(reader, tag, annotationIndex);
    
    // now set the annotation value pairs
    parseAnnotationValues( reader, tag, annotationIndex);
  }
  
  /*
   * Runtime[In]VisibleTypeAnnotations_attribute {
   *    u2 attribute_name_index;
   *    u4 attribute_length;
   *    u2 num_annotations;
   *    type_annotation annotations[num_annotations];
   * }
   */
  public void parseTypeAnnotationsAttr (ClassFileReader reader, Object tag) {
    int numAnnotations = readU2();
    setTypeAnnotationCount(reader, tag, numAnnotations);

    for (int i=0; i<numAnnotations; i++){
      parseTypeAnnotation(reader, tag, i);
    }

    setTypeAnnotationsDone(reader, tag);
  }
  
  /*
   *   RuntimeInvisibleParameterAnnotations_attribute {
   *     u2 attribute_name_index;
   *     u4 attribute_length;
   *     u1 num_parameters; << pos
   *     {
   *       u2 num_annotations;
   *       annotation annotations[num_annotations];
   *     } parameter_annotations[num_parameters];
   *   }
   */
   public void parseParameterAnnotationsAttr(ClassFileReader reader, Object tag){
     int numParameters = readUByte();
     setParameterCount(reader, tag, numParameters);
     for (int i=0; i<numParameters; i++){
       int numAnnotations = readU2();

       setParameterAnnotationCount(reader, tag, i, numAnnotations);
       for (int j=0; j<numAnnotations; j++){
         parseAnnotation(reader, tag, j, true);
       }
       setParameterAnnotationsDone(reader, tag, i);
     }
     setParametersDone(reader, tag);
   }

  /**
   *  Signature_attribute {
   *    u2 attribute_name_index;
   *    u4 attr-length;
   *    u2 signature-index << pos
   *  }
   */
   public void parseSignatureAttr(ClassFileReader reader, Object tag){
     int cpIdx = readU2();
     setSignature(reader, tag, utf8At(cpIdx));
   }


  /**
   *    AnnotationDefault_attribute {
   *      u2 attribute_name_index;
   *      u4 attribute_length;
   *      element_value default_value; << pos
   *    }
   */
   public void parseAnnotationDefaultAttr(ClassFileReader reader, Object tag){
     parseAnnotationValue(reader, tag, -1, -1, null, -1);
   }


//   EnclosingMethod_attribute {
//     u2 attribute_name_index;
//     u4 attribute_length;
//     u2 class_index
//     u2 method_index;
//   }

//   LocalVariableTypeTable_attribute {  // Code attr
//     u2 attribute_name_index;
//     u4 attribute_length;
//     u2 local_variable_type_table_length;
//     {
//       u2 start_pc;
//       u2 length;
//       u2 name_index;
//       u2 signature_index;
//       u2 index;
//     } local_variable_type_table[local_variable_type_table_length];
//   }




  public void parseBytecode(JVMByteCodeReader reader, Object tag, int codeLength){
    int localVarIndex;
    int cpIdx;
    int constVal;
    int offset;
    int defaultOffset;

    boolean isWide = false; // modifier for Xload,Xstore,ret and iinc

    int startPos = pos;
    int endPos = pos+codeLength;
    int nextPos;


    while (pos < endPos){
      pc = pos - startPos;

      int opcode = readUByte();
      switch (opcode){
        case 0: // nop
          reader.nop();
          break;
        case 1:  // aconst_null
          reader.aconst_null();
          break;
        case 2: // iconst_m1
          reader.iconst_m1();
          break;
        case 3: // iconst_0
          reader.iconst_0();
          break;
        case 4: // iconst_1
          reader.iconst_1();
          break;
        case 5: // iconst_2
          reader.iconst_2();
          break;
        case 6: // iconst_3
          reader.iconst_3();
          break;
        case 7: // iconst_4
          reader.iconst_4();
          break;
        case 8: // iconst_5
          reader.iconst_5();
          break;
        case 9: // lconst_0
          reader.lconst_0();
          break;
        case 10: // lconst_1
          reader.lconst_1();
          break;
        case 11: // fconst_0
          reader.fconst_0();
          break;
        case 12: // fconst_1
          reader.fconst_1();
          break;
        case 13: // fconst_2
          reader.fconst_2();
          break;
        case 14: // dconst_0
          reader.dconst_0();
          break;
        case 15: // dconst_1
          reader.dconst_1();
          break;
        case 16: // bipush
          constVal = readByte();
          reader.bipush(constVal);
          break;
        case 17: // sipush
          constVal = readI2();
          reader.sipush(constVal);
          break;
        case 18: // ldc
          cpIdx = readUByte();
          reader.ldc_(cpIdx);
          break;
        case 19: // ldc_w
          cpIdx = readU2();
          reader.ldc_w_(cpIdx);
          break;
        case 20: // ldc2_w
          cpIdx = readU2();
          reader.ldc2_w(cpIdx);
          break;
        case 21: // iload
          localVarIndex = isWide ? readU2() : readUByte();
          reader.iload(localVarIndex);
          break;
        case 22: // lload
          localVarIndex = isWide ? readU2() : readUByte();
          reader.lload(localVarIndex);
          break;
        case 23: // fload
          localVarIndex = isWide ? readU2() : readUByte();
          reader.fload(localVarIndex);
          break;
        case 24: // dload
          localVarIndex = isWide ? readU2() : readUByte();
          reader.dload(localVarIndex);
          break;
        case 25: // aload
          localVarIndex = isWide ? readU2() : readUByte();
          reader.aload(localVarIndex);
          break;
        case 26: // iload_0
          reader.iload_0();
          break;
        case 27: // iload_1
          reader.iload_1();
          break;
        case 28: // iload_2
          reader.iload_2();
          break;
        case 29: // iload_3
          reader.iload_3();
          break;
        case 30: // lload_0
          reader.lload_0();
          break;
        case 31: // lload_1
          reader.lload_1();
          break;
        case 32: // lload_2
          reader.lload_2();
          break;
        case 33: // lload_3
          reader.lload_3();
          break;
        case 34: // fload_0
          reader.fload_0();
          break;
        case 35: // fload_1
          reader.fload_1();
          break;
        case 36: // fload_2
          reader.fload_2();
          break;
        case 37: // fload_3
          reader.fload_3();
          break;
        case 38: // dload_0
          reader.dload_0();
          break;
        case 39: // dload_1
          reader.dload_1();
          break;
        case 40: // dload_2
          reader.dload_2();
          break;
        case 41: // dload_3
          reader.dload_3();
          break;
        case 42: // aload_0
          reader.aload_0();
          break;
        case 43: // aload_1
          reader.aload_1();
          break;
        case 44: // aload_2
          reader.aload_2();
          break;
        case 45: // aload_3
          reader.aload_3();
          break;
        case 46: // iaload
          reader.iaload();
          break;
        case 47: // laload
          reader.laload();
          break;
        case 48: // faload
          reader.faload();
          break;
        case 49: // daload
          reader.daload();
          break;
        case 50: // aaload
          reader.aaload();
          break;
        case 51: // baload
          reader.baload();
          break;
        case 52: // caload
          reader.caload();
          break;
        case 53: // saload
          reader.saload();
          break;
        case 54: // istore
          localVarIndex = isWide ? readU2() : readUByte();
          reader.istore(localVarIndex);
          break;
        case 55: // lstore
          localVarIndex = isWide ? readU2() : readUByte();
          reader.lstore(localVarIndex);
          break;
        case 56: // fstore
          localVarIndex = isWide ? readU2() : readUByte();
          reader.fstore(localVarIndex);
          break;
        case 57: // dstore
          localVarIndex = isWide ? readU2() : readUByte();
          reader.dstore(localVarIndex);
          break;
        case 58: // astore
          localVarIndex = isWide ? readU2() : readUByte();
          reader.astore(localVarIndex);
          break;
        case 59: // istore_0
          reader.istore_0();
          break;
        case 60: // istore_1
          reader.istore_1();
          break;
        case 61: // istore_2
          reader.istore_2();
          break;
        case 62: // istore_3
          reader.istore_3();
          break;
        case 63: // lstore_0
          reader.lstore_0();
          break;
        case 64: // lstore_1
          reader.lstore_1();
          break;
        case 65: // lstore_2
          reader.lstore_2();
          break;
        case 66: // lstore_3
          reader.lstore_3();
          break;
        case 67: // fstore_0
          reader.fstore_0();
          break;
        case 68: // fstore_1
          reader.fstore_1();
          break;
        case 69: // fstore_2
          reader.fstore_2();
          break;
        case 70: // fstore_3
          reader.fstore_3();
          break;
        case 71: //dstore_0
          reader.dstore_0();
          break;
        case 72: //dstore_1
          reader.dstore_1();
          break;
        case 73: //dstore_2
          reader.dstore_2();
          break;
        case 74: //dstore_3
          reader.dstore_3();
          break;
        case 75: // astore_0
          reader.astore_0();
          break;
        case 76: // astore_1
          reader.astore_1();
          break;
        case 77: // astore_2
          reader.astore_2();
          break;
        case 78: // astore_3
          reader.astore_3();
          break;
        case 79: // iastore
          reader.iastore();
          break;
        case 80: // lastore
          reader.lastore();
          break;
        case 81: // fastore
          reader.fastore();
          break;
        case 82: // dastore
          reader.dastore();
          break;
        case 83: // aastore
          reader.aastore();
          break;
        case 84: // bastore
          reader.bastore();
          break;
        case 85: // castore
          reader.castore();
          break;
        case 86: // sastore
          reader.sastore();
          break;
        case 87: // pop
          reader.pop();
          break;
        case 88: // pop2
          reader.pop2();
          break;
        case 89: // dup
          reader.dup();
          break;
        case 90: // dup_x1
          reader.dup_x1();
          break;
        case 91: // dup_x2
          reader.dup_x2();
          break;
        case 92: // dup2
          reader.dup2();
          break;
        case 93: // dup2_x1
          reader.dup2_x1();
          break;
        case 94: // dup2_x2
          reader.dup2_x2();
          break;
        case 95: // swap
          reader.swap();
          break;
        case 96: // iadd
          reader.iadd();
          break;
        case 97: // ladd
          reader.ladd();
          break;
        case 98: // fadd
          reader.fadd();
          break;
        case 99: // dadd
          reader.dadd();
          break;
        case 100: // isub
          reader.isub();
          break;
        case 101: // lsub
          reader.lsub();
          break;
        case 102: // fsub
          reader.fsub();
          break;
        case 103: // dsub
          reader.dsub();
          break;
        case 104: // imul
          reader.imul();
          break;
        case 105: // lmul
          reader.lmul();
          break;
        case 106: // fmul
          reader.fmul();
          break;
        case 107: // dmul
          reader.dmul();
          break;
        case 108: // idiv
          reader.idiv();
          break;
        case 109: // ldiv
          reader.ldiv();
          break;
        case 110: // fdiv
          reader.fdiv();
          break;
        case 111: //ddiv
          reader.ddiv();
          break;
        case 112: // irem
          reader.irem();
          break;
        case 113: // lrem
          reader.lrem();
          break;
        case 114: // frem
          reader.frem();
          break;
        case 115: // drem
          reader.drem();
          break;
        case 116: // ineg
          reader.ineg();
          break;
        case 117: // lneg
          reader.lneg();
          break;
        case 118: // fneg
          reader.fneg();
          break;
        case 119: // dneg
          reader.dneg();
          break;
        case 120: // ishl
          reader.ishl();
          break;
        case 121: // lshl
          reader.lshl();
          break;
        case 122: // ishr
          reader.ishr();
          break;
        case 123: // lshr
          reader.lshr();
          break;
        case 124: // iushr
          reader.iushr();
          break;
        case 125: // lushr
          reader.lushr();
          break;
        case 126: // iand
          reader.iand();
          break;
        case 127: // land
          reader.land();
          break;
        case 128: // ior
          reader.ior();
          break;
        case 129: // lor
          reader.lor();
          break;
        case 130: // ixor
          reader.ixor();
          break;
        case 131: // lxor
          reader.lxor();
          break;
        case 132: // iinc
          if (isWide){
            localVarIndex = readU2();
            constVal = readI2();
          } else {
            localVarIndex = readUByte();
            constVal = readByte();
          }
          reader.iinc(localVarIndex, constVal);
          break;
        case 133: // i2l
          reader.i2l();
          break;
        case 134: // i2f
          reader.i2f();
          break;
        case 135: // i2d
          reader.i2d();
          break;
        case 136: // l2i
          reader.l2i();
          break;
        case 137: // l2f
          reader.l2f();
          break;
        case 138: // l2d
          reader.l2d();
          break;
        case 139: // f2i
          reader.f2i();
          break;
        case 140: // f2l
          reader.f2l();
          break;
        case 141: // f2d
          reader.f2d();
          break;
        case 142: // d2i
          reader.d2i();
          break;
        case 143: // d2l
          reader.d2l();
          break;
        case 144: // d2f
          reader.d2f();
          break;
        case 145: // i2b
          reader.i2b();
          break;
        case 146: // i2c
          reader.i2c();
          break;
        case 147: // i2s
          reader.i2s();
          break;
        case 148: // lcmp
          reader.lcmp();
          break;
        case 149: // fcmpl
          reader.fcmpl();
          break;
        case 150: // fcmpg
          reader.fcmpg();
          break;
        case 151: // dcmpl
          reader.dcmpl();
          break;
        case 152: // dcmpg
          reader.dcmpg();
          break;
        case 153: // ifeq
          offset = readI2();
          reader.ifeq(offset);
          break;
        case 154: // ifne
          offset = readI2();
          reader.ifne(offset);
          break;
        case 155: // iflt
          offset = readI2();
          reader.iflt(offset);
          break;
        case 156: // ifge
          offset = readI2();
          reader.ifge(offset);
          break;
        case 157: // ifgt
          offset = readI2();
          reader.ifgt(offset);
          break;
        case 158: // ifle
          offset = readI2();
          reader.ifle(offset);
          break;
        case 159: // if_icmpeq
          offset = readI2();
          reader.if_icmpeq(offset);
          break;
        case 160: // if_icmpne
          offset = readI2();
          reader.if_icmpne(offset);
          break;
        case 161: // if_icmplt
          offset = readI2();
          reader.if_icmplt(offset);
          break;
        case 162: // if_icmpge
          offset = readI2();
          reader.if_icmpge(offset);
          break;
        case 163: // if_icmpgt
          offset = readI2();
          reader.if_icmpgt(offset);
          break;
        case 164: // if_icmple
          offset = readI2();
          reader.if_icmple(offset);
          break;
        case 165: // if_acmpeq
          offset = readI2();
          reader.if_acmpeq(offset);
          break;
        case 166: // if_acmpne
          offset = readI2();
          reader.if_acmpne(offset);
          break;
        case 167: // goto
          offset = readI2();
          reader.goto_(offset);
          break;
        case 168: // jsr
          offset = readI2();
          reader.jsr(offset);
          break;
        case 169: // ret
          localVarIndex = isWide ? readU2() : readUByte();
          reader.ret(localVarIndex);
          break;
        case 170: // tableswitch
          pos = (((pc+4)>>2)<<2)+startPos; // skip over padding

          defaultOffset = readI4();
          int low = readI4();
          int high = readI4();

          int len = high-low+1;
          nextPos = pos + len*4;
          reader.tableswitch(defaultOffset, low, high);
          pos = nextPos;
          break;
        case 171: // lookupswitch
          pos = (((pc+4)>>2)<<2)+startPos; // skip over padding

          defaultOffset = readI4();
          int nPairs = readI4();

          nextPos = pos + (nPairs*8);
          reader.lookupswitch(defaultOffset, nPairs);
          pos = nextPos;
          break;
        case 172: // ireturn
          reader.ireturn();
          break;
        case 173: // lreturn
          reader.lreturn();
          break;
        case 174: // freturn
          reader.freturn();
          break;
        case 175: // dreturn
          reader.dreturn();
          break;
        case 176: // areturn
          reader.areturn();
          break;
        case 177: // return
          reader.return_();
          break;
        case 178: // getstatic
          cpIdx = readU2(); // CP index of fieldRef
          reader.getstatic(cpIdx);
          break;
        case 179: // putstatic
          cpIdx = readU2(); // CP index of fieldRef
          reader.putstatic(cpIdx);
          break;
        case 180: // getfield
          cpIdx = readU2(); // CP index of fieldRef
          reader.getfield(cpIdx);
          break;
        case 181: // putfield
          cpIdx = readU2(); // CP index of fieldRef
          reader.putfield(cpIdx);
          break;
        case 182: // invokevirtual
          cpIdx = readU2(); // CP index of methodRef
          reader.invokevirtual(cpIdx);
          break;
        case 183: // invokespecial
          cpIdx = readU2(); // CP index of methodRef
          reader.invokespecial(cpIdx);
          break;
        case 184: // invokestatic
          cpIdx = readU2(); // CP index of methodRef
          reader.invokestatic(cpIdx);
          break;
        case 185: // invokeinterface
          cpIdx = readU2(); // CP index of methodRef
          int count = readUByte();
          int zero = readUByte(); // must be 0
          reader.invokeinterface(cpIdx, count, zero);
          break;
        case 186: // invokedynamic
          cpIdx = readU2(); // CP index of bootstrap method
          readUByte();  // 0
          readUByte(); //  0
          reader.invokedynamic(cpIdx);
          break;
        case 187: // new
          cpIdx = readU2();
          reader.new_(cpIdx);
          break;
        case 188: // newarray
          int aType = readUByte();
          reader.newarray(aType);
          break;
        case 189: // anewarray
          cpIdx = readU2(); // CP index of component type
          reader.anewarray(cpIdx);
          break;
        case 190: // arraylength
          reader.arraylength();
          break;
        case 191: // athrow
          reader.athrow();
          break;
        case 192: // checkcast
          cpIdx = readU2(); // cast type cp index
          reader.checkcast(cpIdx);
          break;
        case 193: // instanceof
          cpIdx = readU2(); // check type cp index
          reader.instanceof_(cpIdx);
          break;
        case 194: // monitorenter
          reader.monitorenter();
          break;
        case 195: // monitorexit
          reader.monitorexit();
          break;
        case 196: // wide
          isWide = true;
          // affects immediate operand width if next bytecode is:
          //  iload,fload,aload,lload,dload,
          //  istore,fstore,astore,lstore,dstore
          //  ret
          reader.wide();
          continue;
        case 197: // multianewarray
          cpIdx = readU2();
          int dimensions = readUByte();
          reader.multianewarray(cpIdx, dimensions);
          break;
        case 198: // ifnull
          offset = readI2();
          reader.ifnull(offset);
          break;
        case 199: // ifnonnull
          offset = readI2();
          reader.ifnonnull(offset);
          break;
        case 200: // goto_w
          offset = readI4();
          reader.goto_w(offset);
          break;
        case 201: // jsr_w
          offset = readI4();
          reader.jsr_w(offset);
          break;
          
          
        default:
          reader.unknown(opcode);
      }

      isWide = false; // reset wide modifier
    }

  }

  //--- those can only be called from within a JVMByteCodeReader.tableswitch() notification
  public void parseTableSwitchEntries(JVMByteCodeReader reader, int low, int high){
    for (int val=low; val<=high; val++){
      int offset = readI4();
      reader.tableswitchEntry(val, offset);
    }
  }
  public int getTableSwitchOffset(int low, int high, int defaultOffset, int val){
    if (val < low || val > high){
      return defaultOffset;
    }

    int n = Math.abs(val - low);
    pos += n*4;
    int pcOffset = readI4();

    return pcOffset;
  }

  //--- those can only be called from within a JVMByteCodeReader.lookupswitch() notification
  public void parseLookupSwitchEntries(JVMByteCodeReader reader, int nEntries){
    for (int i=0; i<nEntries; i++){
      int value = readI4();
      int offset = readI4();
      reader.lookupswitchEntry(i, value, offset);
    }
  }
  public int getLookupSwitchOffset(int nEntries, int defaultOffset, int val){
    for (int i=0; i<nEntries; i++){
      int match = readI4();
      if (val > match){
        pos +=4;
      } else if (val == match) {
        int offset = readI4();
        return offset;
      } else {
        break;
      }
    }
    return defaultOffset;
  }

}