001    package AST;
002    
003    import java.util.HashSet;
004    import java.io.File;
005    import java.util.*;
006    import beaver.*;
007    import java.util.ArrayList;
008    import java.util.zip.*;
009    import java.io.*;
010    import java.io.FileNotFoundException;
011    import java.util.Collection;
012    /**
013      * @ast class
014     * 
015     */
016    public class BytecodeParser extends java.lang.Object implements Flags, BytecodeReader {
017    
018        public CompilationUnit read(InputStream is, String fullName, Program p) throws FileNotFoundException, IOException {
019          return new BytecodeParser(is, fullName).parse(null, null, p);
020        }
021    
022    
023    
024        public static final boolean VERBOSE = false;
025    
026    
027    
028        private DataInputStream is;
029    
030    
031        public CONSTANT_Class_Info classInfo;
032    
033    
034        public String outerClassName;
035    
036    
037        public String name;
038    
039    
040    
041        public BytecodeParser(byte[] buffer, int size, String name) {
042          //this.is = new DataInputStream(new DummyInputStream(buffer, size));
043          this.is = new DataInputStream(new ByteArrayInputStream(buffer, 0, size));
044          this.name = name;
045        }
046    
047    
048        public BytecodeParser(InputStream in, String name) {
049          //this.is = new DataInputStream(new DummyInputStream(buffer, size));
050          this.is = new DataInputStream(new DummyInputStream(in));
051          this.name = name;
052        }
053    
054    
055    
056        public BytecodeParser() {
057          this("");
058        }
059    
060    
061        public BytecodeParser(String name) {
062          if (!name.endsWith(".class")) {
063            //name = name.replaceAll("\\.", "/") + ".class";
064            name = name.replace('.', '/') + ".class";
065          }
066          this.name = name;
067        }
068    
069    
070    
071        private static class DummyInputStream extends InputStream {
072          byte[] bytes;
073          int pos;
074          int size;
075          public DummyInputStream(byte[] buffer, int size) {
076            bytes = buffer;
077            this.size = size;
078          }
079          public DummyInputStream(InputStream is) {
080            bytes = new byte[1024];
081            int index = 0;
082            size = 1024;
083            try {
084              int status;
085              do {
086                status = is.read(bytes, index, size - index);
087                if(status != -1) {
088                  index += status;
089                  if(index == size) {
090                    byte[] newBytes = new byte[size*2];
091                    System.arraycopy(bytes, 0, newBytes, 0, size);
092                    bytes = newBytes;
093                    size *= 2;
094                  }
095                }
096              } while (status != -1);
097            } catch (IOException e) {
098              System.err.println("Something went wrong trying to read " + is);
099              //System.exit(1);
100            }
101            size = index;
102            pos = 0;
103          }
104    
105          public int available() {
106            return size - pos;
107          }
108    
109          public void close() {
110          }
111    
112          public void mark(int readlimit) {
113          }
114    
115          public boolean markSupported() {
116            return false;
117          }
118    
119          public int read(byte[] b) {
120            int actualLength = Math.min(b.length, size-pos);
121            System.arraycopy(bytes, pos, b, 0, actualLength);
122            pos += actualLength;
123            return actualLength;
124          }
125    
126          public int read(byte[] b, int offset, int length) {
127            int actualLength = Math.min(length, size-pos);
128            System.arraycopy(bytes, pos, b, offset, actualLength);
129            pos += actualLength;
130            return actualLength;
131          }
132    
133          public void reset() {
134          }
135    
136          public long skip(long n) {
137            if(size == pos)
138              return -1;
139            long skipSize = Math.min(n, size-pos);
140            pos += skipSize;
141            return skipSize;
142          }
143    
144          public int read() throws IOException {
145            if(pos < size) {
146              int i = bytes[pos++];
147              if(i < 0)
148                i = 256 + i;
149              return i;
150            }
151            return -1;
152          }
153        }
154    
155    
156    
157        public int next() {
158          try {
159            return is.read();
160          } catch (IOException e) {
161            System.exit(1);
162          }
163          return -1;
164        }
165    
166    
167    
168        public int u1() {
169          try {
170            return is.readUnsignedByte();
171          } catch (IOException e) {
172            System.exit(1);
173          }
174          return -1;
175        }
176    
177    
178    
179        public int u2() {
180          try {
181            return is.readUnsignedShort();
182          } catch (IOException e) {
183            System.exit(1);
184          }
185          return -1;
186        }
187    
188    
189    
190        public int u4() {
191          try {
192            return is.readInt();
193          } catch (IOException e) {
194            System.exit(1);
195          }
196          return -1;
197        }
198    
199    
200    
201        public int readInt() {
202          try {
203            return is.readInt();
204          } catch (IOException e) {
205            System.exit(1);
206          }
207          return -1;
208        }
209    
210    
211    
212        public float readFloat() {
213          try {
214            return is.readFloat();
215          } catch (IOException e) {
216            System.exit(1);
217          }
218          return -1;
219        }
220    
221    
222    
223        public long readLong() {
224          try {
225            return is.readLong();
226          } catch (IOException e) {
227            System.exit(1);
228          }
229          return -1;
230        }
231    
232    
233    
234        public double readDouble() {
235          try {
236            return is.readDouble();
237          } catch (IOException e) {
238            System.exit(1);
239          }
240          return -1;
241        }
242    
243    
244    
245        public String readUTF() {
246          try {
247            return is.readUTF();
248          } catch (IOException e) {
249            System.exit(1);
250          }
251          return "";
252        }
253    
254    
255    
256        public void skip(int length) {
257          try {
258            is.skip(length);
259          } catch (IOException e) {
260            System.exit(1);
261          }
262        }
263    
264    
265    
266        public void error(String s) {
267          throw new RuntimeException(s);
268        }
269    
270    
271    
272        public void print(String s) {
273          //System.out.print(s);
274        }
275    
276    
277    
278        public void println(String s) {
279          print(s + "\n");
280        }
281    
282    
283    
284        public void println() {
285          print("\n");
286        }
287    
288    
289    
290         public CompilationUnit parse(TypeDecl outerTypeDecl, String outerClassName, Program classPath, boolean isInner) 
291            throws FileNotFoundException, IOException {
292              isInnerClass = isInner;
293              return parse(outerTypeDecl, outerClassName, classPath);
294        }
295    
296    
297    
298        public CompilationUnit parse(TypeDecl outerTypeDecl, String outerClassName, Program program) 
299          throws FileNotFoundException, IOException {
300            //InputStream file = ClassLoader.getSystemResourceAsStream(name);
301    
302            if(is == null) {
303              FileInputStream file = new FileInputStream(name);
304              //System.err.println("/home/torbjorn/sandbox/jdk/" + name);
305    
306              if(file == null) {
307                throw new FileNotFoundException(name);
308              }
309    
310              // // Does not work without DummyInputStream. Why?
311              //is = new DataInputStream(new DummyInputStream(new BufferedInputStream(file)));
312              is = new DataInputStream(new BufferedInputStream(file));
313            }
314            if(BytecodeParser.VERBOSE) 
315              println("Parsing byte codes in " + name);
316    
317            this.outerClassName = outerClassName;
318            parseMagic();
319            parseMinor();
320            parseMajor();
321            parseConstantPool();
322            CompilationUnit cu = new CompilationUnit();
323            TypeDecl typeDecl = parseTypeDecl();
324            cu.setPackageDecl(classInfo.packageDecl());
325            cu.addTypeDecl(typeDecl);
326            parseFields(typeDecl);
327            parseMethods(typeDecl);
328            //parse attributes, and if we have an inner class, then execute the branch...
329            if(new Attributes.TypeAttributes(this, typeDecl, outerTypeDecl, program).isInnerClass()) {        
330                //this is a workaround for the fact that JastAdd stores inner classes as members of
331                //their outer classes, even for inner classes that come from bytecode;
332                //to avoid having inner classes show up as top-level classes, we remove them here
333                //from the compilation unit again...
334                
335                //first add the cu to the program, so that getTypeDecls() won't fail 
336               program.addCompilationUnit(cu);
337               //then clear the cu
338               for(int i=0;i<cu.getTypeDecls().getNumChild();i++) {
339                       cu.getTypeDecls().removeChild(i);
340               }
341               //and remove the cu from the program again
342               program.getCompilationUnits().removeChild(program.getCompilationUnits().getIndexOfChild(cu));
343            }
344            
345            is.close();
346            is = null;
347            return cu;
348          }
349    
350    
351    
352        public void parseMagic() {
353          if (next() != 0xca || next() != 0xfe || next() != 0xba || next() != 0xbe)
354            error("magic error");
355        }
356    
357    
358    
359        public void parseMinor() {
360          int low = u1();
361          int high = u1();
362          if(BytecodeParser.VERBOSE) 
363            println("Minor: " + high + "." + low);
364        }
365    
366    
367    
368        public void parseMajor() {
369          int low = u1();
370          int high = u1();
371          if(BytecodeParser.VERBOSE) 
372            println("Major: " + high + "." + low);
373        }
374    
375    
376    
377        public boolean isInnerClass = false;
378    
379    
380    
381        public TypeDecl parseTypeDecl() {
382          int flags = u2();
383          Modifiers modifiers = modifiers(flags & 0xfddf);
384          if((flags & (ACC_INTERFACE | ACC_ENUM)) == ACC_ENUM) {
385            // Modifiers <ID:String> /[SuperClassAccess:Access]/ Implements:Access* BodyDecl*;
386            EnumDecl decl = new EnumDecl();
387            decl.setModifiers(modifiers);
388            decl.setID(parseThisClass());
389            Access superClass = parseSuperClass();
390            decl.setImplementsList(parseInterfaces(new List()));
391            return decl;
392          }
393          else if ((flags & ACC_INTERFACE) == 0) {
394            ClassDecl decl = new ClassDecl();
395            decl.setModifiers(modifiers);
396            decl.setID(parseThisClass());
397            Access superClass = parseSuperClass();
398            decl.setSuperClassAccessOpt(superClass == null ? new Opt()
399                : new Opt(superClass));
400            decl.setImplementsList(parseInterfaces(new List()));
401            return decl;
402          } else if((flags & ACC_ANNOTATION) == 0) {
403            InterfaceDecl decl = new InterfaceDecl();
404            decl.setModifiers(modifiers);
405            decl.setID(parseThisClass());
406            Access superClass = parseSuperClass();
407            decl.setSuperInterfaceIdList(
408                parseInterfaces(
409                  superClass == null ? new List()
410                  : new List().add(superClass)));
411            return decl;
412          } else {
413            AnnotationDecl decl = new AnnotationDecl();
414            decl.setModifiers(modifiers);
415            decl.setID(parseThisClass());
416            Access superClass = parseSuperClass();
417            parseInterfaces(
418                superClass == null ? new List()
419                : new List().add(superClass));
420            return decl;
421          }
422        }
423    
424    
425    
426    
427        public String parseThisClass() {
428          int index = u2();
429          CONSTANT_Class_Info info = (CONSTANT_Class_Info) constantPool[index];
430          classInfo = info;
431          return info.simpleName();
432        }
433    
434    
435    
436        public Access parseSuperClass() {
437          int index = u2();
438          if (index == 0)
439            return null;
440          CONSTANT_Class_Info info = (CONSTANT_Class_Info) constantPool[index];
441          return info.access();
442        }
443    
444    
445    
446        public List parseInterfaces(List list) {
447          int count = u2();
448          for (int i = 0; i < count; i++) {
449            CONSTANT_Class_Info info = (CONSTANT_Class_Info) constantPool[u2()];
450            list.add(info.access());
451          }
452          return list;
453        }
454    
455    
456    
457    
458        public Access fromClassName(String s) {
459          // Sample ClassName: a/b/c$d$e
460          // the package name ends at the last '/'
461          // after that follows a list of type names separated by '$'
462          // all except the first are nested types
463    
464          String packageName = "";
465          int index = s.lastIndexOf('/');
466          if(index != -1)
467            packageName = s.substring(0, index).replace('/', '.');
468          String typeName = s.substring(index + 1, s.length());
469          if(typeName.indexOf('$') != -1)
470            return new BytecodeTypeAccess(packageName, typeName);
471          else
472            return new TypeAccess(packageName, typeName);
473        }
474    
475    
476    
477        public static Modifiers modifiers(int flags) {
478          Modifiers m = new Modifiers();
479          if ((flags & 0x0001) != 0)
480            m.addModifier(new Modifier("public"));
481          if ((flags & 0x0002) != 0)
482            m.addModifier(new Modifier("private"));
483          if ((flags & 0x0004) != 0)
484            m.addModifier(new Modifier("protected"));
485          if ((flags & 0x0008) != 0)
486            m.addModifier(new Modifier("static"));
487          if ((flags & 0x0010) != 0)
488            m.addModifier(new Modifier("final"));
489          if ((flags & 0x0020) != 0)
490            m.addModifier(new Modifier("synchronized"));
491          if ((flags & 0x0040) != 0)
492            m.addModifier(new Modifier("volatile"));
493          if ((flags & 0x0080) != 0)
494            m.addModifier(new Modifier("transient"));
495          if ((flags & 0x0100) != 0)
496            m.addModifier(new Modifier("native"));
497          if ((flags & 0x0400) != 0)
498            m.addModifier(new Modifier("abstract"));
499          if ((flags & 0x0800) != 0)
500            m.addModifier(new Modifier("strictfp"));
501          return m;
502        }
503    
504    
505    
506        public void parseFields(TypeDecl typeDecl) {
507          int count = u2();
508          if(BytecodeParser.VERBOSE) 
509            println("Fields (" + count + "):");
510          for (int i = 0; i < count; i++) {
511            if(BytecodeParser.VERBOSE) 
512              print(" Field nbr " + i + " ");
513            FieldInfo fieldInfo = new FieldInfo(this);
514            if(!fieldInfo.isSynthetic())
515              typeDecl.addBodyDecl(fieldInfo.bodyDecl());
516          }
517        }
518    
519    
520    
521    
522        public void parseMethods(TypeDecl typeDecl) {
523          int count = u2();
524          if(BytecodeParser.VERBOSE) 
525            println("Methods (" + count + "):");
526          for (int i = 0; i < count; i++) {
527            if(BytecodeParser.VERBOSE) 
528              print("  Method nbr " + i + " ");
529            MethodInfo info = new MethodInfo(this);
530            if(!info.isSynthetic() && !info.name.equals("<clinit>")) {
531              typeDecl.addBodyDecl(info.bodyDecl());
532            }
533          }
534        }
535    
536    
537    
538    
539        public CONSTANT_Info[] constantPool = null;
540    
541    
542    
543        private void checkLengthAndNull(int index) {
544          if(index >= constantPool.length) {
545            throw new Error("Trying to access element " + index  + " in constant pool of length " + constantPool.length);
546          }
547          if(constantPool[index] == null)
548            throw new Error("Unexpected null element in constant pool at index " + index);
549        }
550    
551    
552        public boolean validConstantPoolIndex(int index) {
553          return index < constantPool.length && constantPool[index] != null;
554        }
555    
556    
557        public CONSTANT_Info getCONSTANT_Info(int index) {
558          checkLengthAndNull(index);
559          return constantPool[index];
560        }
561    
562    
563        public CONSTANT_Utf8_Info getCONSTANT_Utf8_Info(int index) {
564          checkLengthAndNull(index);
565          CONSTANT_Info info = constantPool[index];
566          if(!(info instanceof CONSTANT_Utf8_Info))
567            throw new Error("Expected CONSTANT_Utf8_info at " + index + " in constant pool but found " + info.getClass().getName());
568          return (CONSTANT_Utf8_Info)info;
569        }
570    
571    
572        public CONSTANT_Class_Info getCONSTANT_Class_Info(int index) {
573          checkLengthAndNull(index);
574          CONSTANT_Info info = constantPool[index];
575          if(!(info instanceof CONSTANT_Class_Info))
576            throw new Error("Expected CONSTANT_Class_info at " + index + " in constant pool but found " + info.getClass().getName());
577          return (CONSTANT_Class_Info)info;
578        }
579    
580    
581    
582        public void parseConstantPool() {
583          int count = u2();
584          if(BytecodeParser.VERBOSE) 
585            println("constant_pool_count: " + count);
586          constantPool = new CONSTANT_Info[count + 1];
587          for (int i = 1; i < count; i++) {
588            parseEntry(i);
589            if (constantPool[i] instanceof CONSTANT_Long_Info
590                || constantPool[i] instanceof CONSTANT_Double_Info)
591              i++;
592          }
593    
594          //println("ConstantPool: ");
595          //for(int i = 1; i < count; i++) {
596          //  println(i + ", " + constantPool[i]);
597          //}
598    
599        }
600    
601    
602    
603        private static final int CONSTANT_Class = 7;
604    
605    
606        private static final int CONSTANT_FieldRef = 9;
607    
608    
609        private static final int CONSTANT_MethodRef = 10;
610    
611    
612        private static final int CONSTANT_InterfaceMethodRef = 11;
613    
614    
615        private static final int CONSTANT_String = 8;
616    
617    
618        private static final int CONSTANT_Integer = 3;
619    
620    
621        private static final int CONSTANT_Float = 4;
622    
623    
624        private static final int CONSTANT_Long = 5;
625    
626    
627        private static final int CONSTANT_Double = 6;
628    
629    
630        private static final int CONSTANT_NameAndType = 12;
631    
632    
633        private static final int CONSTANT_Utf8 = 1;
634    
635    
636    
637        public void parseEntry(int i) {
638          int tag = u1();
639          switch (tag) {
640            case CONSTANT_Class:
641              constantPool[i] = new CONSTANT_Class_Info(this);
642              break;
643            case CONSTANT_FieldRef:
644              constantPool[i] = new CONSTANT_Fieldref_Info(this);
645              break;
646            case CONSTANT_MethodRef:
647              constantPool[i] = new CONSTANT_Methodref_Info(this);
648              break;
649            case CONSTANT_InterfaceMethodRef:
650              constantPool[i] = new CONSTANT_InterfaceMethodref_Info(this);
651              break;
652            case CONSTANT_String:
653              constantPool[i] = new CONSTANT_String_Info(this);
654              break;
655            case CONSTANT_Integer:
656              constantPool[i] = new CONSTANT_Integer_Info(this);
657              break;
658            case CONSTANT_Float:
659              constantPool[i] = new CONSTANT_Float_Info(this);
660              break;
661            case CONSTANT_Long:
662              constantPool[i] = new CONSTANT_Long_Info(this);
663              break;
664            case CONSTANT_Double:
665              constantPool[i] = new CONSTANT_Double_Info(this);
666              break;
667            case CONSTANT_NameAndType:
668              constantPool[i] = new CONSTANT_NameAndType_Info(this);
669              break;
670            case CONSTANT_Utf8:
671              constantPool[i] = new CONSTANT_Utf8_Info(this);
672              break;
673            default:
674              println("Unknown entry: " + tag);
675          }
676        }
677    
678    
679    }