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:
parent
766bcd608e
commit
b5f2cffcc6
120
build.xml
120
build.xml
|
@ -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,11 +79,15 @@ 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"
|
||||
|
@ -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>
|
||||
|
@ -101,19 +113,23 @@ END-USER TARGETS
|
|||
<antcall target="freshlocker"/>
|
||||
</target>
|
||||
|
||||
<target name="newlocker"
|
||||
description="Unlocks the Locker compiler and library and lets them be updated by Scalac.">
|
||||
<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="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>
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue