From 0f10ffedc8d0cdf8410cc6fc143a08a9333c9832 Mon Sep 17 00:00:00 2001 From: Gilles Dubochet Date: Mon, 21 Sep 2009 19:24:41 +0000 Subject: SABBUS now builds FJBG, which sources are now p... SABBUS now builds FJBG, which sources are now part of the Scala module. --- src/fjbg/ch/epfl/lamp/fjbg/FJBGContext.java | 170 +++ src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java | 32 + src/fjbg/ch/epfl/lamp/fjbg/JArrayType.java | 59 + src/fjbg/ch/epfl/lamp/fjbg/JAttribute.java | 75 ++ src/fjbg/ch/epfl/lamp/fjbg/JAttributeFactory.java | 91 ++ .../ch/epfl/lamp/fjbg/JBootstrapInvokeDynamic.java | 35 + src/fjbg/ch/epfl/lamp/fjbg/JClass.java | 320 +++++ src/fjbg/ch/epfl/lamp/fjbg/JCode.java | 1131 ++++++++++++++++++ src/fjbg/ch/epfl/lamp/fjbg/JCodeAttribute.java | 89 ++ src/fjbg/ch/epfl/lamp/fjbg/JCodeIterator.java | 374 ++++++ src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java | 608 ++++++++++ src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java | 614 ++++++++++ src/fjbg/ch/epfl/lamp/fjbg/JField.java | 30 + src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java | 122 ++ .../ch/epfl/lamp/fjbg/JInnerClassesAttribute.java | 89 ++ src/fjbg/ch/epfl/lamp/fjbg/JLabel.java | 27 + .../epfl/lamp/fjbg/JLineNumberTableAttribute.java | 99 ++ src/fjbg/ch/epfl/lamp/fjbg/JLocalVariable.java | 35 + src/fjbg/ch/epfl/lamp/fjbg/JMember.java | 99 ++ src/fjbg/ch/epfl/lamp/fjbg/JMethod.java | 133 ++ src/fjbg/ch/epfl/lamp/fjbg/JMethodType.java | 72 ++ src/fjbg/ch/epfl/lamp/fjbg/JObjectType.java | 53 + src/fjbg/ch/epfl/lamp/fjbg/JOpcode.java | 1264 ++++++++++++++++++++ src/fjbg/ch/epfl/lamp/fjbg/JOtherAttribute.java | 54 + src/fjbg/ch/epfl/lamp/fjbg/JReferenceType.java | 16 + .../ch/epfl/lamp/fjbg/JSourceFileAttribute.java | 53 + src/fjbg/ch/epfl/lamp/fjbg/JType.java | 289 +++++ src/fjbg/ch/epfl/lamp/util/ByteArray.java | 140 +++ 28 files changed, 6173 insertions(+) create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/FJBGContext.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JArrayType.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JAttribute.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JAttributeFactory.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JBootstrapInvokeDynamic.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JClass.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JCode.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JCodeAttribute.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JCodeIterator.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JField.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JLabel.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JLineNumberTableAttribute.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JLocalVariable.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JMember.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JMethod.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JMethodType.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JObjectType.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JOpcode.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JOtherAttribute.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JReferenceType.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JSourceFileAttribute.java create mode 100644 src/fjbg/ch/epfl/lamp/fjbg/JType.java create mode 100644 src/fjbg/ch/epfl/lamp/util/ByteArray.java (limited to 'src/fjbg/ch') diff --git a/src/fjbg/ch/epfl/lamp/fjbg/FJBGContext.java b/src/fjbg/ch/epfl/lamp/fjbg/FJBGContext.java new file mode 100644 index 0000000000..a2eb6e7fa1 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/FJBGContext.java @@ -0,0 +1,170 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * Context in which FJBG executes. Used both as a factory for most + * FJBG classes and as a repository for other factories. + * + * @author Michel Schinz + * @version 1.0 + */ + +public class FJBGContext { + /** Class file major version */ + final int MAJOR_VERSION; + + /** Class file minor version */ + final int MINOR_VERSION; + + public FJBGContext() { + this(45, 3); + } + + public FJBGContext(int major, int minor) { + MAJOR_VERSION = major; + MINOR_VERSION = minor; + } + + // Factory methods + ////////////////////////////////////////////////////////////////////// + + public JClass JClass(int accessFlags, + String name, + String superclassName, + String[] interfaceNames, + String sourceFileName) { + return new JClass(this, + accessFlags, + name, + superclassName, + interfaceNames, + sourceFileName); + } + + public JClass JClass(DataInputStream stream) + throws IOException { + return new JClass(this, stream); + } + + public JConstantPool JConstantPool() { + return new JConstantPool(this); + } + + public JConstantPool JConstantPool(DataInputStream stream) + throws IOException { + return new JConstantPool(this, stream); + } + + public JField JField(JClass owner, + int accessFlags, + String name, + JType type) { + return new JField(this, + owner, + accessFlags, + name, + type); + } + + public JField JField(JClass owner, DataInputStream stream) + throws IOException { + return new JField(this, owner, stream); + } + + public JMethod JMethod(JClass owner, + int accessFlags, + String name, + JType returnType, + JType[] argTypes, + String[] argNames) { + return new JMethod(this, + owner, + accessFlags, + name, + returnType, + argTypes, + argNames); + } + + public JMethod JMethod(JClass owner, + int accessFlags, + String name, + JMethodType type, + String[] argNames) { + return JMethod(owner, + accessFlags, + name, + type.getReturnType(), + type.getArgumentTypes(), + argNames); + } + + public JMethod JMethod(JClass owner, DataInputStream stream) + throws IOException { + return new JMethod(this, owner, stream); + } + + public JLocalVariable JLocalVariable(JMethod owner, + JType type, + String name, + int index) { + return new JLocalVariable(this, owner, type, name, index); + } + + public JCode JCode(JClass clazz, JMethod owner) { + return new JExtendedCode(this, clazz, owner); + } + + public JCode JCode(JClass clazz, JMethod owner, DataInputStream stream) + throws IOException { + return new JCode(this, clazz, owner, stream); + } + + public JAttributeFactory JAttributeFactory() { + return new JAttributeFactory(this); + } + + // Attributes + public JCodeAttribute JCodeAttribute(JClass clazz, JMethod owner) { + return new JCodeAttribute(this, clazz, owner); + } + + public JLineNumberTableAttribute JLineNumberTableAttribute(JClass clazz, + JCode owner) { + return new JLineNumberTableAttribute(this, clazz, owner); + } + + public JOtherAttribute JOtherAttribute(JClass clazz, + Object owner, + String name, + byte[] contents, + int length) { + return new JOtherAttribute(this, clazz, owner, name, contents, length); + } + + public JOtherAttribute JOtherAttribute(JClass clazz, + Object owner, + String name, + byte[] contents) { + return JOtherAttribute(clazz, owner, name, contents, contents.length); + } + + public JSourceFileAttribute JSourceFileAttribute(JClass clazz, + String sourceFileName) { + return new JSourceFileAttribute(this, clazz, sourceFileName); + } + + /// Repository + ////////////////////////////////////////////////////////////////////// + + protected JAttributeFactory jAttributeFactory = null; + public JAttributeFactory getJAttributeFactory() { + if (jAttributeFactory == null) + jAttributeFactory = JAttributeFactory(); + return jAttributeFactory; + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java b/src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java new file mode 100644 index 0000000000..8df4f9350e --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java @@ -0,0 +1,32 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +/** + * Definition of access flags for fields, methods and classes. + * + * @author Michel Schinz + * @version 1.0 + */ + +public interface JAccessFlags { + public static int ACC_PUBLIC = 0x0001; + public static int ACC_PRIVATE = 0x0002; + public static int ACC_PROTECTED = 0x0004; + public static int ACC_STATIC = 0x0008; + public static int ACC_FINAL = 0x0010; + public static int ACC_SUPER = 0x0020; + public static int ACC_VOLATILE = 0x0040; + public static int ACC_TRANSIENT = 0x0080; + public static int ACC_NATIVE = 0x0100; + public static int ACC_INTERFACE = 0x0200; + public static int ACC_ABSTRACT = 0x0400; + public static int ACC_STRICT = 0x0800; + public static int ACC_SYNTHETIC = 0x1000; + public static int ACC_ANNOTATION= 0x2000; + public static int ACC_ENUM = 0x4000; + + // 1.5 specifics + public static int ACC_BRIDGE = 0x0040; + public static int ACC_VARARGS = 0x0080; +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JArrayType.java b/src/fjbg/ch/epfl/lamp/fjbg/JArrayType.java new file mode 100644 index 0000000000..85662428b6 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JArrayType.java @@ -0,0 +1,59 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +/** + * Types for Java arrays. + * + * @author Michel Schinz + * @version 1.0 + */ + +public class JArrayType extends JReferenceType { + protected final JType elementType; + protected String signature = null; + + public JArrayType(JType elementType) { + this.elementType = elementType; + } + + public int getSize() { return 1; } + + public String getSignature() { + if (signature == null) + signature = "[" + elementType.getSignature(); + return signature; + } + + public String getDescriptor() { + return getSignature(); + } + + public int getTag() { return T_ARRAY; } + + public JType getElementType() { return elementType; } + + public String toString() { + return elementType.toString() + "[]"; + } + + public boolean isArrayType() { return true; } + + public boolean isCompatibleWith(JType other) { + if (other instanceof JObjectType) + return (JObjectType)other == JObjectType.JAVA_LANG_OBJECT; + else if (other instanceof JArrayType) + return elementType.isCompatibleWith(((JArrayType)other).elementType); + else return other == JType.REFERENCE; + } + + public static JArrayType BOOLEAN = new JArrayType(JType.BOOLEAN); + public static JArrayType BYTE = new JArrayType(JType.BYTE); + public static JArrayType CHAR = new JArrayType(JType.CHAR); + public static JArrayType SHORT = new JArrayType(JType.SHORT); + public static JArrayType INT = new JArrayType(JType.INT); + public static JArrayType FLOAT = new JArrayType(JType.FLOAT); + public static JArrayType LONG = new JArrayType(JType.LONG); + public static JArrayType DOUBLE = new JArrayType(JType.DOUBLE); + public static JArrayType REFERENCE = new JArrayType(JType.REFERENCE); +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JAttribute.java new file mode 100644 index 0000000000..981fbfc645 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JAttribute.java @@ -0,0 +1,75 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.io.*; +import java.util.*; + +/** + * Abstract superclass for attributes which can be attached to various + * parts of a class file. + * + * @author Michel Schinz + * @version 1.0 + */ + +public abstract class JAttribute { + protected final int nameIdx; + + static public void writeTo(List/**/ attrs, DataOutputStream stream) + throws IOException { + stream.writeShort(attrs.size()); + Iterator attrsIt = attrs.iterator(); + while (attrsIt.hasNext()) { + JAttribute attr = (JAttribute)attrsIt.next(); + attr.writeTo(stream); + } + } + + static public List/**/ readFrom(FJBGContext context, + JClass clazz, + Object owner, + DataInputStream stream) + throws IOException { + JAttributeFactory factory = context.getJAttributeFactory(); + int count = stream.readShort(); + ArrayList list = new ArrayList(count); + for (int i = 0; i < count; ++i) + list.add(factory.newInstance(clazz, owner, stream)); + return list; + } + + public JAttribute(FJBGContext context, JClass clazz) { + this.nameIdx = clazz.getConstantPool().addUtf8(getName()); + } + + public JAttribute(FJBGContext context, JClass clazz, String name) { + this.nameIdx = clazz.getConstantPool().addUtf8(name); + } + + abstract public String getName(); + + /** + * Write the attribute to a stream. + */ + public void writeTo(DataOutputStream stream) throws IOException { + int contentsSize = getSize(); + + stream.writeShort(nameIdx); + stream.writeInt(contentsSize); + int streamSizeBefore = stream.size(); + writeContentsTo(stream); + int streamSizeDiff = stream.size() - streamSizeBefore; + + assert contentsSize == streamSizeDiff + : "invalid size for attribute " + getName() + + " given: " + contentsSize + + " actual: " + streamSizeDiff; + } + + // Note: it is not legal to add data to the constant pool during + // the execution of any of the following two methods. + protected abstract int getSize(); + protected abstract void writeContentsTo(DataOutputStream stream) + throws IOException; +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JAttributeFactory.java b/src/fjbg/ch/epfl/lamp/fjbg/JAttributeFactory.java new file mode 100644 index 0000000000..a0f575e7d0 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JAttributeFactory.java @@ -0,0 +1,91 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.io.*; +import java.util.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +/** + * Extensible factory to build subclasses of JAttribute based on an + * attribute name. + * + * @author Michel Schinz + * @version 1.0 + */ + +public class JAttributeFactory { + protected FJBGContext context; + protected HashMap/**/ constructors = new HashMap(); + + protected final static Class[] CONSTRUCTOR_ARGS = new Class[] { + FJBGContext.class, + JClass.class, + Object.class, + String.class, + int.class, + DataInputStream.class + }; + + protected final static Constructor defaultDefaultConstructor; + static { + try { + defaultDefaultConstructor = + JOtherAttribute.class.getConstructor(CONSTRUCTOR_ARGS); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + protected final Constructor defaultConstructor; + + public JAttributeFactory(FJBGContext context, + Constructor defaultConstructor) { + this.context = context; + this.defaultConstructor = defaultConstructor; + registerClass("Code", JCodeAttribute.class); + registerClass("LineNumberTable", JLineNumberTableAttribute.class); + registerClass("SourceFile", JSourceFileAttribute.class); + } + + public JAttributeFactory(FJBGContext context) { + this(context, defaultDefaultConstructor); + } + + public void registerClass(String attributeName, + Class clazz) { + if (! JAttribute.class.isAssignableFrom(clazz)) + throw new IllegalArgumentException("Not a subclass of JAttribute: " + + clazz); + + try { + Constructor constr = clazz.getConstructor(CONSTRUCTOR_ARGS); + constructors.put(attributeName, constr); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("No appropriate constructor for " + + clazz); + } + } + + public JAttribute newInstance(JClass clazz, + Object owner, + DataInputStream stream) + throws IOException { + String name = clazz.getConstantPool().lookupUtf8(stream.readShort()); + Integer size = new Integer(stream.readInt()); + Constructor constr = (Constructor)constructors.get(name); + if (constr == null) constr = defaultConstructor; + + Object[] args = new Object[] { context, clazz, owner, name, size, stream }; + try { + return (JAttribute)constr.newInstance(args); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JBootstrapInvokeDynamic.java b/src/fjbg/ch/epfl/lamp/fjbg/JBootstrapInvokeDynamic.java new file mode 100644 index 0000000000..ad4acfc329 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JBootstrapInvokeDynamic.java @@ -0,0 +1,35 @@ +package ch.epfl.lamp.fjbg; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Iterator; + +/** + * BootstrapInvokeDynamic entry, as described by JSR 292 (invoke dynamic) + * + * @author Iulian Dragos + * + */ +public class JBootstrapInvokeDynamic extends JAttribute { + /** Constant pool of the current classfile. */ + private JConstantPool pool; + + private int classIndex = -1; + + public JBootstrapInvokeDynamic(FJBGContext context, + JClass clazz, String className) { + super(context, clazz); + this.pool = clazz.pool; + this.classIndex = pool.addClass(className); + } + + public String getName() { return "BootstrapInvokeDynamic"; } + + protected int getSize() { + return 2; + } + + protected void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeShort(classIndex); + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JClass.java b/src/fjbg/ch/epfl/lamp/fjbg/JClass.java new file mode 100644 index 0000000000..1542bb1104 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JClass.java @@ -0,0 +1,320 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.util.*; +import java.io.*; + +/** + * Representation of a Java class. + * + * @author Michel Schinz + * @version 1.0 + */ +public class JClass extends JMember { + + /** Magic number for Java class files. */ + public final static int MAGIC_NUMBER = 0xCAFEBABE; + + protected final JAttributeFactory attributeFactory; + + protected final String superclassName; + protected final String[] interfaceNames; + protected final String sourceFileName; + protected final JConstantPool pool; + + protected JBootstrapInvokeDynamic bootstrapClassAttr = null; + + public final static String[] NO_INTERFACES = new String[0]; + + protected final LinkedList/**/ methods = new LinkedList(); + protected final LinkedList/**/ fields = new LinkedList(); + + protected JInnerClassesAttribute innerClasses; + + protected int major; + protected int minor; + + /** + * Creates a new class with its access flags, name, superclass name, + * interfaces names and source file name initialized to a given value. + * The constructor also initializes the pool and adds a sourceFileName + * attribute to the class. + * @param accessFlags the int representing the access flags of the class. + * @param name the string representing the name of the class. + * @param superclassName the string representing the name of the class' + * superclass. + * @param interfaceNames the list of strings representing the names of the + * interfaces implemented by the class. + * @param sourceFileName name of the file from which the class was compiled. + */ + protected JClass(FJBGContext context, + int accessFlags, + String name, + String superclassName, + String[] interfaceNames, + String sourceFileName) { + super(context, accessFlags, name); + this.attributeFactory = context.getJAttributeFactory(); + + this.major = context.MAJOR_VERSION; + this.minor = context.MINOR_VERSION; + + this.superclassName = superclassName; + this.interfaceNames = interfaceNames; + this.sourceFileName = sourceFileName; + this.pool = context.JConstantPool(); + if (sourceFileName != null) + addAttribute(context.JSourceFileAttribute(this, sourceFileName)); + } + + protected JClass(FJBGContext context, DataInputStream stream) + throws IOException { + super(context); + this.attributeFactory = context.getJAttributeFactory(); + + int magic = stream.readInt(); + if (magic != MAGIC_NUMBER) + throw new IllegalArgumentException("invalid magic number: "+magic); + + minor = stream.readShort(); + major = stream.readShort(); + pool = context.JConstantPool(stream); + accessFlags = stream.readShort(); + + // This class, super class and interfaces + name = pool.lookupClass(stream.readShort()); + superclassName = pool.lookupClass(stream.readShort()); + interfaceNames = new String[stream.readShort()]; + for (int i = 0; i < interfaceNames.length; ++i) + interfaceNames[i] = pool.lookupClass(stream.readShort()); + + // Fields, methods and attributes + int fieldsCount = stream.readShort(); + for (int i = 0; i < fieldsCount; ++i) + addField(context.JField(this, stream)); + + int methodsCount = stream.readShort(); + for (int i = 0; i < methodsCount; ++i) + addMethod(context.JMethod(this, stream)); + + int attributesCount = stream.readShort(); + for (int i = 0; i < attributesCount; ++i) + addAttribute(attributeFactory.newInstance(this, this, stream)); + + sourceFileName = null; + } + + /** + * Gets the name of the class' superclass. + * @return The string representing the name of the class' superclass. + */ + public String getSuperclassName() { return superclassName; } + + /** + * Gets the names of the interfaces implemented by the class. + * @return The array containing the string representations of the + * names of the interfaces implemented by the class. + */ + public String[] getInterfaceNames() { return interfaceNames; } + + /** + * Gets the type of the objects that are instances of the class. + * @return The type of the instances of the class. + */ + public JType getType() { return new JObjectType(name); } + + public JClass getJClass() { return this; } + + /** + * Gets the version number of the class. + * @param major The int representing the major part of the version number + * of the class. + * @param minor The int representing the minor part of the version number + * of the class. + */ + public void setVersion(int major, int minor) { + assert !frozen; + this.major = major; + this.minor = minor; + } + + /** + * Gets the major part of the number describing the version of the class. + * @return The int representing the major part of the version number of + * the class. + */ + public int getMajorVersion() { return major; } + + /** + * Gets the minor part of the number describing the version of the class. + * @return The int representing the minor part of the version number of + * the class. + */ + public int getMinorVersion() { return minor; } + + /** + * Gets the constant pool of the class. + * @return The constant pool of the class. + */ + public JConstantPool getConstantPool() { return pool; } + + public JInnerClassesAttribute getInnerClasses() { + if (innerClasses == null) { + innerClasses = new JInnerClassesAttribute(context, this); + addAttribute(innerClasses); + } + return innerClasses; + } + + /** + * Decides if the class is an interface. + * @return The boolean representing if the class is an interface or not. + */ + public boolean isInterface() { + return (accessFlags & JAccessFlags.ACC_INTERFACE) != 0; + } + + public void addField(JField field) { + assert !frozen; + fields.add(field); + } + + /** + * Create and add a new field to the class. + */ + public JField addNewField(int accessFlags, String name, JType type) { + assert !frozen; + JField f = context.JField(this, accessFlags, name, type); + addField(f); + return f; + } + + protected void addMethod(JMethod method) { + assert !frozen; + methods.add(method); + } + + /** + * Create and add a new method to the class. + */ + public JMethod addNewMethod(int accessFlags, + String name, + JType returnType, + JType[] argTypes, + String[] argNames) { + assert !frozen; + JMethod m = context.JMethod(this, + accessFlags, + name, + returnType, + argTypes, + argNames); + addMethod(m); + return m; + } + + /** + * Remove a previously-added method. This makes no attempt at + * minimising the constant pool by removing all constants which + * were used only by this method. + */ + public void removeMethod(JMethod m) { + assert !frozen; + methods.remove(m); + } + + public JMethod[] getMethods() { + return (JMethod[])methods.toArray(new JMethod[methods.size()]); + } + + /** + * Freeze the contents of this class so that it can be written to + * a file. + */ + public void freeze() { + assert !frozen; + frozen = true; + } + + /** + * Writes the contents of the class to a file referenced by its name. + * @param fileName The name of the file in which the class must be written. + */ + public void writeTo(String fileName) throws IOException { + writeTo(new File(fileName)); + } + + /** + * Writes the contents of the class to a file. + * @param file The file in which the class must be written. + */ + public void writeTo(File file) throws IOException { + File parent = file.getParentFile(); + if (parent != null && !parent.isDirectory()) + if (!parent.mkdirs()) + throw new IOException("cannot create directory " + parent); + + FileOutputStream fStream = new FileOutputStream(file); + BufferedOutputStream bStream = new BufferedOutputStream(fStream); + DataOutputStream dStream = new DataOutputStream(bStream); + writeTo(dStream); + dStream.close(); + bStream.close(); + fStream.close(); + } + + public void setBootstrapClass(String bootstrapClass) { + assert bootstrapClassAttr == null; + bootstrapClassAttr = new JBootstrapInvokeDynamic(context, this, bootstrapClass); + addAttribute(bootstrapClassAttr); + } + + /** + * Writes the contents of the class to a data stream. + * @param stream The data stream in which the class must be written. + */ + 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]; + + 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()); + Iterator fieldsIt = fields.iterator(); + while (fieldsIt.hasNext()) + ((JField)fieldsIt.next()).writeTo(stream); + + stream.writeShort(methods.size()); + Iterator methodsIt = methods.iterator(); + while (methodsIt.hasNext()) + ((JMethod)methodsIt.next()).writeTo(stream); + + // Attributes + JAttribute.writeTo(attributes, stream); + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JCode.java b/src/fjbg/ch/epfl/lamp/fjbg/JCode.java new file mode 100644 index 0000000000..252186a18e --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JCode.java @@ -0,0 +1,1131 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.util.*; +import java.io.*; + +import ch.epfl.lamp.util.*; + +/** + * List of instructions, to which Java byte-code instructions can be + * added. + * + * @author Michel Schinz, Thomas Friedli + * @version 1.0 + */ + +public class JCode { + protected boolean frozen = false; + + protected final FJBGContext context; + protected final JMethod owner; + + protected final ByteArray codeArray; + + protected final LinkedList/**/ exceptionHandlers = + new LinkedList(); + + protected final JConstantPool pool; + + protected final ArrayList/**/ offsetToPatch = + new ArrayList(); + + protected static int UNKNOWN_STACK_SIZE = Integer.MIN_VALUE; + protected int maxStackSize = UNKNOWN_STACK_SIZE; + protected int[] stackProduction = null; + protected int[] stackSizes; + + protected JCode(FJBGContext context, JClass clazz, JMethod owner) { + this.context = context; + this.pool = clazz.getConstantPool(); + this.owner = owner; + this.codeArray = new ByteArray(); + } + + protected JCode(FJBGContext context, + JClass clazz, + JMethod owner, + DataInputStream stream) + throws IOException { + this.context = context; + this.pool = clazz.getConstantPool(); + this.owner = owner; + int size = stream.readInt(); + this.codeArray = new ByteArray(stream, size); + } + + /** + * Gets the program counter, which is defined as the address of the + * next instruction. + * @return The int representing the value of the program counter + */ + public int getPC() { + return codeArray.getSize(); + } + + /** + * Gets the size of the code + * @return The number of bytes of the code + */ + public int getSize() { + return codeArray.getSize(); + } + + /** + * Gets the method to which the code belongs + * @return The method to which the code belongs + */ + public JMethod getOwner() { + return owner; + } + + // Stack size + public int getMaxStackSize() { + if (maxStackSize == UNKNOWN_STACK_SIZE) + maxStackSize = computeMaxStackSize(); + return maxStackSize; + } + + // Freezing + ////////////////////////////////////////////////////////////////////// + + public void freeze() throws OffsetTooBigException { + assert !frozen; + patchAllOffset(); + codeArray.freeze(); + frozen = true; + } + + // Attributes + ////////////////////////////////////////////////////////////////////// + + protected final LinkedList/**/ attributes = new LinkedList(); + + public void addAttribute(JAttribute attr) { + attributes.add(attr); + } + + public List/**/ getAttributes() { + return attributes; + } + + // Emitting code + ////////////////////////////////////////////////////////////////////// + + public void emit(JOpcode opcode) { + setStackProduction(getPC(), opcode); + codeArray.addU1(opcode.code); + } + + public void emitNOP() { emit(JOpcode.NOP); } + + // Constant loading. + public void emitACONST_NULL() { emit(JOpcode.ACONST_NULL); } + public void emitICONST_M1() { emit(JOpcode.ICONST_M1); } + public void emitICONST_0() { emit(JOpcode.ICONST_0); } + public void emitICONST_1() { emit(JOpcode.ICONST_1); } + public void emitICONST_2() { emit(JOpcode.ICONST_2); } + public void emitICONST_3() { emit(JOpcode.ICONST_3); } + public void emitICONST_4() { emit(JOpcode.ICONST_4); } + public void emitICONST_5() { emit(JOpcode.ICONST_5); } + public void emitLCONST_0() { emit(JOpcode.LCONST_0); } + public void emitLCONST_1() { emit(JOpcode.LCONST_1); } + public void emitFCONST_0() { emit(JOpcode.FCONST_0); } + public void emitFCONST_1() { emit(JOpcode.FCONST_1); } + public void emitFCONST_2() { emit(JOpcode.FCONST_2); } + public void emitDCONST_0() { emit(JOpcode.DCONST_0); } + public void emitDCONST_1() { emit(JOpcode.DCONST_1); } + + public void emitBIPUSH(int b) { emitU1(JOpcode.BIPUSH, b); } + public void emitSIPUSH(int s) { emitU2(JOpcode.SIPUSH, s); } + public void emitLDC(int value) { + emitU1(JOpcode.LDC, pool.addInteger(value)); + } + public void emitLDC(float value) { + emitU1(JOpcode.LDC, pool.addFloat(value)); + } + public void emitLDC(String value) { + emitU1(JOpcode.LDC, pool.addString(value)); + } + public void emitLDC_W(int value) { + emitU1(JOpcode.LDC_W, pool.addInteger(value)); + } + public void emitLDC_W(float value) { + emitU1(JOpcode.LDC_W, pool.addFloat(value)); + } + public void emitLDC_W(String value) { + emitU1(JOpcode.LDC_W, pool.addString(value)); + } + public void emitLDC2_W(long value) { + emitU2(JOpcode.LDC2_W, pool.addLong(value)); + } + public void emitLDC2_W(double value) { + emitU2(JOpcode.LDC2_W, pool.addDouble(value)); + } + + // Loading variables. + public void emitILOAD(int index) { emitU1(JOpcode.ILOAD, index); } + public void emitLLOAD(int index) { emitU1(JOpcode.LLOAD, index); } + public void emitFLOAD(int index) { emitU1(JOpcode.FLOAD, index); } + public void emitDLOAD(int index) { emitU1(JOpcode.DLOAD, index); } + public void emitALOAD(int index) { emitU1(JOpcode.ALOAD, index); } + + public void emitILOAD_0() { emit(JOpcode.ILOAD_0); } + public void emitILOAD_1() { emit(JOpcode.ILOAD_1); } + public void emitILOAD_2() { emit(JOpcode.ILOAD_2); } + public void emitILOAD_3() { emit(JOpcode.ILOAD_3); } + public void emitLLOAD_0() { emit(JOpcode.LLOAD_0); } + public void emitLLOAD_1() { emit(JOpcode.LLOAD_1); } + public void emitLLOAD_2() { emit(JOpcode.LLOAD_2); } + public void emitLLOAD_3() { emit(JOpcode.LLOAD_3); } + public void emitFLOAD_0() { emit(JOpcode.FLOAD_0); } + public void emitFLOAD_1() { emit(JOpcode.FLOAD_1); } + public void emitFLOAD_2() { emit(JOpcode.FLOAD_2); } + public void emitFLOAD_3() { emit(JOpcode.FLOAD_3); } + public void emitDLOAD_0() { emit(JOpcode.DLOAD_0); } + public void emitDLOAD_1() { emit(JOpcode.DLOAD_1); } + public void emitDLOAD_2() { emit(JOpcode.DLOAD_2); } + public void emitDLOAD_3() { emit(JOpcode.DLOAD_3); } + public void emitALOAD_0() { emit(JOpcode.ALOAD_0); } + public void emitALOAD_1() { emit(JOpcode.ALOAD_1); } + public void emitALOAD_2() { emit(JOpcode.ALOAD_2); } + public void emitALOAD_3() { emit(JOpcode.ALOAD_3); } + + public void emitIALOAD() { emit(JOpcode.IALOAD); } + public void emitLALOAD() { emit(JOpcode.LALOAD); } + public void emitFALOAD() { emit(JOpcode.FALOAD); } + public void emitDALOAD() { emit(JOpcode.DALOAD); } + public void emitAALOAD() { emit(JOpcode.AALOAD); } + public void emitBALOAD() { emit(JOpcode.BALOAD); } + public void emitCALOAD() { emit(JOpcode.CALOAD); } + public void emitSALOAD() { emit(JOpcode.SALOAD); } + + // Storing variables. + public void emitISTORE(int index) { emitU1(JOpcode.ISTORE, index); } + public void emitLSTORE(int index) { emitU1(JOpcode.LSTORE, index); } + public void emitFSTORE(int index) { emitU1(JOpcode.FSTORE, index); } + public void emitDSTORE(int index) { emitU1(JOpcode.DSTORE, index); } + public void emitASTORE(int index) { emitU1(JOpcode.ASTORE, index); } + + public void emitISTORE_0() { emit(JOpcode.ISTORE_0); } + public void emitISTORE_1() { emit(JOpcode.ISTORE_1); } + public void emitISTORE_2() { emit(JOpcode.ISTORE_2); } + public void emitISTORE_3() { emit(JOpcode.ISTORE_3); } + public void emitLSTORE_0() { emit(JOpcode.LSTORE_0); } + public void emitLSTORE_1() { emit(JOpcode.LSTORE_1); } + public void emitLSTORE_2() { emit(JOpcode.LSTORE_2); } + public void emitLSTORE_3() { emit(JOpcode.LSTORE_3); } + public void emitFSTORE_0() { emit(JOpcode.FSTORE_0); } + public void emitFSTORE_1() { emit(JOpcode.FSTORE_1); } + public void emitFSTORE_2() { emit(JOpcode.FSTORE_2); } + public void emitFSTORE_3() { emit(JOpcode.FSTORE_3); } + public void emitDSTORE_0() { emit(JOpcode.DSTORE_0); } + public void emitDSTORE_1() { emit(JOpcode.DSTORE_1); } + public void emitDSTORE_2() { emit(JOpcode.DSTORE_2); } + public void emitDSTORE_3() { emit(JOpcode.DSTORE_3); } + public void emitASTORE_0() { emit(JOpcode.ASTORE_0); } + public void emitASTORE_1() { emit(JOpcode.ASTORE_1); } + public void emitASTORE_2() { emit(JOpcode.ASTORE_2); } + public void emitASTORE_3() { emit(JOpcode.ASTORE_3); } + + public void emitIASTORE() { emit(JOpcode.IASTORE); } + public void emitLASTORE() { emit(JOpcode.LASTORE); } + public void emitFASTORE() { emit(JOpcode.FASTORE); } + public void emitDASTORE() { emit(JOpcode.DASTORE); } + public void emitAASTORE() { emit(JOpcode.AASTORE); } + public void emitBASTORE() { emit(JOpcode.BASTORE); } + public void emitCASTORE() { emit(JOpcode.CASTORE); } + public void emitSASTORE() { emit(JOpcode.SASTORE); } + + // Stack manipulation. + public void emitPOP() { emit(JOpcode.POP); } + public void emitPOP2() { emit(JOpcode.POP2); } + public void emitDUP() { emit(JOpcode.DUP); } + public void emitDUP_X1() { emit(JOpcode.DUP_X1); } + public void emitDUP_X2() { emit(JOpcode.DUP_X2); } + public void emitDUP2() { emit(JOpcode.DUP2); } + public void emitDUP2_X1() { emit(JOpcode.DUP2_X1); } + public void emitDUP2_X2() { emit(JOpcode.DUP2_X2); } + public void emitSWAP() { emit(JOpcode.SWAP); } + + // Artithmetic and logic operations. + public void emitIADD() { emit(JOpcode.IADD); } + public void emitLADD() { emit(JOpcode.LADD); } + public void emitFADD() { emit(JOpcode.FADD); } + public void emitDADD() { emit(JOpcode.DADD); } + + public void emitISUB() { emit(JOpcode.ISUB); } + public void emitLSUB() { emit(JOpcode.LSUB); } + public void emitFSUB() { emit(JOpcode.FSUB); } + public void emitDSUB() { emit(JOpcode.DSUB); } + + public void emitIMUL() { emit(JOpcode.IMUL); } + public void emitLMUL() { emit(JOpcode.LMUL); } + public void emitFMUL() { emit(JOpcode.FMUL); } + public void emitDMUL() { emit(JOpcode.DMUL); } + + public void emitIDIV() { emit(JOpcode.IDIV); } + public void emitLDIV() { emit(JOpcode.LDIV); } + public void emitFDIV() { emit(JOpcode.FDIV); } + public void emitDDIV() { emit(JOpcode.DDIV); } + + public void emitIREM() { emit(JOpcode.IREM); } + public void emitLREM() { emit(JOpcode.LREM); } + public void emitFREM() { emit(JOpcode.FREM); } + public void emitDREM() { emit(JOpcode.DREM); } + + public void emitINEG() { emit(JOpcode.INEG); } + public void emitLNEG() { emit(JOpcode.LNEG); } + public void emitFNEG() { emit(JOpcode.FNEG); } + public void emitDNEG() { emit(JOpcode.DNEG); } + + public void emitISHL() { emit(JOpcode.ISHL); } + public void emitLSHL() { emit(JOpcode.LSHL); } + + public void emitISHR() { emit(JOpcode.ISHR); } + public void emitLSHR() { emit(JOpcode.LSHR); } + + public void emitIUSHR() { emit(JOpcode.IUSHR); } + public void emitLUSHR() { emit(JOpcode.LUSHR); } + + public void emitIAND() { emit(JOpcode.IAND); } + public void emitLAND() { emit(JOpcode.LAND); } + + public void emitIOR() { emit(JOpcode.IOR); } + public void emitLOR() { emit(JOpcode.LOR); } + + public void emitIXOR() { emit(JOpcode.IXOR); } + public void emitLXOR() { emit(JOpcode.LXOR); } + + public void emitIINC(int index, int increment) { + emitU1U1(JOpcode.IINC, index, increment); + } + + // (Numeric) type conversions. + public void emitI2L() { emit(JOpcode.I2L); } + public void emitI2F() { emit(JOpcode.I2F); } + public void emitI2D() { emit(JOpcode.I2D); } + public void emitL2I() { emit(JOpcode.L2I); } + public void emitL2F() { emit(JOpcode.L2F); } + public void emitL2D() { emit(JOpcode.L2D); } + public void emitF2I() { emit(JOpcode.F2I); } + public void emitF2L() { emit(JOpcode.F2L); } + public void emitF2D() { emit(JOpcode.F2D); } + public void emitD2I() { emit(JOpcode.D2I); } + public void emitD2L() { emit(JOpcode.D2L); } + public void emitD2F() { emit(JOpcode.D2F); } + public void emitI2B() { emit(JOpcode.I2B); } + public void emitI2C() { emit(JOpcode.I2C); } + public void emitI2S() { emit(JOpcode.I2S); } + + // Comparisons and tests. + public void emitLCMP() { emit(JOpcode.LCMP); } + public void emitFCMPL() { emit(JOpcode.FCMPL); } + public void emitFCMPG() { emit(JOpcode.FCMPG); } + public void emitDCMPL() { emit(JOpcode.DCMPL); } + public void emitDCMPG() { emit(JOpcode.DCMPG); } + + protected void emitGenericIF(JOpcode opcode, Label label) + throws OffsetTooBigException { + emitU2(opcode, label.getOffset16(getPC() + 1, getPC())); + } + + public void emitIFEQ(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IFEQ, label); + } + public void emitIFEQ(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IFEQ, targetPC - getPC()); + } + public void emitIFEQ() { + emitU2(JOpcode.IFEQ, 0); + } + + public void emitIFNE(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IFNE, label); + } + public void emitIFNE(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IFNE, targetPC - getPC()); + } + public void emitIFNE() { + emitU2(JOpcode.IFNE, 0); + } + + public void emitIFLT(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IFLT, label); + } + public void emitIFLT(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IFLT, targetPC - getPC()); + } + public void emitIFLT() { + emitU2(JOpcode.IFLT, 0); + } + + public void emitIFGE(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IFGE, label); + } + public void emitIFGE(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IFGE, targetPC - getPC()); + } + public void emitIFGE() { + emitU2(JOpcode.IFGE, 0); + } + + public void emitIFGT(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IFGT, label); + } + public void emitIFGT(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IFGT, targetPC - getPC()); + } + public void emitIFGT() { + emitU2(JOpcode.IFGT, 0); + } + + public void emitIFLE(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IFLE, label); + } + public void emitIFLE(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IFLE, targetPC - getPC()); + } + public void emitIFLE() { + emitU2(JOpcode.IFLE, 0); + } + + public void emitIF_ICMPEQ(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IF_ICMPEQ, label); + } + public void emitIF_ICMPEQ(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IF_ICMPEQ, targetPC - getPC()); + } + public void emitIF_ICMPEQ() { + emitU2(JOpcode.IF_ICMPEQ, 0); + } + + public void emitIF_ICMPNE(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IF_ICMPNE, label); + } + public void emitIF_ICMPNE(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IF_ICMPNE, targetPC - getPC()); + } + public void emitIF_ICMPNE() { + emitU2(JOpcode.IF_ICMPNE, 0); + } + + public void emitIF_ICMPLT(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IF_ICMPLT, label); + } + public void emitIF_ICMPLT(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IF_ICMPLT, targetPC - getPC()); + } + public void emitIF_ICMPLT() { + emitU2(JOpcode.IF_ICMPLT, 0); + } + + public void emitIF_ICMPGE(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IF_ICMPGE, label); + } + public void emitIF_ICMPGE(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IF_ICMPGE, targetPC - getPC()); + } + public void emitIF_ICMPGE() { + emitU2(JOpcode.IF_ICMPGE, 0); + } + + public void emitIF_ICMPGT(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IF_ICMPGT, label); + } + public void emitIF_ICMPGT(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IF_ICMPGT, targetPC - getPC()); + } + public void emitIF_ICMPGT() { + emitU2(JOpcode.IF_ICMPGT, 0); + } + + public void emitIF_ICMPLE(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IF_ICMPLE, label); + } + public void emitIF_ICMPLE(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IF_ICMPLE, targetPC - getPC()); + } + public void emitIF_ICMPLE() { + emitU2(JOpcode.IF_ICMPLE, 0); + } + + public void emitIF_ACMPEQ(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IF_ACMPEQ, label); + } + public void emitIF_ACMPEQ(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IF_ACMPEQ, targetPC - getPC()); + } + public void emitIF_ACMPEQ() { + emitU2(JOpcode.IF_ACMPEQ, 0); + } + + public void emitIF_ACMPNE(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IF_ACMPNE, label); + } + public void emitIF_ACMPNE(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IF_ACMPNE, targetPC - getPC()); + } + public void emitIF_ACMPNE() { + emitU2(JOpcode.IF_ACMPNE, 0); + } + + public void emitIFNULL(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IFNULL, label); + } + public void emitIFNULL(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IFNULL, targetPC - getPC()); + } + public void emitIFNULL() { + emitU2(JOpcode.IFNULL, 0); + } + + public void emitIFNONNULL(Label label) throws OffsetTooBigException { + emitGenericIF(JOpcode.IFNONNULL, label); + } + public void emitIFNONNULL(int targetPC) throws OffsetTooBigException { + emitU2(JOpcode.IFNONNULL, targetPC - getPC()); + } + public void emitIFNONNULL() { + emitU2(JOpcode.IFNONNULL, 0); + } + + public void emitGOTO(Label label) throws OffsetTooBigException { + emitU2(JOpcode.GOTO, label.getOffset16(getPC() + 1, getPC())); + } + public void emitGOTO(int targetPC) throws OffsetTooBigException { + int offset = targetPC - getPC(); + checkOffset16(offset); + emitU2(JOpcode.GOTO, offset); + } + public void emitGOTO() { + emitU2(JOpcode.GOTO, 0); + } + + public void emitGOTO_W(Label label) { + emitU4(JOpcode.GOTO_W, label.getOffset32(getPC() + 1, getPC())); + } + public void emitGOTO_W(int targetPC) { + emitU4(JOpcode.GOTO_W, targetPC - getPC()); + } + public void emitGOTO_W() { + emitU4(JOpcode.GOTO_W, 0); + } + + public void emitJSR(Label label) throws OffsetTooBigException { + emitU2(JOpcode.JSR, label.getOffset16(getPC() + 1, getPC())); + } + public void emitJSR(int targetPC) { + emitU2(JOpcode.JSR, targetPC - getPC()); + } + public void emitJSR() { + 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()); + } + public void emitJSR_W() { + emitU4(JOpcode.JSR_W, 0); + } + + /* + public void emitRET(Label label) throws OffsetTooBigException { + emitU2(JOpcode.RET, label.getOffset16(getPC() + 1, getPC())); + } + public void emitRET(int targetPC) { + emitU1(JOpcode.RET, targetPC); + } + public void emitRET() { + emitU1(JOpcode.RET, 0); + } + */ + + public void emitRET(int index) { + emitU1(JOpcode.RET, index); + } + + public void emitRET(JLocalVariable var) { + emitRET(var.getIndex()); + } + + public void emitTABLESWITCH(int[] keys, + Label[] branches, + Label defaultBranch) { + assert keys.length == branches.length; + + int low = keys[0], high = keys[keys.length - 1]; + int instrPC = getPC(); + + 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++) { + 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; + + 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[i].getOffset32(getPC(), instrPC)); + } + } + + public void emitIRETURN() { emit(JOpcode.IRETURN); } + public void emitLRETURN() { emit(JOpcode.LRETURN); } + public void emitFRETURN() { emit(JOpcode.FRETURN); } + public void emitDRETURN() { emit(JOpcode.DRETURN); } + public void emitARETURN() { emit(JOpcode.ARETURN); } + public void emitRETURN() { emit(JOpcode.RETURN); } + + // Field access + public void emitGETSTATIC(String className, String name, JType type) { + setStackProduction(getPC(), type.getSize()); + int index = pool.addFieldRef(className, name, type.getSignature()); + emitU2(JOpcode.GETSTATIC, index); + } + public void emitPUTSTATIC(String className, String name, JType type) { + setStackProduction(getPC(), -type.getSize()); + int index = pool.addFieldRef(className, name, type.getSignature()); + emitU2(JOpcode.PUTSTATIC, index); + } + public void emitGETFIELD(String className, String name, JType type) { + setStackProduction(getPC(), type.getSize() - 1); + int index = pool.addFieldRef(className, name, type.getSignature()); + emitU2(JOpcode.GETFIELD, index); + } + public void emitPUTFIELD(String className, String name, JType type) { + setStackProduction(getPC(), -(type.getSize() + 1)); + int index = pool.addFieldRef(className, name, type.getSignature()); + emitU2(JOpcode.PUTFIELD, index); + } + + // Method invocation + public void emitINVOKEVIRTUAL(String className, + String name, + JMethodType type) { + setStackProduction(getPC(), type.getProducedStack() - 1); + int index = + pool.addClassMethodRef(className, name, type.getSignature()); + emitU2(JOpcode.INVOKEVIRTUAL, index); + } + public void emitINVOKESPECIAL(String className, + String name, + JMethodType type) { + setStackProduction(getPC(), type.getProducedStack() - 1); + int index = + pool.addClassMethodRef(className, name, type.getSignature()); + emitU2(JOpcode.INVOKESPECIAL, index); + } + public void emitINVOKESTATIC(String className, + String name, + JMethodType type) { + setStackProduction(getPC(), type.getProducedStack()); + int index = + pool.addClassMethodRef(className, name, type.getSignature()); + emitU2(JOpcode.INVOKESTATIC, index); + } + public void emitINVOKEINTERFACE(String className, + String name, + JMethodType type) { + setStackProduction(getPC(), type.getProducedStack() - 1); + int index = + pool.addInterfaceMethodRef(className, name, type.getSignature()); + emitU2U1U1(JOpcode.INVOKEINTERFACE, index, type.getArgsSize() + 1, 0); + } + + // Object creation + public void emitNEW(String className) { + emitU2(JOpcode.NEW, pool.addClass(className)); + } + public void emitNEWARRAY(JType elemType) { + emitU1(JOpcode.NEWARRAY, elemType.getTag()); + } + public void emitANEWARRAY(JReferenceType elemType) { + emitU2(JOpcode.ANEWARRAY, pool.addDescriptor(elemType)); + } + public void emitMULTIANEWARRAY(JReferenceType elemType, int dimensions) { + setStackProduction(getPC(), -dimensions + 1); + emitU2U1(JOpcode.MULTIANEWARRAY, + pool.addDescriptor(elemType), + dimensions); + } + public void emitARRAYLENGTH() { emit(JOpcode.ARRAYLENGTH); } + + // Exception throwing + public void emitATHROW() { emit(JOpcode.ATHROW); } + + // Dynamic typing + public void emitCHECKCAST(JReferenceType type) { + emitU2(JOpcode.CHECKCAST, pool.addDescriptor(type)); + } + public void emitINSTANCEOF(JReferenceType type) { + emitU2(JOpcode.INSTANCEOF, pool.addDescriptor(type)); + } + + // Monitors + public void emitMONITORENTER() { emit(JOpcode.MONITORENTER); } + public void emitMONITOREXIT() { emit(JOpcode.MONITOREXIT); } + + // Wide variants + // FIXME setStackProd. will here raise an exception + public void emitWIDE(JOpcode opcode, int index) { + assert (opcode.code == JOpcode.cILOAD) + || (opcode.code == JOpcode.cLLOAD) + || (opcode.code == JOpcode.cFLOAD) + || (opcode.code == JOpcode.cDLOAD) + || (opcode.code == JOpcode.cALOAD) + || (opcode.code == JOpcode.cISTORE) + || (opcode.code == JOpcode.cLSTORE) + || (opcode.code == JOpcode.cFSTORE) + || (opcode.code == JOpcode.cDSTORE) + || (opcode.code == JOpcode.cASTORE) + || (opcode.code == JOpcode.cRET) + : "invalide opcode for WIDE: " + opcode; + + setStackProduction(getPC(), opcode); + codeArray.addU1(JOpcode.WIDE.code); + codeArray.addU1(opcode.code); + codeArray.addU2(index); + } + public void emitWIDE(JOpcode opcode, int index, int constant) { + assert opcode.code == JOpcode.cIINC + : "invalid opcode for WIDE: " + opcode; + + setStackProduction(getPC(), opcode); + codeArray.addU1(JOpcode.cWIDE); + codeArray.addU1(opcode.code); + codeArray.addU2(index); + codeArray.addU2(constant); + } + + protected void emitU1(JOpcode opcode, int i1) { + setStackProduction(getPC(), opcode); + codeArray.addU1(opcode.code); + 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); + } + + protected void emitU2(JOpcode opcode, int i1) { + setStackProduction(getPC(), opcode); + codeArray.addU1(opcode.code); + 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); + } + + 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); + } + + protected void emitU4(JOpcode opcode, int i1) { + setStackProduction(getPC(), opcode); + codeArray.addU1(opcode.code); + codeArray.addU4(i1); + } + + protected int getU1(int sourcePos) { + return codeArray.getU1(sourcePos); + } + + protected int getU2(int sourcePos) { + return codeArray.getU2(sourcePos); + } + + protected int getU4(int sourcePos) { + return codeArray.getU4(sourcePos); + } + + protected int getS1(int sourcePos) { + return codeArray.getS1(sourcePos); + } + + protected int getS2(int sourcePos) { + return codeArray.getS2(sourcePos); + } + + protected int getS4(int sourcePos) { + return codeArray.getS4(sourcePos); + } + + // Stack size computation + ////////////////////////////////////////////////////////////////////// + + protected int getStackProduction(int pc) { + if (stackProduction == null || pc >= stackProduction.length) + return UNKNOWN_STACK_SIZE; + else + return stackProduction[pc]; + } + + protected void setStackProduction(int pc, int production) { + if (stackProduction == null) { + 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; + } + } + 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()) + setStackProduction(pc, + opcode.getProducedDataSize() + - opcode.getConsumedDataSize()); + } + + protected int computeMaxStackSize() { + if (stackSizes == null) { + stackSizes = new int[getSize()]; + Arrays.fill(stackSizes, UNKNOWN_STACK_SIZE); + stackSizes[0] = 0; + } + int size = computeMaxStackSize(0, 0, 0); + + // compute stack sizes for exception handlers too + ExceptionHandler exh = null; + for (Iterator it = exceptionHandlers.iterator(); + it.hasNext();) { + exh = (ExceptionHandler)it.next(); + int exhSize = computeMaxStackSize(exh.getHandlerPC(), 1, 1); + if (size < exhSize) + size = exhSize; + } + + return size; + } + + protected int computeMaxStackSize(int pc, int stackSize, int maxStackSize) { + JCodeIterator iterator = new JCodeIterator(this, pc); + for (;;) { + int successors = iterator.getSuccessorCount(); + if (successors == 0) + return maxStackSize; + else { + assert stackProduction[iterator.getPC()] != UNKNOWN_STACK_SIZE + : "unknown stack production, pc=" + iterator.getPC() + + " in method " + owner.getName(); + stackSize += stackProduction[iterator.getPC()]; + if (stackSize > maxStackSize) + maxStackSize = stackSize; + int nextPC = -1; + for (int i = 0; i < successors; ++i) { + int succPC = iterator.getSuccessorPC(i); + assert succPC >= 0 && succPC < stackSizes.length + : iterator.getPC() + ": invalid pc: " + succPC + + " op: " + iterator.getOpcode(); + if (stackSizes[succPC] == UNKNOWN_STACK_SIZE) { + stackSizes[succPC] = stackSize; + if (nextPC == -1) + nextPC = succPC; + else + maxStackSize = computeMaxStackSize(succPC, + stackSize, + maxStackSize); + } + } + if (nextPC == -1) + return maxStackSize; + else + iterator.moveTo(nextPC); + } + } + } + + // Labels + ////////////////////////////////////////////////////////////////////// + + public static class OffsetTooBigException extends Exception { + public OffsetTooBigException() { super(); } + public OffsetTooBigException(String message) { super(message); } + } + + protected void checkOffset16(int offset) throws OffsetTooBigException { + if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) + throw new OffsetTooBigException("offset too big to fit" + + " in 16 bits: " + offset); + } + + public class Label { + protected boolean anchored = false; + protected int targetPC = 0; + + public void anchorToNext() { + assert !anchored; + this.targetPC = getPC(); + anchored = true; + } + + public int getAnchor() { + assert anchored; + return targetPC; + } + + protected int getOffset16(int pc, int instrPC) + throws OffsetTooBigException { + if (anchored) { + int offset = targetPC - instrPC; + checkOffset16(offset); + return offset; + } else { + recordOffsetToPatch(pc, 16, instrPC, this); + return 0; + } + } + + protected int getOffset32(int pc, int instrPC) { + if (anchored) + return targetPC - instrPC; + else { + recordOffsetToPatch(pc, 32, instrPC, this); + return 0; + } + } + } + + public Label newLabel() { + return new Label(); + } + + public Label[] newLabels(int count) { + Label[] labels = new Label[count]; + for (int i = 0; i < labels.length; ++i) + labels[i] = newLabel(); + return labels; + } + + protected static class OffsetToPatch { + public final int pc; + public final int size; + public final int instrPC; + public final Label label; + + public OffsetToPatch(int pc, int size, int instrPC, Label label) { + this.pc = pc; + this.size = size; + this.instrPC = instrPC; + this.label = label; + } + } + + protected void recordOffsetToPatch(int offsetPC, + int size, + int instrPC, + Label label) { + offsetToPatch.add(new OffsetToPatch(offsetPC, size, instrPC, label)); + } + + protected void patchAllOffset() throws OffsetTooBigException { + Iterator offsetIt = offsetToPatch.iterator(); + while (offsetIt.hasNext()) { + OffsetToPatch offset = (OffsetToPatch)offsetIt.next(); + int offsetValue = offset.label.getAnchor() - offset.instrPC; + if (offset.size == 16) { + checkOffset16(offsetValue); + codeArray.putU2(offset.pc, offsetValue); + } else + codeArray.putU4(offset.pc, offsetValue); + } + } + + // Exception handling + ////////////////////////////////////////////////////////////////////// + + public class ExceptionHandler { + protected int startPC, endPC, handlerPC; + protected final String catchType; + protected final int catchTypeIndex; + + public void setStartPC(int pc) { + this.startPC = pc; + } + + public int getStartPC() { + return this.startPC; + } + + public void setEndPC(int pc) { + this.endPC = pc; + } + + public int getEndPC() { + return this.endPC; + } + + public void setHandlerPC(int pc) { + this.handlerPC = pc; + } + + public int getHandlerPC() { + return this.handlerPC; + } + + public ExceptionHandler(String catchType) { + this(0, 0, 0, catchType); + } + + public ExceptionHandler(int startPC, + int endPC, + int handlerPC, + String catchType) { + this.startPC = startPC; + this.endPC = endPC; + this.handlerPC = handlerPC; + this.catchType = catchType; + this.catchTypeIndex = (catchType == null + ? 0 + : pool.addClass(catchType)); + } + + public ExceptionHandler(DataInputStream stream) throws IOException { + this.startPC = stream.readShort(); + this.endPC = stream.readShort(); + this.handlerPC = stream.readShort(); + this.catchTypeIndex = stream.readShort(); + this.catchType = (catchTypeIndex == 0 + ? null + : pool.lookupClass(catchTypeIndex)); + } + + public void writeTo(DataOutputStream stream) throws IOException { + stream.writeShort(startPC); + stream.writeShort(endPC); + stream.writeShort(handlerPC); + stream.writeShort(catchTypeIndex); + } + } + + public void addExceptionHandler(ExceptionHandler handler) { + assert !frozen; + exceptionHandlers.add(handler); + } + + public void addExceptionHandler(int startPC, + int endPC, + int handlerPC, + String catchType) { + addExceptionHandler(new ExceptionHandler(startPC, + endPC, + handlerPC, + catchType)); + } + + public void addFinallyHandler(int startPC, int endPC, int handlerPC) { + assert !frozen; + addExceptionHandler(startPC, endPC, handlerPC, null); + } + + public List/**/ getExceptionHandlers() { + return exceptionHandlers; + } + + // Line numbers + ////////////////////////////////////////////////////////////////////// + + protected int[] lineNumbers = null; + protected void ensureLineNumberCapacity(int endPC) { + assert !frozen; + if (lineNumbers == null) { + lineNumbers = new int[endPC]; + addAttribute(context.JLineNumberTableAttribute(owner.getOwner(), + this)); + } else if (lineNumbers.length < endPC) { + int[] newLN = new int[Math.max(endPC, lineNumbers.length * 2)]; + System.arraycopy(lineNumbers, 0, newLN, 0, lineNumbers.length); + lineNumbers = newLN; + } + } + + /** + * Set all line numbers in the interval [startPC, endPC) to + * line, overwriting existing line numbers. + */ + public void setLineNumber(int startPC, int endPC, int line) { + ensureLineNumberCapacity(endPC); + Arrays.fill(lineNumbers, startPC, endPC, line); + } + + public void setLineNumber(int instrPC, int line) { + setLineNumber(instrPC, instrPC + 1, line); + } + + /** Sets all non-filled line numbers in the interval [startPC, endPC) + * to 'line'. + */ + public void completeLineNumber(int startPC, int endPC, int line) { + ensureLineNumberCapacity(endPC); + for (int pc = startPC; pc < endPC; ++pc) + if (lineNumbers[pc] == 0) lineNumbers[pc] = line; + } + + public int[] getLineNumbers() { + assert frozen; + if (lineNumbers == null) return new int[0]; + else if (lineNumbers.length == getPC()) return lineNumbers; + else { + int[] trimmedLN = new int[getPC()]; + System.arraycopy(lineNumbers, 0, + trimmedLN, 0, + Math.min(lineNumbers.length, trimmedLN.length)); + return trimmedLN; + } + } + + // Output + public void writeTo(DataOutputStream stream) throws IOException { + assert frozen; + stream.writeInt(getSize()); + codeArray.writeTo(stream); + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JCodeAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JCodeAttribute.java new file mode 100644 index 0000000000..79afaded44 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JCodeAttribute.java @@ -0,0 +1,89 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.util.*; +import java.io.*; + +/** + * Code attribute, containing code of methods. + * + * @author Michel Schinz + * @version 1.0 + */ + +public class JCodeAttribute extends JAttribute { + protected final JCode code; + + public JCodeAttribute(FJBGContext context, JClass clazz, JMethod owner) { + super(context, clazz); + this.code = owner.getCode(); + + assert clazz == owner.getOwner(); + } + + public JCodeAttribute(FJBGContext context, + JClass clazz, + Object owner, + String name, + int size, + DataInputStream stream) + throws IOException { + super(context, clazz); + + stream.readShort(); // skip max stack size + stream.readShort(); // skip max locals + + 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/**/ attributes = + JAttribute.readFrom(context, clazz, owner, stream); + Iterator attrIt = attributes.iterator(); + while (attrIt.hasNext()) + code.addAttribute((JAttribute)attrIt.next()); + + assert name.equals(getName()); + } + + public String getName() { return "Code"; } + + protected int getSize() { + int handlersNum = code.getExceptionHandlers().size(); + + int attrsSize = 0; + Iterator attrsIt = code.getAttributes().iterator(); + while (attrsIt.hasNext()) { + JAttribute attr = (JAttribute)attrsIt.next(); + attrsSize += attr.getSize() + 6; + } + + return 2 // max stack + + 2 // max locals + + 4 // code size + + code.getSize() // code + + 2 // exception table size + + 8 * handlersNum // exception table + + 2 // attributes count + + attrsSize; // attributes + } + + protected void writeContentsTo(DataOutputStream stream) throws IOException { + List/**/ handlers = code.getExceptionHandlers(); + + stream.writeShort(code.getMaxStackSize()); + stream.writeShort(code.getOwner().getMaxLocals()); + + code.writeTo(stream); + + stream.writeShort(handlers.size()); + 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 new file mode 100644 index 0000000000..d4effed412 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JCodeIterator.java @@ -0,0 +1,374 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import ch.epfl.lamp.util.ByteArray; + +/** + * Iterator used to examine the contents of an instruction list. + * + * @version 1.0 + * @author Michel Schinz, Thomas Friedli + */ + +public class JCodeIterator { + protected final JCode code; + protected final JConstantPool pool; + protected final ByteArray codeArray; + + protected int pc; + protected JOpcode opcode; + + /** + * Creates a new code iterator with its instruction list + * and its pc initialized to a given value. + */ + public JCodeIterator(JCode code, int pc) { + this.code = code; + this.pool = code.getOwner().getOwner().getConstantPool(); + this.codeArray = code.codeArray; + this.pc = pc; + setOpcode(); + } + + public JCodeIterator(JCode code) { + this(code, 0); + } + + /** + * Get the current program counter. + * @return The current program counter. + */ + public int getPC() { return pc; } + + /** + * Searches the type of the instruction positionned at the + * current address and updates the current instruction. + */ + protected void setOpcode() { + // TODO : check if the current pc is the beginning + // of an instruction + opcode = isValid() ? JOpcode.OPCODES[codeArray.getU1(pc)] : null; + } + + /** + * Returns the opcode of the current instruction. + * @return The opcode of the current instruction. + */ + public JOpcode getOpcode() { + return opcode; + } + + /** + * Updates the program counter to an given value. + * @param pc The new value of the program counter. + */ + public void moveTo(int pc) { + this.pc = pc; + setOpcode(); + } + + /** + * Check the validity of the iterator. + * @return true iff the iterator points to a valid address. + */ + public boolean isValid() { + return pc < codeArray.getSize(); + } + + /** + * Updates the current instruction with the next one in the + * sense of their position in the code. + */ + public void moveToNext() { + moveTo(pc + getInstructionSize()); + } + + /** + * Updates the current instruction with a specific successor + * of it. + * @param succ The index of the wanted successor in the list of + * the successors of the current instruction. + */ + public void moveToSuccessor(int succ) { + moveTo(getSuccessorPC(succ)); + } + + /** + * Updates the current instruction with the one positionned + * at a given index relatively to the actual program counter + * @param offset The relative position of the instruction + * compared with the position of the current one + */ + public void moveRelatively(int offset) { + moveTo(pc + offset); + } + + /** + * Returns the size in bytes of the current instruction. + * @return The size in bytes of the current instruction. + */ + public int getInstructionSize() { + if (opcode.size != JOpcode.UNKNOWN) { + return opcode.size; + } else if (opcode == JOpcode.TABLESWITCH) { + int lowOffset = 1 + pad4(pc + 1) + 4; + int low = codeArray.getS4(pc + lowOffset); + int high = codeArray.getS4(pc + lowOffset + 4); + return lowOffset + 8 + 4 * (high - low + 1); + } else if (opcode == JOpcode.LOOKUPSWITCH) { + int npairsOffset = 1 + pad4(pc + 1) + 4; + int npairs = codeArray.getS4(pc + npairsOffset); + return npairsOffset + 4 + 8 * npairs; + } else if (opcode == JOpcode.WIDE) { + if (codeArray.getU1(pc + 1) == JOpcode.cIINC) + return 6; + else + return 4; + } else + throw new Error("Unknown size for instruction " + opcode); + } + + /** + * Returns the number of successors of the current instruction. + * @return The number of successors of the current instruction. + */ + public int getSuccessorCount() { + if (opcode.successorCount != JOpcode.UNKNOWN) { + return opcode.successorCount; + } else if (opcode == JOpcode.TABLESWITCH) { + int lowPos = pc + 1 + pad4(pc + 1) + 4; + return 1 // default case + + codeArray.getS4(lowPos + 4) // value of HIGH field + - codeArray.getS4(lowPos) + 1; // value of LOW field + } else if (opcode == JOpcode.LOOKUPSWITCH) { + int npairsPos = pc + 1 + pad4(pc + 1) + 4; + return 1 + codeArray.getS4(npairsPos); + } else + throw new Error("Unknown successors for instruction " + opcode); + } + + /** + * Returns the address of the successor of the current instruction + * given its index in the list of successors of the current + * instruction. + * @param index The index of the wanted successor in the list of + * the successors of the current instruction. + * @return The address of the specific successor. + */ + public int getSuccessorPC(int index) { + assert (index >= 0) && (index < getSuccessorCount()) : index; + + switch (opcode.jumpKind) { + case JOpcode.JMP_NEXT: + return pc + getInstructionSize(); + case JOpcode.JMP_ALWAYS_S2_OFFSET: + return pc + codeArray.getS2(pc + 1); + case JOpcode.JMP_ALWAYS_S4_OFFSET: + return pc + codeArray.getS4(pc + 1); + case JOpcode.JMP_MAYBE_S2_OFFSET: + if (index == 0) + return pc + getInstructionSize(); + else + return pc + codeArray.getS2(pc + 1); + case JOpcode.JMP_TABLE: { + int defaultPos = pc + 1 + pad4(pc + 1); + if (index == 0) + return pc + codeArray.getS4(defaultPos); + else + return pc + codeArray.getS4(defaultPos + 3*4 + 4 * (index - 1)); + } + case JOpcode.JMP_LOOKUP: { + int defaultPos = pc + 1 + pad4(pc + 1); + if (index == 0) + return pc + codeArray.getS4(defaultPos); + else + return pc + codeArray.getS4(defaultPos + 2*4 + 4 + 8 * (index - 1)); + } + default: + throw new Error(); + } + } + + /** + * Returns the total size of data words put on the stack by the current + * instruction. + * @return The total size of data words put on the stack by the current + * instruction. + */ + public int getProducedDataSize() { + if (opcode.getProducedDataTypes() == JOpcode.UNKNOWN_TYPE) { + switch (opcode.code) { + case JOpcode.cLDC: case JOpcode.cLDC_W: case JOpcode.cBALOAD: + return 1; + case JOpcode.cLDC2_W: case JOpcode.cDUP: case JOpcode.cSWAP: + return 2; + case JOpcode.cDUP_X1: + return 3; + case JOpcode.cDUP_X2: case JOpcode.cDUP2: + return 4; + case JOpcode.cDUP2_X1: + return 5; + case JOpcode.cDUP2_X2: + return 6; + case JOpcode.cGETSTATIC: case JOpcode.cGETFIELD: { + JConstantPool.FieldOrMethodRefEntry entry = + (JConstantPool.FieldOrMethodRefEntry) + pool.lookupEntry(codeArray.getU2(pc + 1)); + return JType.parseSignature(entry.getSignature()).getSize(); + } + case JOpcode.cWIDE : { + int op = codeArray.getU1(pc + 1); + if (op >= JOpcode.cILOAD && op <= JOpcode.cALOAD) { + JOpcode opcode2 = JOpcode.OPCODES[op]; + return JType.getTotalSize(opcode2.getProducedDataTypes()); + } else if (op >= JOpcode.cISTORE && op <= JOpcode.cASTORE) + return 0; + else return 0; // (IINC) + } + default : + throw new Error(opcode.toString()); + } + } else + return JType.getTotalSize(opcode.getProducedDataTypes()); + } + + /** + * Returns the total size of data words taken from the stack by the current + * instruction. + * @return The total size of data words taken from the stack by the current + * instruction. + */ + public int getConsumedDataSize() { + if (opcode.getConsumedDataTypes() != JOpcode.UNKNOWN_TYPE) + return JType.getTotalSize(opcode.getConsumedDataTypes()); + else { + switch (opcode.code) { + case JOpcode.cPOP: case JOpcode.cDUP: + return 1; + case JOpcode.cPOP2: case JOpcode.cSWAP: + case JOpcode.cDUP_X1: case JOpcode.cDUP2: + return 2; + case JOpcode.cDUP_X2: case JOpcode.cDUP2_X1: + return 3; + case JOpcode.cDUP2_X2: + return 4; + case JOpcode.cPUTSTATIC: case JOpcode.cPUTFIELD: { + JConstantPool.FieldOrMethodRefEntry entry = + (JConstantPool.FieldOrMethodRefEntry) + pool.lookupEntry(codeArray.getU2(pc + 1)); + return JType.parseSignature(entry.getSignature()).getSize(); + } + case JOpcode.cINVOKEVIRTUAL: case JOpcode.cINVOKESPECIAL: + case JOpcode.cINVOKESTATIC: case JOpcode.cINVOKEINTERFACE : { + JConstantPool.FieldOrMethodRefEntry entry = + (JConstantPool.FieldOrMethodRefEntry) + pool.lookupEntry(codeArray.getU2(pc + 1)); + JMethodType tp = (JMethodType) + JType.parseSignature(entry.getSignature()); + return tp.getArgsSize() + + (opcode == JOpcode.INVOKESTATIC ? 0 : 1); + } + case JOpcode.cWIDE : { + int op = codeArray.getU1(pc + 1); + if (op >= JOpcode.cILOAD && op <= JOpcode.cALOAD) + return 0; + else if (op >= JOpcode.cISTORE && op <= JOpcode.cASTORE) { + JOpcode opcode2 = JOpcode.OPCODES[op]; + return JType.getTotalSize(opcode2.getConsumedDataTypes()); + } else + return 0; // (IINC) + } + case JOpcode.cMULTIANEWARRAY : + return codeArray.getU1(pc + 3); + default: + throw new Error(opcode.toString()); + } + } + } + + /** + * Returns the number of data types put on the stack by the current + * instruction. + * @return The number of data types put on the stack by the current + * instruction. + */ + public int getProducedDataTypesNumber() { + if (opcode.getProducedDataTypes() != JOpcode.UNKNOWN_TYPE) + return opcode.getProducedDataTypes().length; + else { + switch (opcode.code) { + case JOpcode.cLDC: case JOpcode.cLDC_W: case JOpcode.cLDC2_W: + case JOpcode.cBALOAD: case JOpcode.cGETSTATIC: + case JOpcode.cGETFIELD: + return 1; + case JOpcode.cDUP: case JOpcode.cSWAP: + return 2; + case JOpcode.cDUP_X1: + return 3; + case JOpcode.cWIDE: { + int op = codeArray.getU1(pc + 1); + if (op >= JOpcode.cILOAD && op <= JOpcode.cALOAD) + return 1; + else if (op >= JOpcode.cISTORE && op <= JOpcode.cASTORE) + return 0; + else + return 0; // (IINC) + } + default: + throw new Error("JOpcode implementation error"); + } + } + } + + /** + * Returns the number of data types taken from the stack by the current + * instruction. + * @return The number of data types taken from the stack by the current + * instruction. + */ +// public int getConsumedDataTypesNumber() { +// if (opcode.getConsumedDataTypes() == JOpcode.UNKNOWN_TYPE) { +// switch (opcode.code) { +// case 87 : return 1; // POP +// case 88 : return 2; // POP2 +// case 89 : return 1; // DUP +// case 90 : return 2; // DUP_X1 +// case 91 : // DUP_X2 +// case 92 : // DUP2 +// case 93 : // DUP2_X1 +// case 94 : // DUP2_X2 +// throw new UnsupportedOperationException("Opcode " + opcode.name +// + " has a stack-dependant" +// + " data types consumption"); +// case 95 : return 2; // SWAP +// case 179 : return 1; // PUTSTATIC +// case 181 : return 1; // PUTFIELD +// case 182 : // INVOKEVIRTUAL +// case 183 : // INVOKESPECIAL +// case 185 : // INVOKEINTERFACE +// s = epool.getClassMethodRef(codeArray.getU2(pc + 1)).split(" ")[3]; +// return ((JMethodType)JType.parseSignature(s)).argTypes.length + 1; +// case 184 : // INVOKESTATIC +// s = epool.getClassMethodRef(codeArray.getU2(pc + 1)).split(" ")[3]; +// return ((JMethodType)JType.parseSignature(s)).argTypes.length; +// case 196 : // WIDE +// int op = codeArray.getU1(pc + 1); +// if (op >= 21 && op <= 25) return 0; // (xLOAD) +// else if (op >= 54 && op <= 58) // (xSTORE) +// return JOpcode.OPCODES[op].getConsumedDataTypes().length; +// else return 0; // (IINC) +// case 197 : return codeArray.getU1(pc + 3); // MULTIANEWARRAY +// default : throw new Error("JOpcode implementation error"); +// } +// } else return opcode.getConsumedDataTypes().length; +// } + + + // Return the number between 0 and 3 which, if added to the given + // value, would yield a multiple of 4. + protected int[] padding = { 0, 3, 2, 1 }; + protected int pad4(int value) { + return padding[value % 4]; + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java b/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java new file mode 100644 index 0000000000..911acd18da --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java @@ -0,0 +1,608 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.util.*; +import java.io.*; + +/** + * Constant pool, holding constants for a Java class file. + * + * @author Michel Schinz + * @version 2.0 + */ + +public class JConstantPool { + protected boolean frozen = false; + + protected HashMap/**/ entryToIndex = new HashMap(); + protected Entry[] indexToEntry; + protected int currIndex; + + public static final short CONSTANT_Utf8 = 1; + public static final short CONSTANT_Integer = 3; + public static final short CONSTANT_Float = 4; + public static final short CONSTANT_Long = 5; + public static final short CONSTANT_Double = 6; + public static final short CONSTANT_Class = 7; + public static final short CONSTANT_String = 8; + public static final short CONSTANT_Fieldref = 9; + public static final short CONSTANT_Methodref = 10; + public static final short CONSTANT_InterfaceMethodref = 11; + public static final short CONSTANT_NameAndType = 12; + + protected JConstantPool(FJBGContext context) { + indexToEntry = new Entry[8]; + currIndex = 1; + } + + protected JConstantPool(FJBGContext context, DataInputStream stream) + throws IOException { + int count = stream.readShort(); + indexToEntry = new EntryIndex[count]; + + currIndex = 1; + while (currIndex < count) { + EntryIndex e; + int tag = stream.readByte(); + + switch (tag) { + case CONSTANT_Utf8: + e = new Utf8Entry(stream); + break; + case CONSTANT_Integer: + e = new IntegerEntry(stream); + break; + case CONSTANT_Float: + e = new FloatEntry(stream); + break; + case CONSTANT_Long: + e = new LongEntry(stream); + break; + case CONSTANT_Double: + e = new DoubleEntry(stream); + break; + case CONSTANT_Class: + e = new DescriptorEntryIndex(stream); + break; + case CONSTANT_String: + e = new StringEntryIndex(stream); + break; + case CONSTANT_Fieldref: + case CONSTANT_Methodref: + case CONSTANT_InterfaceMethodref: + e = new FieldOrMethodRefEntryIndex(tag, stream); + break; + case CONSTANT_NameAndType: + e = new NameAndTypeEntryIndex(stream); + break; + default: + throw new IllegalArgumentException("unknown entry in pool: " + tag); + } + indexToEntry[currIndex] = e; + currIndex += e.getSize(); + } + } + + public void freeze() { frozen = true; } + + /** + * Returns a string representing the type of an entry + * knowing its tag + * @param tag The tag representing the type of the + * constant pool entry + */ + 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"; + default : throw new Error("invalid constant pool tag : " + tag); + } + } + + public int addClass(String className) { + 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()); + } + + protected int addDescriptor(String name) { + return addEntry(new DescriptorEntryValue(name)); + } + + public int addClassMethodRef(String className, + String methodName, + String signature) { + return addMethodRef(true, className, methodName, signature); + } + + public int addInterfaceMethodRef(String className, + String methodName, + String signature) { + return addMethodRef(false, className, methodName, signature); + } + + public int addMethodRef(boolean isClass, + String className, + String methodName, + String signature) { + return addEntry(new FieldOrMethodRefEntryValue(isClass + ? CONSTANT_Methodref + : CONSTANT_InterfaceMethodref, + className, + methodName, + signature)); + } + + public int addFieldRef(String className, + String fieldName, + String signature) { + return addEntry(new FieldOrMethodRefEntryValue(CONSTANT_Fieldref, + className, + fieldName, + signature)); + } + + public int addInteger(int value) { + return addEntry(new IntegerEntry(value)); + } + + public int addFloat(float value) { + return addEntry(new FloatEntry(value)); + } + + public int addLong(long value) { + return addEntry(new LongEntry(value)); + } + + public int addDouble(double value) { + return addEntry(new DoubleEntry(value)); + } + + public int addString(String value) { + return addEntry(new StringEntryValue(value)); + } + + public int addNameAndType(String name, String descriptor) { + return addEntry(new NameAndTypeEntryValue(name, descriptor)); + } + + public int addUtf8(String value) { + 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(); + + e.addChildren(); + + int index = currIndex; + currIndex += e.getSize(); + + entryToIndex.put(e, new Integer(index)); + if (index >= indexToEntry.length) { + Entry[] newI2E = new Entry[indexToEntry.length * 2]; + System.arraycopy(indexToEntry, 0, newI2E, 0, indexToEntry.length); + indexToEntry = newI2E; + } + indexToEntry[index] = e; + return index; + } + + public Entry lookupEntry(int index) { + assert index > 0 && index < currIndex + : "invalid index: " + index; + assert indexToEntry[index] != null + : "invalid index (null contents): " + index; + return indexToEntry[index]; + } + + public void writeTo(DataOutputStream stream) throws IOException { + if (! frozen) freeze(); + + stream.writeShort(currIndex); + for (int i = 0; i < currIndex; ++i) { + Entry entry = indexToEntry[i]; + if (entry != null) { + stream.writeByte(entry.getTag()); + entry.writeContentsTo(stream); + } + } + } + + /// Classes for the various kinds of entries + ////////////////////////////////////////////////////////////////////// + + public interface Entry { + public int getTag(); + + int getSize(); + void writeContentsTo(DataOutputStream stream) throws IOException; + } + + protected interface EntryValue extends Entry { + abstract void addChildren(); + } + + protected interface EntryIndex extends Entry { + abstract void fetchChildren(); + } + + abstract protected class ChildlessEntry implements EntryValue, EntryIndex { + public void addChildren() {} + public void fetchChildren() {} + } + + public class IntegerEntry extends ChildlessEntry implements Entry { + private final int value; + public IntegerEntry(int value) { this.value = value; } + public IntegerEntry(DataInputStream stream) throws IOException { + this(stream.readInt()); + } + + public int hashCode() { return value; } + public boolean equals(Object o) { + return o instanceof IntegerEntry && ((IntegerEntry)o).value == value; + } + + public int getTag() { return CONSTANT_Integer; } + public int getValue() { return value; } + + public int getSize() { return 1; } + public void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeInt(value); + } + } + + public class FloatEntry extends ChildlessEntry implements Entry { + private final float value; + public FloatEntry(float value) { this.value = value; } + public FloatEntry(DataInputStream stream) throws IOException { + this(stream.readFloat()); + } + + public int hashCode() { return (int)value; } + public boolean equals(Object o) { + return o instanceof FloatEntry && ((FloatEntry)o).value == value; + } + + public int getTag() { return CONSTANT_Float; } + public float getValue() { return value; } + + public int getSize() { return 1; } + public void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeFloat(value); + } + } + + public class LongEntry extends ChildlessEntry implements Entry { + private final long value; + public LongEntry(long value) { this.value = value; } + public LongEntry(DataInputStream stream) throws IOException { + this(stream.readLong()); + } + + public int hashCode() { return (int)value; } + public boolean equals(Object o) { + return o instanceof LongEntry && ((LongEntry)o).value == value; + } + + public int getTag() { return CONSTANT_Long; } + public long getValue() { return value; } + + public int getSize() { return 2; } + public void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeLong(value); + } + } + + public class DoubleEntry extends ChildlessEntry implements Entry { + private final double value; + public DoubleEntry(double value) { this.value = value; } + public DoubleEntry(DataInputStream stream) throws IOException { + this(stream.readDouble()); + } + + public int hashCode() { return (int)value; } + public boolean equals(Object o) { + return o instanceof DoubleEntry && ((DoubleEntry)o).value == value; + } + + public int getTag() { return CONSTANT_Double; } + public double getValue() { return value; } + + public int getSize() { return 2; } + public void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeDouble(value); + } + } + + public class Utf8Entry extends ChildlessEntry implements Entry { + private final String value; + public Utf8Entry(String value) { this.value = value.intern(); } + public Utf8Entry(DataInputStream stream) throws IOException { + this(stream.readUTF()); + } + + public int hashCode() { return value.hashCode(); } + public boolean equals(Object o) { + return o instanceof Utf8Entry && ((Utf8Entry)o).value == value; + } + + public int getTag() { return CONSTANT_Utf8; } + public String getValue() { return value; } + + public int getSize() { return 1; } + public void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeUTF(value); + } + } + + abstract public class StringEntry implements Entry { + protected String value; + protected int valueIndex; + + public int hashCode() { + assert value != null; + return value.hashCode(); + } + public boolean equals(Object o) { + return o instanceof StringEntry && ((StringEntry)o).value == value; + } + + public int getTag() { return CONSTANT_String; } + public String getValue() { return value; } + + public int getSize() { return 1; } + public void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeShort(valueIndex); + } + } + + public class StringEntryValue extends StringEntry implements EntryValue { + public StringEntryValue(String value) { + this.value = value.intern(); + } + public void addChildren() { + valueIndex = addUtf8(value); + } + } + + public class StringEntryIndex extends StringEntry implements EntryIndex { + public StringEntryIndex(int valueIndex) { + this.valueIndex = valueIndex; + } + public StringEntryIndex(DataInputStream stream) throws IOException { + this(stream.readShort()); + } + public String getValue() { + if (value == null) fetchChildren(); + return super.getValue(); + } + public void fetchChildren() { + value = lookupUtf8(valueIndex); + } + } + + abstract public class DescriptorEntry implements Entry { + protected String name; + protected int nameIndex; + + public int hashCode() { + assert name != null; + return name.hashCode(); + } + public boolean equals(Object o) { + return o instanceof DescriptorEntry && ((DescriptorEntry)o).name == name; + } + + public int getTag() { return CONSTANT_Class; } + public String getValue() { return name; } + + public int getSize() { return 1; } + public void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeShort(nameIndex); + } + } + + protected class DescriptorEntryValue + extends DescriptorEntry + implements EntryValue { + public DescriptorEntryValue(String name) { this.name = name.intern(); } + public void addChildren() { + nameIndex = addUtf8(name); + } + } + + protected class DescriptorEntryIndex + extends DescriptorEntry + implements EntryIndex { + public DescriptorEntryIndex(int nameIndex) { this.nameIndex = nameIndex; } + public DescriptorEntryIndex(DataInputStream stream) throws IOException { + this(stream.readShort()); + } + public String getValue() { + if (name == null) fetchChildren(); + return super.getValue(); + } + public void fetchChildren() { + name = lookupUtf8(nameIndex); + } + } + + abstract public class FieldOrMethodRefEntry implements Entry { + private final int tag; + protected String className, thingName, signature; + protected int classIndex, nameAndTypeIndex; + + public FieldOrMethodRefEntry(int tag) { + assert tag == CONSTANT_Fieldref + || tag == CONSTANT_Methodref + || tag == CONSTANT_InterfaceMethodref; + + this.tag = tag; + } + + public int hashCode() { + return tag + + className.hashCode() + + thingName.hashCode() + + signature.hashCode(); + } + public boolean equals(Object o) { + return o instanceof FieldOrMethodRefEntry + && ((FieldOrMethodRefEntry)o).tag == tag + && ((FieldOrMethodRefEntry)o).className == className + && ((FieldOrMethodRefEntry)o).thingName == thingName + && ((FieldOrMethodRefEntry)o).signature == signature; + } + + public int getTag() { return tag; } + public String getClassName() { return className; } + public String getFieldOrMethodName() { return thingName; } + public String getSignature() { return signature; } + + public int getSize() { return 1; } + public void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeShort(classIndex); + stream.writeShort(nameAndTypeIndex); + } + } + + protected class FieldOrMethodRefEntryValue + extends FieldOrMethodRefEntry + implements EntryValue { + public FieldOrMethodRefEntryValue(int tag, + String className, + String thingName, + String signature) { + super(tag); + this.className = className.intern(); + this.thingName = thingName.intern(); + this.signature = signature.intern(); + } + + public void addChildren() { + classIndex = addClass(className); + nameAndTypeIndex = addNameAndType(thingName, signature); + } + } + + protected class FieldOrMethodRefEntryIndex + extends FieldOrMethodRefEntry + implements EntryIndex { + public FieldOrMethodRefEntryIndex(int tag, + int classIndex, + int nameAndTypeIndex) { + super(tag); + this.classIndex = classIndex; + this.nameAndTypeIndex = nameAndTypeIndex; + } + public FieldOrMethodRefEntryIndex(int tag, DataInputStream stream) + throws IOException { + this(tag, stream.readShort(), stream.readShort()); + } + public String getClassName() { + if (className == null) fetchChildren(); + return super.getClassName(); + } + public String getFieldOrMethodName() { + if (thingName == null) fetchChildren(); + return super.getFieldOrMethodName(); + } + public String getSignature() { + if (signature == null) fetchChildren(); + return super.getSignature(); + } + public void fetchChildren() { + className = lookupClass(classIndex); + NameAndTypeEntry nat = (NameAndTypeEntry)lookupEntry(nameAndTypeIndex); + thingName = nat.getName(); + signature = nat.getDescriptor(); + } + } + + abstract public class NameAndTypeEntry implements Entry { + protected String name, descriptor; + protected int nameIndex, descriptorIndex; + + public int hashCode() { return name.hashCode() + descriptor.hashCode(); } + public boolean equals(Object o) { + return o instanceof NameAndTypeEntry + && ((NameAndTypeEntry)o).name == name + && ((NameAndTypeEntry)o).descriptor == descriptor; + } + + public int getTag() { return CONSTANT_NameAndType; } + public String getName() { return name; } + public String getDescriptor() { return descriptor; } + + public int getSize() { return 1; } + public void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeShort(nameIndex); + stream.writeShort(descriptorIndex); + } + } + + protected class NameAndTypeEntryValue + extends NameAndTypeEntry + implements EntryValue { + public NameAndTypeEntryValue(String name, String descriptor) { + this.name = name.intern(); + this.descriptor = descriptor.intern(); + } + public void addChildren() { + nameIndex = addUtf8(name); + descriptorIndex = addUtf8(descriptor); + } + } + + protected class NameAndTypeEntryIndex + extends NameAndTypeEntry + implements EntryIndex { + public NameAndTypeEntryIndex(int nameIndex, int descriptorIndex) { + this.nameIndex = nameIndex; + this.descriptorIndex = descriptorIndex; + } + public NameAndTypeEntryIndex(DataInputStream stream) throws IOException { + this(stream.readShort(), stream.readShort()); + } + public String getName() { + if (name == null) fetchChildren(); + return super.getName(); + } + public String getDescriptor() { + if (descriptor == null) fetchChildren(); + return super.getDescriptor(); + } + public void fetchChildren() { + name = lookupUtf8(nameIndex); + descriptor = lookupUtf8(descriptorIndex); + } + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java b/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java new file mode 100644 index 0000000000..b8f29a6a2b --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java @@ -0,0 +1,614 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +/** + * Extended list of instructions, providing pseudo-instructions which + * are easier to use than the standard ones. + * + * @author Michel Schinz, Thomas Friedli + * @version 1.0 + */ + +public class JExtendedCode extends JCode { + public final static int COND_EQ = 0; + public final static int COND_NE = 1; + public final static int COND_LT = 2; + public final static int COND_GE = 3; + public final static int COND_GT = 4; + public final static int COND_LE = 5; + + private final JOpcode[] forbidden = new JOpcode[0]; + private final JOpcode[] nothingToDo = new JOpcode[0]; + + private final JOpcode[][][] typeConversions = { + { + /* T_BOOLEAN -> T_BOOLEAN */ nothingToDo, + /* T_BOOLEAN -> T_CHAR */ forbidden, + /* T_BOOLEAN -> T_FLOAT */ forbidden, + /* T_BOOLEAN -> T_DOUBLE */ forbidden, + /* T_BOOLEAN -> T_BYTE */ forbidden, + /* T_BOOLEAN -> T_SHORT */ forbidden, + /* T_BOOLEAN -> T_INT */ forbidden, + /* T_BOOLEAN -> T_LONG */ forbidden + }, + { + /* T_CHAR -> T_BOOLEAN */ forbidden, + /* T_CHAR -> T_CHAR */ nothingToDo, + /* T_CHAR -> T_FLOAT */ {JOpcode.I2F}, + /* T_CHAR -> T_DOUBLE */ {JOpcode.I2D}, + /* T_CHAR -> T_BYTE */ {JOpcode.I2B}, + /* T_CHAR -> T_SHORT */ {JOpcode.I2S}, + /* T_CHAR -> T_INT */ nothingToDo, + /* T_CHAR -> T_LONG */ {JOpcode.I2L} + }, + { + /* T_FLOAT -> T_BOOLEAN */ forbidden, + /* T_FLOAT -> T_CHAR */ {JOpcode.F2I, JOpcode.I2C}, + /* T_FLOAT -> T_FLOAT */ nothingToDo, + /* T_FLOAT -> T_DOUBLE */ {JOpcode.F2D}, + /* T_FLOAT -> T_BYTE */ {JOpcode.F2I, JOpcode.I2B}, + /* T_FLOAT -> T_SHORT */ {JOpcode.F2I, JOpcode.I2S}, + /* T_FLOAT -> T_INT */ {JOpcode.F2I}, + /* T_FLOAT -> T_LONG */ {JOpcode.F2L} + }, + { + /* T_DOUBLE -> T_BOOLEAN */ forbidden, + /* T_DOUBLE -> T_CHAR */ {JOpcode.D2I, JOpcode.I2C}, + /* T_DOUBLE -> T_FLOAT */ {JOpcode.D2F}, + /* T_DOUBLE -> T_DOUBLE */ nothingToDo, + /* T_DOUBLE -> T_BYTE */ {JOpcode.D2I, JOpcode.I2B}, + /* T_DOUBLE -> T_SHORT */ {JOpcode.D2I, JOpcode.I2S}, + /* T_DOUBLE -> T_INT */ {JOpcode.D2I}, + /* T_DOUBLE -> T_LONG */ {JOpcode.D2L} + }, + { + /* T_BYTE -> T_BOOLEAN */ forbidden, + /* T_BYTE -> T_CHAR */ {JOpcode.I2C}, + /* T_BYTE -> T_FLOAT */ {JOpcode.I2F}, + /* T_BYTE -> T_DOUBLE */ {JOpcode.I2D}, + /* T_BYTE -> T_BYTE */ nothingToDo, + /* T_BYTE -> T_SHORT */ nothingToDo, + /* T_BYTE -> T_INT */ nothingToDo, + /* T_BYTE -> T_LONG */ {JOpcode.I2L} + }, + { + /* T_SHORT -> T_BOOLEAN */ forbidden, + /* T_SHORT -> T_CHAR */ nothingToDo, + /* T_SHORT -> T_FLOAT */ {JOpcode.I2F}, + /* T_SHORT -> T_DOUBLE */ {JOpcode.I2D}, + /* T_SHORT -> T_BYTE */ {JOpcode.I2B}, + /* T_SHORT -> T_SHORT */ nothingToDo, + /* T_SHORT -> T_INT */ nothingToDo, + /* T_SHORT -> T_LONG */ {JOpcode.I2L} + }, + { + /* T_INT -> T_BOOLEAN */ forbidden, + /* T_INT -> T_CHAR */ {JOpcode.I2C}, + /* T_INT -> T_FLOAT */ {JOpcode.I2F}, + /* T_INT -> T_DOUBLE */ {JOpcode.I2D}, + /* T_INT -> T_BYTE */ {JOpcode.I2B}, + /* T_INT -> T_SHORT */ {JOpcode.I2S}, + /* T_INT -> T_INT */ nothingToDo, + /* T_INT -> T_LONG */ {JOpcode.I2L} + }, + { + /* T_LONG -> T_BOOLEAN */ forbidden, + /* T_LONG -> T_CHAR */ {JOpcode.L2I, JOpcode.I2C}, + /* T_LONG -> T_FLOAT */ {JOpcode.L2F}, + /* T_LONG -> T_DOUBLE */ {JOpcode.L2D}, + /* T_LONG -> T_BYTE */ {JOpcode.L2I, JOpcode.I2B}, + /* T_LONG -> T_SHORT */ {JOpcode.L2I, JOpcode.I2S}, + /* T_LONG -> T_INT */ {JOpcode.L2I}, + /* T_LONG -> T_LONG */ nothingToDo + } + }; + + public JExtendedCode(FJBGContext context, + JClass clazz, + JMethod owner) { + super(context, clazz, owner); + } + + public void emitPUSH(boolean value) { emitPUSH(value ? 1 : 0); } + public void emitPUSH(Boolean value) { emitPUSH(value.booleanValue()); } + + public void emitPUSH(byte value) { emitBIPUSH(value); } + public void emitPUSH(Byte value) { emitPUSH(value.byteValue()); } + + public void emitPUSH(short value) { emitSIPUSH(value); } + public void emitPUSH(Short value) { emitPUSH(value.shortValue()); } + + // TODO check that we do the right thing here + public void emitPUSH(char value) { emitPUSH((int)value); } + public void emitPUSH(Character value) { emitPUSH(value.charValue()); } + + public void emitPUSH(int value) { + switch (value) { + case -1: emitICONST_M1(); break; + case 0: emitICONST_0(); break; + case 1: emitICONST_1(); break; + case 2: emitICONST_2(); break; + case 3: emitICONST_3(); break; + case 4: emitICONST_4(); break; + case 5: emitICONST_5(); break; + default: emitPUSH_index(pool.addInteger(value)); break; + } + } + public void emitPUSH(Integer value) { emitPUSH(value.intValue()); } + + public void emitPUSH(long value) { + if (value == 0L) + emitLCONST_0(); + else if (value == 1L) + emitLCONST_1(); + else + emitLDC2_W(value); + } + public void emitPUSH(Long value) { emitPUSH(value.longValue()); } + + public void emitPUSH(float value) { + if (value == 0.0F) + emitFCONST_0(); + else if (value == 1.0F) + emitFCONST_1(); + else if (value == 2.0F) + emitFCONST_2(); + else + emitPUSH_index(pool.addFloat(value)); + } + public void emitPUSH(Float value) { emitPUSH(value.floatValue()); } + + public void emitPUSH(double value) { + if (value == 0.0) + emitDCONST_0(); + else if (value == 1.0) + emitDCONST_1(); + else + emitLDC2_W(value); + } + public void emitPUSH(Double value) { emitPUSH(value.doubleValue()); } + + public void emitPUSH(String s) { + emitPUSH_index(pool.addString(s)); + } + + /** Pushes a class literal on the stack */ + public void emitPUSH(JReferenceType type) { + assert owner.owner.major >= 49; + emitPUSH_index(pool.addClass(type.getDescriptor())); + } + + protected void emitPUSH_index(int index) { + if (index <= 0xFF) + emitU1(JOpcode.LDC, index); + else + emitU2(JOpcode.LDC_W, index); + } + + public void emitLOAD(int index, JType type) { + JOpcode opcode; + + switch (type.getTag()) { + case JType.T_BOOLEAN: case JType.T_BYTE: case JType.T_CHAR: + case JType.T_SHORT: case JType.T_INT: + switch (index) { + case 0: emitILOAD_0(); return; + case 1: emitILOAD_1(); return; + case 2: emitILOAD_2(); return; + case 3: emitILOAD_3(); return; + default: opcode = JOpcode.ILOAD; + } break; + case JType.T_FLOAT: + switch (index) { + case 0: emitFLOAD_0(); return; + case 1: emitFLOAD_1(); return; + case 2: emitFLOAD_2(); return; + case 3: emitFLOAD_3(); return; + default: opcode = JOpcode.FLOAD; + } break; + case JType.T_LONG: + switch (index) { + case 0: emitLLOAD_0(); return; + case 1: emitLLOAD_1(); return; + case 2: emitLLOAD_2(); return; + case 3: emitLLOAD_3(); return; + default: opcode = JOpcode.LLOAD; + } break; + case JType.T_DOUBLE: + switch (index) { + case 0: emitDLOAD_0(); return; + case 1: emitDLOAD_1(); return; + case 2: emitDLOAD_2(); return; + case 3: emitDLOAD_3(); return; + default: opcode = JOpcode.DLOAD; + } break; + case JType.T_ARRAY: case JType.T_OBJECT: + switch (index) { + case 0: emitALOAD_0(); return; + case 1: emitALOAD_1(); return; + case 2: emitALOAD_2(); return; + case 3: emitALOAD_3(); return; + default: opcode = JOpcode.ALOAD; + } break; + default: + throw new IllegalArgumentException("invalid type for load "+type); + } + + if (index > 0xFF) + emitWIDE(opcode, index); + else + emitU1(opcode, index); + } + public void emitLOAD(JLocalVariable var) { + emitLOAD(var.index, var.type); + } + + public void emitSTORE(int index, JType type) { + JOpcode opcode; + + switch (type.getTag()) { + case JType.T_BOOLEAN: case JType.T_BYTE: case JType.T_CHAR: + case JType.T_SHORT: case JType.T_INT: + switch (index) { + case 0: emitISTORE_0(); return; + case 1: emitISTORE_1(); return; + case 2: emitISTORE_2(); return; + case 3: emitISTORE_3(); return; + default: opcode = JOpcode.ISTORE; + } break; + case JType.T_FLOAT: + switch (index) { + case 0: emitFSTORE_0(); return; + case 1: emitFSTORE_1(); return; + case 2: emitFSTORE_2(); return; + case 3: emitFSTORE_3(); return; + default: opcode = JOpcode.FSTORE; + } break; + case JType.T_LONG: + switch (index) { + case 0: emitLSTORE_0(); return; + case 1: emitLSTORE_1(); return; + case 2: emitLSTORE_2(); return; + case 3: emitLSTORE_3(); return; + default: opcode = JOpcode.LSTORE; + } break; + case JType.T_DOUBLE: + switch (index) { + case 0: emitDSTORE_0(); return; + case 1: emitDSTORE_1(); return; + case 2: emitDSTORE_2(); return; + case 3: emitDSTORE_3(); return; + default: opcode = JOpcode.DSTORE; + } break; + case JType.T_ARRAY: case JType.T_OBJECT: case JType.T_ADDRESS: + switch (index) { + case 0: emitASTORE_0(); return; + case 1: emitASTORE_1(); return; + case 2: emitASTORE_2(); return; + case 3: emitASTORE_3(); return; + default: opcode = JOpcode.ASTORE; + } break; + default: + throw new IllegalArgumentException("invalid type for store "+type); + } + + if (index > 0xFF) + emitWIDE(opcode, index); + else + emitU1(opcode, index); + } + public void emitSTORE(JLocalVariable var) { + emitSTORE(var.index, var.type); + } + + public void emitALOAD(JType type) { + switch (type.getTag()) { + case JType.T_BOOLEAN: + case JType.T_BYTE: + emitBALOAD(); + break; + case JType.T_CHAR: + emitCALOAD(); + break; + case JType.T_SHORT: + emitSALOAD(); + break; + case JType.T_INT: + emitIALOAD(); + break; + case JType.T_FLOAT: + emitFALOAD(); + break; + case JType.T_LONG: + emitLALOAD(); + break; + case JType.T_DOUBLE: + emitDALOAD(); + break; + case JType.T_ARRAY: + case JType.T_OBJECT: + emitAALOAD(); + break; + default: + throw new IllegalArgumentException("invalid type for aload " + type); + } + } + + public void emitASTORE(JType type) { + switch (type.getTag()) { + case JType.T_BOOLEAN: + case JType.T_BYTE: + emitBASTORE(); + break; + case JType.T_CHAR: + emitCASTORE(); + break; + case JType.T_SHORT: + emitSASTORE(); + break; + case JType.T_INT: + emitIASTORE(); + break; + case JType.T_FLOAT: + emitFASTORE(); + break; + case JType.T_LONG: + emitLASTORE(); + break; + case JType.T_DOUBLE: + emitDASTORE(); + break; + case JType.T_ARRAY: + case JType.T_OBJECT: + emitAASTORE(); + break; + default: + throw new IllegalArgumentException("invalid type for astore " + type); + } + } + + public void emitRETURN(JType type) { + if (type.isValueType()) { + switch (type.getTag()) { + case JType.T_BOOLEAN: + case JType.T_BYTE: + case JType.T_CHAR: + case JType.T_SHORT: + case JType.T_INT: + emitIRETURN(); + break; + case JType.T_FLOAT: + emitFRETURN(); + break; + case JType.T_LONG: + emitLRETURN(); + break; + case JType.T_DOUBLE: + emitDRETURN(); + break; + } + } else if (type.isArrayType() || type.isObjectType()) + emitARETURN(); + else if (type == JType.VOID) + emitRETURN(); + else + throw new IllegalArgumentException("invalid type for RETURN " + type); + } + + public void emitADD(JType type) { + switch (type.getTag()) { + case JType.T_BOOLEAN: case JType.T_BYTE: case JType.T_CHAR: + case JType.T_SHORT: case JType.T_INT: + emitIADD(); break; + case JType.T_FLOAT: + emitFADD(); break; + case JType.T_LONG: + emitLADD(); break; + case JType.T_DOUBLE: + emitDADD(); break; + } + } + + /** + * Emits a basic type conversion instruction choosen according to the + * types given in parameter. + * + * @param fromType The type of the value to be cast into another type. + * @param toType The type the value will be cast into. + */ + public void emitT2T(JType fromType, JType toType) { + assert fromType.getTag() >= JType.T_BOOLEAN + && fromType.getTag() <= JType.T_LONG + && toType.getTag() >= JType.T_BOOLEAN + && toType.getTag() <= JType.T_LONG; + + JOpcode[] conv = typeConversions[fromType.getTag() - 4][toType.getTag() - 4]; + if (conv == forbidden) { + throw new Error("inconvertible types : " + fromType.toString() + + " -> " + toType.toString()); + } else if (conv != nothingToDo) { + for (int i = 0; i < conv.length; i++) { + emit(conv[i]); + } + } + } + + public void emitIF(int cond, Label label) throws OffsetTooBigException { + assert cond >= COND_EQ && cond <= COND_LE; + emitU2(JOpcode.OPCODES[153 + cond], label.getOffset16(getPC() + 1, getPC())); + } + public void emitIF(int cond, int targetPC) throws OffsetTooBigException { + int offset = targetPC - getPC(); + emitU2(JOpcode.OPCODES[153 + cond], offset); + } + public void emitIF(int cond) throws OffsetTooBigException { + emitIF(cond, 0); + } + + public void emitIF_ICMP(int cond, Label label) throws OffsetTooBigException { + assert cond >= COND_EQ && cond <= COND_LE; + emitU2(JOpcode.OPCODES[159 + cond], label.getOffset16(getPC() + 1, getPC())); + } + public void emitIF_ICMP(int cond, int targetPC) throws OffsetTooBigException { + int offset = targetPC - getPC(); + emitU2(JOpcode.OPCODES[159 + cond], offset); + } + public void emitIF_ICMP(int cond) throws OffsetTooBigException { + emitIF_ICMP(cond, 0); + } + + public void emitIF_ACMP(int cond, Label label) throws OffsetTooBigException { + assert cond == COND_EQ || cond == COND_NE; + emitU2(JOpcode.OPCODES[165 + cond], label.getOffset16(getPC() + 1, getPC())); + } + public void emitIF_ACMP(int cond, int targetPC) throws OffsetTooBigException { + int offset = targetPC - getPC(); + emitU2(JOpcode.OPCODES[165 + cond], offset); + } + public void emitIF_ACMP(int cond) throws OffsetTooBigException { + emitIF_ACMP(cond, 0); + } + + public void emitGOTO_maybe_W(Label label, boolean defaultToWide) { + if (label.anchored) + emitGOTO_maybe_W(label.targetPC); + else { + if (defaultToWide) + emitGOTO_W(label); + else { + try { + emitGOTO(label); + } catch (OffsetTooBigException e) { + throw new Error(e); + } + } + } + } + + public void emitGOTO_maybe_W(int targetPC) { + int offset = targetPC - (getPC() + 1); + if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) + emitGOTO_W(targetPC); + else { + try { + emitGOTO(targetPC); + } catch (OffsetTooBigException e) { + throw new Error(e); + } + } + } + + /** + * Emits a switch instruction choosen according to the caracteristics + * of the given list of keys and a default maxRate. + * + * @param keySets The array of all keys that must be compared to the + * value on stack. + * @param branches The labels representing the jump addresses linked + * with the corresponding keys. + * @param defaultBranch The label representing the default branch + * address. + */ + public void emitSWITCH(int[][] keySets, + Label[] branches, + Label defaultBranch, + double minDensity) { + assert keySets.length == branches.length; + + int flatSize = 0; + for (int i = 0; i < keySets.length; ++i) + flatSize += keySets[i].length; + + int[] flatKeys = new int[flatSize]; + Label[] flatBranches = new Label[flatSize]; + int flatI = 0; + for (int i = 0; i < keySets.length; ++i) { + Label branch = branches[i]; + int[] keys = keySets[i]; + for (int j = 0; j < keys.length; ++j) { + flatKeys[flatI] = keys[j]; + flatBranches[flatI] = branch; + } + ++flatI; + } + assert flatI == flatSize; + emitSWITCH(flatKeys, flatBranches, defaultBranch, minDensity); + } + + /** + * Emits a switch instruction choosen according to the caracteristics + * of the given list of keys and a given maxRate. + * + * @param keys The array of all keys that must be compared to the + * value on stack. + * @param branches The labels representing the jump addresses linked + * with the corresponding keys. + * @param defaultBranch The label representing the default branch + * address. + * @param minDensity The minimum density to use for TABLESWITCH. + */ + public void emitSWITCH(int[] keys, + Label[] branches, + Label defaultBranch, + double minDensity) { + assert keys.length == branches.length; + + // sorting the tables + // FIXME use quicksort + for (int i = 1; i < keys.length; i++) { + for (int j = 1; j <= keys.length - i; j++) { + if (keys[j] < keys[j - 1]) { + int tmp = keys[j]; + keys[j] = keys[j - 1]; + keys[j - 1] = tmp; + + Label tmp_l = branches[j]; + branches[j] = branches[j - 1]; + branches[j - 1] = tmp_l; + } + } + } + + int keyMin = keys[0], keyMax = keys[keys.length - 1]; + int keyRange = keyMax - keyMin + 1; + if ((double)keys.length / (double)keyRange >= minDensity) { + // Keys are dense enough, use a table in which holes are + // filled with defaultBranch. + int[] newKeys = new int[keyRange]; + Label[] newBranches = new Label[keyRange]; + int oldPos = 0; + for (int i = 0; i < keyRange; ++i) { + int key = keyMin + i; + newKeys[i] = key; + if (keys[oldPos] == key) { + newBranches[i] = branches[oldPos]; + ++oldPos; + } else + newBranches[i] = defaultBranch; + } + assert oldPos == keys.length; + emitTABLESWITCH(newKeys, newBranches, defaultBranch); + } else + emitLOOKUPSWITCH(keys, branches, defaultBranch); + } + + /** + * Emits a method invocation instruction choosen according to + * the caracteristics of the given method. + * + * @param method The method to be invoked. + */ + public void emitINVOKE(JMethod method) { + String mName = method.getName(); + String cName = method.getOwner().getName(); + JMethodType mType = (JMethodType)method.getType(); + if (method.isStatic()) + emitINVOKESTATIC(cName, mName, mType); + else if (method.getOwner().isInterface()) + emitINVOKEINTERFACE(cName, mName, mType); + else + emitINVOKEVIRTUAL(cName, mName, mType); + } + +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JField.java b/src/fjbg/ch/epfl/lamp/fjbg/JField.java new file mode 100644 index 0000000000..d94bf8c64f --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JField.java @@ -0,0 +1,30 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * Java class field. + * + * @author Michel Schinz + * @version 1.0 + */ + +public class JField extends JFieldOrMethod { + protected JField(FJBGContext context, + JClass owner, + int accessFlags, + String name, + JType type) { + super(context, owner, accessFlags, name, type); + } + + protected JField(FJBGContext context, + JClass owner, + DataInputStream stream) + throws IOException { + super(context, owner, stream); + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java b/src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java new file mode 100644 index 0000000000..bf82015c42 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java @@ -0,0 +1,122 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.io.*; + +/** + * Abstract superclass for a Java field or method. + * + * @author Michel Schinz + * @version 1.0 + */ + +abstract public class JFieldOrMethod extends JMember { + + protected final JClass owner; + protected final JType type; + + protected final int nameIndex, signatureIndex; + + protected JFieldOrMethod(FJBGContext context, + JClass owner, + int accessFlags, + String name, + JType type) { + super(context, accessFlags, name); + this.owner = owner; + this.type = type; + + JConstantPool pool = owner.getConstantPool(); + nameIndex = pool.addUtf8(name); + signatureIndex = pool.addUtf8(type.getSignature()); + } + + protected JFieldOrMethod(FJBGContext context, + JClass owner, + DataInputStream stream) + throws IOException { + super(context); + this.owner = owner; + this.accessFlags = stream.readShort(); + this.nameIndex = stream.readShort(); + this.name = owner.pool.lookupUtf8(nameIndex); + this.signatureIndex = stream.readShort(); + this.type = JType.parseSignature(owner.pool.lookupUtf8(signatureIndex)); + this.attributes.addAll(JAttribute.readFrom(context, owner, this, stream)); + } + + public void freeze() throws JCode.OffsetTooBigException { + assert !frozen; + frozen = true; + } + + public JClass getOwner() { return owner; } + + public JType getType() { return type; } + + public JClass getJClass() { return owner; } + + 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 isSuper() { + return (accessFlags & JAccessFlags.ACC_SUPER) != 0; + } + + public boolean isVolatile() { + return (accessFlags & JAccessFlags.ACC_VOLATILE) != 0; + } + + public boolean isTransient() { + return (accessFlags & JAccessFlags.ACC_TRANSIENT) != 0; + } + + public boolean isNative() { + return (accessFlags & JAccessFlags.ACC_NATIVE) != 0; + } + + public boolean isInterface() { + return (accessFlags & JAccessFlags.ACC_INTERFACE) != 0; + } + + public boolean isAbstract() { + return (accessFlags & JAccessFlags.ACC_ABSTRACT) != 0; + } + + public boolean isStrict() { + return (accessFlags & JAccessFlags.ACC_STRICT) != 0; + } + + public void writeTo(DataOutputStream stream) throws IOException { + if (! frozen) { + try { + freeze(); + } + catch (JCode.OffsetTooBigException e) { + throw new Error(e); + } + } + stream.writeShort(accessFlags); + stream.writeShort(nameIndex); + stream.writeShort(signatureIndex); + JAttribute.writeTo(getAttributes(), stream); + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java new file mode 100644 index 0000000000..81a7db7488 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java @@ -0,0 +1,89 @@ +package ch.epfl.lamp.fjbg; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.*; + +/** + * InnerClasses attribute. See section 4.7.5 of the JVM + * Specification. + * + * @author Iulian Dragos + */ +public class JInnerClassesAttribute extends JAttribute { + /** InnerClass entries */ + private Map/**/ entries = new LinkedHashMap(); + + /** Constant pool of the current classfile. */ + private JConstantPool pool; + + public JInnerClassesAttribute(FJBGContext context, + JClass clazz) { + super(context, clazz); + this.pool = clazz.pool; + } + + public void addEntry(String inner, String outer, String name, int flags) { + Entry e = new Entry(inner, outer, name, 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; + } + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JLabel.java b/src/fjbg/ch/epfl/lamp/fjbg/JLabel.java new file mode 100644 index 0000000000..a8ba479c7b --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JLabel.java @@ -0,0 +1,27 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +/** + * Labels which can be attached to instructions. + * + * @version 1.0 + * @author Michel Schinz + */ + +public class JLabel { + public final static int UNDEFINED_ANCHOR = -1; + protected int anchor = UNDEFINED_ANCHOR; + + public boolean isAnchored() { return anchor != UNDEFINED_ANCHOR; } + + public int getAnchor() { + assert isAnchored(); + return anchor; + } + + public void setAnchor(int anchor) { + 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 new file mode 100644 index 0000000000..2ca4a73252 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JLineNumberTableAttribute.java @@ -0,0 +1,99 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.io.DataOutputStream; +import java.io.DataInputStream; +import java.io.IOException; + +/** + * Attribute storing correspondance between instructions and source + * line numbers. + * + * @version 1.0 + * @author Michel Schinz + */ + +public class JLineNumberTableAttribute extends JAttribute { + protected final JCode code; + + public JLineNumberTableAttribute(FJBGContext context, + JClass clazz, + JCode owner) { + super(context, clazz); + this.code = owner; + + assert owner.getOwner().getOwner() == clazz; + } + + public JLineNumberTableAttribute(FJBGContext context, + JClass clazz, + Object owner, + String name, + int size, + DataInputStream stream) + throws IOException { + super(context, clazz); + code = (JCode)owner; + + int[] mapping = new int[code.getSize()]; + + int count = stream.readShort(); + for (int i = 0; i < count; ++i) { + int startPC = stream.readShort(); + int lineNum = stream.readShort(); + mapping[startPC] = lineNum; + } + + int lineNum = 0; + for (int pc = 0; pc < mapping.length; ++pc) { + if (mapping[pc] != 0) lineNum = mapping[pc]; + if (lineNum != 0) code.setLineNumber(pc, lineNum); + } + + assert name.equals(getName()); + } + + public String getName() { return "LineNumberTable"; } + + protected int[] encoding; + protected int[] encode() { + if (encoding == null) { + int[] lineNumbers = code.getLineNumbers(); + int[] preEncoding = new int[lineNumbers.length * 2]; + int prevLineNum = 0; + + int i = 0; + for (int pc = 0; pc < lineNumbers.length; ++pc) { + int lineNum = lineNumbers[pc]; + if (lineNum != 0 & lineNum != prevLineNum) { + preEncoding[i++] = pc; + preEncoding[i++] = lineNum; + prevLineNum = lineNum; + } + } + if (i == preEncoding.length) + encoding = preEncoding; + else { + encoding = new int[i]; + System.arraycopy(preEncoding, 0, encoding, 0, i); + } + } + return encoding; + } + + protected int getSize() { + int[] encoding = encode(); + return 2 + encoding.length * 2; + } + + protected void writeContentsTo(DataOutputStream stream) throws IOException { + int[] encoding = encode(); + int entries = encoding.length / 2; + stream.writeShort(entries); + for (int i = 0; i < entries; ++i) { + stream.writeShort(encoding[i * 2]); + stream.writeShort(encoding[i * 2 + 1]); + } + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JLocalVariable.java b/src/fjbg/ch/epfl/lamp/fjbg/JLocalVariable.java new file mode 100644 index 0000000000..b708a37e3c --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JLocalVariable.java @@ -0,0 +1,35 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +/** + * Representation of a local variable or method argument. + * + * @version 1.0 + * @author Michel Schinz + */ + +public class JLocalVariable { + protected final JMethod owner; + protected final JType type; + protected final String name; + protected final int index; + + protected JLocalVariable(FJBGContext context, + JMethod owner, + JType type, + String name, + int index) { + this.owner = owner; + this.type = type; + this.name = name; + this.index = index; + + assert index < 0xFFFF : "index too big for local variable: " + index; + } + + public JMethod getOwner() { return owner; } + public int getIndex() { return index; } + public String getName() { return name; } + public JType getType() { return type; } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JMember.java b/src/fjbg/ch/epfl/lamp/fjbg/JMember.java new file mode 100644 index 0000000000..f2378cf473 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JMember.java @@ -0,0 +1,99 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.util.List; +import java.util.LinkedList; +import java.util.Iterator; + +/** + * Abstract superclass for a Java class, field or method. + * + * @author Nikolay Mihaylov + * @version 1.0 + */ + +abstract public class JMember { + + protected boolean frozen = false; + + protected final FJBGContext context; + + protected String name; + + protected int accessFlags; + + protected final List/**/ attributes = new LinkedList(); + + protected JMember(FJBGContext context) { this.context = context; } + + protected JMember(FJBGContext context, int accessFlags, String name) { + this(context); + this.name = name; + this.accessFlags = accessFlags; + } + + /** + * Gets the access flags of the class. + * @return The int representing the access flags of the class. + */ + public int getAccessFlags() { return accessFlags; } + + /** + * Gets the name of the member. + * @return The string representing the name of the member. + */ + public String getName() { return name; } + + /** + * Gets the type of the objects that are instances of the class. + * @return The type of the instances of the class. + */ + public abstract JType getType(); + + /** + * Gets the class corresponding to/owning this member + * @return The class owning this member or the class itself. + */ + public abstract JClass getJClass(); + + /** + * Gets the constant pool of the class. + * @return The constant pool of the class. + */ + public JConstantPool getConstantPool() { return getJClass().getConstantPool(); } + + public FJBGContext getContext() { return context; } + + /** + * Adds an attribute to the class. + * @param attr The attribute to be added. + */ + public void addAttribute(JAttribute attr) { + assert !frozen; + attributes.add(attr); + } + + /** + * Gets the list of all attributes of the class. + * @return The list of the attributes of the class representation. + */ + public List/**/ getAttributes() { + return attributes; + } + + /** + * Get the attribute with the given name, or null if it doesn't + * exist. + */ + public JAttribute getAttribute(String name) { + Iterator attrIt = getAttributes().iterator(); + while (attrIt.hasNext()) { + JAttribute attr = (JAttribute)attrIt.next(); + if (attr.getName().equals(name)) + return attr; + } + return null; + } + +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JMethod.java b/src/fjbg/ch/epfl/lamp/fjbg/JMethod.java new file mode 100644 index 0000000000..dbddf6547d --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JMethod.java @@ -0,0 +1,133 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.util.LinkedList; +import java.util.Iterator; +import java.io.DataInputStream; +import java.io.IOException; + +/** + * Representation of a Java method. + * + * @version 1.0 + * @author Michel Schinz + */ + +public class JMethod extends JFieldOrMethod { + public final static String CLASS_CONSTRUCTOR_NAME = ""; + public final static String INSTANCE_CONSTRUCTOR_NAME = ""; + + protected final JCode code; + protected final String[] argNames; + + protected final LinkedList/**/ localVariables = + new LinkedList(); + protected int localVariableIndex = 0; + + protected JMethod(FJBGContext context, + JClass owner, + int accessFlags, + String name, + JType returnType, + JType[] argTypes, + String[] argNames) { + super(context, + owner, + accessFlags, + name, + new JMethodType(returnType, argTypes)); + this.argNames = argNames; + + assert argTypes.length == argNames.length; + + if (isAbstract() || isNative()) { + code = null; + } else { + JConstantPool pool = owner.getConstantPool(); + code = context.JCode(owner, this); + addAttribute(context.JCodeAttribute(owner, this)); + + if (!isStatic()) + addNewLocalVariable(owner.getType(), "this"); + + for (int i = 0; i < argTypes.length; ++i) + addNewLocalVariable(argTypes[i], argNames[i]); + } + } + + protected JMethod(FJBGContext context, + JClass owner, + DataInputStream stream) + 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; + } + } + code = null; + } + argNames = null; // TODO get from attribute + } + + public void freeze() throws JCode.OffsetTooBigException { + if (code != null) code.freeze(); + super.freeze(); + } + + public JType getReturnType() { + return ((JMethodType)type).getReturnType(); + } + + public JType[] getArgumentTypes() { + return ((JMethodType)type).getArgumentTypes(); + } + + public String[] getArgumentNames() { + return argNames; + } + + public JCode getCode() { + assert !isAbstract(); + return code; + } + + public JCodeIterator codeIterator() { + return new JCodeIterator(code); + } + + // Local variables + // FIXME : find a better management method for local variables + public JLocalVariable addNewLocalVariable(JType type, String name) { + assert !frozen; + JLocalVariable var = + context.JLocalVariable(this, type, name, localVariableIndex); + localVariableIndex += type.getSize(); + localVariables.add(var); + return var; + } + + public JLocalVariable getLocalVariable(int index) { + for (int i = 0; i < localVariables.size(); i++) { + if (((JLocalVariable)localVariables.get(i)).index == index) + return (JLocalVariable)localVariables.get(i); + } + return null; + } + + public JLocalVariable[] getLocalVariables() { + return (JLocalVariable[])localVariables + .toArray(new JLocalVariable[localVariables.size()]); + } + + public int getMaxLocals() { + return localVariableIndex; + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JMethodType.java b/src/fjbg/ch/epfl/lamp/fjbg/JMethodType.java new file mode 100644 index 0000000000..de136bf53d --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JMethodType.java @@ -0,0 +1,72 @@ +// $Id$ + +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 + */ + +public class JMethodType extends JType { + protected final JType returnType; + protected final JType[] argTypes; + protected String signature = null; + + public final static JMethodType ARGLESS_VOID_FUNCTION = + new JMethodType(JType.VOID, JType.EMPTY_ARRAY); + + public JMethodType(JType returnType, JType[] argTypes) { + this.returnType = returnType; + this.argTypes = argTypes; + } + + public JType getReturnType() { return returnType; } + public JType[] getArgumentTypes() { return argTypes; } + + public int getSize() { + throw new UnsupportedOperationException(); + } + + public String getSignature() { + if (signature == null) { + StringBuffer buf = new StringBuffer(); + buf.append('('); + for (int i = 0; i < argTypes.length; ++i) + buf.append(argTypes[i].getSignature()); + buf.append(')'); + buf.append(returnType.getSignature()); + signature = buf.toString(); + } + return signature; + } + + public int getTag() { return T_UNKNOWN; } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append('('); + for (int i = 0; i < argTypes.length; ++i) + buf.append(argTypes[i].toString()); + buf.append(')'); + buf.append(returnType.toString()); + return buf.toString(); + } + + public int getArgsSize() { + int size = 0; + for (int i = 0; i < argTypes.length; ++i) + size += argTypes[i].getSize(); + return size; + } + + public int getProducedStack() { + return returnType.getSize() - getArgsSize(); + } + + public boolean isCompatibleWith(JType other) { + return false; + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JObjectType.java b/src/fjbg/ch/epfl/lamp/fjbg/JObjectType.java new file mode 100644 index 0000000000..a1930f77ef --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JObjectType.java @@ -0,0 +1,53 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +/** + * Types for Java objects. + * + * @version 1.0 + * @author Michel Schinz + */ + +public class JObjectType extends JReferenceType { + protected final String name; + protected String signature = null; + + public final static JObjectType JAVA_LANG_OBJECT = + new JObjectType("java.lang.Object"); + public final static JObjectType JAVA_LANG_STRING = + new JObjectType("java.lang.String"); + public final static JObjectType CLONEABLE = + new JObjectType("Cloneable"); + public final static JObjectType JAVA_IO_SERIALIZABLE = + new JObjectType("java.io.Serializable"); + + public JObjectType(String name) { + this.name = name; + } + + public int getSize() { return 1; } + + public String getName() { return name; } + + public String getSignature() { + if (signature == null) + signature = "L" + name.replace('.','/') + ";"; + return signature; + } + + public String getDescriptor() { + return name.replace('.','/'); + } + + public int getTag() { return T_OBJECT; } + + public String toString() { return name; } + + public boolean isObjectType() { return true; } + + public boolean isCompatibleWith(JType other) { + return other instanceof JObjectType + || other == JType.REFERENCE; + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JOpcode.java b/src/fjbg/ch/epfl/lamp/fjbg/JOpcode.java new file mode 100644 index 0000000000..6b6d3b6a74 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JOpcode.java @@ -0,0 +1,1264 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +/** + * Definition of opcodes for the JVM. + * + * @author Michel Schinz, Thomas Friedli + * @version 1.0 + */ + +public class JOpcode { + public final String name; + public final int code; + + // The following attributes can be (statically) unknown for some + // instructions, and are therefore not public. To know their value, + // functions have to be used (see JCodeIterator). + protected final int size; + protected final JType[] producedDataTypes; + protected final JType[] consumedDataTypes; + protected final int jumpKind; + protected final int successorCount; + + protected final static int UNKNOWN = Integer.MIN_VALUE; + + protected final static int JMP_NONE = 0; + protected final static int JMP_NEXT = 1; + protected final static int JMP_ALWAYS_S2_OFFSET = 2; + protected final static int JMP_ALWAYS_S4_OFFSET = 3; + protected final static int JMP_MAYBE_S2_OFFSET = 4; + protected final static int JMP_TABLE = 5; + protected final static int JMP_LOOKUP = 6; + + protected final static JType[] NO_DATA = new JType[0]; + + protected final static JType[] INT_TYPE = + new JType[] { JType.INT }; + protected final static JType[] FLOAT_TYPE = + new JType[] { JType.FLOAT }; + protected final static JType[] LONG_TYPE = + new JType[] { JType.LONG }; + protected final static JType[] DOUBLE_TYPE = + new JType[] { JType.DOUBLE }; + protected final static JType[] OBJECT_REF_TYPE = + new JType[] { JObjectType.JAVA_LANG_OBJECT }; + protected final static JType[] ARRAY_REF_TYPE = + new JType[] { new JArrayType(JType.VOID) }; + protected final static JType[] REFERENCE_TYPE = + new JType[] { JType.REFERENCE }; + protected final static JType[] ADDRESS_TYPE = + new JType[] { JType.ADDRESS }; + protected final static JType[] UNKNOWN_TYPE = + new JType[] { JType.UNKNOWN }; + + /// Instruction codes + public final static int cNOP = 0; + public final static int cACONST_NULL = 1; + public final static int cICONST_M1 = 2; + public final static int cICONST_0 = 3; + public final static int cICONST_1 = 4; + public final static int cICONST_2 = 5; + public final static int cICONST_3 = 6; + public final static int cICONST_4 = 7; + public final static int cICONST_5 = 8; + public final static int cLCONST_0 = 9; + public final static int cLCONST_1 = 10; + public final static int cFCONST_0 = 11; + public final static int cFCONST_1 = 12; + public final static int cFCONST_2 = 13; + public final static int cDCONST_0 = 14; + public final static int cDCONST_1 = 15; + public final static int cBIPUSH = 16; + public final static int cSIPUSH = 17; + public final static int cLDC = 18; + public final static int cLDC_W = 19; + public final static int cLDC2_W = 20; + public final static int cILOAD = 21; + public final static int cLLOAD = 22; + public final static int cFLOAD = 23; + public final static int cDLOAD = 24; + public final static int cALOAD = 25; + public final static int cILOAD_0 = 26; + public final static int cILOAD_1 = 27; + public final static int cILOAD_2 = 28; + public final static int cILOAD_3 = 29; + public final static int cLLOAD_0 = 30; + public final static int cLLOAD_1 = 31; + public final static int cLLOAD_2 = 32; + public final static int cLLOAD_3 = 33; + public final static int cFLOAD_0 = 34; + public final static int cFLOAD_1 = 35; + public final static int cFLOAD_2 = 36; + public final static int cFLOAD_3 = 37; + public final static int cDLOAD_0 = 38; + public final static int cDLOAD_1 = 39; + public final static int cDLOAD_2 = 40; + public final static int cDLOAD_3 = 41; + public final static int cALOAD_0 = 42; + public final static int cALOAD_1 = 43; + public final static int cALOAD_2 = 44; + public final static int cALOAD_3 = 45; + public final static int cIALOAD = 46; + public final static int cLALOAD = 47; + public final static int cFALOAD = 48; + public final static int cDALOAD = 49; + public final static int cAALOAD = 50; + public final static int cBALOAD = 51; + public final static int cCALOAD = 52; + public final static int cSALOAD = 53; + public final static int cISTORE = 54; + public final static int cLSTORE = 55; + public final static int cFSTORE = 56; + public final static int cDSTORE = 57; + public final static int cASTORE = 58; + public final static int cISTORE_0 = 59; + public final static int cISTORE_1 = 60; + public final static int cISTORE_2 = 61; + public final static int cISTORE_3 = 62; + public final static int cLSTORE_0 = 63; + public final static int cLSTORE_1 = 64; + public final static int cLSTORE_2 = 65; + public final static int cLSTORE_3 = 66; + public final static int cFSTORE_0 = 67; + public final static int cFSTORE_1 = 68; + public final static int cFSTORE_2 = 69; + public final static int cFSTORE_3 = 70; + public final static int cDSTORE_0 = 71; + public final static int cDSTORE_1 = 72; + public final static int cDSTORE_2 = 73; + public final static int cDSTORE_3 = 74; + public final static int cASTORE_0 = 75; + public final static int cASTORE_1 = 76; + public final static int cASTORE_2 = 77; + public final static int cASTORE_3 = 78; + public final static int cIASTORE = 79; + public final static int cLASTORE = 80; + public final static int cFASTORE = 81; + public final static int cDASTORE = 82; + public final static int cAASTORE = 83; + public final static int cBASTORE = 84; + public final static int cCASTORE = 85; + public final static int cSASTORE = 86; + public final static int cPOP = 87; + public final static int cPOP2 = 88; + public final static int cDUP = 89; + public final static int cDUP_X1 = 90; + public final static int cDUP_X2 = 91; + public final static int cDUP2 = 92; + public final static int cDUP2_X1 = 93; + public final static int cDUP2_X2 = 94; + public final static int cSWAP = 95; + public final static int cIADD = 96; + public final static int cLADD = 97; + public final static int cFADD = 98; + public final static int cDADD = 99; + public final static int cISUB = 100; + public final static int cLSUB = 101; + public final static int cFSUB = 102; + public final static int cDSUB = 103; + public final static int cIMUL = 104; + public final static int cLMUL = 105; + public final static int cFMUL = 106; + public final static int cDMUL = 107; + public final static int cIDIV = 108; + public final static int cLDIV = 109; + public final static int cFDIV = 110; + public final static int cDDIV = 111; + public final static int cIREM = 112; + public final static int cLREM = 113; + public final static int cFREM = 114; + public final static int cDREM = 115; + public final static int cINEG = 116; + public final static int cLNEG = 117; + public final static int cFNEG = 118; + public final static int cDNEG = 119; + public final static int cISHL = 120; + public final static int cLSHL = 121; + public final static int cISHR = 122; + public final static int cLSHR = 123; + public final static int cIUSHR = 124; + public final static int cLUSHR = 125; + public final static int cIAND = 126; + public final static int cLAND = 127; + public final static int cIOR = 128; + public final static int cLOR = 129; + public final static int cIXOR = 130; + public final static int cLXOR = 131; + public final static int cIINC = 132; + public final static int cI2L = 133; + public final static int cI2F = 134; + public final static int cI2D = 135; + public final static int cL2I = 136; + public final static int cL2F = 137; + public final static int cL2D = 138; + public final static int cF2I = 139; + public final static int cF2L = 140; + public final static int cF2D = 141; + public final static int cD2I = 142; + public final static int cD2L = 143; + public final static int cD2F = 144; + public final static int cI2B = 145; + public final static int cI2C = 146; + public final static int cI2S = 147; + public final static int cLCMP = 148; + public final static int cFCMPL = 149; + public final static int cFCMPG = 150; + public final static int cDCMPL = 151; + public final static int cDCMPG = 152; + public final static int cIFEQ = 153; + public final static int cIFNE = 154; + public final static int cIFLT = 155; + public final static int cIFGE = 156; + public final static int cIFGT = 157; + public final static int cIFLE = 158; + public final static int cIF_ICMPEQ = 159; + public final static int cIF_ICMPNE = 160; + public final static int cIF_ICMPLT = 161; + public final static int cIF_ICMPGE = 162; + public final static int cIF_ICMPGT = 163; + public final static int cIF_ICMPLE = 164; + public final static int cIF_ACMPEQ = 165; + public final static int cIF_ACMPNE = 166; + public final static int cGOTO = 167; + public final static int cJSR = 168; + public final static int cRET = 169; + public final static int cTABLESWITCH = 170; + public final static int cLOOKUPSWITCH = 171; + public final static int cIRETURN = 172; + public final static int cLRETURN = 173; + public final static int cFRETURN = 174; + public final static int cDRETURN = 175; + public final static int cARETURN = 176; + public final static int cRETURN = 177; + public final static int cGETSTATIC = 178; + public final static int cPUTSTATIC = 179; + public final static int cGETFIELD = 180; + public final static int cPUTFIELD = 181; + public final static int cINVOKEVIRTUAL = 182; + public final static int cINVOKESPECIAL = 183; + public final static int cINVOKESTATIC = 184; + public final static int cINVOKEINTERFACE = 185; + public final static int cNEW = 187; + public final static int cNEWARRAY = 188; + public final static int cANEWARRAY = 189; + public final static int cARRAYLENGTH = 190; + public final static int cATHROW = 191; + public final static int cCHECKCAST = 192; + public final static int cINSTANCEOF = 193; + public final static int cMONITORENTER = 194; + public final static int cMONITOREXIT = 195; + public final static int cWIDE = 196; + public final static int cMULTIANEWARRAY = 197; + public final static int cIFNULL = 198; + public final static int cIFNONNULL = 199; + public final static int cGOTO_W = 200; + public final static int cJSR_W = 201; + + // Objects representing instructions + public final static JOpcode NOP = + new JOpcode("NOP", cNOP, 1, NO_DATA, NO_DATA, JMP_NEXT); + public final static JOpcode ACONST_NULL = new JOpcode("ACONST_NULL", + cACONST_NULL, + 1, + REFERENCE_TYPE, + NO_DATA, + JMP_NEXT); + public final static JOpcode ICONST_M1 = + new JOpcode("ICONST_M1", cICONST_M1, 1, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ICONST_0 = + new JOpcode("ICONST_0", cICONST_0, 1, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ICONST_1 = + new JOpcode("ICONST_1", cICONST_1, 1, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ICONST_2 = + new JOpcode("ICONST_2", cICONST_2, 1, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ICONST_3 = + new JOpcode("ICONST_3", cICONST_3, 1, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ICONST_4 = + new JOpcode("ICONST_4", cICONST_4, 1, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ICONST_5 = + new JOpcode("ICONST_5", cICONST_5, 1, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode LCONST_0 = + new JOpcode("LCONST_0", cLCONST_0, 1, LONG_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode LCONST_1 = + new JOpcode("LCONST_1", cLCONST_1, 1, LONG_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode FCONST_0 = + new JOpcode("FCONST_0", cFCONST_0, 1, FLOAT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode FCONST_1 = + new JOpcode("FCONST_1", cFCONST_1, 1, FLOAT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode FCONST_2 = + new JOpcode("FCONST_2", cFCONST_2, 1, FLOAT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode DCONST_0 = + new JOpcode("DCONST_0", cDCONST_0, 1, DOUBLE_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode DCONST_1 = + new JOpcode("DCONST_1", cDCONST_1, 1, DOUBLE_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode BIPUSH = + new JOpcode("BIPUSH", cBIPUSH, 2, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode SIPUSH = + new JOpcode("SIPUSH", cSIPUSH, 3, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode LDC = + new JOpcode("LDC", cLDC, 2, UNKNOWN_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode LDC_W = + new JOpcode("LDC_W", cLDC_W, 3, UNKNOWN_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode LDC2_W = + new JOpcode("LDC2_W", cLDC2_W, 3, UNKNOWN_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ILOAD = + new JOpcode("ILOAD", cILOAD, 2, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode LLOAD = + new JOpcode("LLOAD", cLLOAD, 2, LONG_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode FLOAD = + new JOpcode("FLOAD", cFLOAD, 2, FLOAT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode DLOAD = + new JOpcode("DLOAD", cDLOAD, 2, DOUBLE_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ALOAD = + new JOpcode("ALOAD", cALOAD, 2, REFERENCE_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ILOAD_0 = + new JOpcode("ILOAD_0", cILOAD_0, 1, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ILOAD_1 = + new JOpcode("ILOAD_1", cILOAD_1, 1, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ILOAD_2 = + new JOpcode("ILOAD_2", cILOAD_2, 1, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ILOAD_3 = + new JOpcode("ILOAD_3", cILOAD_3, 1, INT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode LLOAD_0 = + new JOpcode("LLOAD_0", cLLOAD_0, 1, LONG_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode LLOAD_1 = + new JOpcode("LLOAD_1", cLLOAD_1, 1, LONG_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode LLOAD_2 = + new JOpcode("LLOAD_2", cLLOAD_2, 1, LONG_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode LLOAD_3 = + new JOpcode("LLOAD_3", cLLOAD_3, 1, LONG_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode FLOAD_0 = + new JOpcode("FLOAD_0", cFLOAD_0, 1, FLOAT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode FLOAD_1 = + new JOpcode("FLOAD_1", cFLOAD_1, 1, FLOAT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode FLOAD_2 = + new JOpcode("FLOAD_2", cFLOAD_2, 1, FLOAT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode FLOAD_3 = + new JOpcode("FLOAD_3", cFLOAD_3, 1, FLOAT_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode DLOAD_0 = + new JOpcode("DLOAD_0", cDLOAD_0, 1, DOUBLE_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode DLOAD_1 = + new JOpcode("DLOAD_1", cDLOAD_1, 1, DOUBLE_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode DLOAD_2 = + new JOpcode("DLOAD_2", cDLOAD_2, 1, DOUBLE_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode DLOAD_3 = + new JOpcode("DLOAD_3", cDLOAD_3, 1, DOUBLE_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ALOAD_0 = + new JOpcode("ALOAD_0", cALOAD_0, 1, REFERENCE_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ALOAD_1 = + new JOpcode("ALOAD_1", cALOAD_1, 1, REFERENCE_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ALOAD_2 = + new JOpcode("ALOAD_2", cALOAD_2, 1, REFERENCE_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode ALOAD_3 = + new JOpcode("ALOAD_3", cALOAD_3, 1, REFERENCE_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode IALOAD = + new JOpcode("IALOAD", + cIALOAD, + 1, + INT_TYPE, + new JType[] {JType.INT, JArrayType.INT}, + JMP_NEXT); + public final static JOpcode LALOAD = + new JOpcode("LALOAD", + cLALOAD, + 1, + LONG_TYPE, + new JType[] {JType.INT, JArrayType.LONG}, + JMP_NEXT); + public final static JOpcode FALOAD = + new JOpcode("FALOAD", + cFALOAD, + 1, + FLOAT_TYPE, + new JType[] {JType.INT, JArrayType.FLOAT}, + JMP_NEXT); + public final static JOpcode DALOAD = + new JOpcode("DALOAD", + cDALOAD, + 1, + DOUBLE_TYPE, + new JType[] {JType.INT, JArrayType.DOUBLE}, + JMP_NEXT); + public final static JOpcode AALOAD = + new JOpcode("AALOAD", + cAALOAD, + 1, + REFERENCE_TYPE, + new JType[] {JType.INT, JArrayType.REFERENCE}, + JMP_NEXT); + public final static JOpcode BALOAD = + new JOpcode("BALOAD", + cBALOAD, + 1, + INT_TYPE, + new JType[] {JType.INT, new JArrayType(JType.UNKNOWN)}, + JMP_NEXT); + public final static JOpcode CALOAD = + new JOpcode("CALOAD", + cCALOAD, + 1, + INT_TYPE, + new JType[] {JType.INT, JArrayType.CHAR}, + JMP_NEXT); + public final static JOpcode SALOAD = + new JOpcode("SALOAD", + cSALOAD, + 1, + INT_TYPE, + new JType[] {JType.INT, JArrayType.SHORT}, + JMP_NEXT); + public final static JOpcode ISTORE = + new JOpcode("ISTORE", cISTORE, 2, NO_DATA, INT_TYPE, JMP_NEXT); + public final static JOpcode LSTORE = + new JOpcode("LSTORE", cLSTORE, 2, NO_DATA, LONG_TYPE, JMP_NEXT); + public final static JOpcode FSTORE = + new JOpcode("FSTORE", cFSTORE, 2, NO_DATA, FLOAT_TYPE, JMP_NEXT); + public final static JOpcode DSTORE = + new JOpcode("DSTORE", cDSTORE, 2, NO_DATA, DOUBLE_TYPE, JMP_NEXT); + public final static JOpcode ASTORE = + new JOpcode("ASTORE", cASTORE, 2, NO_DATA, REFERENCE_TYPE, JMP_NEXT); + public final static JOpcode ISTORE_0 = + new JOpcode("ISTORE_0", cISTORE_0, 1, NO_DATA, INT_TYPE, JMP_NEXT); + public final static JOpcode ISTORE_1 = + new JOpcode("ISTORE_1", cISTORE_1, 1, NO_DATA, INT_TYPE, JMP_NEXT); + public final static JOpcode ISTORE_2 = + new JOpcode("ISTORE_2", cISTORE_2, 1, NO_DATA, INT_TYPE, JMP_NEXT); + public final static JOpcode ISTORE_3 = + new JOpcode("ISTORE_3", cISTORE_3, 1, NO_DATA, INT_TYPE, JMP_NEXT); + public final static JOpcode LSTORE_0 = + new JOpcode("LSTORE_0", cLSTORE_0, 1, NO_DATA, LONG_TYPE, JMP_NEXT); + public final static JOpcode LSTORE_1 = + new JOpcode("LSTORE_1", cLSTORE_1, 1, NO_DATA, LONG_TYPE, JMP_NEXT); + public final static JOpcode LSTORE_2 = + new JOpcode("LSTORE_2", cLSTORE_2, 1, NO_DATA, LONG_TYPE, JMP_NEXT); + public final static JOpcode LSTORE_3 = + new JOpcode("LSTORE_3", cLSTORE_3, 1, NO_DATA, LONG_TYPE, JMP_NEXT); + public final static JOpcode FSTORE_0 = + new JOpcode("FSTORE_0", cFSTORE_0, 1, NO_DATA, FLOAT_TYPE, JMP_NEXT); + public final static JOpcode FSTORE_1 = + new JOpcode("FSTORE_1", cFSTORE_1, 1, NO_DATA, FLOAT_TYPE, JMP_NEXT); + public final static JOpcode FSTORE_2 = + new JOpcode("FSTORE_2", cFSTORE_2, 1, NO_DATA, FLOAT_TYPE, JMP_NEXT); + public final static JOpcode FSTORE_3 = + new JOpcode("FSTORE_3", cFSTORE_3, 1, NO_DATA, FLOAT_TYPE, JMP_NEXT); + public final static JOpcode DSTORE_0 = + new JOpcode("DSTORE_0", cDSTORE_0, 1, NO_DATA, DOUBLE_TYPE, JMP_NEXT); + public final static JOpcode DSTORE_1 = + new JOpcode("DSTORE_1", cDSTORE_1, 1, NO_DATA, DOUBLE_TYPE, JMP_NEXT); + public final static JOpcode DSTORE_2 = + new JOpcode("DSTORE_2", cDSTORE_2, 1, NO_DATA, DOUBLE_TYPE, JMP_NEXT); + public final static JOpcode DSTORE_3 = + new JOpcode("DSTORE_3", cDSTORE_3, 1, NO_DATA, DOUBLE_TYPE, JMP_NEXT); + public final static JOpcode ASTORE_0 = new JOpcode("ASTORE_0", + cASTORE_0, + 1, + NO_DATA, + REFERENCE_TYPE, + JMP_NEXT); + public final static JOpcode ASTORE_1 = new JOpcode("ASTORE_1", + cASTORE_1, + 1, + NO_DATA, + REFERENCE_TYPE, + JMP_NEXT); + public final static JOpcode ASTORE_2 = new JOpcode("ASTORE_2", + cASTORE_2, + 1, + NO_DATA, + REFERENCE_TYPE, + JMP_NEXT); + public final static JOpcode ASTORE_3 = new JOpcode("ASTORE_3", + cASTORE_3, + 1, + NO_DATA, + REFERENCE_TYPE, + JMP_NEXT); + public final static JOpcode IASTORE = + new JOpcode("IASTORE", + cIASTORE, + 1, + NO_DATA, + new JType[] { JType.INT, + JType.INT, + JArrayType.INT}, + JMP_NEXT); + public final static JOpcode LASTORE = + new JOpcode("LASTORE", + cLASTORE, + 1, + NO_DATA, + new JType[] { JType.LONG, + JType.INT, + JArrayType.LONG}, + JMP_NEXT); + public final static JOpcode FASTORE = + new JOpcode("FASTORE", + cFASTORE, + 1, + NO_DATA, + new JType[] { JType.FLOAT, + JType.INT, + JArrayType.FLOAT}, + JMP_NEXT); + public final static JOpcode DASTORE = + new JOpcode("DASTORE", + cDASTORE, + 1, + NO_DATA, + new JType[] { JType.DOUBLE, + JType.INT, + JArrayType.DOUBLE}, + JMP_NEXT); + public final static JOpcode AASTORE = + new JOpcode("AASTORE", + cAASTORE, + 1, + NO_DATA, + new JType[] { JType.REFERENCE, + JType.INT, + JArrayType.REFERENCE}, + JMP_NEXT); + public final static JOpcode BASTORE = + new JOpcode("BASTORE", + cBASTORE, + 1, + NO_DATA, + new JType[] { JType.INT, + JType.INT, + new JArrayType(JType.UNKNOWN)}, + JMP_NEXT); + public final static JOpcode CASTORE = + new JOpcode("CASTORE", + cCASTORE, + 1, + NO_DATA, + new JType[] { JType.INT, + JType.INT, + JArrayType.CHAR}, + JMP_NEXT); + public final static JOpcode SASTORE = + new JOpcode("SASTORE", + cSASTORE, + 1, + NO_DATA, + new JType[] { JType.INT, + JType.INT, + JArrayType.SHORT}, + JMP_NEXT); + public final static JOpcode POP = + new JOpcode("POP", cPOP, 1, NO_DATA, UNKNOWN_TYPE, JMP_NEXT); + public final static JOpcode POP2 = + new JOpcode("POP2", cPOP2, 1, NO_DATA, UNKNOWN_TYPE, JMP_NEXT); + public final static JOpcode DUP = + new JOpcode("DUP", cDUP, 1, UNKNOWN_TYPE, UNKNOWN_TYPE, JMP_NEXT); + public final static JOpcode DUP_X1 = new JOpcode("DUP_X1", + cDUP_X1, + 1, + UNKNOWN_TYPE, + UNKNOWN_TYPE, + JMP_NEXT); + public final static JOpcode DUP_X2 = new JOpcode("DUP_X2", + cDUP_X2, + 1, + UNKNOWN_TYPE, + UNKNOWN_TYPE, + JMP_NEXT); + public final static JOpcode DUP2 = + new JOpcode("DUP2", cDUP2, 1, UNKNOWN_TYPE, UNKNOWN_TYPE, JMP_NEXT); + public final static JOpcode DUP2_X1 = new JOpcode("DUP2_X1", + cDUP2_X1, + 1, + UNKNOWN_TYPE, + UNKNOWN_TYPE, + JMP_NEXT); + public final static JOpcode DUP2_X2 = new JOpcode("DUP2_X2", + cDUP2_X2, + 1, + UNKNOWN_TYPE, + UNKNOWN_TYPE, + JMP_NEXT); + public final static JOpcode SWAP = + new JOpcode("SWAP", cSWAP, 1, UNKNOWN_TYPE, UNKNOWN_TYPE, JMP_NEXT); + public final static JOpcode IADD = + new JOpcode("IADD", + cIADD, + 1, + INT_TYPE, + new JType[] { JType.INT, JType.INT }, + JMP_NEXT); + public final static JOpcode LADD = + new JOpcode("LADD", + cLADD, + 1, + LONG_TYPE, + new JType[] { JType.LONG, JType.LONG }, + JMP_NEXT); + public final static JOpcode FADD = + new JOpcode("FADD", + cFADD, + 1, + FLOAT_TYPE, + new JType[] { JType.FLOAT, JType.FLOAT }, + JMP_NEXT); + public final static JOpcode DADD = + new JOpcode("DADD", + cDADD, + 1, + DOUBLE_TYPE, + new JType[] { JType.DOUBLE, JType.DOUBLE }, + JMP_NEXT); + public final static JOpcode ISUB = + new JOpcode("ISUB", + cISUB, + 1, + INT_TYPE, + new JType[] {JType.INT, JType.INT }, + JMP_NEXT); + public final static JOpcode LSUB = + new JOpcode("LSUB", + cLSUB, + 1, + LONG_TYPE, + new JType[] { JType.LONG, JType.LONG }, + JMP_NEXT); + public final static JOpcode FSUB = + new JOpcode("FSUB", + cFSUB, + 1, + FLOAT_TYPE, + new JType[] { JType.FLOAT, JType.FLOAT }, + JMP_NEXT); + public final static JOpcode DSUB = + new JOpcode("DSUB", + cDSUB, + 1, + DOUBLE_TYPE, + new JType[] { JType.DOUBLE, JType.DOUBLE }, + JMP_NEXT); + public final static JOpcode IMUL = + new JOpcode("IMUL", + cIMUL, + 1, + INT_TYPE, + new JType[] {JType.INT, JType.INT }, + JMP_NEXT); + public final static JOpcode LMUL = + new JOpcode("LMUL", + cLMUL, + 1, + LONG_TYPE, + new JType[] { JType.LONG, JType.LONG }, + JMP_NEXT); + public final static JOpcode FMUL = + new JOpcode("FMUL", + cFMUL, + 1, + FLOAT_TYPE, + new JType[] { JType.FLOAT, JType.FLOAT }, + JMP_NEXT); + public final static JOpcode DMUL = + new JOpcode("DMUL", + cDMUL, + 1, + DOUBLE_TYPE, + new JType[] { JType.DOUBLE, JType.DOUBLE }, + JMP_NEXT); + public final static JOpcode IDIV = + new JOpcode("IDIV", + cIDIV, + 1, + INT_TYPE, + new JType[] {JType.INT, JType.INT }, + JMP_NEXT); + public final static JOpcode LDIV = + new JOpcode("LDIV", + cLDIV, + 1, + LONG_TYPE, + new JType[] { JType.LONG, JType.LONG }, + JMP_NEXT); + public final static JOpcode FDIV = + new JOpcode("FDIV", + cFDIV, + 1, + FLOAT_TYPE, + new JType[] { JType.FLOAT, JType.FLOAT }, + JMP_NEXT); + public final static JOpcode DDIV = + new JOpcode("DDIV", + cDDIV, + 1, + DOUBLE_TYPE, + new JType[] { JType.DOUBLE, JType.DOUBLE }, + JMP_NEXT); + public final static JOpcode IREM = + new JOpcode("IREM", + cIREM, + 1, + INT_TYPE, + new JType[] {JType.INT, JType.INT }, + JMP_NEXT); + public final static JOpcode LREM = + new JOpcode("LREM", + cLREM, + 1, + LONG_TYPE, + new JType[] { JType.LONG, JType.LONG }, + JMP_NEXT); + public final static JOpcode FREM = + new JOpcode("FREM", + cFREM, + 1, + FLOAT_TYPE, + new JType[] { JType.FLOAT, JType.FLOAT }, + JMP_NEXT); + public final static JOpcode DREM = + new JOpcode("DREM", + cDREM, + 1, + DOUBLE_TYPE, + new JType[] { JType.DOUBLE, JType.DOUBLE }, + JMP_NEXT); + public final static JOpcode INEG = + new JOpcode("INEG", cINEG, 1, INT_TYPE, INT_TYPE, JMP_NEXT); + public final static JOpcode LNEG = + new JOpcode("LNEG", cLNEG, 1, LONG_TYPE, LONG_TYPE, JMP_NEXT); + public final static JOpcode FNEG = + new JOpcode("FNEG", cFNEG, 1, FLOAT_TYPE, FLOAT_TYPE, JMP_NEXT); + public final static JOpcode DNEG = + new JOpcode("DNEG", cDNEG, 1, DOUBLE_TYPE, DOUBLE_TYPE, JMP_NEXT); + public final static JOpcode ISHL = + new JOpcode("ISHL", cISHL, + 1, + INT_TYPE, + new JType[] { JType.INT, JType.INT }, + JMP_NEXT); + public final static JOpcode LSHL = + new JOpcode("LSHL", + cLSHL, + 1, + LONG_TYPE, + new JType [] { JType.INT, JType.LONG }, + JMP_NEXT); + public final static JOpcode ISHR = + new JOpcode("ISHR", + cISHR, + 1, + INT_TYPE, + new JType[] { JType.INT, JType.INT }, + JMP_NEXT); + public final static JOpcode LSHR = + new JOpcode("LSHR", + cLSHR, + 1, + LONG_TYPE, + new JType[] { JType.INT, JType.LONG }, + JMP_NEXT); + public final static JOpcode IUSHR = + new JOpcode("IUSHR", + cIUSHR, + 1, + INT_TYPE, + new JType[] { JType.INT, JType.INT }, + JMP_NEXT); + public final static JOpcode LUSHR = + new JOpcode("LUSHR", + cLUSHR, + 1, + LONG_TYPE, + new JType[] { JType.INT, JType.LONG }, + JMP_NEXT); + public final static JOpcode IAND = + new JOpcode("IAND", + cIAND, + 1, + INT_TYPE, + new JType[] { JType.INT, JType.INT }, + JMP_NEXT); + public final static JOpcode LAND = + new JOpcode("LAND", + cLAND, + 1, + LONG_TYPE, + new JType[] { JType.LONG, JType.LONG }, + JMP_NEXT); + public final static JOpcode IOR = + new JOpcode("IOR", + cIOR, + 1, + INT_TYPE, + new JType[] { JType.INT, JType.INT }, + JMP_NEXT); + public final static JOpcode LOR = + new JOpcode("LOR", + cLOR, + 1, + LONG_TYPE, + new JType[] { JType.LONG, JType.LONG }, + JMP_NEXT); + public final static JOpcode IXOR = + new JOpcode("IXOR", + cIXOR, + 1, + INT_TYPE, + new JType[] { JType.INT, JType.INT }, + JMP_NEXT); + public final static JOpcode LXOR = + new JOpcode("LXOR", + cLXOR, + 1, + LONG_TYPE, + new JType[] { JType.LONG, JType.LONG }, + JMP_NEXT); + public final static JOpcode IINC = + new JOpcode("IINC", cIINC, 3, NO_DATA, NO_DATA, JMP_NEXT); + public final static JOpcode I2L = + new JOpcode("I2L", cI2L, 1, LONG_TYPE, INT_TYPE, JMP_NEXT); + public final static JOpcode I2F = + new JOpcode("I2F", cI2F, 1, FLOAT_TYPE, INT_TYPE, JMP_NEXT); + public final static JOpcode I2D = + new JOpcode("I2D", cI2D, 1, DOUBLE_TYPE, INT_TYPE, JMP_NEXT); + public final static JOpcode L2I = + new JOpcode("L2I", cL2I, 1, INT_TYPE, LONG_TYPE, JMP_NEXT); + public final static JOpcode L2F = + new JOpcode("L2F", cL2F, 1, FLOAT_TYPE, LONG_TYPE, JMP_NEXT); + public final static JOpcode L2D = + new JOpcode("L2D", cL2D, 1, DOUBLE_TYPE, LONG_TYPE, JMP_NEXT); + public final static JOpcode F2I = + new JOpcode("F2I", cF2I, 1, INT_TYPE, FLOAT_TYPE, JMP_NEXT); + public final static JOpcode F2L = + new JOpcode("F2L", cF2L, 1, LONG_TYPE, FLOAT_TYPE, JMP_NEXT); + public final static JOpcode F2D = + new JOpcode("F2D", cF2D, 1, DOUBLE_TYPE, FLOAT_TYPE, JMP_NEXT); + public final static JOpcode D2I = + new JOpcode("D2I", cD2I, 1, INT_TYPE, DOUBLE_TYPE, JMP_NEXT); + public final static JOpcode D2L = + new JOpcode("D2L", cD2L, 1, LONG_TYPE, DOUBLE_TYPE, JMP_NEXT); + public final static JOpcode D2F = + new JOpcode("D2F", cD2F, 1, FLOAT_TYPE, DOUBLE_TYPE, JMP_NEXT); + public final static JOpcode I2B = + new JOpcode("I2B", cI2B, 1, INT_TYPE, INT_TYPE, JMP_NEXT); + public final static JOpcode I2C = + new JOpcode("I2C", cI2C, 1, INT_TYPE, INT_TYPE, JMP_NEXT); + public final static JOpcode I2S = + new JOpcode("I2S", cI2S, 1, INT_TYPE, INT_TYPE, JMP_NEXT); + public final static JOpcode LCMP = + new JOpcode("LCMP", + cLCMP, + 1, + INT_TYPE, + new JType[] { JType.LONG, JType.LONG }, + JMP_NEXT); + public final static JOpcode FCMPL = + new JOpcode("FCMPL", + cFCMPL, + 1, + INT_TYPE, + new JType[] { JType.FLOAT, JType.FLOAT }, + JMP_NEXT); + public final static JOpcode FCMPG = + new JOpcode("FCMPG", + cFCMPG, + 1, + INT_TYPE, + new JType[] { JType.FLOAT, JType.FLOAT }, + JMP_NEXT); + public final static JOpcode DCMPL = + new JOpcode("DCMPL", + cDCMPL, + 1, + INT_TYPE, + new JType[] { JType.LONG, JType.LONG }, + JMP_NEXT); + public final static JOpcode DCMPG = + new JOpcode("DCMPG", + cDCMPG, + 1, + INT_TYPE, + new JType[] { JType.DOUBLE, JType.DOUBLE }, + JMP_NEXT); + public final static JOpcode IFEQ = + new JOpcode("IFEQ", cIFEQ, 3, NO_DATA, INT_TYPE, JMP_MAYBE_S2_OFFSET); + public final static JOpcode IFNE = + new JOpcode("IFNE", cIFNE, 3, NO_DATA, INT_TYPE, JMP_MAYBE_S2_OFFSET); + public final static JOpcode IFLT = + new JOpcode("IFLT", cIFLT, 3, NO_DATA, INT_TYPE, JMP_MAYBE_S2_OFFSET); + public final static JOpcode IFGE = + new JOpcode("IFGE", cIFGE, 3, NO_DATA, INT_TYPE, JMP_MAYBE_S2_OFFSET); + public final static JOpcode IFGT = + new JOpcode("IFGT", cIFGT, 3, NO_DATA, INT_TYPE, JMP_MAYBE_S2_OFFSET); + public final static JOpcode IFLE = + new JOpcode("IFLE", cIFLE, 3, NO_DATA, INT_TYPE, JMP_MAYBE_S2_OFFSET); + public final static JOpcode IF_ICMPEQ = + new JOpcode("IF_ICMPEQ", + cIF_ICMPEQ, + 3, + NO_DATA, + new JType[] { JType.INT, JType.INT }, + JMP_MAYBE_S2_OFFSET); + public final static JOpcode IF_ICMPNE = + new JOpcode("IF_ICMPNE", + cIF_ICMPNE, + 3, + NO_DATA, + new JType[] { JType.INT, JType.INT }, + JMP_MAYBE_S2_OFFSET); + public final static JOpcode IF_ICMPLT = + new JOpcode("IF_ICMPLT", + cIF_ICMPLT, + 3, + NO_DATA, + new JType[] { JType.INT, JType.INT }, + JMP_MAYBE_S2_OFFSET); + public final static JOpcode IF_ICMPGE = + new JOpcode("IF_ICMPGE", + cIF_ICMPGE, + 3, + NO_DATA, + new JType[] { JType.INT, JType.INT }, + JMP_MAYBE_S2_OFFSET); + public final static JOpcode IF_ICMPGT = + new JOpcode("IF_ICMPGT", + cIF_ICMPGT, + 3, + NO_DATA, + new JType[] { JType.INT, JType.INT }, + JMP_MAYBE_S2_OFFSET); + public final static JOpcode IF_ICMPLE = + new JOpcode("IF_ICMPLE", + cIF_ICMPLE, + 3, + NO_DATA, + new JType[] { JType.INT, JType.INT }, + JMP_MAYBE_S2_OFFSET); + public final static JOpcode IF_ACMPEQ = + new JOpcode("IF_ACMPEQ", + cIF_ACMPEQ, + 3, + NO_DATA, + new JType[] { JType.REFERENCE, JType.REFERENCE }, + JMP_MAYBE_S2_OFFSET); + public final static JOpcode IF_ACMPNE = + new JOpcode("IF_ACMPNE", + cIF_ACMPNE, + 3, + NO_DATA, + new JType[] { JType.REFERENCE, JType.REFERENCE }, + JMP_MAYBE_S2_OFFSET); + public final static JOpcode GOTO = + new JOpcode("GOTO", cGOTO, 3, NO_DATA, NO_DATA, JMP_ALWAYS_S2_OFFSET); + public final static JOpcode JSR = + new JOpcode("JSR", cJSR, 3, ADDRESS_TYPE, NO_DATA, JMP_ALWAYS_S2_OFFSET); + public final static JOpcode RET = + new JOpcode("RET", cRET, 2, NO_DATA, NO_DATA, JMP_NONE); + public final static JOpcode TABLESWITCH = new JOpcode("TABLESWITCH", + cTABLESWITCH, + UNKNOWN, + NO_DATA, + INT_TYPE, + JMP_TABLE); + public final static JOpcode LOOKUPSWITCH = new JOpcode("LOOKUPSWITCH", + cLOOKUPSWITCH, + UNKNOWN, + NO_DATA, + INT_TYPE, + JMP_LOOKUP); + public final static JOpcode IRETURN = + new JOpcode("IRETURN", cIRETURN, 1, NO_DATA, INT_TYPE, JMP_NONE); + public final static JOpcode LRETURN = + new JOpcode("LRETURN", cLRETURN, 1, NO_DATA, LONG_TYPE, JMP_NONE); + public final static JOpcode FRETURN = + new JOpcode("FRETURN", cFRETURN, 1, NO_DATA, FLOAT_TYPE, JMP_NONE); + public final static JOpcode DRETURN = + new JOpcode("DRETURN", cDRETURN, 1, NO_DATA, DOUBLE_TYPE, JMP_NONE); + public final static JOpcode ARETURN = new JOpcode("ARETURN", + cARETURN, + 1, + NO_DATA, + OBJECT_REF_TYPE, + JMP_NONE); + public final static JOpcode RETURN = + new JOpcode("RETURN", cRETURN, 1, NO_DATA, NO_DATA, JMP_NONE); + public final static JOpcode GETSTATIC = new JOpcode("GETSTATIC", + cGETSTATIC, + 3, + UNKNOWN_TYPE, + NO_DATA, + JMP_NEXT); + public final static JOpcode PUTSTATIC = new JOpcode("PUTSTATIC", + cPUTSTATIC, + 3, + NO_DATA, + UNKNOWN_TYPE, + JMP_NEXT); + public final static JOpcode GETFIELD = new JOpcode("GETFIELD", + cGETFIELD, + 3, + UNKNOWN_TYPE, + OBJECT_REF_TYPE, + JMP_NEXT); + public final static JOpcode PUTFIELD = + new JOpcode("PUTFIELD", cPUTFIELD, 3, NO_DATA, UNKNOWN_TYPE, JMP_NEXT); + public final static JOpcode INVOKEVIRTUAL = new JOpcode("INVOKEVIRTUAL", + cINVOKEVIRTUAL, + 3, + NO_DATA, + UNKNOWN_TYPE, + JMP_NEXT); + public final static JOpcode INVOKESPECIAL = new JOpcode("INVOKESPECIAL", + cINVOKESPECIAL, + 3, + NO_DATA, + UNKNOWN_TYPE, + JMP_NEXT); + public final static JOpcode INVOKESTATIC = new JOpcode("INVOKESTATIC", + cINVOKESTATIC, + 3, + NO_DATA, + UNKNOWN_TYPE, + JMP_NEXT); + public final static JOpcode INVOKEINTERFACE = + new JOpcode("INVOKEINTERFACE", + cINVOKEINTERFACE, + 5, + NO_DATA, + UNKNOWN_TYPE, + JMP_NEXT); + public final static JOpcode NEW = + new JOpcode("NEW", cNEW, 3, OBJECT_REF_TYPE, NO_DATA, JMP_NEXT); + public final static JOpcode NEWARRAY = + new JOpcode("NEWARRAY", + cNEWARRAY, + 2, + ARRAY_REF_TYPE, + INT_TYPE, + JMP_NEXT); + public final static JOpcode ANEWARRAY = + new JOpcode("ANEWARRAY", + cANEWARRAY, + 3, + ARRAY_REF_TYPE, + INT_TYPE, + JMP_NEXT); + public final static JOpcode ARRAYLENGTH = new JOpcode("ARRAYLENGTH", + cARRAYLENGTH, + 1, + INT_TYPE, + ARRAY_REF_TYPE, + JMP_NEXT); + public final static JOpcode ATHROW = new JOpcode("ATHROW", + cATHROW, + 1, + OBJECT_REF_TYPE, + OBJECT_REF_TYPE, + JMP_NONE); + public final static JOpcode CHECKCAST = new JOpcode("CHECKCAST", + cCHECKCAST, + 3, + OBJECT_REF_TYPE, + OBJECT_REF_TYPE, + JMP_NEXT); + public final static JOpcode INSTANCEOF = new JOpcode("INSTANCEOF", + cINSTANCEOF, + 3, + INT_TYPE, + OBJECT_REF_TYPE, + JMP_NEXT); + public final static JOpcode MONITORENTER = new JOpcode("MONITORENTER", + cMONITORENTER, + 1, + NO_DATA, + OBJECT_REF_TYPE, + JMP_NEXT); + public final static JOpcode MONITOREXIT = new JOpcode("MONITOREXIT", + cMONITOREXIT, + 1, + NO_DATA, + OBJECT_REF_TYPE, + JMP_NEXT); + public final static JOpcode WIDE = new JOpcode("WIDE", + cWIDE, + UNKNOWN, + UNKNOWN_TYPE, + UNKNOWN_TYPE, + JMP_NEXT); + public final static JOpcode MULTIANEWARRAY = new JOpcode("MULTIANEWARRAY", + cMULTIANEWARRAY, + 4, + ARRAY_REF_TYPE, + UNKNOWN_TYPE, + JMP_NEXT); + public final static JOpcode IFNULL = new JOpcode("IFNULL", + cIFNULL, + 3, + NO_DATA, + REFERENCE_TYPE, + JMP_MAYBE_S2_OFFSET); + public final static JOpcode IFNONNULL = new JOpcode("IFNONNULL", + cIFNONNULL, + 3, + NO_DATA, + REFERENCE_TYPE, + JMP_MAYBE_S2_OFFSET); + public final static JOpcode GOTO_W = new JOpcode("GOTO_W", + cGOTO_W, + 5, + NO_DATA, + NO_DATA, + JMP_ALWAYS_S4_OFFSET); + public final static JOpcode JSR_W = + new JOpcode("JSR_W", cJSR_W, 5, ADDRESS_TYPE, NO_DATA, JMP_NEXT); + + public final static JOpcode[] OPCODES = { + NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, + ICONST_2, ICONST_3, ICONST_4, ICONST_5, LCONST_0, + LCONST_1, FCONST_0, FCONST_1, FCONST_2, DCONST_0, + DCONST_1, BIPUSH, SIPUSH, LDC, LDC_W, + LDC2_W, ILOAD, LLOAD, FLOAD, DLOAD, + ALOAD, ILOAD_0, ILOAD_1, ILOAD_2, ILOAD_3, + LLOAD_0, LLOAD_1, LLOAD_2, LLOAD_3, FLOAD_0, + FLOAD_1, FLOAD_2, FLOAD_3, DLOAD_0, DLOAD_1, + DLOAD_2, DLOAD_3, ALOAD_0, ALOAD_1, ALOAD_2, + ALOAD_3, IALOAD, LALOAD, FALOAD, DALOAD, + AALOAD, BALOAD, CALOAD, SALOAD, ISTORE, + LSTORE, FSTORE, DSTORE, ASTORE, ISTORE_0, + ISTORE_1, ISTORE_2, ISTORE_3, LSTORE_0, LSTORE_1, + LSTORE_2, LSTORE_3, FSTORE_0, FSTORE_1, FSTORE_2, + FSTORE_3, DSTORE_0, DSTORE_1, DSTORE_2, DSTORE_3, + ASTORE_0, ASTORE_1, ASTORE_2, ASTORE_3, IASTORE, + LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, + CASTORE, SASTORE, POP, POP2, DUP, + DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, + SWAP, IADD, LADD, FADD, DADD, + ISUB, LSUB, FSUB, DSUB, IMUL, + LMUL, FMUL, DMUL, IDIV, LDIV, + FDIV, DDIV, IREM, LREM, FREM, + DREM, INEG, LNEG, FNEG, DNEG, + ISHL, LSHL, ISHR, LSHR, IUSHR, + LUSHR, IAND, LAND, IOR, LOR, + IXOR, LXOR, IINC, I2L, I2F, + I2D, L2I, L2F, L2D, F2I, + F2L, F2D, D2I, D2L, D2F, + I2B, I2C, I2S, LCMP, FCMPL, + FCMPG, DCMPL, DCMPG, IFEQ, IFNE, + IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, + IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, + IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, RET, + TABLESWITCH, LOOKUPSWITCH, IRETURN, LRETURN, FRETURN, + DRETURN, ARETURN, RETURN, GETSTATIC, PUTSTATIC, + GETFIELD, PUTFIELD, INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, + INVOKEINTERFACE, null, NEW, NEWARRAY, ANEWARRAY, + ARRAYLENGTH, ATHROW, CHECKCAST, INSTANCEOF, MONITORENTER, + MONITOREXIT, WIDE, MULTIANEWARRAY, IFNULL, IFNONNULL, + GOTO_W, JSR_W + }; + + protected JOpcode(String name, + int code, + int size, + JType[] producedDataTypes, + JType[] consumedDataTypes, + int jumpKind) { + this.name = name; + this.code = code; + this.size = size; + this.producedDataTypes = producedDataTypes; + this.consumedDataTypes = consumedDataTypes; + this.jumpKind = jumpKind; + switch (jumpKind) { + case JMP_NONE: successorCount = 0; break; + case JMP_NEXT: successorCount = 1; break; + case JMP_ALWAYS_S2_OFFSET: successorCount = 1; break; + case JMP_ALWAYS_S4_OFFSET: successorCount = 1; break; + case JMP_MAYBE_S2_OFFSET: successorCount = 2; break; + case JMP_TABLE: successorCount = UNKNOWN; break; + case JMP_LOOKUP: successorCount = UNKNOWN; break; + default: successorCount = UNKNOWN; break; + } + } + + public String toString() { return name; } + protected int getSize() { return size; } + protected JType[] getProducedDataTypes() { return producedDataTypes; } + protected JType[] getConsumedDataTypes() { return consumedDataTypes; } + + protected int getProducedDataSize() { + if (producedDataTypes != UNKNOWN_TYPE) + return JType.getTotalSize(producedDataTypes); + else { + switch (code) { + case cLDC: case cLDC_W: case cBALOAD: + return 1; + case cLDC2_W: case cDUP: case cSWAP: + return 2; + case cDUP_X1: + return 3; + case cDUP_X2: case cDUP2: + return 4; + case cDUP2_X1: + return 5; + case cDUP2_X2: + return 6; + default: + throw new Error(this.toString()); + } + } + } + + protected int getConsumedDataSize() { + if (consumedDataTypes != UNKNOWN_TYPE) + return JType.getTotalSize(consumedDataTypes); + else { + switch (code) { + case cPOP: case cDUP: + return 1; + case cPOP2: case cDUP_X1: case cDUP2: case cSWAP: + return 2; + case cDUP_X2: case cDUP2_X1: + return 3; + case cDUP2_X2: + return 4; + default: + throw new Error(this.toString()); + } + } + } + + protected int getProducedDataTypesNumber() { + if (producedDataTypes != UNKNOWN_TYPE) + return producedDataTypes.length; + else { + switch (code) { + case cLDC: case cLDC_W: case cLDC2_W: case cBALOAD: + case cGETSTATIC: case cGETFIELD: + return 1; + case cDUP: case cSWAP: + return 2; + case cDUP_X2: case cDUP2: case cDUP2_X1: case cDUP2_X2: + return 2; + case cDUP_X1: + return 3; + default: + throw new Error(this.toString()); + } + } + } + + protected int getConsumedDataTypesNumber() { + if (consumedDataTypes != UNKNOWN_TYPE) + return consumedDataTypes.length; + else { + switch (code) { + case cPOP: case cDUP: case cPUTSTATIC: + return 1; + case cPUTFIELD: case cDUP_X1: case cDUP_X2: + case cDUP2: case cDUP2_X1: case cPOP2: case cSWAP: + return 2; + default: + throw new Error(this.toString()); + } + } + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JOtherAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JOtherAttribute.java new file mode 100644 index 0000000000..ad73540940 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JOtherAttribute.java @@ -0,0 +1,54 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.io.*; + +/** + * Attributes which are unknown to the JVM (or at least to this + * library). + * + * @author Michel Schinz + * @version 1.0 + */ + +public class JOtherAttribute extends JAttribute { + protected final String name; + protected final byte[] contents; + protected final int length; + + public JOtherAttribute(FJBGContext context, + JClass clazz, + Object owner, + String name, + byte[] contents, + int length) { + super(context, clazz, name); + this.name = name; + this.contents = contents; + this.length = length; + } + + public JOtherAttribute(FJBGContext context, + JClass clazz, + Object owner, + String name, + int size, + DataInputStream stream) + throws IOException { + super(context, clazz, name); + this.name = name; + this.contents = new byte[size]; + this.length = size; + + stream.read(contents, 0, length); + } + + public String getName() { return name; } + + protected int getSize() { return length; } + + protected void writeContentsTo(DataOutputStream stream) throws IOException { + stream.write(contents, 0, length); + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JReferenceType.java b/src/fjbg/ch/epfl/lamp/fjbg/JReferenceType.java new file mode 100644 index 0000000000..99c6acff71 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JReferenceType.java @@ -0,0 +1,16 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +/** + * Types for Java references, i.e. arrays and objects. + * + * @version 1.0 + * @author Michel Schinz + */ + +abstract public class JReferenceType extends JType { + public boolean isReferenceType() { return true; } + + abstract public String getDescriptor(); +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JSourceFileAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JSourceFileAttribute.java new file mode 100644 index 0000000000..77d6783c87 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JSourceFileAttribute.java @@ -0,0 +1,53 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Sourcefile attribute, which can be attached to class files to + * associate them with their source file. + * + * @version 1.0 + * @author Michel Schinz + */ + +public class JSourceFileAttribute extends JAttribute { + protected final String sourceFileName; + protected final int sourceFileIndex; + + public JSourceFileAttribute(FJBGContext context, + JClass clazz, + String sourceFileName) { + super(context, clazz); + this.sourceFileName = sourceFileName; + this.sourceFileIndex = clazz.getConstantPool().addUtf8(sourceFileName); + } + + public JSourceFileAttribute(FJBGContext context, + JClass clazz, + Object owner, + String name, + int size, + DataInputStream stream) + throws IOException { + super(context, clazz); + stream.readInt(); // ignore size + this.sourceFileIndex = stream.readShort(); + this.sourceFileName = clazz.getConstantPool().lookupUtf8(sourceFileIndex); + + assert name.equals(getName()); + } + + public String getName() { return "SourceFile"; } + + protected int getSize() { + return 2; + } + + protected void writeContentsTo(DataOutputStream stream) throws IOException { + stream.writeShort(sourceFileIndex); + } +} diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JType.java b/src/fjbg/ch/epfl/lamp/fjbg/JType.java new file mode 100644 index 0000000000..2557d45b83 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/fjbg/JType.java @@ -0,0 +1,289 @@ +// $Id$ + +package ch.epfl.lamp.fjbg; + +import java.util.*; +import java.io.StringReader; +import java.io.IOException; + +/** + * Representation of Java types. + * + * @version 1.0 + * @author Michel Schinz + */ + +abstract public class JType { + abstract public int getSize(); + abstract public String getSignature(); + abstract public int getTag(); + abstract public String toString(); + abstract public boolean isCompatibleWith(JType other); + + public boolean isValueType() { return false; } + public boolean isObjectType() { return false; } + public boolean isArrayType() { return false; } + public boolean isReferenceType() { return false; } + + // Tags for types. Taken from BCEL. + public static final int T_BOOLEAN = 4; + public static final int T_CHAR = 5; + public static final int T_FLOAT = 6; + public static final int T_DOUBLE = 7; + public static final int T_BYTE = 8; + public static final int T_SHORT = 9; + public static final int T_INT = 10; + public static final int T_LONG = 11; + public static final int T_VOID = 12; // Non-standard + public static final int T_ARRAY = 13; + public static final int T_OBJECT = 14; + public static final int T_UNKNOWN = 15; + public static final int T_ADDRESS = 16; + + public static final int T_REFERENCE = 17; // type compatible with references + + public static final JType[] EMPTY_ARRAY = new JType[0]; + + protected static JType parseSig(StringReader s) throws IOException { + int nextChar = s.read(); + if (nextChar == -1) throw new IllegalArgumentException(); + + switch ((char)nextChar) { + case 'V' : return VOID; + case 'Z' : return BOOLEAN; + case 'B' : return BYTE; + case 'C' : return CHAR; + case 'S' : return SHORT; + case 'I' : return INT; + case 'F' : return FLOAT; + case 'J' : return LONG; + case 'D' : return DOUBLE; + case 'L': { + StringBuffer className = new StringBuffer(); + for (;;) { + nextChar = s.read(); + if (nextChar == -1 || nextChar == ';') break; + className.append(nextChar == '/' ? ':' : ((char)nextChar)); + } + if (nextChar != ';') throw new IllegalArgumentException(); + return new JObjectType(className.toString()); + } + case '[': { + JType elemType = parseSig(s); + return new JArrayType(elemType); + } + case '(': { + ArrayList argTps = new ArrayList(); + for (;;) { + s.mark(1); + nextChar = s.read(); + if (nextChar == -1 || nextChar == ')') break; + s.reset(); + argTps.add(parseSig(s)); + } + if (nextChar != ')') throw new IllegalArgumentException("a"); + JType[] argTpsA = (JType[])argTps.toArray(new JType[argTps.size()]); + JType returnType = parseSig(s); + return new JMethodType(returnType, argTpsA); + } + default: + throw new IllegalArgumentException(); + } + } + + public static JType parseSignature(String signature) { + try { + StringReader sigReader = new StringReader(signature); + JType parsed = parseSig(sigReader); + if (sigReader.read() != -1) + throw new IllegalArgumentException(); + return parsed; + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("invalid signature " + signature); + } catch (IOException e) { + throw new Error(e); + } + } + + public static int getTotalSize(JType[] types) { + int size = 0; + for (int i = 0; i < types.length; ++i) + size += types[i].getSize(); + return size; + } + + 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 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 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 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 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 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 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 static JType LONG = new JType() { + public int getSize() { return 2; } + public String getSignature() { return "J"; } + public int getTag() { return T_LONG; } + public String toString() { return "long"; } + public boolean isValueType() { return true; } + public boolean isCompatibleWith(JType other) { + return other == LONG; + } + }; + + public static JType DOUBLE = new JType() { + public int getSize() { return 2; } + public String getSignature() { return "D"; } + public int getTag() { return T_DOUBLE; } + public String toString() { return "double"; } + public boolean isValueType() { return true; } + public boolean isCompatibleWith(JType other) { + return other == DOUBLE; + } + }; + + public static JType REFERENCE = new JType() { + public int getSize() { return 1; } + public String getSignature() { + throw new UnsupportedOperationException("type REFERENCE is no real " + + "data type and therefore " + + "has no signature"); + } + public int getTag() { return T_REFERENCE; } + public String toString() { return ""; } + public boolean isCompatibleWith(JType other) { + throw new UnsupportedOperationException("type REFERENCE is no real " + + "data type and therefore " + + "cannot be assigned to " + + other.toString()); + } + }; + + 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 "
"; } + 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 ""; } + public boolean isCompatibleWith(JType other) { + throw new UnsupportedOperationException("type UNKNOWN is no real " + + "data type and therefore " + + "cannot be assigned to " + + other.toString()); + } + }; +} diff --git a/src/fjbg/ch/epfl/lamp/util/ByteArray.java b/src/fjbg/ch/epfl/lamp/util/ByteArray.java new file mode 100644 index 0000000000..800fc9d0d8 --- /dev/null +++ b/src/fjbg/ch/epfl/lamp/util/ByteArray.java @@ -0,0 +1,140 @@ +// $Id$ + +package ch.epfl.lamp.util; + +import java.io.*; + +/** + * Array of bytes. + * + * @author Michel Schinz + * @version 1.0 + */ + +public class ByteArray { + protected final static int BYTE_BLOCK_BITS = 8; + protected final static int BYTE_BLOCK_SIZE = 1 << BYTE_BLOCK_BITS; + 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 boolean frozen = false; + + public ByteArray() { } + + public ByteArray(InputStream stream, int size) throws IOException { + size = pos; + for (int block = 0; size > 0; ++block) { + int sizeToRead = Math.min(BYTE_BLOCK_SIZE, size); + stream.read(data[block], 0, sizeToRead); + + size -= sizeToRead; + if (size > 0) addNewBlock(); + } + } + + public void freeze() { frozen = true; } + + public int nextBytePosition() { + return pos; + } + + public int getSize() { + return pos; + } + + protected void addNewBlock() { + int nextBlockPos = pos >>> BYTE_BLOCK_BITS; + if (nextBlockPos == data.length) { + byte[][] newData = new byte[data.length * 2][]; + System.arraycopy(data, 0, newData, 0, data.length); + data = newData; + } + assert data[nextBlockPos] == null : pos + " " + nextBlockPos; + data[nextBlockPos] = new byte[BYTE_BLOCK_SIZE]; + } + + protected void addByte(int b) { + assert !frozen; + + if ((pos & BYTE_BLOCK_MASK) == 0 && pos > 0) + addNewBlock(); + int currPos = pos++; + data[currPos >>> BYTE_BLOCK_BITS][currPos & BYTE_BLOCK_MASK] = (byte)b; + } + + public void addU1(int i) { + assert i <= 0xFF : i; + addByte(i); + } + + public void addU2(int i) { + assert i <= 0xFFFF : i; + + 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); + } + + public void putByte(int targetPos, int b) { + assert !frozen; + assert targetPos < pos : targetPos + " >= " + pos; + + 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); + } + + 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); + } + + public int getU1(int sourcePos) { + assert sourcePos < pos : sourcePos + " >= " + pos; + return data[sourcePos >>> BYTE_BLOCK_BITS][sourcePos & BYTE_BLOCK_MASK] & 0xFF; + } + + public int getU2(int sourcePos) { + return (getU1(sourcePos) << 8) | getU1(sourcePos + 1); + } + + public int getU4(int sourcePos) { + return (getU2(sourcePos) << 16) | getU2(sourcePos + 2); + } + + public int getS1(int sourcePos) { + assert sourcePos < pos : sourcePos + " >= " + pos; + return data[sourcePos >>> BYTE_BLOCK_BITS][sourcePos & BYTE_BLOCK_MASK]; + } + + public int getS2(int sourcePos) { + return (getS1(sourcePos) << 8) | getU1(sourcePos + 1); + } + + public int getS4(int sourcePos) { + return (getS2(sourcePos) << 16) | getU2(sourcePos + 2); + } + + 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); + } + } +} -- cgit v1.2.3