summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormichelou <michelou@epfl.ch>2010-12-30 23:32:18 +0000
committermichelou <michelou@epfl.ch>2010-12-30 23:32:18 +0000
commit096bc81a90b746ea3fa8a5bd8eb9009c08a71082 (patch)
tree5df06808f3f561311865a70adff6abbb8ca2ec07
parentc0bfbce726c9cb74154ee9f30166feb97bfd0331 (diff)
downloadscala-096bc81a90b746ea3fa8a5bd8eb9009c08a71082.tar.gz
scala-096bc81a90b746ea3fa8a5bd8eb9009c08a71082.tar.bz2
scala-096bc81a90b746ea3fa8a5bd8eb9009c08a71082.zip
fixed (and extended) bytecode reader
IMPORTANT NOTE Modifying code of the FJBG library is a very sensitive task since its write operations are eg. used by the Scala compiler. This SVN commit affects (should affect) only the read operations (which did never work) of the FJBG library. Changes include: - fixed several killer bugs in the bytecode reader (see below). - added missing input stream constructors in several Attribute classes (eg. JBootstrapInvokeDynamic, JEnclosingMethodAttribute, JInnerClassesAttribute) together with the corresponding class registrations in class JAttributeFactory. - added classes JExceptionsTable, JLocalVariableTableAttribute and JStackMapTableAttribute (including corresponding factory methods in class FJBGContext). - overriden method toString in most classes to support output format similar to javap (see below). - did some code cleanup (tabs, etc..). Bug fixes include: - fixed incorrect stream.readInt() in JSourceFileAttribute constructor. - fixed missing code.lineNumbers initialization in JLineNumberTableAttribute constructor. - fixed incorrect code in class util.ByteArray constructor (stream). - added method setCode in class JMethod to link them together. Output of decoded bytecode: The added toString() methods return javap-like formatted strings for the decoded data, eg. for the LocalVariableTable attribute you get the following output: ... LocalVariableTable: Start Length Slot Name Signature 0 6 0 this LTest$; 0 6 1 args [Ljava/lang/String; Executing the following Java code will produce decoded bytecode in a format similar to the command line "javap -p -v -classpath <cpath> <classes>": static void printClass(String filePath) throws Exception { FJBGContext fjbgContext = new FJBGContext(49, 0); DataInputStream in = new DataInputStream(new FileInputStream(filePath)); JClass jclass = fjbgContext.JClass(in); System.out.println(jclass); in.close(); } Mainly for providing a demonstrator of the FJBG reader we added to the FJBG library the main class ch.epfl.lamp.fjbg.Main which behaves similarly to javap, the class file disassembler of the J2SE SDK. For instance the following commands produce (more or less) the same output: ~$ scala ch.epfl.lamp.fjbg.Main -classpath classes 'Test javap -p -v$' ~$ -classpath classes 'Test $' In several cases fjbg.Main will provide more information for access flags (eg. brigde methods) and class file attributes (eg. enclosing methods). TODO - integration of FJBGContext.JLocalVariableTableAttribute into the JVM backend of the Scala compiler (cleaner handling of local variables). - The source code of the FJBG library is currently generated using the compiler option "-source 1.4"; moving to source release 1.5 would allow further code improvements like: List --> List<T> (cast removals) StringBuffer --> StringBuilder (faster implementation) /*@Override*/ --> @Override A A A A A A A A A A A A A A A A A A A A A A A A A A A A A
-rw-r--r--lib/fjbg.jar.desired.sha12
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/FJBGContext.java34
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java4
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JArrayType.java4
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JAttribute.java12
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JAttributeFactory.java16
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JBootstrapInvokeDynamic.java60
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JClass.java195
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JCode.java295
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JCodeAttribute.java63
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JCodeIterator.java6
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java189
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JConstantValueAttribute.java69
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JEnclosingMethodAttribute.java60
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JExceptionsAttribute.java90
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java4
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JField.java33
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java25
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java257
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JLabel.java10
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JLineNumberTableAttribute.java31
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JLocalVariable.java12
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JLocalVariableTableAttribute.java167
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JMember.java15
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JMethod.java99
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JMethodType.java6
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JObjectType.java6
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JOpcode.java4
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JOtherAttribute.java30
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JReferenceType.java6
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JSourceFileAttribute.java25
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JStackMapTableAttribute.java282
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JType.java264
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/Main.java131
-rw-r--r--src/fjbg/ch/epfl/lamp/util/ByteArray.java62
35 files changed, 2122 insertions, 446 deletions
diff --git a/lib/fjbg.jar.desired.sha1 b/lib/fjbg.jar.desired.sha1
index 25a98b9fe9..fd3def241f 100644
--- a/lib/fjbg.jar.desired.sha1
+++ b/lib/fjbg.jar.desired.sha1
@@ -1 +1 @@
-61cc3142a929ae79b7c905b2f094d429a837b4b2 ?fjbg.jar
+bd8e22a955eeb82671c5fdb8a7a14bc7f25e9eb1 ?fjbg.jar
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);
+ }
}
}