/* --- Copyright Jonathan Meyer 1997. All rights reserved. -----------------
 > File:        jasmin/src/jasmin/ClassFile.java
 > Purpose:     Uses a parser and the JAS package to create Java class files
 > Author:      Jonathan Meyer, 10 July 1996
 */

package jasmin;


/*
 * This class is a bit monolithic, and should probably be converted into
 * several smaller classes. However, for this specific application,
 * its acceptable.
 *
 */



import static jas.MethodHandleCP.STATIC_METHOD_KIND;
import jas.*;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.*;

/**
 * A ClassFile object is used to represent the binary data that makes up a
 * Java class file - it also serves as the public
 * API to the Jasmin assembler, though users should beware: the API
 * is likely to change in future versions (though its such a small API
 * at the moment that changes are likely to have only a small impact).<p>
 *
 * To assemble a file, you first construct a jasmin.ClassFile object, then
 * call readJasmin() to read in the contents of a Jasmin assembly file, then
 * call write() to write out the binary representation of the class file.<p>
 *
 * There are a few other utility methods as well. See Main.java for an example
 * which uses all of the public methods provided in ClassFile.
 *
 * @author Jonathan Meyer
 * @version 1.05, 8 Feb 1997
 */
public class ClassFile {

    // state info for the class being built
    String filename;
    ClassEnv class_env;
    String class_name;
    String source_name;
    Scanner scanner;

    // state info for the current method being defined
    String method_name;
    String method_signature;
    short  method_access;
    ExceptAttr except_attr;
    Catchtable catch_table;
    LocalVarTableAttr var_table;
    LineTableAttr line_table;
    CodeAttr  code;
    InnerClassAttr inner_class_attr;
    Hashtable labels;
    boolean methSynth;
    boolean methDepr;
    String methSigAttr;
    VisibilityAnnotationAttr methAnnotAttrVis;
    VisibilityAnnotationAttr methAnnotAttrInvis;
    ParameterVisibilityAnnotationAttr methParamAnnotAttrVis;
    ParameterVisibilityAnnotationAttr methParamAnnotAttrInvis;
    ElemValPair methAnnotDef;
    
    int line_label_count, line_num;
    boolean auto_number;

    // state info for lookupswitch and tableswitch instructions
    Vector switch_vec;
    int low_value;
    int high_value;

    int lastInstSize;
    Method currentMethod;
    Var currentField;

    VisibilityAnnotationAttr vis_annot_attr;
    
    public void addSootCodeAttr(String name, String value)
    {
	class_env.addCPItem(new AsciiCP(name));
	code.addSootCodeAttr(name, value);
    }
    
    public void addGenericAttrToMethod(String name, byte[] value)
    {
	if(currentMethod == null) 
	    System.err.println("Error: no field in scope to add attribute onto.");
	else {
	    class_env.addCPItem(new AsciiCP(name));
	    currentMethod.addGenericAttr(new GenericAttr(name, value));
	}
    }

    public void addGenericAttrToField(String name, byte[] value)
    {
	if(currentField == null) 
	    System.err.println("Error: no field in scope to add attribute onto.");
	else {
	    class_env.addCPItem(new AsciiCP(name));
	    currentField.addGenericAttr(new GenericAttr(name, value));
	}
    }

    public void addDeprecatedToField(){
        if (currentField == null){
            System.err.println("Error: no field in scope to add deprecated attribute onto");
        }
        else {
            //System.out.println("adding deprec to field" );
            currentField.addDeprecatedAttr(new DeprecatedAttr());
        }
    }

    public void addGenericAttrToClass(GenericAttr g)
    {
	class_env.addGenericAttr(g);
    }

    static final String BGN_METHOD = "bgnmethod:";
    static final String END_METHOD = "endmethod:";

    // number of errors reported in a file.
    int errors;

    //
    // Error reporting method
    //
    void report_error(String msg) {
        // Print out filename/linenumber/message
        System.err.print(filename + ":");
        System.err.print(scanner.line_num);
        System.err.println(": " + msg + ".");
        if (scanner.char_num >= 0) {
            System.err.println(scanner.line.toString());

            // Print out where on the line the scanner got to
            int i;
            for (i = 0; i < scanner.char_num; i++) {
                if (scanner.line.charAt(i) == '\t') {
                    System.err.print("\t");
                } else {
                    System.err.print(" ");
                }
            }
            System.err.println("^");
        }
        errors++;
    }

