SABBUS now builds FJBG, which sources are now part of the Scala module.

git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@18727 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
dubochet 2009-09-21 19:24:41 +00:00
parent 766bcd608e
commit b5f2cffcc6
29 changed files with 6268 additions and 27 deletions

122
build.xml
View File

@ -3,7 +3,7 @@
<project name="sabbus" default="build">
<description>
SuperSabbus for Scala core, builds the scala library and compiler. It can also package it as a simple distribution, tests it for stable bootstrapping and against the Scala test suite. Use PackSabbus to package it for different install management tools such as Sbaz.
SuperSabbus for Scala core, builds the scala library and compiler. It can also package it as a simple distribution, tests it for stable bootstrapping and against the Scala test suite.
</description>
<!-- ===========================================================================
@ -12,11 +12,15 @@ END-USER TARGETS
<target name="build" depends="pack.done"
description="Builds the Scala compiler and library. Executables are in 'build/pack/bin'."/>
<target name="build.optimise">
<target name="build-opt"
description="Builds the optimised Scala compiler and library. Executables are in 'build/pack/bin'.">
<property name="scalac.args.optimise" value="-optimise"/>
<antcall target="build"/>
</target>
<target name="optimise" depends="build.optimise"/>
<target name="build.optimise"><antcall target="build"/></target> <!-- for compat, remove when unused -->
<target name="optimise"><antcall target="build"/></target> <!-- for compat, remove when unused -->
<target name="clean" depends="quick.clean"
description="Removes binaries of compiler and library. Distributions are untouched."/>
@ -43,11 +47,15 @@ END-USER TARGETS
<antcall target="docs.clean"/>
<antcall target="all.done"/>
</target>
<target name="dist.optimise">
<target name="dist-opt"
description="Makes a new optimised distribution and tests it. Will remove existing binaries and documentation.">
<property name="scalac.args.optimise" value="-optimise"/>
<antcall target="dist"/>
</target>
<target name="dist.optimise"><antcall target="optdist"/></target> <!-- for compat, remove when unused -->
<target name="fastdist" depends="dist.done"
description="Makes a new distribution without testing it or removing partially build elements"/>
<target name="fastdist.optimise">
@ -58,8 +66,8 @@ END-USER TARGETS
<target name="distclean" depends="dist.clean"
description="Removes all distributions. Binaries and documentation are untouched."/>
<target name="newstarr"
description="Replaces the Starr compiler and library by one built from current sources and tests it.">
<target name="replacestarr"
description="Replaces the Starr compiler and library by fresh ones built from current sources and tests them.">
<fail message="This target is not available on Windows. Use 'ant newstarrwin' instead.">
<condition>
<os family="windows"/>
@ -71,15 +79,19 @@ END-USER TARGETS
<antcall target="locker.clean"/>
<antcall target="test.done"/>
</target>
<target name="newstarr.optimise">
<target name="replacestarr-opt"
description="Replaces the Starr compiler and library by fresh, optimised ones built from current sources and tests them.">
<property name="scalac.args.optimise" value="-optimise"/>
<antcall target="newstarr"/>
</target>
<target name="newstarr.optimise"><antcall target="optnewstarr"/></target> <!-- for compat, remove when unused -->
<!-- Ant on Windows is not able to delete jar files that are referenced in any <path>.
See ticket 1290 on trac. -->
<target name="newstarrwin"
description="Creates a new Starr on Windows. Manually execute 'ant locker.clean build' first!">
description="Creates a new Starr on Windows. Manually execute 'ant locker.clean build' first!">
<fail message="This target is only available on Windows. Use 'ant newstarr' instead.">
<condition>
<not><os family="windows"/></not>
@ -91,8 +103,8 @@ END-USER TARGETS
<antcall target="test.done"/>
</target>
<target name="freshlocker"
description="Replaces the Locker compiler and library by one built from current sources.">
<target name="replacelocker"
description="Replaces the Locker compiler and library by fresh ones built from current sources.">
<antcall target="palo.clean"/>
<antcall target="palo.done"/>
</target>
@ -100,20 +112,24 @@ END-USER TARGETS
<property name="scalac.args.optimise" value="-optimise"/>
<antcall target="freshlocker"/>
</target>
<target name="replacelocker-opt"
description="Replaces the Locker compiler and library by fresh, optimised ones built from current sources.">
<property name="scalac.args.optimise" value="-optimise"/>
<antcall target="replacelocker"/>
</target>
<target name="newlocker.optimise"><antcall target="replacelocker"/></target> <!-- for compat, remove when unused -->
<target name="newlocker"
description="Unlocks the Locker compiler and library and lets them be updated by Scalac.">
<target name="unlocklocker"
description="Unlocks Locker, allowing its compiler and library to be rebuilt">
<antcall target="locker.unlock"/>
<antcall target="palo.done"/>
</target>
<target name="newlocker.optimise">
<property name="scalac.args.optimise" value="-optimise"/>
<antcall target="newlocker"/>
</target>
<target name="newlibs"
description="Forces compiler libraries (MSIL) to be rebuilt. Add this target before any other if class file format is incompatible.">
<property name="libs.msil.outdated" value="yes"/>
description="Requires compiler libraries (MSIL and FJBG) to be rebuilt. Add this target before any other if class file format is incompatible.">
<property name="libs.outdated" value="yes"/>
</target>
<!-- ===========================================================================
@ -349,7 +365,7 @@ LOCAL REFERENCE BUILD (LOCKER)
<delete dir="${build-locker.dir}" includeemptydirs="yes" quiet="yes" failonerror="no"/>
</target>
<target name="locker.unlock" depends="quick.clean">
<target name="locker.unlock">
<delete file="${build-locker.dir}/all.complete"/>
<delete file="${build-locker.dir}/library.complete"/>
<delete file="${build-locker.dir}/compiler.complete"/>
@ -512,13 +528,13 @@ QUICK BUILD (QUICK)
<stopwatch name="quick.lib.timer" action="total"/>
</target>
<target name="quick.newlibs" depends="quick.lib" if="libs.msil.outdated">
<target name="quick.newlibs" depends="quick.lib" if="libs.outdated">
<antcall target="libs.done"/>
<property name="fjbg.jar" value="${lib.dir}/fjbg.jar"/>
<property name="fjbg.jar" value="${build-libs.dir}/fjbg.jar"/>
<property name="msil.jar" value="${build-libs.dir}/msil.jar"/>
</target>
<target name="quick.libs" depends="quick.newlibs" unless="libs.msil.outdated">
<target name="quick.libs" depends="quick.newlibs" unless="libs.outdated">
<property name="fjbg.jar" value="${lib.dir}/fjbg.jar"/>
<property name="msil.jar" value="${lib.dir}/msil.jar"/>
</target>
@ -1121,7 +1137,53 @@ LIBRARIES (MSIL, FJBG maybe later)
</jar>
</target>
<target name="libs.done" depends="libs.msilpack"/>
<target name="libs.pre-fjbg" depends="libs.start">
<uptodate property="libs.fjbg.available" targetfile="${build-libs.dir}/fjbg.complete">
<srcfiles dir="${src.dir}/fjbg">
<include name="**/*.java"/>
<include name="**/*.scala"/>
</srcfiles>
</uptodate>
</target>
<target name="libs.fjbg" depends="libs.pre-fjbg" unless="libs.fjbg.available">
<mkdir dir="${build-libs.dir}/classes/fjbg"/>
<javac
srcdir="${src.dir}/fjbg"
destdir="${build-libs.dir}/classes/fjbg"
classpath="${build-libs.dir}/classes/fjbg"
includes="**/*.java"
target="1.5" source="1.4">
<compilerarg line="${javac.args}"/>
</javac>
<!-- For now, JFBG is written in pure Java
<scalacfork
destdir="${build-libs.dir}/classes/fjbg"
compilerpathref="locker.classpath"
srcpath="${src.dir}/fjbg"
params="${scalac.args.all}"
srcdir="${src.dir}/fjbg"
jvmargs="${scalacfork.jvmargs}">
<include name="**/*.scala"/>
<compilationpath>
<pathelement location="${build-quick.dir}/classes/library"/>
<pathelement location="${build-libs.dir}/classes/fjbg"/>
</compilationpath>
</scalacfork>
-->
<touch file="${build-libs.dir}/fjbg.complete" verbose="no"/>
</target>
<target name="libs.pre-fjbgpack" depends="libs.fjbg">
</target>
<target name="libs.fjbgpack" depends="libs.pre-fjbgpack" unless="libs.fjbgpack.available">
<jar destfile="${build-libs.dir}/fjbg.jar">
<fileset dir="${build-libs.dir}/classes/fjbg"/>
</jar>
</target>
<target name="libs.done" depends="libs.msilpack, libs.fjbgpack"/>
<target name="libs.clean" depends="pack.clean">
<delete dir="${build-libs.dir}" includeemptydirs="yes" quiet="yes" failonerror="no"/>
@ -1426,27 +1488,33 @@ STABLE REFERENCE (STARR)
<delete file="${basedir}/lib/scala-library-src.jar"/>
</target>
<target name="starr.lib" depends="starr.clean">
<target name="starr.lib" depends="starr.start">
<copy file="${basedir}/build/pack/lib/scala-library.jar"
toFile="${basedir}/lib/scala-library.jar"/>
toFile="${basedir}/lib/scala-library.jar"
overwrite="yes"/>
</target>
<target name="starr.comp" depends="starr.lib">
<delete file="${basedir}/lib/scala-compiler.jar"/>
<jar destfile="${basedir}/lib/scala-compiler.jar">
<fileset dir="${basedir}/build/quick/classes/compiler"/>
</jar>
</target>
<target name="starr.src" depends="starr.comp">
<delete file="${basedir}/lib/scala-library-src.jar"/>
<jar destfile="${basedir}/lib/scala-library-src.jar">
<fileset dir="${basedir}/src/library"/>
<fileset dir="${basedir}/src/actors"/>
</jar>
</target>
<target name="starr.libs" depends="starr.src" if="libs.msil.outdated">
<target name="starr.libs" depends="starr.src" if="libs.outdated">
<copy toDir="${lib.dir}" overwrite="yes">
<fileset dir="${build-libs.dir}" includes="*.jar"/>
<fileset dir="${build-libs.dir}">
<include name="fjbg.jar"/>
<include name="msil.jar"/>
</fileset>
</copy>
</target>

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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/*<JAttribute>*/ 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/*<JAttribute>*/ 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;
}

