1 /* --- Copyright Jonathan Meyer 1997. All rights reserved. -----------------
2  > File:        jasmin/src/jasmin/ClassFile.java
3  > Purpose:     Uses a parser and the JAS package to create Java class files
4  > Author:      Jonathan Meyer, 10 July 1996
5  */
6 
7 package jasmin;
8 
9 
10 /*
11  * This class is a bit monolithic, and should probably be converted into
12  * several smaller classes. However, for this specific application,
13  * its acceptable.
14  *
15  */
16 
17 import jas.*;
18 import java.io.*;
19 import java.util.*;
20 
21 /**
22  * A ClassFile object is used to represent the binary data that makes up a
23  * Java class file - it also serves as the public
24  * API to the Jasmin assembler, though users should beware: the API
25  * is likely to change in future versions (though its such a small API
26  * at the moment that changes are likely to have only a small impact).<p>
27  *
28  * To assemble a file, you first construct a jasmin.ClassFile object, then
29  * call readJasmin() to read in the contents of a Jasmin assembly file, then
30  * call write() to write out the binary representation of the class file.<p>
31  *
32  * There are a few other utility methods as well. See Main.java for an example
33  * which uses all of the public methods provided in ClassFile.
34  *
35  * @author Jonathan Meyer
36  * @version 1.05, 8 Feb 1997
37  */
38 public class ClassFile {
39 
40     private static final boolean PARSER_DEBUG = false;
41 
42     // state info for the class being built
43     String filename;
44     ClassEnv class_env;
45     String class_name;
46     String source_name;
47     Scanner scanner;
48 
49     // state info for the current method being defined
50     ExceptAttr except_attr;
51     Catchtable catch_table;
52     LocalVarTableAttr var_table;
53     LocalVarTypeTableAttr vtype_table;
54     LineTableAttr line_table;
55     CodeAttr  code;
56     Method cur_method;
57     Var    cur_field;
58     Hashtable labels;
59     StackMap stackmap;
60     VerifyFrame verifyframe;
61     Annotation cur_annotation;
62 
63     int line_label_count, line_num, stack_map_label_count;
64     boolean auto_number, class_header;
65     Insn buffered_insn;
66 
67     // state info for lookupswitch and tableswitch instructions
68     Vector switch_vec;
69     int low_value;
70     int high_value;
71 
72 
73     static final String BGN_METHOD = "jasmin_reserved_bgnmethod:";
74     static final String END_METHOD = "jasmin_reserved_endmethod:";
75 
76     // number of errors reported in a file.
77     int errors;
78 
79     //
80     // Error reporting method
81     //
report_error(String msg)82     void report_error(String msg) { report_error(msg, false); }
83 
report_error(String msg, boolean BadIntVal)84     void report_error(String msg, boolean BadIntVal) {
85         if(Main.DEBUG)
86             throw new RuntimeException();
87 
88         errors++;
89 
90         // Print out filename/linenumber/message
91         System.err.print(filename + ":");
92         if (scanner == null)
93             System.err.println(" " + msg + ".");
94         else {
95             String dia_line;
96             int    dia_linnum, dia_charpos;
97 
98             if (BadIntVal && scanner.char_num >= 0) {
99               dia_line    = scanner.int_line;
100               dia_linnum  = scanner.int_line_num;
101               dia_charpos = scanner.int_char_num;
102             } else {
103               dia_line    = scanner.line.toString();
104               dia_linnum  = scanner.line_num;
105               dia_charpos = scanner.char_num;
106             }
107             System.err.println(dia_linnum + ": " + msg + ".");
108             if (scanner.char_num >= 0) {
109                 System.err.println(dia_line);
110 
111                 // Print out where on the line the scanner got to
112                 int i;
113                 for (i = 0; i < dia_charpos; i++) {
114                     if (dia_line.charAt(i) == '\t') {
115                         System.err.print("\t");
116                     } else {
117                         System.err.print(" ");
118                     }
119                 }
120                 System.err.println("^");
121             }
122         }
123     }
124 
125     //
126     // called by the .source directive
127     //
setSource(String name)128     void setSource(String name) {
129         source_name = name;
130     }
131 
132     //
133     // called by the .bytecode directive
134     //
setVersion(Number version)135     void setVersion(Number version) {
136         String str = version.toString();
137         int idx = str.indexOf(".");
138         if(!(version instanceof Float) || (idx == -1))
139             report_error("invalid bytecode version number : " + str);
140         class_env.setVersion(Short.parseShort(str.substring(0,idx)),
141                              Short.parseShort(str.substring(idx+1,str.length())));
142     }
143 
144     //
145     // called by the .class directive
146     //
setClass(String name, short acc)147     void setClass(String name, short acc) {
148         class_name = name;
149         class_env.setClass(new ClassCP(name));
150         class_env.setClassAccess(acc);
151         class_header = true;
152     }
153 
154     //
155     // called by the .super directive
156     //
setSuperClass(String name)157     void setSuperClass(String name) {
158         class_env.setSuperClass(new ClassCP(name));
159     }
160 
161     //
162     // called by the .implements directive
163     //
addInterface(String name)164     void addInterface(String name) {
165         class_env.addInterface(new ClassCP(name));
166     }
167 
168 
169     //
170     // called by the .debug directive
171     //
setSourceDebugExtension(String str)172     void setSourceDebugExtension(String str) {
173         class_env.setSourceDebugExtension(str);
174     }
175 
176 
177     //
178     // called by the .enclosing directive
179     //
setEnclosingMethod(String str)180     void setEnclosingMethod(String str) {
181         try {
182             if(str.indexOf("(") != -1) { // full method description
183                 String[] split = ScannerUtils.splitClassMethodSignature(str);
184                 class_env.setEnclosingMethod(split[0], split[1], split[2]);
185             }
186             else                         // just a class name
187                 class_env.setEnclosingMethod(str, null, null);
188         } catch(IllegalArgumentException e) {
189             report_error(e.toString());
190         }
191     }
192 
193     // parser diagnostics
opened_annotation(String fld)194     private static void opened_annotation(String fld) throws jasError
195     { throw new jasError("Skipped .end annotation in " + fld); }
196 
outside(String dir)197     private static void outside(String dir) throws jasError
198     { throw new jasError("illegal use of " +dir+
199                  " outside of method/field definition or class header"); }
200 
201     //
202     // called at end of jasmin-header (resolve class variables)
203     //
endHeader()204     public void endHeader() throws jasError
205       {
206         if(cur_annotation != null)
207           opened_annotation("class header");
208 
209         class_env.endHeader();  // resolve annotations
210         class_header = false;
211       }
212 
213     //
214     // called by the .signature directive
215     //
setSignature(String str)216     void setSignature(String str) throws jasError
217     {
218       SignatureAttr sig = new SignatureAttr(str);
219       if (cur_method != null) {
220         cur_method.setSignature(sig);
221       } else if(cur_field != null) {
222         cur_field.setSignature(sig);
223       } else if(class_header) {
224         class_env.setSignature(str);
225       } else {
226         outside(".signature");
227       }
228     }
229 
230     //
231     // called by the .deprecated directive
232     //
233     //
setDeprecated()234     void setDeprecated() throws jasError
235     {
236       DeprecatedAttr depr = new DeprecatedAttr();
237       if ( cur_method != null ) {
238         cur_method.setDeprecated(depr);
239       } else if(cur_field != null) {
240         cur_field.setDeprecated(depr);
241       } else if(class_header) {
242         class_env.setDeprecated(depr);
243       } else {
244         outside(".deprecated");
245       }
246     }
247 
248     //
249     // called by the .attribute directive
250     //
addGenericAttr(String name, String file)251     void addGenericAttr(String name, String file)
252         throws IOException, jasError
253     {
254       GenericAttr gattr = new GenericAttr(name, file);
255       if (cur_method != null) {
256         flushInsnBuffer();
257         if(code != null)  code.addGenericAttr(gattr);
258         else cur_method.addGenericAttr(gattr);
259       } else if(cur_field != null) {
260         cur_field.addGenericAttr(gattr);
261       } else if (class_header) {
262         class_env.addGenericAttr(gattr);
263       } else {
264         outside(".attribute");
265       }
266     }
267 
268     //
269     // called by the .inner directive
270     //
271     //
addInner(short iacc, String name, String inner, String outer)272     public void addInner(short iacc, String name, String inner, String outer)
273     { class_env.addInnerClass(iacc, name, inner, outer); }
274 
275     //
276     // procedure group for annotation description
277     //
annotation_internal()278     private static void annotation_internal() throws jasError
279     { throw new jasError("logic error in .annotation parsing"); }
280 
addAnnotation()281     void addAnnotation() throws jasError
282     {
283       if(cur_method == null) Annotation.ParserError();
284       cur_annotation = cur_method.addAnnotation();
285     }
286 
addAnnotation(boolean visible, String clname, int paramnum)287     void addAnnotation(boolean visible, String clname, int paramnum)
288         throws jasError
289     {
290       if(cur_method == null) Annotation.ParserError();
291       cur_annotation = cur_method.addAnnotation(visible, clname, paramnum);
292     }
293 
addAnnotation(boolean visible, String clname)294     void addAnnotation(boolean visible, String clname) throws jasError
295     {
296       if (cur_method != null) {
297         cur_annotation = cur_method.addAnnotation(visible, clname);
298       } else if(cur_field != null) {
299         cur_annotation = cur_field.addAnnotation(visible, clname);
300       } else if (class_header) {
301         cur_annotation = class_env.addAnnotation(visible, clname);
302       } else {
303         outside(".annotation");
304       }
305     }
306 
addAnnotationField(String name, String type, String add)307     void addAnnotationField(String name, String type, String add)
308         throws jasError
309     {
310       if(cur_annotation == null) annotation_internal();
311       cur_annotation.addField(name, type, add);
312     }
313 
addAnnotationValue(Object value)314     void addAnnotationValue(Object value) throws jasError
315     {
316       if(cur_annotation == null) annotation_internal();
317       cur_annotation.addValue(value);
318     }
319 
nestAnnotation()320     void nestAnnotation() throws jasError
321     {
322       if(cur_annotation == null) annotation_internal();
323       cur_annotation = cur_annotation.nestAnnotation();
324     }
325 
endAnnotation()326     void endAnnotation() throws jasError
327     {
328       if(cur_annotation == null)
329         throw new jasError(".end annotation without .annotation");
330       cur_annotation = cur_annotation.endAnnotation();
331     }
332 
333     // called by the .field directive to begin 'prompted' field
334     //
beginField(short access, String name, String desc, Object value)335     void beginField(short access, String name, String desc, Object value)
336         throws jasError
337     {
338       ConstAttr ca = null;
339 
340       if (value != null) {
341         CP cp;
342         // create a constant pool entry for the initial value
343         if (value instanceof Integer) {
344           cp = new IntegerCP(((Integer)value).intValue());
345         } else if (value instanceof Float) {
346           cp = new FloatCP(((Float)value).floatValue());
347         } else if (value instanceof Double) {
348           cp = new DoubleCP(((Double)value).doubleValue());
349         } else if (value instanceof Long) {
350           cp = new LongCP(((Long)value).longValue());
351         } else if (value instanceof String) {
352           cp = new StringCP((String)value);
353         } else {
354           throw new jasError("usupported value type");
355         }
356         ca = new ConstAttr(cp);
357       }
358       cur_field = new Var(access, new AsciiCP(name), new AsciiCP(desc), ca);
359     }
360 
361     //
362     // called by the .end field directive to end 'prompted' field
363     //
endField()364     void endField() throws jasError {
365       if (cur_field == null)
366         throw new jasError(".end field without .field");
367 
368       if (cur_annotation != null)
369           opened_annotation("field");
370 
371       class_env.addField(cur_field);
372       cur_field = null;
373     }
374     //
375     // called by the .field directive
376     //
addField(short access, String name, String desc, String sig, Object value)377     void addField(short access, String name, String desc,
378                                 String sig, Object value) throws jasError {
379         beginField(access, name, desc, value);
380         if (sig != null) cur_field.setSignature(new SignatureAttr(sig));
381         endField();
382     }
383 
384     //
385     // called by the .method directive to start the definition for a method
386     //
newMethod(String name, String descriptor, int access)387     void newMethod(String name, String descriptor, int access) {
388         // set method state variables
389         labels      = new Hashtable();
390         code        = null;
391         except_attr = null;
392         catch_table = null;
393         var_table   = null;
394         vtype_table = null;
395         line_table  = null;
396         line_label_count  = 0;
397         stack_map_label_count = 0;
398         stackmap = null;
399         verifyframe = null;
400         cur_method = new Method((short)access, new AsciiCP(name),
401                                 new AsciiCP(descriptor));
402     }
403 
404 
405     //
406     // called by the .end method directive to end the definition for a method
407     //
endMethod()408     void endMethod() throws jasError {
409         if (cur_method == null)
410             throw new jasError(".end method without .method");
411 
412         if (cur_annotation != null)
413             opened_annotation("method");
414 
415         if (code != null) {
416 
417             plantLabel(END_METHOD);
418             flushInsnBuffer();
419 
420             if (catch_table != null) {
421                 code.setCatchtable(catch_table);
422             }
423 
424             if (var_table != null) {
425                 code.setLocalVarTable(var_table);
426             }
427             if (vtype_table != null) {
428                 code.setLocalVarTypeTable(vtype_table);
429             }
430             if (line_table != null) {
431                 code.setLineTable(line_table);
432             }
433             if (stackmap != null) {
434                 code.setStackMap(stackmap);
435             }
436         }
437         cur_method.setCode(code, except_attr);
438         class_env.addMethod(cur_method);
439 
440         // clear method state variables
441         cur_method  = null;
442         code        = null;
443         labels      = null;
444         catch_table = null;
445         line_table  = null;
446         var_table   = null;
447         vtype_table = null;
448         stackmap    = null;
449         verifyframe = null;
450     }
451 
452 // get last stackmap locals vector
prevStack(int count)453     private Vector prevStack(int count) throws jasError {
454         Vector prev = null;
455         if(stackmap != null)
456             prev = stackmap.getLastFrame(count);
457         return prev;
458     }
459 
460 // define a new stack map frame (possible with copy previous)
beginStack(boolean copy)461     void beginStack(boolean copy) throws jasError {
462         Vector prev = null;
463         if(copy)
464             prev = prevStack(0);
465         verifyframe = new VerifyFrame(prev);
466     }
467 
468 // define a new stack map frame and copy 'n' previous
469 // (type-independet) elements
beginStack(int n)470     void beginStack(int n) throws jasError {
471         if(n <= 0)
472             throw new jasError("Invalid counter", true);
473         verifyframe = new VerifyFrame(prevStack(n));
474     }
475 
476 // define the offset of the current stack map frame
plantStackOffset(int n)477     void plantStackOffset(int n) {
478         try {
479             verifyframe.setOffset(n);
480         } catch(jasError e) {
481             report_error(e.toString());
482         }
483     }
484 
plantStackOffset(String label)485     void plantStackOffset(String label) throws jasError {
486         Label l = getLabel(label);
487         try {
488             verifyframe.setOffset(l);
489         } catch(jasError e) {
490             report_error(e.toString());
491         }
492     }
493 
494 // add a local variable item to the current stack map frame
plantStackLocals(String item, String val)495     void plantStackLocals(String item, String val) {
496         try {
497             verifyframe.addLocalsItem(item, val);
498         } catch(jasError e) {
499             report_error(e.toString());
500         }
501     }
502 
503 // add a stack item element to the current stack map frame
plantStackStack(String item, String val)504     void plantStackStack(String item, String val) {
505         try {
506             verifyframe.addStackItem(item, val);
507         } catch(jasError e) {
508             report_error(e.toString());
509         }
510     }
511 
512 // add the current stack map frame to the current stack map attribute
endStack()513     void endStack() {
514         if(!verifyframe.haveOffset()) {
515             String l = "jasmin_reserved_SM:" + (stack_map_label_count++);
516             try {
517                 plantLabel(l);
518                 verifyframe.setOffset(getLabel(l));
519             } catch(jasError e) {
520                 report_error(e.toString());
521             }
522         }
523         if(stackmap==null)
524             stackmap = new StackMap(class_env);
525 
526         stackmap.addFrame(verifyframe);
527         verifyframe = null; // PARANOYA
528     }
529 
530 
531     //
532     // plant routines - these use addInsn to add instructions to the
533     //                  code for the current method.
534     //
535 
536     //
537     // used for instructions that take no arguments
538     //
plant(String name)539     void plant(String name) throws jasError {
540         InsnInfo insn = InsnInfo.get(name);
541         autoNumber();
542         flushInsnBuffer();
543 
544         if (insn.args.equals("")) {
545             bufferInsn(new Insn(insn.opcode));
546         } else {
547             throw new jasError("Missing arguments for instruction " + name);
548         }
549     }
550 
551     //
552     // used for relative branch targets (ie $+5, $-12, ...)
553     //
plantRelativeGoto(String name, int val)554     void plantRelativeGoto(String name, int val) throws jasError {
555         InsnInfo insn = InsnInfo.get(name);
556         if (insn.args.equals("label")) {
557             bufferInsn(new Insn(insn.opcode, val, true));
558         } else {  // forward the request for "normal" instructions
559             plant(name, val);
560         }
561     }
562 
563 
564     //
565     // used for iinc
566     //
plant(String name, int v1, int v2)567     void plant(String name, int v1, int v2)
568         throws jasError
569     {
570         InsnInfo insn = InsnInfo.get(name);
571         autoNumber();
572         flushInsnBuffer();
573 
574         if (insn.args.equalsIgnoreCase("ii")) {
575             bufferInsn(new IincInsn(v1, v2, insn.args.charAt(0) == 'I'));
576         } else {
577             throw new jasError("Bad arguments for instruction " + name);
578         }
579     }
580 
581     //
582     // used for instructions that take an integer parameter
583     // (branches are part of this, the int is converted to a label)
584     //
plant(String name, int val)585     void plant(String name, int val) throws jasError {
586         InsnInfo insn = InsnInfo.get(name);
587         CodeAttr code = _getCode();
588         autoNumber();
589         flushInsnBuffer();
590 
591         if (insn.args.equalsIgnoreCase("i")) {
592             bufferInsn(new Insn(insn.opcode, val, insn.args.charAt(0) == 'I'));
593         } else if (insn.args.equals("constant")) {
594             bufferInsn(new Insn(insn.opcode, new IntegerCP(val)));
595         } else if (insn.args.equals("bigconstant")) {
596             bufferInsn(new Insn(insn.opcode, new LongCP(val)));
597         } else if (insn.args.equals("label")) {
598             plant(name, String.valueOf(val));        // the target is not signed
599                                                      // assume it is a label
600         } else {
601             throw new jasError("Bad arguments for instruction " + name);
602         }
603     }
604 
605     //
606     // used for ldc and other instructions that take a numeric argument
607     //
plant(String name, Number val)608     void plant(String name, Number val) throws jasError {
609         InsnInfo insn = InsnInfo.get(name);
610         CodeAttr code = _getCode();
611         autoNumber();
612         flushInsnBuffer();
613 
614         if (insn.args.equalsIgnoreCase("i") && (val instanceof Integer)) {
615             bufferInsn(new Insn(insn.opcode, val.intValue(), insn.args.charAt(0) == 'I'));
616         } else if (insn.args.equals("constant")) {
617             if (val instanceof Integer || val instanceof Long) {
618                 bufferInsn(new Insn(insn.opcode,
619                              new IntegerCP(val.intValue())));
620             } else if (val instanceof Float || val instanceof Double) {
621                 bufferInsn(new Insn(insn.opcode,
622                              new FloatCP(val.floatValue())));
623             }
624         } else if (insn.args.equals("bigconstant")) {
625             if (val instanceof Integer || val instanceof Long) {
626                 bufferInsn(new Insn(insn.opcode,
627                              new LongCP(val.longValue())));
628             } else if (val instanceof Float || val instanceof Double) {
629                 bufferInsn(new Insn(insn.opcode,
630                              new DoubleCP(val.doubleValue())));
631             }
632         } else {
633             throw new jasError("Bad arguments for instruction " + name);
634         }
635     }
636 
637     //
638     // used for ldc <quoted-string>
639     //
plantString(String name, String val)640     void plantString(String name, String val) throws jasError {
641         InsnInfo insn = InsnInfo.get(name);
642         autoNumber();
643         flushInsnBuffer();
644 
645         if (insn.args.equals("constant")) {
646             bufferInsn(new Insn(insn.opcode, new StringCP(val)));
647         } else {
648             throw new jasError("Bad arguments for instruction " + name);
649         }
650     }
651 
652     //
653     // used for invokeinterface and multianewarray
654     //
plant(String name, String val, int nargs)655     void plant(String name, String val, int nargs)
656             throws jasError
657     {
658         InsnInfo insn = InsnInfo.get(name);
659         CodeAttr code = _getCode();
660         autoNumber();
661         flushInsnBuffer();
662 
663         if (insn.args.equals("interface")) {
664             String split[] = ScannerUtils.splitClassMethodSignature(val);
665             bufferInsn(new InvokeinterfaceInsn(
666                          new InterfaceCP(split[0], split[1],
667                          split[2]), nargs));
668 
669         } else if (insn.args.equals("marray")) {
670             bufferInsn(new MultiarrayInsn(new ClassCP(val), nargs));
671         } else {
672             throw new jasError("Bad arguments for instruction " + name);
673         }
674     }
675 
676     //
677     // used for instructions that take a word as a parameter
678     // (e.g. branches, newarray, invokemethod)
679     //
plant(String name, String val)680     void plant(String name, String val) throws jasError {
681         InsnInfo insn = InsnInfo.get(name);
682         CodeAttr code = _getCode();
683         autoNumber();
684         flushInsnBuffer();
685 
686         if (insn.args.equals("method")) {
687             String split[] = ScannerUtils.splitClassMethodSignature(val);
688             bufferInsn(new Insn(insn.opcode,
689                          new MethodCP(split[0], split[1], split[2])));
690         } else if (insn.args.equals("constant")) {
691             bufferInsn(new Insn(insn.opcode, new ClassCP(val)));
692         } else if (insn.args.equals("atype")) {
693             int atype = 0;
694             if (val.equals("boolean")) {
695                 atype = 4;
696             } else if (val.equals("char")) {
697                 atype = 5;
698             } else if (val.equals("float")) {
699                 atype = 6;
700             } else if (val.equals("double")) {
701                 atype = 7;
702             } else if (val.equals("byte")) {
703                 atype = 8;
704             } else if (val.equals("short")) {
705                 atype = 9;
706             } else if (val.equals("int")) {
707                 atype = 10;
708             } else if (val.equals("long")) {
709                 atype = 11;
710             } else {
711                 throw new jasError("Bad array type: " + name);
712             }
713             bufferInsn(new Insn(insn.opcode, atype, false));
714         } else if (insn.args.indexOf("label")>=0) {
715             bufferInsn(new Insn(insn.opcode, getLabel(val), scanner.line_num));
716         } else if (insn.args.equals("class")) {
717             bufferInsn(new Insn(insn.opcode, new ClassCP(val)));
718         } else {
719             throw new jasError("(gloups)Bad arguments for instruction " + name);
720         }
721     }
722 
723     //
724     // used for instructions that take a field and a signature as parameters
725     // (e.g. getstatic, putstatic)
726     //
plant(String name, String v1, String v2)727     void plant(String name, String v1, String v2)
728             throws jasError
729     {
730         InsnInfo info = InsnInfo.get(name);
731         CodeAttr code = _getCode();
732         autoNumber();
733         flushInsnBuffer();
734 
735         if (info.args.equals("field")) {
736             String split[] = ScannerUtils.splitClassField(v1);
737             if(split[1] == null)
738                 throw new jasError("can't extract field from "+v1);
739             bufferInsn(new Insn(info.opcode,
740                          new FieldCP(split[0], split[1], v2)));
741         } else {
742             throw new jasError("Bad arguments for instruction " + name);
743         }
744     }
745 
746     //
747     // Lookupswitch instruction
748     //
newLookupswitch()749     void newLookupswitch() throws jasError {
750         switch_vec = new Vector();
751         autoNumber();
752     };
753 
addLookupswitch(int val, String label)754     void addLookupswitch(int val, String label)
755             throws jasError {
756         switch_vec.addElement(new Integer(val));
757         switch_vec.addElement(new LabelOrOffset(getLabel(label)));
758     };
759 
addLookupswitch(int val, int offset)760     void addLookupswitch(int val, int offset)
761             throws jasError {
762         switch_vec.addElement(new Integer(val));
763         switch_vec.addElement(new LabelOrOffset(offset));
764     };
765 
endLookupswitch(String deflabel)766     void endLookupswitch(String deflabel) throws jasError {
767         flushInsnBuffer();
768         Object[] offlabs = checkLookupswitch();
769         int[] offsets = (int[])offlabs[0];
770         LabelOrOffset[] labels = (LabelOrOffset[])offlabs[1];
771         LabelOrOffset defl = new LabelOrOffset(getLabel(deflabel));
772         bufferInsn(new LookupswitchInsn(defl, offsets, labels));
773     }
774 
endLookupswitch(int defoffset)775     void endLookupswitch(int defoffset) throws jasError {
776         flushInsnBuffer();
777         Object[] offlabs = checkLookupswitch();
778         int[] offsets = (int[])offlabs[0];
779         LabelOrOffset[] labels = (LabelOrOffset[])offlabs[1];
780         bufferInsn(new LookupswitchInsn(new LabelOrOffset(defoffset),
781                           offsets, labels));
782     }
783 
784 
785     //
786     // called by both endLookupswitch() methods
787     //
checkLookupswitch()788     private Object[] checkLookupswitch() {
789         int n = switch_vec.size() >> 1;
790         int offsets[] = new int[n];
791         LabelOrOffset labels[] = new LabelOrOffset[n];
792         Enumeration e = switch_vec.elements();
793         int i = 0;
794         while (e.hasMoreElements()) {
795             offsets[i] = ((Integer)e.nextElement()).intValue();
796             labels[i] = (LabelOrOffset)e.nextElement();
797             i++;
798         }
799         Object result[] = { offsets, labels };
800         return result;
801     }
802 
803 
804     //
805     // Tableswitch instruction
806     //
newTableswitch(int lowval)807     void newTableswitch(int lowval) throws jasError {
808         newTableswitch(lowval, -1);
809     };
810 
newTableswitch(int lowval, int hival)811     void newTableswitch(int lowval, int hival) throws jasError {
812         switch_vec = new Vector();
813         low_value = lowval;
814         high_value = hival;
815         autoNumber();
816     };
817 
addTableswitch(String label)818     void addTableswitch(String label) throws jasError {
819         switch_vec.addElement(new LabelOrOffset(getLabel(label)));
820     };
821 
addTableswitch(int offset)822     void addTableswitch(int offset) throws jasError {
823         switch_vec.addElement(new LabelOrOffset(offset));
824     };
825 
endTableswitch(String deflabel)826     void endTableswitch(String deflabel) throws jasError {
827         flushInsnBuffer();
828         LabelOrOffset labels[] = checkTableswitch();
829         bufferInsn(new TableswitchInsn(low_value,
830                            low_value+labels.length-1,
831                            new LabelOrOffset(getLabel(deflabel)),
832                            labels));
833     }
834 
endTableswitch(int defoffset)835     void endTableswitch(int defoffset) throws jasError {
836         flushInsnBuffer();
837         LabelOrOffset labels[] = checkTableswitch();
838         bufferInsn(new TableswitchInsn(low_value,
839              low_value+labels.length-1, new LabelOrOffset(defoffset), labels));
840     }
841 
842 
843     //
844     // called by both endTableswitch() methods
845     //
checkTableswitch()846     private LabelOrOffset[] checkTableswitch() {
847         int n = switch_vec.size();
848         LabelOrOffset labels[] = new LabelOrOffset[n];
849         Enumeration e = switch_vec.elements();
850         int i = 0;
851         while (e.hasMoreElements()) {
852             labels[i] = (LabelOrOffset)e.nextElement();
853             i++;
854         }
855         if (high_value != -1 && (high_value != low_value + n - 1)) {
856             report_error("tableswitch - given incorrect value for <high>");
857         }
858         return labels;
859     }
860 
861 
862     // Used by the parser to tell ClassFile what the line number
863     // for the next statement is. ClassFile's autoNumber mechanism
864     // uses this info.
setLine(int l)865     void setLine(int l) { line_num = l; }
866 
867     //
868     // If auto_number is true, output debugging line number table
869     // for Jasmin assembly instructions.
870 
871     //
autoNumber()872     void autoNumber() throws jasError {
873         if (auto_number) {
874             // use the line number of the last token
875             addLineInfo(line_num);
876         }
877     }
878 
879     //
880     // Label management
881     //
882 
883     //
884     // gets the Label object for a label, creating it if it doesn't exist
885     //
getLabel(String name)886     Label getLabel(String name) throws jasError {
887 
888         // check that we are inside of a method definition
889         if (cur_method == null) {
890             throw new jasError( "illegal use of label outside of method definition");
891         }
892 
893         Label lab = (Label)labels.get(name);
894         if (lab == null) {
895             lab = new Label(name);
896             labels.put(name, lab);
897         }
898         return lab;
899     }
900 
901     //
902     // defines a label
903     //
plantLabel(String name)904     void plantLabel(String name) throws jasError {
905     //    System.out.println("planting label "+name);
906         try {
907             Integer.parseInt(name);
908           // the label is a number, we must add it *before* the buffered insn
909           // this is the reason to use a buffer instructions
910             _getCode().addInsn(getLabel(name));
911             flushInsnBuffer();
912         } catch(NumberFormatException e) {
913           // traditional label (word), add it *after* the buffered insn
914             flushInsnBuffer();
915             bufferInsn(getLabel(name));
916         }
917     }
918 
919 // if there is an instruction in the buffer, add it to the code
920 // and put the new instruction in the buffer
bufferInsn(Insn i)921     void bufferInsn(Insn i) throws jasError{
922         if(i == null)
923             throw new jasError("trying to buffer a null instruction");
924         if(buffered_insn != null)
925             flushInsnBuffer();
926         buffered_insn = i;
927     }
928 
929 // if the buffer is not empty, add the instruction to the code array
flushInsnBuffer()930     void flushInsnBuffer() throws jasError {
931         if(buffered_insn != null) {
932             _getCode().addInsn(buffered_insn);
933             buffered_insn = null;
934         }
935     }
936 
937     //
938     // used by the .var directive
939     //
addVar(String startLab, String endLab, String name, String desc, String sign, int var_num)940     void addVar(String startLab, String endLab,
941                 String name, String desc, String sign, int var_num)
942                throws jasError {
943         if (startLab == null) {
944             startLab = BGN_METHOD;
945         }
946 
947         if (endLab == null) {
948             endLab = END_METHOD;
949         }
950         Label slab, elab;
951         slab = getLabel(startLab);
952         elab = getLabel(endLab);
953 
954         if (var_table == null) {
955             var_table = new LocalVarTableAttr();
956         }
957 
958         var_table.addEntry(new LocalVarEntry(slab, elab, name, desc, var_num));
959 
960         if (sign != null) {
961             if (vtype_table == null) {
962               vtype_table = new LocalVarTypeTableAttr();
963             }
964 
965             vtype_table.addEntry(new LocalVarEntry(slab, elab, name, sign, var_num));
966         }
967     }
968 
addVar(int startOffset, int endOffset, String name, String desc, String sign, int var_num)969     void addVar(int startOffset, int endOffset, String name,
970                   String desc, String sign, int var_num) throws jasError {
971         if (var_table == null) {
972             var_table = new LocalVarTableAttr();
973         }
974         var_table.addEntry(new LocalVarEntry(startOffset, endOffset,
975                            name, desc, var_num));
976 
977         if (sign != null) {
978             if (vtype_table == null) {
979               vtype_table = new LocalVarTypeTableAttr();
980             }
981 
982             vtype_table.addEntry(new LocalVarEntry(startOffset, endOffset,
983                                  name, sign, var_num));
984         }
985     }
986 
987     //
988     // used by .line directive
989     //
addLineInfo(int line_num)990     void addLineInfo(int line_num) throws jasError {
991         String l = "jasmin_reserved_L:" + (line_label_count++);
992         if (line_table == null) {
993             line_table = new LineTableAttr();
994         }
995         plantLabel(l);
996         line_table.addEntry(getLabel(l), line_num);
997     }
998 
addLine(int line_num)999     void addLine(int line_num) throws jasError {
1000         if (!auto_number) {
1001             addLineInfo(line_num);
1002         }
1003     }
1004 
1005     //
1006     // used by the .throws directive
1007     //
addThrow(String name)1008     void addThrow(String name) throws jasError {
1009 
1010         // check that we are inside of a method definition
1011         if (cur_method == null) {
1012             throw new jasError( "illegal use of .throw outside of method definition");
1013         }
1014 
1015         if (except_attr == null) {
1016             except_attr = new ExceptAttr();
1017         }
1018         except_attr.addException(new ClassCP(name));
1019     }
1020 
1021     //
1022     // used by the .catch directive
1023     //
addCatch(String name, String start_lab, String end_lab, String branch_lab)1024     void addCatch(String name, String start_lab, String end_lab,
1025                                 String branch_lab) throws jasError {
1026         ClassCP class_cp = checkCatch(name);
1027 
1028         catch_table.addEntry(getLabel(start_lab), getLabel(end_lab),
1029                              getLabel(branch_lab), class_cp);
1030     }
1031 
1032 
addCatch(String name, int start_off, int end_off, int branch_off)1033     void addCatch(String name, int start_off, int end_off,
1034                                 int branch_off) throws jasError {
1035         ClassCP class_cp = checkCatch(name);
1036 
1037         catch_table.addEntry(start_off, end_off, branch_off, class_cp);
1038     }
1039 
checkLimit(int v)1040     short checkLimit(int v) throws jasError {
1041         if(v < 0 || v > 65535)
1042             throw new jasError("Illegal limit value", true);
1043         return (short)v;
1044     }
1045 
1046     //
1047     // used by the .limit stack directive
1048     //
setStackSize(int v)1049     void setStackSize(int v) throws jasError {
1050         _getCode().setStackSize(checkLimit(v));
1051     }
1052 
1053     //
1054     // used by the .limit vars directive
1055     //
setVarSize(int v)1056     void setVarSize(int v) throws jasError {
1057         _getCode().setVarSize(checkLimit(v));
1058     }
1059 
1060     // --- Private stuff ---
1061 
1062 
1063     //
1064     // verifications made by both addCatch() methods
1065     //
checkCatch(String name)1066     private ClassCP checkCatch(String name) throws jasError {
1067         ClassCP class_cp;
1068         // check that we are inside of a method definition
1069         if (cur_method == null) {
1070             throw new jasError( "illegal use of .catch outside of method definition");
1071         }
1072 
1073         if (catch_table == null) {
1074             catch_table = new Catchtable();
1075         }
1076 
1077         if (name.equals("all")) {
1078             class_cp = null;
1079         } else {
1080             class_cp = new ClassCP(name);
1081         }
1082         return class_cp;
1083     }
1084 
1085 
1086     //
1087     // returns the code block, creating it if it doesn't exist
1088     //
_getCode()1089     CodeAttr _getCode() throws jasError {
1090 
1091         // check that we are inside of a method definition
1092         if (cur_method == null) {
1093             throw new jasError("illegal use of instruction outside of method definition");
1094         }
1095 
1096         if (code == null) {
1097             code = new CodeAttr();
1098             plantLabel(BGN_METHOD);
1099         }
1100 
1101         return (code);
1102     }
1103 
1104 
1105     // PUBLIC API TO JASMIN:
1106 
1107     /** Makes a new ClassFile object, used to represent a Java class file.
1108       * You can then use readJasmin to read in a class file stored in
1109       * Jasmin assembly format.
1110       */
1111 
ClassFile()1112     public ClassFile() {}
1113 
1114     /**
1115       * Parses a Jasmin file, converting it internally into a binary
1116       * representation.
1117       * If something goes wrong, this throws one of
1118       * an IOException, or a jasError, or one of a few other exceptions.
1119       * I'll tie this down more formally in the next version.
1120       *
1121       * @param input is the stream containing the Jasmin assembly code for the
1122       *        class.
1123       *
1124       * @param name is the name of the stream. This name will be
1125       *        concatenated to error messages printed to System.err.
1126       *
1127       * @param numberLines true if you want Jasmin to generate line
1128       *        numbers automatically, based on the assembly source, or
1129       *        false if you are using the ".line" directive and don't
1130       *        want Jasmin to help out.
1131       */
readJasmin(Reader input, String name, boolean numberLines)1132     public void readJasmin(Reader input, String name,
1133                            boolean numberLines)
1134                    throws IOException, Exception {
1135         // initialize variables for error reporting
1136         errors = 0;
1137         filename = name;
1138         source_name = name;
1139 
1140         //initialize local-frame variables
1141         cur_method = null;
1142         cur_field = null;
1143         cur_annotation = null;
1144         class_header = false;
1145 
1146         // if numberLines is true, we output LineTableAttr's that indicate what line
1147         // numbers the Jasmin code falls on.
1148         auto_number = numberLines;
1149 
1150         // Parse the input file
1151         class_env = new ClassEnv();
1152 
1153         scanner = new Scanner(input);
1154         parser parse_obj = new parser(this, scanner);
1155 
1156         if (PARSER_DEBUG) {
1157             // for debugging
1158             parse_obj.debug_parse();
1159         } else {
1160             parse_obj.parse();
1161         }
1162     }
1163 
1164     /**
1165      * Returns the number of warnings/errors encountered while parsing a file.
1166      * 0 if everything went OK.
1167      */
errorCount()1168     public int errorCount() {
1169         return errors;
1170     }
1171 
1172     /**
1173      * Returns the name of the class in the file (i.e. the string given to
1174      * the .class parameter in Jasmin)
1175      *
1176      */
getClassName()1177     public String getClassName() {
1178         return class_name;
1179     }
1180 
1181     /**
1182      * Writes the binary data for the class represented by this ClassFile
1183      * object to the specified
1184      * output stream, using the Java Class File format. Throws either an
1185      * IOException or a jasError if something goes wrong.
1186      */
write(OutputStream outp)1187     public void write(OutputStream outp) throws IOException, jasError {
1188         class_env.setSource(source_name);
1189         class_env.write(new DataOutputStream(outp));
1190     }
1191 };
1192 
1193 /* --- Revision History ---------------------------------------------------
1194 --- Iouri Kharon, Aug 10 2006
1195     Added 'Wide' prefix support to some instructions
1196 
1197 --- Iouri Kharon, Feb 17 2006
1198     Added extended syntax for .stack
1199 
1200 --- Iouri Kharon, Dec 30 2005
1201     Added multiline .field, directive .inner and .anotation
1202 
1203 --- Iouri Kharon, Dec 20 2005
1204     Added LocalVariableTypeTable support in addVar methods (called by .var)
1205 
1206 --- Daniel Reynaud, Oct 22 2005
1207     Added setSourceDebugExtension() method (called by .debug)
1208     Added setEnclosingMethod() method (called by .enclosing)
1209 
1210 --- Daniel Reynaud, Oct 19 2005
1211     Added setVersion() method (called by .bytecode)
1212     Changed BGN_METHOD, END_METHOD and line number label to avoid collision
1213 
1214 --- Jonathan Meyer, April 11 1997
1215     Fixed bug where source_name was not being set in class_env.
1216 
1217 --- Jonathan Meyer, Mar 1 1997
1218     Renamed "Jasmin" class "ClassFile".
1219 
1220 --- Jonathan Meyer, Feb 8 1997
1221     Converted to non-static. Made a public API. Split off InsnInfo to a
1222     separate file.
1223 
1224 --- Jonathan Meyer, Oct 1 1996
1225     Added addInterface method, used by the .implements directive.
1226 
1227 --- Jonathan Meyer, July 25 1996
1228     Added setLine and line_num, to fix problem with autoNumber.
1229     Added report_error method.
1230 
1231 --- Jonathan Meyer, July 24 1996 added version constant.
1232 */
1233