    //
    // called by the .source directive
    //
    void setSource(String name) {
        source_name = name;
    }

    //
    // called by the .class directive
    //
    void setClass(String name, short acc) {
        class_name = name;
        class_env.setClass(new ClassCP(name));
        class_env.setClassAccess(acc);
    }

    //
    // called by the .super directive
    //
    void setSuperClass(String name) {
        class_env.setSuperClass(new ClassCP(name));
    }

    //
    // called by the .no_super directive 
    // (for java.lang.Object only)
    void setNoSuperClass() {
        class_env.setNoSuperClass();
    }

    //
    // called by the .implements directive
    //
    void addInterface(String name) {
        class_env.addInterface(new ClassCP(name));
    }

    void addClassDeprAttr(Object res){
        if (res != null){
            class_env.setClassDepr(new DeprecatedAttr());
        }
    }
    
    void addClassSigAttr(Object res){
        if (res != null){
            class_env.setClassSigAttr(new SignatureAttr((String)res));
        }
    }
    
    void addClassAnnotAttrVisible(Object res){
        if (res != null){
            class_env.setClassAnnotAttrVis((VisibilityAnnotationAttr)res);
        }
    }
    
    void addClassAnnotAttrInvisible(Object res){
        if (res != null){
            class_env.setClassAnnotAttrInvis((VisibilityAnnotationAttr)res);
        }
    }
    
    void addField(short access, String name, String sig, Object value, Object dep_attr, Object sig_attr, Object vis_annot_attr, Object vis_annot_attr2){
        addField(access, name, sig, value, null, dep_attr, sig_attr, vis_annot_attr, vis_annot_attr2);
    }

    //
    // called by the .field directive
    //
    void addField(short access, String name,
                                String sig, Object value, String synth, Object dep_attr, Object sig_attr, Object vis_annot_attr, Object vis_annot_attr2) {


        if (value == null) {
            // defining a field which doesn't have an initial value
            if (synth == null){
                currentField = new Var(access, new AsciiCP(name), new AsciiCP(sig), null);
            }
            else {
                currentField = new Var(access, new AsciiCP(name), new AsciiCP(sig), null, new SyntheticAttr());
            }
        if(dep_attr != null){
            currentField.addDeprecatedAttr(new DeprecatedAttr());
        }
        if(sig_attr != null){
            currentField.addSignatureAttr(new SignatureAttr((String)sig_attr));
        }
        if (vis_annot_attr != null){
            VisibilityAnnotationAttr attribute = (VisibilityAnnotationAttr)vis_annot_attr;
            if(attribute.getKind().equals("RuntimeVisible"))
            	currentField.addVisibilityAnnotationAttrVis(attribute);
            else
            	currentField.addVisibilityAnnotationAttrInvis(attribute);
        }
        if (vis_annot_attr2 != null){
            VisibilityAnnotationAttr attribute = (VisibilityAnnotationAttr)vis_annot_attr2;
            if(attribute.getKind().equals("RuntimeVisible"))
            	currentField.addVisibilityAnnotationAttrVis(attribute);
            else
            	currentField.addVisibilityAnnotationAttrInvis(attribute);
        }
            
	    /*currentField =
		new Var(access, new AsciiCP(name),
			new AsciiCP(sig), null);*/
	    class_env.addField(currentField);

        } else {
            // defining a field with an initial value...

            // create a constant pool entry for the initial value
            CP cp = null;

            // correctness patch due to Brian Demsky (bdemsky@mit.edu)
            // it's strange that Soot doesn't trigger this bug.
            if (sig.equals("I")||sig.equals("Z")||sig.equals("C")||sig.equals("B")||sig.equals("S")) {
                cp = new IntegerCP(((Number)value).intValue());
            } else if (sig.equals("F")) {
                cp = new FloatCP(((Number)value).floatValue());
            } else if (sig.equals("D")) {
                cp = new DoubleCP(((Number)value).doubleValue());
            } else if (sig.equals("J")) {
                cp = new LongCP(((Number)value).longValue());
            } else if (sig.equals("Ljava/lang/String;")) {
               cp = new StringCP((String)value);
            }

            // add the field
            if (synth == null){
	    currentField = 
            new Var(access, new AsciiCP(name),
                               new AsciiCP(sig), new ConstAttr(cp));
	        }
            else {
	    currentField = 
            new Var(access, new AsciiCP(name),
                               new AsciiCP(sig), new ConstAttr(cp), new SyntheticAttr());
            }
        if(dep_attr != null){
            currentField.addDeprecatedAttr(new DeprecatedAttr());
        }
        if(sig_attr != null){
            currentField.addSignatureAttr(new SignatureAttr((String)sig_attr));
        }
	    class_env.addField(currentField);
        }
    }