View File

@ -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/*<String, Constructor>*/ 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);
}
}
}

View File

@ -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);
}
}

View File

@ -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/*<JMethod>*/ methods = new LinkedList();
protected final LinkedList/*<JField>*/ 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);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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/*<JAttribute>*/ 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/*<JExceptionHandler>*/ 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);
}
}

View File

@ -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];
}
}

View File

@ -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/*<Entry,Integer>*/ 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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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/*<InnerClassEntry>*/ 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;
}
}
}

View File

@ -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;
}
}

View File

@ -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]);
}
}
}

View File

@ -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; }
}

View File

@ -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/*<JAttribute>*/ 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/*<JAttribute>*/ 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;
}
}

View File

@ -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 = "<clinit>";
public final static String INSTANCE_CONSTRUCTOR_NAME = "<init>";
protected final JCode code;
protected final String[] argNames;
protected final LinkedList/*<JLocalVariable>*/ localVariables =
new LinkedList();
protected int localVariableIndex = 0;
protected JMethod(FJBGContext context,
JClass owner,
int accessFlags,
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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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 "<reference>"; }
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 "<address>"; }
public boolean isCompatibleWith(JType other) {
return other == ADDRESS;
}
};
public static JType UNKNOWN = new JType() {
public int getSize() {
throw new UnsupportedOperationException("type UNKNOWN is no real "
+ "data type and therefore "
+ "has no size");
}
public String getSignature() {
throw new UnsupportedOperationException("type UNKNOWN is no real "
+ "data type and therefore "
+ "has no signature");
}
public int getTag() { return T_UNKNOWN; }
public String toString() { return "<unknown>"; }
public boolean isCompatibleWith(JType other) {
throw new UnsupportedOperationException("type UNKNOWN is no real "
+ "data type and therefore "
+ "cannot be assigned to "
+ other.toString());
}
};
}

View File

@ -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);
}
}
}