/* * The JastAdd Extensible Java Compiler (http://jastadd.org) is covered * by the modified BSD License. You should have received a copy of the * modified BSD license with this compiler. * * Copyright (c) 2005-2008, Torbjorn Ekman * All rights reserved. */ aspect BytecodeReader { public class BytecodeParser implements Flags, BytecodeReader { public CompilationUnit read(InputStream is, String fullName, Program p) throws FileNotFoundException, IOException { return new BytecodeParser(is, fullName).parse(null, null, p); } public static final boolean VERBOSE = false; private DataInputStream is; public CONSTANT_Class_Info classInfo; public CONSTANT_Class_Info outerClassInfo; public String name; public BytecodeParser(byte[] buffer, int size, String name) { //this.is = new DataInputStream(new DummyInputStream(buffer, size)); this.is = new DataInputStream(new ByteArrayInputStream(buffer, 0, size)); this.name = name; } public BytecodeParser(InputStream in, String name) { //this.is = new DataInputStream(new DummyInputStream(buffer, size)); this.is = new DataInputStream(new DummyInputStream(in)); this.name = name; } public BytecodeParser() { this(""); } public BytecodeParser(String name) { if (!name.endsWith(".class")) { //name = name.replaceAll("\\.", "/") + ".class"; name = name.replace('.', '/') + ".class"; } this.name = name; } private static class DummyInputStream extends InputStream { byte[] bytes; int pos; int size; public DummyInputStream(byte[] buffer, int size) { bytes = buffer; this.size = size; } public DummyInputStream(InputStream is) { bytes = new byte[1024]; int index = 0; size = 1024; try { int status; do { status = is.read(bytes, index, size - index); if(status != -1) { index += status; if(index == size) { byte[] newBytes = new byte[size*2]; System.arraycopy(bytes, 0, newBytes, 0, size); bytes = newBytes; size *= 2; } } } while (status != -1); } catch (IOException e) { System.err.println("Something went wrong trying to read " + is); //System.exit(1); } size = index; pos = 0; } public int available() { return size - pos; } public void close() { } public void mark(int readlimit) { } public boolean markSupported() { return false; } public int read(byte[] b) { int actualLength = Math.min(b.length, size-pos); System.arraycopy(bytes, pos, b, 0, actualLength); pos += actualLength; return actualLength; } public int read(byte[] b, int offset, int length) { int actualLength = Math.min(length, size-pos); System.arraycopy(bytes, pos, b, offset, actualLength); pos += actualLength; return actualLength; } public void reset() { } public long skip(long n) { if(size == pos) return -1; long skipSize = Math.min(n, size-pos); pos += skipSize; return skipSize; } public int read() throws IOException { if(pos < size) { int i = bytes[pos++]; if(i < 0) i = 256 + i; return i; } return -1; } } public int next() { try { return is.read(); } catch (IOException e) { System.exit(1); } return -1; } public int u1() { try { return is.readUnsignedByte(); } catch (IOException e) { System.exit(1); } return -1; } public int u2() { try { return is.readUnsignedShort(); } catch (IOException e) { System.exit(1); } return -1; } public int u4() { try { return is.readInt(); } catch (IOException e) { System.exit(1); } return -1; } public int readInt() { try { return is.readInt(); } catch (IOException e) { System.exit(1); } return -1; } public float readFloat() { try { return is.readFloat(); } catch (IOException e) { System.exit(1); } return -1; } public long readLong() { try { return is.readLong(); } catch (IOException e) { System.exit(1); } return -1; } public double readDouble() { try { return is.readDouble(); } catch (IOException e) { System.exit(1); } return -1; } public String readUTF() { try { return is.readUTF(); } catch (IOException e) { System.exit(1); } return ""; } public void skip(int length) { try { is.skip(length); } catch (IOException e) { System.exit(1); } } public void error(String s) { throw new RuntimeException(s); } public void print(String s) { //System.out.print(s); } public void println(String s) { print(s + "\n"); } public void println() { print("\n"); } public CompilationUnit parse(TypeDecl outerTypeDecl, CONSTANT_Class_Info outerClassInfo, Program classPath, boolean isInner) throws FileNotFoundException, IOException { isInnerClass = isInner; return parse(outerTypeDecl, outerClassInfo, classPath); } public CompilationUnit parse(TypeDecl outerTypeDecl, CONSTANT_Class_Info outerClassInfo, Program classPath) throws FileNotFoundException, IOException { //InputStream file = ClassLoader.getSystemResourceAsStream(name); if(is == null) { FileInputStream file = new FileInputStream(name); //System.err.println("/home/torbjorn/sandbox/jdk/" + name); if(file == null) { throw new FileNotFoundException(name); } // // Does not work without DummyInputStream. Why? //is = new DataInputStream(new DummyInputStream(new BufferedInputStream(file))); is = new DataInputStream(new BufferedInputStream(file)); } if(BytecodeParser.VERBOSE) println("Parsing byte codes in " + name); this.outerClassInfo = outerClassInfo; parseMagic(); parseMinor(); parseMajor(); parseConstantPool(); CompilationUnit cu = new CompilationUnit(); TypeDecl typeDecl = parseTypeDecl(); cu.setPackageDecl(classInfo.packageDecl()); cu.addTypeDecl(typeDecl); parseFields(typeDecl); parseMethods(typeDecl); new Attributes.TypeAttributes(this, typeDecl, outerTypeDecl, classPath); is.close(); is = null; return cu; } public void parseMagic() { if (next() != 0xca || next() != 0xfe || next() != 0xba || next() != 0xbe) error("magic error"); } public void parseMinor() { int low = u1(); int high = u1(); if(BytecodeParser.VERBOSE) println("Minor: " + high + "." + low); } public void parseMajor() { int low = u1(); int high = u1(); if(BytecodeParser.VERBOSE) println("Major: " + high + "." + low); } public boolean isInnerClass = false; public TypeDecl parseTypeDecl() { int flags = u2(); Modifiers modifiers = modifiers(flags & 0xfddf); if((flags & (ACC_INTERFACE | ACC_ENUM)) == ACC_ENUM) { // Modifiers /[SuperClassAccess:Access]/ Implements:Access* BodyDecl*; EnumDecl decl = new EnumDecl(); decl.setModifiers(modifiers); decl.setID(parseThisClass()); Access superClass = parseSuperClass(); decl.setImplementsList(parseInterfaces(new List())); return decl; } else if ((flags & ACC_INTERFACE) == 0) { ClassDecl decl = new ClassDecl(); decl.setModifiers(modifiers); decl.setID(parseThisClass()); Access superClass = parseSuperClass(); decl.setSuperClassAccessOpt(superClass == null ? new Opt() : new Opt(superClass)); decl.setImplementsList(parseInterfaces(new List())); return decl; } else if((flags & ACC_ANNOTATION) == 0) { InterfaceDecl decl = new InterfaceDecl(); decl.setModifiers(modifiers); decl.setID(parseThisClass()); Access superClass = parseSuperClass(); decl.setSuperInterfaceIdList( parseInterfaces( superClass == null ? new List() : new List().add(superClass))); return decl; } else { AnnotationDecl decl = new AnnotationDecl(); decl.setModifiers(modifiers); decl.setID(parseThisClass()); Access superClass = parseSuperClass(); parseInterfaces( superClass == null ? new List() : new List().add(superClass)); return decl; } } public String parseThisClass() { int index = u2(); CONSTANT_Class_Info info = (CONSTANT_Class_Info) constantPool[index]; classInfo = info; return info.simpleName(); } public Access parseSuperClass() { int index = u2(); if (index == 0) return null; CONSTANT_Class_Info info = (CONSTANT_Class_Info) constantPool[index]; return info.access(); } public List parseInterfaces(List list) { int count = u2(); for (int i = 0; i < count; i++) { CONSTANT_Class_Info info = (CONSTANT_Class_Info) constantPool[u2()]; list.add(info.access()); } return list; } public Access fromClassName(String s) { // Sample ClassName: a/b/c$d$e // the package name ends at the last '/' // after that follows a list of type names separated by '$' // all except the first are nested types String packageName = ""; int index = s.lastIndexOf('/'); if(index != -1) packageName = s.substring(0, index).replace('/', '.'); String[] typeNames = s.substring(index + 1).split("\\$"); Access result = new TypeAccess(packageName, typeNames[0]); for(int i = 1; i < typeNames.length; i++) result = result.qualifiesAccess(new TypeAccess(typeNames[i])); return result; } public static Modifiers modifiers(int flags) { Modifiers m = new Modifiers(); if ((flags & 0x0001) != 0) m.addModifier(new Modifier("public")); if ((flags & 0x0002) != 0) m.addModifier(new Modifier("private")); if ((flags & 0x0004) != 0) m.addModifier(new Modifier("protected")); if ((flags & 0x0008) != 0) m.addModifier(new Modifier("static")); if ((flags & 0x0010) != 0) m.addModifier(new Modifier("final")); if ((flags & 0x0020) != 0) m.addModifier(new Modifier("synchronized")); if ((flags & 0x0040) != 0) m.addModifier(new Modifier("volatile")); if ((flags & 0x0080) != 0) m.addModifier(new Modifier("transient")); if ((flags & 0x0100) != 0) m.addModifier(new Modifier("native")); if ((flags & 0x0400) != 0) m.addModifier(new Modifier("abstract")); if ((flags & 0x0800) != 0) m.addModifier(new Modifier("strictfp")); return m; } public void parseFields(TypeDecl typeDecl) { int count = u2(); if(BytecodeParser.VERBOSE) println("Fields (" + count + "):"); for (int i = 0; i < count; i++) { if(BytecodeParser.VERBOSE) print(" Field nbr " + i + " "); FieldInfo fieldInfo = new FieldInfo(this); if(!fieldInfo.isSynthetic()) typeDecl.addBodyDecl(fieldInfo.bodyDecl()); } } public void parseMethods(TypeDecl typeDecl) { int count = u2(); if(BytecodeParser.VERBOSE) println("Methods (" + count + "):"); for (int i = 0; i < count; i++) { if(BytecodeParser.VERBOSE) print(" Method nbr " + i + " "); MethodInfo info = new MethodInfo(this); if(!info.isSynthetic() && !info.name.equals("")) { typeDecl.addBodyDecl(info.bodyDecl()); } } } public CONSTANT_Info[] constantPool = null; private void checkLengthAndNull(int index) { if(index >= constantPool.length) { throw new Error("Trying to access element " + index + " in constant pool of length " + constantPool.length); } if(constantPool[index] == null) throw new Error("Unexpected null element in constant pool at index " + index); } public boolean validConstantPoolIndex(int index) { return index < constantPool.length && constantPool[index] != null; } public CONSTANT_Info getCONSTANT_Info(int index) { checkLengthAndNull(index); return constantPool[index]; } public CONSTANT_Utf8_Info getCONSTANT_Utf8_Info(int index) { checkLengthAndNull(index); CONSTANT_Info info = constantPool[index]; if(!(info instanceof CONSTANT_Utf8_Info)) throw new Error("Expected CONSTANT_Utf8_info at " + index + " in constant pool but found " + info.getClass().getName()); return (CONSTANT_Utf8_Info)info; } public CONSTANT_Class_Info getCONSTANT_Class_Info(int index) { checkLengthAndNull(index); CONSTANT_Info info = constantPool[index]; if(!(info instanceof CONSTANT_Class_Info)) throw new Error("Expected CONSTANT_Class_info at " + index + " in constant pool but found " + info.getClass().getName()); return (CONSTANT_Class_Info)info; } public void parseConstantPool() { int count = u2(); if(BytecodeParser.VERBOSE) println("constant_pool_count: " + count); constantPool = new CONSTANT_Info[count + 1]; for (int i = 1; i < count; i++) { parseEntry(i); if (constantPool[i] instanceof CONSTANT_Long_Info || constantPool[i] instanceof CONSTANT_Double_Info) i++; } //println("ConstantPool: "); //for(int i = 1; i < count; i++) { // println(i + ", " + constantPool[i]); //} } private static final int CONSTANT_Class = 7; private static final int CONSTANT_FieldRef = 9; private static final int CONSTANT_MethodRef = 10; private static final int CONSTANT_InterfaceMethodRef = 11; private static final int CONSTANT_String = 8; private static final int CONSTANT_Integer = 3; private static final int CONSTANT_Float = 4; private static final int CONSTANT_Long = 5; private static final int CONSTANT_Double = 6; private static final int CONSTANT_NameAndType = 12; private static final int CONSTANT_Utf8 = 1; public void parseEntry(int i) { int tag = u1(); switch (tag) { case CONSTANT_Class: constantPool[i] = new CONSTANT_Class_Info(this); break; case CONSTANT_FieldRef: constantPool[i] = new CONSTANT_Fieldref_Info(this); break; case CONSTANT_MethodRef: constantPool[i] = new CONSTANT_Methodref_Info(this); break; case CONSTANT_InterfaceMethodRef: constantPool[i] = new CONSTANT_InterfaceMethodref_Info(this); break; case CONSTANT_String: constantPool[i] = new CONSTANT_String_Info(this); break; case CONSTANT_Integer: constantPool[i] = new CONSTANT_Integer_Info(this); break; case CONSTANT_Float: constantPool[i] = new CONSTANT_Float_Info(this); break; case CONSTANT_Long: constantPool[i] = new CONSTANT_Long_Info(this); break; case CONSTANT_Double: constantPool[i] = new CONSTANT_Double_Info(this); break; case CONSTANT_NameAndType: constantPool[i] = new CONSTANT_NameAndType_Info(this); break; case CONSTANT_Utf8: constantPool[i] = new CONSTANT_Utf8_Info(this); break; default: println("Unknown entry: " + tag); } } } public class Signatures { // simple parser framework String data; int pos; public Signatures(String s) { data = s; pos = 0; } public boolean next(String s) { for(int i = 0; i < s.length(); i++) if(data.charAt(pos + i) != s.charAt(i)) return false; return true; } public void eat(String s) { for(int i = 0; i < s.length(); i++) if(data.charAt(pos + i) != s.charAt(i)) error(s); pos += s.length(); } public void error(String s) { throw new Error("Expected " + s + " but found " + data.substring(pos)); } public String identifier() { int i = pos; while(Character.isJavaIdentifierPart(data.charAt(i))) i++; String result = data.substring(pos, i); pos = i; return result; } public boolean eof() { return pos == data.length(); } // 4.4.4 Signatures public static class ClassSignature extends Signatures { public ClassSignature(String s) { super(s); classSignature(); } void classSignature() { if(next("<")) formalTypeParameters(); superclassSignature = parseSuperclassSignature(); while(!eof()) { superinterfaceSignature.add(parseSuperinterfaceSignature()); } } public boolean hasFormalTypeParameters() { return typeParameters != null; } public List typeParameters() { return typeParameters; } public boolean hasSuperclassSignature() { return superclassSignature != null; } public Access superclassSignature() { return superclassSignature; } protected Access superclassSignature; public boolean hasSuperinterfaceSignature() { return superinterfaceSignature.getNumChildNoTransform() != 0; } public List superinterfaceSignature() { return superinterfaceSignature; } protected List superinterfaceSignature = new List(); Access parseSuperclassSignature() { return classTypeSignature(); } Access parseSuperinterfaceSignature() { return classTypeSignature(); } } public static class FieldSignature extends Signatures { public FieldSignature(String s) { super(s); fieldTypeAccess = fieldTypeSignature(); } Access fieldTypeAccess() { return fieldTypeAccess; } private Access fieldTypeAccess; } public static class MethodSignature extends Signatures { public MethodSignature(String s) { super(s); methodTypeSignature(); } void methodTypeSignature() { if(next("<")) formalTypeParameters(); eat("("); while(!next(")")) { parameterTypes.add(typeSignature()); } eat(")"); returnType = parseReturnType(); while(!eof()) { exceptionList.add(throwsSignature()); } } Access parseReturnType() { if(next("V")) { eat("V"); return new PrimitiveTypeAccess("void"); } else { return typeSignature(); } } Access throwsSignature() { eat("^"); if(next("L")) { return classTypeSignature(); } else { return typeVariableSignature(); } } public boolean hasFormalTypeParameters() { return typeParameters != null; } public List typeParameters() { return typeParameters; } public Collection parameterTypes() { return parameterTypes; } protected Collection parameterTypes = new ArrayList(); public List exceptionList() { return exceptionList; } public boolean hasExceptionList() { return exceptionList.getNumChildNoTransform() != 0; } protected List exceptionList = new List(); protected Access returnType = null; public boolean hasReturnType() { return returnType != null; } public Access returnType() { return returnType; } } protected List typeParameters; void formalTypeParameters() { eat("<"); typeParameters = new List(); do { typeParameters.add(formalTypeParameter()); } while(!next(">")); eat(">"); } TypeVariable formalTypeParameter() { String id = identifier(); List bounds = new List(); Access classBound = classBound(); if(classBound != null) bounds.add(classBound); while(next(":")) { bounds.add(interfaceBound()); } if(bounds.getNumChildNoTransform() == 0) bounds.add(new TypeAccess("java.lang", "Object")); return new TypeVariable(new Modifiers(new List()), id, new List(), bounds); } Access classBound() { eat(":"); if(nextIsFieldTypeSignature()) { return fieldTypeSignature(); } else { return null; //return new TypeAccess("java.lang", "Object"); } } Access interfaceBound() { eat(":"); return fieldTypeSignature(); } Access fieldTypeSignature() { if(next("L")) return classTypeSignature(); else if(next("[")) return arrayTypeSignature(); else if(next("T")) return typeVariableSignature(); else error("L or [ or T"); return null; // error never returns } boolean nextIsFieldTypeSignature() { return next("L") || next("[") || next("T"); } Access classTypeSignature() { eat("L"); // Package and Type Name StringBuffer packageName = new StringBuffer(); String typeName = identifier(); while(next("/")) { eat("/"); if(packageName.length() != 0) packageName.append("."); packageName.append(typeName); typeName = identifier(); } String[] names = typeName.split("\\$"); Access a = new TypeAccess(packageName.toString(), names[0]); for(int i = 1; i < names.length; i++) a = a.qualifiesAccess(new TypeAccess(names[i])); if(next("<")) { // type arguments of top level type a = new ParTypeAccess(a, typeArguments()); } while(next(".")) { // inner classes a = a.qualifiesAccess(classTypeSignatureSuffix()); } eat(";"); return a; } Access classTypeSignatureSuffix() { eat("."); String id = identifier(); String[] names = id.split("\\$"); Access a = new TypeAccess(names[0]); for(int i = 1; i < names.length; i++) a = a.qualifiesAccess(new TypeAccess(names[i])); if(next("<")) { a = new ParTypeAccess(a, typeArguments()); } return a; } Access typeVariableSignature() { eat("T"); String id = identifier(); eat(";"); return new TypeAccess(id); } List typeArguments() { eat("<"); List list = new List(); do { list.add(typeArgument()); } while(!next(">")); eat(">"); return list; } Access typeArgument() { if(next("*")) { eat("*"); return new Wildcard(); } else if(next("+")) { eat("+"); return new WildcardExtends(fieldTypeSignature()); } else if(next("-")) { eat("-"); return new WildcardSuper(fieldTypeSignature()); } else { return fieldTypeSignature(); } } Access arrayTypeSignature() { eat("["); return new ArrayTypeAccess(typeSignature()); } Access typeSignature() { if(nextIsFieldTypeSignature()) { return fieldTypeSignature(); } else { return baseType(); } } Access baseType() { if(next("B")) { eat("B"); return new PrimitiveTypeAccess("byte"); } else if(next("C")) { eat("C"); return new PrimitiveTypeAccess("char"); } else if(next("D")) { eat("D"); return new PrimitiveTypeAccess("double"); } else if(next("F")) { eat("F"); return new PrimitiveTypeAccess("float"); } else if(next("I")) { eat("I"); return new PrimitiveTypeAccess("int"); } else if(next("J")) { eat("J"); return new PrimitiveTypeAccess("long"); } else if(next("S")) { eat("S"); return new PrimitiveTypeAccess("short"); } else if(next("Z")) { eat("Z"); return new PrimitiveTypeAccess("boolean"); } error("baseType"); return null; // error never returns } public static void main(String[] args) { //new Signatures("Ljava/lang/Object;").classSignature(); //new ClassSignature("Ljava/lang/Object;").classSignature(); new ClassSignature("Ljava/lang/Object;Ljava/lang/Comparable;"); } } public interface Flags { public int ACC_PUBLIC = 0x0001; public int ACC_PRIVATE = 0x0002; public int ACC_PROTECTED = 0x0004; public int ACC_STATIC = 0x0008; public int ACC_FINAL = 0x0010; public int ACC_SUPER = 0x0020; public int ACC_SYNCHRONIZED = 0x0020; public int ACC_VOLATILE = 0x0040; public int ACC_BRIDGE = 0x0040; public int ACC_TRANSIENT = 0x0080; public int ACC_VARARGS = 0x0080; public int ACC_NATIVE = 0x0100; public int ACC_INTERFACE = 0x0200; public int ACC_ABSTRACT = 0x0400; public int ACC_STRICT = 0x0800; public int ACC_SYNTHETIC = 0x1000; public int ACC_ANNOTATION = 0x2000; public int ACC_ENUM = 0x4000; } }