    //
    // called by the .method directive to start the definition for a method
    //
    void newMethod(String name, String signature, int access) {
        // set method state variables

        labels      = new Hashtable();
        method_name = name;
        code        = null;
        except_attr = null;
        catch_table = null;
        var_table   = null;
        line_table  = null;
        line_label_count  = 0;
        method_signature = signature;
        method_access    = (short)access;
        methSynth = false;
        methDepr = false;
        methSigAttr = null;
        methAnnotAttrVis = null;
        methAnnotAttrInvis = null;
        methParamAnnotAttrVis = null;
        methParamAnnotAttrInvis = null;
        methAnnotDef = null;
    }

    //
    // called by the .end method directive to end the definition for a method
    //
    void endMethod() throws jasError {

        if (code != null) {

            plantLabel(END_METHOD);

            if (catch_table != null) {
                code.setCatchtable(catch_table);
            }
            if (var_table != null) {
                code.setLocalVarTable(var_table);
            }
            if (line_table != null) {
                code.setLineTable(line_table);
            }
	   
	    code.setLabelTable(labels);
        }
    
        if (!methSynth){
	currentMethod  =  new Method(method_access, new AsciiCP(method_name),
					    new AsciiCP(method_signature), code, except_attr);
        }
        else {
	currentMethod  =  new Method(method_access, new AsciiCP(method_name),
					    new AsciiCP(method_signature), code, except_attr, new SyntheticAttr());
        }

        if (methDepr){
            currentMethod.addDeprecatedAttr(new DeprecatedAttr());
        }
        if (methSigAttr != null){ 
            currentMethod.addSignatureAttr(new SignatureAttr(methSigAttr));
        }
        if (methAnnotAttrVis != null){ 
            currentMethod.addVisAnnotationAttr(methAnnotAttrVis);
        }
        if (methAnnotAttrInvis != null){ 
            currentMethod.addInvisAnnotationAttr(methAnnotAttrInvis);
        }
        if (methParamAnnotAttrVis != null){ 
            currentMethod.addVisParamAnnotationAttr(methParamAnnotAttrVis);
        }
        if (methParamAnnotAttrInvis != null){ 
            currentMethod.addInvisParamAnnotationAttr(methParamAnnotAttrInvis);
        }
        if (methAnnotDef != null){ 
            methAnnotDef.setNoName();
            currentMethod.addAnnotationDef(new AnnotationDefaultAttr(methAnnotDef));
        }
	class_env.addMethod( currentMethod);
	
	
	
        // clear method state variables
        code        = null;
        labels      = null;
        method_name = null;
        code        = null;
        except_attr = null;
        catch_table = null;
        line_table  = null;
        var_table   = null;
	    methSynth   = false;
        methDepr    = false;
        methSigAttr = null;
        methAnnotAttrVis = null;
        methParamAnnotAttrVis = null;
        methAnnotDef = null;


    }

    

    //
    // plant routines - these use addInsn to add instructions to the
    //                  code for the current method.
    //

    //
    // used for instructions that take no arguments
    //
    void plant(String name) throws jasError {
        InsnInfo insn = InsnInfo.get(name);
        autoNumber();
        if (insn.args.equals("")) {
	    Insn inst = new Insn(insn.opcode);


            _getCode().addInsn(inst);
        } else if (insn.name.equals("wide")) {
            // don't do anything for this one...
        } else {
            throw new jasError("Missing arguments for instruction " + name);
        }
    }

    //
    // used for iinc
    //
    void plant(String name, int v1, int v2) throws jasError {
        autoNumber();
        if (name.equals("iinc")) {
	    Insn inst = new IincInsn(v1, v2);

            _getCode().addInsn(inst);
        } else {
            throw new jasError("Bad arguments for instruction " + name);
        }
    }

    //
    // used for instructions that take an integer parameter
    //
    void plant(String name, int val) throws jasError {
        InsnInfo insn = InsnInfo.get(name);
        CodeAttr code = _getCode();
        autoNumber();
	
	Insn inst = null;
        if (insn.args.equals("i")) {
            inst = new Insn(insn.opcode, val);
        } else if (insn.args.equals("constant")) {
            inst = new Insn(insn.opcode, new IntegerCP(val));
        } else if (insn.args.equals("bigconstant")) {
            inst = new Insn(insn.opcode, new LongCP(val));
        } else {
            throw new jasError("Bad arguments for instruction " + name);
        }
	
	code.addInsn(inst);

    }

