diff options
Diffstat (limited to 'src/fjbg/ch')
34 files changed, 2121 insertions, 445 deletions
diff --git a/src/fjbg/ch/epfl/lamp/fjbg/FJBGContext.java b/src/fjbg/ch/epfl/lamp/fjbg/FJBGContext.java index 07030fbb9c..4c5bc27237 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/FJBGContext.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/FJBGContext.java @@ -1,3 +1,7 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; @@ -132,11 +136,28 @@ public class FJBGContext { return new JCodeAttribute(this, clazz, owner); } + public JEnclosingMethodAttribute JEnclosingMethodAttribute(JClass clazz, + String className, + String methodName, + JType methodType) { + return new JEnclosingMethodAttribute(this, clazz, className, methodName, methodType); + } + + public JExceptionsAttribute JExceptionsAttribute(JClass clazz, + JMethod owner) { + return new JExceptionsAttribute(this, clazz, owner); + } + public JLineNumberTableAttribute JLineNumberTableAttribute(JClass clazz, JCode owner) { return new JLineNumberTableAttribute(this, clazz, owner); } + public JLocalVariableTableAttribute JLocalVariableTableAttribute(JClass clazz, + JCode owner) { + return new JLocalVariableTableAttribute(this, clazz, owner); + } + public JOtherAttribute JOtherAttribute(JClass clazz, Object owner, String name, @@ -145,14 +166,6 @@ public class FJBGContext { return new JOtherAttribute(this, clazz, owner, name, contents, length); } - public JEnclosingMethodAttribute JEnclosingMethodAttribute(JClass clazz, - String className, - String methodName, - JType methodType) { - return new JEnclosingMethodAttribute(this, clazz, className, methodName, methodType); - } - - public JOtherAttribute JOtherAttribute(JClass clazz, Object owner, String name, @@ -165,6 +178,11 @@ public class FJBGContext { return new JSourceFileAttribute(this, clazz, sourceFileName); } + public JStackMapTableAttribute JStackMapTableAttribute(JClass clazz, + JCode owner) { + return new JStackMapTableAttribute(this, clazz, owner); + } + /// Repository ////////////////////////////////////////////////////////////////////// diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java b/src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java index 7db047ee98..0a48fc1628 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java @@ -1,3 +1,7 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JArrayType.java b/src/fjbg/ch/epfl/lamp/fjbg/JArrayType.java index 53aeee3ab6..85f9408f46 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JArrayType.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JArrayType.java @@ -1,3 +1,7 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JAttribute.java index caaff778ec..3b9e5f3a7c 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JAttribute.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JAttribute.java @@ -1,13 +1,23 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; -import java.io.*; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.util.*; /** * Abstract superclass for attributes which can be attached to various * parts of a class file. * + * Attributes are used for classes (section 4.2), fields (section 4.6), + * methods (section 4.7) and the Code attribute (section 4.8.3). + * See sections 4.2 and later of the JVM specification. + * * @author Michel Schinz * @version 1.0 */ diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JAttributeFactory.java b/src/fjbg/ch/epfl/lamp/fjbg/JAttributeFactory.java index 92333dc4b4..212058a660 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JAttributeFactory.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JAttributeFactory.java @@ -1,10 +1,15 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; -import java.io.*; -import java.util.*; +import java.io.DataInputStream; +import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; /** * Extensible factory to build subclasses of JAttribute based on an @@ -43,9 +48,16 @@ public class JAttributeFactory { Constructor defaultConstructor) { this.context = context; this.defaultConstructor = defaultConstructor; + registerClass("BootstrapInvokeDynamic", JBootstrapInvokeDynamic.class); registerClass("Code", JCodeAttribute.class); + registerClass("ConstantValue", JConstantValueAttribute.class); + registerClass("EnclosingMethod", JEnclosingMethodAttribute.class); + registerClass("Exceptions", JExceptionsAttribute.class); + registerClass("InnerClasses", JInnerClassesAttribute.class); registerClass("LineNumberTable", JLineNumberTableAttribute.class); + registerClass("LocalVariableTable", JLocalVariableTableAttribute.class); registerClass("SourceFile", JSourceFileAttribute.class); + registerClass("StackMapTable", JStackMapTableAttribute.class); } public JAttributeFactory(FJBGContext context) { diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JBootstrapInvokeDynamic.java b/src/fjbg/ch/epfl/lamp/fjbg/JBootstrapInvokeDynamic.java index ad4acfc329..91c4a465ae 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JBootstrapInvokeDynamic.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JBootstrapInvokeDynamic.java @@ -1,5 +1,11 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ + package ch.epfl.lamp.fjbg; +import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Iterator; @@ -8,28 +14,56 @@ import java.util.Iterator; * BootstrapInvokeDynamic entry, as described by JSR 292 (invoke dynamic) * * @author Iulian Dragos + * @version 1.0 * */ public class JBootstrapInvokeDynamic extends JAttribute { - /** Constant pool of the current classfile. */ - private JConstantPool pool; + /** Constant pool of the current classfile. */ + private JConstantPool pool; + + protected final int classIdx; - private int classIndex = -1; + public JBootstrapInvokeDynamic(FJBGContext context, + JClass clazz, + String className) { + super(context, clazz); + this.pool = clazz.pool; + + this.classIdx = pool.addClass(className); + } public JBootstrapInvokeDynamic(FJBGContext context, - JClass clazz, String className) { - super(context, clazz); - this.pool = clazz.pool; - this.classIndex = pool.addClass(className); + JClass clazz, + Object owner, + String name, + int size, + DataInputStream stream) + throws IOException { + super(context, clazz, name); + + this.classIdx = stream.readShort(); + + assert name.equals(getName()); } public String getName() { return "BootstrapInvokeDynamic"; } - protected int getSize() { - return 2; - } + // Follows javap output format for BootstrapInvokeDynamic attribute. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(" BootstrapInvokeDynamic:"); + buf.append("\n #"); + buf.append(classIdx); + buf.append("; // class "); + buf.append(pool.lookupClass(classIdx)); + buf.append("\n"); + return buf.toString(); + } + + protected int getSize() { + return 2; // Short.SIZE + } - protected void writeContentsTo(DataOutputStream stream) throws IOException { - stream.writeShort(classIndex); - } + protected void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeShort(classIdx); + } } diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JClass.java b/src/fjbg/ch/epfl/lamp/fjbg/JClass.java index 0fc604424a..31e30725c9 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JClass.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JClass.java @@ -1,3 +1,7 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; @@ -7,8 +11,8 @@ import java.io.*; /** * Representation of a Java class. * - * @author Michel Schinz - * @version 1.0 + * @author Michel Schinz, Stephane Micheloud + * @version 1.1 */ public class JClass extends JMember { @@ -81,7 +85,7 @@ public class JClass extends JMember { pool = context.JConstantPool(stream); accessFlags = stream.readShort(); - // This class, super class and interfaces + // This class, super class and interfaces name = pool.lookupClass(stream.readShort()); superclassName = pool.lookupClass(stream.readShort()); interfaceNames = new String[stream.readShort()]; @@ -97,11 +101,17 @@ public class JClass extends JMember { for (int i = 0; i < methodsCount; ++i) addMethod(context.JMethod(this, stream)); + String fileName = null; int attributesCount = stream.readShort(); - for (int i = 0; i < attributesCount; ++i) - addAttribute(attributeFactory.newInstance(this, this, stream)); - - sourceFileName = null; + for (int i = 0; i < attributesCount; ++i) { + JAttribute attr = attributeFactory.newInstance(this, this, stream); + if (attr instanceof JSourceFileAttribute) + fileName = ((JSourceFileAttribute)attr).getFileName(); + else if (attr instanceof JInnerClassesAttribute) + innerClasses = (JInnerClassesAttribute)attr; + addAttribute(attr); + } + sourceFileName = fileName; } /** @@ -118,6 +128,12 @@ public class JClass extends JMember { public String[] getInterfaceNames() { return interfaceNames; } /** + * Gets the source file name of this class. + * @return The string representing the source file name of this class. + */ + public String getSourceFileName() { return sourceFileName; } + + /** * Gets the type of the objects that are instances of the class. * @return The type of the instances of the class. */ @@ -125,6 +141,30 @@ public class JClass extends JMember { public JClass getJClass() { return this; } + public boolean isPublic() { + return (accessFlags & JAccessFlags.ACC_PUBLIC) != 0; + } + + public boolean isPrivate() { + return (accessFlags & JAccessFlags.ACC_PRIVATE) != 0; + } + + public boolean isProtected() { + return (accessFlags & JAccessFlags.ACC_PROTECTED) != 0; + } + + public boolean isStatic() { + return (accessFlags & JAccessFlags.ACC_STATIC) != 0; + } + + public boolean isFinal() { + return (accessFlags & JAccessFlags.ACC_FINAL) != 0; + } + + public boolean isAbstract() { + return (accessFlags & JAccessFlags.ACC_ABSTRACT) != 0; + } + /** * Gets the version number of the class. * @param major The int representing the major part of the version number @@ -134,8 +174,8 @@ public class JClass extends JMember { */ public void setVersion(int major, int minor) { assert !frozen; - this.major = major; - this.minor = minor; + this.major = major; + this.minor = minor; } /** @@ -171,12 +211,12 @@ public class JClass extends JMember { * @return The boolean representing if the class is an interface or not. */ public boolean isInterface() { - return (accessFlags & JAccessFlags.ACC_INTERFACE) != 0; + return (accessFlags & JAccessFlags.ACC_INTERFACE) != 0; } public void addField(JField field) { assert !frozen; - fields.add(field); + fields.add(field); } /** @@ -191,7 +231,7 @@ public class JClass extends JMember { protected void addMethod(JMethod method) { assert !frozen; - methods.add(method); + methods.add(method); } /** @@ -223,6 +263,10 @@ public class JClass extends JMember { methods.remove(m); } + public JField[] getFields() { + return (JField[])fields.toArray(new JField[fields.size()]); + } + public JMethod[] getMethods() { return (JMethod[])methods.toArray(new JMethod[methods.size()]); } @@ -254,13 +298,13 @@ public class JClass extends JMember { if (!parent.mkdirs()) throw new IOException("cannot create directory " + parent); - FileOutputStream fStream = new FileOutputStream(file); + FileOutputStream fStream = new FileOutputStream(file); BufferedOutputStream bStream = new BufferedOutputStream(fStream); - DataOutputStream dStream = new DataOutputStream(bStream); - writeTo(dStream); - dStream.close(); + DataOutputStream dStream = new DataOutputStream(bStream); + writeTo(dStream); + dStream.close(); bStream.close(); - fStream.close(); + fStream.close(); } public void setBootstrapClass(String bootstrapClass) { @@ -276,44 +320,109 @@ public class JClass extends JMember { public void writeTo(DataOutputStream stream) throws IOException { if (!frozen) freeze(); - int thisClassIdx = pool.addClass(name); - int superClassIdx = pool.addClass(superclassName); - int[] interfacesIdx = new int[interfaceNames.length]; + int thisClassIdx = pool.addClass(name); + int superClassIdx = pool.addClass(superclassName); + int[] interfacesIdx = new int[interfaceNames.length]; - for (int i = 0; i < interfaceNames.length; ++i) - interfacesIdx[i] = pool.addClass(interfaceNames[i]); + for (int i = 0; i < interfaceNames.length; ++i) + interfacesIdx[i] = pool.addClass(interfaceNames[i]); pool.freeze(); - // Magic number. - stream.writeInt(MAGIC_NUMBER); - // Version - stream.writeShort(minor); - stream.writeShort(major); - // Constant pool - pool.writeTo(stream); - // Access flags - stream.writeShort(accessFlags); - - // This class, super class and interfaces - stream.writeShort(thisClassIdx); - stream.writeShort(superClassIdx); - stream.writeShort(interfacesIdx.length); - for (int i = 0; i < interfacesIdx.length; ++i) - stream.writeShort(interfacesIdx[i]); - - // Fields and methods - stream.writeShort(fields.size()); + // Magic number. + stream.writeInt(MAGIC_NUMBER); + // Version + stream.writeShort(minor); + stream.writeShort(major); + // Constant pool + pool.writeTo(stream); + // Access flags + stream.writeShort(accessFlags); + + // This class, super class and interfaces + stream.writeShort(thisClassIdx); + stream.writeShort(superClassIdx); + stream.writeShort(interfacesIdx.length); + for (int i = 0; i < interfacesIdx.length; ++i) + stream.writeShort(interfacesIdx[i]); + + // Fields and methods + stream.writeShort(fields.size()); Iterator fieldsIt = fields.iterator(); while (fieldsIt.hasNext()) ((JField)fieldsIt.next()).writeTo(stream); - stream.writeShort(methods.size()); + stream.writeShort(methods.size()); Iterator methodsIt = methods.iterator(); while (methodsIt.hasNext()) ((JMethod)methodsIt.next()).writeTo(stream); - // Attributes + // Attributes JAttribute.writeTo(attributes, stream); } + + // Follows javap output format for ClassFile. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(); + if (sourceFileName != null) { + buf.append("Compiled from \""); + buf.append(sourceFileName); + buf.append("\"\n"); + } + buf.append(getMemberName()); + buf.append(toExternalName(getName())); + if (!isInterface()) { + buf.append(" extends "); + buf.append(toExternalName(getSuperclassName())); + } + if (interfaceNames.length > 0) { + if (isInterface()) buf.append(" extends "); + else buf.append(" implements "); + for (int i = 0; i < interfaceNames.length; ++i) { + if (i > 0) buf.append(","); + buf.append(toExternalName(interfaceNames[i])); + } + } + buf.append("\n"); + Iterator attrsIt = attributes.iterator(); + while (attrsIt.hasNext()) { + JAttribute attr = (JAttribute)attrsIt.next(); + buf.append(attr); + } + buf.append(" minor version: "); + buf.append(minor); + buf.append("\n major version: "); + buf.append(major); + buf.append("\n"); + buf.append(pool); + buf.append("\n{\n"); + JField[] jfields = getFields(); + for (int i = 0; i < jfields.length; ++i) { + if (i > 0) buf.append("\n"); + buf.append(jfields[i]); + } + buf.append("\n"); + JMethod[] jmethods = getMethods(); + for (int i = 0; i < jmethods.length; ++i) { + if (i > 0) buf.append("\n"); + buf.append(jmethods[i]); + } + buf.append("\n}\n"); + return buf.toString(); + } + + private String getMemberName() { + StringBuffer buf = new StringBuffer(); + if (isPublic()) buf.append("public "); + else if (isProtected()) buf.append("protected "); + else if (isPrivate()) buf.append("private "); + if (isInterface()) + buf.append("interface "); + else { + if (isAbstract()) buf.append("abstract "); + else if (isFinal()) buf.append("final "); + buf.append("class "); + } + return buf.toString(); + } } diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JCode.java b/src/fjbg/ch/epfl/lamp/fjbg/JCode.java index f7d275c8a7..332cc7c518 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JCode.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JCode.java @@ -1,14 +1,19 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.util.*; -import java.io.*; -import ch.epfl.lamp.util.*; +import ch.epfl.lamp.util.ByteArray; /** - * List of instructions, to which Java byte-code instructions can be - * added. + * List of instructions, to which Java byte-code instructions can be added. * * @author Michel Schinz, Thomas Friedli * @version 1.0 @@ -50,7 +55,10 @@ public class JCode { this.context = context; this.pool = clazz.getConstantPool(); this.owner = owner; + owner.setCode(this); int size = stream.readInt(); + if (size >= 65536) // section 4.10 + throw new Error("code size must be less than 65536: " + size); this.codeArray = new ByteArray(stream, size); } @@ -516,20 +524,20 @@ public class JCode { emitU2(JOpcode.JSR, label.getOffset16(getPC() + 1, getPC())); } public void emitJSR(int targetPC) { - emitU2(JOpcode.JSR, targetPC - getPC()); + emitU2(JOpcode.JSR, targetPC - getPC()); } public void emitJSR() { - emitU2(JOpcode.JSR, 0); + emitU2(JOpcode.JSR, 0); } public void emitJSR_W(Label label) { emitU4(JOpcode.JSR_W, label.getOffset32(getPC() + 1, getPC())); } public void emitJSR_W(int targetPC) { - emitU4(JOpcode.JSR_W, targetPC - getPC()); + emitU4(JOpcode.JSR_W, targetPC - getPC()); } public void emitJSR_W() { - emitU4(JOpcode.JSR_W, 0); + emitU4(JOpcode.JSR_W, 0); } /* @@ -537,10 +545,10 @@ public class JCode { emitU2(JOpcode.RET, label.getOffset16(getPC() + 1, getPC())); } public void emitRET(int targetPC) { - emitU1(JOpcode.RET, targetPC); + emitU1(JOpcode.RET, targetPC); } public void emitRET() { - emitU1(JOpcode.RET, 0); + emitU1(JOpcode.RET, 0); } */ @@ -553,42 +561,42 @@ public class JCode { } public void emitTABLESWITCH(int[] keys, - Label[] branches, - Label defaultBranch) { - assert keys.length == branches.length; + Label[] branches, + Label defaultBranch) { + assert keys.length == branches.length; int low = keys[0], high = keys[keys.length - 1]; - int instrPC = getPC(); + int instrPC = getPC(); - setStackProduction(instrPC, JOpcode.TABLESWITCH); - codeArray.addU1(JOpcode.cTABLESWITCH); - while (getPC() % 4 != 0) codeArray.addU1(0); + setStackProduction(instrPC, JOpcode.TABLESWITCH); + codeArray.addU1(JOpcode.cTABLESWITCH); + while (getPC() % 4 != 0) codeArray.addU1(0); codeArray.addU4(defaultBranch.getOffset32(getPC(), instrPC)); codeArray.addU4(low); codeArray.addU4(high); - for (int i = 0; i < branches.length; i++) { + for (int i = 0; i < branches.length; i++) { assert keys[i] == low + i; codeArray.addU4(branches[i].getOffset32(getPC(), instrPC)); - } + } } public void emitLOOKUPSWITCH(int[] keys, - Label[] branches, - Label defaultBranch) { - assert keys.length == branches.length; + Label[] branches, + Label defaultBranch) { + assert keys.length == branches.length; - int instrPC = getPC(); - setStackProduction(getPC(), JOpcode.LOOKUPSWITCH); - codeArray.addU1(JOpcode.cLOOKUPSWITCH); - while (getPC() % 4 != 0) codeArray.addU1(0); + int instrPC = getPC(); + setStackProduction(getPC(), JOpcode.LOOKUPSWITCH); + codeArray.addU1(JOpcode.cLOOKUPSWITCH); + while (getPC() % 4 != 0) codeArray.addU1(0); codeArray.addU4(defaultBranch.getOffset32(getPC(), instrPC)); - codeArray.addU4(branches.length); - for (int i = 0; i < branches.length; i++) { - codeArray.addU4(keys[i]); + codeArray.addU4(branches.length); + for (int i = 0; i < branches.length; i++) { + codeArray.addU4(keys[i]); codeArray.addU4(branches[i].getOffset32(getPC(), instrPC)); - } + } } public void emitIRETURN() { emit(JOpcode.IRETURN); } @@ -700,63 +708,63 @@ public class JCode { || (opcode.code == JOpcode.cFSTORE) || (opcode.code == JOpcode.cDSTORE) || (opcode.code == JOpcode.cASTORE) - || (opcode.code == JOpcode.cRET) + || (opcode.code == JOpcode.cRET) : "invalide opcode for WIDE: " + opcode; - setStackProduction(getPC(), opcode); - codeArray.addU1(JOpcode.WIDE.code); + setStackProduction(getPC(), opcode); + codeArray.addU1(JOpcode.WIDE.code); codeArray.addU1(opcode.code); - codeArray.addU2(index); + codeArray.addU2(index); } public void emitWIDE(JOpcode opcode, int index, int constant) { - assert opcode.code == JOpcode.cIINC + assert opcode.code == JOpcode.cIINC : "invalid opcode for WIDE: " + opcode; - setStackProduction(getPC(), opcode); - codeArray.addU1(JOpcode.cWIDE); + setStackProduction(getPC(), opcode); + codeArray.addU1(JOpcode.cWIDE); codeArray.addU1(opcode.code); - codeArray.addU2(index); - codeArray.addU2(constant); + codeArray.addU2(index); + codeArray.addU2(constant); } protected void emitU1(JOpcode opcode, int i1) { setStackProduction(getPC(), opcode); codeArray.addU1(opcode.code); - codeArray.addU1(i1); + codeArray.addU1(i1); } protected void emitU1U1(JOpcode opcode, int i1, int i2) { setStackProduction(getPC(), opcode); codeArray.addU1(opcode.code); - codeArray.addU1(i1); - codeArray.addU1(i2); + codeArray.addU1(i1); + codeArray.addU1(i2); } protected void emitU2(JOpcode opcode, int i1) { setStackProduction(getPC(), opcode); codeArray.addU1(opcode.code); - codeArray.addU2(i1); + codeArray.addU2(i1); } protected void emitU2U1(JOpcode opcode, int i1, int i2) { setStackProduction(getPC(), opcode); codeArray.addU1(opcode.code); - codeArray.addU2(i1); - codeArray.addU1(i2); + codeArray.addU2(i1); + codeArray.addU1(i2); } protected void emitU2U1U1(JOpcode opcode, int i1, int i2, int i3) { setStackProduction(getPC(), opcode); codeArray.addU1(opcode.code); - codeArray.addU2(i1); - codeArray.addU1(i2); - codeArray.addU1(i3); + codeArray.addU2(i1); + codeArray.addU1(i2); + codeArray.addU1(i3); } protected void emitU4(JOpcode opcode, int i1) { setStackProduction(getPC(), opcode); codeArray.addU1(opcode.code); - codeArray.addU4(i1); + codeArray.addU4(i1); } protected int getU1(int sourcePos) { @@ -798,27 +806,27 @@ public class JCode { stackProduction = new int[256]; Arrays.fill(stackProduction, UNKNOWN_STACK_SIZE); } else { - while (pc >= stackProduction.length) { - int[] newStackProduction = new int[stackProduction.length * 2]; - System.arraycopy(stackProduction, 0, - newStackProduction, 0, - stackProduction.length); - Arrays.fill(newStackProduction, - stackProduction.length, - newStackProduction.length, - UNKNOWN_STACK_SIZE); - stackProduction = newStackProduction; - } + while (pc >= stackProduction.length) { + int[] newStackProduction = new int[stackProduction.length * 2]; + System.arraycopy(stackProduction, 0, + newStackProduction, 0, + stackProduction.length); + Arrays.fill(newStackProduction, + stackProduction.length, + newStackProduction.length, + UNKNOWN_STACK_SIZE); + stackProduction = newStackProduction; + } } - stackProduction[pc] = production; + stackProduction[pc] = production; } protected void setStackProduction(int pc, JOpcode opcode) { // TODO we should instead check whether the opcode has known // stack consumption/production. if (getStackProduction(pc) == UNKNOWN_STACK_SIZE) -// && opcode.hasKnownProducedDataSize() -// && opcode.hasKnownConsumedDataSize()) +// && opcode.hasKnownProducedDataSize() +// && opcode.hasKnownConsumedDataSize()) setStackProduction(pc, opcode.getProducedDataSize() - opcode.getConsumedDataSize()); @@ -1043,6 +1051,27 @@ public class JCode { stream.writeShort(handlerPC); stream.writeShort(catchTypeIndex); } + + // Follows javap output format for exception handlers. + /*@Override*/public String toString() { + StringBuffer buf = new StringBuffer(" "); + if (startPC < 10) buf.append(" "); + buf.append(startPC); + buf.append(" "); + if (endPC < 10) buf.append(" "); + buf.append(endPC); + buf.append(" "); + buf.append(handlerPC); + buf.append(" "); + if (catchType != null) { + buf.append("Class "); + buf.append(catchType); + } + else + buf.append("any"); + return buf.toString(); + } + } public void addExceptionHandler(ExceptionHandler handler) { @@ -1065,7 +1094,7 @@ public class JCode { addExceptionHandler(startPC, endPC, handlerPC, null); } - public List/*<JExceptionHandler>*/ getExceptionHandlers() { + public List/*<ExceptionHandler>*/ getExceptionHandlers() { return exceptionHandlers; } @@ -1122,9 +1151,145 @@ public class JCode { } // Output + ////////////////////////////////////////////////////////////////////// + public void writeTo(DataOutputStream stream) throws IOException { assert frozen; stream.writeInt(getSize()); - codeArray.writeTo(stream); + codeArray.writeTo(stream); + } + + // Follows javap output format for opcodes. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(); + JOpcode opcode = null; + int pc = 0, addr = 0; + while (pc < codeArray.getSize()) { + buf.append("\n "); + buf.append(pc); + buf.append(":\t"); + opcode = JOpcode.OPCODES[codeArray.getU1(pc)]; + buf.append(decode(opcode, pc)); + if (opcode.code == JOpcode.cTABLESWITCH || + opcode.code == JOpcode.cLOOKUPSWITCH) { + addr = ((pc / 4 + 1) + 1) * 4; // U4 aligned data + int low = codeArray.getU4(addr); + int high = codeArray.getU4(addr+4); + pc = addr + (2/*low+high*/ + (high - low + 1)/*targets*/) * 4; + } else + pc += opcode.getSize(); + } + if (exceptionHandlers.size() > 0) { + buf.append("\n Exception table:\n from to target type\n"); + Iterator it = exceptionHandlers.iterator(); + while (it.hasNext()) { + ExceptionHandler exh = (ExceptionHandler)it.next(); + buf.append(exh); + buf.append("\n"); + } + } + return buf.toString(); + } + + private String decode(JOpcode opcode, int pc) { + String ownerClassName = owner.getOwner().getName(); + int data, data2; + StringBuilder buf = new StringBuilder(); + buf.append(opcode.name.toLowerCase()); + switch (opcode.code) { + case JOpcode.cALOAD: case JOpcode.cASTORE: case JOpcode.cBIPUSH: + case JOpcode.cDLOAD: case JOpcode.cDSTORE: + case JOpcode.cFLOAD: case JOpcode.cFSTORE: + case JOpcode.cILOAD: case JOpcode.cISTORE: + case JOpcode.cLLOAD: case JOpcode.cLSTORE: + data = codeArray.getU1(pc+1); + buf.append("\t"); + buf.append(data); + break; + case JOpcode.cLDC: + data = codeArray.getU1(pc+1); + buf.append("\t#"); + buf.append(data); + buf.append("; "); + buf.append(pool.lookupEntry(data).toComment(ownerClassName)); + break; + case JOpcode.cNEWARRAY: + data = codeArray.getU1(pc+1); + buf.append(" "); + buf.append(JType.tagToString(data)); + break; + case JOpcode.cIINC: + data = codeArray.getU1(pc+1); + data2 = codeArray.getU1(pc+2); + buf.append("\t"); + buf.append(data); + buf.append(", "); + buf.append(data2); + break; + case JOpcode.cSIPUSH: + data = codeArray.getU2(pc+1); + buf.append("\t"); + buf.append(data); + break; + case JOpcode.cANEWARRAY: case JOpcode.cCHECKCAST: + case JOpcode.cGETFIELD: case JOpcode.cGETSTATIC: + case JOpcode.cINSTANCEOF: + case JOpcode.cINVOKESPECIAL: case JOpcode.cINVOKESTATIC: + case JOpcode.cINVOKEVIRTUAL: + case JOpcode.cLDC_W: case JOpcode.cLDC2_W: case JOpcode.cNEW: + case JOpcode.cPUTFIELD: case JOpcode.cPUTSTATIC: + data = codeArray.getU2(pc+1); + buf.append("\t#"); + buf.append(data); + buf.append("; "); + buf.append(pool.lookupEntry(data).toComment(ownerClassName)); + break; + case JOpcode.cIF_ACMPEQ: case JOpcode.cIF_ACMPNE: + case JOpcode.cIFEQ: case JOpcode.cIFGE: case JOpcode.cIFGT: + case JOpcode.cIFLE: case JOpcode.cIFLT: case JOpcode.cIFNE: + case JOpcode.cIFNONNULL: case JOpcode.cIFNULL: + case JOpcode.cIF_ICMPEQ: case JOpcode.cIF_ICMPGE: + case JOpcode.cIF_ICMPGT: case JOpcode.cIF_ICMPLE: + case JOpcode.cIF_ICMPLT: case JOpcode.cIF_ICMPNE: + data = codeArray.getU2(pc+1); // maybe S2 offset + buf.append("\t"); + buf.append(pc+data); + break; + case JOpcode.cGOTO: + data = codeArray.getS2(pc+1); // always S2 offset + buf.append("\t"); + buf.append(pc+data); + break; + case JOpcode.cINVOKEINTERFACE: + data = codeArray.getU2(pc+1); + data2 = codeArray.getU1(pc+3); + buf.append("\t#"); + buf.append(data); + buf.append(", "); + buf.append(data2); + buf.append("; "); + buf.append(pool.lookupEntry(data).toComment(ownerClassName)); + break; + case JOpcode.cTABLESWITCH: + buf.append("{ //"); + int addr = ((pc / 4 + 1) + 1) * 4; // U4 aligned data + int low = codeArray.getU4(addr); + int high = codeArray.getU4(addr+4); + buf.append(low); + buf.append(" to "); + buf.append(high); + for (int i = low; i <= high; ++i) { + buf.append("\n\t\t"); + buf.append(i); + buf.append(": "); + buf.append(pc+codeArray.getU4(addr+(i-1)*4)); + buf.append(";"); + } + buf.append("\n\t\tdefault: "); + buf.append(pc+codeArray.getU4(addr-4)); + buf.append(" }"); + default: + } + return buf.toString(); } } diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JCodeAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JCodeAttribute.java index 44a3d551aa..153f156c55 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JCodeAttribute.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JCodeAttribute.java @@ -1,21 +1,40 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; -import java.util.*; -import java.io.*; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.List; /** * Code attribute, containing code of methods. * - * @author Michel Schinz - * @version 1.0 + * A Code attribute contains the JVM instructions and auxiliary information + * for a single method, instance initialization method, or class or interface + * initialization method. See section 4.8.3 of the JVM specification. + * + * @author Michel Schinz, Stephane Micheloud + * @version 1.1 */ public class JCodeAttribute extends JAttribute { protected final JCode code; + protected final JMethod owner; + protected static int UNKNOWN_STACK_SIZE = Integer.MIN_VALUE; + protected final int maxStackSize; + protected final int maxLocals; public JCodeAttribute(FJBGContext context, JClass clazz, JMethod owner) { super(context, clazz); + this.owner = owner; + + this.maxStackSize = UNKNOWN_STACK_SIZE; + this.maxLocals = 0; // unknown this.code = owner.getCode(); assert clazz == owner.getOwner(); @@ -28,19 +47,18 @@ public class JCodeAttribute extends JAttribute { int size, DataInputStream stream) throws IOException { - super(context, clazz); - - stream.readShort(); // skip max stack size - stream.readShort(); // skip max locals + super(context, clazz, name); + this.owner = (JMethod)owner; + this.maxStackSize = stream.readShort(); + this.maxLocals = stream.readShort(); this.code = context.JCode(clazz, (JMethod)owner, stream); int handlersCount = stream.readShort(); for (int i = 0; i < handlersCount; ++i) code.addExceptionHandler(code.new ExceptionHandler(stream)); - List/*<JAttribute>*/ attributes = - JAttribute.readFrom(context, clazz, owner, stream); + JAttribute.readFrom(context, clazz, code, stream); Iterator attrIt = attributes.iterator(); while (attrIt.hasNext()) code.addAttribute((JAttribute)attrIt.next()); @@ -50,6 +68,26 @@ public class JCodeAttribute extends JAttribute { public String getName() { return "Code"; } + // Follows javap output format for Code attribute. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(" Code:"); + buf.append("\n Stack="); + buf.append(maxStackSize); + buf.append(", Locals="); + buf.append(maxLocals); + buf.append(", Args_size="); + buf.append(owner.getArgsSize()); + buf.append(code); + buf.append("\n"); + Iterator it = code.getAttributes().iterator(); + while (it.hasNext()) { + JAttribute attr = (JAttribute)it.next(); + buf.append(attr); + buf.append("\n"); + } + return buf.toString(); + } + protected int getSize() { int handlersNum = code.getExceptionHandlers().size(); @@ -71,10 +109,10 @@ public class JCodeAttribute extends JAttribute { } protected void writeContentsTo(DataOutputStream stream) throws IOException { - List/*<JExceptionHandler>*/ handlers = code.getExceptionHandlers(); + List/*<ExceptionHandler>*/ handlers = code.getExceptionHandlers(); stream.writeShort(code.getMaxStackSize()); - stream.writeShort(code.getOwner().getMaxLocals()); + stream.writeShort(owner.getMaxLocals()); code.writeTo(stream); @@ -82,7 +120,6 @@ public class JCodeAttribute extends JAttribute { Iterator handlerIt = handlers.iterator(); while (handlerIt.hasNext()) ((JCode.ExceptionHandler)handlerIt.next()).writeTo(stream); - JAttribute.writeTo(code.getAttributes(), stream); } } diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JCodeIterator.java b/src/fjbg/ch/epfl/lamp/fjbg/JCodeIterator.java index 34b38c828d..989c313c3e 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JCodeIterator.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JCodeIterator.java @@ -1,3 +1,7 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; @@ -6,8 +10,8 @@ import ch.epfl.lamp.util.ByteArray; /** * Iterator used to examine the contents of an instruction list. * - * @version 1.0 * @author Michel Schinz, Thomas Friedli + * @version 1.0 */ public class JCodeIterator { diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java b/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java index 0df4498d8e..ca2985c9e8 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java @@ -1,8 +1,14 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; -import java.util.*; -import java.io.*; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.HashMap; /** * Constant pool, holding constants for a Java class file. @@ -48,6 +54,8 @@ public class JConstantPool { switch (tag) { case CONSTANT_Utf8: e = new Utf8Entry(stream); + // no duplicates + entryToIndex.put(e, new Integer(currIndex)); break; case CONSTANT_Integer: e = new IntegerEntry(stream); @@ -93,17 +101,17 @@ public class JConstantPool { */ public String getEntryType(int tag) { switch (tag) { - case 4 : return "Utf8"; - case 5 : return "Integer"; - case 6 : return "Float"; - case 7 : return "Long"; - case 8 : return "Double"; - case 9 : return "Class"; - case 10 : return "String"; - case 11 : return "Fieldref"; - case 12 : return "Methodref"; - case 13 : return "InterfaceMethodref"; - case 14 : return "NameAndType"; + case CONSTANT_Utf8 : return "Utf8"; + case CONSTANT_Integer : return "Integer"; + case CONSTANT_Float : return "Float"; + case CONSTANT_Long : return "Long"; + case CONSTANT_Double : return "Double"; + case CONSTANT_Class : return "Class"; + case CONSTANT_String : return "String"; + case CONSTANT_Fieldref : return "Field"; + case CONSTANT_Methodref : return "Method"; + case CONSTANT_InterfaceMethodref : return "InterfaceMethod"; + case CONSTANT_NameAndType : return "NameAndType"; default : throw new Error("invalid constant pool tag : " + tag); } } @@ -112,11 +120,6 @@ public class JConstantPool { return addDescriptor(className.replace('.', '/')); } - public String lookupClass(int index) { - DescriptorEntry entry = (DescriptorEntry)lookupEntry(index); - return entry.getValue().replace('/', '.'); - } - public int addDescriptor(JReferenceType type) { return addDescriptor(type.getDescriptor()); } @@ -138,9 +141,9 @@ public class JConstantPool { } public int addMethodRef(boolean isClass, - String className, - String methodName, - String signature) { + String className, + String methodName, + String signature) { return addEntry(new FieldOrMethodRefEntryValue(isClass ? CONSTANT_Methodref : CONSTANT_InterfaceMethodref, @@ -190,14 +193,8 @@ public class JConstantPool { return addEntry(new Utf8Entry(value)); } - public String lookupUtf8(int index) { - Utf8Entry entry = (Utf8Entry)lookupEntry(index); - return entry.getValue(); - } - protected int addEntry(EntryValue e) { assert !frozen; - Integer idx = (Integer)entryToIndex.get(e); if (idx != null) return idx.intValue(); @@ -217,6 +214,9 @@ public class JConstantPool { return index; } + /// Lookup methods + ////////////////////////////////////////////////////////////////////// + public Entry lookupEntry(int index) { assert index > 0 && index < currIndex : "invalid index: " + index; @@ -225,10 +225,28 @@ public class JConstantPool { return indexToEntry[index]; } + public String lookupClass(int index) { + DescriptorEntry entry = (DescriptorEntry)lookupEntry(index); + return entry.getValue(); + } + + public String lookupNameAndType(int index) { + NameAndTypeEntry entry = (NameAndTypeEntry)lookupEntry(index); + return entry.getName()+":"+entry.getDescriptor(); + } + + public String lookupUtf8(int index) { + Utf8Entry entry = (Utf8Entry)lookupEntry(index); + return entry.getValue(); + } + + /// Output + ////////////////////////////////////////////////////////////////////// + public void writeTo(DataOutputStream stream) throws IOException { if (! frozen) freeze(); - stream.writeShort(currIndex); + stream.writeShort(currIndex); for (int i = 0; i < currIndex; ++i) { Entry entry = indexToEntry[i]; if (entry != null) { @@ -238,6 +256,23 @@ public class JConstantPool { } } + // Follows javap output format for constant pool. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(" Constant pool:"); + for (int i = 0; i < currIndex; ++i) { + Entry entry = indexToEntry[i]; + if (entry != null) { + if (i > 0) buf.append("\n"); + buf.append("const #"); + buf.append(i); + buf.append(" = "); + buf.append(entry); + } + } + buf.append("\n"); + return buf.toString(); + } + /// Classes for the various kinds of entries ////////////////////////////////////////////////////////////////////// @@ -246,6 +281,7 @@ public class JConstantPool { int getSize(); void writeContentsTo(DataOutputStream stream) throws IOException; + String toComment(String ownerClassName); } protected interface EntryValue extends Entry { @@ -280,6 +316,15 @@ public class JConstantPool { public void writeContentsTo(DataOutputStream stream) throws IOException { stream.writeInt(value); } + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer("int\t"); + buf.append(getValue()); + buf.append(";"); + return buf.toString(); + } + public String toComment(String ownerClassname) { + return "//int "+getValue(); + } } public class FloatEntry extends ChildlessEntry implements Entry { @@ -301,6 +346,15 @@ public class JConstantPool { public void writeContentsTo(DataOutputStream stream) throws IOException { stream.writeFloat(value); } + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer("float\t"); + buf.append(getValue()); + buf.append("f"); + return buf.toString(); + } + public String toComment(String ownerClassname) { + return "//float "+getValue()+"f"; + } } public class LongEntry extends ChildlessEntry implements Entry { @@ -322,6 +376,15 @@ public class JConstantPool { public void writeContentsTo(DataOutputStream stream) throws IOException { stream.writeLong(value); } + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer("long\t"); + buf.append(getValue()); + buf.append("l;"); + return buf.toString(); + } + public String toComment(String ownerClassname) { + return "//long "+getValue()+"l"; + } } public class DoubleEntry extends ChildlessEntry implements Entry { @@ -343,6 +406,14 @@ public class JConstantPool { public void writeContentsTo(DataOutputStream stream) throws IOException { stream.writeDouble(value); } + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer("double\t"); + buf.append(getValue()); + return buf.toString(); + } + public String toComment(String ownerClassname) { + return "//double "+getValue(); + } } public class Utf8Entry extends ChildlessEntry implements Entry { @@ -391,6 +462,14 @@ public class JConstantPool { else stream.writeUTF(value); } + // Follows javap output format for Utf8 pool entries. + public String toString() { return "Asciz\t"+escaped(getValue())+";"; } + public String toComment(String ownerClassname) { + return "//Asciz "+escaped(getValue()); + } + private String escaped(String s) { + return s.replace("\n", "\\n"); + } } abstract public class StringEntry implements Entry { @@ -412,6 +491,16 @@ public class JConstantPool { public void writeContentsTo(DataOutputStream stream) throws IOException { stream.writeShort(valueIndex); } + // Follows javap output format for String pool entries. + public String toString() { + return "String\t#"+valueIndex+";\t// "+escaped(getValue()); + } + public String toComment(String ownerClassname) { + return "//String "+escaped(getValue()); + } + private String escaped(String s) { + return s.replace("\n", "\\n"); + } } public class StringEntryValue extends StringEntry implements EntryValue { @@ -458,6 +547,25 @@ public class JConstantPool { public void writeContentsTo(DataOutputStream stream) throws IOException { stream.writeShort(nameIndex); } + // Follows javap output format for class pool entries. + public String toString() { + StringBuffer buf = new StringBuffer("class\t#"); + buf.append(nameIndex); + buf.append(";\t// "); + buf.append(getClassName()); + return buf.toString(); + } + public String toComment(String ownerClassname) { + return "//class "+getClassName(); + } + private String getClassName() { + StringBuffer buf = new StringBuffer(); + String value = getValue(); + if (value.startsWith("[")) buf.append("\""); + buf.append(value); + if (value.startsWith("[")) buf.append("\""); + return buf.toString(); + } } protected class DescriptorEntryValue @@ -522,6 +630,22 @@ public class JConstantPool { stream.writeShort(classIndex); stream.writeShort(nameAndTypeIndex); } + // Follows javap output format for field/method pool entries. + public String toString() { + return getEntryType(tag)+"\t#"+classIndex+".#"+nameAndTypeIndex+ + ";\t// "+getName("")+":"+signature; + } + public String toComment(String ownerClassName) { + return "//"+getEntryType(tag)+" "+getName(ownerClassName)+":"+signature; + } + private String getName(String ownerClassName) { + String name = getFieldOrMethodName(); + if (JMethod.INSTANCE_CONSTRUCTOR_NAME.equals(name)) + name = "\""+name+"\""; + if (!getClassName().equals(ownerClassName)) + name = getClassName()+"."+name; + return name; + } } protected class FieldOrMethodRefEntryValue @@ -597,6 +721,15 @@ public class JConstantPool { stream.writeShort(nameIndex); stream.writeShort(descriptorIndex); } + // Follows javap output format for name/type pool entries. + public String toString() { + String natName = getName(); + if (JMethod.INSTANCE_CONSTRUCTOR_NAME.equals(natName)) + natName = "\""+natName+"\""; + return "NameAndType\t#"+nameIndex+":#"+descriptorIndex+ + ";// "+natName+":"+getDescriptor(); + } + public String toComment(String ownerClassname) { return ""; } } protected class NameAndTypeEntryValue diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JConstantValueAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JConstantValueAttribute.java new file mode 100644 index 0000000000..6efd3d7d34 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JConstantValueAttribute.java @@ -0,0 +1,69 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ + +package ch.epfl.lamp.fjbg; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * ConstantValue attribute representing the value of a constant field. + * + * There can be no more than one ConstantValue attribute in the attributes + * table of a given field_info structure.. See section 4.8.2 of the JVM + * specification. + * + * @author Stephane Micheloud + * @version 1.0 + */ + +public class JConstantValueAttribute extends JAttribute { + /** Constant pool of the current classfile. */ + private JConstantPool pool; + + protected int constantValueIndex; + + public JConstantValueAttribute(FJBGContext context, + JClass clazz, + JField field) { + super(context, clazz); + this.pool = clazz.pool; + + assert field.getOwner() == clazz; + } + + public JConstantValueAttribute(FJBGContext context, + JClass clazz, + Object owner, // JField + String name, + int size, + DataInputStream stream) + throws IOException { + super(context, clazz, name); + this.pool = clazz.pool; + + this.constantValueIndex = stream.readShort(); + + assert name.equals(getName()); + } + + public String getName() { return "ConstantValue"; } + + // Follows javap output format for ConstantValue attribute. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(" Constant value: "); + buf.append(pool.lookupEntry(constantValueIndex)); + return buf.toString(); + } + + protected int getSize() { + return 2; // Short.SIZE + } + + protected void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeShort(constantValueIndex); + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JEnclosingMethodAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JEnclosingMethodAttribute.java index 4351e2fc7b..5536a3bbcd 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JEnclosingMethodAttribute.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JEnclosingMethodAttribute.java @@ -1,3 +1,7 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; @@ -6,30 +10,70 @@ import java.io.DataOutputStream; import java.io.IOException; /** - * Sourcefile attribute, which can be attached to class files to - * associate them with their enclosing method. A class may have - * at most one EclosingMethod attribute. + * EclosingMethod attribute + + * A class must have an EnclosingMethod attribute if and only if it is a + * local class or an anonymous class. A class may have no more than one + * EnclosingMethod attribute. See section 4.8.6 of the JVM specification. * - * @version 1.0 * @author Michel Schinz + * @version 1.0 */ public class JEnclosingMethodAttribute extends JAttribute { + /** Constant pool of the current classfile. */ + private JConstantPool pool; + protected final int classIdx; protected final int nameAndTypeIdx; public JEnclosingMethodAttribute(FJBGContext context, JClass clazz, - String className, String methodName, JType methodType) { + String className, + String methodName, + JType methodType) { super(context, clazz); - this.classIdx = clazz.getConstantPool().addClass(className); - this.nameAndTypeIdx = clazz.getConstantPool().addNameAndType(methodName, methodType.getSignature()); + this.pool = clazz.pool; + + this.classIdx = pool.addClass(className); + this.nameAndTypeIdx = pool.addNameAndType(methodName, methodType.getSignature()); + } + + public JEnclosingMethodAttribute(FJBGContext context, + JClass clazz, + Object owner, + String name, + int size, + DataInputStream stream) + throws IOException { + super(context, clazz, name); + this.pool = clazz.pool; + + this.classIdx = stream.readShort(); + this.nameAndTypeIdx = stream.readShort(); + + assert name.equals(getName()); } public String getName() { return "EnclosingMethod"; } + // Follows javap output format for EnclosingMethod attribute. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(" EnclosingMethod:"); + buf.append("\n #"); + buf.append(classIdx); + if (nameAndTypeIdx != 0) { + buf.append(" of #"); + buf.append(nameAndTypeIdx); + } + buf.append(";\t// "); + buf.append(pool.lookupEntry(classIdx)); + buf.append("\n"); + return buf.toString(); + } + protected int getSize() { - return 4; + return 4; // 2 * Short.SIZE } protected void writeContentsTo(DataOutputStream stream) throws IOException { diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JExceptionsAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JExceptionsAttribute.java new file mode 100644 index 0000000000..aee34de219 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JExceptionsAttribute.java @@ -0,0 +1,90 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ + +package ch.epfl.lamp.fjbg; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Exceptions attribute + + * This table is used by compilers to indicate which Exceptions a method + * is declared to throw. See section 2.6.4 of the JVM specification. + * + * @author Stephane Micheloud + * @version 1.0 + */ + +public class JExceptionsAttribute extends JAttribute { + /** Constant pool of the current classfile. */ + private JConstantPool pool; + + protected int[] indexTable; + protected int count; + + public JExceptionsAttribute(FJBGContext context, + JClass clazz, + JMethod owner) { + super(context, clazz); + this.pool = clazz.pool; + + this.count = 0; + this.indexTable = new int[8]; // some size > count + + assert clazz == owner.getOwner(); + } + + public JExceptionsAttribute(FJBGContext context, + JClass clazz, + Object owner, //JMethod + String name, + int size, + DataInputStream stream) + throws IOException { + super(context, clazz, name); + this.pool = clazz.pool; + + this.count = stream.readShort(); + this.indexTable = new int[count]; + for (int i = 0; i < count; ++i) + indexTable[i] = stream.readShort(); + + assert name.equals(getName()); + } + + public void addEntry(int classIndex) { + if (count >= indexTable.length) { + int[] newIT = new int[indexTable.length * 2]; + System.arraycopy(indexTable, 0, newIT, 0, indexTable.length); + indexTable = newIT; + } + indexTable[count++] = classIndex; + } + + public String getName() { return "Exceptions"; } + + // Follows javap output format for Exceptions attribute. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(" Exceptions: "); + for (int i = 0; i < indexTable.length; ++i) { + buf.append("\n throws "); + buf.append(JClass.toExternalName(pool.lookupClass(indexTable[i]))); + } + buf.append("\n"); + return buf.toString(); + } + + protected int getSize() { + return 2 + indexTable.length * 2; + } + + protected void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeShort(count); + for (int i = 0; i < count; ++i) + stream.writeShort(indexTable[i]); + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java b/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java index 69b522b622..8b0338ed29 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java @@ -1,3 +1,7 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JField.java b/src/fjbg/ch/epfl/lamp/fjbg/JField.java index 2c1e3063bf..1456157bc7 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JField.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JField.java @@ -1,3 +1,7 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; @@ -12,6 +16,7 @@ import java.io.IOException; */ public class JField extends JFieldOrMethod { + protected JField(FJBGContext context, JClass owner, int accessFlags, @@ -26,4 +31,32 @@ public class JField extends JFieldOrMethod { throws IOException { super(context, owner, stream); } + + // Follows javap output format for fields. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(flagsToString()); + buf.append(toExternalName(getType())); + buf.append(" "); + buf.append(getName()); + buf.append(";\n"); + java.util.Iterator attrsIt = attributes.iterator(); + while (attrsIt.hasNext()) { + JAttribute attrs = (JAttribute)attrsIt.next(); + buf.append(attrs); + } + return buf.toString(); + } + + private String flagsToString() { + StringBuffer buf = new StringBuffer(); + if (isPublic()) buf.append("public "); + else if (isProtected()) buf.append("protected "); + else if (isPrivate()) buf.append("private "); + if (isStatic()) buf.append("static "); + else if (isTransient()) buf.append("transient "); + else if (isVolatile()) buf.append("volatile "); + if (isAbstract()) buf.append("abstract "); + else if (isFinal()) buf.append("final "); + return buf.toString(); + } } diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java b/src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java index fec7310bdf..3d2bf87264 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java @@ -1,11 +1,20 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; -import java.io.*; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; /** * Abstract superclass for a Java field or method. * + * No two methods of fields in one class file may have the same name and + * descriptor. See sections 4.6 and 4.7 of the JVM specification. + * * @author Michel Schinz * @version 1.0 */ @@ -26,9 +35,8 @@ abstract public class JFieldOrMethod extends JMember { this.owner = owner; this.type = type; - JConstantPool pool = owner.getConstantPool(); - nameIndex = pool.addUtf8(name); - signatureIndex = pool.addUtf8(type.getSignature()); + nameIndex = owner.pool.addUtf8(name); + signatureIndex = owner.pool.addUtf8(type.getSignature()); } protected JFieldOrMethod(FJBGContext context, @@ -104,6 +112,15 @@ abstract public class JFieldOrMethod extends JMember { return (accessFlags & JAccessFlags.ACC_STRICT) != 0; } + // 1.5 specifics + public boolean isBridge() { + return (accessFlags & JAccessFlags.ACC_BRIDGE) != 0; + } + + public boolean hasVarargs() { + return (accessFlags & JAccessFlags.ACC_VARARGS) != 0; + } + public void writeTo(DataOutputStream stream) throws IOException { if (! frozen) { try { diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java index ac69d24947..200bf1713b 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java @@ -1,96 +1,201 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ + package ch.epfl.lamp.fjbg; +import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.util.*; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; /** - * InnerClasses attribute. See section 4.7.5 of the JVM - * Specification. + * InnerClasses attribute. + * + * The ClassFile structure of a class/interface C must have exactly one + * InnerClasses attribute in its attributes table if the constant pool of C + * contains a CONSTANT_Class_info entry which represents a class or interface + * that is not a member of a package. See section 4.8.5 of the JVM Specification. * - * @author Iulian Dragos + * @author Iulian Dragos, Stephane Micheloud + * @version 1.1 */ public class JInnerClassesAttribute extends JAttribute { - /** InnerClass entries */ - private Map/*<InnerClassEntry>*/ entries = new LinkedHashMap(); + /** Constant pool of the current classfile. */ + private JConstantPool pool; - /** Constant pool of the current classfile. */ - private JConstantPool pool; + /** InnerClass entries */ + private Map/*<String, Entry>*/ entries = new LinkedHashMap(); + + public JInnerClassesAttribute(FJBGContext context, JClass clazz) { + super(context, clazz); + this.pool = clazz.pool; + } public JInnerClassesAttribute(FJBGContext context, - JClass clazz) { - super(context, clazz); - this.pool = clazz.pool; + JClass clazz, + Object owner, + String name, + int size, + DataInputStream stream) + throws IOException { + super(context, clazz, name); + this.pool = clazz.pool; + + String inner = null; + int count = stream.readShort(); + for (int i = 0; i < count; ++i) { + int innerIdx = stream.readShort(); + int outerIdx = stream.readShort(); + int nameIdx = stream.readShort(); + int flags = stream.readShort(); + inner = pool.lookupClass(innerIdx); + entries.put(inner, new Entry(innerIdx, outerIdx, nameIdx, flags)); + } + + assert name.equals(getName()); } public void addEntry(String inner, String outer, String name, int flags) { - int inIdx = pool.addClass(inner); - int ouIdx = 0; - if (outer != null) ouIdx = pool.addClass(outer); - int nIdx = 0; - if (name != null) nIdx = pool.addUtf8(name); - - Entry e = new Entry(inIdx, ouIdx, nIdx, flags); - - if (entries.containsKey(inner)) { - Entry other = (Entry) entries.get(inner); - assert other.outerInfo == e.outerInfo && other.originalName == e.originalName && other.innerFlags == e.innerFlags - : inner + "already declared as " + other; - } else - entries.put(inner, e); + int innerIdx = pool.addClass(inner); + int outerIdx = 0; + if (outer != null) outerIdx = pool.addClass(outer); + int nameIdx = 0; + if (name != null) nameIdx = pool.addUtf8(name); + + Entry e = new Entry(innerIdx, outerIdx, nameIdx, flags); + + if (entries.containsKey(inner)) { + Entry other = (Entry) entries.get(inner); + assert other.outerInfo == e.outerInfo && other.originalName == e.originalName && other.innerFlags == e.innerFlags + : inner + " already declared as " + other; + } else + entries.put(inner, e); } public String getName() { return "InnerClasses"; } - protected int getSize() { - return 2 + entries.size() * 8; - } - - protected void writeContentsTo(DataOutputStream stream) throws IOException { - stream.writeShort(entries.size()); - for (Iterator it = entries.values().iterator(); it.hasNext(); ) { - Entry e = (Entry)it.next(); - stream.writeShort(e.innerInfo); - stream.writeShort(e.outerInfo); - stream.writeShort(e.originalName); - stream.writeShort(e.innerFlags); - } - } - - /** An entry in the InnerClasses attribute, as defined by the JVM Spec. */ - private class Entry { - /** CONSTANT_Class_info index in the pool for the inner class (mangled). */ - int innerInfo; - - /** CONSTANT_Class_info index in the pool for the outer class (mangled). */ - int outerInfo; - - /** CONSTANT_Utf8_info index in the pool for the original name of the inner class. */ - int originalName; - - /** Short int for modifier flags. */ - int innerFlags; - - public Entry(int iI, int oI, int oN, int f) { - this.innerInfo = iI; - this.outerInfo = oI; - this.originalName = oN; - this.innerFlags = f; - } - - public Entry(String innerClass, String outerClass, String name, int flags) { - this(pool.addClass(innerClass),pool.addClass(outerClass), pool.addUtf8(name), flags); - } - - /** Two entries are equal if they refer to the same inner class. - * innerInfo represents a unique name (mangled). - */ - public boolean equals(Object other) { - if (other instanceof Entry) { - Entry otherEntry = (Entry) other; - return otherEntry.innerInfo == this.innerInfo; - } - return false; - } - } + // Follows javap output format for the InnerClass attribute. + /*@Override*/ public String toString() { + // Here we intentionally use "InnerClass" as javap :-( + StringBuffer buf = new StringBuffer(" InnerClass: "); + for (Iterator it = entries.values().iterator(); it.hasNext(); ) { + Entry e = (Entry)it.next(); + buf.append("\n "); + buf.append(e.innerFlagsToString()); + buf.append("#"); + if (e.originalName != 0) { + buf.append(e.originalName); + buf.append("= #"); + } + buf.append(e.innerInfo); + if (e.outerInfo != 0) { + buf.append(" of #"); + buf.append(e.outerInfo); + } + buf.append("; //"); + if (e.originalName != 0) { + buf.append(pool.lookupUtf8(e.originalName)); + buf.append("="); + } + buf.append("class "); + buf.append(pool.lookupClass(e.innerInfo)); + if (e.outerInfo != 0) { + buf.append(" of class "); + buf.append(pool.lookupClass(e.outerInfo)); + } + } + buf.append("\n"); + return buf.toString(); + } + + protected int getSize() { + return 2 + entries.size() * 8; + } + + protected void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeShort(entries.size()); + for (Iterator it = entries.values().iterator(); it.hasNext(); ) { + Entry e = (Entry)it.next(); + stream.writeShort(e.innerInfo); + stream.writeShort(e.outerInfo); + stream.writeShort(e.originalName); + stream.writeShort(e.innerFlags); + } + } + + /** An entry in the InnerClasses attribute, as defined by the JVM Spec. */ + private class Entry { + /** CONSTANT_Class_info index in the pool for the inner class (mangled). */ + int innerInfo; + + /** CONSTANT_Class_info index in the pool for the outer class (mangled). */ + int outerInfo; + + /** CONSTANT_Utf8_info index in the pool for the original name of the inner class. */ + int originalName; + + /** Short int for modifier flags. */ + int innerFlags; + + public Entry(int iI, int oI, int oN, int f) { + this.innerInfo = iI; + this.outerInfo = oI; + this.originalName = oN; + this.innerFlags = f; + } + + public Entry(String innerClass, String outerClass, String name, int flags) { + this(pool.addClass(innerClass), pool.addClass(outerClass), pool.addUtf8(name), flags); + } + + /** Two entries are equal if they refer to the same inner class. + * innerInfo represents a unique name (mangled). + */ + public boolean equals(Object other) { + if (other instanceof Entry) { + Entry otherEntry = (Entry) other; + return otherEntry.innerInfo == this.innerInfo; + } + return false; + } + + public String innerFlagsToString() { + StringBuffer buf = new StringBuffer(); + if (isPublic()) buf.append("public "); + else if (isProtected()) buf.append("protected "); + else if (isPrivate()) buf.append("private "); + //if (isStatic()) buf.append("static "); // as javap + if (isAbstract()) buf.append("abstract "); + else if (isFinal()) buf.append("final "); + return buf.toString(); + } + + private boolean isPublic() { + return (innerFlags & JAccessFlags.ACC_PUBLIC) != 0; + } + + private boolean isPrivate() { + return (innerFlags & JAccessFlags.ACC_PRIVATE) != 0; + } + + private boolean isProtected() { + return (innerFlags & JAccessFlags.ACC_PROTECTED) != 0; + } + + private boolean isStatic() { + return (innerFlags & JAccessFlags.ACC_STATIC) != 0; + } + + private boolean isFinal() { + return (innerFlags & JAccessFlags.ACC_FINAL) != 0; + } + + private boolean isAbstract() { + return (innerFlags & JAccessFlags.ACC_ABSTRACT) != 0; + } + } } diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JLabel.java b/src/fjbg/ch/epfl/lamp/fjbg/JLabel.java index 39d7147c42..77148c89aa 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JLabel.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JLabel.java @@ -1,11 +1,15 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; /** * Labels which can be attached to instructions. * - * @version 1.0 * @author Michel Schinz + * @version 1.0 */ public class JLabel { @@ -15,12 +19,12 @@ public class JLabel { public boolean isAnchored() { return anchor != UNDEFINED_ANCHOR; } public int getAnchor() { - assert isAnchored(); + assert isAnchored(); return anchor; } public void setAnchor(int anchor) { - assert !isAnchored(); + assert !isAnchored(); this.anchor = anchor; } } diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JLineNumberTableAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JLineNumberTableAttribute.java index 1403c34cf2..af7145939e 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JLineNumberTableAttribute.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JLineNumberTableAttribute.java @@ -1,16 +1,20 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; -import java.io.DataOutputStream; import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.IOException; /** * Attribute storing correspondance between instructions and source * line numbers. * - * @version 1.0 * @author Michel Schinz + * @version 1.0 */ public class JLineNumberTableAttribute extends JAttribute { @@ -32,8 +36,8 @@ public class JLineNumberTableAttribute extends JAttribute { int size, DataInputStream stream) throws IOException { - super(context, clazz); - code = (JCode)owner; + super(context, clazz, name); + this.code = (JCode)owner; int[] mapping = new int[code.getSize()]; @@ -44,6 +48,11 @@ public class JLineNumberTableAttribute extends JAttribute { mapping[startPC] = lineNum; } + // Avoids duplication of LineNumberTable attribute + // (see method ensureLineNumberCapacity in class JCode). + assert code.lineNumbers == null; + code.lineNumbers = new int[0]; + int lineNum = 0; for (int pc = 0; pc < mapping.length; ++pc) { if (mapping[pc] != 0) lineNum = mapping[pc]; @@ -55,6 +64,20 @@ public class JLineNumberTableAttribute extends JAttribute { public String getName() { return "LineNumberTable"; } + // Follows javap output format for LineNumberTable attribute. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(" LineNumberTable: "); + int[] encoding = encode(); + for (int i = 0; i < encoding.length/2; ++i) { + buf.append("\n line "); + buf.append(encoding[i * 2 + 1]); + buf.append(": "); + buf.append(encoding[i * 2]); + } + buf.append("\n"); + return buf.toString(); + } + protected int[] encoding; protected int[] encode() { if (encoding == null) { diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JLocalVariable.java b/src/fjbg/ch/epfl/lamp/fjbg/JLocalVariable.java index 9cbc8bb08a..ab2c8f2c72 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JLocalVariable.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JLocalVariable.java @@ -1,11 +1,15 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; /** * Representation of a local variable or method argument. * - * @version 1.0 * @author Michel Schinz + * @version 1.0 */ public class JLocalVariable { @@ -20,7 +24,7 @@ public class JLocalVariable { String name, int index) { this.owner = owner; - this.type = type; + this.type = type; this.name = name; this.index = index; @@ -31,4 +35,8 @@ public class JLocalVariable { public int getIndex() { return index; } public String getName() { return name; } public JType getType() { return type; } + + /*@Override*/ public String toString() { + return "0\t"+type.getSize()+"\t"+index+"\t"+name+"\t"+type; + } } diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JLocalVariableTableAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JLocalVariableTableAttribute.java new file mode 100644 index 0000000000..a091898c00 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JLocalVariableTableAttribute.java @@ -0,0 +1,167 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ + +package ch.epfl.lamp.fjbg; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedList; + +import ch.epfl.lamp.fjbg.JConstantPool.*; + +/** + * Attribute storing local variables. + * + * @author Stephane Micheloud + * @version 1.0 + */ + +public class JLocalVariableTableAttribute extends JAttribute { + /** Constant pool of the current classfile. */ + private JConstantPool pool; + + protected final LinkedList/*<Entry>*/ entries = new LinkedList(); + protected int localVariableIndex = 0; + + public JLocalVariableTableAttribute(FJBGContext context, + JClass clazz, + JCode code) { + super(context, clazz); + this.pool = clazz.pool; + + assert code.getOwner().getOwner() == clazz; + } + + public JLocalVariableTableAttribute(FJBGContext context, + JClass clazz, + Object owner, + String name, + int size, + DataInputStream stream) + throws IOException { + super(context, clazz, name); + this.pool = clazz.pool; + + int count = stream.readShort(); + for (int i = 0; i < count; ++i) { + int startPc = stream.readShort(); + int length = stream.readShort(); + int nameIndex = stream.readShort(); + int descIndex = stream.readShort(); + int index = stream.readShort(); + addEntry(startPc, length, nameIndex, descIndex, index); + } + + assert name.equals(getName()); + } + + public void addEntry(int startPc, int length, int nameIndex, + int descIndex, int index) { + entries.add(new Entry(startPc, length, nameIndex, descIndex, index)); + } + + public void addEntry(int startPc, int length, String name, + String desc, int index) { + Entry e = new Entry(startPc, length, name, desc, index); + Entry other = getEntry(index); + if (other != null) { + assert other.nameIndex == e.nameIndex && other.descIndex == e.descIndex + : e + " already declared as " + other; + } else + entries.add(e); + } + + public void addEntry(int startPc, int length, String name, String desc) { + entries.add(new Entry(startPc, length, name, desc)); + } + + public String getName() { return "LocalVariableTable"; } + + // Follows javap output format for LocalVariableTable attribute. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(" LocalVariableTable: "); + buf.append("\n Start Length Slot Name Signature"); + for (Iterator it = entries.iterator(); it.hasNext(); ) { + buf.append("\n "); + Entry e = (Entry)it.next(); + Utf8Entry name = (Utf8Entry)pool.lookupEntry(e.nameIndex); + Utf8Entry sig = (Utf8Entry)pool.lookupEntry(e.descIndex); + buf.append(e.startPc); + buf.append(" "); + buf.append(e.length); + buf.append(" "); + buf.append(e.index); + buf.append(" "); + buf.append(name.getValue()); + buf.append(" "); + buf.append(sig.getValue()); + } + buf.append("\n"); + return buf.toString(); + } + + public int getMaxLocals() { + return localVariableIndex; + } + + public int getSize() { + return 2 + entries.size() * 10; + } + + protected void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeShort(entries.size()); + for (Iterator it = entries.iterator(); it.hasNext(); ) { + Entry e = (Entry)it.next(); + stream.writeShort(e.startPc); + stream.writeShort(e.length); + stream.writeShort(e.nameIndex); + stream.writeShort(e.descIndex); + stream.writeShort(e.index); + } + } + + private Entry getEntry(int index) { + Entry e = null; + try { e = (Entry)entries.get(index); } catch (Exception ex) {} + return e; + } + + private class Entry { + int startPc; + int length; + int nameIndex; + int descIndex; + int index; + + public Entry(int startPc, int length, int nameIndex, int descIndex, int index) { + this.startPc = startPc; + this.length = length; + this.nameIndex = nameIndex; + this.descIndex = descIndex; + this.index = index; + localVariableIndex += length; + } + + public Entry(int startPc, int length, String name, String desc, int index) { + this(startPc, length, pool.addUtf8(name), pool.addUtf8(desc), index); + } + + public Entry(int startPc, int length, String name, String desc) { + this(startPc, length, pool.addUtf8(name), pool.addUtf8(desc), localVariableIndex); + } + + /** Two entries are equal if they refer to the same index. + */ + public boolean equals(Object other) { + if (other instanceof Entry) { + Entry otherEntry = (Entry) other; + return otherEntry.index == this.index; + } + return false; + } + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JMember.java b/src/fjbg/ch/epfl/lamp/fjbg/JMember.java index 8d082fb90d..70d6189b4b 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JMember.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JMember.java @@ -1,9 +1,13 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; -import java.util.List; -import java.util.LinkedList; import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; /** * Abstract superclass for a Java class, field or method. @@ -95,4 +99,11 @@ abstract public class JMember { return null; } + protected static String toExternalName(String name) { + return name.replace('/', '.'); + } + + protected static String toExternalName(JType tpe) { + return tpe.toString().replace(':', '.'); + } } diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JMethod.java b/src/fjbg/ch/epfl/lamp/fjbg/JMethod.java index 804b4314f2..ad35c76da3 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JMethod.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JMethod.java @@ -1,29 +1,35 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; -import java.util.LinkedList; -import java.util.Iterator; import java.io.DataInputStream; import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; /** * Representation of a Java method. * - * @version 1.0 * @author Michel Schinz + * @version 1.0 */ public class JMethod extends JFieldOrMethod { public final static String CLASS_CONSTRUCTOR_NAME = "<clinit>"; public final static String INSTANCE_CONSTRUCTOR_NAME = "<init>"; - protected final JCode code; + protected /*final*/ JCode code; protected final String[] argNames; protected final LinkedList/*<JLocalVariable>*/ localVariables = new LinkedList(); protected int localVariableIndex = 0; + protected JMethod(FJBGContext context, JClass owner, int accessFlags, @@ -43,7 +49,6 @@ public class JMethod extends JFieldOrMethod { if (isAbstract() || isNative()) { code = null; } else { - JConstantPool pool = owner.getConstantPool(); code = context.JCode(owner, this); addAttribute(context.JCodeAttribute(owner, this)); @@ -61,19 +66,23 @@ public class JMethod extends JFieldOrMethod { throws IOException { super(context, owner, stream); - // Fetch code from the attributes. - setCode: { - Iterator attrIt = attributes.iterator(); - while (attrIt.hasNext()) { - Object attr = attrIt.next(); - if (attr instanceof JCodeAttribute) { - code = ((JCodeAttribute)attr).code; - break setCode; - } + assert isAbstract() || isNative() || code != null; + + int n = 0; + if (code != null) { + for (Iterator it = code.getAttributes().iterator(); it.hasNext(); ) { + JAttribute attr = (JAttribute)it.next(); + if (attr instanceof JLocalVariableTableAttribute) + n = ((JLocalVariableTableAttribute)attr).getMaxLocals(); } - code = null; } - argNames = null; // TODO get from attribute + this.localVariableIndex = n; + + + JType[] argTypes = ((JMethodType)getType()).getArgumentTypes(); + argNames = new String[argTypes.length]; // TODO get from attribute + for (int i = 0; i < argNames.length; ++i) + argNames[i] = "v"+i; } public void freeze() throws JCode.OffsetTooBigException { @@ -89,6 +98,12 @@ public class JMethod extends JFieldOrMethod { return ((JMethodType)type).getArgumentTypes(); } + public int getArgsSize() { + int size = ((JMethodType)type).getArgsSize(); + if (!isStatic()) size += 1; // for this + return size; + } + public String[] getArgumentNames() { return argNames; } @@ -98,6 +113,12 @@ public class JMethod extends JFieldOrMethod { return code; } + // Invoked by the JCode constructor + protected void setCode(JCode code) { + assert null == this.code; + this.code = code; + } + public JCodeIterator codeIterator() { return new JCodeIterator(code); } @@ -126,7 +147,53 @@ public class JMethod extends JFieldOrMethod { .toArray(new JLocalVariable[localVariables.size()]); } + public int getMaxLocals() { return localVariableIndex; } + + // Follows javap output format for methods. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(flagsToString()); + String name = getName(); + if (CLASS_CONSTRUCTOR_NAME.equals(name)) + buf.append("{}"); + else { + if (INSTANCE_CONSTRUCTOR_NAME.equals(name)) + name = getOwner().getName(); + else { + buf.append(toExternalName(getReturnType())); + buf.append(" "); + } + buf.append(toExternalName(name)); + buf.append("("); + JType[] ts = getArgumentTypes(); + for (int i = 0; i < ts.length; ++i) { + if (i > 0) buf.append(", "); + buf.append(toExternalName(ts[i])); + } + buf.append(")"); + } + buf.append(";\n"); + Iterator it = attributes.iterator(); + while(it.hasNext()) { + JAttribute attr = (JAttribute)it.next(); + buf.append(attr); + } + return buf.toString(); + } + + private String flagsToString() { + StringBuffer buf = new StringBuffer(); + if (isPublic()) buf.append("public "); + else if (isProtected()) buf.append("protected "); + else if (isPrivate()) buf.append("private "); + if (isBridge()) buf.append("<bridge> "); + if (hasVarargs()) buf.append("<varargs> "); + if (isStatic()) buf.append("static "); + else if (isNative()) buf.append("native "); + if (isAbstract()) buf.append("abstract "); + else if (isFinal()) buf.append("final "); + return buf.toString(); + } } diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JMethodType.java b/src/fjbg/ch/epfl/lamp/fjbg/JMethodType.java index ec44967e27..1d2952b84e 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JMethodType.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JMethodType.java @@ -1,3 +1,7 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; @@ -5,8 +9,8 @@ package ch.epfl.lamp.fjbg; * Type for Java methods. These types do not really exist in Java, but * are provided here because they are useful in several places. * - * @version 1.0 * @author Michel Schinz + * @version 1.0 */ public class JMethodType extends JType { diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JObjectType.java b/src/fjbg/ch/epfl/lamp/fjbg/JObjectType.java index b4edb86fec..b8567e8d30 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JObjectType.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JObjectType.java @@ -1,11 +1,15 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; /** * Types for Java objects. * - * @version 1.0 * @author Michel Schinz + * @version 1.0 */ public class JObjectType extends JReferenceType { diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JOpcode.java b/src/fjbg/ch/epfl/lamp/fjbg/JOpcode.java index f7ee688784..4c971a1838 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JOpcode.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JOpcode.java @@ -1,3 +1,7 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JOtherAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JOtherAttribute.java index 7a64f91ee9..dcee9c2c13 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JOtherAttribute.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JOtherAttribute.java @@ -1,11 +1,16 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; -import java.io.*; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; /** - * Attributes which are unknown to the JVM (or at least to this - * library). + * Attributes which are unknown to the JVM (or at least to this library). * * @author Michel Schinz * @version 1.0 @@ -45,9 +50,28 @@ public class JOtherAttribute extends JAttribute { public String getName() { return name; } + // Follows javap output format for user-defined attributes. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(" "); + buf.append(name); + buf.append(": length = 0x"); + buf.append(Integer.toHexString(length).toUpperCase()); + for (int i = 0; i < length; ++i) { + if (i % 16 == 0) buf.append("\n "); + buf.append(hexString(contents[i])); + buf.append(" "); + } + buf.append("\n"); + return buf.toString(); + } + protected int getSize() { return length; } protected void writeContentsTo(DataOutputStream stream) throws IOException { stream.write(contents, 0, length); } + + private static final String hexString(int i) { + return ((0 <= i && i < 16) ? "0" : "")+Integer.toHexString(i).toUpperCase(); + } } diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JReferenceType.java b/src/fjbg/ch/epfl/lamp/fjbg/JReferenceType.java index 0ed6ef4dea..646b0f878d 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JReferenceType.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JReferenceType.java @@ -1,11 +1,15 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; /** * Types for Java references, i.e. arrays and objects. * - * @version 1.0 * @author Michel Schinz + * @version 1.0 */ abstract public class JReferenceType extends JType { diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JSourceFileAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JSourceFileAttribute.java index e4478728ae..46090b77e9 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JSourceFileAttribute.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JSourceFileAttribute.java @@ -1,3 +1,7 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; @@ -9,8 +13,11 @@ import java.io.IOException; * Sourcefile attribute, which can be attached to class files to * associate them with their source file. * - * @version 1.0 + * There can be no more than one SourceFile attribute in the attributes table + * of a given ClassFile structure. See section 4.8.9 of the JVM specification. + * * @author Michel Schinz + * @version 1.0 */ public class JSourceFileAttribute extends JAttribute { @@ -32,8 +39,8 @@ public class JSourceFileAttribute extends JAttribute { int size, DataInputStream stream) throws IOException { - super(context, clazz); - stream.readInt(); // ignore size + super(context, clazz, name); + this.sourceFileIndex = stream.readShort(); this.sourceFileName = clazz.getConstantPool().lookupUtf8(sourceFileIndex); @@ -42,8 +49,18 @@ public class JSourceFileAttribute extends JAttribute { public String getName() { return "SourceFile"; } + public String getFileName() { return sourceFileName; } + + // Follows javap output format for SourceFile attribute. + /*@Override*/ public String toString() { + StringBuffer buf = new StringBuffer(" SourceFile: \""); + buf.append(sourceFileName); + buf.append("\"\n"); + return buf.toString(); + } + protected int getSize() { - return 2; + return 2; // Short.SIZE } protected void writeContentsTo(DataOutputStream stream) throws IOException { diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JStackMapTableAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JStackMapTableAttribute.java new file mode 100644 index 0000000000..da7f00bf3e --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JStackMapTableAttribute.java @@ -0,0 +1,282 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ + +package ch.epfl.lamp.fjbg; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * + * @author Stephane Micheloud + * @version 1.0 + */ + +public class JStackMapTableAttribute extends JAttribute { + /** Constant pool of the current classfile. */ + private JConstantPool pool; + + /** StackMapTable entries */ + protected final List/*<Frame>*/ entries = new ArrayList(); + protected int entriesSize = 0; + protected boolean usesU2; + + public JStackMapTableAttribute(FJBGContext context, + JClass clazz, + JCode code) { + super(context, clazz); + this.pool = clazz.pool; + + assert code.getOwner().getOwner() == clazz; + } + + public JStackMapTableAttribute(FJBGContext context, + JClass clazz, + Object owner, + String name, + int size, + DataInputStream stream) + throws IOException { + super(context, clazz, name); + this.pool = clazz.pool; + + int count = stream.readShort(); + this.usesU2 = count < 65536; + for (int i = 0; i < count; ++i) + this.entries.add(new Frame(stream)); + this.entriesSize = computeSize(); + + assert name.equals(getName()); + } + + public String getName() { return "StackMapTable"; } + + // Follows javap output format for StackMapTable attribute. + /*@Override*/ public String toString() { + Frame frame = null; + StringBuffer buf = new StringBuffer(" StackMapTable: number_of_entries = "); + buf.append(entries.size()); + Iterator it = entries.iterator(); + while (it.hasNext()) { + frame = (Frame)it.next(); + buf.append("\n frame_type = "); + buf.append(frame.tag); + buf.append(" /* "); + buf.append(getFrameType(frame.tag)); + buf.append(" */"); + if (frame.offsetDelta != -1) + buf.append("\n offset_delta = "+frame.offsetDelta); + if (frame.locals != null) + appendTypeInfoArray(buf, "locals", frame.locals); + if (frame.stackItems != null) + appendTypeInfoArray(buf, "stack", frame.stackItems); + } + buf.append("\n"); + return buf.toString(); + } + + protected int getSize() { + return entriesSize; + } + + protected void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeShort(entriesSize); + Iterator it = entries.iterator(); + while (it.hasNext()) { + Frame frame = (Frame)it.next(); + frame.writeContentsTo(stream); + } + } + + private class TypeInfo { + final int tag; + final int poolIndexOrOffset; // tag == 7 => poolIndex, tag = 8 => offset + private int bytes; + TypeInfo(DataInputStream stream) throws IOException { + int size = 1; + this.tag = stream.readByte(); + if (tag == 7) { // ITEM_Object; // 7 + poolIndexOrOffset = stream.readShort(); + size += 2; + } else if (tag == 8) { // ITEM_Uninitialized // 8 + poolIndexOrOffset = (usesU2) ? stream.readShort() : stream.readInt(); + size += (usesU2) ? 2 : 4; + } else + poolIndexOrOffset = -1; + this.bytes += size; + } + int getSize() { return bytes; } + void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeByte(tag); + if (tag == 7) { // ITEM_Object; // 7 + stream.writeShort(poolIndexOrOffset); + } else if (tag == 8) { // ITEM_Uninitialized // 8 + if (usesU2) stream.writeShort(poolIndexOrOffset); + else stream.writeInt(poolIndexOrOffset); + } + } + /*@Override*/ public String toString() { + switch (tag) { + case 0: // ITEM_Top + return "<top>"; + case 1: // ITEM_Integer + return "int"; + case 2: // ITEM_Float + return "float"; + case 3: // ITEM_Double + return "double"; + case 4: // ITEM_Long + return "long"; + case 5: // ITEM_Null + return "null"; + case 6: // ITEM_UninializedThis + return "this"; + case 7: // ITEM_Object + String name = pool.lookupClass(poolIndexOrOffset); + if (name.startsWith("[")) name = "\""+name+"\""; + return "class "+name; + case 8: // ITEM_Uninitialized + return "<uninitialized>"; + default: + return ""+tag; + } + } + } + + private class Frame { + final int tag; + int offsetDelta = -1; + TypeInfo[] stackItems = null; + TypeInfo[] locals = null; + private int bytes; + Frame(DataInputStream stream) throws IOException { + // The stack_map_frame structure consists of a one-byte tag + // followed by zero or more bytes. + this.tag = stream.readUnsignedByte(); + if (tag < 64) { // SAME; // 0-63 + //done + } else if (tag < 128) { // SAME_LOCALS_1_STACK_ITEM; // 64-127 + this.offsetDelta = tag - 64; + readStackItems(stream, 1); + } else if (tag < 248) { // reserved for future use. + assert false : "Tags in the range [128-247] are reserved for future use."; + } else if (tag < 251) { // CHOP; // 248-250 + int k = 251 - tag; + readOffsetDelta(stream); + } else if (tag == 251) { // SAME_FRAME_EXTENDED + readOffsetDelta(stream); + } else if (tag < 255) { // APPEND; // 252-254 + readOffsetDelta(stream); + readLocals(stream, tag - 251); + } else { // FULL_FRAME; // 255 + readOffsetDelta(stream); + readLocals(stream); + readStackItems(stream); + } + } + int getSize() { return bytes; } + void readOffsetDelta(DataInputStream stream) throws IOException { + this.offsetDelta = (usesU2) ? stream.readShort() : stream.readInt(); + this.bytes += (usesU2) ? 2 : 4; + } + int getOffsetDelta() { return offsetDelta; } + void readStackItems(DataInputStream stream, int k) throws IOException { + this.stackItems = new TypeInfo[k]; + for (int i = 0; i < k; ++i) { + stackItems[i] = new TypeInfo(stream); + this.bytes += stackItems[i].getSize(); + } + } + void readStackItems(DataInputStream stream) throws IOException { + int k = (usesU2) ? stream.readShort() : stream.readInt(); + this.bytes += (usesU2) ? 2 : 4; + readStackItems(stream, k); + } + void readLocals(DataInputStream stream, int k) throws IOException { + this.locals = new TypeInfo[k]; + for (int i = 0; i < k; ++i) { + locals[i] = new TypeInfo(stream); + this.bytes += locals[i].getSize(); + } + } + void readLocals(DataInputStream stream) throws IOException { + int k = (usesU2) ? stream.readShort() : stream.readInt(); + this.bytes += (usesU2) ? 2 : 4; + readLocals(stream, k); + } + void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeByte(tag); + if (tag < 64) { + //done + } else if (tag < 128) { // SAME; // 0-63 + assert stackItems.length == 1; + stackItems[0].writeContentsTo(stream); + } else if (tag < 248) { + assert false : "Tags in the range [128-247] are reserved for future use."; + } else if (tag < 251) { + if (usesU2) stream.writeShort(offsetDelta); + else stream.writeInt(offsetDelta); + } else if (tag == 251) { + if (usesU2) stream.writeShort(offsetDelta); + else stream.writeInt(offsetDelta); + } else if (tag < 255) { // APPEND; // 252-254 + if (usesU2) stream.writeShort(offsetDelta); + else stream.writeInt(offsetDelta); + for (int i = 0; i < locals.length; ++i) + locals[i].writeContentsTo(stream); + } else { + if (usesU2) stream.writeShort(offsetDelta); + else stream.writeInt(offsetDelta); + for (int i = 0; i < locals.length; ++i) + locals[i].writeContentsTo(stream); + for (int i = 0; i < stackItems.length; ++i) + stackItems[i].writeContentsTo(stream); + } + } + } + + private int computeSize() { + int size = (usesU2) ? 2 : 4; // number of frames + Iterator it = entries.iterator(); + while (it.hasNext()) { + Frame frame = (Frame)it.next(); + size += frame.getSize(); + } + return size; + } + + private static final String getFrameType(int tag) { + if (tag < 64) return "same"; + else if (tag < 128) return "same locals 1 stack item"; + else if (tag < 248) return "<reserved>"; + else if (tag < 251) return "chop"; + else if (tag == 251) return "same frame extended"; + else if (tag < 255) return "append"; + else return "full frame"; + } + + private static StringBuffer appendTypeInfoArray(StringBuffer buf, + String s, TypeInfo[] a) { + buf.append("\n "); + buf.append(s); + buf.append(" = "); + if (a.length > 0) { + buf.append("[ "); + for (int i = 0; i < a.length; ++i) { + if (i > 0) buf.append(", "); + buf.append(a[i]); + } + buf.append(" ]"); + } + else + buf.append("[]"); + return buf; + } + +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JType.java b/src/fjbg/ch/epfl/lamp/fjbg/JType.java index b926a59f5a..86649b07de 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JType.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JType.java @@ -1,9 +1,13 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.fjbg; -import java.util.*; -import java.io.StringReader; import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; /** * Representation of Java types. @@ -90,6 +94,11 @@ abstract public class JType { } } + /** + * A signature is a string representing the generic type of a field or + * method, or generic type information for a class declaration. + * See section 4.4.4 of the JVM specification. + */ public static JType parseSignature(String signature) { try { StringReader sigReader = new StringReader(signature); @@ -114,103 +123,103 @@ abstract public class JType { protected JType() {} public static JType VOID = new JType() { - public int getSize() { return 0; } - public String getSignature() { return "V"; } - public int getTag() { return T_VOID; } - public String toString() { return "void"; } - public boolean isCompatibleWith(JType other) { - throw new UnsupportedOperationException("type VOID is no real " - + "data type therefore " - + "cannot be assigned to " - + other.toString()); - } - }; + public int getSize() { return 0; } + public String getSignature() { return "V"; } + public int getTag() { return T_VOID; } + public String toString() { return "void"; } + public boolean isCompatibleWith(JType other) { + throw new UnsupportedOperationException("type VOID is no real " + + "data type therefore " + + "cannot be assigned to " + + other.toString()); + } + }; public static JType BOOLEAN = new JType() { - public int getSize() { return 1; } - public String getSignature() { return "Z"; } - public int getTag() { return T_BOOLEAN; } - public String toString() { return "boolean"; } - public boolean isValueType() { return true; } - public boolean isCompatibleWith(JType other) { - return other == BOOLEAN - || other == INT - || other == BYTE - || other == CHAR - || other == SHORT; - } - }; + public int getSize() { return 1; } + public String getSignature() { return "Z"; } + public int getTag() { return T_BOOLEAN; } + public String toString() { return "boolean"; } + public boolean isValueType() { return true; } + public boolean isCompatibleWith(JType other) { + return other == BOOLEAN + || other == INT + || other == BYTE + || other == CHAR + || other == SHORT; + } + }; public static JType BYTE = new JType() { - public int getSize() { return 1; } - public String getSignature() { return "B"; } - public int getTag() { return T_BYTE; } - public String toString() { return "byte"; } - public boolean isValueType() { return true; } - public boolean isCompatibleWith(JType other) { - return other == BOOLEAN - || other == INT - || other == BYTE - || other == CHAR - || other == SHORT; - } - }; + public int getSize() { return 1; } + public String getSignature() { return "B"; } + public int getTag() { return T_BYTE; } + public String toString() { return "byte"; } + public boolean isValueType() { return true; } + public boolean isCompatibleWith(JType other) { + return other == BOOLEAN + || other == INT + || other == BYTE + || other == CHAR + || other == SHORT; + } + }; public static JType CHAR = new JType() { - public int getSize() { return 1; } - public String getSignature() { return "C"; } - public int getTag() { return T_CHAR; } - public String toString() { return "char"; } - public boolean isValueType() { return true; } - public boolean isCompatibleWith(JType other) { - return other == BOOLEAN - || other == INT - || other == BYTE - || other == CHAR - || other == SHORT; - } - }; + public int getSize() { return 1; } + public String getSignature() { return "C"; } + public int getTag() { return T_CHAR; } + public String toString() { return "char"; } + public boolean isValueType() { return true; } + public boolean isCompatibleWith(JType other) { + return other == BOOLEAN + || other == INT + || other == BYTE + || other == CHAR + || other == SHORT; + } + }; public static JType SHORT = new JType() { - public int getSize() { return 1; } - public String getSignature() { return "S"; } - public int getTag() { return T_SHORT; } - public String toString() { return "short"; } - public boolean isValueType() { return true; } - public boolean isCompatibleWith(JType other) { - return other == BOOLEAN - || other == INT - || other == BYTE - || other == CHAR - || other == SHORT; - } - }; + public int getSize() { return 1; } + public String getSignature() { return "S"; } + public int getTag() { return T_SHORT; } + public String toString() { return "short"; } + public boolean isValueType() { return true; } + public boolean isCompatibleWith(JType other) { + return other == BOOLEAN + || other == INT + || other == BYTE + || other == CHAR + || other == SHORT; + } + }; public static JType INT = new JType() { - public int getSize() { return 1; } - public String getSignature() { return "I"; } - public int getTag() { return T_INT; } - public String toString() { return "int"; } - public boolean isValueType() { return true; } - public boolean isCompatibleWith(JType other) { - return other == BOOLEAN - || other == INT - || other == BYTE - || other == CHAR - || other == SHORT; - } - }; + public int getSize() { return 1; } + public String getSignature() { return "I"; } + public int getTag() { return T_INT; } + public String toString() { return "int"; } + public boolean isValueType() { return true; } + public boolean isCompatibleWith(JType other) { + return other == BOOLEAN + || other == INT + || other == BYTE + || other == CHAR + || other == SHORT; + } + }; public static JType FLOAT = new JType() { - public int getSize() { return 1; } - public String getSignature() { return "F"; } - public int getTag() { return T_FLOAT; } - public String toString() { return "float"; } - public boolean isValueType() { return true; } - public boolean isCompatibleWith(JType other) { - return other == FLOAT; - } - }; + public int getSize() { return 1; } + public String getSignature() { return "F"; } + public int getTag() { return T_FLOAT; } + public String toString() { return "float"; } + public boolean isValueType() { return true; } + public boolean isCompatibleWith(JType other) { + return other == FLOAT; + } + }; public static JType LONG = new JType() { public int getSize() { return 2; } @@ -252,37 +261,56 @@ abstract public class JType { }; public static JType ADDRESS = new JType() { - public int getSize() { return 1; } - public String getSignature() { - throw new UnsupportedOperationException("type ADDRESS is no usable " - + "data type and therefore " - + "has no signature"); - } - public int getTag() { return T_ADDRESS; } - public String toString() { return "<address>"; } - public boolean isCompatibleWith(JType other) { - return other == ADDRESS; - } - }; + public int getSize() { return 1; } + public String getSignature() { + throw new UnsupportedOperationException("type ADDRESS is no usable " + + "data type and therefore " + + "has no signature"); + } + public int getTag() { return T_ADDRESS; } + public String toString() { return "<address>"; } + public boolean isCompatibleWith(JType other) { + return other == ADDRESS; + } + }; public static JType UNKNOWN = new JType() { - public int getSize() { - throw new UnsupportedOperationException("type UNKNOWN is no real " - + "data type and therefore " - + "has no size"); - } - public String getSignature() { - throw new UnsupportedOperationException("type UNKNOWN is no real " - + "data type and therefore " - + "has no signature"); - } - public int getTag() { return T_UNKNOWN; } - public String toString() { return "<unknown>"; } - public boolean isCompatibleWith(JType other) { - throw new UnsupportedOperationException("type UNKNOWN is no real " - + "data type and therefore " - + "cannot be assigned to " - + other.toString()); - } - }; + public int getSize() { + throw new UnsupportedOperationException("type UNKNOWN is no real " + + "data type and therefore " + + "has no size"); + } + public String getSignature() { + throw new UnsupportedOperationException("type UNKNOWN is no real " + + "data type and therefore " + + "has no signature"); + } + public int getTag() { return T_UNKNOWN; } + public String toString() { return "<unknown>"; } + public boolean isCompatibleWith(JType other) { + throw new UnsupportedOperationException("type UNKNOWN is no real " + + "data type and therefore " + + "cannot be assigned to " + + other.toString()); + } + }; + + protected static String tagToString(int tag) { + switch (tag) { + case T_BOOLEAN : return "boolean"; + case T_CHAR : return "char"; + case T_FLOAT : return "float"; + case T_DOUBLE : return "double"; + case T_BYTE : return "byte"; + case T_SHORT : return "short"; + case T_INT : return "int"; + case T_LONG : return "long"; + case T_VOID : return "void"; // Non-standard + case T_ARRAY : return "[]"; + case T_OBJECT : return "Object"; + case T_UNKNOWN : return "<unknown>"; + case T_ADDRESS : return "<address>"; + default: return ""+tag; + } + } } diff --git a/src/fjbg/ch/epfl/lamp/fjbg/Main.java b/src/fjbg/ch/epfl/lamp/fjbg/Main.java new file mode 100644 index 0000000000..d8fcdacd95 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/Main.java @@ -0,0 +1,131 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ + +package ch.epfl.lamp.fjbg; + +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; + +/** + * Main program entry to execute the FJBG reader from the command line. + * + * The reader prints out the decoded data in the same output format as + * javap, the Java bytecode disassembler of the Sun J2SE SDK. + * + * @author Stephane Micheloud + * @version 1.1 + */ + +public class Main { + private static final String PRODUCT_STRING = "Fast Java Bytecode Generator"; + private static final String VERSION_STRING = "version 1.1"; + + private static final int ACTION_USAGE = 0; + private static final int ACTION_DONE = 1; + private static final int ACTION_PROCEED = 2; + + private static String classPath = "."; + private static String[] classNames = null; + + public static void main(String[] args) { + switch (parseArgs(args)) { + case ACTION_USAGE: printUsage(); break; + case ACTION_PROCEED: processClasses(); break; + default: + } + } + + private static void processClasses() { + FJBGContext fjbgContext = new FJBGContext(49, 0); + if (classNames.length > 0) + try { + for (int i = 0; i < classNames.length; ++i) + processClass(fjbgContext, classNames[i]); + } catch (IOException e) { + System.err.println(e.getMessage()); + } + else + System.err.println( + "No classes were specified on the command line. Try -help."); + } + + private static void processClass(FJBGContext fjbgContext, String className) + throws IOException { + InputStream in = getInputStream(className); + JClass jclass = fjbgContext.JClass(new DataInputStream(in)); + System.out.println(jclass); + in.close(); + } + + private static InputStream getInputStream(String className) throws IOException { + String name = null; + String[] paths = classPath.split(File.pathSeparator); + for (int i = 0; i < paths.length; ++i) { + File parent = new File(paths[i]); + if (parent.isDirectory()) { + name = className.replace('.', File.separatorChar)+".class"; + File f = new File(parent, name); + if (f.isFile()) return new FileInputStream(f); + } else if (paths[i].endsWith(".jar")) { + JarFile f = new JarFile(parent); + name = className.replace('.', '/')+".class"; + ZipEntry e = f.getEntry(name); + if (e != null) return f.getInputStream(e); + } + } + throw new IOException("ERROR:Could not find "+className); + } + + private static int parseArgs(String[] args) { + ArrayList/*<String>*/ classes = new ArrayList(); + String arg = null; + int action = ACTION_USAGE; + int i = 0, n = args.length; + while (i < n) { + arg = args[i]; + if (arg.equals("-classpath") && (i+1) < n) { + classPath = args[i+1]; i += 2; + } else if (arg.equals("-cp") && (i+1) < n) { + classPath = args[i+1]; i += 2; + } else if (arg.equals("-help")) { + i = n+1; + //} else if (arg.equals("-v")) { + // verbose = true; i += 1; + } else if (arg.equals("-version")) { + System.err.println(PRODUCT_STRING+" "+VERSION_STRING); + action = ACTION_DONE; i = n+1; + } else if (arg.startsWith("-")) { + System.err.println("invalid flag: "+arg); + i = n+1; + } else { + classes.add(arg); i += 1; + } + } + if (i == n && i > 0) { + classNames = (String[])classes.toArray(new String[classes.size()]); + action = ACTION_PROCEED; + } + return action; + } + + private static void printUsage() { + System.out.println("Usage: fjbg <options> <classes>"); + System.out.println(); + System.out.println("where possible options include:"); + System.out.println(" -cp <path> Specify where to find user class files"); + System.out.println(" -classpath <path> Specify where to find user class files"); + System.out.println(" -help Print a synopsis of standard options"); + System.out.println(" -version Version information"); + System.out.println(); + System.exit(1); + } +} + diff --git a/src/fjbg/ch/epfl/lamp/util/ByteArray.java b/src/fjbg/ch/epfl/lamp/util/ByteArray.java index d6b70485bc..0605ae7c20 100644 --- a/src/fjbg/ch/epfl/lamp/util/ByteArray.java +++ b/src/fjbg/ch/epfl/lamp/util/ByteArray.java @@ -1,7 +1,13 @@ +/* FJBG -- Fast Java Bytecode Generator + * Copyright 2002-2011 LAMP/EPFL + * @author Michel Schinz + */ package ch.epfl.lamp.util; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; /** * Array of bytes. @@ -16,17 +22,17 @@ public class ByteArray { protected final static int BYTE_BLOCK_MASK = BYTE_BLOCK_SIZE - 1; protected byte[][] data = new byte[][] { new byte[BYTE_BLOCK_SIZE] }; - protected int pos = 0; // The next free position. + protected int pos = 0; // The next free position. protected boolean frozen = false; public ByteArray() { } public ByteArray(InputStream stream, int size) throws IOException { - size = pos; - for (int block = 0; size > 0; ++block) { + pos = size; + for (int i = 0; size > 0; ++i) { int sizeToRead = Math.min(BYTE_BLOCK_SIZE, size); - stream.read(data[block], 0, sizeToRead); + stream.read(data[i], 0, sizeToRead); size -= sizeToRead; if (size > 0) addNewBlock(); @@ -40,7 +46,7 @@ public class ByteArray { } public int getSize() { - return pos; + return pos; } protected void addNewBlock() { @@ -64,42 +70,42 @@ public class ByteArray { } public void addU1(int i) { - assert i <= 0xFF : i; - addByte(i); + assert i <= 0xFF : i; + addByte(i); } public void addU2(int i) { - assert i <= 0xFFFF : i; + assert i <= 0xFFFF : i; - addByte(i >>> 8); - addByte(i & 0xFF); + addByte(i >>> 8); + addByte(i & 0xFF); } public void addU4(int i) { - addByte(i >>> 24); - addByte((i >>> 16) & 0xFF); - addByte((i >>> 8) & 0xFF); - addByte(i & 0xFF); + addByte(i >>> 24); + addByte((i >>> 16) & 0xFF); + addByte((i >>> 8) & 0xFF); + addByte(i & 0xFF); } public void putByte(int targetPos, int b) { assert !frozen; - assert targetPos < pos : targetPos + " >= " + pos; + assert targetPos < pos : targetPos + " >= " + pos; - data[targetPos >>> BYTE_BLOCK_BITS][targetPos & BYTE_BLOCK_MASK] = (byte)b; + data[targetPos >>> BYTE_BLOCK_BITS][targetPos & BYTE_BLOCK_MASK] = (byte)b; } public void putU2(int targetPos, int i) { - assert i < 0xFFFF : i; - putByte(targetPos, i >>> 8); - putByte(targetPos + 1, i & 0xFF); + assert i < 0xFFFF : i; + putByte(targetPos, i >>> 8); + putByte(targetPos + 1, i & 0xFF); } public void putU4(int targetPos, int i) { - putByte(targetPos, i >>> 24); - putByte(targetPos + 1, (i >>> 16) & 0xFF); - putByte(targetPos + 2, (i >>> 8) & 0xFF); - putByte(targetPos + 3, i & 0xFF); + putByte(targetPos, i >>> 24); + putByte(targetPos + 1, (i >>> 16) & 0xFF); + putByte(targetPos + 2, (i >>> 8) & 0xFF); + putByte(targetPos + 3, i & 0xFF); } public int getU1(int sourcePos) { @@ -131,9 +137,9 @@ public class ByteArray { public void writeTo(OutputStream stream) throws IOException { if (!frozen) freeze(); - for (int i = 0; i < data.length && data[i] != null; ++i) { - int len = Math.min(BYTE_BLOCK_SIZE, pos - (i << BYTE_BLOCK_BITS)); - stream.write(data[i], 0, len); - } + for (int i = 0; i < data.length && data[i] != null; ++i) { + int len = Math.min(BYTE_BLOCK_SIZE, pos - (i << BYTE_BLOCK_BITS)); + stream.write(data[i], 0, len); + } } } |