summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);
+ }
}
}