    //
    // used for ldc and other instructions that take a numeric argument
    //
    void plant(String name, Number val) throws jasError {
        InsnInfo insn = InsnInfo.get(name);
        CodeAttr code = _getCode();
        autoNumber();
	
	Insn inst = null;
        if (insn.args.equals("i") && (val instanceof Integer)) {
            inst = new Insn(insn.opcode, val.intValue());
        } else if (insn.args.equals("constant")) {
            if (val instanceof Integer || val instanceof Long) {
                inst = new Insn(insn.opcode,
                             new IntegerCP(val.intValue()));
            } else if (val instanceof Float || val instanceof Double) {
                inst = new Insn(insn.opcode,
                             new FloatCP(val.floatValue()));
            }
        } else if (insn.args.equals("bigconstant")) {
            if (val instanceof Integer || val instanceof Long) {
                inst = new Insn(insn.opcode,
                             new LongCP(val.longValue()));
            } else if (val instanceof Float || val instanceof Double) {
                inst = new Insn(insn.opcode,
                             new DoubleCP(val.doubleValue()));
            }
        } else {
            throw new jasError("Bad arguments for instruction " + name);
        }
	_getCode().addInsn(inst);

    }

    //
    // used for ldc <quoted-string>
    //
    void plantString(String name, String val) throws jasError {
        //System.out.println("plant string");
        InsnInfo insn = InsnInfo.get(name);
        autoNumber();
	Insn inst = null;
        if (insn.args.equals("constant")) {
            inst = new Insn(insn.opcode, new StringCP(val));
        } else {
            throw new jasError("Bad arguments for instruction " + name);
        }
	
	code.addInsn(inst);

    }

    //
    // used for invokeinterface and multianewarray
    //
    void plant(String name, String val, int nargs)
            throws jasError
    {
        InsnInfo insn = InsnInfo.get(name);
        CodeAttr code = _getCode();
        autoNumber();
	Insn inst = null;
        if (insn.args.equals("interface")) {
            String split[] = ScannerUtils.splitClassMethodSignature(val);
            inst = new InvokeinterfaceInsn(
                         new InterfaceCP(split[0], split[1],
                         split[2]), nargs);

        } else if (insn.args.equals("marray")) {
            inst = new MultiarrayInsn(new ClassCP(val), nargs);
        } else {
            throw new jasError("Bad arguments for instruction " + name);
        }

	code.addInsn(inst);

    }

    //
    // used for instructions that take a word as a parameter
    // (e.g. branches, newarray, invokemethod)
    //
    void plant(String name, String val) throws jasError {
        //System.out.println("plant name, val: "+val);
        InsnInfo insn = InsnInfo.get(name);
        CodeAttr code = _getCode();
        autoNumber();
	Insn inst = null;
        if (insn.args.equals("method")) {
            String split[] = ScannerUtils.splitClassMethodSignature(val);
            inst = new Insn(insn.opcode,
                         new MethodCP(split[0], split[1], split[2]));
        } 
        else if (insn.args.equals("constant")) {
            //System.out.println("constant");
            inst = new Insn(insn.opcode, new ClassCP(val));
        } else if (insn.args.equals("atype")) {
            int atype = 0;
            if (val.equals("boolean")) {
                atype = 4;
            } else if (val.equals("char")) {
                atype = 5;
            } else if (val.equals("float")) {
                atype = 6;
            } else if (val.equals("double")) {
                atype = 7;
            } else if (val.equals("byte")) {
                atype = 8;
            } else if (val.equals("short")) {
                atype = 9;
            } else if (val.equals("int")) {
                atype = 10;
            } else if (val.equals("long")) {
                atype = 11;
            } else {
                throw new jasError("Bad array type: " + name);
            }
            inst = new Insn(insn.opcode, atype);
        } else if (insn.args.equals("label")) {
            inst = new Insn(insn.opcode, getLabel(val));
        } else if (insn.args.equals("class")) {
            inst = new Insn(insn.opcode, new ClassCP(val));
        } else {
            throw new jasError("Bad arguments for instruction " + name);
        }


	code.addInsn(inst);

    }

