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
122
build.xml
122
build.xml
|
@ -3,7 +3,7 @@
|
||||||
<project name="sabbus" default="build">
|
<project name="sabbus" default="build">
|
||||||
|
|
||||||
<description>
|
<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>
|
</description>
|
||||||
|
|
||||||
<!-- ===========================================================================
|
<!-- ===========================================================================
|
||||||
|
@ -12,11 +12,15 @@ END-USER TARGETS
|
||||||
|
|
||||||
<target name="build" depends="pack.done"
|
<target name="build" depends="pack.done"
|
||||||
description="Builds the Scala compiler and library. Executables are in 'build/pack/bin'."/>
|
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"/>
|
<property name="scalac.args.optimise" value="-optimise"/>
|
||||||
<antcall target="build"/>
|
<antcall target="build"/>
|
||||||
</target>
|
</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"
|
<target name="clean" depends="quick.clean"
|
||||||
description="Removes binaries of compiler and library. Distributions are untouched."/>
|
description="Removes binaries of compiler and library. Distributions are untouched."/>
|
||||||
|
@ -43,11 +47,15 @@ END-USER TARGETS
|
||||||
<antcall target="docs.clean"/>
|
<antcall target="docs.clean"/>
|
||||||
<antcall target="all.done"/>
|
<antcall target="all.done"/>
|
||||||
</target>
|
</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"/>
|
<property name="scalac.args.optimise" value="-optimise"/>
|
||||||
<antcall target="dist"/>
|
<antcall target="dist"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
<target name="dist.optimise"><antcall target="optdist"/></target> <!-- for compat, remove when unused -->
|
||||||
|
|
||||||
<target name="fastdist" depends="dist.done"
|
<target name="fastdist" depends="dist.done"
|
||||||
description="Makes a new distribution without testing it or removing partially build elements"/>
|
description="Makes a new distribution without testing it or removing partially build elements"/>
|
||||||
<target name="fastdist.optimise">
|
<target name="fastdist.optimise">
|
||||||
|
@ -58,8 +66,8 @@ END-USER TARGETS
|
||||||
<target name="distclean" depends="dist.clean"
|
<target name="distclean" depends="dist.clean"
|
||||||
description="Removes all distributions. Binaries and documentation are untouched."/>
|
description="Removes all distributions. Binaries and documentation are untouched."/>
|
||||||
|
|
||||||
<target name="newstarr"
|
<target name="replacestarr"
|
||||||
description="Replaces the Starr compiler and library by one built from current sources and tests it.">
|
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.">
|
<fail message="This target is not available on Windows. Use 'ant newstarrwin' instead.">
|
||||||
<condition>
|
<condition>
|
||||||
<os family="windows"/>
|
<os family="windows"/>
|
||||||
|
@ -71,15 +79,19 @@ END-USER TARGETS
|
||||||
<antcall target="locker.clean"/>
|
<antcall target="locker.clean"/>
|
||||||
<antcall target="test.done"/>
|
<antcall target="test.done"/>
|
||||||
</target>
|
</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"/>
|
<property name="scalac.args.optimise" value="-optimise"/>
|
||||||
<antcall target="newstarr"/>
|
<antcall target="newstarr"/>
|
||||||
</target>
|
</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>.
|
<!-- Ant on Windows is not able to delete jar files that are referenced in any <path>.
|
||||||
See ticket 1290 on trac. -->
|
See ticket 1290 on trac. -->
|
||||||
<target name="newstarrwin"
|
<target name="newstarrwin"
|
||||||
description="Creates a new Starr on Windows. Manually execute 'ant locker.clean build' first!">
|
description="Creates a new Starr on Windows. Manually execute 'ant locker.clean build' first!">
|
||||||
<fail message="This target is only available on Windows. Use 'ant newstarr' instead.">
|
<fail message="This target is only available on Windows. Use 'ant newstarr' instead.">
|
||||||
<condition>
|
<condition>
|
||||||
<not><os family="windows"/></not>
|
<not><os family="windows"/></not>
|
||||||
|
@ -91,8 +103,8 @@ END-USER TARGETS
|
||||||
<antcall target="test.done"/>
|
<antcall target="test.done"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="freshlocker"
|
<target name="replacelocker"
|
||||||
description="Replaces the Locker compiler and library by one built from current sources.">
|
description="Replaces the Locker compiler and library by fresh ones built from current sources.">
|
||||||
<antcall target="palo.clean"/>
|
<antcall target="palo.clean"/>
|
||||||
<antcall target="palo.done"/>
|
<antcall target="palo.done"/>
|
||||||
</target>
|
</target>
|
||||||
|
@ -100,20 +112,24 @@ END-USER TARGETS
|
||||||
<property name="scalac.args.optimise" value="-optimise"/>
|
<property name="scalac.args.optimise" value="-optimise"/>
|
||||||
<antcall target="freshlocker"/>
|
<antcall target="freshlocker"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
<target name="replacelocker-opt"
|
||||||
|
description="Replaces the Locker compiler and library by fresh, optimised ones built from current sources.">
|
||||||
|
<property name="scalac.args.optimise" value="-optimise"/>
|
||||||
|
<antcall target="replacelocker"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="newlocker.optimise"><antcall target="replacelocker"/></target> <!-- for compat, remove when unused -->
|
||||||
|
|
||||||
<target name="newlocker"
|
<target name="unlocklocker"
|
||||||
description="Unlocks the Locker compiler and library and lets them be updated by Scalac.">
|
description="Unlocks Locker, allowing its compiler and library to be rebuilt">
|
||||||
<antcall target="locker.unlock"/>
|
<antcall target="locker.unlock"/>
|
||||||
<antcall target="palo.done"/>
|
<antcall target="palo.done"/>
|
||||||
</target>
|
</target>
|
||||||
<target name="newlocker.optimise">
|
|
||||||
<property name="scalac.args.optimise" value="-optimise"/>
|
|
||||||
<antcall target="newlocker"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="newlibs"
|
<target name="newlibs"
|
||||||
description="Forces compiler libraries (MSIL) to be rebuilt. Add this target before any other if class file format is incompatible.">
|
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.msil.outdated" value="yes"/>
|
<property name="libs.outdated" value="yes"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<!-- ===========================================================================
|
<!-- ===========================================================================
|
||||||
|
@ -349,7 +365,7 @@ LOCAL REFERENCE BUILD (LOCKER)
|
||||||
<delete dir="${build-locker.dir}" includeemptydirs="yes" quiet="yes" failonerror="no"/>
|
<delete dir="${build-locker.dir}" includeemptydirs="yes" quiet="yes" failonerror="no"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="locker.unlock" depends="quick.clean">
|
<target name="locker.unlock">
|
||||||
<delete file="${build-locker.dir}/all.complete"/>
|
<delete file="${build-locker.dir}/all.complete"/>
|
||||||
<delete file="${build-locker.dir}/library.complete"/>
|
<delete file="${build-locker.dir}/library.complete"/>
|
||||||
<delete file="${build-locker.dir}/compiler.complete"/>
|
<delete file="${build-locker.dir}/compiler.complete"/>
|
||||||
|
@ -512,13 +528,13 @@ QUICK BUILD (QUICK)
|
||||||
<stopwatch name="quick.lib.timer" action="total"/>
|
<stopwatch name="quick.lib.timer" action="total"/>
|
||||||
</target>
|
</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"/>
|
<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"/>
|
<property name="msil.jar" value="${build-libs.dir}/msil.jar"/>
|
||||||
</target>
|
</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="fjbg.jar" value="${lib.dir}/fjbg.jar"/>
|
||||||
<property name="msil.jar" value="${lib.dir}/msil.jar"/>
|
<property name="msil.jar" value="${lib.dir}/msil.jar"/>
|
||||||
</target>
|
</target>
|
||||||
|
@ -1121,7 +1137,53 @@ LIBRARIES (MSIL, FJBG maybe later)
|
||||||
</jar>
|
</jar>
|
||||||
</target>
|
</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">
|
<target name="libs.clean" depends="pack.clean">
|
||||||
<delete dir="${build-libs.dir}" includeemptydirs="yes" quiet="yes" failonerror="no"/>
|
<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"/>
|
<delete file="${basedir}/lib/scala-library-src.jar"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="starr.lib" depends="starr.clean">
|
<target name="starr.lib" depends="starr.start">
|
||||||
<copy file="${basedir}/build/pack/lib/scala-library.jar"
|
<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>
|
||||||
|
|
||||||
<target name="starr.comp" depends="starr.lib">
|
<target name="starr.comp" depends="starr.lib">
|
||||||
|
<delete file="${basedir}/lib/scala-compiler.jar"/>
|
||||||
<jar destfile="${basedir}/lib/scala-compiler.jar">
|
<jar destfile="${basedir}/lib/scala-compiler.jar">
|
||||||
<fileset dir="${basedir}/build/quick/classes/compiler"/>
|
<fileset dir="${basedir}/build/quick/classes/compiler"/>
|
||||||
</jar>
|
</jar>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="starr.src" depends="starr.comp">
|
<target name="starr.src" depends="starr.comp">
|
||||||
|
<delete file="${basedir}/lib/scala-library-src.jar"/>
|
||||||
<jar destfile="${basedir}/lib/scala-library-src.jar">
|
<jar destfile="${basedir}/lib/scala-library-src.jar">
|
||||||
<fileset dir="${basedir}/src/library"/>
|
<fileset dir="${basedir}/src/library"/>
|
||||||
<fileset dir="${basedir}/src/actors"/>
|
<fileset dir="${basedir}/src/actors"/>
|
||||||
</jar>
|
</jar>
|
||||||
</target>
|
</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">
|
<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>
|
</copy>
|
||||||
</target>
|
</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