    //
    // used for instructions that take a field and a signature as parameters
    // (e.g. getstatic, putstatic)
    //
    void plant(String name, String v1, String v2)
            throws jasError
    {
        InsnInfo info = InsnInfo.get(name);
        CodeAttr code = _getCode();
        autoNumber();
	Insn inst = null;
        if (info.args.equals("field")) {
            String split[] = ScannerUtils.splitClassField(v1);
            inst = new Insn(info.opcode,
			    new FieldCP(split[0], split[1], v2));
        }
        else {
            throw new jasError("Bad arguments for instruction " + name);
        }

	code.addInsn(inst);

    }
    
    
    /*
     * used for invokedynamic instructions 
     */
    void plant(String name, String v1, String v2, String v3)
    throws jasError
		{
		InsnInfo info = InsnInfo.get(name);
		CodeAttr code = _getCode();
		autoNumber();
		Insn inst = null;
		if (name.equals("invokedynamic")) {
				class_env.requireJava7();
				
		    	String utf8MethodName = v1;
		    	String methodSig = v2;
		    	String bsmNameAndSig = v3.substring(0,v3.indexOf("Ljava/lang/invoke/CallSite;(")+"Ljava/lang/invoke/CallSite;".length());
		    	String bsmName = bsmNameAndSig.substring(0,bsmNameAndSig.indexOf("("));
		    	String bsmClassName = bsmName.substring(0,bsmName.lastIndexOf("/"));
		    	String bsmMethodName = bsmName.substring(bsmName.lastIndexOf("/")+1);
		    	String bsmSig = bsmNameAndSig.substring(bsmNameAndSig.indexOf("("));
		    	String bsmArgs = v3.substring(v3.indexOf("Ljava/lang/invoke/CallSite;(")+"Ljava/lang/invoke/CallSite;(".length(),v3.length()-1);
		    	String bsmArgsList[] = bsmArgs.split(",");		    	
		    	
		    	CP[] argCPs;
		    	if(!bsmArgs.isEmpty()) {
					argCPs = new CP[bsmArgsList.length];		    	
			    	for (int i = 0; i < bsmArgsList.length; i++) {
			    		String sig = bsmArgsList[i].substring(1,bsmArgsList[i].indexOf(")"));
			    		String val = bsmArgsList[i].substring(bsmArgsList[i].indexOf(")")+1);
			    		CP cp = null;
			            if (sig.equals("I")||sig.equals("Z")||sig.equals("C")||sig.equals("B")||sig.equals("S")) {
			                cp = new IntegerCP(Integer.parseInt(val));
			            } else if (sig.equals("F")) {
			                cp = new FloatCP(Float.parseFloat(val));
			            } else if (sig.equals("D")) {
			                cp = new DoubleCP(Double.parseDouble(val));
			            } else if (sig.equals("J")) {
			                cp = new LongCP(Long.parseLong(val));
			            } else if (sig.equals("Ljava/lang/String;")) {
			               cp = new StringCP(val);
			            } else if (sig.equals("Ljava/lang/Class;")) {
				               cp = new ClassCP(val);
			            } else {
			            	throw new UnsupportedOperationException("static argument type not currently supported: "+sig);
			            }
			            argCPs[i] = cp;
					}
		    	} else {
		    		argCPs = new CP[0];
		    	}
		    	
		    	//maintain BootstrapMethods attribute
		    	int index = class_env.addBootstrapMethod(new MethodHandleCP(
		    			STATIC_METHOD_KIND, //bootstrap methods are always static methods
		    			bsmClassName, bsmMethodName, bsmSig),argCPs);
		    	
		    	inst = new Insn(info.opcode, new InvokeDynamicCP(bsmClassName,bsmMethodName,bsmSig,utf8MethodName,methodSig,index));
		} else {
		    throw new jasError("Bad arguments for instruction " + name);
		}
		
		code.addInsn(inst);
		
		}

    //
    // Lookupswitch instruction
    //
    void newLookupswitch() throws jasError {
        switch_vec = new Vector();
        autoNumber();
    };

    void addLookupswitch(int val, String label)
            throws jasError {
        switch_vec.addElement(new Integer(val));
        switch_vec.addElement(getLabel(label));
    };

    void endLookupswitch(String deflabel) throws jasError {
        int n = switch_vec.size() >> 1;
        int offsets[] = new int[n];
        Label labels[] = new Label[n];
        Enumeration e = switch_vec.elements();
        int i = 0;
        while (e.hasMoreElements()) {
            offsets[i] = ((Integer)e.nextElement()).intValue();
            labels[i] = (Label)e.nextElement();
            i++;
        }
        _getCode().addInsn(new LookupswitchInsn(getLabel(deflabel),
                          offsets, labels));
    }

    //
    // Tableswitch instruction
    //
    void newTableswitch(int lowval) throws jasError {
        newTableswitch(lowval, -1);
    };

    void newTableswitch(int lowval, int hival) throws jasError {
        switch_vec = new Vector();
        low_value = lowval;
        high_value = hival;
        autoNumber();
    };

    void addTableswitch(String label) throws jasError {
        switch_vec.addElement(getLabel(label));
    };

    void endTableswitch(String deflabel) throws jasError {
        int n = switch_vec.size();
        Label labels[] = new Label[n];
        Enumeration e = switch_vec.elements();
        int i = 0;
        while (e.hasMoreElements()) {
            labels[i] = (Label)e.nextElement();
            i++;
        }
        if (high_value != -1 && (high_value != low_value + n - 1)) {
            report_error("tableswitch - given incorrect value for <high>");

        }
        _getCode().addInsn(new TableswitchInsn(low_value, low_value + n - 1,
                          getLabel(deflabel), labels));

    }


    // Used by the parser to tell ClassFile what the line number
    // for the next statement is. ClassFile's autoNumber mechanism
    // uses this info.
    void setLine(int l) { line_num = l; }

    //
    // If auto_number is true, output debugging line number table
    // for Jasmin assembly instructions.
    //
    void autoNumber() throws jasError {
        if (auto_number) {
            // use the line number of the last token
            addLineInfo(line_num);
        }
    }

    //
    // Label management
    //

    //
    // gets the Label object for a label, creating it if it doesn't exist
    //
    Label getLabel(String name) throws jasError {

        // check that we are inside of a method definition
        if (method_name == null) {
            throw new jasError( "illegal use of label outside of method definition");
        }

        Label lab = (Label)labels.get(name);
        if (lab == null) {
            lab = new Label(name);
            labels.put(name, lab);
        }
        return lab;
    }

    //
    // defines a label
    //
    void plantLabel(String name) throws jasError {
        // unsure what happens if you use a label twice?
        _getCode().addInsn(getLabel(name));
    }

    //
    // used by the .var directive
    //
    void addVar(String startLab, String endLab,
                              String name, String sig, int var_num)
               throws jasError {
        if (startLab == null) {
            startLab = BGN_METHOD;
        }

        if (endLab == null) {
            endLab = END_METHOD;
        }
        Label slab, elab;
        slab = getLabel(startLab);
        elab = getLabel(endLab);

        if (var_table == null) {
            var_table = new LocalVarTableAttr();
        }

        var_table.addEntry(new LocalVarEntry(slab, elab, name, sig, var_num));
    }

    //
    // used by .line directive
    //
    void addLineInfo(int line_num) throws jasError {
        String l = "L:" + (line_label_count++);
        if (line_table == null) {
            line_table = new LineTableAttr();
        }
        plantLabel(l);
        line_table.addEntry(getLabel(l), line_num);
    }

    void addLine(int line_num) throws jasError {
        if (!auto_number) {
            addLineInfo(line_num);
        }
    }

    //
    // used by the .throws directive
    //
    void addThrow(String name) throws jasError {

        // check that we are inside of a method definition
        if (method_name == null) {
            throw new jasError( "illegal use of .throw outside of method definition");
        }

        if (except_attr == null) {
            except_attr = new ExceptAttr();
        }
        except_attr.addException(new ClassCP(name));
    }

    //
    // used by the .catch directive
    //
    void addCatch(String name, String start_lab, String end_lab,
                                String branch_lab) throws jasError {
        ClassCP class_cp;

        // check that we are inside of a method definition
        if (method_name == null) {
            throw new jasError( "illegal use of .catch outside of method definition");
        }

        if (catch_table == null) {
            catch_table = new Catchtable();
        }

        if (name.equals("all")) {
            class_cp = null;
        } else {
            class_cp = new ClassCP(name);
        }

        catch_table.addEntry(getLabel(start_lab), getLabel(end_lab),
                             getLabel(branch_lab), class_cp);
    }

    //
    // used by the .limit stack directive
    //
    void setStackSize(short v) throws jasError {
        _getCode().setStackSize(v);
    }

    //
    // used by the .limit vars directive
    //
    void setVarSize(short v) throws jasError {
        _getCode().setVarSize(v);
    }

    // --- Private stuff ---


    //
    // returns the code block, creating it if it doesn't exist
    //
    CodeAttr _getCode() throws jasError {

        // check that we are inside of a method definition
        if (method_name == null) {
            throw new jasError( "illegal use of instruction outside of method definition");
        }

        if (code == null) {
            code = new CodeAttr();
            plantLabel(BGN_METHOD);
        }

        return (code);
    }


    void addInnerClassAttr(){
    }

    void addInnerClassSpec(String inner_class_name, String outer_class_name, String inner_name, short access){
        if (inner_class_attr == null){
            inner_class_attr = new InnerClassAttr();
        }
        //inner_class_attr.addInnerClassSpec(new InnerClassSpecAttr(new ClassCP(inner_class_name), new ClassCP(outer_class_name), new AsciiCP(inner_name), access));
        inner_class_attr.addInnerClassSpec(new InnerClassSpecAttr(inner_class_name, outer_class_name, inner_name, access));
        
    }
   
    void endInnerClassAttr(){
        class_env.finishInnerClassAttr(inner_class_attr);
    }
    
    void addClassSynthAttr(){
        class_env.setClassSynth(true);
    }

    void addMethSynthAttr(){
        methSynth = true;
    }
   
    void addMethDeprAttr(){
         methDepr = true;
    }
   
    void addMethSigAttr(String s){
        methSigAttr = s;
    }
   
    void addEnclMethAttr(String cls, String meth, String sig){
        class_env.addEnclMethAttr(new EnclMethAttr(cls, meth, sig));
    }
   
    void addMethAnnotAttrVisible(Object attr){
		methAnnotAttrVis = (VisibilityAnnotationAttr)attr;
    }

    void addMethAnnotAttrInvisible(Object attr){
		methAnnotAttrInvis = (VisibilityAnnotationAttr)attr;
    }

    void addMethParamAnnotAttrVisible(Object attr){
        methParamAnnotAttrVis = (ParameterVisibilityAnnotationAttr)attr;
    }
   
    void addMethParamAnnotAttrInvisible(Object attr){
        methParamAnnotAttrInvis = (ParameterVisibilityAnnotationAttr)attr;
    }

    void addMethAnnotDefault(Object attr){
        methAnnotDef = (ElemValPair)attr;
    }
   
    
    VisibilityAnnotationAttr makeVisibilityAnnotation(Object tval, Object list){
        return new VisibilityAnnotationAttr((String)tval, (ArrayList)list);
    }
    
    ParameterVisibilityAnnotationAttr makeParameterVisibilityAnnotation(Object kind, Object list){
        return new ParameterVisibilityAnnotationAttr((String)kind+"Parameter", (ArrayList)list);
    }
    

    void endVisibilityAnnotation(){
    }

    /*void addAnnotAttrToField(){
        System.out.println("curr Field: "+currentField);
        if (currentField != null){
            currentField.addVisibilityAnnotationAttr(vis_annot_attr, class_env);
        }
    }*/
   
    AnnotationAttr currAnn = null;
  
    ArrayList makeNewAnnotAttrList(Object annot){
        ArrayList list = new ArrayList();
        list.add(annot);
        return (ArrayList)list;
    }

    ArrayList mergeNewAnnotAttr(Object list, Object elem){
        ((ArrayList)list).add(elem);
        return (ArrayList)list;
    }
    
    ArrayList makeNewAnnotationList(Object elem){
        ArrayList list = new ArrayList();
        list.add(elem);
        return (ArrayList)list;
    }

    ArrayList mergeNewAnnotation(Object list, Object elem){
        ((ArrayList)list).add(elem);
        return (ArrayList)list;
    }
    
    AnnotationAttr makeAnnotation(String type, Object elems){
        return new AnnotationAttr(type, (ArrayList)elems);
    }

    void endAnnotation(){
        if (vis_annot_attr == null){
            vis_annot_attr = new VisibilityAnnotationAttr();
        }
        vis_annot_attr.addAnnotation(currAnn);
    }

    ArrayList makeNewElemValPairList(Object elem){
        ArrayList list = new ArrayList();
        list.add(elem);
        return list;
    }

    ArrayList mergeNewElemValPair(Object list, Object elem){
        ((ArrayList)list).add(elem);
        return (ArrayList)list;
    }
    
    ElemValPair makeConstantElem(String name, char kind, Object val){
        //System.out.println("making constant elem val pair: "+val);
        ElemValPair result = null;
        //System.out.println("val: "+val);
        switch(kind){
            case 'S' : {
                       }
            case 'B' : {
                       }
            case 'Z' : {
                       }
            case 'C' : {
                       }
            case 'I' : {
                           result = new IntElemValPair(name, kind, ((Integer)val).intValue());
                           break;
                       }
            case 'J' : {
                           result = new LongElemValPair(name, kind, ((Number)val).longValue());
                           break;
                       }
            case 'F' : {
                           result = new FloatElemValPair(name, kind, ((Number)val).floatValue());
                           break;
                       }
            case 'D' : {
                           result = new DoubleElemValPair(name, kind, ((Number)val).doubleValue());
                           break;
                       }
            case 's' : {
                           result = new StringElemValPair(name, kind, (String)val);
                           break;
                       }
                
        }
        return result;
    }
   
    ElemValPair makeEnumElem(String name, char kind, String tval, String cval){
        return new EnumElemValPair(name, kind, tval, cval);
    }

    ElemValPair makeClassElem(String name, char kind, String cval){
        return new ClassElemValPair(name, kind, cval);
    }

    ElemValPair makeAnnotElem(String name, char kind, Object attr){
        return new AnnotElemValPair(name, kind, (AnnotationAttr)attr);
    }
    
    ElemValPair makeArrayElem(String name, char kind, Object list){
        ArrayElemValPair elem = new ArrayElemValPair(name, kind, (ArrayList)list);
        elem.setNoName();
        return elem;
    }
    
    void endAnnotElem(){
    }

    void endArrayElem(){
        
    }
    // PUBLIC API TO JASMIN:
	
    /** Makes a new ClassFile object, used to represent a Java class file.
      * You can then use readJasmin to read in a class file stored in
      * Jasmin assembly format.
      */

    public ClassFile() { }

    /**
      * Parses a Jasmin file, converting it internally into a binary
      * representation.
      * If something goes wrong, this throws one of
      * an IOException, or a jasError, or one of a few other exceptions.
      * I'll tie this down more formally in the next version.
      *
      * @param input is the stream containing the Jasmin assembly code for the
      *        class.
      *
      * @param name is the name of the stream. This name will be
      *        concatenated to error messages printed to System.err.
      *
      * @param numberLines true if you want Jasmin to generate line
      *        numbers automatically, based on the assembly source, or
      *        false if you are using the ".line" directive and don't
      *        want Jasmin to help out.
      */
    public void readJasmin(InputStream input, String name,
                           boolean numberLines)
                   throws IOException, Exception {
        // initialize variables for error reporting
        errors = 0;
        filename = name;
	source_name = name;

        // if numberLines is true, we output LineTableAttr's that indicate what line
        // numbers the Jasmin code falls on.
        auto_number = numberLines;

        // Parse the input file
        class_env = new ClassEnv();

	scanner = new Scanner(input);
        parser parse_obj = new parser(this, scanner);

        if (false) {
            // for debugging
            // parse_obj.debug_parse();
        } else {
            parse_obj.parse();
        }
    }

    /**
     * Returns the number of warnings/errors encountered while parsing a file.
     * 0 if everything went OK.
     */
    public int errorCount() {
        return errors;
    }

    /**
     * Returns the name of the class in the file (i.e. the string given to
     * the .class parameter in Jasmin)
     *
     */
    public String getClassName() {
        return class_name;
    }

    /**
     * Writes the binary data for the class represented by this ClassFile
     * object to the specified
     * output stream, using the Java Class File format. Throws either an
     * IOException or a jasError if something goes wrong.
     */
    public void write(OutputStream outp) throws IOException, jasError {
        class_env.setSource(source_name);
        class_env.write(new DataOutputStream(outp));
    }
};

/* --- Revision History ---------------------------------------------------
--- Jonathan Meyer, April 11 1997 
    Fixed bug where source_name was not being set in class_env.
--- Jonathan Meyer, Mar 1 1997
    Renamed "Jasmin" class "ClassFile".
--- Jonathan Meyer, Feb 8 1997
    Converted to non-static. Made a public API. Split off InsnInfo to a
    separate file.
--- Jonathan Meyer, Oct 1 1996
    Added addInterface method, used by the .implements directive.
--- Jonathan Meyer, July 25 1996
    Added setLine and line_num, to fix problem with autoNumber.
    Added report_error method.
--- Jonathan Meyer, July 24 1996 added version constant.
*/
