commit
f06322c01e
@ -0,0 +1,6 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<classpath> |
||||||
|
<classpathentry kind="src" path="src"/> |
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> |
||||||
|
<classpathentry kind="output" path="bin"/> |
||||||
|
</classpath> |
@ -0,0 +1 @@ |
|||||||
|
bin |
@ -0,0 +1,17 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<projectDescription> |
||||||
|
<name>bloat</name> |
||||||
|
<comment></comment> |
||||||
|
<projects> |
||||||
|
</projects> |
||||||
|
<buildSpec> |
||||||
|
<buildCommand> |
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name> |
||||||
|
<arguments> |
||||||
|
</arguments> |
||||||
|
</buildCommand> |
||||||
|
</buildSpec> |
||||||
|
<natures> |
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature> |
||||||
|
</natures> |
||||||
|
</projectDescription> |
@ -0,0 +1,7 @@ |
|||||||
|
#Tue Oct 18 04:56:05 CEST 2005 |
||||||
|
eclipse.preferences.version=1 |
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.1 |
||||||
|
org.eclipse.jdt.core.compiler.compliance=1.3 |
||||||
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=ignore |
||||||
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=ignore |
||||||
|
org.eclipse.jdt.core.compiler.source=1.3 |
@ -0,0 +1,7 @@ |
|||||||
|
- FlowGraph#addHandlerEdges: added visited parameter to keep track of |
||||||
|
blocks already visited to break 'infinite loop' in compiler-generated |
||||||
|
'self-handling' exception handlers since JDK 1.4 |
||||||
|
|
||||||
|
- Type#getType(Class): small fix for array type descriptors |
||||||
|
|
||||||
|
- Tree#visit_ldc: broken hack for JDK5 class constants - check usages and fix |
@ -0,0 +1,74 @@ |
|||||||
|
# All files in the distribution of BLOAT (Bytecode Level Optimization and
|
||||||
|
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
||||||
|
# Research Foundation of Purdue University. All rights reserved.
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
|
||||||
|
SUBDIRS = util \
|
||||||
|
reflect \
|
||||||
|
file \
|
||||||
|
editor \
|
||||||
|
inline \
|
||||||
|
tree \
|
||||||
|
cfg \
|
||||||
|
ssa \
|
||||||
|
tbaa \
|
||||||
|
trans \
|
||||||
|
diva \
|
||||||
|
codegen \
|
||||||
|
context \
|
||||||
|
decorate \
|
||||||
|
optimize \
|
||||||
|
shrink \
|
||||||
|
strip \
|
||||||
|
dump \
|
||||||
|
tools \
|
||||||
|
benchmark
|
||||||
|
|
||||||
|
all: |
||||||
|
@for i in $(SUBDIRS) ""; do \
|
||||||
|
if [ "x$$i" != "x" ]; then \
|
||||||
|
$(MAKE) -C $$i all; \
|
||||||
|
fi; \
|
||||||
|
done
|
||||||
|
|
||||||
|
clean: |
||||||
|
@for i in $(SUBDIRS) ""; do \
|
||||||
|
if [ "x$$i" != "x" ]; then \
|
||||||
|
$(MAKE) -C $$i clean; \
|
||||||
|
fi; \
|
||||||
|
done
|
||||||
|
|
||||||
|
docs: |
||||||
|
javadoc -d ../../../../../docs -sourcepath ../../../.. \
|
||||||
|
EDU.purdue.cs.bloat.util \
|
||||||
|
EDU.purdue.cs.bloat.reflect \
|
||||||
|
EDU.purdue.cs.bloat.file \
|
||||||
|
EDU.purdue.cs.bloat.editor \
|
||||||
|
EDU.purdue.cs.bloat.tree \
|
||||||
|
EDU.purdue.cs.bloat.cfg \
|
||||||
|
EDU.purdue.cs.bloat.ssa \
|
||||||
|
EDU.purdue.cs.bloat.tbaa \
|
||||||
|
EDU.purdue.cs.bloat.trans \
|
||||||
|
EDU.purdue.cs.bloat.diva \
|
||||||
|
EDU.purdue.cs.bloat.codegen \
|
||||||
|
EDU.purdue.cs.bloat.context \
|
||||||
|
EDU.purdue.cs.bloat.decorate \
|
||||||
|
EDU.purdue.cs.bloat.optimize \
|
||||||
|
EDU.purdue.cs.bloat.shrink \
|
||||||
|
EDU.purdue.cs.bloat.strip \
|
||||||
|
EDU.purdue.cs.bloat.dump \
|
||||||
|
EDU.purdue.cs.bloat.benchmark
|
@ -0,0 +1,2 @@ |
|||||||
|
*.class |
||||||
|
EDU_purdue_cs_bloat_benchmark_Benchmark.h |
@ -0,0 +1,143 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.benchmark; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class is used to run a benchmark Java program with Perfmon running in |
||||||
|
* the background. Perfmon is a software package developed at Michigan State |
||||||
|
* University that allows user-level programs to access the hardware counters on |
||||||
|
* Sparc processors. |
||||||
|
* |
||||||
|
* <p> |
||||||
|
* |
||||||
|
* The <tt>main</tt> method of this class takes several arguments (note that |
||||||
|
* the first four arguments are mutually exclusive): |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* -inst-load-stall Count load interlock induced stalls |
||||||
|
* -dcache Count data cache hit rate |
||||||
|
* -cycle-ic-miss-stall Count I-cache miss induced stalls (and cycles) |
||||||
|
* -inst-cycle Count instructions (and cycles) |
||||||
|
* |
||||||
|
* -run n How many times is the program run |
||||||
|
* |
||||||
|
* class Java class to run (the benchmark) |
||||||
|
* args Arguments to benchmark class
|
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* The real work is done by the native <tt>run</tt> method that is implemented |
||||||
|
* in benchmark.c. |
||||||
|
* |
||||||
|
* @see BenchmarkSecurityManager |
||||||
|
*/ |
||||||
|
public class Benchmark { |
||||||
|
static { |
||||||
|
// Load native code from libbenchmark.so
|
||||||
|
System.loadLibrary("benchmark"); |
||||||
|
} |
||||||
|
|
||||||
|
public static native void init(Class main); |
||||||
|
|
||||||
|
public static native void run(Class main, String[] args); |
||||||
|
|
||||||
|
public static native void setMode(int mode); |
||||||
|
|
||||||
|
public static void main(final String[] args) throws Exception { |
||||||
|
int mode = 0; |
||||||
|
|
||||||
|
int runs = 1; |
||||||
|
int eat = 0; |
||||||
|
|
||||||
|
if (args.length <= 1) { |
||||||
|
Benchmark.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
for (eat = 0; eat < args.length; eat++) { |
||||||
|
if (args[eat].equals("-inst-cycle")) { |
||||||
|
mode = 3; |
||||||
|
} else if (args[eat].equals("-inst-load-stall")) { |
||||||
|
mode = 0; |
||||||
|
} else if (args[eat].equals("-dcache")) { |
||||||
|
mode = 1; |
||||||
|
} else if (args[eat].equals("-cycle-ic-miss-stall")) { |
||||||
|
mode = 2; |
||||||
|
} else if (args[eat].equals("-run")) { |
||||||
|
if (++eat >= args.length) { |
||||||
|
Benchmark.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
runs = Integer.parseInt(args[eat]); |
||||||
|
|
||||||
|
if (runs <= 0) { |
||||||
|
Benchmark.usage(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
// The main class
|
||||||
|
eat++; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Got all the args. */ |
||||||
|
if (eat > args.length) { |
||||||
|
Benchmark.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final BenchmarkSecurityManager sec = new BenchmarkSecurityManager(); |
||||||
|
System.setSecurityManager(sec); |
||||||
|
|
||||||
|
final String mainClassName = args[eat - 1]; |
||||||
|
final String[] a = new String[args.length - eat]; |
||||||
|
|
||||||
|
System.err.println("Running " + mainClassName + " in mode " + mode); |
||||||
|
Benchmark.setMode(mode); |
||||||
|
|
||||||
|
final Class mainClass = Class.forName(mainClassName); |
||||||
|
Benchmark.init(mainClass); |
||||||
|
|
||||||
|
for (int i = 0; i < runs; i++) { |
||||||
|
try { |
||||||
|
System.arraycopy(args, eat, a, 0, a.length); |
||||||
|
Benchmark.run(mainClass, a); |
||||||
|
} catch (final SecurityException e) { |
||||||
|
continue; |
||||||
|
} catch (final Exception e) { |
||||||
|
e.printStackTrace(System.err); |
||||||
|
sec.allowExit = true; |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sec.allowExit = true; |
||||||
|
} |
||||||
|
|
||||||
|
private static void usage() { |
||||||
|
System.err.print("usage: java EDU.purdue.cs.bloat.Benchmark "); |
||||||
|
System.err.println("options class args..."); |
||||||
|
System.err.println("where options are one of:"); |
||||||
|
System.err.println(" -inst-load-stall"); |
||||||
|
System.err.println(" -inst-cycle"); |
||||||
|
System.err.println(" -cycle-ic-miss-stall"); |
||||||
|
System.err.println(" -dcache"); |
||||||
|
System.err.println(" -run n"); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,115 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.benchmark; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* The <tt>BenchmarkSecurityManager</tt> allows us to execute a "main" method |
||||||
|
* multiple times without the virtual machine exiting. If exit is not allowed, |
||||||
|
* the <tt>checkExit</tt> method will throw a <tt>SecurityException</tt> |
||||||
|
* that can be caught, thus allowing execution to continue. |
||||||
|
* |
||||||
|
* @see Shade |
||||||
|
* @see Stats |
||||||
|
*/ |
||||||
|
public class BenchmarkSecurityManager extends SecurityManager { |
||||||
|
boolean allowExit = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* A <tt>SecurityException</tt> is thrown if we do not allow the virtual |
||||||
|
* machine to exit. |
||||||
|
*/ |
||||||
|
public void checkExit(final int status) { |
||||||
|
if (!allowExit) { |
||||||
|
System.err.println("exit " + status); |
||||||
|
throw new SecurityException("Tried to exit (status=" + status + ")"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void checkCreateClassLoader() { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkAccess(final Thread t) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkAccess(final ThreadGroup g) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkExec(final String cmd) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkLink(final String lib) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkRead(final FileDescriptor fd) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkRead(final String file) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkRead(final String file, final Object context) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkWrite(final FileDescriptor fd) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkWrite(final String file) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkDelete(final String file) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkConnect(final String host, final int port) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkConnect(final String host, final int port, |
||||||
|
final Object context) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkListen(final int port) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkAccept(final String host, final int port) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkPropertiesAccess() { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkPropertyAccess(final String key) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkPropertyAccess(final String key, final String val) { |
||||||
|
} |
||||||
|
|
||||||
|
public boolean checkTopLevelWindow(final Object window) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public void checkPackageAccess(final String pkg) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkPackageDefinition(final String pkg) { |
||||||
|
} |
||||||
|
|
||||||
|
public void checkSetFactory() { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,361 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.benchmark; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.context.*; |
||||||
|
import EDU.purdue.cs.bloat.editor.*; |
||||||
|
import EDU.purdue.cs.bloat.file.*; |
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
public class CounterDecorate implements Opcode { |
||||||
|
private static final String COUNTER_TYPE = "I"; |
||||||
|
|
||||||
|
private static final String COUNTER_RCNAME = "rcCount"; |
||||||
|
|
||||||
|
private static final String COUNTER_AUNAME = "auCount"; |
||||||
|
|
||||||
|
private static final String COUNTER_SUNAME = "suCount"; |
||||||
|
|
||||||
|
private static final String COUNTER_MAIN = "LEDU/purdue/cs/bloat/benchmark/Counter;"; |
||||||
|
|
||||||
|
private static int VERBOSE = 0; |
||||||
|
|
||||||
|
private static boolean FORCE = false; |
||||||
|
|
||||||
|
private static boolean CLOSURE = false; |
||||||
|
|
||||||
|
private static final List SKIP = new ArrayList(); |
||||||
|
|
||||||
|
private static final List ONLY = new ArrayList(); |
||||||
|
|
||||||
|
public static void main(final String[] args) { |
||||||
|
final ClassFileLoader loader = new ClassFileLoader(); |
||||||
|
List classes = new ArrayList(); |
||||||
|
boolean gotdir = false; |
||||||
|
|
||||||
|
for (int i = 0; i < args.length; i++) { |
||||||
|
if (args[i].equals("-v") || args[i].equals("-verbose")) { |
||||||
|
CounterDecorate.VERBOSE++; |
||||||
|
} else if (args[i].equals("-help")) { |
||||||
|
CounterDecorate.usage(); |
||||||
|
} else if (args[i].equals("-classpath")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
CounterDecorate.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final String classpath = args[i]; |
||||||
|
loader.setClassPath(classpath); |
||||||
|
} else if (args[i].equals("-skip")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
CounterDecorate.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final String pkg = args[i].replace('.', '/'); |
||||||
|
CounterDecorate.SKIP.add(pkg); |
||||||
|
} else if (args[i].equals("-only")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
CounterDecorate.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final String pkg = args[i].replace('.', '/'); |
||||||
|
CounterDecorate.ONLY.add(pkg); |
||||||
|
} else if (args[i].equals("-closure")) { |
||||||
|
CounterDecorate.CLOSURE = true; |
||||||
|
} else if (args[i].equals("-relax-loading")) { |
||||||
|
ClassHierarchy.RELAX = true; |
||||||
|
} else if (args[i].equals("-f")) { |
||||||
|
CounterDecorate.FORCE = true; |
||||||
|
} else if (args[i].startsWith("-")) { |
||||||
|
CounterDecorate.usage(); |
||||||
|
} else if (i == args.length - 1) { |
||||||
|
final File f = new File(args[i]); |
||||||
|
|
||||||
|
if (f.exists() && !f.isDirectory()) { |
||||||
|
System.err.println("No such directory: " + f.getPath()); |
||||||
|
System.exit(2); |
||||||
|
} |
||||||
|
|
||||||
|
loader.setOutputDir(f); |
||||||
|
gotdir = true; |
||||||
|
} else { |
||||||
|
classes.add(args[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!gotdir) { |
||||||
|
CounterDecorate.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
if (classes.size() == 0) { |
||||||
|
CounterDecorate.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
if (CounterDecorate.VERBOSE > 3) { |
||||||
|
ClassFileLoader.DEBUG = true; |
||||||
|
ClassEditor.DEBUG = true; |
||||||
|
} |
||||||
|
|
||||||
|
boolean errors = false; |
||||||
|
|
||||||
|
final Iterator iter = classes.iterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final String name = (String) iter.next(); |
||||||
|
|
||||||
|
try { |
||||||
|
loader.loadClass(name); |
||||||
|
} catch (final ClassNotFoundException ex) { |
||||||
|
System.err.println("Couldn't find class: " + ex.getMessage()); |
||||||
|
errors = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (errors) { |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
final BloatContext context = new CachingBloatContext(loader, classes, |
||||||
|
CounterDecorate.CLOSURE); |
||||||
|
|
||||||
|
if (!CounterDecorate.CLOSURE) { |
||||||
|
final Iterator e = classes.iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final String name = (String) e.next(); |
||||||
|
try { |
||||||
|
final ClassInfo info = loader.loadClass(name); |
||||||
|
CounterDecorate.decorateClass(context, info); |
||||||
|
} catch (final ClassNotFoundException ex) { |
||||||
|
System.err.println("Couldn't find class: " |
||||||
|
+ ex.getMessage()); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
classes = null; |
||||||
|
|
||||||
|
final ClassHierarchy hier = context.getHierarchy(); |
||||||
|
|
||||||
|
final Iterator e = hier.classes().iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Type t = (Type) e.next(); |
||||||
|
|
||||||
|
if (t.isObject()) { |
||||||
|
try { |
||||||
|
final ClassInfo info = loader.loadClass(t.className()); |
||||||
|
CounterDecorate.decorateClass(context, info); |
||||||
|
} catch (final ClassNotFoundException ex) { |
||||||
|
System.err.println("Couldn't find class: " |
||||||
|
+ ex.getMessage()); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void usage() { |
||||||
|
System.err |
||||||
|
.println("Usage: java EDU.purdue.cs.bloat.count.Main " |
||||||
|
+ "\n [-options] classes output_dir" |
||||||
|
+ "\n" |
||||||
|
+ "\nwhere options include:" |
||||||
|
+ "\n -help print out this message" |
||||||
|
+ "\n -v -verbose turn on verbose mode " |
||||||
|
+ "(can be given multiple times)" |
||||||
|
+ "\n -classpath <directories separated by colons>" |
||||||
|
+ "\n list directories in which to look for classes" |
||||||
|
+ "\n -f decorate files even if up-to-date" |
||||||
|
+ "\n -closure recursively decorate referenced classes" |
||||||
|
+ "\n -relax-loading don't report errors if a class is not found" |
||||||
|
+ "\n -skip <class|package.*>" |
||||||
|
+ "\n skip the given class or package" |
||||||
|
+ "\n (this option can be given more than once)" |
||||||
|
+ "\n -only <class|package.*>" |
||||||
|
+ "\n skip all but the given class or package" |
||||||
|
+ "\n (this option can be given more than once)"); |
||||||
|
System.exit(0); |
||||||
|
} |
||||||
|
|
||||||
|
private static void decorateClass(final EditorContext editor, |
||||||
|
final ClassInfo info) { |
||||||
|
final ClassFile classFile = (ClassFile) info; |
||||||
|
|
||||||
|
if (!CounterDecorate.FORCE) { |
||||||
|
final File source = classFile.file(); |
||||||
|
final File target = classFile.outputFile(); |
||||||
|
|
||||||
|
if ((source != null) && (target != null) && source.exists() |
||||||
|
&& target.exists() |
||||||
|
&& (source.lastModified() < target.lastModified())) { |
||||||
|
|
||||||
|
if (CounterDecorate.VERBOSE > 1) { |
||||||
|
System.out.println(classFile.name() + " is up to date"); |
||||||
|
} |
||||||
|
|
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (CounterDecorate.VERBOSE > 2) { |
||||||
|
classFile.print(System.out); |
||||||
|
} |
||||||
|
|
||||||
|
final ClassEditor c = editor.editClass(info); |
||||||
|
|
||||||
|
boolean skip = false; |
||||||
|
|
||||||
|
final String name = c.type().className(); |
||||||
|
final String qual = c.type().qualifier() + "/*"; |
||||||
|
|
||||||
|
// Edit only classes explicitly mentioned.
|
||||||
|
if (CounterDecorate.ONLY.size() > 0) { |
||||||
|
skip = true; |
||||||
|
|
||||||
|
// Only edit classes we explicitly don't name.
|
||||||
|
for (int i = 0; i < CounterDecorate.ONLY.size(); i++) { |
||||||
|
final String pkg = (String) CounterDecorate.ONLY.get(i); |
||||||
|
|
||||||
|
if (name.equals(pkg) || qual.equals(pkg)) { |
||||||
|
skip = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Don't edit classes we explicitly skip.
|
||||||
|
if (!skip) { |
||||||
|
for (int i = 0; i < CounterDecorate.SKIP.size(); i++) { |
||||||
|
final String pkg = (String) CounterDecorate.SKIP.get(i); |
||||||
|
|
||||||
|
if (name.equals(pkg) || qual.equals(pkg)) { |
||||||
|
skip = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (skip) { |
||||||
|
if (CounterDecorate.VERBOSE > 0) { |
||||||
|
System.out.println("Skipping " + c.type().className()); |
||||||
|
} |
||||||
|
|
||||||
|
editor.release(info); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (CounterDecorate.VERBOSE > 0) { |
||||||
|
System.out.println("Decorating class " + c.type().className()); |
||||||
|
} |
||||||
|
|
||||||
|
if (CounterDecorate.VERBOSE > 2) { |
||||||
|
((ClassFile) info).print(System.out); |
||||||
|
} |
||||||
|
|
||||||
|
final MethodInfo[] methods = c.methods(); |
||||||
|
|
||||||
|
for (int j = 0; j < methods.length; j++) { |
||||||
|
MethodEditor m; |
||||||
|
|
||||||
|
try { |
||||||
|
m = editor.editMethod(methods[j]); |
||||||
|
} catch (final ClassFormatException ex) { |
||||||
|
System.err.println(ex.getMessage()); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
CounterDecorate.transform(m); |
||||||
|
editor.commit(methods[j]); |
||||||
|
} |
||||||
|
|
||||||
|
editor.commit(info); |
||||||
|
} |
||||||
|
|
||||||
|
private static void transform(final MethodEditor method) { |
||||||
|
if (CounterDecorate.VERBOSE > 1) { |
||||||
|
System.out.println("Decorating method " + method); |
||||||
|
} |
||||||
|
|
||||||
|
final MemberRef rcfield = new MemberRef(Type |
||||||
|
.getType(CounterDecorate.COUNTER_MAIN), new NameAndType( |
||||||
|
CounterDecorate.COUNTER_RCNAME, Type |
||||||
|
.getType(CounterDecorate.COUNTER_TYPE))); |
||||||
|
final MemberRef aufield = new MemberRef(Type |
||||||
|
.getType(CounterDecorate.COUNTER_MAIN), new NameAndType( |
||||||
|
CounterDecorate.COUNTER_AUNAME, Type |
||||||
|
.getType(CounterDecorate.COUNTER_TYPE))); |
||||||
|
final MemberRef sufield = new MemberRef(Type |
||||||
|
.getType(CounterDecorate.COUNTER_MAIN), new NameAndType( |
||||||
|
CounterDecorate.COUNTER_SUNAME, Type |
||||||
|
.getType(CounterDecorate.COUNTER_TYPE))); |
||||||
|
|
||||||
|
final ListIterator iter = method.code().listIterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final Object ce = iter.next(); |
||||||
|
|
||||||
|
if (CounterDecorate.VERBOSE > 2) { |
||||||
|
System.out.println("Examining " + ce); |
||||||
|
} |
||||||
|
|
||||||
|
if (ce instanceof Instruction) { |
||||||
|
final Instruction inst = (Instruction) ce; |
||||||
|
|
||||||
|
if (inst.opcodeClass() == Opcode.opcx_aupdate) { |
||||||
|
iter.remove(); |
||||||
|
iter.add(new Instruction(Opcode.opcx_getstatic, aufield)); |
||||||
|
iter.next(); |
||||||
|
iter.add(new Instruction(Opcode.opcx_ldc, new Integer(1))); |
||||||
|
iter.next(); |
||||||
|
iter.add(new Instruction(Opcode.opcx_iadd)); |
||||||
|
iter.next(); |
||||||
|
iter.add(new Instruction(Opcode.opcx_putstatic, aufield)); |
||||||
|
iter.next(); |
||||||
|
} |
||||||
|
if (inst.opcodeClass() == Opcode.opcx_supdate) { |
||||||
|
iter.remove(); |
||||||
|
iter.add(new Instruction(Opcode.opcx_getstatic, sufield)); |
||||||
|
iter.next(); |
||||||
|
iter.add(new Instruction(Opcode.opcx_ldc, new Integer(1))); |
||||||
|
iter.next(); |
||||||
|
iter.add(new Instruction(Opcode.opcx_iadd)); |
||||||
|
iter.next(); |
||||||
|
iter.add(new Instruction(Opcode.opcx_putstatic, sufield)); |
||||||
|
iter.next(); |
||||||
|
} else if (inst.opcodeClass() == Opcode.opcx_rc) { |
||||||
|
iter.remove(); |
||||||
|
iter.add(new Instruction(Opcode.opcx_getstatic, rcfield)); |
||||||
|
iter.next(); |
||||||
|
iter.add(new Instruction(Opcode.opcx_ldc, new Integer(1))); |
||||||
|
iter.next(); |
||||||
|
iter.add(new Instruction(Opcode.opcx_iadd)); |
||||||
|
iter.next(); |
||||||
|
iter.add(new Instruction(Opcode.opcx_putstatic, rcfield)); |
||||||
|
iter.next(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,129 @@ |
|||||||
|
# All files in the distribution of BLOAT (Bytecode Level Optimization and
|
||||||
|
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
||||||
|
# Research Foundation of Purdue University. All rights reserved.
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
|
||||||
|
CLASS = \
|
||||||
|
Benchmark.class\
|
||||||
|
Shade.class\
|
||||||
|
Stats.class\
|
||||||
|
BenchmarkSecurityManager.class\
|
||||||
|
Nonstop.class\
|
||||||
|
Times.class\
|
||||||
|
CounterDecorate.class
|
||||||
|
|
||||||
|
JNIH = EDU_purdue_cs_bloat_benchmark_Benchmark.h \
|
||||||
|
EDU_purdue_cs_bloat_benchmark_Shade.h \
|
||||||
|
EDU_purdue_cs_bloat_benchmark_Stats.h \
|
||||||
|
EDU_purdue_cs_bloat_benchmark_Times.h \
|
||||||
|
EDU_purdue_cs_bloat_benchmark_Nonstop.h
|
||||||
|
|
||||||
|
OBJ = benchmark.o shade.o stats.o nonstop.o times.o
|
||||||
|
|
||||||
|
LIB = libbenchmark_g.so libbenchmark.so libshade.so libstats.so libstats_g.so libnonstop.so libtimes.so libtimes_g.so
|
||||||
|
|
||||||
|
.SUFFIXES: .java .class |
||||||
|
|
||||||
|
JAVA_HOME = /u/u83/pps/java
|
||||||
|
JAVAC = $(JAVA_HOME)/bin/javac
|
||||||
|
JAVAH = $(JAVA_HOME)/bin/javah
|
||||||
|
JFLAGS = -g
|
||||||
|
CLASSPATH = $(JAVA_HOME)/lib/classes.zip
|
||||||
|
CFLAGS = -K pic -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/solaris \
|
||||||
|
-I/u/u83/pps/perfmon/include
|
||||||
|
|
||||||
|
all: class $(LIB) |
||||||
|
|
||||||
|
clean: |
||||||
|
rm -f *.class *.o $(JNIH) $(OBJ) $(LIB)
|
||||||
|
|
||||||
|
class: |
||||||
|
@files=`$(MAKE) -n _class | grep javac | cut -d' ' -f4`; \
|
||||||
|
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
|
||||||
|
if [ "x$$files" != "x" ]; then \
|
||||||
|
echo $(JAVAC) $(JFLAGS) -classpath $$cpath $$files; \
|
||||||
|
$(JAVAC) $(JFLAGS) -classpath $$cpath $$files; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
_class: $(CLASS) |
||||||
|
|
||||||
|
.java.class: |
||||||
|
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
|
||||||
|
$(JAVAC) -classpath $$cpath $<
|
||||||
|
|
||||||
|
libbenchmark.so: benchmark.o |
||||||
|
cc benchmark.o -o libbenchmark.so /u/u83/pps/perfmon/lib/libperfmon.a -G
|
||||||
|
|
||||||
|
libbenchmark_g.so: benchmark.o |
||||||
|
cc benchmark.o -o libbenchmark_g.so /u/u83/pps/perfmon/lib/libperfmon.a -G
|
||||||
|
|
||||||
|
libshade.so: shade.o |
||||||
|
cc shade.o -o libshade.so -G
|
||||||
|
|
||||||
|
libstats_g.so: stats.o |
||||||
|
cc stats.o -o libstats_g.so -G
|
||||||
|
|
||||||
|
libstats.so: stats.o |
||||||
|
cc stats.o -o libstats.so -G
|
||||||
|
|
||||||
|
libtimes.so: times.o |
||||||
|
cc times.o -o libtimes.so -G
|
||||||
|
|
||||||
|
libtimes_g.so: times.o |
||||||
|
cc times.o -o libtimes_g.so -G
|
||||||
|
|
||||||
|
libnonstop.so: nonstop.o |
||||||
|
cc nonstop.o -o libnonstop.so -G
|
||||||
|
|
||||||
|
EDU_purdue_cs_bloat_benchmark_Benchmark.h: Benchmark.java |
||||||
|
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
|
||||||
|
$(JAVAH) -jni -classpath $$cpath \
|
||||||
|
EDU.purdue.cs.bloat.benchmark.Benchmark
|
||||||
|
|
||||||
|
EDU_purdue_cs_bloat_benchmark_Shade.h: Shade.java |
||||||
|
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
|
||||||
|
$(JAVAH) -jni -classpath $$cpath \
|
||||||
|
EDU.purdue.cs.bloat.benchmark.Shade
|
||||||
|
|
||||||
|
EDU_purdue_cs_bloat_benchmark_Stats.h: Stats.java |
||||||
|
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
|
||||||
|
$(JAVAH) -jni -classpath $$cpath \
|
||||||
|
EDU.purdue.cs.bloat.benchmark.Stats
|
||||||
|
|
||||||
|
EDU_purdue_cs_bloat_benchmark_Times.h: Times.java |
||||||
|
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
|
||||||
|
$(JAVAH) -jni -classpath $$cpath \
|
||||||
|
EDU.purdue.cs.bloat.benchmark.Times
|
||||||
|
|
||||||
|
EDU_purdue_cs_bloat_benchmark_Nonstop.h: Nonstop.java |
||||||
|
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
|
||||||
|
$(JAVAH) -jni -classpath $$cpath \
|
||||||
|
EDU.purdue.cs.bloat.benchmark.Nonstop
|
||||||
|
|
||||||
|
nonstop.o: nonstop.c EDU_purdue_cs_bloat_benchmark_Nonstop.h |
||||||
|
|
||||||
|
benchmark.o: benchmark.c EDU_purdue_cs_bloat_benchmark_Benchmark.h |
||||||
|
|
||||||
|
times.o: times.c EDU_purdue_cs_bloat_benchmark_Times.h |
||||||
|
|
||||||
|
shade.o: shade.c EDU_purdue_cs_bloat_benchmark_Shade.h |
||||||
|
|
||||||
|
stats.o: stats.c EDU_purdue_cs_bloat_benchmark_Stats.h |
||||||
|
|
||||||
|
.c.o: |
||||||
|
cc -c $(CFLAGS) $<
|
||||||
|
|
@ -0,0 +1,101 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.benchmark; |
||||||
|
|
||||||
|
/** |
||||||
|
* Runs a Java program multiple times without the Virtual Machine exiting. |
||||||
|
* |
||||||
|
* @see BenchmarkSecurityManager |
||||||
|
*/ |
||||||
|
public class Nonstop { |
||||||
|
static { |
||||||
|
// Load native code from libbenchmark.so
|
||||||
|
System.loadLibrary("nonstop"); |
||||||
|
} |
||||||
|
|
||||||
|
public static native void run(Class main, String[] args); |
||||||
|
|
||||||
|
public static void main(final String[] args) throws Exception { |
||||||
|
int runs = 1; |
||||||
|
int eat = 0; |
||||||
|
|
||||||
|
if (args.length <= 1) { |
||||||
|
Nonstop.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
for (eat = 0; eat < args.length; eat++) { |
||||||
|
if (args[eat].equals("-run")) { |
||||||
|
if (++eat >= args.length) { |
||||||
|
Nonstop.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
runs = Integer.parseInt(args[eat]); |
||||||
|
|
||||||
|
if (runs <= 0) { |
||||||
|
Nonstop.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
// The main class
|
||||||
|
eat++; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Got all the args. */ |
||||||
|
if (eat > args.length) { |
||||||
|
Nonstop.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final BenchmarkSecurityManager sec = new BenchmarkSecurityManager(); |
||||||
|
System.setSecurityManager(sec); |
||||||
|
|
||||||
|
final String mainClassName = args[eat - 1]; |
||||||
|
final String[] a = new String[args.length - eat]; |
||||||
|
|
||||||
|
System.err.println("Running " + mainClassName); |
||||||
|
|
||||||
|
for (int i = 0; i < runs; i++) { |
||||||
|
try { |
||||||
|
final Class mainClass = Class.forName(mainClassName); |
||||||
|
|
||||||
|
System.arraycopy(args, eat, a, 0, a.length); |
||||||
|
|
||||||
|
Benchmark.run(mainClass, a); |
||||||
|
} catch (final SecurityException e) { |
||||||
|
continue; |
||||||
|
} catch (final Exception e) { |
||||||
|
e.printStackTrace(System.err); |
||||||
|
sec.allowExit = true; |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sec.allowExit = true; |
||||||
|
} |
||||||
|
|
||||||
|
private static void usage() { |
||||||
|
System.err.print("usage: java EDU.purdue.cs.bloat.Benchmark "); |
||||||
|
System.err.println("options class args..."); |
||||||
|
System.err.println("where options are one of:"); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,110 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.benchmark; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class is used to execute the BLOAT benchmarks while the Shade |
||||||
|
* performanace monitoring software is running. |
||||||
|
*/ |
||||||
|
public class Shade { |
||||||
|
static { |
||||||
|
System.loadLibrary("shade"); |
||||||
|
} |
||||||
|
|
||||||
|
public static native void run(Class main, String[] args, boolean quit); |
||||||
|
|
||||||
|
public static void main(final String[] args) throws Exception { |
||||||
|
boolean quit = false; |
||||||
|
int runs = 1; |
||||||
|
int eat = 0; |
||||||
|
|
||||||
|
if (args.length <= 1) { |
||||||
|
Shade.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
for (eat = 0; eat < args.length; eat++) { |
||||||
|
if (args[eat].equals("-quit")) { |
||||||
|
quit = true; |
||||||
|
} else if (args[eat].equals("-run")) { |
||||||
|
if (++eat >= args.length) { |
||||||
|
Shade.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
runs = Integer.parseInt(args[eat]); |
||||||
|
|
||||||
|
if (runs <= 0) { |
||||||
|
Shade.usage(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
// The main class
|
||||||
|
eat++; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Got all the args. */ |
||||||
|
if (eat > args.length) { |
||||||
|
Shade.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
// Install a secutiry manager in which we can control whether or
|
||||||
|
// not the virtual machine is allowed to exit. We want to be able
|
||||||
|
// to make multiple runs of the main class without the VM exiting.
|
||||||
|
final BenchmarkSecurityManager sec = new BenchmarkSecurityManager(); |
||||||
|
System.setSecurityManager(sec); |
||||||
|
|
||||||
|
final String mainClassName = args[eat - 1]; |
||||||
|
final String[] a = new String[args.length - eat]; |
||||||
|
|
||||||
|
System.err.println("Running " + mainClassName); |
||||||
|
|
||||||
|
for (int i = 0; i < runs; i++) { |
||||||
|
try { |
||||||
|
final Class mainClass = Class.forName(mainClassName); |
||||||
|
|
||||||
|
System.arraycopy(args, eat, a, 0, a.length); |
||||||
|
|
||||||
|
Shade.run(mainClass, a, quit); |
||||||
|
|
||||||
|
} catch (final SecurityException e) { |
||||||
|
// An execution of the mainClass finished and the VM attempted
|
||||||
|
// to exit, thus causing a SecutiryException to be thrown by
|
||||||
|
// the BenchmarkSecurityManager.
|
||||||
|
continue; |
||||||
|
|
||||||
|
} catch (final Exception e) { |
||||||
|
e.printStackTrace(System.err); |
||||||
|
sec.allowExit = true; |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sec.allowExit = true; |
||||||
|
} |
||||||
|
|
||||||
|
private static void usage() { |
||||||
|
System.err.print("usage: java EDU.purdue.cs.bloat.Shade "); |
||||||
|
System.err.println("options class args..."); |
||||||
|
System.err.println("where options are one of:"); |
||||||
|
System.err.println(" -run n time n runs of the program"); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,105 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.benchmark; |
||||||
|
|
||||||
|
/** |
||||||
|
* <tt>Stats</tt> is used to run a benchmark using an instrumented Java |
||||||
|
* Virtual machine that counts the number of each kind of bytecodes executed. |
||||||
|
* The counts are maintained in two C variables, <tt>instruction_count</tt> |
||||||
|
* and <tt>redundant_count</tt>. |
||||||
|
*/ |
||||||
|
public class Stats { |
||||||
|
|
||||||
|
static { |
||||||
|
System.loadLibrary("stats"); |
||||||
|
} |
||||||
|
|
||||||
|
public static native void run(Class main, String[] args); |
||||||
|
|
||||||
|
public static void main(final String[] args) throws Exception { |
||||||
|
int runs = 1; |
||||||
|
int eat = 0; |
||||||
|
|
||||||
|
if (args.length <= 1) { |
||||||
|
Stats.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
for (eat = 0; eat < args.length; eat++) { |
||||||
|
if (args[eat].equals("-run")) { |
||||||
|
if (++eat >= args.length) { |
||||||
|
Stats.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
runs = Integer.parseInt(args[eat]); |
||||||
|
|
||||||
|
if (runs <= 0) { |
||||||
|
Stats.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
// The main class
|
||||||
|
eat++; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Got all the args. */ |
||||||
|
if (eat > args.length) { |
||||||
|
Stats.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final BenchmarkSecurityManager sec = new BenchmarkSecurityManager(); |
||||||
|
System.setSecurityManager(sec); |
||||||
|
|
||||||
|
final String mainClassName = args[eat - 1]; |
||||||
|
final String[] a = new String[args.length - eat]; |
||||||
|
|
||||||
|
System.err.println("Running " + mainClassName); |
||||||
|
|
||||||
|
for (int i = 0; i < runs; i++) { |
||||||
|
try { |
||||||
|
final Class mainClass = Class.forName(mainClassName); |
||||||
|
|
||||||
|
System.arraycopy(args, eat, a, 0, a.length); |
||||||
|
|
||||||
|
Stats.run(mainClass, a); |
||||||
|
|
||||||
|
} catch (final SecurityException e) { |
||||||
|
continue; |
||||||
|
|
||||||
|
} catch (final Exception e) { |
||||||
|
e.printStackTrace(System.err); |
||||||
|
sec.allowExit = true; |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sec.allowExit = true; |
||||||
|
} |
||||||
|
|
||||||
|
private static void usage() { |
||||||
|
System.err.print("usage: java EDU.purdue.cs.bloat.Stats "); |
||||||
|
System.err.println("options class args..."); |
||||||
|
System.err.println("where options are one of:"); |
||||||
|
System.err.println(" -run n time n runs of the program"); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.benchmark; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class allows Java to access the information obtained by the UNIX system |
||||||
|
* call <tt>times</tt>. |
||||||
|
*/ |
||||||
|
public class Times { |
||||||
|
static { |
||||||
|
// Load native code from libbenchmark.so
|
||||||
|
System.loadLibrary("times"); |
||||||
|
} |
||||||
|
|
||||||
|
static float userTime; |
||||||
|
|
||||||
|
static float systemTime; |
||||||
|
|
||||||
|
/** |
||||||
|
* Takes a "snapshot" of the system. Reads various items from the result of |
||||||
|
* <tt>times</tt>. |
||||||
|
* |
||||||
|
* @return <tt>true</tt> if everything is successful |
||||||
|
*/ |
||||||
|
public static native boolean snapshot(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the user time used by this process in seconds. |
||||||
|
*/ |
||||||
|
public static float userTime() { |
||||||
|
return (Times.userTime); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the system time used by this process in seconds. |
||||||
|
*/ |
||||||
|
public static float systemTime() { |
||||||
|
return (Times.systemTime); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Test program. |
||||||
|
*/ |
||||||
|
public static void main(final String[] args) throws Exception { |
||||||
|
System.out.println("Starting Test"); |
||||||
|
|
||||||
|
if (Times.snapshot() == false) { |
||||||
|
System.err.println("Error during snapshot"); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.println("System time: " + Times.systemTime()); |
||||||
|
System.out.println("User time: " + Times.userTime()); |
||||||
|
|
||||||
|
System.out.println("Ending Test"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,260 @@ |
|||||||
|
/*
|
||||||
|
* This C file provides the implementation of |
||||||
|
* EDU.purdue.cs.bloat.Benchmark's native run method. It is loosely |
||||||
|
* based on the "timeit" example found in:
|
||||||
|
* /u/u83/pps/perfmon/examples/ |
||||||
|
*/ |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <signal.h> |
||||||
|
#include <unistd.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <errno.h> |
||||||
|
#include <sys/types.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include <sys/processor.h> |
||||||
|
#include <sys/procset.h> |
||||||
|
#include <sys/wait.h> |
||||||
|
#include <sys/priocntl.h> |
||||||
|
#include <sys/rtpriocntl.h> |
||||||
|
|
||||||
|
#include <jni.h> |
||||||
|
#include "EDU_purdue_cs_bloat_benchmark_Benchmark.h" |
||||||
|
#include "perfmon.h" |
||||||
|
|
||||||
|
#define MAIN_NAME "main" |
||||||
|
#define MAIN_SIG "([Ljava/lang/String;)V" |
||||||
|
#define FALSE 0 |
||||||
|
#define TRUE (!FALSE) |
||||||
|
#define PIC_SNAPSHOT_INTERVAL 5 |
||||||
|
#define check_syscall(exp) \ |
||||||
|
do { if ((exp) == -1) die_with_errno(#exp, __FILE__, __LINE__); } while(0); |
||||||
|
|
||||||
|
#ifndef U |
||||||
|
#define U(a) a |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
/* Global locations for holding PIC values */ |
||||||
|
static unsigned long long pic0_counter; /* Cumulative PIC0 */ |
||||||
|
static unsigned long long pic1_counter; /* Cumulative PIC1 */ |
||||||
|
static unsigned long pic0_interval; /* Change since last alarm */ |
||||||
|
static unsigned long pic1_interval; |
||||||
|
|
||||||
|
/* Other interesting global variables */ |
||||||
|
static int snapshotting = TRUE; |
||||||
|
static int want_realtime = FALSE; |
||||||
|
static int processor = 0; /* Processor we use */ |
||||||
|
static int mode; /* Determines what we count */ |
||||||
|
|
||||||
|
/* Stuff for running method */ |
||||||
|
static jmethodID method; /* Pointer to main method */ |
||||||
|
|
||||||
|
/* Error reporting function */ |
||||||
|
static void |
||||||
|
die_with_errno(const char* s, const char* file, int line) |
||||||
|
{ |
||||||
|
fprintf( stderr, "%s:%d:%s:%s\n", file, line, s, strerror(errno) ); |
||||||
|
exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* Functions for reading the PIC registers and keeping a running total
|
||||||
|
* in memory. Called before the Java program is run and when the |
||||||
|
* alarm goes off (i.e. when a "snapshot is taken"). |
||||||
|
*/ |
||||||
|
static void start_pic_snapshot() { |
||||||
|
unsigned long long pic; |
||||||
|
|
||||||
|
pic = get_pic(); |
||||||
|
pic0_interval -= extract_pic0( pic ); |
||||||
|
pic1_interval -= extract_pic1( pic ); |
||||||
|
} |
||||||
|
|
||||||
|
/* Computes the difference in the PIC between now and the last
|
||||||
|
* "snapshot" (i.e. the "interval"). Adds increments the overall |
||||||
|
* counts by these values. Resets the interval. |
||||||
|
*/ |
||||||
|
static void end_pic_snapshot() { |
||||||
|
unsigned long long pic; |
||||||
|
|
||||||
|
pic = get_pic(); |
||||||
|
|
||||||
|
pic0_interval += extract_pic0( pic ); |
||||||
|
pic1_interval += extract_pic1( pic ); |
||||||
|
|
||||||
|
pic0_counter += (unsigned long long)pic0_interval; |
||||||
|
pic1_counter += (unsigned long long)pic1_interval; |
||||||
|
|
||||||
|
pic0_interval = pic1_interval = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/* Function to catch interrupts and update the PIC values. This is
|
||||||
|
* called whenever the alarm "goes off". |
||||||
|
*/ |
||||||
|
static void alarm_handler( int U(sig), |
||||||
|
siginfo_t* U(info), |
||||||
|
ucontext_t* U(ctxt) ) |
||||||
|
{ |
||||||
|
if ( snapshotting ) { |
||||||
|
end_pic_snapshot(); |
||||||
|
start_pic_snapshot(); |
||||||
|
alarm(PIC_SNAPSHOT_INTERVAL); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Performs initialization necessary for running a Java program |
||||||
|
* multiple times and monitoring what goes on using perfmon. For |
||||||
|
* instance, we obtain a pointer to the main method, set up the alarm |
||||||
|
* stuff, and initialize perfmon. |
||||||
|
* |
||||||
|
* Runs a Java program (a main method) with the perfmon counters |
||||||
|
* turned on. We have to be careful to make sure that we correctly |
||||||
|
* account for counters that may overflow. Thus, we set an alarm to |
||||||
|
* go off every five seconds and record the counters. The code that |
||||||
|
* handles all of the alarm stuff was contributed by Kevin Corry. |
||||||
|
* |
||||||
|
* Parameters: |
||||||
|
* env JNI Environment |
||||||
|
* clazz Reference to EDU.purdue.cs.bloat.benchmark.Benchmark |
||||||
|
* main Class containing main method |
||||||
|
* args Arguments to main method |
||||||
|
*/ |
||||||
|
JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Benchmark_init( |
||||||
|
JNIEnv *env, jclass clazz, jclass main) |
||||||
|
{ |
||||||
|
unsigned long long pcr; |
||||||
|
int fd; |
||||||
|
struct sigaction act; |
||||||
|
|
||||||
|
(*env)->ExceptionClear(env); |
||||||
|
|
||||||
|
method = (*env)->GetStaticMethodID(env, main, MAIN_NAME, MAIN_SIG); |
||||||
|
|
||||||
|
if ((*env)->ExceptionOccurred(env) != NULL) { |
||||||
|
fprintf(stderr, "Method not found: %s%s\n", MAIN_NAME, MAIN_SIG); |
||||||
|
(*env)->ExceptionDescribe(env); |
||||||
|
(*env)->ExceptionClear(env); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
/* Determine which things we want to count */ |
||||||
|
if (mode == 0) { |
||||||
|
/* Count load interlock induced stalls and instructions. */ |
||||||
|
pcr = PCR_USER_TRACE | PCR_S0_STALL_LOAD | PCR_S1_INSTR_CNT; |
||||||
|
|
||||||
|
} else if (mode == 1) { |
||||||
|
/* Count data cache hit rate. */ |
||||||
|
pcr = PCR_USER_TRACE | PCR_S0_DC_READ | PCR_S1_DC_READ_HIT; |
||||||
|
|
||||||
|
} else if (mode == 2) { |
||||||
|
/* Count icache miss induced stalls and cycles. */ |
||||||
|
pcr = PCR_USER_TRACE | PCR_S0_STALL_IC_MISS | PCR_S1_CYCLE_CNT; |
||||||
|
|
||||||
|
} else if (mode == 3) { |
||||||
|
/* Count instructions and cycles. */ |
||||||
|
pcr = PCR_USER_TRACE | PCR_S0_INSTR_CNT | PCR_S1_CYCLE_CNT; |
||||||
|
|
||||||
|
} else { |
||||||
|
fprintf(stderr, "invalid mode: %d, must be [012]\n", mode); |
||||||
|
exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
/* Set up perfmon. Bind this thread to a processor so that it can't
|
||||||
|
get away from us. Flush the cache for the heck of it. */ |
||||||
|
check_syscall(processor_bind(P_PID, P_MYID, processor, 0)); |
||||||
|
check_syscall(fd = open( "/dev/perfmon", O_RDONLY)); |
||||||
|
check_syscall(ioctl(fd, PERFMON_FLUSH_CACHE )); |
||||||
|
check_syscall(ioctl(fd, PERFMON_SETPCR, &pcr)); |
||||||
|
check_syscall(close(fd)); |
||||||
|
|
||||||
|
/* Set up the periodic interrupts to make measurements. This way
|
||||||
|
we don't have to worry about the PIC counters wrapping around. */ |
||||||
|
act.sa_handler = 0; |
||||||
|
act.sa_sigaction = (void(*)(int,siginfo_t*,void*))alarm_handler; |
||||||
|
sigemptyset(&act.sa_mask); |
||||||
|
act.sa_flags = SA_SIGINFO; |
||||||
|
check_syscall(sigaction(SIGALRM, &act, 0)); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Actually runs the program and takes measurements before and after. |
||||||
|
* Prints the results out. |
||||||
|
*/ |
||||||
|
JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Benchmark_run( |
||||||
|
JNIEnv *env, jclass clazz, jclass main, jobjectArray args) { |
||||||
|
pid_t pid; |
||||||
|
|
||||||
|
hrtime_t starttime; |
||||||
|
hrtime_t endtime; |
||||||
|
unsigned long long starttick; |
||||||
|
unsigned long long endtick; |
||||||
|
|
||||||
|
/* Initialize counters and intervals */ |
||||||
|
snapshotting = TRUE; |
||||||
|
pic0_counter = pic1_counter = 0LL; |
||||||
|
pic0_interval = pic1_interval = 0; |
||||||
|
|
||||||
|
/* Print this guy. I'm not too sure why. */ |
||||||
|
fprintf(stderr, "0x%x\n", &gethrtime); |
||||||
|
|
||||||
|
/* Take an initial reading of the PICs */ |
||||||
|
fprintf(stderr, "reset\n"); |
||||||
|
cpu_sync(); |
||||||
|
clr_pic(); |
||||||
|
start_pic_snapshot(); |
||||||
|
|
||||||
|
starttick = get_tick(); /* Get number of cycles since power-on */ |
||||||
|
starttime = gethrtime(); /* Get the (high-resolution) time */ |
||||||
|
|
||||||
|
alarm(PIC_SNAPSHOT_INTERVAL); /* Set the alarm */ |
||||||
|
check_syscall(processor_bind(P_PID, P_MYID, processor, 0)); |
||||||
|
|
||||||
|
/* Run the Java program (benchmark). */ |
||||||
|
(*env)->CallStaticVoidMethod(env, main, method, args); |
||||||
|
|
||||||
|
/* Get whatever data from the counters and report it. */ |
||||||
|
cpu_sync(); |
||||||
|
snapshotting = FALSE; /* No more snapshooting */ |
||||||
|
end_pic_snapshot(); |
||||||
|
alarm(0); /* Turn off alarm */ |
||||||
|
|
||||||
|
endtime = gethrtime(); /* Get time */ |
||||||
|
endtick = get_tick(); /* Get number of cycles since power-on */ |
||||||
|
|
||||||
|
fprintf(stderr, "wall time %llu ns\n", endtime-starttime); |
||||||
|
fprintf(stderr, "ticks %llu\n", endtick-starttick); |
||||||
|
|
||||||
|
if (mode == 0) { |
||||||
|
/* Count load stalls and instructions. */ |
||||||
|
fprintf(stderr, "load stalls %llu\n", pic0_counter); |
||||||
|
fprintf(stderr, "insts %llu\n", pic1_counter); |
||||||
|
} |
||||||
|
else if (mode == 1) { |
||||||
|
/* Count data cache hit rate. */ |
||||||
|
fprintf(stderr, "D-cache reads %llu\n", pic0_counter); |
||||||
|
fprintf(stderr, "D-cache hits %llu\n", pic1_counter); |
||||||
|
} |
||||||
|
else if (mode == 2) { |
||||||
|
/* Count I-cache miss stalls and cycles. */ |
||||||
|
fprintf(stderr, "I-miss stalls %llu\n", pic0_counter); |
||||||
|
fprintf(stderr, "cycles %llu\n", pic1_counter); |
||||||
|
} |
||||||
|
else if (mode == 3) { |
||||||
|
/* Count instructions and cycles. */ |
||||||
|
fprintf(stderr, "insts %llu\n", pic0_counter); |
||||||
|
fprintf(stderr, "cycles %llu\n", pic1_counter); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
fflush(stderr); |
||||||
|
} |
||||||
|
|
||||||
|
/* Set the mode. The mode tells us which things to count.
|
||||||
|
*/ |
||||||
|
JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Benchmark_setMode( |
||||||
|
JNIEnv *env, jclass clazz, jint m) |
||||||
|
{ |
||||||
|
mode = m; |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
<html> |
||||||
|
<body> |
||||||
|
|
||||||
|
<p>Contains classes that are used to run benchmarks that measure the |
||||||
|
performance of BLOATed code. These classes allow us to do things like |
||||||
|
run a program several times in the same virtual machine |
||||||
|
invocation.</p> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,39 @@ |
|||||||
|
#include <jni.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include "EDU_purdue_cs_bloat_benchmark_Shade.h" |
||||||
|
|
||||||
|
#define MAIN_NAME "main" |
||||||
|
#define MAIN_SIG "([Ljava/lang/String;)V" |
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Shade_run( |
||||||
|
JNIEnv *env, jclass clazz, jclass main, jobjectArray args, jboolean quit) |
||||||
|
{ |
||||||
|
jmethodID method; |
||||||
|
|
||||||
|
(*env)->ExceptionClear(env); |
||||||
|
|
||||||
|
method = (*env)->GetStaticMethodID(env, main, MAIN_NAME, MAIN_SIG); |
||||||
|
|
||||||
|
if ((*env)->ExceptionOccurred(env) != NULL) { |
||||||
|
fprintf(stderr, "Method not found: %s%s\n", MAIN_NAME, MAIN_SIG); |
||||||
|
(*env)->ExceptionDescribe(env); |
||||||
|
(*env)->ExceptionClear(env); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
system("sh ./run_pre"); |
||||||
|
|
||||||
|
if (quit) { |
||||||
|
fprintf(stderr, "0x%x\n", &gethrtime); |
||||||
|
fflush(stderr); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
gethrtime(); |
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(env, main, method, args); |
||||||
|
|
||||||
|
gethrtime(); |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
#include <jni.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include "EDU_purdue_cs_bloat_benchmark_Stats.h" |
||||||
|
|
||||||
|
extern int instruction_count[256][256]; |
||||||
|
extern int redundant_count[256]; |
||||||
|
|
||||||
|
#define MAIN_NAME "main" |
||||||
|
#define MAIN_SIG "([Ljava/lang/String;)V" |
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Stats_run( |
||||||
|
JNIEnv *env, jclass clazz, jclass main, jobjectArray args) { |
||||||
|
jmethodID method; |
||||||
|
|
||||||
|
(*env)->ExceptionClear(env); |
||||||
|
|
||||||
|
method = (*env)->GetStaticMethodID(env, main, MAIN_NAME, MAIN_SIG); |
||||||
|
|
||||||
|
if ((*env)->ExceptionOccurred(env) != NULL) { |
||||||
|
fprintf(stderr, "Method not found: %s%s\n", MAIN_NAME, MAIN_SIG); |
||||||
|
(*env)->ExceptionDescribe(env); |
||||||
|
(*env)->ExceptionClear(env); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
system("sh ./run_pre"); |
||||||
|
|
||||||
|
memset(instruction_count, 0, sizeof(instruction_count)); |
||||||
|
memset(redundant_count, 0, sizeof(redundant_count)); |
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(env, main, method, args); |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
*.class |
@ -0,0 +1,371 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.cfg; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.editor.*; |
||||||
|
import EDU.purdue.cs.bloat.tree.*; |
||||||
|
import EDU.purdue.cs.bloat.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* <tt>Block</tt> represents a basic block of code used in control flow |
||||||
|
* graphs. A basic block is always entered at its beginning and exits at its |
||||||
|
* end. That is, its first statement is a label and its last statement is a |
||||||
|
* jump. There are no other labels or jumps in between. |
||||||
|
* <p> |
||||||
|
* Each <tt>Block</tt> knows its parent block and its children in the |
||||||
|
* dominator and postdominator trees. It also knows which blocks are in its |
||||||
|
* dominance frontier and its postdominance frontier. |
||||||
|
* |
||||||
|
* @see FlowGraph |
||||||
|
* @see DominatorTree |
||||||
|
* @see DominanceFrontier |
||||||
|
*/ |
||||||
|
public class Block extends GraphNode { |
||||||
|
// There are several "types" of Blocks. A NON_HEADER block is not the
|
||||||
|
// header of a loop. An IRREDUCIBLE block is one of the headers of an
|
||||||
|
// irreducible loop. An irriducible loop has more than one entry
|
||||||
|
// point. They are very rare and are really ugly. The loop
|
||||||
|
// transformer tries to fix up mutiple headers. A REDUCIBLE header is
|
||||||
|
// a header for a reducible loop.
|
||||||
|
public static final int NON_HEADER = 0; |
||||||
|
|
||||||
|
public static final int IRREDUCIBLE = 1; |
||||||
|
|
||||||
|
public static final int REDUCIBLE = 2; |
||||||
|
|
||||||
|
FlowGraph graph; // CFG to which this Block belongs
|
||||||
|
|
||||||
|
Label label; // This Block's Label
|
||||||
|
|
||||||
|
Tree tree; // Expression tree for this block
|
||||||
|
|
||||||
|
Block domParent; // Block that (immediately) dominates this Block
|
||||||
|
|
||||||
|
Block pdomParent; |
||||||
|
|
||||||
|
Set domChildren; // Blocks that this Block dominates
|
||||||
|
|
||||||
|
Set pdomChildren; // The postdominator children of this block
|
||||||
|
|
||||||
|
Set domFrontier; // This Block's dominance frontier
|
||||||
|
|
||||||
|
Set pdomFrontier; // This Block's postdominace frontier
|
||||||
|
|
||||||
|
int blockType; // NON_HEADER, IRREDUCIBLE, or REDUCIBLE
|
||||||
|
|
||||||
|
Block header; // The block's loop header
|
||||||
|
|
||||||
|
StackOptimizer stackOptimizer; // Stack Optimizer
|
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param label |
||||||
|
* The block's label. The label may be thought of as the line of |
||||||
|
* code at which the block begins. |
||||||
|
* @param graph |
||||||
|
* The CFG containing the block. |
||||||
|
*/ |
||||||
|
Block(final Label label, final FlowGraph graph) { |
||||||
|
this.label = label; |
||||||
|
this.graph = graph; |
||||||
|
this.tree = null; |
||||||
|
this.header = null; |
||||||
|
this.blockType = Block.NON_HEADER; |
||||||
|
|
||||||
|
label.setStartsBlock(true); |
||||||
|
|
||||||
|
domParent = null; |
||||||
|
pdomParent = null; |
||||||
|
|
||||||
|
domChildren = new HashSet(); |
||||||
|
pdomChildren = new HashSet(); |
||||||
|
|
||||||
|
domFrontier = new HashSet(); |
||||||
|
pdomFrontier = new HashSet(); |
||||||
|
|
||||||
|
stackOptimizer = new StackOptimizer(this); // make StackOptimizer
|
||||||
|
// object
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the stack optimizer for this block. |
||||||
|
* |
||||||
|
* @return The stack optimizer. |
||||||
|
*/ |
||||||
|
public StackOptimizer stackOptimizer() { |
||||||
|
return stackOptimizer; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the expression tree for this block. |
||||||
|
* |
||||||
|
* @return The tree. |
||||||
|
*/ |
||||||
|
public Tree tree() { |
||||||
|
return tree; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the expression tree for this block. |
||||||
|
*/ |
||||||
|
public void setTree(final Tree tree) { |
||||||
|
this.tree = tree; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the CFG containing the block. |
||||||
|
* |
||||||
|
* @return The CFG. |
||||||
|
*/ |
||||||
|
public FlowGraph graph() { |
||||||
|
return graph; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the label associated with this block. |
||||||
|
*/ |
||||||
|
public Label label() { |
||||||
|
return label; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Visits the expression tree contained in this block. |
||||||
|
*/ |
||||||
|
public void visitChildren(final TreeVisitor visitor) { |
||||||
|
if (tree != null) { |
||||||
|
tree.visit(visitor); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visit(final TreeVisitor visitor) { |
||||||
|
visitor.visitBlock(this); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the type of this Block. A Block may have one of three types: |
||||||
|
* |
||||||
|
* <ul> |
||||||
|
* <li><tt>NON_HEADER</tt>: Not the header of any loop |
||||||
|
* <li><tt>IRREDUCIBLE</tt>: Header of an irreducible loop |
||||||
|
* <li><tt>REDUCIBLE</tt>: Header of a reducible loop |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* A <i>loop</i> is a strongly connected component of a control flow graph. |
||||||
|
* A loop's <i>header</i> is the block that dominates all other blocks in |
||||||
|
* the loop. A loop is <i>reducible</i> if the only way to enter the loop |
||||||
|
* is through the header. |
||||||
|
*/ |
||||||
|
void setBlockType(final int blockType) { |
||||||
|
this.blockType = blockType; |
||||||
|
|
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println(" Set block type " + this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the type of this Block. |
||||||
|
*/ |
||||||
|
int blockType() { |
||||||
|
return blockType; |
||||||
|
} |
||||||
|
|
||||||
|
public void setHeader(final Block header) { |
||||||
|
this.header = header; |
||||||
|
|
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println(" Set header " + this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Block header() { |
||||||
|
return header; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a string representation of this block. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
String s = "<block " + label + " hdr="; |
||||||
|
|
||||||
|
if (header != null) { |
||||||
|
s += header.label(); |
||||||
|
} else { |
||||||
|
s += "null"; |
||||||
|
} |
||||||
|
|
||||||
|
switch (blockType) { |
||||||
|
case NON_HEADER: |
||||||
|
break; |
||||||
|
case IRREDUCIBLE: |
||||||
|
s += " irred"; |
||||||
|
break; |
||||||
|
case REDUCIBLE: |
||||||
|
s += " red"; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (this == graph.source()) { |
||||||
|
return s + " source>"; |
||||||
|
} else if (this == graph.init()) { |
||||||
|
return s + " init>"; |
||||||
|
} else if (this == graph.sink()) { |
||||||
|
return s + " sink>"; |
||||||
|
} else { |
||||||
|
return s + ">"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the basic blocks that this Block immediately dominates. That is, |
||||||
|
* it returns this Block's children in the dominator tree for the CFG. |
||||||
|
*/ |
||||||
|
Collection domChildren() { |
||||||
|
return domChildren; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the immediate dominator of this Block. That is, it returns the |
||||||
|
* Block's parent in the dominator tree, its immediate dominator. |
||||||
|
*/ |
||||||
|
Block domParent() { |
||||||
|
return domParent; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Specifies that Block dominates this Block (parent in the dominator tree, |
||||||
|
* the immediate dominator). |
||||||
|
* |
||||||
|
* @param block |
||||||
|
* Block that dominates this Block. |
||||||
|
*/ |
||||||
|
void setDomParent(final Block block) { |
||||||
|
// If this Block already had a dominator specified, remove
|
||||||
|
// it from its dominator's children.
|
||||||
|
if (domParent != null) { |
||||||
|
domParent.domChildren.remove(this); |
||||||
|
} |
||||||
|
|
||||||
|
domParent = block; |
||||||
|
|
||||||
|
// Add this Block to its new dominator's children.
|
||||||
|
if (domParent != null) { |
||||||
|
domParent.domChildren.add(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns whether or this Block dominates another given Block. A node X |
||||||
|
* dominates a node Y when every path from the first node in the CFG (Enter) |
||||||
|
* to Y must pass through X. |
||||||
|
*/ |
||||||
|
public boolean dominates(final Block block) { |
||||||
|
Block p = block; |
||||||
|
|
||||||
|
while (p != null) { |
||||||
|
if (p == this) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
p = p.domParent(); |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the children of this Block in the CFG's postdominator tree. |
||||||
|
*/ |
||||||
|
Collection pdomChildren() { |
||||||
|
return pdomChildren; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the parent of this Block in the CFG's postdominator tree. |
||||||
|
*/ |
||||||
|
Block pdomParent() { |
||||||
|
return pdomParent; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets this Block's parent in the postdominator tree. |
||||||
|
*/ |
||||||
|
void setPdomParent(final Block block) { |
||||||
|
if (pdomParent != null) { |
||||||
|
pdomParent.pdomChildren.remove(this); |
||||||
|
} |
||||||
|
|
||||||
|
pdomParent = block; |
||||||
|
|
||||||
|
if (pdomParent != null) { |
||||||
|
pdomParent.pdomChildren.add(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Determines whether or not this block postdominates a given block. A block |
||||||
|
* X is said to postdominate a block Y when every path from Y to the last |
||||||
|
* node in the CFG (Exit) passes through X. This relationship can be thought |
||||||
|
* of as the reverse of dominance. That is, X dominates Y in the reverse |
||||||
|
* CFG. |
||||||
|
* |
||||||
|
* @see DominatorTree |
||||||
|
*/ |
||||||
|
public boolean postdominates(final Block block) { |
||||||
|
Block p = block; |
||||||
|
|
||||||
|
while (p != null) { |
||||||
|
if (p == this) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
p = p.pdomParent(); |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the blocks that are in this block's dominance frontier. The |
||||||
|
* dominance frontier of a node X in a CFG is the set of all nodes Y such |
||||||
|
* that X dominates a predacessor of Y, but does not strictly dominate Y. |
||||||
|
* Nodes in the dominance frontier always have more than one parent (a |
||||||
|
* join). |
||||||
|
* |
||||||
|
* @see DominanceFrontier |
||||||
|
*/ |
||||||
|
Collection domFrontier() { |
||||||
|
Assert.isTrue(domFrontier != null); |
||||||
|
return domFrontier; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the postdominance frontier for this node. A postdominace frontier |
||||||
|
* is essentially the same as a dominace frontier, but the postdominance |
||||||
|
* relationship is used instead of the dominance relationship. |
||||||
|
*/ |
||||||
|
Collection pdomFrontier() { |
||||||
|
Assert.isTrue(pdomFrontier != null); |
||||||
|
return pdomFrontier; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,158 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.cfg; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* <tt>DominanceFrontier</tt> is used to calculate the <i>dominance frontier</i> |
||||||
|
* of each node in a control flow graph. |
||||||
|
* <p> |
||||||
|
* The <i>dominance frontier</i> of a node x is the set of all nodes w such |
||||||
|
* that x dominates a predacessor of w, but does not strictly dominate w. |
||||||
|
* Basically, nodes in the dominance frontier have one parent that <b>is</b> |
||||||
|
* dominated by x and at least one parent that <b>is not</b> dominated by x. |
||||||
|
* <p> |
||||||
|
* <tt>DominanceFrontier</tt> can be used to calculate both the dominance |
||||||
|
* (forward) and the postdominance (reverse) frontiers for a control flow graph. |
||||||
|
* |
||||||
|
* @see FlowGraph |
||||||
|
*/ |
||||||
|
|
||||||
|
public class DominanceFrontier { |
||||||
|
/** |
||||||
|
* Calculates the dominance frontier for a cfg and notifies the blocks in it |
||||||
|
* appropriately. |
||||||
|
* |
||||||
|
* @param graph |
||||||
|
* The cfg to operate on |
||||||
|
* @param reverse |
||||||
|
* Do we calculate the postdominance frontier? |
||||||
|
*/ |
||||||
|
public static void buildFrontier(final FlowGraph graph, boolean reverse) { |
||||||
|
if (!reverse) { |
||||||
|
DominanceFrontier.calcFrontier(graph.source(), graph, reverse); |
||||||
|
} else { |
||||||
|
DominanceFrontier.calcFrontier(graph.sink(), graph, reverse); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Recursively traverses the cfg and builds up the dominance frontier. |
||||||
|
* <p> |
||||||
|
* A block n's dominance frontier is the union of two sets of nodes. The |
||||||
|
* first set is the nodes in the dominance frontier of the nodes that n |
||||||
|
* dominates that are not dominated by n's immediate dominator. The second |
||||||
|
* set consists of the successors of n that are not strictly dominated by n. |
||||||
|
* |
||||||
|
* @param block |
||||||
|
* The block to start from (either source or sink) |
||||||
|
* @param graph |
||||||
|
* The cfg from which to get blocks |
||||||
|
* @param reverse |
||||||
|
* Do we calculate the dominance or postdominance frontier? |
||||||
|
* |
||||||
|
* @return The blocks in the (post)dominance frontier of block |
||||||
|
*/ |
||||||
|
private static LinkedList calcFrontier(final Block block, |
||||||
|
final FlowGraph graph, boolean reverse) { |
||||||
|
// local is an array of Blocks that are in block's dominance
|
||||||
|
// frontier. It is indexed by the block's pre-order index. I
|
||||||
|
// suppose an array is used so that no block is added to the
|
||||||
|
// dominance frontier twice.
|
||||||
|
final Block[] local = new Block[graph.size()]; |
||||||
|
|
||||||
|
Iterator children; // The blocks that are dominated by block
|
||||||
|
|
||||||
|
if (!reverse) { |
||||||
|
children = block.domChildren().iterator(); |
||||||
|
} else { |
||||||
|
children = block.pdomChildren().iterator(); |
||||||
|
} |
||||||
|
|
||||||
|
// Recursively calculate the nodes in the dominance frontier of
|
||||||
|
// block that are not dominated by block's immediate dominator
|
||||||
|
while (children.hasNext()) { |
||||||
|
final Block child = (Block) children.next(); |
||||||
|
|
||||||
|
final LinkedList df = DominanceFrontier.calcFrontier(child, graph, |
||||||
|
reverse); |
||||||
|
|
||||||
|
final Iterator e = df.iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Block dfChild = (Block) e.next(); |
||||||
|
|
||||||
|
if (!reverse) { |
||||||
|
if (block != dfChild.domParent()) { |
||||||
|
local[graph.preOrderIndex(dfChild)] = dfChild; |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
if (block != dfChild.pdomParent()) { |
||||||
|
local[graph.preOrderIndex(dfChild)] = dfChild; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final Iterator succs = reverse ? graph.preds(block).iterator() : graph |
||||||
|
.succs(block).iterator(); |
||||||
|
|
||||||
|
// Caculate the successors of block that are not strictly
|
||||||
|
// dominated by block.
|
||||||
|
while (succs.hasNext()) { |
||||||
|
final Block succ = (Block) succs.next(); |
||||||
|
|
||||||
|
// If block is not the immediate (post)dominator of its
|
||||||
|
// successor, add it to block's dominance frontier.
|
||||||
|
if (!reverse) { |
||||||
|
if (block != succ.domParent()) { |
||||||
|
local[graph.preOrderIndex(succ)] = succ; |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
if (block != succ.pdomParent()) { |
||||||
|
local[graph.preOrderIndex(succ)] = succ; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final LinkedList v = new LinkedList(); // The dominance frontier
|
||||||
|
|
||||||
|
for (int i = 0; i < local.length; i++) { |
||||||
|
if (local[i] != null) { |
||||||
|
v.add(local[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Set block's (post)dominance frontier
|
||||||
|
if (!reverse) { |
||||||
|
block.domFrontier().clear(); |
||||||
|
block.domFrontier().addAll(v); |
||||||
|
} else { |
||||||
|
block.pdomFrontier().clear(); |
||||||
|
block.pdomFrontier().addAll(v); |
||||||
|
} |
||||||
|
|
||||||
|
return v; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,387 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.cfg; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* DominatorTree finds the dominator tree of a FlowGraph. |
||||||
|
* <p> |
||||||
|
* The algorithm used is Purdum-Moore. It isn't as fast as Lengauer-Tarjan, but |
||||||
|
* it's a lot simpler. |
||||||
|
* |
||||||
|
* @see FlowGraph |
||||||
|
* @see Block |
||||||
|
*/ |
||||||
|
public class DominatorTree { |
||||||
|
public static boolean DEBUG = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* Calculates what vertices dominate other verices and notify the basic |
||||||
|
* Blocks as to who their dominator is. |
||||||
|
* |
||||||
|
* @param graph |
||||||
|
* The cfg that is used to find the dominator tree. |
||||||
|
* @param reverse |
||||||
|
* Do we go in revsers? That is, are we computing the dominatance |
||||||
|
* (false) or postdominance (true) tree. |
||||||
|
* @see Block |
||||||
|
*/ |
||||||
|
public static void buildTree(final FlowGraph graph, boolean reverse) { |
||||||
|
final int size = graph.size(); // The number of vertices in the cfg
|
||||||
|
|
||||||
|
final Map snkPreds = new HashMap(); // The predacessor vertices from the
|
||||||
|
// sink
|
||||||
|
|
||||||
|
// Determine the predacessors of the cfg's sink node
|
||||||
|
DominatorTree.insertEdgesToSink(graph, snkPreds, reverse); |
||||||
|
|
||||||
|
// Get the index of the root
|
||||||
|
final int root = reverse ? graph.preOrderIndex(graph.sink()) : graph |
||||||
|
.preOrderIndex(graph.source()); |
||||||
|
|
||||||
|
Assert.isTrue((0 <= root) && (root < size)); |
||||||
|
|
||||||
|
// Bit matrix indicating the dominators of each vertex.
|
||||||
|
// If bit j of dom[i] is set, then node j dominates node i.
|
||||||
|
final BitSet[] dom = new BitSet[size]; |
||||||
|
|
||||||
|
// A bit vector of all 1's
|
||||||
|
final BitSet ALL = new BitSet(size); |
||||||
|
|
||||||
|
for (int i = 0; i < size; i++) { |
||||||
|
ALL.set(i); |
||||||
|
} |
||||||
|
|
||||||
|
// Initially, all the bits in the dominance matrix are set, except
|
||||||
|
// for the root node. The root node is initialized to have itself
|
||||||
|
// as an immediate dominator.
|
||||||
|
//
|
||||||
|
for (int i = 0; i < size; i++) { |
||||||
|
final BitSet blockDoms = new BitSet(size); |
||||||
|
dom[i] = blockDoms; |
||||||
|
|
||||||
|
if (i != root) { |
||||||
|
blockDoms.or(ALL); |
||||||
|
} else { |
||||||
|
blockDoms.set(root); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Did the dominator bit vector array change?
|
||||||
|
boolean changed = true; |
||||||
|
|
||||||
|
while (changed) { |
||||||
|
changed = false; |
||||||
|
|
||||||
|
// Get the basic blocks contained in the cfg
|
||||||
|
final Iterator blocks = reverse ? graph.postOrder().iterator() |
||||||
|
: graph.preOrder().iterator(); |
||||||
|
|
||||||
|
// Compute the dominators of each node in the cfg. We iterate
|
||||||
|
// over every node in the cfg. The dominators of a node, x, are
|
||||||
|
// found by taking the intersection of the dominator bit vectors
|
||||||
|
// of each predacessor of x and unioning that with x. This
|
||||||
|
// process is repeated until no changes are made to any dominator
|
||||||
|
// bit vector.
|
||||||
|
|
||||||
|
while (blocks.hasNext()) { |
||||||
|
final Block block = (Block) blocks.next(); |
||||||
|
|
||||||
|
final int i = graph.preOrderIndex(block); |
||||||
|
|
||||||
|
Assert.isTrue((0 <= i) && (i < size), "Unreachable block " |
||||||
|
+ block); |
||||||
|
|
||||||
|
// We already know the dominators of the root, keep looking
|
||||||
|
if (i == root) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
final BitSet oldSet = dom[i]; |
||||||
|
final BitSet blockDoms = new BitSet(size); |
||||||
|
blockDoms.or(oldSet); |
||||||
|
|
||||||
|
// print(graph, reverse, "old set", i, blockDoms);
|
||||||
|
|
||||||
|
// blockDoms := intersection of dom(pred) for all pred(block).
|
||||||
|
Collection preds = reverse ? graph.succs(block) : graph |
||||||
|
.preds(block); |
||||||
|
|
||||||
|
Iterator e = preds.iterator(); |
||||||
|
|
||||||
|
// Find the intersection of the dominators of block's
|
||||||
|
// predacessors.
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Block pred = (Block) e.next(); |
||||||
|
|
||||||
|
final int j = graph.preOrderIndex(pred); |
||||||
|
Assert.isTrue(j >= 0, "Unreachable block " + pred); |
||||||
|
|
||||||
|
blockDoms.and(dom[j]); |
||||||
|
} |
||||||
|
|
||||||
|
// Don't forget to account for the sink node if block is a
|
||||||
|
// leaf node. Appearantly, there are not edges between
|
||||||
|
// leaf nodes and the sink node!
|
||||||
|
preds = (Collection) snkPreds.get(block); |
||||||
|
|
||||||
|
if (preds != null) { |
||||||
|
e = preds.iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Block pred = (Block) e.next(); |
||||||
|
|
||||||
|
final int j = graph.preOrderIndex(pred); |
||||||
|
Assert.isTrue(j >= 0, "Unreachable block " + pred); |
||||||
|
|
||||||
|
blockDoms.and(dom[j]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Include yourself in your dominators?!
|
||||||
|
blockDoms.set(i); |
||||||
|
|
||||||
|
// print(graph, reverse, "intersecting " + preds, i, blockDoms);
|
||||||
|
|
||||||
|
// If the set changed, set the changed bit.
|
||||||
|
if (!blockDoms.equals(oldSet)) { |
||||||
|
changed = true; |
||||||
|
dom[i] = blockDoms; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Once we have the predacessor bit vectors all squared away, we can
|
||||||
|
// determine which vertices dominate which vertices.
|
||||||
|
|
||||||
|
Iterator blocks = graph.nodes().iterator(); |
||||||
|
|
||||||
|
// Initialize each block's (post)dominator parent and children
|
||||||
|
while (blocks.hasNext()) { |
||||||
|
final Block block = (Block) blocks.next(); |
||||||
|
if (!reverse) { |
||||||
|
block.setDomParent(null); |
||||||
|
block.domChildren().clear(); |
||||||
|
} else { |
||||||
|
block.setPdomParent(null); |
||||||
|
block.pdomChildren().clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
blocks = graph.nodes().iterator(); |
||||||
|
|
||||||
|
// A block's immediate dominator is its closest dominator. So, we
|
||||||
|
// start with the dominators, dom(b), of a block, b. To find the
|
||||||
|
// imediate dominator of b, we remove all blocks from dom(b) that
|
||||||
|
// dominate any block in dom(b).
|
||||||
|
|
||||||
|
while (blocks.hasNext()) { |
||||||
|
final Block block = (Block) blocks.next(); |
||||||
|
|
||||||
|
final int i = graph.preOrderIndex(block); |
||||||
|
|
||||||
|
Assert.isTrue((0 <= i) && (i < size), "Unreachable block " + block); |
||||||
|
|
||||||
|
if (i == root) { |
||||||
|
if (!reverse) { |
||||||
|
block.setDomParent(null); |
||||||
|
} else { |
||||||
|
block.setPdomParent(null); |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
// Find the immediate dominator
|
||||||
|
// idom := dom(block) - dom(dom(block)) - block
|
||||||
|
final BitSet blockDoms = dom[i]; |
||||||
|
|
||||||
|
// print(graph, reverse, "dom set", i, blockDoms);
|
||||||
|
|
||||||
|
final BitSet idom = new BitSet(size); |
||||||
|
idom.or(blockDoms); |
||||||
|
idom.clear(i); |
||||||
|
|
||||||
|
for (int j = 0; j < size; j++) { |
||||||
|
if ((i != j) && blockDoms.get(j)) { |
||||||
|
final BitSet domDomBlocks = dom[j]; |
||||||
|
|
||||||
|
// idom = idom - (domDomBlocks - {j})
|
||||||
|
final BitSet b = new BitSet(size); |
||||||
|
b.or(domDomBlocks); |
||||||
|
b.xor(ALL); |
||||||
|
b.set(j); |
||||||
|
idom.and(b); |
||||||
|
|
||||||
|
// print(graph, reverse,
|
||||||
|
// "removing dom(" + graph.preOrder().get(j) +")",
|
||||||
|
// i, idom);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Block parent = null; |
||||||
|
|
||||||
|
// A block should only have one immediate dominator.
|
||||||
|
for (int j = 0; j < size; j++) { |
||||||
|
if (idom.get(j)) { |
||||||
|
final Block p = (Block) graph.preOrder().get(j); |
||||||
|
|
||||||
|
Assert.isTrue(parent == null, block |
||||||
|
+ " has more than one immediate dominator: " |
||||||
|
+ parent + " and " + p); |
||||||
|
|
||||||
|
parent = p; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Assert.isTrue(parent != null, block + " has 0 immediate " |
||||||
|
+ (reverse ? "postdominators" : "dominators")); |
||||||
|
|
||||||
|
if (!reverse) { |
||||||
|
if (DominatorTree.DEBUG) { |
||||||
|
System.out.println(parent + " dominates " + block); |
||||||
|
} |
||||||
|
|
||||||
|
block.setDomParent(parent); |
||||||
|
|
||||||
|
} else { |
||||||
|
if (DominatorTree.DEBUG) { |
||||||
|
System.out.println(parent + " postdominates " + block); |
||||||
|
} |
||||||
|
|
||||||
|
block.setPdomParent(parent); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Determines which nodes are predacessors of a cfg's sink node. Creates a |
||||||
|
* Map that maps the sink node to its predacessors (or the leaf nodes to the |
||||||
|
* sink node, their predacessor, if we're going backwards). |
||||||
|
* |
||||||
|
* @param graph |
||||||
|
* The cfg to operate on. |
||||||
|
* @param preds |
||||||
|
* A mapping from leaf nodes to their predacessors. The exact |
||||||
|
* semantics depend on whether or not we are going forwards. |
||||||
|
* @param reverse |
||||||
|
* Are we computing the dominators or postdominators? |
||||||
|
*/ |
||||||
|
private static void insertEdgesToSink(final FlowGraph graph, |
||||||
|
final Map preds, final boolean reverse) { |
||||||
|
final BitSet visited = new BitSet(); // see insertEdgesToSinkDFS
|
||||||
|
final BitSet returned = new BitSet(); |
||||||
|
|
||||||
|
visited.set(graph.preOrderIndex(graph.source())); |
||||||
|
|
||||||
|
DominatorTree.insertEdgesToSinkDFS(graph, graph.source(), visited, |
||||||
|
returned, preds, reverse); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method determines which nodes are the predacessor of the sink node |
||||||
|
* of a cfg. A depth-first traversal of the cfg is performed. When a leaf |
||||||
|
* node (that is not the sink node) is encountered, add an entry to the |
||||||
|
* preds Map. |
||||||
|
* |
||||||
|
* @param graph |
||||||
|
* The cfg being operated on. |
||||||
|
* @param block |
||||||
|
* The basic Block to start at. |
||||||
|
* @param visited |
||||||
|
* Vertices that were visited |
||||||
|
* @param returned |
||||||
|
* Vertices that returned |
||||||
|
* @param preds |
||||||
|
* Maps a node to a HashSet representing its predacessors. In the |
||||||
|
* case that we're determining the dominace tree, preds maps the |
||||||
|
* sink node to its predacessors. In the case that we're |
||||||
|
* determining the postdominance tree, preds maps the sink node's |
||||||
|
* predacessors to the sink node. |
||||||
|
* @param reverse |
||||||
|
* Do we go in reverse? |
||||||
|
*/ |
||||||
|
private static void insertEdgesToSinkDFS(final FlowGraph graph, |
||||||
|
final Block block, final BitSet visited, final BitSet returned, |
||||||
|
final Map preds, boolean reverse) { |
||||||
|
boolean leaf = true; // Is a vertex a leaf node?
|
||||||
|
|
||||||
|
// Get the successors of block
|
||||||
|
final Iterator e = graph.succs(block).iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Block succ = (Block) e.next(); |
||||||
|
|
||||||
|
// Determine index of succ vertex in a pre-order traversal
|
||||||
|
final int index = graph.preOrderIndex(succ); |
||||||
|
Assert.isTrue(index >= 0, "Unreachable block " + succ); |
||||||
|
|
||||||
|
if (!visited.get(index)) { |
||||||
|
// If the successor block hasn't been visited, visit it
|
||||||
|
visited.set(index); |
||||||
|
DominatorTree.insertEdgesToSinkDFS(graph, succ, visited, |
||||||
|
returned, preds, reverse); |
||||||
|
returned.set(index); |
||||||
|
leaf = false; |
||||||
|
|
||||||
|
} else if (returned.get(index)) { |
||||||
|
// Already visited and returned, so a descendent of succ
|
||||||
|
// has an edge to the sink.
|
||||||
|
leaf = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (leaf && (block != graph.sink())) { |
||||||
|
// If we're dealing with a leaf node that is not the sink, set
|
||||||
|
// up its predacessor set.
|
||||||
|
|
||||||
|
if (!reverse) { |
||||||
|
// If we're going forwards (computing dominators), get the
|
||||||
|
// predacessor vertices from the sink
|
||||||
|
Set p = (Set) preds.get(graph.sink()); |
||||||
|
|
||||||
|
// If there are no (known) predacessors, make a new HashSet to
|
||||||
|
// store them and register it in the pred Map.
|
||||||
|
if (p == null) { |
||||||
|
p = new HashSet(); |
||||||
|
preds.put(graph.sink(), p); |
||||||
|
} |
||||||
|
|
||||||
|
// The block is in the predacessors of the sink
|
||||||
|
p.add(block); |
||||||
|
|
||||||
|
} else { |
||||||
|
// If we're going backwards, get the block's predacessors
|
||||||
|
Set p = (Set) preds.get(block); |
||||||
|
|
||||||
|
if (p == null) { |
||||||
|
p = new HashSet(); |
||||||
|
preds.put(block, p); |
||||||
|
} |
||||||
|
|
||||||
|
// Add the sink vertex to the predacessors of the block
|
||||||
|
p.add(graph.sink()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,79 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.cfg; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.editor.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* <tt>Handler</tt> represents a try-catch block. It containes a set of |
||||||
|
* protected <tt>Block</tt>s (the "try" blocks), a catch <tt>Block</tt>, |
||||||
|
* and the <tt>Type</tt> of exception that is caught by the catch block. |
||||||
|
* |
||||||
|
* @see Block |
||||||
|
* @see EDU.purdue.cs.bloat.reflect.Catch |
||||||
|
* @see EDU.purdue.cs.bloat.editor.TryCatch |
||||||
|
*/ |
||||||
|
public class Handler { |
||||||
|
Set protectedBlocks; |
||||||
|
|
||||||
|
Block catchBlock; |
||||||
|
|
||||||
|
Type type; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param catchBlock |
||||||
|
* The block of code that handles an exception |
||||||
|
* @param type |
||||||
|
* The type of exception that is thrown |
||||||
|
*/ |
||||||
|
public Handler(final Block catchBlock, final Type type) { |
||||||
|
this.protectedBlocks = new HashSet(); |
||||||
|
this.catchBlock = catchBlock; |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a <tt>Collection</tt> of the "try" blocks. |
||||||
|
*/ |
||||||
|
public Collection protectedBlocks() { |
||||||
|
return protectedBlocks; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCatchBlock(final Block block) { |
||||||
|
catchBlock = block; |
||||||
|
} |
||||||
|
|
||||||
|
public Block catchBlock() { |
||||||
|
return catchBlock; |
||||||
|
} |
||||||
|
|
||||||
|
public Type catchType() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return "try -> catch (" + type + ") " + catchBlock; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
# All files in the distribution of BLOAT (Bytecode Level Optimization and
|
||||||
|
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
||||||
|
# Research Foundation of Purdue University. All rights reserved.
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
CLASS = \
|
||||||
|
Block.class\
|
||||||
|
DominanceFrontier.class\
|
||||||
|
DominatorTree.class\
|
||||||
|
FlowGraph.class\
|
||||||
|
ReplaceTarget.class\
|
||||||
|
Subroutine.class\
|
||||||
|
VerifyCFG.class
|
||||||
|
|
||||||
|
include ../class.mk |
@ -0,0 +1,167 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.cfg; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.tree.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* <tt>ReplaceTarget</tt> replaces the block that is the target of a |
||||||
|
* <tt>JumpStmt</tt>, <tt>JsrStmt</tt>, <tt>RetStmt</tt>, |
||||||
|
* <tt>GotoStmt</tt>, <tt>SwitchStmt</tt>, or <tt>IfStmt</tt> with |
||||||
|
* another <tt>Block</tt>. |
||||||
|
*/ |
||||||
|
public class ReplaceTarget extends TreeVisitor { |
||||||
|
Block oldDst; |
||||||
|
|
||||||
|
Block newDst; |
||||||
|
|
||||||
|
public ReplaceTarget(final Block oldDst, final Block newDst) { |
||||||
|
this.oldDst = oldDst; |
||||||
|
this.newDst = newDst; |
||||||
|
} |
||||||
|
|
||||||
|
public void visitTree(final Tree tree) { |
||||||
|
final Stmt last = tree.lastStmt(); |
||||||
|
|
||||||
|
if (last instanceof JumpStmt) { |
||||||
|
final JumpStmt stmt = (JumpStmt) last; |
||||||
|
|
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println(" Replacing " + oldDst + " with " + newDst |
||||||
|
+ " in " + stmt); |
||||||
|
} |
||||||
|
|
||||||
|
if (stmt.catchTargets().remove(oldDst)) { |
||||||
|
stmt.catchTargets().add(newDst); |
||||||
|
} |
||||||
|
|
||||||
|
stmt.visit(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitJsrStmt(final JsrStmt stmt) { |
||||||
|
if (stmt.sub().entry() == oldDst) { |
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.print(" replacing " + stmt); |
||||||
|
} |
||||||
|
|
||||||
|
stmt.block().graph().setSubEntry(stmt.sub(), newDst); |
||||||
|
|
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println(" with " + stmt); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitRetStmt(final RetStmt stmt) { |
||||||
|
final Iterator paths = stmt.sub().paths().iterator(); |
||||||
|
|
||||||
|
while (paths.hasNext()) { |
||||||
|
final Block[] path = (Block[]) paths.next(); |
||||||
|
|
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println(" path = " + path[0] + " " + path[1]); |
||||||
|
} |
||||||
|
|
||||||
|
if (path[1] == oldDst) { |
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println(" replacing ret to " + oldDst |
||||||
|
+ " with ret to " + newDst); |
||||||
|
} |
||||||
|
|
||||||
|
path[1] = newDst; |
||||||
|
((JsrStmt) path[0].tree().lastStmt()).setFollow(newDst); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitGotoStmt(final GotoStmt stmt) { |
||||||
|
if (stmt.target() == oldDst) { |
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.print(" replacing " + stmt); |
||||||
|
} |
||||||
|
|
||||||
|
stmt.setTarget(newDst); |
||||||
|
|
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println(" with " + stmt); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitSwitchStmt(final SwitchStmt stmt) { |
||||||
|
if (stmt.defaultTarget() == oldDst) { |
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.print(" replacing " + stmt); |
||||||
|
} |
||||||
|
|
||||||
|
stmt.setDefaultTarget(newDst); |
||||||
|
|
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println(" with " + stmt); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final Block[] targets = stmt.targets(); |
||||||
|
|
||||||
|
for (int i = 0; i < targets.length; i++) { |
||||||
|
if (targets[i] == oldDst) { |
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.print(" replacing " + stmt); |
||||||
|
} |
||||||
|
|
||||||
|
targets[i] = newDst; |
||||||
|
|
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println(" with " + stmt); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitIfStmt(final IfStmt stmt) { |
||||||
|
if (stmt.trueTarget() == oldDst) { |
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.print(" replacing " + stmt); |
||||||
|
} |
||||||
|
|
||||||
|
stmt.setTrueTarget(newDst); |
||||||
|
|
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println(" with " + stmt); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (stmt.falseTarget() == oldDst) { |
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.print(" replacing " + stmt); |
||||||
|
} |
||||||
|
|
||||||
|
stmt.setFalseTarget(newDst); |
||||||
|
|
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println(" with " + stmt); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,259 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.cfg; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.editor.*; |
||||||
|
import EDU.purdue.cs.bloat.tree.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Subroutine represents a subroutine (target of a <i>jsr</i> instruction) in |
||||||
|
* java bytecode. Subroutines are used to implement the finally part of a |
||||||
|
* try-catch-finally block. |
||||||
|
* <p> |
||||||
|
* Each Subroutine belongs in a control flow graph, has an entry and exit block, |
||||||
|
* and has a local variable that contains its return address. Additionally, it |
||||||
|
* maintains a list of paths from blocks in which the subroutine is called to |
||||||
|
* block that is executed after the subroutine returns. |
||||||
|
* <p> |
||||||
|
* Note that it is assumed that each subroutine ends with a <i>ret</i>. While |
||||||
|
* this is true for bytecode generated by javac, it is not required. |
||||||
|
* |
||||||
|
* @see AddressStoreStmt |
||||||
|
* @see Block |
||||||
|
*/ |
||||||
|
|
||||||
|
// Important: I assume there is a ret statement for each jsr.
|
||||||
|
// This is true for javac code, but not in general.
|
||||||
|
public class Subroutine { |
||||||
|
FlowGraph graph; // CFG containing this Subroutine
|
||||||
|
|
||||||
|
Block entry; // Basic Block at beginning of code
|
||||||
|
|
||||||
|
Block exit; // Basic Block ending code
|
||||||
|
|
||||||
|
ArrayList paths; |
||||||
|
|
||||||
|
LocalVariable returnAddress; // This Subroutine's return address
|
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param graph |
||||||
|
* The CFG containing the block. |
||||||
|
*/ |
||||||
|
public Subroutine(final FlowGraph graph) { |
||||||
|
this.graph = graph; |
||||||
|
this.entry = null; |
||||||
|
this.exit = null; |
||||||
|
this.paths = new ArrayList(); |
||||||
|
this.returnAddress = null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the local variable containing the return address of this |
||||||
|
* subroutine. |
||||||
|
*/ |
||||||
|
public LocalVariable returnAddress() { |
||||||
|
return returnAddress; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the address (stored in a LocalVariable) to which this subroutine |
||||||
|
* will return once it is finished. |
||||||
|
* |
||||||
|
* @param returnAddress |
||||||
|
* Local variable that stores the address to which the subroutine |
||||||
|
* returns when it is completed. |
||||||
|
* |
||||||
|
* @see Tree#visit_astore |
||||||
|
*/ |
||||||
|
public void setReturnAddress(final LocalVariable returnAddress) { |
||||||
|
this.returnAddress = returnAddress; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the number of places that this subroutine is called. |
||||||
|
*/ |
||||||
|
public int numPaths() { |
||||||
|
return paths.size(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the paths (a Collection of two-element arrays of Blocks) that |
||||||
|
* represent the Blocks that end in a call to this subroutine and the block |
||||||
|
* that begin with the return address from this subroutine. |
||||||
|
*/ |
||||||
|
public Collection paths() { |
||||||
|
return paths; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the CFG that contains this subroutine. |
||||||
|
*/ |
||||||
|
public FlowGraph graph() { |
||||||
|
return graph; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes all paths involving block regardless of whether it is a calling |
||||||
|
* (source) block or a returning (target) block. |
||||||
|
*/ |
||||||
|
public void removePathsContaining(final Block block) { |
||||||
|
for (int i = paths.size() - 1; i >= 0; i--) { |
||||||
|
final Block[] path = (Block[]) paths.get(i); |
||||||
|
if ((path[0] == block) || (path[1] == block)) { |
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println("removing path " + path[0] + " -> " |
||||||
|
+ path[1]); |
||||||
|
} |
||||||
|
paths.remove(i); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes a path between a caller Block and a return Block. |
||||||
|
*/ |
||||||
|
public void removePath(final Block callerBlock, final Block returnBlock) { |
||||||
|
for (int i = 0; i < paths.size(); i++) { |
||||||
|
final Block[] path = (Block[]) paths.get(i); |
||||||
|
if ((path[0] == callerBlock) && (path[1] == returnBlock)) { |
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println("removing path " + path[0] + " -> " |
||||||
|
+ path[1]); |
||||||
|
} |
||||||
|
paths.remove(i); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes all caller-return paths. |
||||||
|
*/ |
||||||
|
public void removeAllPaths() { |
||||||
|
paths = new ArrayList(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds a path from the block before a Subroutine is called to a block after |
||||||
|
* the subroutine is called. If the callerBlock is already associated with a |
||||||
|
* returnBlock, the old returnBlock is replaced. |
||||||
|
* |
||||||
|
* @param callerBlock |
||||||
|
* The block in which the subroutine is called. This Block ends |
||||||
|
* with a <i>jsr</i> to this subroutine. |
||||||
|
* @param returnBlock |
||||||
|
* The block to which the subroutine returns. This Block begins |
||||||
|
* at the return address of this subroutine. |
||||||
|
*/ |
||||||
|
public void addPath(final Block callerBlock, final Block returnBlock) { |
||||||
|
for (int i = 0; i < paths.size(); i++) { |
||||||
|
final Block[] path = (Block[]) paths.get(i); |
||||||
|
if (path[0] == callerBlock) { |
||||||
|
path[1] = returnBlock; |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
paths.add(new Block[] { callerBlock, returnBlock }); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the "return block" for a given "caller block". |
||||||
|
*/ |
||||||
|
public Block pathTarget(final Block block) { |
||||||
|
for (int i = 0; i < paths.size(); i++) { |
||||||
|
final Block[] path = (Block[]) paths.get(i); |
||||||
|
if (path[0] == block) { |
||||||
|
return path[1]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the "caller block" for a given "return block". |
||||||
|
*/ |
||||||
|
public Block pathSource(final Block block) { |
||||||
|
for (int i = 0; i < paths.size(); i++) { |
||||||
|
final Block[] path = (Block[]) paths.get(i); |
||||||
|
if (path[1] == block) { |
||||||
|
return path[0]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the entry Block for this Subroutine. |
||||||
|
*/ |
||||||
|
public void setEntry(final Block entry) { |
||||||
|
this.entry = entry; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the exit Block for this Subroutine. |
||||||
|
*/ |
||||||
|
public void setExit(final Block exit) { |
||||||
|
this.exit = exit; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the first Block in the subroutine. |
||||||
|
*/ |
||||||
|
public Block entry() { |
||||||
|
return entry; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the last Block in the subroutine. |
||||||
|
*/ |
||||||
|
public Block exit() { |
||||||
|
return exit; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Prints a textual representation of this Subroutine. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The PrintStream to which to print. |
||||||
|
*/ |
||||||
|
public void print(final PrintStream out) { |
||||||
|
out.println(" " + entry); |
||||||
|
|
||||||
|
final Iterator e = paths().iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Block[] path = (Block[]) e.next(); |
||||||
|
out.println(" path: " + path[0] + " -> " + path[1]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return "sub " + entry; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,384 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.cfg; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.tree.*; |
||||||
|
import EDU.purdue.cs.bloat.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* VerifyCFG visits the nodes in a control flow graph and verifies that certain |
||||||
|
* properties of the graph are true. For instance, value numbers of expressions |
||||||
|
* are not equal to -1, node connections are consistent, exception handlers are |
||||||
|
* set up correctly, etc. Mostly used for debugging purposes. |
||||||
|
*/ |
||||||
|
public class VerifyCFG extends TreeVisitor { |
||||||
|
Block block; // The Block containing the node being visited
|
||||||
|
|
||||||
|
Node parent; // The (expected) parent of the node being visited
|
||||||
|
|
||||||
|
FlowGraph cfg; // The CFG being visited
|
||||||
|
|
||||||
|
Set uses; // Expressions in which a definition is used
|
||||||
|
|
||||||
|
Set nodes; // (Visited) nodes in the CFG
|
||||||
|
|
||||||
|
boolean checkValueNumbers; // Do we check the value numbers of expressions?
|
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Don't check value numbers. |
||||||
|
*/ |
||||||
|
public VerifyCFG() { |
||||||
|
this(false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Since value numbers are not strictly part of the control |
||||||
|
* flow graph, they may or may not be checked. For instance, if a CFG is |
||||||
|
* being verfied before value numbers are assigned, we would not want to |
||||||
|
* check them. |
||||||
|
* |
||||||
|
* @param checkValueNumbers |
||||||
|
* Are the value numbers of expressions checked? |
||||||
|
*/ |
||||||
|
public VerifyCFG(final boolean checkValueNumbers) { |
||||||
|
this.checkValueNumbers = checkValueNumbers; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Visit the blocks and expression trees in a control flow graph. Examine |
||||||
|
* the uses of a variable that is defined in the CFG. Make that all uses are |
||||||
|
* reachable (i.e. are in the CFG). |
||||||
|
*/ |
||||||
|
public void visitFlowGraph(final FlowGraph cfg) { |
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println("Verifying CFG for " + cfg.method()); |
||||||
|
} |
||||||
|
|
||||||
|
this.cfg = cfg; |
||||||
|
|
||||||
|
uses = new HashSet(); |
||||||
|
nodes = new HashSet(); |
||||||
|
|
||||||
|
cfg.visitChildren(this); |
||||||
|
|
||||||
|
final Iterator e = uses.iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Expr use = (Expr) e.next(); |
||||||
|
Assert.isTrue(nodes.contains(use), "use = " + use + " (" |
||||||
|
+ System.identityHashCode(use) + ") is not in the CFG"); |
||||||
|
} |
||||||
|
|
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println("Verification successful"); |
||||||
|
} |
||||||
|
|
||||||
|
this.cfg = null; |
||||||
|
uses = null; |
||||||
|
nodes = null; |
||||||
|
block = null; |
||||||
|
parent = null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* First make sure that the <tt>Block</tt> indeed is in the CFG. If the |
||||||
|
* block begins an exception handler, then make sure that all edges from |
||||||
|
* protected blocks lead to the handler block. Also make sure that all of |
||||||
|
* the handler block's predacessor lead to protected blocks. Finally, make |
||||||
|
* sure that the successor/predacessor relationship holds. |
||||||
|
*/ |
||||||
|
public void visitBlock(final Block block) { |
||||||
|
Assert.isTrue(block.graph() == cfg, block + " is not in the CFG"); |
||||||
|
|
||||||
|
Iterator e; |
||||||
|
|
||||||
|
final Handler handler = (Handler) cfg.handlersMap().get(block); |
||||||
|
|
||||||
|
// If this block is the first block in an exception handler, make
|
||||||
|
// sure that the only predacessor edges the block has are
|
||||||
|
// protected blocks that may throw the exception handled by the
|
||||||
|
// block. Additionally, we check that there are edges from all
|
||||||
|
// protected blocks to the handler block.
|
||||||
|
//
|
||||||
|
// The predacessor to the first block in an exception handler may
|
||||||
|
// be the init block. However, it is not really a protected
|
||||||
|
// block, so just overlook it.
|
||||||
|
|
||||||
|
if (handler != null) { |
||||||
|
final HashSet handlerPreds = new HashSet(); |
||||||
|
|
||||||
|
e = handler.protectedBlocks().iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Block prot = (Block) e.next(); |
||||||
|
handlerPreds.add(prot); |
||||||
|
handlerPreds.addAll(cfg.preds(prot)); |
||||||
|
} |
||||||
|
|
||||||
|
final HashSet extra = new HashSet(cfg.preds(block)); |
||||||
|
extra.removeAll(handlerPreds); |
||||||
|
final HashSet missing = new HashSet(handlerPreds); |
||||||
|
missing.removeAll(cfg.preds(block)); |
||||||
|
|
||||||
|
Assert.isTrue(((extra.size() == 0) && (missing.size() == 0)) |
||||||
|
|| ((missing.size() == 1) && missing.contains(cfg.init())), |
||||||
|
"Handler prots = " + handlerPreds |
||||||
|
+ " != handler block preds = " + cfg.preds(block) |
||||||
|
+ " extra = " + extra + " missing = " + missing); |
||||||
|
} |
||||||
|
|
||||||
|
// Make sure that the predacessor has a successor and vice versa.
|
||||||
|
e = cfg.preds(block).iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Block pred = (Block) e.next(); |
||||||
|
Assert.isTrue(cfg.succs(pred).contains(block), pred |
||||||
|
+ " has no succ " + block); |
||||||
|
Assert.isTrue(cfg.preds(block).contains(pred), block |
||||||
|
+ " has no pred " + pred); |
||||||
|
} |
||||||
|
|
||||||
|
e = cfg.succs(block).iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Block succ = (Block) e.next(); |
||||||
|
Assert.isTrue(cfg.succs(block).contains(succ), block |
||||||
|
+ " has no succ " + succ); |
||||||
|
Assert.isTrue(cfg.preds(succ).contains(block), succ |
||||||
|
+ " has no pred " + block); |
||||||
|
} |
||||||
|
|
||||||
|
this.block = block; |
||||||
|
|
||||||
|
parent = null; |
||||||
|
block.visitChildren(this); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Make sure that all of targets of the <tt>ret</tt> are valid. The |
||||||
|
* targets are the blocks to which the subroutine can return. |
||||||
|
*/ |
||||||
|
public void visitRetStmt(final RetStmt stmt) { |
||||||
|
final Set targets = new HashSet(); |
||||||
|
|
||||||
|
final Iterator iter = stmt.sub().paths().iterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final Block[] path = (Block[]) iter.next(); |
||||||
|
targets.add(path[1]); |
||||||
|
} |
||||||
|
|
||||||
|
targets.addAll(stmt.catchTargets()); |
||||||
|
|
||||||
|
verifyTargets(stmt.block(), targets); |
||||||
|
|
||||||
|
visitNode(stmt); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Make sure that all of the targets of the <tt>jsr</tt> are valid. The |
||||||
|
* only target is the entry block of the subroutine. |
||||||
|
*/ |
||||||
|
public void visitJsrStmt(final JsrStmt stmt) { |
||||||
|
final Set targets = new HashSet(); |
||||||
|
targets.add(stmt.sub().entry()); |
||||||
|
targets.addAll(stmt.catchTargets()); |
||||||
|
verifyTargets(stmt.block(), targets); |
||||||
|
|
||||||
|
visitNode(stmt); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Make sure that that all of the targets of the switch are valid. |
||||||
|
*/ |
||||||
|
public void visitSwitchStmt(final SwitchStmt stmt) { |
||||||
|
final Set targets = new HashSet(); |
||||||
|
|
||||||
|
targets.add(stmt.defaultTarget()); |
||||||
|
|
||||||
|
for (int i = 0; i < stmt.targets().length; i++) { |
||||||
|
targets.add(stmt.targets()[i]); |
||||||
|
} |
||||||
|
|
||||||
|
targets.addAll(stmt.catchTargets()); |
||||||
|
|
||||||
|
verifyTargets(stmt.block(), targets); |
||||||
|
|
||||||
|
visitNode(stmt); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Make sure that the targets of the if statement are valid. Targets consist |
||||||
|
* of the true target, the false target, and the first blocks of any |
||||||
|
* exceptions that may be thrown by the if statement. |
||||||
|
*/ |
||||||
|
public void visitIfStmt(final IfStmt stmt) { |
||||||
|
final Set targets = new HashSet(); |
||||||
|
targets.add(stmt.trueTarget()); |
||||||
|
targets.add(stmt.falseTarget()); |
||||||
|
targets.addAll(stmt.catchTargets()); |
||||||
|
verifyTargets(stmt.block(), targets); |
||||||
|
|
||||||
|
visitNode(stmt); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Make sure that the target of <tt>goto</tt> is valid. |
||||||
|
*/ |
||||||
|
public void visitGotoStmt(final GotoStmt stmt) { |
||||||
|
final Set targets = new HashSet(); |
||||||
|
targets.add(stmt.target()); |
||||||
|
targets.addAll(stmt.catchTargets()); |
||||||
|
verifyTargets(stmt.block(), targets); |
||||||
|
|
||||||
|
visitNode(stmt); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Verifies information about the targets of a jump in a given block. First, |
||||||
|
* make sure that the number of targets is the same as the number of |
||||||
|
* sucessor nodes to the block. Make sure that targets of all of the jumps |
||||||
|
* are indeed in the CFG. Make sure that every target is a successor of the |
||||||
|
* block. |
||||||
|
*/ |
||||||
|
private void verifyTargets(final Block block, final Set targets) { |
||||||
|
Assert.isTrue(targets.size() == cfg.succs(block).size(), block |
||||||
|
+ " has succs " + cfg.succs(block) + " != " + targets + " in " |
||||||
|
+ block.tree().lastStmt()); |
||||||
|
|
||||||
|
final Iterator iter = targets.iterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final Block target = (Block) iter.next(); |
||||||
|
Assert.isTrue(block.graph().hasNode(target), target |
||||||
|
+ " is not in the CFG"); |
||||||
|
Assert.isTrue(cfg.succs(block).contains(target), target |
||||||
|
+ " is not a succ of " + block + " " |
||||||
|
+ block.tree().lastStmt()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* If desired, makes sure that the store expression's value number is not |
||||||
|
* -1. Makes sure that the store expression's block and parent <tt>Node</tt> |
||||||
|
* are what we expect them to be. If the type of the <tt>StoreExpr</tt> is |
||||||
|
* void, then make sure that its parent is an <tt>ExprStmt</tt> (i.e. make |
||||||
|
* sure it is not nested within another expression). |
||||||
|
*/ |
||||||
|
public void visitStoreExpr(final StoreExpr node) { |
||||||
|
nodes.add(node); |
||||||
|
|
||||||
|
if (checkValueNumbers) { |
||||||
|
Assert.isTrue(node.valueNumber() != -1, node |
||||||
|
+ ".valueNumber() = -1"); |
||||||
|
} |
||||||
|
|
||||||
|
Assert.isTrue(node.block() == block, node + ".block() = " |
||||||
|
+ node.block() + " != block = " + block); |
||||||
|
Assert.isTrue(node.parent() == parent, node + ".parent() = " |
||||||
|
+ node.parent() + " != parent = " + parent); |
||||||
|
|
||||||
|
// Visit the MemExpr into which node stores.
|
||||||
|
parent = node; |
||||||
|
node.target().visit(this); |
||||||
|
|
||||||
|
// Visit the expression whose value is being stored by node
|
||||||
|
parent = node; |
||||||
|
node.expr().visit(this); |
||||||
|
|
||||||
|
parent = node.parent(); |
||||||
|
|
||||||
|
if (node.type().isVoid()) { |
||||||
|
Assert.isTrue(parent instanceof ExprStmt, "parent of " + node |
||||||
|
+ " = " + parent + " is not an ExprStmt"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Make sure that the <tt>Node</tt> resides in the block that we expect it |
||||||
|
* to and that it has the expected parent expression tree <tt>Node</tt>. |
||||||
|
* Make sure that the children of this <tt>Node</tt> are also correct. |
||||||
|
*/ |
||||||
|
public void visitNode(final Node node) { |
||||||
|
nodes.add(node); |
||||||
|
|
||||||
|
Assert.isTrue(node.block() == block, node + ".block() = " |
||||||
|
+ node.block() + " != block = " + block); |
||||||
|
Assert.isTrue(node.parent() == parent, node + ".parent() = " |
||||||
|
+ node.parent() + " != parent = " + parent); |
||||||
|
|
||||||
|
final ArrayList children = new ArrayList(); |
||||||
|
|
||||||
|
node.visitChildren(new TreeVisitor() { |
||||||
|
public void visitNode(final Node n) { |
||||||
|
children.add(n); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
final Iterator e = children.iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Node child = (Node) e.next(); |
||||||
|
parent = node; |
||||||
|
child.visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
parent = node.parent(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* If desired, make sure that the value number of the <tt>Expr</tt> is not |
||||||
|
* -1. |
||||||
|
*/ |
||||||
|
public void visitExpr(final Expr expr) { |
||||||
|
if (checkValueNumbers) { |
||||||
|
Assert.isTrue(expr.valueNumber() != -1, expr |
||||||
|
+ ".valueNumber() = -1"); |
||||||
|
} |
||||||
|
|
||||||
|
visitNode(expr); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Keep track of all the uses of the expression defined by the |
||||||
|
* <tt>DefExpr</tt>. This information is used when verifying the |
||||||
|
* <tt>FlowGraph</tt>. |
||||||
|
*/ |
||||||
|
public void visitDefExpr(final DefExpr expr) { |
||||||
|
uses.addAll(expr.uses()); |
||||||
|
visitExpr(expr); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Make sure that the <tt>VarExpr</tt> either defines a local variable, is |
||||||
|
* defined by another expression, or is the child of a <tt>PhiStmt</tt> |
||||||
|
* (therefore making the <tt>VarExpr</tt> a phi-variable). |
||||||
|
*/ |
||||||
|
public void visitVarExpr(final VarExpr expr) { |
||||||
|
Assert.isTrue(expr.isDef() || (expr.def() != null) |
||||||
|
|| (expr.parent() instanceof PhiStmt), "Null def for variable " |
||||||
|
+ expr); |
||||||
|
|
||||||
|
visitDefExpr(expr); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
<html> |
||||||
|
<body> |
||||||
|
|
||||||
|
<p>Classes to represent a Java method as a control flow graph of basic |
||||||
|
blocks. A <em>basic block</em> is a sequence of code that is entered |
||||||
|
only in once place (e.g. the target of a branch statement) and is |
||||||
|
exited at only one place (e.g. a branch statement). Each basic block |
||||||
|
consists of an expression tree. There are also classes to represent |
||||||
|
try-catch blocks, Java subroutines (<tt>finally</tt> blocks), certain |
||||||
|
properties of the control flow graph such as its dominator tree, and |
||||||
|
to visualize a control flow graph.</p> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,43 @@ |
|||||||
|
# All files in the distribution of BLOAT (Bytecode Level Optimization and
|
||||||
|
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
||||||
|
# Research Foundation of Purdue University. All rights reserved.
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
.SUFFIXES: .java .class |
||||||
|
|
||||||
|
JAVA_HOME = /gcm/where/jdk/1.3/sparc.Solaris
|
||||||
|
JAVAC = $(JAVA_HOME)/bin/javac
|
||||||
|
JFLAGS = -g
|
||||||
|
CLASSPATH = $(JAVA_HOME)/lib/classes.zip
|
||||||
|
|
||||||
|
all: class |
||||||
|
|
||||||
|
clean: |
||||||
|
rm -f *.class
|
||||||
|
|
||||||
|
class: |
||||||
|
@files=`$(MAKE) -n _class | grep $(JAVAC) | cut -d' ' -f4`; \
|
||||||
|
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
|
||||||
|
if [ "x$$files" != "x" ]; then \
|
||||||
|
echo $(JAVAC) $(JFLAGS) -classpath $$cpath $$files; \
|
||||||
|
$(JAVAC) $(JFLAGS) -classpath $$cpath $$files; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
_class: $(CLASS) |
||||||
|
|
||||||
|
.java.class: |
||||||
|
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
|
||||||
|
$(JAVAC) -classpath $$cpath $<
|
@ -0,0 +1 @@ |
|||||||
|
*.class |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,774 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.codegen; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.cfg.*; |
||||||
|
import EDU.purdue.cs.bloat.tree.*; |
||||||
|
import EDU.purdue.cs.bloat.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Liveness represents the interference graph of the local variables contained |
||||||
|
* in a control flow graph. |
||||||
|
* |
||||||
|
* When the liveness of two variables overlap each other, the two variables are |
||||||
|
* said to <i>interfere</i> with each other. The interference graph represents |
||||||
|
* this relationship between variables. There is an (un-directed) edge between |
||||||
|
* variables <tt>a</tt> and <tt>b</tt> in the interference graph if variable |
||||||
|
* <tt>a</tt> interferes with variable <tt>b</tt>. |
||||||
|
*/ |
||||||
|
public class Liveness { |
||||||
|
public static boolean DEBUG = false; |
||||||
|
|
||||||
|
public static boolean UNIQUE = false; |
||||||
|
|
||||||
|
public static final boolean BEFORE = false; |
||||||
|
|
||||||
|
public static final boolean AFTER = true; |
||||||
|
|
||||||
|
FlowGraph cfg; |
||||||
|
|
||||||
|
Graph ig; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param cfg |
||||||
|
* Control flow graph on which to perform liveness analysis. |
||||||
|
*/ |
||||||
|
public Liveness(final FlowGraph cfg) { |
||||||
|
this.cfg = cfg; |
||||||
|
computeIntersections(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes a local expression from the interference graph. |
||||||
|
*/ |
||||||
|
public void removeVar(final LocalExpr expr) { |
||||||
|
ig.removeNode(expr); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Should not be called. |
||||||
|
*/ |
||||||
|
public boolean liveAtUse(final VarExpr isLive, final VarExpr at, |
||||||
|
final boolean after) { |
||||||
|
throw new RuntimeException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Should not be called. |
||||||
|
*/ |
||||||
|
public boolean liveAtStartOfBlock(final VarExpr isLive, final Block block) { |
||||||
|
throw new RuntimeException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Should not be called. |
||||||
|
*/ |
||||||
|
public boolean liveAtEndOfBlock(final VarExpr isLive, final Block block) { |
||||||
|
throw new RuntimeException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the <tt>LocalExpr</tt>s (variables) that occur in the CFG. |
||||||
|
* They correspond to nodes in the interference graph. |
||||||
|
*/ |
||||||
|
public Collection defs() { |
||||||
|
return ig.keySet(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an <tt>Iterator</tt> of <tt>LocalExpr</tt>s that interfere |
||||||
|
* with a given <tt>VarExpr</tt>. |
||||||
|
*/ |
||||||
|
public Iterator intersections(final VarExpr a) { |
||||||
|
Assert.isTrue(a != null, "Cannot get intersections for null def"); |
||||||
|
Assert.isTrue(a.isDef(), "Cannot get intersections for variable uses"); |
||||||
|
|
||||||
|
final GraphNode node = ig.getNode(a); |
||||||
|
|
||||||
|
Assert.isTrue(node != null, "Cannot find IG node for " + a); |
||||||
|
|
||||||
|
return new Iterator() { |
||||||
|
Iterator succs = ig.succs(node).iterator(); |
||||||
|
|
||||||
|
public boolean hasNext() { |
||||||
|
return succs.hasNext(); |
||||||
|
} |
||||||
|
|
||||||
|
public Object next() { |
||||||
|
final IGNode next = (IGNode) succs.next(); |
||||||
|
return next.def; |
||||||
|
} |
||||||
|
|
||||||
|
public void remove() { |
||||||
|
throw new RuntimeException(); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Determines whether or not two variables interfere with one another. |
||||||
|
*/ |
||||||
|
public boolean liveRangesIntersect(final VarExpr a, final VarExpr b) { |
||||||
|
Assert.isTrue((a != null) && (b != null), |
||||||
|
"Cannot get intersections for null def"); |
||||||
|
Assert.isTrue(a.isDef() && b.isDef(), |
||||||
|
"Cannot get intersections for variable uses"); |
||||||
|
|
||||||
|
if (a == b) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// If all locals should have unique colors, return true.
|
||||||
|
if (Liveness.UNIQUE) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
final IGNode na = (IGNode) ig.getNode(a); |
||||||
|
final IGNode nb = (IGNode) ig.getNode(b); |
||||||
|
|
||||||
|
Assert.isTrue((na != null) && (nb != null)); |
||||||
|
|
||||||
|
return ig.hasEdge(na, nb); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs the interference graph. |
||||||
|
*/ |
||||||
|
private void computeIntersections() { |
||||||
|
ig = new Graph(); // The interference graph
|
||||||
|
|
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out.println("-----------Computing live ranges-----------"); |
||||||
|
} |
||||||
|
|
||||||
|
// All of the nodes (IGNodes) in the IG
|
||||||
|
final List defNodes = new ArrayList(); |
||||||
|
|
||||||
|
// The IGNodes whose local variable is defined by a PhiCatchStmt
|
||||||
|
final List phiCatchNodes = new ArrayList(); |
||||||
|
|
||||||
|
// An array of NodeInfo for each node in the CFG (indexed by the
|
||||||
|
// node's pre-order index). Gives information about the local
|
||||||
|
// variables (nodes in the IG) that are defined in each block.
|
||||||
|
// The NodeInfos are stored in reverse order. That is, the
|
||||||
|
// NodeInfo for the final variable occurrence in the block is the
|
||||||
|
// first element in the list.
|
||||||
|
final List[] nodes = new ArrayList[cfg.size()]; |
||||||
|
|
||||||
|
// We need to keep track of the order of the statements in which
|
||||||
|
// variables occur. There is an entry in nodeIndices for each
|
||||||
|
// block in the CFG. Each entry consists of a mapping between a
|
||||||
|
// statement in which a variable occurs and the number of the
|
||||||
|
// statement (with respect to the other statements in which
|
||||||
|
// variables occur) of interest. This is hard to explain in
|
||||||
|
// words. This numbering comes into play in the liveOut method.
|
||||||
|
final Map[] nodeIndices = new HashMap[cfg.size()]; |
||||||
|
|
||||||
|
Iterator iter = cfg.nodes().iterator(); |
||||||
|
|
||||||
|
// Initialize nodes and nodeIndices
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final Block block = (Block) iter.next(); |
||||||
|
final int blockIndex = cfg.preOrderIndex(block); |
||||||
|
nodes[blockIndex] = new ArrayList(); |
||||||
|
nodeIndices[blockIndex] = new HashMap(); |
||||||
|
} |
||||||
|
|
||||||
|
// Go in trace order. Code generation for phis in the presence of
|
||||||
|
// critical edges depends on it!
|
||||||
|
|
||||||
|
iter = cfg.trace().iterator(); |
||||||
|
|
||||||
|
// When performing liveness analysis, we traverse the tree from
|
||||||
|
// the bottom up. That is, we do a REVERSE traversal.
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final Block block = (Block) iter.next(); |
||||||
|
|
||||||
|
block.visit(new TreeVisitor(TreeVisitor.REVERSE) { |
||||||
|
public void visitPhiJoinStmt(final PhiJoinStmt stmt) { |
||||||
|
if (!(stmt.target() instanceof LocalExpr)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
final LocalExpr target = (LocalExpr) stmt.target(); |
||||||
|
|
||||||
|
// Examine each predacessor and maintain some information
|
||||||
|
// about the definitions. Remember that we're dealing with
|
||||||
|
// a PhiJoinStmt. The predacessors of PhiJoinStmts are
|
||||||
|
// statements that define or use the local (SSA) variable.
|
||||||
|
final Iterator preds = cfg.preds(block).iterator(); |
||||||
|
|
||||||
|
while (preds.hasNext()) { |
||||||
|
final Block pred = (Block) preds.next(); |
||||||
|
final int predIndex = cfg.preOrderIndex(pred); |
||||||
|
|
||||||
|
final List n = nodes[predIndex]; |
||||||
|
final Map indices = nodeIndices[predIndex]; |
||||||
|
|
||||||
|
indices.put(stmt, new Integer(n.size())); |
||||||
|
final NodeInfo info = new NodeInfo(stmt); |
||||||
|
n.add(info); |
||||||
|
|
||||||
|
// Make a new node in the interference graph for target,
|
||||||
|
// if one does not already exists
|
||||||
|
IGNode node = (IGNode) ig.getNode(target); |
||||||
|
|
||||||
|
if (node == null) { |
||||||
|
node = new IGNode(target); |
||||||
|
ig.addNode(target, node); |
||||||
|
defNodes.add(node); |
||||||
|
} |
||||||
|
|
||||||
|
info.defNodes.add(node); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitPhiCatchStmt(final PhiCatchStmt stmt) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visitStmt(final Stmt stmt) { |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
iter = cfg.trace().iterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final Block block = (Block) iter.next(); |
||||||
|
final int blockIndex = cfg.preOrderIndex(block); |
||||||
|
|
||||||
|
block.visit(new TreeVisitor(TreeVisitor.REVERSE) { |
||||||
|
Node parent = null; |
||||||
|
|
||||||
|
public void visitNode(final Node node) { |
||||||
|
final Node p = parent; |
||||||
|
parent = node; |
||||||
|
node.visitChildren(this); |
||||||
|
parent = p; |
||||||
|
} |
||||||
|
|
||||||
|
public void visitLocalExpr(final LocalExpr expr) { |
||||||
|
Assert.isTrue(parent != null); |
||||||
|
|
||||||
|
// Recall that a LocalExpr represents a use or a definition
|
||||||
|
// of a local variable. If the LocalExpr is defined by a
|
||||||
|
// PhiJoinStmt, the block in which it resides should already
|
||||||
|
// have some information about it.
|
||||||
|
|
||||||
|
NodeInfo info; |
||||||
|
|
||||||
|
final List n = nodes[blockIndex]; |
||||||
|
final Map indices = nodeIndices[blockIndex]; |
||||||
|
|
||||||
|
final Integer i = (Integer) indices.get(parent); |
||||||
|
|
||||||
|
if (i == null) { |
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out.println("adding " + parent + " at " |
||||||
|
+ n.size()); |
||||||
|
} |
||||||
|
|
||||||
|
indices.put(parent, new Integer(n.size())); |
||||||
|
info = new NodeInfo(parent); |
||||||
|
n.add(info); |
||||||
|
|
||||||
|
} else { |
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out.println("found " + parent + " at " + i); |
||||||
|
} |
||||||
|
|
||||||
|
info = (NodeInfo) n.get(i.intValue()); |
||||||
|
Assert.isTrue(info != null); |
||||||
|
} |
||||||
|
|
||||||
|
if (expr.isDef()) { |
||||||
|
IGNode node = (IGNode) ig.getNode(expr); |
||||||
|
|
||||||
|
if (node == null) { |
||||||
|
node = new IGNode(expr); |
||||||
|
ig.addNode(expr, node); |
||||||
|
defNodes.add(node); |
||||||
|
} |
||||||
|
|
||||||
|
info.defNodes.add(node); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitPhiCatchStmt(final PhiCatchStmt stmt) { |
||||||
|
NodeInfo info; |
||||||
|
|
||||||
|
final List n = nodes[blockIndex]; |
||||||
|
final Map indices = nodeIndices[blockIndex]; |
||||||
|
|
||||||
|
final Integer i = (Integer) indices.get(stmt); |
||||||
|
|
||||||
|
if (i == null) { |
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out.println("adding " + stmt + " at " |
||||||
|
+ n.size()); |
||||||
|
} |
||||||
|
|
||||||
|
indices.put(stmt, new Integer(n.size())); |
||||||
|
info = new NodeInfo(stmt); |
||||||
|
n.add(info); |
||||||
|
|
||||||
|
} else { |
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out.println("found " + parent + " at " + i); |
||||||
|
} |
||||||
|
|
||||||
|
info = (NodeInfo) n.get(i.intValue()); |
||||||
|
Assert.isTrue(info != null); |
||||||
|
} |
||||||
|
|
||||||
|
final LocalExpr target = (LocalExpr) stmt.target(); |
||||||
|
|
||||||
|
IGNode node = (IGNode) ig.getNode(target); |
||||||
|
|
||||||
|
if (node == null) { |
||||||
|
node = new IGNode(target); |
||||||
|
ig.addNode(target, node); |
||||||
|
defNodes.add(node); |
||||||
|
phiCatchNodes.add(node); |
||||||
|
} |
||||||
|
|
||||||
|
info.defNodes.add(node); |
||||||
|
} |
||||||
|
|
||||||
|
public void visitPhiJoinStmt(final PhiJoinStmt stmt) { |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
// Iterate over all of the nodes in the IG
|
||||||
|
final int numDefs = defNodes.size(); |
||||||
|
|
||||||
|
for (int i = 0; i < numDefs; i++) { |
||||||
|
final IGNode node = (IGNode) defNodes.get(i); |
||||||
|
final LocalExpr def = node.def; |
||||||
|
|
||||||
|
// Set of blocks where this variable is live out (i.e. live on
|
||||||
|
// any of the block's outgoing edges).
|
||||||
|
final BitSet m = new BitSet(cfg.size()); |
||||||
|
|
||||||
|
final Iterator uses = def.uses().iterator(); |
||||||
|
|
||||||
|
// Look at each use of the local variable
|
||||||
|
while (uses.hasNext()) { |
||||||
|
final LocalExpr use = (LocalExpr) uses.next(); |
||||||
|
Node parent = use.parent(); |
||||||
|
|
||||||
|
if ((parent instanceof MemRefExpr) |
||||||
|
&& ((MemRefExpr) parent).isDef()) { |
||||||
|
parent = parent.parent(); |
||||||
|
} |
||||||
|
|
||||||
|
// Skip catch-phis. We handle this later.
|
||||||
|
if (parent instanceof PhiCatchStmt) { |
||||||
|
// If we want to be less conservative:
|
||||||
|
// Need to search back from the operand from all
|
||||||
|
// points in the protected region where it is live
|
||||||
|
// back to the def of the operand. For each block
|
||||||
|
// in protected region, if the operand def is closest
|
||||||
|
// dominator of the block
|
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out.println("searching for " + def + " from " |
||||||
|
+ parent); |
||||||
|
} |
||||||
|
|
||||||
|
final Block block = parent.block(); |
||||||
|
|
||||||
|
if (parent instanceof PhiJoinStmt) { |
||||||
|
final PhiJoinStmt phi = (PhiJoinStmt) parent; |
||||||
|
|
||||||
|
// The local variable (LocalExpr) occurs within a
|
||||||
|
// PhiJoinStmt. Look at the predacessors of the
|
||||||
|
// PhiJoinStmt. Recall that each predacessor defines one of
|
||||||
|
// the operands to the PhiJoinStmt. Locate the block that
|
||||||
|
// defines the LocalExpr in question. Call liveOut to
|
||||||
|
// determine for which nodes the LocalExpr is live out.
|
||||||
|
|
||||||
|
// Examine the predacessors of the block containing the
|
||||||
|
// LocalExpr
|
||||||
|
final Iterator preds = cfg.preds(block).iterator(); |
||||||
|
|
||||||
|
while (preds.hasNext()) { |
||||||
|
final Block pred = (Block) preds.next(); |
||||||
|
|
||||||
|
if (phi.operandAt(pred) == use) { |
||||||
|
final Map indices = nodeIndices[cfg |
||||||
|
.preOrderIndex(pred)]; |
||||||
|
final Integer index = (Integer) indices.get(parent); |
||||||
|
Assert.isTrue(index != null, "No index for " |
||||||
|
+ parent); |
||||||
|
|
||||||
|
liveOut(m, nodes, pred, index.intValue(), node, |
||||||
|
phiCatchNodes); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
// The LocalExpr is define in a non-Phi statement. Figure
|
||||||
|
// out which number definition define the LocalExpr in quest
|
||||||
|
// and call liveOut to compute the set of block in which the
|
||||||
|
// LocalExpr is live out.
|
||||||
|
|
||||||
|
final Map indices = nodeIndices[cfg.preOrderIndex(block)]; |
||||||
|
final Integer index = (Integer) indices.get(parent); |
||||||
|
Assert.isTrue(index != null, "No index for " + parent); |
||||||
|
|
||||||
|
liveOut(m, nodes, block, index.intValue(), node, |
||||||
|
phiCatchNodes); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Go through all of the variables that are defined by
|
||||||
|
// PhiCatchStmts and make them (the variables) conflict with
|
||||||
|
// everything that the operands of the PhiCatchStmt conflict
|
||||||
|
// with. See liveOut for a discussion.
|
||||||
|
|
||||||
|
final int numPhiCatches = phiCatchNodes.size(); |
||||||
|
|
||||||
|
for (int i = 0; i < numPhiCatches; i++) { |
||||||
|
final IGNode node = (IGNode) phiCatchNodes.get(i); |
||||||
|
|
||||||
|
final PhiCatchStmt phi = (PhiCatchStmt) node.def.parent(); |
||||||
|
|
||||||
|
final Iterator operands = phi.operands().iterator(); |
||||||
|
|
||||||
|
while (operands.hasNext()) { |
||||||
|
final LocalExpr operand = (LocalExpr) operands.next(); |
||||||
|
final LocalExpr def = (LocalExpr) operand.def(); |
||||||
|
|
||||||
|
if (def != null) { |
||||||
|
final IGNode opNode = (IGNode) ig.getNode(def); |
||||||
|
|
||||||
|
// Conflict with everything the operand conflicts with.
|
||||||
|
final Iterator edges = new ImmutableIterator(ig |
||||||
|
.succs(opNode)); |
||||||
|
|
||||||
|
while (edges.hasNext()) { |
||||||
|
final IGNode otherNode = (IGNode) edges.next(); |
||||||
|
|
||||||
|
if (otherNode != node) { |
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out.println(otherNode.def |
||||||
|
+ " conflicts with " + opNode.def |
||||||
|
+ " and thus with " + node.def); |
||||||
|
} |
||||||
|
|
||||||
|
ig.addEdge(otherNode, node); |
||||||
|
ig.addEdge(node, otherNode); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out.println("Interference graph ="); |
||||||
|
System.out.println(ig); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Computes (a portion of) the "live out" set for a given local variable. If |
||||||
|
* a variable is live on a block's outgoing edge in the CFG, then it is |
||||||
|
* "live out" at that block. |
||||||
|
* |
||||||
|
* @param m |
||||||
|
* Bit vector that indicates the block for which block the |
||||||
|
* defNode is live out |
||||||
|
* @param nodes |
||||||
|
* The NodeInfo for the local variables used or defined in each |
||||||
|
* block |
||||||
|
* @param block |
||||||
|
* The block in which the LocalExpr of interest is defined |
||||||
|
* @param nodeIndex |
||||||
|
* Which number definition in the defining block |
||||||
|
* @param defNode |
||||||
|
* The node in the IG whose live out set we are interested in |
||||||
|
* @param phiCatchNodes |
||||||
|
* The nodes in the interference graph that represent local |
||||||
|
* variables defined by PhiCatchStmts |
||||||
|
*/ |
||||||
|
// Nate sez:
|
||||||
|
//
|
||||||
|
// In a PhiJoin pred, add
|
||||||
|
// ...
|
||||||
|
// phi-target := phi-operand
|
||||||
|
// jump with throw succs
|
||||||
|
//
|
||||||
|
// Don't kill Phi targets in protected blocks
|
||||||
|
// The phi target and operand don't conflict
|
||||||
|
void liveOut(final BitSet m, final List[] nodes, Block block, |
||||||
|
int nodeIndex, final IGNode defNode, final Collection phiCatchNodes) { |
||||||
|
boolean firstNode = true; |
||||||
|
|
||||||
|
int blockIndex = cfg.preOrderIndex(block); |
||||||
|
|
||||||
|
final ArrayList stack = new ArrayList(); |
||||||
|
|
||||||
|
Pos pos = new Pos(); |
||||||
|
pos.block = block; |
||||||
|
pos.blockIndex = blockIndex; |
||||||
|
pos.nodeIndex = nodeIndex; |
||||||
|
|
||||||
|
stack.add(pos); |
||||||
|
|
||||||
|
while (!stack.isEmpty()) { |
||||||
|
pos = (Pos) stack.remove(stack.size() - 1); |
||||||
|
|
||||||
|
block = pos.block; |
||||||
|
blockIndex = pos.blockIndex; |
||||||
|
nodeIndex = pos.nodeIndex; |
||||||
|
|
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out.println(defNode.def + " is live at position " |
||||||
|
+ nodeIndex + " of " + block); |
||||||
|
} |
||||||
|
|
||||||
|
boolean stop = false; |
||||||
|
|
||||||
|
// The nodes are sorted in reverse. So, the below gets all of
|
||||||
|
// the nodes defined at this block after nodeIndex. I believe
|
||||||
|
// this is an optimization so we don't calculate things twice.
|
||||||
|
// Or maybe its how we get things to terminate.
|
||||||
|
final ListIterator iter = nodes[blockIndex].listIterator(nodeIndex); |
||||||
|
|
||||||
|
while (!stop && iter.hasNext()) { |
||||||
|
final NodeInfo info = (NodeInfo) iter.next(); |
||||||
|
|
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out |
||||||
|
.println(defNode.def + " is live at " + info.node); |
||||||
|
} |
||||||
|
|
||||||
|
if (firstNode) { |
||||||
|
// We don't care about the definition in the block that
|
||||||
|
// defines the LocalExpr of interest.
|
||||||
|
firstNode = false; |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Look at all (?) of the definitions of the LocalExpr
|
||||||
|
final Iterator e = info.defNodes.iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final IGNode node = (IGNode) e.next(); |
||||||
|
|
||||||
|
final Iterator catchPhis = phiCatchNodes.iterator(); |
||||||
|
|
||||||
|
// Calculating the live region of the target of a phi-catch
|
||||||
|
// node is a little tricky. The target (variable) must be
|
||||||
|
// live throughout the protected region as well as after the
|
||||||
|
// PhiCatchStmt (its definition). However, we do not want
|
||||||
|
// the phi-catch target to conflict (interfere) with any of
|
||||||
|
// its operands. So, we make the target conflict with all
|
||||||
|
// of the variables that its operand conflict with. See
|
||||||
|
// page 37 of Nate's Thesis.
|
||||||
|
|
||||||
|
PHIS: while (catchPhis.hasNext()) { |
||||||
|
final IGNode catchNode = (IGNode) catchPhis.next(); |
||||||
|
|
||||||
|
final PhiCatchStmt phi = (PhiCatchStmt) catchNode.def |
||||||
|
.parent(); |
||||||
|
|
||||||
|
final Handler handler = (Handler) cfg.handlersMap() |
||||||
|
.get(phi.block()); |
||||||
|
|
||||||
|
Assert.isTrue(handler != null, "Null handler for " |
||||||
|
+ phi.block()); |
||||||
|
|
||||||
|
if (handler.protectedBlocks().contains(block)) { |
||||||
|
final Iterator operands = phi.operands().iterator(); |
||||||
|
|
||||||
|
// If the block containing the LocalExpr in question
|
||||||
|
// resides inside a protected region. Make sure that
|
||||||
|
// the LocalExpr is not one of the operands to the
|
||||||
|
// PhiCatchStmt associated with the protected
|
||||||
|
// region.
|
||||||
|
|
||||||
|
while (operands.hasNext()) { |
||||||
|
final LocalExpr expr = (LocalExpr) operands |
||||||
|
.next(); |
||||||
|
|
||||||
|
if (expr.def() == node.def) { |
||||||
|
continue PHIS; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out.println(defNode.def |
||||||
|
+ " conflicts with " + node.def); |
||||||
|
} |
||||||
|
|
||||||
|
// Hey, wow. The variable defined in the phi-catch
|
||||||
|
// interferes with the variable from the worklist.
|
||||||
|
ig.addEdge(node, catchNode); |
||||||
|
ig.addEdge(catchNode, node); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (node != defNode) { |
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out.println(defNode.def + " conflicts with " |
||||||
|
+ node.def); |
||||||
|
} |
||||||
|
|
||||||
|
// If the node in the worklist is not the node we
|
||||||
|
// started
|
||||||
|
// with, then they conflict.
|
||||||
|
ig.addEdge(node, defNode); |
||||||
|
ig.addEdge(defNode, node); |
||||||
|
|
||||||
|
} else { |
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out.println("def found stopping search"); |
||||||
|
} |
||||||
|
|
||||||
|
// We've come across a definition of the LocalExpr in
|
||||||
|
// question, so we don't need to do any more.
|
||||||
|
stop = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!stop) { |
||||||
|
// Propagate the liveness to each of the predacessors of the
|
||||||
|
// block in which the variable of interest is defined. This
|
||||||
|
// is accomplished by setting the appropriate bit in m. We
|
||||||
|
// also add another Pos to the worklist to work on the
|
||||||
|
// predacessor block.
|
||||||
|
final Iterator preds = cfg.preds(block).iterator(); |
||||||
|
|
||||||
|
while (preds.hasNext()) { |
||||||
|
final Block pred = (Block) preds.next(); |
||||||
|
final int predIndex = cfg.preOrderIndex(pred); |
||||||
|
|
||||||
|
if (Liveness.DEBUG) { |
||||||
|
System.out.println(defNode.def + " is live at end of " |
||||||
|
+ pred); |
||||||
|
} |
||||||
|
|
||||||
|
if (!m.get(predIndex)) { |
||||||
|
pos = new Pos(); |
||||||
|
pos.block = pred; |
||||||
|
pos.blockIndex = predIndex; |
||||||
|
|
||||||
|
// Look at all of the statements in which a variable
|
||||||
|
// occur
|
||||||
|
pos.nodeIndex = 0; |
||||||
|
|
||||||
|
m.set(predIndex); |
||||||
|
stack.add(pos); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Represents a node in the interference graph. Connected nodes in the |
||||||
|
* interference graph interfere with each other. That is, their live regions |
||||||
|
*/ |
||||||
|
class IGNode extends GraphNode { |
||||||
|
LocalExpr def; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param def |
||||||
|
* The local variable represented by this node. |
||||||
|
*/ |
||||||
|
public IGNode(final LocalExpr def) { |
||||||
|
this.def = def; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return def.toString(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Stores information about each Node in an expression tree (!) that defines |
||||||
|
* a local variable (i.e. PhiJoinStmt, PhiCatchStmt, and the parent of a |
||||||
|
* LocalExpr). |
||||||
|
*/ |
||||||
|
class NodeInfo { |
||||||
|
Node node; // Node in an expression tree in which a variable occurs
|
||||||
|
|
||||||
|
List defNodes; // node(s) in IG that define above Node
|
||||||
|
|
||||||
|
public NodeInfo(final Node node) { |
||||||
|
this.node = node; |
||||||
|
defNodes = new ArrayList(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class Key { |
||||||
|
int blockIndex; |
||||||
|
|
||||||
|
Node node; |
||||||
|
|
||||||
|
public Key(final Node node, final int blockIndex) { |
||||||
|
this.blockIndex = blockIndex; |
||||||
|
this.node = node; |
||||||
|
} |
||||||
|
|
||||||
|
public int hashCode() { |
||||||
|
return node.hashCode() ^ blockIndex; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean equals(final Object obj) { |
||||||
|
if (obj instanceof Key) { |
||||||
|
final Key key = (Key) obj; |
||||||
|
return (key.node == node) && (key.blockIndex == blockIndex); |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A Pos is an element in the worklist used to determine the live out set of |
||||||
|
* a given LocalExpr. It consists of the block in which a local variable |
||||||
|
* definition occurs, the block's index (i.e. pre-order traversal number) in |
||||||
|
* the CFG, and the number of the definition in the block that defines the |
||||||
|
* LocalExpr of interest. |
||||||
|
*/ |
||||||
|
class Pos { |
||||||
|
Block block; |
||||||
|
|
||||||
|
int blockIndex; |
||||||
|
|
||||||
|
int nodeIndex; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
# All files in the distribution of BLOAT (Bytecode Level Optimization and
|
||||||
|
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
||||||
|
# Research Foundation of Purdue University. All rights reserved.
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
CLASS = \
|
||||||
|
CodeGenerator.class\
|
||||||
|
Liveness.class\
|
||||||
|
RegisterAllocator.class
|
||||||
|
|
||||||
|
include ../class.mk |
@ -0,0 +1,657 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.codegen; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.cfg.*; |
||||||
|
import EDU.purdue.cs.bloat.editor.*; |
||||||
|
import EDU.purdue.cs.bloat.tree.*; |
||||||
|
import EDU.purdue.cs.bloat.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* RegisterAllocator performs analysis on a control flow graph and determines |
||||||
|
* the minimum amount of local variables needed in a method. |
||||||
|
* |
||||||
|
* @see LocalVariable |
||||||
|
*/ |
||||||
|
// Note that RegisterAllocator uses a different IGNode from Liveness!
|
||||||
|
public class RegisterAllocator { |
||||||
|
FlowGraph cfg; |
||||||
|
|
||||||
|
Liveness liveness; |
||||||
|
|
||||||
|
Map colors; |
||||||
|
|
||||||
|
int colorsUsed; |
||||||
|
|
||||||
|
final static float MAX_WEIGHT = Float.MAX_VALUE; |
||||||
|
|
||||||
|
final static float LOOP_FACTOR = 10.0F; |
||||||
|
|
||||||
|
final static int MAX_DEPTH = (int) (Math.log(RegisterAllocator.MAX_WEIGHT) / Math |
||||||
|
.log(RegisterAllocator.LOOP_FACTOR)); |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Builds an interference graph based on the expression nodes |
||||||
|
* found in liveness. Traverses the graph and determines which nodes needs |
||||||
|
* to be precolored and which nodes can be coalesced (move statements). |
||||||
|
* Nodes are coalesced and local variables are assigned to expressions. |
||||||
|
* |
||||||
|
* @see FlowGraph |
||||||
|
* @see LocalVariable |
||||||
|
*/ |
||||||
|
public RegisterAllocator(final FlowGraph cfg, final Liveness liveness) { |
||||||
|
this.cfg = cfg; |
||||||
|
this.liveness = liveness; |
||||||
|
colorsUsed = 0; |
||||||
|
colors = new HashMap(); |
||||||
|
|
||||||
|
// Construct the interference graph.
|
||||||
|
final Graph ig = new Graph(); |
||||||
|
|
||||||
|
Iterator iter = liveness.defs().iterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final VarExpr def = (VarExpr) iter.next(); |
||||||
|
|
||||||
|
if (!(def instanceof LocalExpr)) { |
||||||
|
// Ignore node in the Liveness IG that are not LocalExprs
|
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Create a new node in the IG, if one does not already exist
|
||||||
|
IGNode defNode = (IGNode) ig.getNode(def); |
||||||
|
|
||||||
|
if (defNode == null) { |
||||||
|
defNode = new IGNode((LocalExpr) def); |
||||||
|
ig.addNode(def, defNode); |
||||||
|
} |
||||||
|
|
||||||
|
// Examine each variable that interferes with def
|
||||||
|
final Iterator intersections = liveness.intersections(def); |
||||||
|
|
||||||
|
while (intersections.hasNext()) { |
||||||
|
final VarExpr expr = (VarExpr) intersections.next(); |
||||||
|
|
||||||
|
if (expr == def) { |
||||||
|
// If for some reason, def interferes with itself, ignore it
|
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Add an edge in RegisterAllocator's IG between the variables
|
||||||
|
// that interfere
|
||||||
|
if (expr instanceof LocalExpr) { |
||||||
|
IGNode node = (IGNode) ig.getNode(expr); |
||||||
|
|
||||||
|
if (node == null) { |
||||||
|
node = new IGNode((LocalExpr) expr); |
||||||
|
ig.addNode(expr, node); |
||||||
|
} |
||||||
|
|
||||||
|
ig.addEdge(defNode, node); |
||||||
|
ig.addEdge(node, defNode); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Arrays of expressions that invovle a copy of one local variable
|
||||||
|
// to another. Expressions invovled in copies (i.e. "moves") can
|
||||||
|
// be coalesced into one expression.
|
||||||
|
final ArrayList copies = new ArrayList(); |
||||||
|
|
||||||
|
// Nodes that are the targets of InitStmt are considered to be
|
||||||
|
// precolored.
|
||||||
|
final ArrayList precolor = new ArrayList(); |
||||||
|
|
||||||
|
cfg.visit(new TreeVisitor() { |
||||||
|
public void visitBlock(final Block block) { |
||||||
|
// Don't visit the sink block. There's nothing interesting
|
||||||
|
// there.
|
||||||
|
if (block != RegisterAllocator.this.cfg.sink()) { |
||||||
|
block.visitChildren(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitPhiStmt(final PhiStmt stmt) { |
||||||
|
stmt.visitChildren(this); |
||||||
|
|
||||||
|
if (!(stmt.target() instanceof LocalExpr)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// A PhiStmt invovles an assignment (copy). So note the copy
|
||||||
|
// between the target and all of the PhiStmt's operands in the
|
||||||
|
// copies list.
|
||||||
|
|
||||||
|
final IGNode lnode = (IGNode) ig.getNode(stmt.target()); |
||||||
|
|
||||||
|
final HashSet set = new HashSet(); |
||||||
|
|
||||||
|
final Iterator e = stmt.operands().iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Expr op = (Expr) e.next(); |
||||||
|
|
||||||
|
if ((op instanceof LocalExpr) && (op.def() != null)) { |
||||||
|
if (!set.contains(op.def())) { |
||||||
|
set.add(op.def()); |
||||||
|
|
||||||
|
if (op.def() != stmt.target()) { |
||||||
|
final IGNode rnode = (IGNode) ig.getNode(op |
||||||
|
.def()); |
||||||
|
copies.add(new IGNode[] { lnode, rnode }); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitStoreExpr(final StoreExpr expr) { |
||||||
|
expr.visitChildren(this); |
||||||
|
|
||||||
|
if (!(expr.target() instanceof LocalExpr)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
final IGNode lnode = (IGNode) ig.getNode(expr.target()); |
||||||
|
|
||||||
|
if ((expr.expr() instanceof LocalExpr) |
||||||
|
&& (expr.expr().def() != null)) { |
||||||
|
|
||||||
|
// A store of a variable into another variable is a copy
|
||||||
|
final IGNode rnode = (IGNode) ig.getNode(expr.expr().def()); |
||||||
|
copies.add(new IGNode[] { lnode, rnode }); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Treat L := L + k as a copy so that they get converted
|
||||||
|
// back to iincs.
|
||||||
|
if (expr.target().type().equals(Type.INTEGER)) { |
||||||
|
if (!(expr.expr() instanceof ArithExpr)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// We're dealing with integer arithmetic. Remember that an
|
||||||
|
// ArithExpr has a left and a right operand. If one of the
|
||||||
|
// operands is a variable and if the other is a constant and
|
||||||
|
// the operation is addition or substraction, we have an
|
||||||
|
// increment.
|
||||||
|
|
||||||
|
final ArithExpr rhs = (ArithExpr) expr.expr(); |
||||||
|
LocalExpr var = null; |
||||||
|
|
||||||
|
Integer value = null; |
||||||
|
|
||||||
|
if ((rhs.left() instanceof LocalExpr) |
||||||
|
&& (rhs.right() instanceof ConstantExpr)) { |
||||||
|
|
||||||
|
var = (LocalExpr) rhs.left(); |
||||||
|
|
||||||
|
final ConstantExpr c = (ConstantExpr) rhs.right(); |
||||||
|
|
||||||
|
if (c.value() instanceof Integer) { |
||||||
|
value = (Integer) c.value(); |
||||||
|
} |
||||||
|
|
||||||
|
} else if ((rhs.right() instanceof LocalExpr) |
||||||
|
&& (rhs.left() instanceof ConstantExpr)) { |
||||||
|
|
||||||
|
var = (LocalExpr) rhs.right(); |
||||||
|
|
||||||
|
final ConstantExpr c = (ConstantExpr) rhs.left(); |
||||||
|
|
||||||
|
if (c.value() instanceof Integer) { |
||||||
|
value = (Integer) c.value(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (rhs.operation() == ArithExpr.SUB) { |
||||||
|
if (value != null) { |
||||||
|
value = new Integer(-value.intValue()); |
||||||
|
} |
||||||
|
|
||||||
|
} else if (rhs.operation() != ArithExpr.ADD) { |
||||||
|
value = null; |
||||||
|
} |
||||||
|
|
||||||
|
if ((value != null) && (var.def() != null)) { |
||||||
|
final int incr = value.intValue(); |
||||||
|
|
||||||
|
if ((short) incr == incr) { |
||||||
|
// Only generate an iinc if the increment
|
||||||
|
// fits in a short
|
||||||
|
final IGNode rnode = (IGNode) ig.getNode(var.def()); |
||||||
|
copies.add(new IGNode[] { lnode, rnode }); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitInitStmt(final InitStmt stmt) { |
||||||
|
stmt.visitChildren(this); |
||||||
|
|
||||||
|
// The initialized variables are precolored.
|
||||||
|
final LocalExpr[] t = stmt.targets(); |
||||||
|
|
||||||
|
for (int i = 0; i < t.length; i++) { |
||||||
|
precolor.add(t[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
// Coalesce move related nodes, maximum weight first.
|
||||||
|
while (copies.size() > 0) { |
||||||
|
// We want the copy (v <- w) with the maximum:
|
||||||
|
// weight(v) + weight(w)
|
||||||
|
// ---------------------
|
||||||
|
// size(union)
|
||||||
|
// where union is the intersection of the nodes that conflict
|
||||||
|
// with v and the nodes that conflict with w. This equation
|
||||||
|
// appears to be in conflict with the one given on page 38 of
|
||||||
|
// Nate's thesis.
|
||||||
|
|
||||||
|
HashSet union; // The union of neighboring nodes
|
||||||
|
|
||||||
|
int max = 0; |
||||||
|
|
||||||
|
IGNode[] copy = (IGNode[]) copies.get(max); |
||||||
|
|
||||||
|
float maxWeight = copy[0].weight + copy[1].weight; |
||||||
|
union = new HashSet(); |
||||||
|
union.addAll(ig.succs(copy[0])); |
||||||
|
union.addAll(ig.succs(copy[1])); |
||||||
|
maxWeight /= union.size(); |
||||||
|
|
||||||
|
for (int i = 1; i < copies.size(); i++) { |
||||||
|
copy = (IGNode[]) copies.get(i); |
||||||
|
|
||||||
|
float weight = copy[0].weight + copy[1].weight; |
||||||
|
union.clear(); |
||||||
|
union.addAll(ig.succs(copy[0])); |
||||||
|
union.addAll(ig.succs(copy[1])); |
||||||
|
weight /= union.size(); |
||||||
|
|
||||||
|
if (weight > maxWeight) { |
||||||
|
// The ith copy has the maximum weight
|
||||||
|
maxWeight = weight; |
||||||
|
max = i; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Remove the copy with the max weight from the copies list. He
|
||||||
|
// does it in a rather round-about way.
|
||||||
|
copy = (IGNode[]) copies.get(max); |
||||||
|
copies.set(max, copies.get(copies.size() - 1)); |
||||||
|
copies.remove(copies.size() - 1); |
||||||
|
|
||||||
|
if (!ig.hasEdge(copy[0], copy[1])) { |
||||||
|
// If the variables involved in the copy do not interfere with
|
||||||
|
// each other, they are coalesced.
|
||||||
|
|
||||||
|
if (CodeGenerator.DEBUG) { |
||||||
|
System.out.println("coalescing " + copy[0] + " " + copy[1]); |
||||||
|
System.out.println(" 0 conflicts " + ig.succs(copy[0])); |
||||||
|
System.out.println(" 1 conflicts " + ig.succs(copy[1])); |
||||||
|
} |
||||||
|
|
||||||
|
ig.succs(copy[0]).addAll(ig.succs(copy[1])); |
||||||
|
ig.preds(copy[0]).addAll(ig.preds(copy[1])); |
||||||
|
|
||||||
|
copy[0].coalesce(copy[1]); |
||||||
|
|
||||||
|
if (CodeGenerator.DEBUG) { |
||||||
|
System.out.println(" coalesced " + copy[0]); |
||||||
|
System.out.println(" conflicts " + ig.succs(copy[0])); |
||||||
|
} |
||||||
|
|
||||||
|
// Remove coalesced node from the IG
|
||||||
|
ig.removeNode(copy[1].key); |
||||||
|
|
||||||
|
iter = copies.iterator(); |
||||||
|
|
||||||
|
// Examine all copies. If the copy involves the node that was
|
||||||
|
// coalesced, the copy is no longer interesting. Remove it.
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final IGNode[] c = (IGNode[]) iter.next(); |
||||||
|
|
||||||
|
if ((c[0] == copy[1]) || (c[1] == copy[1])) { |
||||||
|
iter.remove(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Create a list of uncolored nodes.
|
||||||
|
final ArrayList uncoloredNodes = new ArrayList(); |
||||||
|
|
||||||
|
Iterator nodes = ig.nodes().iterator(); |
||||||
|
|
||||||
|
while (nodes.hasNext()) { |
||||||
|
final IGNode node = (IGNode) nodes.next(); |
||||||
|
|
||||||
|
final ArrayList p = new ArrayList(precolor); |
||||||
|
p.retainAll(node.defs); |
||||||
|
|
||||||
|
// See if any node got coalesced with a precolored node.
|
||||||
|
if (p.size() == 1) { |
||||||
|
// Precolored
|
||||||
|
node.color = ((LocalExpr) p.get(0)).index(); |
||||||
|
|
||||||
|
if (CodeGenerator.DEBUG) { |
||||||
|
System.out.println("precolored " + node + " " + node.color); |
||||||
|
} |
||||||
|
|
||||||
|
} else if (p.size() == 0) { |
||||||
|
// Uncolored (i.e. not coalesced with any of the pre-colored
|
||||||
|
// nodes.
|
||||||
|
node.color = -1; |
||||||
|
uncoloredNodes.add(node); |
||||||
|
|
||||||
|
} else { |
||||||
|
// If two or more pre-colored nodes were coalesced, we have a
|
||||||
|
// problem.
|
||||||
|
throw new RuntimeException("coalesced pre-colored defs " + p); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Sort the uncolored nodes, by decreasing weight. Wide nodes
|
||||||
|
// have half their original weight since they take up two indices
|
||||||
|
// and we want to put color nodes with the lower indices.
|
||||||
|
|
||||||
|
Collections.sort(uncoloredNodes, new Comparator() { |
||||||
|
public int compare(final Object a, final Object b) { |
||||||
|
final IGNode na = (IGNode) a; |
||||||
|
final IGNode nb = (IGNode) b; |
||||||
|
|
||||||
|
float wa = na.weight / ig.succs(na).size(); |
||||||
|
float wb = nb.weight / ig.succs(nb).size(); |
||||||
|
|
||||||
|
if (na.wide) { |
||||||
|
wa /= 2; |
||||||
|
} |
||||||
|
|
||||||
|
if (nb.wide) { |
||||||
|
wb /= 2; |
||||||
|
} |
||||||
|
|
||||||
|
if (wb > wa) { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
if (wb < wa) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
nodes = uncoloredNodes.iterator(); |
||||||
|
|
||||||
|
while (nodes.hasNext()) { |
||||||
|
final IGNode node = (IGNode) nodes.next(); |
||||||
|
|
||||||
|
if (CodeGenerator.DEBUG) { |
||||||
|
System.out.println("coloring " + node); |
||||||
|
System.out.println(" conflicts " + ig.succs(node)); |
||||||
|
} |
||||||
|
|
||||||
|
// Make sure node has not been colored
|
||||||
|
Assert.isTrue(node.color == -1); |
||||||
|
|
||||||
|
// Determine which colors have been assigned to the nodes
|
||||||
|
// conflicting with the node of interest
|
||||||
|
final BitSet used = new BitSet(); |
||||||
|
|
||||||
|
final Iterator succs = ig.succs(node).iterator(); |
||||||
|
|
||||||
|
while (succs.hasNext()) { |
||||||
|
final IGNode succ = (IGNode) succs.next(); |
||||||
|
|
||||||
|
if (succ.color != -1) { |
||||||
|
used.set(succ.color); |
||||||
|
|
||||||
|
if (succ.wide) { |
||||||
|
used.set(succ.color + 1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Find the next available color
|
||||||
|
for (int i = 0; node.color == -1; i++) { |
||||||
|
if (!used.get(i)) { |
||||||
|
if (node.wide) { |
||||||
|
// Wide variables need two colors
|
||||||
|
if (!used.get(i + 1)) { |
||||||
|
node.color = i; |
||||||
|
|
||||||
|
if (CodeGenerator.DEBUG) { |
||||||
|
System.out.println(" assigning color " + i |
||||||
|
+ " to " + node); |
||||||
|
} |
||||||
|
|
||||||
|
if (i + 1 >= colorsUsed) { |
||||||
|
colorsUsed = i + 2; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
node.color = i; |
||||||
|
|
||||||
|
if (CodeGenerator.DEBUG) { |
||||||
|
System.out.println(" assigning color " + i |
||||||
|
+ " to " + node); |
||||||
|
} |
||||||
|
|
||||||
|
if (i >= colorsUsed) { |
||||||
|
colorsUsed = i + 1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
nodes = ig.nodes().iterator(); |
||||||
|
|
||||||
|
while (nodes.hasNext()) { |
||||||
|
final IGNode node = (IGNode) nodes.next(); |
||||||
|
|
||||||
|
// Make sure each node has been colored
|
||||||
|
Assert.isTrue(node.color != -1, "No color for " + node); |
||||||
|
|
||||||
|
iter = node.defs.iterator(); |
||||||
|
|
||||||
|
// Set the index of the variable and all of its uses to be the
|
||||||
|
// chosen color.
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final LocalExpr def = (LocalExpr) iter.next(); |
||||||
|
def.setIndex(node.color); |
||||||
|
|
||||||
|
final Iterator uses = def.uses().iterator(); |
||||||
|
|
||||||
|
while (uses.hasNext()) { |
||||||
|
final LocalExpr use = (LocalExpr) uses.next(); |
||||||
|
use.setIndex(node.color); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (CodeGenerator.DEBUG) { |
||||||
|
System.out.println("After allocating locals--------------------"); |
||||||
|
cfg.print(System.out); |
||||||
|
System.out.println("End print----------------------------------"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the maximum number of local variables used by the cfg after its |
||||||
|
* "registers" (local variables) have been allocated. |
||||||
|
*/ |
||||||
|
public int maxLocals() { |
||||||
|
return colorsUsed; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new local variable in this method (as modeled by the cfg). |
||||||
|
* Updates the number of local variables appropriately. |
||||||
|
*/ |
||||||
|
public LocalVariable newLocal(final Type type) { |
||||||
|
// Why don't we add Type information to the LocalVariable? Are we
|
||||||
|
// assuming that type checking has already been done and so its a
|
||||||
|
// moot point?
|
||||||
|
|
||||||
|
final LocalVariable var = new LocalVariable(colorsUsed); |
||||||
|
colorsUsed += type.stackHeight(); |
||||||
|
return var; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* IGNode is a node in the interference graph. Note that this node is |
||||||
|
* different from the one in Liveness. For instance, this one stores |
||||||
|
* information about a node's color, its weight, etc. Because nodes may be |
||||||
|
* coalesced, an IGNode may represent more than one LocalExpr. That's why |
||||||
|
* there is a list of definitions. |
||||||
|
*/ |
||||||
|
class IGNode extends GraphNode { |
||||||
|
Set defs; |
||||||
|
|
||||||
|
LocalExpr key; |
||||||
|
|
||||||
|
int color; |
||||||
|
|
||||||
|
boolean wide; // Is the variable wide?
|
||||||
|
|
||||||
|
float weight; |
||||||
|
|
||||||
|
public IGNode(final LocalExpr def) { |
||||||
|
color = -1; |
||||||
|
key = def; |
||||||
|
defs = new HashSet(); |
||||||
|
defs.add(def); |
||||||
|
wide = def.type().isWide(); |
||||||
|
computeWeight(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Coalesce two nodes in the interference graph. The weight of the other |
||||||
|
* node is added to that of this node. This node also inherits all of |
||||||
|
* the definitions of the other node. |
||||||
|
*/ |
||||||
|
void coalesce(final IGNode node) { |
||||||
|
Assert.isTrue(wide == node.wide); |
||||||
|
|
||||||
|
weight += node.weight; |
||||||
|
|
||||||
|
final Iterator iter = node.defs.iterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final LocalExpr def = (LocalExpr) iter.next(); |
||||||
|
defs.add(def); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return "(color=" + color + " weight=" + weight + " " |
||||||
|
+ defs.toString() + ")"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Calculates the weight of a Block based on its loop depth. If the |
||||||
|
* block does not exceed the MAX_DEPTH, then the weight is LOOP_FACTOR |
||||||
|
* raised to the depth. |
||||||
|
*/ |
||||||
|
private float blockWeight(final Block block) { |
||||||
|
int depth = cfg.loopDepth(block); |
||||||
|
|
||||||
|
if (depth > RegisterAllocator.MAX_DEPTH) { |
||||||
|
return RegisterAllocator.MAX_WEIGHT; |
||||||
|
} |
||||||
|
|
||||||
|
float w = 1.0F; |
||||||
|
|
||||||
|
while (depth-- > 0) { |
||||||
|
w *= RegisterAllocator.LOOP_FACTOR; |
||||||
|
} |
||||||
|
|
||||||
|
return w; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Computes the weight of a node in the interference graph. The weight |
||||||
|
* is based on where the variable represented by this node is used. The |
||||||
|
* method blockWeight is used to determine the weight of a variable used |
||||||
|
* in a block based on the loop depth of the block. Special care must be |
||||||
|
* taken if the variable is used in a PhiStmt. |
||||||
|
*/ |
||||||
|
private void computeWeight() { |
||||||
|
weight = 0.0F; |
||||||
|
|
||||||
|
final Iterator iter = defs.iterator(); |
||||||
|
|
||||||
|
// Look at all(?) of the definitions of the IGNode
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final LocalExpr def = (LocalExpr) iter.next(); |
||||||
|
|
||||||
|
weight += blockWeight(def.block()); |
||||||
|
|
||||||
|
final Iterator uses = def.uses().iterator(); |
||||||
|
|
||||||
|
// If the variable is used as an operand to a PhiJoinStmt,
|
||||||
|
// find the predacessor block to the PhiJoinStmt in which the
|
||||||
|
// variable occurs and add the weight of that block to the
|
||||||
|
// running total weight.
|
||||||
|
while (uses.hasNext()) { |
||||||
|
final LocalExpr use = (LocalExpr) uses.next(); |
||||||
|
|
||||||
|
if (use.parent() instanceof PhiJoinStmt) { |
||||||
|
final PhiJoinStmt phi = (PhiJoinStmt) use.parent(); |
||||||
|
|
||||||
|
final Iterator preds = cfg.preds(phi.block()) |
||||||
|
.iterator(); |
||||||
|
|
||||||
|
while (preds.hasNext()) { |
||||||
|
final Block pred = (Block) preds.next(); |
||||||
|
final Expr op = phi.operandAt(pred); |
||||||
|
|
||||||
|
if (use == op) { |
||||||
|
weight += blockWeight(pred); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} else if (use.parent() instanceof PhiCatchStmt) { |
||||||
|
// If the variable is used in a PhiCatchStmt, add the
|
||||||
|
// weight of the block in which the variable is defined
|
||||||
|
// to
|
||||||
|
// the running total.
|
||||||
|
weight += blockWeight(use.def().block()); |
||||||
|
|
||||||
|
} else { |
||||||
|
// Just add in the weight of the block in which the
|
||||||
|
// variable is used.
|
||||||
|
weight += blockWeight(use.block()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
<html> |
||||||
|
<body> |
||||||
|
|
||||||
|
<p>Generates Java bytecodes from the contents of a control flow graph. |
||||||
|
It also performs "register" allocation of the local variables inside |
||||||
|
the virtual machine.</p> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1 @@ |
|||||||
|
*.class |
@ -0,0 +1,261 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.context; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.editor.*; |
||||||
|
import EDU.purdue.cs.bloat.file.*; |
||||||
|
import EDU.purdue.cs.bloat.inline.*; |
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* This abstract class is a central repository for all things that are necessary |
||||||
|
* for a BLOATing sessions. Its subclasses implement certain schemes for |
||||||
|
* managing BLOAT data structures such as editors and control flow graphs. |
||||||
|
*/ |
||||||
|
public abstract class BloatContext implements InlineContext { |
||||||
|
public static boolean DEBUG = Boolean.getBoolean("BloatContext.DEBUG"); |
||||||
|
|
||||||
|
protected InlineStats inlineStats; |
||||||
|
|
||||||
|
// Ignore stuff for inlining
|
||||||
|
protected Set ignorePackages = new HashSet(); |
||||||
|
|
||||||
|
protected Set ignoreClasses = new HashSet(); |
||||||
|
|
||||||
|
protected Set ignoreMethods = new HashSet(); |
||||||
|
|
||||||
|
protected Set ignoreFields = new HashSet(); |
||||||
|
|
||||||
|
protected boolean ignoreSystem = false; |
||||||
|
|
||||||
|
protected CallGraph callGraph; |
||||||
|
|
||||||
|
protected Set roots; // Root methods of call graph
|
||||||
|
|
||||||
|
protected static void db(final String s) { |
||||||
|
if (BloatContext.DEBUG) { |
||||||
|
System.out.println(s); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected ClassInfoLoader loader; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Each <tt>BloatContext</tt> needs to know about a |
||||||
|
* <tt>ClassInfoLoader</tt>. |
||||||
|
*/ |
||||||
|
public BloatContext(final ClassInfoLoader loader) { |
||||||
|
this.loader = loader; |
||||||
|
} |
||||||
|
|
||||||
|
private static ClassLoader systemCL; |
||||||
|
static { |
||||||
|
final String s = ""; |
||||||
|
BloatContext.systemCL = s.getClass().getClassLoader(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns <tt>true</tt> if the give type is a system class (that is, has |
||||||
|
* the same class loader as java.lang.String). |
||||||
|
*/ |
||||||
|
public static boolean isSystem(final Type type) { |
||||||
|
Class c = null; |
||||||
|
try { |
||||||
|
c = Class.forName(type.className().replace('/', '.')); |
||||||
|
|
||||||
|
} catch (final ClassNotFoundException ex) { |
||||||
|
System.err.println("** Could not find class " + type.className()); |
||||||
|
ex.printStackTrace(System.err); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
// Have to use == because class loader might be null
|
||||||
|
return (c.getClassLoader() == BloatContext.systemCL); |
||||||
|
} |
||||||
|
|
||||||
|
public void setRootMethods(final Set roots) { |
||||||
|
if (this.callGraph != null) { |
||||||
|
// Can't set the call graph roots after its been created
|
||||||
|
throw new IllegalStateException("Cannot set roots after " |
||||||
|
+ "call graph has been created"); |
||||||
|
} |
||||||
|
|
||||||
|
this.roots = roots; |
||||||
|
} |
||||||
|
|
||||||
|
public CallGraph getCallGraph() { |
||||||
|
if (this.callGraph == null) { |
||||||
|
// Create a new CallGraph
|
||||||
|
this.callGraph = new CallGraph(this, this.roots); |
||||||
|
} |
||||||
|
return (this.callGraph); |
||||||
|
} |
||||||
|
|
||||||
|
public InlineStats getInlineStats() { |
||||||
|
if (inlineStats == null) { |
||||||
|
inlineStats = new InlineStats(); |
||||||
|
} |
||||||
|
return (inlineStats); |
||||||
|
} |
||||||
|
|
||||||
|
public void addIgnorePackage(String name) { |
||||||
|
name = name.replace('.', '/'); |
||||||
|
ignorePackages.add(name); |
||||||
|
} |
||||||
|
|
||||||
|
public void addIgnoreClass(final Type type) { |
||||||
|
ignoreClasses.add(type); |
||||||
|
} |
||||||
|
|
||||||
|
public void addIgnoreMethod(final MemberRef method) { |
||||||
|
ignoreMethods.add(method); |
||||||
|
} |
||||||
|
|
||||||
|
public void addIgnoreField(final MemberRef field) { |
||||||
|
ignoreFields.add(field); |
||||||
|
} |
||||||
|
|
||||||
|
public void setIgnoreSystem(final boolean ignore) { |
||||||
|
this.ignoreSystem = ignore; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean ignoreClass(final Type type) { |
||||||
|
// First, check to see if we explicitly ignore it. If not, check
|
||||||
|
// to see if we ignore its package. The ladies always seem to
|
||||||
|
// ignore my package.
|
||||||
|
if (ignoreClasses.contains(type)) { |
||||||
|
return (true); |
||||||
|
|
||||||
|
} else if (type.isPrimitive()) { |
||||||
|
addIgnoreClass(type); |
||||||
|
return (true); |
||||||
|
|
||||||
|
} else { |
||||||
|
if (this.ignoreSystem) { |
||||||
|
if (BloatContext.isSystem(type)) { |
||||||
|
addIgnoreClass(type); |
||||||
|
return (true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
String packageName = type.className(); |
||||||
|
final int lastSlash = packageName.lastIndexOf('/'); |
||||||
|
|
||||||
|
if (lastSlash == -1) { |
||||||
|
return (false); |
||||||
|
} |
||||||
|
|
||||||
|
packageName = packageName.substring(0, lastSlash); |
||||||
|
|
||||||
|
// If any ignore package is a prefix of the class's package,
|
||||||
|
// then ignore it. This makes our lives easier.
|
||||||
|
final Iterator packages = ignorePackages.iterator(); |
||||||
|
while (packages.hasNext()) { |
||||||
|
final String s = (String) packages.next(); |
||||||
|
if (type.className().startsWith(s)) { |
||||||
|
addIgnoreClass(type); |
||||||
|
return (true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return (false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public boolean ignoreMethod(final MemberRef method) { |
||||||
|
if (ignoreMethods.contains(method)) { |
||||||
|
return (true); |
||||||
|
|
||||||
|
} else if (ignoreClass(method.declaringClass())) { |
||||||
|
addIgnoreMethod(method); |
||||||
|
return (true); |
||||||
|
} |
||||||
|
return (false); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean ignoreField(final MemberRef field) { |
||||||
|
if (ignoreMethods.contains(field)) { |
||||||
|
return (true); |
||||||
|
|
||||||
|
} else if (ignoreClass(field.declaringClass())) { |
||||||
|
addIgnoreField(field); |
||||||
|
return (true); |
||||||
|
} |
||||||
|
return (false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Commits all classes, methods, and fields, that have been modified. |
||||||
|
*/ |
||||||
|
public abstract void commitDirty(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Test the ignore stuff. |
||||||
|
*/ |
||||||
|
public static void main(final String[] args) { |
||||||
|
final PrintWriter out = new PrintWriter(System.out, true); |
||||||
|
final PrintWriter err = new PrintWriter(System.err, true); |
||||||
|
|
||||||
|
final BloatContext context = new CachingBloatContext( |
||||||
|
new ClassFileLoader(), new ArrayList(), false); |
||||||
|
|
||||||
|
final List types = new ArrayList(); |
||||||
|
|
||||||
|
for (int i = 0; i < args.length; i++) { |
||||||
|
if (args[i].equals("-ip")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
err.println("** Missing package name"); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
out.println("Ignoring package " + args[i]); |
||||||
|
context.addIgnorePackage(args[i]); |
||||||
|
|
||||||
|
} else if (args[i].equals("-ic")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
err.println("** Missing class name"); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
out.println("Ignoring class " + args[i]); |
||||||
|
final String type = args[i].replace('.', '/'); |
||||||
|
context.addIgnoreClass(Type.getType("L" + type + ";")); |
||||||
|
|
||||||
|
} else { |
||||||
|
// A type
|
||||||
|
final String type = args[i].replace('.', '/'); |
||||||
|
types.add(Type.getType("L" + type + ";")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
out.println(""); |
||||||
|
|
||||||
|
final Iterator iter = types.iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final Type type = (Type) iter.next(); |
||||||
|
out.println("Ignore " + type + "? " + context.ignoreClass(type)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,147 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.context; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.net.*; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.editor.*; |
||||||
|
import EDU.purdue.cs.bloat.file.*; |
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
import EDU.purdue.cs.bloat.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* <code>BloatingClassLoader</code> is a Java class loader that BLOATs a class
|
||||||
|
* before it is loader into a Java Virtual Machine. It loads its classes from a |
||||||
|
* set of {@link URL}s. |
||||||
|
*/ |
||||||
|
public abstract class BloatingClassLoader extends URLClassLoader { |
||||||
|
|
||||||
|
/** |
||||||
|
* A ClassInfoLoader that loads classes from the same locations as this |
||||||
|
* class loader. |
||||||
|
*/ |
||||||
|
ClassInfoLoader loader = new BloatingClassInfoLoader(); |
||||||
|
|
||||||
|
/** The context that is used to edit classes, etc. */ |
||||||
|
private final EditorContext context = new PersistentBloatContext(loader, |
||||||
|
false); |
||||||
|
|
||||||
|
/** |
||||||
|
* Maps ClassInfos to their committed bytes (as a ByteArrayOutputStream) |
||||||
|
*/ |
||||||
|
private final Map classBytes = new HashMap(); |
||||||
|
|
||||||
|
// //////////////////// Constructors /////////////////////////
|
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new <code>BloatingClassLoader</code> that loads its classes |
||||||
|
* from a given set of URLs. |
||||||
|
*/ |
||||||
|
public BloatingClassLoader(final URL[] urls) { |
||||||
|
super(urls); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new <code>BloatingClassLoader</code> that loads its classes |
||||||
|
* from a given set of URLs. Before attempting to load a class, this |
||||||
|
* <code>BloatingClassLoader</code> will delegate to its parent class
|
||||||
|
* loader. |
||||||
|
*/ |
||||||
|
public BloatingClassLoader(final URL[] urls, final ClassLoader parent) { |
||||||
|
super(urls); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Before the <code>Class</code> is created, invoke {@link |
||||||
|
* #bloat(ClassEditor)}. |
||||||
|
*/ |
||||||
|
protected Class findClass(final String name) throws ClassNotFoundException { |
||||||
|
|
||||||
|
final ClassInfo info = this.loader.loadClass(name); |
||||||
|
final ClassEditor ce = this.context.editClass(info); |
||||||
|
|
||||||
|
this.bloat(ce); |
||||||
|
|
||||||
|
ce.commit(); |
||||||
|
final ByteArrayOutputStream baos = (ByteArrayOutputStream) this.classBytes |
||||||
|
.get(info); |
||||||
|
Assert.isNotNull(baos, "No bytes for " + name); |
||||||
|
|
||||||
|
final byte[] bytes = baos.toByteArray(); |
||||||
|
return super.defineClass(name, bytes, 0, bytes.length); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a <code>ClassInfoLoader</code> that loads classes from the same |
||||||
|
* place as this <code>ClassLoader</code>. |
||||||
|
*/ |
||||||
|
public ClassInfoLoader getClassInfoLoader() { |
||||||
|
return this.loader; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method is invoked as a class is being loaded. |
||||||
|
*/ |
||||||
|
protected abstract void bloat(ClassEditor ce); |
||||||
|
|
||||||
|
/** |
||||||
|
* This inner class is a ClassInfoLoader that loads classes from the same |
||||||
|
* locations as the outer BloatClassLoader. The primary reason that we have |
||||||
|
* this class is because the loadClass method of ClassInfoLoader has a |
||||||
|
* different signature from ClassLoader. Hence, a ClassLoader cannot be a |
||||||
|
* ClassInfoLoader. |
||||||
|
*/ |
||||||
|
class BloatingClassInfoLoader implements ClassInfoLoader { |
||||||
|
|
||||||
|
public ClassInfo loadClass(final String name) |
||||||
|
throws ClassNotFoundException { |
||||||
|
|
||||||
|
final String classFileName = name.replace('.', '/') + ".class"; |
||||||
|
final InputStream is = BloatingClassLoader.this |
||||||
|
.getResourceAsStream(classFileName); |
||||||
|
if (is == null) { |
||||||
|
throw new ClassNotFoundException("Could not find class " + name); |
||||||
|
} |
||||||
|
|
||||||
|
final DataInputStream dis = new DataInputStream(is); |
||||||
|
return new ClassFile(null, this, dis); |
||||||
|
} |
||||||
|
|
||||||
|
public ClassInfo newClass(final int modifiers, final int classIndex, |
||||||
|
final int superClassIndex, final int[] interfaceIndexes, |
||||||
|
final java.util.List constants) { |
||||||
|
|
||||||
|
return new ClassFile(modifiers, classIndex, superClassIndex, |
||||||
|
interfaceIndexes, constants, this); |
||||||
|
} |
||||||
|
|
||||||
|
public OutputStream outputStreamFor(final ClassInfo info) |
||||||
|
throws IOException { |
||||||
|
|
||||||
|
// Maintain a mapping between ClassInfos and their committed bytes
|
||||||
|
final OutputStream os = new ByteArrayOutputStream(); |
||||||
|
classBytes.put(info, os); |
||||||
|
return (os); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,461 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.context; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.editor.*; |
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Does a lot of the same stuff as <tt>PersistentBloatContext</tt> except that |
||||||
|
* it manages the chaches of BLOAT objects. For example, when a |
||||||
|
* <tt>MethodEditor</tt> is no longer needed, it is removed from the cache if |
||||||
|
* it is not dirty. This context is meant to used in volatile memory. |
||||||
|
*/ |
||||||
|
public class CachingBloatContext extends PersistentBloatContext { |
||||||
|
|
||||||
|
// Keep track of reference counts in a manner reminiscent of the old
|
||||||
|
// Editor class.
|
||||||
|
protected Map classRC; |
||||||
|
|
||||||
|
protected Map methodRC; |
||||||
|
|
||||||
|
protected Map fieldRC; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param loader |
||||||
|
* Used to load classes |
||||||
|
* @param classes |
||||||
|
* Some initial classes in the context |
||||||
|
* @param closure |
||||||
|
* Do we look for the maximum number of classes? |
||||||
|
*/ |
||||||
|
public CachingBloatContext(final ClassInfoLoader loader, |
||||||
|
final Collection classes, final boolean closure) { |
||||||
|
super(loader, closure); |
||||||
|
|
||||||
|
classRC = new HashMap(); |
||||||
|
methodRC = new HashMap(); |
||||||
|
fieldRC = new HashMap(); |
||||||
|
|
||||||
|
addClasses(classes); |
||||||
|
} |
||||||
|
|
||||||
|
public ClassEditor newClass(final int modifiers, final String className, |
||||||
|
final Type superType, final Type[] interfaces) { |
||||||
|
|
||||||
|
final ClassEditor ce = super.newClass(modifiers, className, superType, |
||||||
|
interfaces); |
||||||
|
final ClassInfo info = ce.classInfo(); |
||||||
|
classRC.put(info, new Integer(1)); |
||||||
|
|
||||||
|
return ce; |
||||||
|
} |
||||||
|
|
||||||
|
public ClassEditor editClass(final ClassInfo info) { |
||||||
|
// Check the cache
|
||||||
|
ClassEditor ce = (ClassEditor) classEditors.get(info); |
||||||
|
|
||||||
|
if (ce == null) { |
||||||
|
ce = new ClassEditor(this, info); |
||||||
|
classEditors.put(info, ce); |
||||||
|
classRC.put(info, new Integer(1)); |
||||||
|
|
||||||
|
if (!classInfos.containsValue(info)) { |
||||||
|
final String className = ce.name().intern(); |
||||||
|
BloatContext.db("editClass(ClassInfo): " + className + " -> " |
||||||
|
+ info); |
||||||
|
classInfos.put(className, info); |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
final Integer rc = (Integer) classRC.get(info); |
||||||
|
classRC.put(info, new Integer(rc.intValue() + 1)); |
||||||
|
} |
||||||
|
|
||||||
|
return (ce); |
||||||
|
} |
||||||
|
|
||||||
|
public MethodEditor editMethod(final MemberRef method) |
||||||
|
throws NoSuchMethodException { |
||||||
|
|
||||||
|
// Check the MethodInfo cache
|
||||||
|
final MethodInfo info = (MethodInfo) methodInfos.get(method); |
||||||
|
|
||||||
|
if (info == null) { |
||||||
|
// Groan, we have to do this the HARD way.
|
||||||
|
BloatContext.db("Creating a new MethodEditor for " + method); |
||||||
|
final NameAndType nat = method.nameAndType(); |
||||||
|
final String name = nat.name(); |
||||||
|
final Type type = nat.type(); |
||||||
|
|
||||||
|
try { |
||||||
|
final ClassEditor ce = editClass(method.declaringClass()); |
||||||
|
final MethodInfo[] methods = ce.methods(); |
||||||
|
|
||||||
|
for (int i = 0; i < methods.length; i++) { |
||||||
|
final MethodEditor me = editMethod(methods[i]); |
||||||
|
|
||||||
|
if (me.name().equals(name) && me.type().equals(type)) { |
||||||
|
// The call to editMethod should have already handled
|
||||||
|
// the
|
||||||
|
// methodEditors mapping, but we still need to do
|
||||||
|
// methodInfos.
|
||||||
|
methodInfos.put(method, methods[i]); |
||||||
|
release(ce.classInfo()); |
||||||
|
return (me); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
release(ce.classInfo()); |
||||||
|
|
||||||
|
} catch (final ClassNotFoundException ex1) { |
||||||
|
throw new NoSuchMethodException(method.toString() + "(" |
||||||
|
+ ex1.getMessage() + ")"); |
||||||
|
|
||||||
|
} catch (final ClassFormatException ex2) { |
||||||
|
throw new NoSuchMethodException(method.toString() + "(" |
||||||
|
+ ex2.getMessage() + ")"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
throw new NoSuchMethodException(method.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
return (editMethod(info)); |
||||||
|
} |
||||||
|
|
||||||
|
public MethodEditor editMethod(final MethodInfo info) { |
||||||
|
// Check methodEditors cache
|
||||||
|
MethodEditor me = (MethodEditor) methodEditors.get(info); |
||||||
|
|
||||||
|
if (me == null) { |
||||||
|
final ClassInfo classInfo = info.declaringClass(); |
||||||
|
me = new MethodEditor(editClass(classInfo), info); |
||||||
|
release(classInfo); |
||||||
|
|
||||||
|
methodEditors.put(info, me); |
||||||
|
methodRC.put(info, new Integer(1)); |
||||||
|
BloatContext |
||||||
|
.db("Creating a new MethodEditor for " + me.memberRef()); |
||||||
|
|
||||||
|
} else { |
||||||
|
final Integer rc = (Integer) methodRC.get(info); |
||||||
|
methodRC.put(info, new Integer(rc.intValue() + 1)); |
||||||
|
} |
||||||
|
|
||||||
|
return (me); |
||||||
|
} |
||||||
|
|
||||||
|
public FieldEditor editField(final MemberRef field) |
||||||
|
throws NoSuchFieldException { |
||||||
|
|
||||||
|
// Just like we had to do with methods
|
||||||
|
final FieldInfo info = (FieldInfo) fieldInfos.get(field); |
||||||
|
|
||||||
|
if (info == null) { |
||||||
|
final NameAndType nat = field.nameAndType(); |
||||||
|
final String name = nat.name(); |
||||||
|
final Type type = nat.type(); |
||||||
|
|
||||||
|
try { |
||||||
|
final ClassEditor ce = editClass(field.declaringClass()); |
||||||
|
final FieldInfo[] fields = ce.fields(); |
||||||
|
|
||||||
|
for (int i = 0; i < fields.length; i++) { |
||||||
|
final FieldEditor fe = editField(fields[i]); |
||||||
|
|
||||||
|
if (fe.name().equals(name) && fe.type().equals(type)) { |
||||||
|
fieldInfos.put(field, fields[i]); |
||||||
|
release(ce.classInfo()); |
||||||
|
return (fe); |
||||||
|
} |
||||||
|
|
||||||
|
release(fields[i]); |
||||||
|
} |
||||||
|
|
||||||
|
release(ce.classInfo()); |
||||||
|
} catch (final ClassNotFoundException ex1) { |
||||||
|
} catch (final ClassFormatException ex2) { |
||||||
|
} |
||||||
|
|
||||||
|
throw new NoSuchFieldException(field.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
return (editField(info)); |
||||||
|
} |
||||||
|
|
||||||
|
public FieldEditor editField(final FieldInfo info) { |
||||||
|
// Check the cache
|
||||||
|
FieldEditor fe = (FieldEditor) fieldEditors.get(info); |
||||||
|
|
||||||
|
BloatContext.db("Editing " + info); |
||||||
|
|
||||||
|
if (fe == null) { |
||||||
|
final ClassInfo classInfo = info.declaringClass(); |
||||||
|
fe = new FieldEditor(editClass(classInfo), info); |
||||||
|
release(classInfo); |
||||||
|
|
||||||
|
fieldEditors.put(info, fe); |
||||||
|
fieldRC.put(info, new Integer(0)); |
||||||
|
BloatContext.db("Creating a new FieldEditor for " |
||||||
|
+ fe.nameAndType()); |
||||||
|
|
||||||
|
} else { |
||||||
|
final Integer rc = (Integer) fieldRC.get(info); |
||||||
|
fieldRC.put(info, new Integer(rc.intValue() + 1)); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return (fe); |
||||||
|
} |
||||||
|
|
||||||
|
public void release(final ClassInfo info) { |
||||||
|
final Integer rc = (Integer) classRC.get(info); |
||||||
|
|
||||||
|
if ((rc != null) && (rc.intValue() > 1)) { |
||||||
|
// Not done yet;
|
||||||
|
classRC.put(info, new Integer(rc.intValue() - 1)); |
||||||
|
return; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
ClassEditor ce = (ClassEditor) classEditors.get(info); |
||||||
|
if ((ce != null) && ce.isDirty()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// We're done with this class, remove all traces of it
|
||||||
|
ce = (ClassEditor) classEditors.remove(info); |
||||||
|
classRC.remove(info); |
||||||
|
classEditors.remove(info); |
||||||
|
|
||||||
|
final Iterator iter = classInfos.keySet().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final String name = (String) iter.next(); |
||||||
|
final ClassInfo info2 = (ClassInfo) classInfos.get(name); |
||||||
|
if (info2 == info) { |
||||||
|
BloatContext.db("Removing ClassInfo: " + name + " -> " + info2); |
||||||
|
classInfos.remove(name); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (ce != null) { |
||||||
|
// Remove all of the class's fields and methods also
|
||||||
|
final MethodInfo[] methods = ce.methods(); |
||||||
|
for (int i = 0; i < methods.length; i++) { |
||||||
|
release(methods[i]); |
||||||
|
} |
||||||
|
|
||||||
|
final FieldInfo[] fields = ce.fields(); |
||||||
|
for (int i = 0; i < fields.length; i++) { |
||||||
|
release(fields[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void release(final MethodInfo info) { |
||||||
|
final Integer rc = (Integer) classRC.get(info); |
||||||
|
|
||||||
|
if ((rc != null) && (rc.intValue() > 1)) { |
||||||
|
methodRC.put(info, new Integer(rc.intValue() - 1)); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
final MethodEditor me = (MethodEditor) methodEditors.get(info); |
||||||
|
|
||||||
|
// We should keep dirty methods around. My original thought was
|
||||||
|
// that if we committed dirty methods when they were released, we
|
||||||
|
// risk having MethodEditors editing different versions of the
|
||||||
|
// same method. So, if we don't release dirty methods, we'll only
|
||||||
|
// have ONE MethodEditor.
|
||||||
|
if ((me != null) && me.isDirty()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// We're done with this method, remove all traces of it
|
||||||
|
methodRC.remove(info); |
||||||
|
methodEditors.remove(info); |
||||||
|
|
||||||
|
final Iterator iter = methodInfos.keySet().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final MemberRef ref = (MemberRef) iter.next(); |
||||||
|
final MethodInfo info2 = (MethodInfo) methodInfos.get(ref); |
||||||
|
if (info2 == info) { |
||||||
|
methodInfos.remove(ref); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void release(final FieldInfo info) { |
||||||
|
final Integer rc = (Integer) fieldRC.get(info); |
||||||
|
|
||||||
|
BloatContext.db("Releasing " + info); |
||||||
|
|
||||||
|
if ((rc != null) && (rc.intValue() > 1)) { |
||||||
|
fieldRC.put(info, new Integer(rc.intValue() - 1)); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
final FieldEditor fe = (FieldEditor) fieldEditors.get(info); |
||||||
|
if ((fe != null) && fe.isDirty()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// We're done with this field, remove all traces of it
|
||||||
|
fieldRC.remove(info); |
||||||
|
fieldEditors.remove(info); |
||||||
|
|
||||||
|
final Iterator iter = fieldInfos.keySet().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final MemberRef ref = (MemberRef) iter.next(); |
||||||
|
final FieldInfo info2 = (FieldInfo) fieldInfos.get(ref); |
||||||
|
if (info2 == info) { |
||||||
|
fieldInfos.remove(ref); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void commit(final ClassInfo info) { |
||||||
|
super.commit(info); |
||||||
|
|
||||||
|
classEditors.remove(info); |
||||||
|
classRC.remove(info); |
||||||
|
} |
||||||
|
|
||||||
|
public void commit(final MethodInfo info) { |
||||||
|
super.commit(info); |
||||||
|
|
||||||
|
methodEditors.remove(info); |
||||||
|
methodRC.remove(info); |
||||||
|
} |
||||||
|
|
||||||
|
public void commit(final FieldInfo info) { |
||||||
|
super.commit(info); |
||||||
|
|
||||||
|
fieldEditors.remove(info); |
||||||
|
fieldRC.remove(info); |
||||||
|
} |
||||||
|
|
||||||
|
public void commit() { |
||||||
|
Iterator iter = fieldEditors.values().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final FieldEditor fe = (FieldEditor) iter.next(); |
||||||
|
commit(fe.fieldInfo()); |
||||||
|
} |
||||||
|
|
||||||
|
iter = methodEditors.values().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final MethodEditor me = (MethodEditor) iter.next(); |
||||||
|
commit(me.methodInfo()); |
||||||
|
} |
||||||
|
|
||||||
|
iter = classEditors.values().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final ClassEditor ce = (ClassEditor) iter.next(); |
||||||
|
commit(ce.classInfo()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return a textual description of all of the caches. Useful if we run out |
||||||
|
* of memory. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
final StringWriter sw = new StringWriter(); |
||||||
|
final PrintWriter pw = new PrintWriter(sw, true); |
||||||
|
|
||||||
|
pw.println("Context of caches in CachingBloatContext..."); |
||||||
|
|
||||||
|
pw.println(" Class Infos"); |
||||||
|
Iterator iter = classInfos.keySet().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final Object key = iter.next(); |
||||||
|
pw.println(" " + key + " -> " + classInfos.get(key)); |
||||||
|
} |
||||||
|
|
||||||
|
pw.println(" Class Editors"); |
||||||
|
iter = classEditors.keySet().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final Object key = iter.next(); |
||||||
|
pw.println(" " + key + " -> " + classEditors.get(key)); |
||||||
|
} |
||||||
|
|
||||||
|
pw.println(" Class RC"); |
||||||
|
iter = classRC.keySet().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final Object key = iter.next(); |
||||||
|
pw.println(" " + key + " -> " + classRC.get(key)); |
||||||
|
} |
||||||
|
|
||||||
|
pw.println(" Method Infos"); |
||||||
|
iter = methodInfos.keySet().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final Object key = iter.next(); |
||||||
|
pw.println(" " + key + " -> " + methodInfos.get(key)); |
||||||
|
} |
||||||
|
|
||||||
|
pw.println(" Method Editors"); |
||||||
|
iter = methodEditors.keySet().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final Object key = iter.next(); |
||||||
|
pw.println(" " + key + " -> " + methodEditors.get(key)); |
||||||
|
} |
||||||
|
|
||||||
|
pw.println(" Method RC"); |
||||||
|
iter = methodRC.keySet().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final Object key = iter.next(); |
||||||
|
pw.println(" " + key + " -> " + methodRC.get(key)); |
||||||
|
} |
||||||
|
|
||||||
|
pw.println(" Field Infos"); |
||||||
|
iter = fieldInfos.keySet().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final Object key = iter.next(); |
||||||
|
pw.println(" " + key + " -> " + fieldInfos.get(key)); |
||||||
|
} |
||||||
|
|
||||||
|
pw.println(" Field Editors"); |
||||||
|
iter = fieldEditors.keySet().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final Object key = iter.next(); |
||||||
|
pw.println(" " + key + " -> " + fieldEditors.get(key)); |
||||||
|
} |
||||||
|
|
||||||
|
pw.println(" Field RC"); |
||||||
|
iter = fieldRC.keySet().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final Object key = iter.next(); |
||||||
|
pw.println(" " + key + " -> " + fieldRC.get(key)); |
||||||
|
} |
||||||
|
|
||||||
|
return (sw.toString()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
# All files in the distribution of BLOAT (Bytecode Level Optimization and
|
||||||
|
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
||||||
|
# Research Foundation of Purdue University. All rights reserved.
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
|
||||||
|
CLASS = \
|
||||||
|
BloatContext.class\
|
||||||
|
CachingBloatContext.class\
|
||||||
|
PersistentBloatContext.class
|
||||||
|
|
||||||
|
include ../class.mk |
@ -0,0 +1,438 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.context; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.editor.*; |
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Maintains all BLOAT data structures as if they were meant to reside in a |
||||||
|
* persistent store. As a result, it keeps every piece of BLOAT data around |
||||||
|
* because it might be needed in the future. No fancing cache maintainence is |
||||||
|
* performed. Because we are going for maximum information we take the closure |
||||||
|
* of classes when working with the class hierarchy. |
||||||
|
*/ |
||||||
|
public class PersistentBloatContext extends BloatContext { |
||||||
|
|
||||||
|
protected final ClassHierarchy hierarchy; |
||||||
|
|
||||||
|
protected Map classInfos; // Maps Strings to ClassInfos
|
||||||
|
|
||||||
|
protected Map methodInfos; // Maps MemberRefs to MethodInfos
|
||||||
|
|
||||||
|
protected Map fieldInfos; // Maps MemberRefs to FieldInfos
|
||||||
|
|
||||||
|
protected Map classEditors; // Maps ClassInfos to ClassEditors
|
||||||
|
|
||||||
|
protected Map methodEditors; // Maps MethodInfos to MethodEditors
|
||||||
|
|
||||||
|
protected Map fieldEditors; // Maps MethodInfos to FieldEditors
|
||||||
|
|
||||||
|
public static boolean DB_COMMIT = false; |
||||||
|
|
||||||
|
protected static void comm(final String s) { |
||||||
|
if (PersistentBloatContext.DB_COMMIT || BloatContext.DEBUG) { |
||||||
|
System.out.println(s); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Each <tt>BloatContext</tt> stems from a |
||||||
|
* <tt>ClassInfoLoader</tt>. Using the loader it can create an |
||||||
|
* <tt>Editor</tt> and such. Initially, no classes are loaded. |
||||||
|
*/ |
||||||
|
public PersistentBloatContext(final ClassInfoLoader loader) { |
||||||
|
this(loader, true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. It is the responsibility of the subclasses to add classes to |
||||||
|
* the hierarchy by calling <tt>addClasses</tt>. |
||||||
|
* |
||||||
|
* @param loader |
||||||
|
* Used to load classes |
||||||
|
* @param closure |
||||||
|
* Do we look for the maximum number of classes? |
||||||
|
*/ |
||||||
|
protected PersistentBloatContext(final ClassInfoLoader loader, |
||||||
|
final boolean closure) { |
||||||
|
super(loader); |
||||||
|
BloatContext.db("Creating a new BloatContext"); |
||||||
|
|
||||||
|
// Create a bunch of the mappings we maintain. Make sure to do
|
||||||
|
// this before anything else!
|
||||||
|
classInfos = new HashMap(); |
||||||
|
methodInfos = new HashMap(); |
||||||
|
fieldInfos = new HashMap(); |
||||||
|
|
||||||
|
classEditors = new HashMap(); |
||||||
|
methodEditors = new HashMap(); |
||||||
|
fieldEditors = new HashMap(); |
||||||
|
|
||||||
|
// Have to create an empty class hierarchy then add the classes.
|
||||||
|
// There is a strange circular dependence between the hierarchy
|
||||||
|
// and the context.
|
||||||
|
this.hierarchy = new ClassHierarchy(this, new ArrayList(), closure); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds a bunch of (names of) classes to the hierarchy. |
||||||
|
*/ |
||||||
|
protected void addClasses(final Collection classes) { |
||||||
|
final Iterator iter = classes.iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final String className = (String) iter.next(); |
||||||
|
this.hierarchy.addClassNamed(className); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public ClassInfo loadClass(String className) throws ClassNotFoundException { |
||||||
|
// Lots of interesting stuff to do here. For the moment, just
|
||||||
|
// load the class from the ClassInfoLoader and add it to the
|
||||||
|
// hierarchy.
|
||||||
|
|
||||||
|
className = className.replace('.', '/').intern(); |
||||||
|
|
||||||
|
// Check the cache of ClassInfos
|
||||||
|
ClassInfo info = (ClassInfo) classInfos.get(className); |
||||||
|
|
||||||
|
if (info == null) { |
||||||
|
BloatContext.db("BloatContext: Loading class " + className); |
||||||
|
info = loader.loadClass(className); |
||||||
|
hierarchy.addClassNamed(className); |
||||||
|
BloatContext.db("loadClass: " + className + " -> " + info); |
||||||
|
classInfos.put(className, info); |
||||||
|
} |
||||||
|
|
||||||
|
return (info); |
||||||
|
} |
||||||
|
|
||||||
|
public ClassInfo newClassInfo(final int modifiers, final int classIndex, |
||||||
|
final int superClassIndex, final int[] interfaceIndexes, |
||||||
|
final List constants) { |
||||||
|
|
||||||
|
return this.loader.newClass(modifiers, classIndex, superClassIndex, |
||||||
|
interfaceIndexes, constants); |
||||||
|
} |
||||||
|
|
||||||
|
public ClassHierarchy getHierarchy() { |
||||||
|
return (this.hierarchy); |
||||||
|
} |
||||||
|
|
||||||
|
public ClassEditor newClass(final int modifiers, String className, |
||||||
|
final Type superType, final Type[] interfaces) { |
||||||
|
|
||||||
|
final ClassEditor ce = new ClassEditor(this, modifiers, className, |
||||||
|
superType, interfaces); |
||||||
|
final ClassInfo info = ce.classInfo(); |
||||||
|
|
||||||
|
className = ce.name().intern(); |
||||||
|
|
||||||
|
BloatContext.db("editClass(ClassInfo): " + className + " -> " + info); |
||||||
|
|
||||||
|
classInfos.put(className, info); |
||||||
|
classEditors.put(info, ce); |
||||||
|
|
||||||
|
return ce; |
||||||
|
} |
||||||
|
|
||||||
|
public ClassEditor editClass(String className) |
||||||
|
throws ClassNotFoundException, ClassFormatException { |
||||||
|
// Only make the name -> classInfo mapping if we edit the class,
|
||||||
|
// this way the mapping will be deleted when the ClassEditor is
|
||||||
|
// released.
|
||||||
|
|
||||||
|
className = className.intern(); |
||||||
|
|
||||||
|
ClassInfo info = (ClassInfo) classInfos.get(className); |
||||||
|
|
||||||
|
if (info == null) { |
||||||
|
info = loadClass(className); |
||||||
|
// db("editClass(String): " + className + " -> " + info);
|
||||||
|
// classInfos.put(className, info);
|
||||||
|
} |
||||||
|
|
||||||
|
return (editClass(info)); |
||||||
|
} |
||||||
|
|
||||||
|
public ClassEditor editClass(final Type classType) |
||||||
|
throws ClassNotFoundException, ClassFormatException { |
||||||
|
return (editClass(classType.className())); |
||||||
|
} |
||||||
|
|
||||||
|
public ClassEditor editClass(final ClassInfo info) { |
||||||
|
// Check the cache
|
||||||
|
ClassEditor ce = (ClassEditor) classEditors.get(info); |
||||||
|
|
||||||
|
if (ce == null) { |
||||||
|
ce = new ClassEditor(this, info); |
||||||
|
classEditors.put(info, ce); |
||||||
|
|
||||||
|
if (!classInfos.containsValue(info)) { |
||||||
|
final String className = ce.name().intern(); |
||||||
|
BloatContext.db("editClass(ClassInfo): " + className + " -> " |
||||||
|
+ info); |
||||||
|
classInfos.put(className, info); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return (ce); |
||||||
|
} |
||||||
|
|
||||||
|
public MethodEditor editMethod(final MemberRef method) |
||||||
|
throws NoSuchMethodException { |
||||||
|
|
||||||
|
// Check the MethodInfo cache
|
||||||
|
final MethodInfo info = (MethodInfo) methodInfos.get(method); |
||||||
|
|
||||||
|
if (info == null) { |
||||||
|
// Groan, we have to do this the HARD way.
|
||||||
|
BloatContext.db("Creating a new MethodEditor for " + method); |
||||||
|
final NameAndType nat = method.nameAndType(); |
||||||
|
final String name = nat.name(); |
||||||
|
final Type type = nat.type(); |
||||||
|
|
||||||
|
try { |
||||||
|
final ClassEditor ce = editClass(method.declaringClass()); |
||||||
|
final MethodInfo[] methods = ce.methods(); |
||||||
|
|
||||||
|
for (int i = 0; i < methods.length; i++) { |
||||||
|
final MethodEditor me = editMethod(methods[i]); |
||||||
|
|
||||||
|
if (me.name().equals(name) && me.type().equals(type)) { |
||||||
|
// The call to editMethod should have already handled
|
||||||
|
// the
|
||||||
|
// methodEditors mapping, but we still need to do
|
||||||
|
// methodInfos.
|
||||||
|
methodInfos.put(method, methods[i]); |
||||||
|
release(ce.classInfo()); |
||||||
|
return (me); |
||||||
|
} |
||||||
|
|
||||||
|
release(methods[i]); |
||||||
|
} |
||||||
|
|
||||||
|
} catch (final ClassNotFoundException ex1) { |
||||||
|
} catch (final ClassFormatException ex2) { |
||||||
|
} |
||||||
|
|
||||||
|
throw new NoSuchMethodException(method.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
return (editMethod(info)); |
||||||
|
} |
||||||
|
|
||||||
|
public MethodEditor editMethod(final MethodInfo info) { |
||||||
|
// Check methodEditors cache
|
||||||
|
MethodEditor me = (MethodEditor) methodEditors.get(info); |
||||||
|
|
||||||
|
if (me == null) { |
||||||
|
me = new MethodEditor(editClass(info.declaringClass()), info); |
||||||
|
methodEditors.put(info, me); |
||||||
|
BloatContext |
||||||
|
.db("Creating a new MethodEditor for " + me.memberRef()); |
||||||
|
} |
||||||
|
|
||||||
|
return (me); |
||||||
|
} |
||||||
|
|
||||||
|
public FieldEditor editField(final MemberRef field) |
||||||
|
throws NoSuchFieldException { |
||||||
|
|
||||||
|
// Just like we had to do with methods
|
||||||
|
final FieldInfo info = (FieldInfo) fieldInfos.get(field); |
||||||
|
|
||||||
|
if (info == null) { |
||||||
|
final NameAndType nat = field.nameAndType(); |
||||||
|
final String name = nat.name(); |
||||||
|
final Type type = nat.type(); |
||||||
|
|
||||||
|
try { |
||||||
|
final ClassEditor ce = editClass(field.declaringClass()); |
||||||
|
final FieldInfo[] fields = ce.fields(); |
||||||
|
|
||||||
|
for (int i = 0; i < fields.length; i++) { |
||||||
|
final FieldEditor fe = editField(fields[i]); |
||||||
|
|
||||||
|
if (fe.name().equals(name) && fe.type().equals(type)) { |
||||||
|
fieldInfos.put(field, fields[i]); |
||||||
|
release(ce.classInfo()); |
||||||
|
return (fe); |
||||||
|
} |
||||||
|
|
||||||
|
release(fields[i]); |
||||||
|
} |
||||||
|
} catch (final ClassNotFoundException ex1) { |
||||||
|
} catch (final ClassFormatException ex2) { |
||||||
|
} |
||||||
|
|
||||||
|
throw new NoSuchFieldException(field.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
return (editField(info)); |
||||||
|
} |
||||||
|
|
||||||
|
public FieldEditor editField(final FieldInfo info) { |
||||||
|
// Check the cache
|
||||||
|
FieldEditor fe = (FieldEditor) fieldEditors.get(info); |
||||||
|
|
||||||
|
if (fe == null) { |
||||||
|
fe = new FieldEditor(editClass(info.declaringClass()), info); |
||||||
|
fieldEditors.put(info, fe); |
||||||
|
BloatContext.db("Creating a new FieldEditor for " |
||||||
|
+ fe.nameAndType()); |
||||||
|
} |
||||||
|
|
||||||
|
return (fe); |
||||||
|
} |
||||||
|
|
||||||
|
public void release(final ClassInfo info) { |
||||||
|
// Since we keep around all data, do nothing
|
||||||
|
} |
||||||
|
|
||||||
|
public void release(final ClassEditor ce) { |
||||||
|
// Since we keep around all data, do nothing
|
||||||
|
} |
||||||
|
|
||||||
|
public void release(final MethodInfo info) { |
||||||
|
// Since we keep around all data, do nothing
|
||||||
|
} |
||||||
|
|
||||||
|
public void release(final FieldInfo info) { |
||||||
|
// Since we keep around all data, do nothing
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Classes that are ignored are not committed. |
||||||
|
* |
||||||
|
* @see #ignoreClass(Type) |
||||||
|
*/ |
||||||
|
public void commit(final ClassInfo info) { |
||||||
|
final Type type = Type.getType("L" + info.name() + ";"); |
||||||
|
if (ignoreClass(type)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
final ClassEditor ce = editClass(info); |
||||||
|
|
||||||
|
// Commit all of the class's methods and fields
|
||||||
|
final MethodInfo[] methods = ce.methods(); |
||||||
|
for (int i = 0; i < methods.length; i++) { |
||||||
|
commit(methods[i]); |
||||||
|
} |
||||||
|
|
||||||
|
final FieldInfo[] fields = ce.fields(); |
||||||
|
for (int i = 0; i < fields.length; i++) { |
||||||
|
commit(fields[i]); |
||||||
|
} |
||||||
|
|
||||||
|
ce.commit(); |
||||||
|
|
||||||
|
ce.setDirty(false); |
||||||
|
release(info); |
||||||
|
} |
||||||
|
|
||||||
|
public void commit(final MethodInfo info) { |
||||||
|
final MethodEditor me = editMethod(info); |
||||||
|
me.commit(); |
||||||
|
|
||||||
|
// We make the method's class dirty so it, too, will be committed
|
||||||
|
me.declaringClass().setDirty(true); |
||||||
|
me.setDirty(false); |
||||||
|
release(info); |
||||||
|
} |
||||||
|
|
||||||
|
public void commit(final FieldInfo info) { |
||||||
|
final FieldEditor fe = editField(info); |
||||||
|
fe.commit(); |
||||||
|
|
||||||
|
// We make the method's class dirty so it, too, will be committed
|
||||||
|
fe.declaringClass().setDirty(true); |
||||||
|
fe.setDirty(false); |
||||||
|
release(info); |
||||||
|
} |
||||||
|
|
||||||
|
public void commit() { |
||||||
|
Object[] array = fieldEditors.values().toArray(); |
||||||
|
for (int i = 0; i < array.length; i++) { |
||||||
|
final FieldEditor fe = (FieldEditor) array[i]; |
||||||
|
if (!ignoreField(fe.memberRef())) { |
||||||
|
commit(fe.fieldInfo()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
array = methodEditors.values().toArray(); |
||||||
|
for (int i = 0; i < array.length; i++) { |
||||||
|
final MethodEditor me = (MethodEditor) array[i]; |
||||||
|
if (!ignoreMethod(me.memberRef())) { |
||||||
|
commit(me.methodInfo()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
array = classEditors.values().toArray(); |
||||||
|
for (int i = 0; i < array.length; i++) { |
||||||
|
final ClassEditor ce = (ClassEditor) array[i]; |
||||||
|
if (!ignoreClass(ce.type())) { |
||||||
|
commit(ce.classInfo()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void commitDirty() { |
||||||
|
PersistentBloatContext.comm("Committing dirty data"); |
||||||
|
|
||||||
|
// Commit all dirty fields
|
||||||
|
Object[] array = this.fieldEditors.values().toArray(); |
||||||
|
for (int i = 0; i < array.length; i++) { |
||||||
|
final FieldEditor fe = (FieldEditor) array[i]; |
||||||
|
if (fe.isDirty() && !ignoreField(fe.memberRef())) { |
||||||
|
PersistentBloatContext.comm(" Committing field: " |
||||||
|
+ fe.declaringClass().name() + "." + fe.name()); |
||||||
|
commit(fe.fieldInfo()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Commit all dirty methods
|
||||||
|
array = this.methodEditors.values().toArray(); |
||||||
|
for (int i = 0; i < array.length; i++) { |
||||||
|
final MethodEditor me = (MethodEditor) array[i]; |
||||||
|
if (me.isDirty() && !ignoreMethod(me.memberRef())) { |
||||||
|
PersistentBloatContext.comm(" Committing method: " |
||||||
|
+ me.declaringClass().name() + "." + me.name() |
||||||
|
+ me.type()); |
||||||
|
commit(me.methodInfo()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Commit all dirty classes
|
||||||
|
array = this.classEditors.values().toArray(); |
||||||
|
for (int i = 0; i < array.length; i++) { |
||||||
|
final ClassEditor ce = (ClassEditor) array[i]; |
||||||
|
if (ce.isDirty() && !ignoreClass(ce.type())) { |
||||||
|
PersistentBloatContext.comm(" Committing class: " + ce.name()); |
||||||
|
commit(ce.classInfo()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
<html> |
||||||
|
<body> |
||||||
|
|
||||||
|
<p>Repositories for important BLOAT objects such as the |
||||||
|
<tt>Editor</tt>, <tt>ClassHierarchy</tt>, and <tt>FlowGraph</tt>s. In |
||||||
|
order to avoid build dependencies with other packages we took the |
||||||
|
novel approach of having a "context" interface in several packages that |
||||||
|
the classes in this package then implement.</p> |
||||||
|
|
||||||
|
<p>The reason we implement all of the methods in one class is so that |
||||||
|
if the user wants to have different contexts with different attributes |
||||||
|
(for instance, caching policies), all one has to do is subclass |
||||||
|
<tt>BloatContext</tt> instead of subclassing other contextes which may |
||||||
|
become ackward.</p> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1 @@ |
|||||||
|
*.class |
@ -0,0 +1,727 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.decorate; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.context.*; |
||||||
|
import EDU.purdue.cs.bloat.editor.*; |
||||||
|
import EDU.purdue.cs.bloat.file.*; |
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
import EDU.purdue.cs.bloat.trans.*; |
||||||
|
import EDU.purdue.cs.bloat.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Inserts residency, update, or swizzle checks into the methods of the classes |
||||||
|
* specified on the command line. |
||||||
|
* |
||||||
|
* Usage: java EDU.purdue.cs.bloat.decorate.Main [-options] classes output_dir |
||||||
|
* |
||||||
|
* where options include: -help print out this message -v -verbose turn on |
||||||
|
* verbose mode (can be given multiple times) -classpath <directories separated |
||||||
|
* by colons list directories in which to look for classes -f decorate files |
||||||
|
* even if up-to-date -closure recursively decorate referenced classes |
||||||
|
* -relax-loading don't report errors if a class is not found -skip |
||||||
|
* <class|package.*> skip the given class or package (this option can be given |
||||||
|
* more than once) -only <class|package.*> skip all but the given class or |
||||||
|
* package (this option can be given more than once) -rc insert residency checks |
||||||
|
* (default) -norc don't insert residency checks -uc insert update checks |
||||||
|
* (default) -sc insert array swizzle checks (default) -nosc don't insert array |
||||||
|
* swizzle checkso |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class Main implements Opcode { |
||||||
|
private static int VERBOSE = 0; // The level of verbosity
|
||||||
|
|
||||||
|
private static boolean FORCE = false; |
||||||
|
|
||||||
|
private static boolean CLOSURE = false; |
||||||
|
|
||||||
|
private static boolean RC = true; // Insert residency checks?
|
||||||
|
|
||||||
|
private static boolean UC = true; // Insert update checks?
|
||||||
|
|
||||||
|
private static boolean SC = true; // Insert swizzle checks?
|
||||||
|
|
||||||
|
private static final List SKIP = new ArrayList(); |
||||||
|
|
||||||
|
private static final List ONLY = new ArrayList(); |
||||||
|
|
||||||
|
private static final int NONE = 0; |
||||||
|
|
||||||
|
private static final int POINTER = 1; |
||||||
|
|
||||||
|
private static final int SCALAR = 2; |
||||||
|
|
||||||
|
/** |
||||||
|
* Parse the command line. Inserts residency, update, and swizzle checks |
||||||
|
* into the bytecode of the methods of the specified classes. |
||||||
|
*/ |
||||||
|
public static void main(final String[] args) { |
||||||
|
final ClassFileLoader loader = new ClassFileLoader(); |
||||||
|
List classes = new ArrayList(); // Names of classes from command line
|
||||||
|
boolean gotdir = false; // Did user specify an output dir?
|
||||||
|
|
||||||
|
for (int i = 0; i < args.length; i++) { |
||||||
|
if (args[i].equals("-v") || args[i].equals("-verbose")) { |
||||||
|
Main.VERBOSE++; |
||||||
|
} else if (args[i].equals("-help")) { |
||||||
|
Main.usage(); |
||||||
|
} else if (args[i].equals("-classpath")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final String classpath = args[i]; |
||||||
|
loader.setClassPath(classpath); |
||||||
|
} else if (args[i].equals("-skip")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final String pkg = args[i].replace('.', '/'); |
||||||
|
Main.SKIP.add(pkg); |
||||||
|
} else if (args[i].equals("-only")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final String pkg = args[i].replace('.', '/'); |
||||||
|
Main.ONLY.add(pkg); |
||||||
|
} else if (args[i].equals("-closure")) { |
||||||
|
Main.CLOSURE = true; |
||||||
|
} else if (args[i].equals("-relax-loading")) { |
||||||
|
ClassHierarchy.RELAX = true; |
||||||
|
} else if (args[i].equals("-f")) { |
||||||
|
Main.FORCE = true; |
||||||
|
} else if (args[i].equals("-norc")) { |
||||||
|
Main.RC = false; |
||||||
|
} else if (args[i].equals("-rc")) { |
||||||
|
Main.RC = true; |
||||||
|
} else if (args[i].equals("-nouc")) { |
||||||
|
Main.UC = false; |
||||||
|
} else if (args[i].equals("-uc")) { |
||||||
|
Main.UC = true; |
||||||
|
} else if (args[i].equals("-nosc")) { |
||||||
|
Main.SC = false; |
||||||
|
} else if (args[i].equals("-sc")) { |
||||||
|
Main.SC = true; |
||||||
|
} else if (args[i].startsWith("-")) { |
||||||
|
Main.usage(); |
||||||
|
} else if (i == args.length - 1) { |
||||||
|
// Last argument is the name of the outpu directory
|
||||||
|
final File f = new File(args[i]); |
||||||
|
|
||||||
|
if (f.exists() && !f.isDirectory()) { |
||||||
|
System.err.println("No such directory: " + f.getPath()); |
||||||
|
System.exit(2); |
||||||
|
} |
||||||
|
|
||||||
|
loader.setOutputDir(f); |
||||||
|
gotdir = true; |
||||||
|
} else { |
||||||
|
classes.add(args[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!gotdir) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
if (classes.size() == 0) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.VERBOSE > 3) { |
||||||
|
ClassFileLoader.DEBUG = true; |
||||||
|
ClassEditor.DEBUG = true; |
||||||
|
} |
||||||
|
|
||||||
|
boolean errors = false; |
||||||
|
|
||||||
|
final Iterator iter = classes.iterator(); |
||||||
|
|
||||||
|
// Load each class specified on the command line
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final String name = (String) iter.next(); |
||||||
|
|
||||||
|
try { |
||||||
|
loader.loadClass(name); |
||||||
|
} catch (final ClassNotFoundException ex) { |
||||||
|
System.err.println("Couldn't find class: " + ex.getMessage()); |
||||||
|
errors = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (errors) { |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
final BloatContext context = new CachingBloatContext(loader, classes, |
||||||
|
Main.CLOSURE); |
||||||
|
|
||||||
|
if (!Main.CLOSURE) { |
||||||
|
final Iterator e = classes.iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final String name = (String) e.next(); |
||||||
|
try { |
||||||
|
final ClassInfo info = loader.loadClass(name); |
||||||
|
Main.decorateClass(context, info); |
||||||
|
} catch (final ClassNotFoundException ex) { |
||||||
|
System.err.println("Couldn't find class: " |
||||||
|
+ ex.getMessage()); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
classes = null; |
||||||
|
|
||||||
|
final ClassHierarchy hier = context.getHierarchy(); |
||||||
|
|
||||||
|
final Iterator e = hier.classes().iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Type t = (Type) e.next(); |
||||||
|
|
||||||
|
if (t.isObject()) { |
||||||
|
try { |
||||||
|
final ClassInfo info = loader.loadClass(t.className()); |
||||||
|
Main.decorateClass(context, info); |
||||||
|
} catch (final ClassNotFoundException ex) { |
||||||
|
System.err.println("Couldn't find class: " |
||||||
|
+ ex.getMessage()); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void usage() { |
||||||
|
System.err |
||||||
|
.println("Usage: java EDU.purdue.cs.bloat.decorate.Main " |
||||||
|
+ "\n [-options] classes output_dir" |
||||||
|
+ "\n" |
||||||
|
+ "\nwhere options include:" |
||||||
|
+ "\n -help print out this message" |
||||||
|
+ "\n -v -verbose turn on verbose mode " |
||||||
|
+ "(can be given multiple times)" |
||||||
|
+ "\n -classpath <directories separated by colons>" |
||||||
|
+ "\n list directories in which to look for classes" |
||||||
|
+ "\n -f decorate files even if up-to-date" |
||||||
|
+ "\n -closure recursively decorate referenced classes" |
||||||
|
+ "\n -relax-loading don't report errors if a class is not found" |
||||||
|
+ "\n -skip <class|package.*>" |
||||||
|
+ "\n skip the given class or package" |
||||||
|
+ "\n (this option can be given more than once)" |
||||||
|
+ "\n -only <class|package.*>" |
||||||
|
+ "\n skip all but the given class or package" |
||||||
|
+ "\n (this option can be given more than once)" |
||||||
|
+ "\n -rc insert residency checks (default)" |
||||||
|
+ "\n -norc don't insert residency checks" |
||||||
|
+ "\n -uc insert update checks (default)" |
||||||
|
+ "\n -sc insert array swizzle checks (default)" |
||||||
|
+ "\n -nosc don't insert array swizzle checks"); |
||||||
|
System.exit(0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds residency/update/swizzle checks to all of the methods in a given |
||||||
|
* class. |
||||||
|
* |
||||||
|
* @param context |
||||||
|
* Information about all the classes we're dealing with |
||||||
|
* @param info |
||||||
|
* Information about the class we're decorating |
||||||
|
*/ |
||||||
|
private static void decorateClass(final EditorContext context, |
||||||
|
final ClassInfo info) { |
||||||
|
final ClassFile classFile = (ClassFile) info; |
||||||
|
|
||||||
|
// Check to see if the class file is up-to-date
|
||||||
|
if (!Main.FORCE) { |
||||||
|
final File source = classFile.file(); |
||||||
|
final File target = classFile.outputFile(); |
||||||
|
|
||||||
|
if ((source != null) && (target != null) && source.exists() |
||||||
|
&& target.exists() |
||||||
|
&& (source.lastModified() < target.lastModified())) { |
||||||
|
|
||||||
|
if (Main.VERBOSE > 1) { |
||||||
|
System.out.println(classFile.name() + " is up to date"); |
||||||
|
} |
||||||
|
|
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.VERBOSE > 2) { |
||||||
|
classFile.print(System.out); |
||||||
|
} |
||||||
|
|
||||||
|
final ClassEditor c = context.editClass(info); |
||||||
|
|
||||||
|
boolean skip = false; |
||||||
|
|
||||||
|
final String name = c.type().className(); |
||||||
|
final String qual = c.type().qualifier() + "/*"; |
||||||
|
|
||||||
|
// Edit only classes explicitly mentioned.
|
||||||
|
if (Main.ONLY.size() > 0) { |
||||||
|
skip = true; |
||||||
|
|
||||||
|
// Only edit classes we explicitly don't name.
|
||||||
|
for (int i = 0; i < Main.ONLY.size(); i++) { |
||||||
|
final String pkg = (String) Main.ONLY.get(i); |
||||||
|
|
||||||
|
if (name.equals(pkg) || qual.equals(pkg)) { |
||||||
|
skip = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Don't edit classes we explicitly skip.
|
||||||
|
if (!skip) { |
||||||
|
for (int i = 0; i < Main.SKIP.size(); i++) { |
||||||
|
final String pkg = (String) Main.SKIP.get(i); |
||||||
|
|
||||||
|
if (name.equals(pkg) || qual.equals(pkg)) { |
||||||
|
skip = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (skip) { |
||||||
|
if (Main.VERBOSE > 0) { |
||||||
|
System.out.println("Skipping " + c.type().className()); |
||||||
|
} |
||||||
|
|
||||||
|
context.release(info); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.VERBOSE > 0) { |
||||||
|
System.out.println("Decorating class " + c.type().className()); |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.VERBOSE > 2) { |
||||||
|
((ClassFile) info).print(System.out); |
||||||
|
} |
||||||
|
|
||||||
|
final MethodInfo[] methods = c.methods(); |
||||||
|
|
||||||
|
// Add residency checks (via transform()) to each method in the class
|
||||||
|
for (int j = 0; j < methods.length; j++) { |
||||||
|
MethodEditor m; |
||||||
|
|
||||||
|
try { |
||||||
|
m = context.editMethod(methods[j]); |
||||||
|
} catch (final ClassFormatException ex) { |
||||||
|
System.err.println(ex.getMessage()); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
Main.transform(m); |
||||||
|
context.commit(methods[j]); |
||||||
|
} |
||||||
|
|
||||||
|
context.commit(info); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Inserts residency/update/swizzle checks into a method. Iterates over the |
||||||
|
* bytecodes in the method and inserts the appropriate residency opcode. |
||||||
|
* |
||||||
|
* @param method |
||||||
|
* The method to which to add checks. |
||||||
|
* |
||||||
|
* @see MethodEditor#code |
||||||
|
*/ |
||||||
|
private static void transform(final MethodEditor method) { |
||||||
|
if (Main.VERBOSE > 1) { |
||||||
|
System.out.println("Decorating method " + method); |
||||||
|
} |
||||||
|
|
||||||
|
// Optimize initialization of arrays to speed things up.
|
||||||
|
CompactArrayInitializer.transform(method); |
||||||
|
|
||||||
|
final ListIterator iter = method.code().listIterator(); |
||||||
|
|
||||||
|
// Go through the code (Instructions and Labels) in the method
|
||||||
|
INST: while (iter.hasNext()) { |
||||||
|
final Object ce = iter.next(); |
||||||
|
|
||||||
|
if (Main.VERBOSE > 2) { |
||||||
|
System.out.println("Examining " + ce); |
||||||
|
} |
||||||
|
|
||||||
|
if (ce instanceof Instruction) { |
||||||
|
final Instruction inst = (Instruction) ce; |
||||||
|
|
||||||
|
int uctype = Main.NONE; // Type of update check (POINTER or
|
||||||
|
// SCALAR)
|
||||||
|
boolean insert_sc = false; // Insert swizzle check (aaload
|
||||||
|
// only)?
|
||||||
|
|
||||||
|
final int opc = inst.opcodeClass(); |
||||||
|
int depth; |
||||||
|
|
||||||
|
switch (opc) { |
||||||
|
case opcx_arraylength: |
||||||
|
case opcx_athrow: |
||||||
|
case opcx_getfield: |
||||||
|
case opcx_instanceof: { |
||||||
|
depth = 0; |
||||||
|
break; |
||||||
|
} |
||||||
|
case opcx_iaload: |
||||||
|
case opcx_laload: |
||||||
|
case opcx_faload: |
||||||
|
case opcx_daload: |
||||||
|
case opcx_baload: |
||||||
|
case opcx_caload: |
||||||
|
case opcx_saload: { |
||||||
|
depth = 1; |
||||||
|
break; |
||||||
|
} |
||||||
|
case opcx_aaload: { |
||||||
|
depth = 1; |
||||||
|
insert_sc = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
case opcx_iastore: |
||||||
|
case opcx_fastore: |
||||||
|
case opcx_aastore: |
||||||
|
case opcx_bastore: |
||||||
|
case opcx_castore: |
||||||
|
case opcx_sastore: { |
||||||
|
depth = 2; |
||||||
|
break; |
||||||
|
} |
||||||
|
case opcx_lastore: |
||||||
|
case opcx_dastore: { |
||||||
|
depth = 3; |
||||||
|
break; |
||||||
|
} |
||||||
|
case opcx_putfield: { |
||||||
|
final MemberRef ref = (MemberRef) inst.operand(); |
||||||
|
depth = ref.type().stackHeight(); |
||||||
|
if (ref.type().isReference()) { |
||||||
|
uctype = Main.POINTER; |
||||||
|
} else { |
||||||
|
uctype = Main.SCALAR; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
case opcx_invokevirtual: |
||||||
|
case opcx_invokespecial: |
||||||
|
case opcx_invokeinterface: { |
||||||
|
final MemberRef ref = (MemberRef) inst.operand(); |
||||||
|
depth = ref.type().stackHeight(); |
||||||
|
break; |
||||||
|
} |
||||||
|
case opcx_rc: { |
||||||
|
// Skip any existing residency checks.
|
||||||
|
iter.remove(); |
||||||
|
continue INST; |
||||||
|
} |
||||||
|
case opcx_aupdate: { |
||||||
|
// Skip any existing update checks.
|
||||||
|
iter.remove(); |
||||||
|
continue INST; |
||||||
|
} |
||||||
|
case opcx_supdate: { |
||||||
|
// Skip any existing update checks.
|
||||||
|
iter.remove(); |
||||||
|
continue INST; |
||||||
|
} |
||||||
|
default: { |
||||||
|
continue INST; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Instruction addInst; |
||||||
|
|
||||||
|
// Insert a residency check...
|
||||||
|
if (Main.RC) { |
||||||
|
Object t; |
||||||
|
|
||||||
|
// //////////////////////////////////
|
||||||
|
// Before...
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// | ... | inst | afterInst |
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
//
|
||||||
|
// After...
|
||||||
|
// +-----+----+------+-----------+
|
||||||
|
// | ... | RC | inst | afterInst |
|
||||||
|
// +-----+----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
// //////////////////////////////////
|
||||||
|
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// | ... | inst | afterInst |
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.previous(); |
||||||
|
Assert.isTrue(t == inst, t + " != " + inst); |
||||||
|
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// | ... | inst | afterInst |
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
addInst = new Instruction(Opcode.opcx_rc, |
||||||
|
new Integer(depth)); |
||||||
|
iter.add(addInst); |
||||||
|
|
||||||
|
// +-----+----+------+-----------+
|
||||||
|
// | ... | RC | inst | afterInst |
|
||||||
|
// +-----+----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.previous(); |
||||||
|
Assert.isTrue(t == addInst, t + " != " + addInst); |
||||||
|
|
||||||
|
// +-----+----+------+-----------+
|
||||||
|
// | ... | RC | inst | afterInst |
|
||||||
|
// +-----+----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.next(); |
||||||
|
Assert.isTrue(t == addInst, t + " != " + addInst); |
||||||
|
|
||||||
|
// +-----+----+------+-----------+
|
||||||
|
// | ... | RC | inst | afterInst |
|
||||||
|
// +-----+----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.next(); |
||||||
|
Assert.isTrue(t == inst, t + " != " + inst); |
||||||
|
|
||||||
|
// +-----+----+------+-----------+
|
||||||
|
// | ... | RC | inst | afterInst |
|
||||||
|
// +-----+----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
if (Main.VERBOSE > 2) { |
||||||
|
System.out.println("Inserting " + addInst + " before " |
||||||
|
+ inst); |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (Main.VERBOSE > 2) { |
||||||
|
System.out.println("Not inserting rc before " + inst); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Insert a swizzle check...
|
||||||
|
if (insert_sc) { |
||||||
|
if (Main.SC) { |
||||||
|
Object t; |
||||||
|
|
||||||
|
// ////////////////////////////////////////////
|
||||||
|
// Before...
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// | ... | inst | afterInst |
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
//
|
||||||
|
// After...
|
||||||
|
// +-----+------+----------+------+-----------+
|
||||||
|
// | ... | dup2 | aswizzle | inst | afterInst |
|
||||||
|
// +-----+------+----------+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
// /////////////////////////////////////////////
|
||||||
|
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// | ... | inst | afterInst |
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.previous(); |
||||||
|
Assert.isTrue(t == inst, t + " != " + inst); |
||||||
|
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// | ... | inst | afterInst |
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
addInst = new Instruction(Opcode.opcx_dup2); |
||||||
|
iter.add(addInst); |
||||||
|
|
||||||
|
// +-----+------+------+-----------+
|
||||||
|
// | ... | dup2 | inst | afterInst |
|
||||||
|
// +-----+------+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.previous(); |
||||||
|
Assert.isTrue(t == addInst, t + " != " + addInst); |
||||||
|
|
||||||
|
// +-----+------+------+-----------+
|
||||||
|
// | ... | dup2 | inst | afterInst |
|
||||||
|
// +-----+------+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.next(); |
||||||
|
Assert.isTrue(t == addInst, t + " != " + addInst); |
||||||
|
|
||||||
|
// +-----+------+------+-----------+
|
||||||
|
// | ... | dup2 | inst | afterInst |
|
||||||
|
// +-----+------+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
addInst = new Instruction(Opcode.opcx_aswizzle); |
||||||
|
iter.add(addInst); |
||||||
|
|
||||||
|
// +-----+------+----------+------+-----------+
|
||||||
|
// | ... | dup2 | aswizzle | inst | afterInst |
|
||||||
|
// +-----+------+----------+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.previous(); |
||||||
|
Assert.isTrue(t == addInst, t + " != " + addInst); |
||||||
|
|
||||||
|
// +-----+------+----------+------+-----------+
|
||||||
|
// | ... | dup2 | aswizzle | inst | afterInst |
|
||||||
|
// +-----+------+----------+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.next(); |
||||||
|
Assert.isTrue(t == addInst, t + " != " + addInst); |
||||||
|
|
||||||
|
// +-----+------+----------+------+-----------+
|
||||||
|
// | ... | dup2 | aswizzle | inst | afterInst |
|
||||||
|
// +-----+------+----------+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.next(); |
||||||
|
Assert.isTrue(t == inst, t + " != " + inst); |
||||||
|
|
||||||
|
// +-----+------+----------+------+-----------+
|
||||||
|
// | ... | dup2 | aswizzle | inst | afterInst |
|
||||||
|
// +-----+------+----------+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
if (Main.VERBOSE > 2) { |
||||||
|
System.out |
||||||
|
.println("Inserting dup2,aswizzle before " |
||||||
|
+ inst); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
else { |
||||||
|
if (Main.VERBOSE > 2) { |
||||||
|
System.out.println("Not inserting aswizzle before " |
||||||
|
+ inst); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Insert an update check...
|
||||||
|
if (uctype != Main.NONE) { |
||||||
|
if (Main.UC) { |
||||||
|
Object t; |
||||||
|
|
||||||
|
// ////////////////////////////////////////////
|
||||||
|
// Before...
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// | ... | inst | afterInst |
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
//
|
||||||
|
// After...
|
||||||
|
// +-----+---------+------+-----------+
|
||||||
|
// | ... | aupdate | inst | afterInst |
|
||||||
|
// +-----+---------+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
// /////////////////////////////////////////////
|
||||||
|
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// | ... | inst | afterInst |
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.previous(); |
||||||
|
Assert.isTrue(t == inst, t + " != " + inst); |
||||||
|
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// | ... | inst | afterInst |
|
||||||
|
// +-----+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
addInst = new Instruction(Opcode.opcx_aupdate, |
||||||
|
new Integer(depth)); |
||||||
|
/* |
||||||
|
* if (uctype == POINTER) { addInst = new |
||||||
|
* Instruction(opcx_aupdate, new Integer(depth)); } else { |
||||||
|
* addInst = new Instruction(opcx_supdate, new |
||||||
|
* Integer(depth)); } |
||||||
|
*/ |
||||||
|
|
||||||
|
iter.add(addInst); |
||||||
|
|
||||||
|
// +-----+---------+------+-----------+
|
||||||
|
// | ... | aupdate | inst | afterInst |
|
||||||
|
// +-----+---------+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.previous(); |
||||||
|
Assert.isTrue(t == addInst, t + " != " + addInst); |
||||||
|
|
||||||
|
// +-----+---------+------+-----------+
|
||||||
|
// | ... | aupdate | inst | afterInst |
|
||||||
|
// +-----+---------+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.next(); |
||||||
|
Assert.isTrue(t == addInst, t + " != " + addInst); |
||||||
|
|
||||||
|
// +-----+---------+------+-----------+
|
||||||
|
// | ... | aupdate | inst | afterInst |
|
||||||
|
// +-----+---------+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
t = iter.next(); |
||||||
|
Assert.isTrue(t == inst, t + " != " + inst); |
||||||
|
|
||||||
|
// +-----+---------+------+-----------+
|
||||||
|
// | ... | aupdate | inst | afterInst |
|
||||||
|
// +-----+---------+------+-----------+
|
||||||
|
// ^prev ^next
|
||||||
|
|
||||||
|
if (Main.VERBOSE > 2) { |
||||||
|
System.out.println("Inserting " + addInst |
||||||
|
+ " before " + inst); |
||||||
|
} |
||||||
|
} else if (Main.VERBOSE > 2) { |
||||||
|
System.out.println("Not inserting uc before " + inst); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
# All files in the distribution of BLOAT (Bytecode Level Optimization and
|
||||||
|
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
||||||
|
# Research Foundation of Purdue University. All rights reserved.
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
|
||||||
|
CLASS = \
|
||||||
|
Main.class
|
||||||
|
|
||||||
|
include ../class.mk |
@ -0,0 +1,8 @@ |
|||||||
|
<html> |
||||||
|
<body> |
||||||
|
|
||||||
|
<p>Contains a program that decorates a Java classfile with opcodes |
||||||
|
used in a persistent Java virtual machine.</p> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1 @@ |
|||||||
|
*.class |
@ -0,0 +1,514 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Demand-driven Induction Variable Analysis (diva)*/ |
||||||
|
package EDU.purdue.cs.bloat.diva; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.cfg.*; |
||||||
|
import EDU.purdue.cs.bloat.ssa.*; |
||||||
|
import EDU.purdue.cs.bloat.tree.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* InductionVarAnalyzer traverses a control flow graph and looks for array |
||||||
|
* element swizzle operations inside loops. If possible, these swizzle |
||||||
|
* operations are hoisted out of the loop and are replaced with range swizzle |
||||||
|
* operations. The technique used is Demand-driven Induction Variable Analysis |
||||||
|
* (DIVA). |
||||||
|
* <p> |
||||||
|
* To accomplish its tasks, InductionVarAnalyzer keeps track of a number of |
||||||
|
* induction variables (represented by Swizzler objects) and local variables. |
||||||
|
* |
||||||
|
* @see Swizzler |
||||||
|
* @see LocalExpr |
||||||
|
*/ |
||||||
|
public class InductionVarAnalyzer { |
||||||
|
public static boolean DEBUG = false; |
||||||
|
|
||||||
|
SSAGraph ssaGraph; |
||||||
|
|
||||||
|
FlowGraph CFG; // Control flow graph being operated on
|
||||||
|
|
||||||
|
HashMap IndStore; // Stores induction variables and
|
||||||
|
|
||||||
|
// associated swizzlers
|
||||||
|
HashMap LocalStore; // Stores local variables
|
||||||
|
|
||||||
|
Expr ind_var = null; // An induction variable
|
||||||
|
|
||||||
|
Expr ind_init = null; // Initial value of an induction variable
|
||||||
|
|
||||||
|
Expr ind_term = null; // Not used???
|
||||||
|
|
||||||
|
Expr ind_inc = null; // Expression used to increment induction
|
||||||
|
|
||||||
|
// variable (all uses commented out)
|
||||||
|
Expr tgt = null; // Target of the phi statement that defines
|
||||||
|
|
||||||
|
// an induction var
|
||||||
|
|
||||||
|
boolean changed = false; // Was the cfg changed?
|
||||||
|
|
||||||
|
/** |
||||||
|
* Searches the list of induction variables for an induction variable with a |
||||||
|
* given value number. |
||||||
|
* |
||||||
|
* @param vn |
||||||
|
* Value number to search for. |
||||||
|
* |
||||||
|
* @return Swizzler object whose target has the desired value number or |
||||||
|
* null, if value number is not found. |
||||||
|
*/ |
||||||
|
public Object get_swizzler(final int vn) { |
||||||
|
final Iterator iter = IndStore.values().iterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final Swizzler swz = (Swizzler) iter.next(); |
||||||
|
if ((swz.target().valueNumber() == vn) |
||||||
|
|| (swz.ind_var().valueNumber() == vn)) { |
||||||
|
return swz; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Searchs the stored list of local variables for a local variable with a |
||||||
|
* given value number. |
||||||
|
* |
||||||
|
* @param vn |
||||||
|
* The value number to search for. |
||||||
|
* |
||||||
|
* @return The local variable with the given value number, or null, if not |
||||||
|
* found. |
||||||
|
*/ |
||||||
|
public MemExpr get_local(final int vn) { |
||||||
|
final Iterator iter = LocalStore.keySet().iterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final MemExpr le = (MemExpr) iter.next(); |
||||||
|
if (le.valueNumber() == vn) { |
||||||
|
return le; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Displays (to System.out) all of the Swizzlers stored in the induction |
||||||
|
* variable store. |
||||||
|
* |
||||||
|
* @see Swizzler |
||||||
|
*/ |
||||||
|
public void Display_store() { |
||||||
|
final Iterator iter = IndStore.values().iterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final Swizzler indswz = (Swizzler) iter.next(); |
||||||
|
|
||||||
|
System.out.println("\nIV: " + indswz.ind_var() + " tgt: " |
||||||
|
+ indswz.target() + "\narray: " + indswz.array() |
||||||
|
+ " init: " + indswz.init_val() + " end: " |
||||||
|
+ indswz.end_val()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Displays the contents of a Swizzler object to System.out. |
||||||
|
* |
||||||
|
* @param indswz |
||||||
|
* The Swizzler to display. |
||||||
|
*/ |
||||||
|
public void displaySwizzler(final Swizzler indswz) { |
||||||
|
System.out.println("\nIV: " + indswz.ind_var() + "vn:" |
||||||
|
+ indswz.ind_var().valueNumber() + " tgt: " + indswz.target() |
||||||
|
+ "vn:" + indswz.target().valueNumber() + "\narray: " |
||||||
|
+ indswz.array() + " init: " + indswz.init_val() + " end: " |
||||||
|
+ indswz.end_val()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds a swizzle range statement (SRStmt) to the end of each predacessor |
||||||
|
* block of the block containing the phi statement that defines an induction |
||||||
|
* variable provided that the phi block does not dominate its predacessor. |
||||||
|
* Phew. |
||||||
|
* |
||||||
|
* @param indswz |
||||||
|
* Swizzler representing the induction variable on which the |
||||||
|
* range swizzle statement is added. |
||||||
|
*/ |
||||||
|
public void insert_aswrange(final Swizzler indswz) { |
||||||
|
final Iterator iter = CFG.preds(indswz.phi_block()).iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final Block blk = (Block) iter.next(); |
||||||
|
if (!indswz.phi_block().dominates(blk)) { |
||||||
|
final SRStmt aswrange = new SRStmt((Expr) indswz.array() |
||||||
|
.clone(), (Expr) indswz.init_val().clone(), |
||||||
|
(Expr) indswz.end_val().clone()); |
||||||
|
blk.tree().addStmtBeforeJump(aswrange); |
||||||
|
changed = true; |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("Inserted ASWR: " + aswrange |
||||||
|
+ "\nin block: " + blk); |
||||||
|
|
||||||
|
System.out.println("$$$ can insert aswrange now\n" |
||||||
|
+ "array: " + indswz.array() + "\nIV: " |
||||||
|
+ indswz.ind_var() + "\ninit: " + indswz.init_val() |
||||||
|
+ "\nend: " + indswz.end_val()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* To determine if a phi statement is a mu */ |
||||||
|
/* Returns null if not a MU and sets ind_var & ind_init */ |
||||||
|
/* to refer to the IV & its initial value otherwise */ |
||||||
|
|
||||||
|
/** |
||||||
|
* Determines whether or not a phi statement is a mu function. A mu function |
||||||
|
* is a phi function that merges values at loop headers, as opposed to those |
||||||
|
* that occur as a result of forward branching. Mu functions always have two |
||||||
|
* arguments. |
||||||
|
* |
||||||
|
* @param phi |
||||||
|
* phi statement that may be a mu function |
||||||
|
* @param cfg |
||||||
|
* CFG to look through <font color="ff0000">Get rid of this |
||||||
|
* parameter</font> |
||||||
|
* |
||||||
|
* @return The block containing the mu functions external (that is, outside |
||||||
|
* the loop) argument, also known as the external ssalink. If the |
||||||
|
* phi statement is not a mu function, null is returned. |
||||||
|
*/ |
||||||
|
public Block isMu(final PhiJoinStmt phi, final FlowGraph cfg) { |
||||||
|
// Does it contain two operands?
|
||||||
|
if (phi.numOperands() != 2) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
// Is it REDUCIBLE?
|
||||||
|
if ((cfg.blockType(phi.block()) == Block.IRREDUCIBLE) |
||||||
|
|| (cfg.blockType(phi.block()) == Block.NON_HEADER)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* Does one of them dominate the phi and the other dominated by the phi? |
||||||
|
*/ |
||||||
|
|
||||||
|
final Iterator iter = cfg.preds(phi.block()).iterator(); |
||||||
|
final Block pred1 = (Block) iter.next(); |
||||||
|
final Block pred2 = (Block) iter.next(); |
||||||
|
|
||||||
|
if (pred1.dominates(phi.block()) && phi.block().dominates(pred2) |
||||||
|
&& (pred1 != phi.block())) { |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("Extlink = 1 pred1:" + pred1 + " pred2:" |
||||||
|
+ pred2); |
||||||
|
} |
||||||
|
ind_var = phi.operandAt(pred2); |
||||||
|
ind_init = phi.operandAt(pred1); |
||||||
|
return pred1; |
||||||
|
} |
||||||
|
if (pred2.dominates(phi.block()) && phi.block().dominates(pred1) |
||||||
|
&& (pred2 != phi.block())) { |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("Extlink = 2 pred1:" + pred1 + " pred2:" |
||||||
|
+ pred2); |
||||||
|
} |
||||||
|
ind_var = phi.operandAt(pred1); |
||||||
|
ind_init = phi.operandAt(pred2); |
||||||
|
return pred2; |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Performs DIVA on a CFG public static void transform(FlowGraph cfg) { //
|
||||||
|
* Create a new instance to allow multiple threads. InductionVarAnalyzer me = |
||||||
|
* new InductionVarAnalyzer(); me.transform(cfg); } |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Performs DIVA on a CFG. |
||||||
|
*/ |
||||||
|
public void transform(final FlowGraph cfg) { |
||||||
|
ssaGraph = new SSAGraph(cfg); |
||||||
|
CFG = cfg; |
||||||
|
IndStore = new HashMap(); |
||||||
|
LocalStore = new HashMap(); |
||||||
|
changed = false; |
||||||
|
|
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out |
||||||
|
.println("----------Before visitComponents--------------"); |
||||||
|
cfg.print(System.out); |
||||||
|
} |
||||||
|
|
||||||
|
// Visit each strongly connected component (SCC) in the CFG. SCCs
|
||||||
|
// correspond to sequences in the program. Visit each node in the
|
||||||
|
// SCC and build the local variable store. Create Swizzlers at
|
||||||
|
// PhiStmts, if approproate, and store them in the induction
|
||||||
|
// variable store. If it can be determined that an array element
|
||||||
|
// swizzle can be hoisted out of a loop, it is hoisted.
|
||||||
|
ssaGraph.visitComponents(new ComponentVisitor() { |
||||||
|
public void visitComponent(final List scc) { |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("SCC ="); |
||||||
|
} |
||||||
|
|
||||||
|
final Iterator e = scc.iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Node v = (Node) e.next(); |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println(" " + v + "{" + v.key() + "} " |
||||||
|
+ v.getClass()); |
||||||
|
} |
||||||
|
|
||||||
|
v.visit(new TreeVisitor() { |
||||||
|
public void visitPhiJoinStmt(final PhiJoinStmt phi) { |
||||||
|
if (isMu(phi, CFG) != null) { |
||||||
|
tgt = phi.target(); |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("IV:" + ind_var + " VN:" |
||||||
|
+ ind_var.valueNumber() + "\ninit:" |
||||||
|
+ ind_init + " target: " + tgt |
||||||
|
+ " VN: " + tgt.valueNumber()); |
||||||
|
} |
||||||
|
final Swizzler swz = new Swizzler(ind_var, tgt, |
||||||
|
ind_init, phi.block()); |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out |
||||||
|
.println("store swizzler for " |
||||||
|
+ ind_var.def() + " & " |
||||||
|
+ tgt.def()); |
||||||
|
displaySwizzler(swz); |
||||||
|
} |
||||||
|
|
||||||
|
if (ind_var.def() != null) { |
||||||
|
IndStore.put(ind_var.def(), swz); |
||||||
|
} |
||||||
|
|
||||||
|
if (tgt.def() != null) { |
||||||
|
IndStore.put(tgt.def(), swz); |
||||||
|
} |
||||||
|
|
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println(" Mu: " + phi + "{" |
||||||
|
+ phi.key() + "}"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("Phi: " + phi + "{" |
||||||
|
+ phi.key() + "}"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitLocalExpr(final LocalExpr me) { |
||||||
|
if (me.def() != null) { |
||||||
|
if (LocalStore.get(me.def()) == null) { |
||||||
|
LocalStore.put(me.def(), me); |
||||||
|
} |
||||||
|
} |
||||||
|
if (LocalStore.get(me) == null) { |
||||||
|
LocalStore.put(me, me); |
||||||
|
} |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("stored ME: " + me |
||||||
|
+ " vn: " + me.valueNumber()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitStaticFieldExpr( |
||||||
|
final StaticFieldExpr me) { |
||||||
|
if (me.def() != null) { |
||||||
|
if (LocalStore.get(me.def()) == null) { |
||||||
|
LocalStore.put(me.def(), me); |
||||||
|
} |
||||||
|
} |
||||||
|
if (LocalStore.get(me) == null) { |
||||||
|
LocalStore.put(me, me); |
||||||
|
} |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("stored ME: " + me |
||||||
|
+ " vn: " + me.valueNumber()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitIfCmpStmt(final IfCmpStmt cmp) { |
||||||
|
Swizzler indswz = null; |
||||||
|
boolean set_term = false; |
||||||
|
|
||||||
|
if (cmp.left().def() != null) { |
||||||
|
indswz = (Swizzler) IndStore.get(cmp.left() |
||||||
|
.def()); |
||||||
|
} |
||||||
|
if (indswz != null) { |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
displaySwizzler(indswz); |
||||||
|
} |
||||||
|
if (indswz.end_val() == null) { |
||||||
|
indswz.set_end_val(cmp.right()); |
||||||
|
set_term = true; |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("Set end_val of " |
||||||
|
+ indswz.ind_var() + " to " |
||||||
|
+ cmp.right()); |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (cmp.right().def() != null) { |
||||||
|
indswz = (Swizzler) IndStore.get(cmp |
||||||
|
.right().def()); |
||||||
|
} |
||||||
|
if (indswz != null) { |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
displaySwizzler(indswz); |
||||||
|
} |
||||||
|
if (indswz.end_val() == null) { |
||||||
|
indswz.set_end_val(cmp.left()); |
||||||
|
set_term = true; |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out |
||||||
|
.println("Set end_val of " |
||||||
|
+ indswz.ind_var() |
||||||
|
+ " to " |
||||||
|
+ cmp.left()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (set_term && (indswz != null) |
||||||
|
&& (indswz.array() != null)) { |
||||||
|
indswz.aswizzle().set_redundant(true); |
||||||
|
insert_aswrange(indswz); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitSCStmt(final SCStmt sc) { |
||||||
|
Swizzler indswz; |
||||||
|
MemExpr le = null; |
||||||
|
|
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("SC: array= " + sc.array() |
||||||
|
+ " VN:" + sc.array().valueNumber() |
||||||
|
+ "\nindex=" + sc.index() + " VN:" |
||||||
|
+ sc.index().valueNumber()); |
||||||
|
} |
||||||
|
|
||||||
|
indswz = (Swizzler) get_swizzler(sc.index() |
||||||
|
.valueNumber()); |
||||||
|
if (indswz != null) { |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
displaySwizzler(indswz); |
||||||
|
} |
||||||
|
if (indswz.array() == null) { |
||||||
|
le = get_local(sc.array().valueNumber()); |
||||||
|
if ((le == null) |
||||||
|
&& (sc.array().def() != null)) { |
||||||
|
le = get_local(sc.array().def() |
||||||
|
.valueNumber()); |
||||||
|
} |
||||||
|
if (le != null) { |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("Le: " + le); |
||||||
|
} |
||||||
|
indswz.set_array(le); |
||||||
|
indswz.set_aswizzle(sc); |
||||||
|
} else { |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
if (indswz.end_val() != null) { |
||||||
|
sc.set_redundant(true); |
||||||
|
insert_aswrange(indswz); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* public void visitStoreExpr(StoreExpr ind_store) { |
||||||
|
* if(ind_var != null) { |
||||||
|
* if(ind_var.equalsExpr(ind_store.target())) { if (tgt != |
||||||
|
* null && ind_store.expr() instanceof ArithExpr){ |
||||||
|
* ArithExpr ind_exp = (ArithExpr)ind_store.expr(); |
||||||
|
* if(tgt.equalsExpr(ind_exp.left())) ind_inc = |
||||||
|
* ind_exp.right(); else if(tgt.equals(ind_exp.right())) |
||||||
|
* ind_inc = ind_exp.left(); else { ind_inc = null; |
||||||
|
* return; } System.out.println("Ind_inc: "+ind_inc); } } } } |
||||||
|
*/ |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("------------After visitComponents---------"); |
||||||
|
cfg.print(System.out); |
||||||
|
} |
||||||
|
|
||||||
|
// If the CFG changed (i.e. if an array range swizzle was added),
|
||||||
|
// traverse
|
||||||
|
// the graph and remove redundent swizzle statements.
|
||||||
|
if (changed) { |
||||||
|
cfg.visit(new TreeVisitor() { |
||||||
|
ListIterator iter; |
||||||
|
|
||||||
|
public void visitTree(final Tree tree) { |
||||||
|
iter = tree.stmts().listIterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final Stmt stmt = (Stmt) iter.next(); |
||||||
|
stmt.visit(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void visitSCStmt(final SCStmt sc) { |
||||||
|
Object dup2stmt; |
||||||
|
if (sc.redundant()) { |
||||||
|
|
||||||
|
iter.remove(); |
||||||
|
dup2stmt = iter.previous(); |
||||||
|
iter.remove(); |
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("Removed Redundant ASW: " + sc |
||||||
|
+ "\nand " + dup2stmt); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
if (InductionVarAnalyzer.DEBUG) { |
||||||
|
System.out.println("----------------After cfg.visit--------------"); |
||||||
|
cfg.print(System.out); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,637 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.diva; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.cfg.*; |
||||||
|
import EDU.purdue.cs.bloat.codegen.*; |
||||||
|
import EDU.purdue.cs.bloat.context.*; |
||||||
|
import EDU.purdue.cs.bloat.editor.*; |
||||||
|
import EDU.purdue.cs.bloat.file.*; |
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
import EDU.purdue.cs.bloat.ssa.*; |
||||||
|
import EDU.purdue.cs.bloat.tbaa.*; |
||||||
|
import EDU.purdue.cs.bloat.trans.*; |
||||||
|
import EDU.purdue.cs.bloat.tree.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Performs a number of analyses on the methods of some specified classes. |
||||||
|
* However, it does not perform some of the optimizations that optimize.Main |
||||||
|
* does. |
||||||
|
* |
||||||
|
* @see EDU.purdue.cs.bloat.optimize.Main |
||||||
|
*/ |
||||||
|
public class Main { |
||||||
|
static boolean DEBUG = false; |
||||||
|
|
||||||
|
static boolean VERBOSE = false; |
||||||
|
|
||||||
|
static boolean FORCE = false; |
||||||
|
|
||||||
|
static boolean CLOSURE = false; |
||||||
|
|
||||||
|
static boolean PRE = true; |
||||||
|
|
||||||
|
static boolean DCE = true; |
||||||
|
|
||||||
|
static boolean PROP = true; |
||||||
|
|
||||||
|
static boolean FOLD = true; |
||||||
|
|
||||||
|
static boolean STACK_ALLOC = false; |
||||||
|
|
||||||
|
static boolean COMPACT_ARRAY_INIT = true; |
||||||
|
|
||||||
|
static boolean ANNO = true; |
||||||
|
|
||||||
|
static String[] ARGS = null; |
||||||
|
|
||||||
|
static List SKIP = new ArrayList(); |
||||||
|
|
||||||
|
static List ONLY = new ArrayList(); |
||||||
|
|
||||||
|
static String METHOD = null; |
||||||
|
|
||||||
|
static BloatContext context = null; |
||||||
|
|
||||||
|
static ClassFileLoader loader = null; |
||||||
|
|
||||||
|
public static void main(final String[] args) { |
||||||
|
try { |
||||||
|
Main.loader = new ClassFileLoader(); |
||||||
|
|
||||||
|
List classes = new ArrayList(args.length); |
||||||
|
boolean gotdir = false; |
||||||
|
|
||||||
|
Main.ARGS = args; |
||||||
|
|
||||||
|
for (int i = 0; i < args.length; i++) { |
||||||
|
if (args[i].equals("-v") || args[i].equals("-verbose")) { |
||||||
|
Main.VERBOSE = true; |
||||||
|
Main.loader.setVerbose(true); |
||||||
|
} else if (args[i].equals("-debug")) { |
||||||
|
Main.DEBUG = true; |
||||||
|
Main.loader.setVerbose(true); |
||||||
|
ClassFileLoader.DEBUG = true; |
||||||
|
CompactArrayInitializer.DEBUG = true; |
||||||
|
ClassEditor.DEBUG = true; |
||||||
|
FlowGraph.DEBUG = true; |
||||||
|
DominatorTree.DEBUG = true; |
||||||
|
Tree.DEBUG = true; |
||||||
|
CodeGenerator.DEBUG = true; |
||||||
|
Liveness.DEBUG = true; |
||||||
|
SSA.DEBUG = true; |
||||||
|
SSAGraph.DEBUG = true; |
||||||
|
PersistentCheckElimination.DEBUG = true; |
||||||
|
ValueNumbering.DEBUG = true; |
||||||
|
ValueFolding.DEBUG = true; |
||||||
|
ClassHierarchy.DEBUG = true; |
||||||
|
TypeInference.DEBUG = true; |
||||||
|
SSAPRE.DEBUG = true; |
||||||
|
StackPRE.DEBUG = true; |
||||||
|
ExprPropagation.DEBUG = true; |
||||||
|
DeadCodeElimination.DEBUG = true; |
||||||
|
} else if (args[i].equals("-help")) { |
||||||
|
Main.usage(); |
||||||
|
} else if (args[i].equals("-noanno")) { |
||||||
|
Main.ANNO = false; |
||||||
|
} else if (args[i].equals("-anno")) { |
||||||
|
Main.ANNO = true; |
||||||
|
} else if (args[i].equals("-preserve-debug")) { |
||||||
|
MethodEditor.PRESERVE_DEBUG = true; |
||||||
|
} else if (args[i].equals("-nouse-stack-vars")) { |
||||||
|
Tree.USE_STACK = false; |
||||||
|
} else if (args[i].equals("-use-stack-vars")) { |
||||||
|
Tree.USE_STACK = true; |
||||||
|
} else if (args[i].equals("-nocompact-array-init")) { |
||||||
|
Main.COMPACT_ARRAY_INIT = false; |
||||||
|
} else if (args[i].equals("-compact-array-init")) { |
||||||
|
Main.COMPACT_ARRAY_INIT = true; |
||||||
|
} else if (args[i].equals("-nostack-alloc")) { |
||||||
|
Main.STACK_ALLOC = false; |
||||||
|
} else if (args[i].equals("-stack-alloc")) { |
||||||
|
Main.STACK_ALLOC = true; |
||||||
|
} else if (args[i].equals("-peel-loops")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final String n = args[i]; |
||||||
|
|
||||||
|
if (n.equals("all")) { |
||||||
|
FlowGraph.PEEL_LOOPS_LEVEL = FlowGraph.PEEL_ALL_LOOPS; |
||||||
|
} else { |
||||||
|
try { |
||||||
|
FlowGraph.PEEL_LOOPS_LEVEL = Integer.parseInt(n); |
||||||
|
|
||||||
|
if (FlowGraph.PEEL_LOOPS_LEVEL < 0) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
} catch (final NumberFormatException ex) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
} |
||||||
|
} else if (args[i].equals("-color")) { |
||||||
|
Liveness.UNIQUE = false; |
||||||
|
} else if (args[i].equals("-nocolor")) { |
||||||
|
Liveness.UNIQUE = true; |
||||||
|
} else if (args[i].equals("-only-method")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
Main.METHOD = args[i]; |
||||||
|
} else if (args[i].equals("-print-flow-graph")) { |
||||||
|
FlowGraph.PRINT_GRAPH = true; |
||||||
|
} else if (args[i].equals("-classpath")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final String classpath = args[i]; |
||||||
|
Main.loader.setClassPath(classpath); |
||||||
|
} else if (args[i].equals("-skip")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final String pkg = args[i].replace('.', '/'); |
||||||
|
Main.SKIP.add(pkg); |
||||||
|
} else if (args[i].equals("-only")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final String pkg = args[i].replace('.', '/'); |
||||||
|
Main.ONLY.add(pkg); |
||||||
|
} else if (args[i].equals("-nodce")) { |
||||||
|
Main.DCE = false; |
||||||
|
} else if (args[i].equals("-noprop")) { |
||||||
|
Main.PROP = false; |
||||||
|
} else if (args[i].equals("-nopre")) { |
||||||
|
Main.PRE = false; |
||||||
|
} else if (args[i].equals("-dce")) { |
||||||
|
Main.DCE = true; |
||||||
|
} else if (args[i].equals("-prop")) { |
||||||
|
Main.PROP = true; |
||||||
|
} else if (args[i].equals("-pre")) { |
||||||
|
Main.PRE = true; |
||||||
|
} else if (args[i].equals("-closure")) { |
||||||
|
Main.CLOSURE = true; |
||||||
|
} else if (args[i].equals("-relax-loading")) { |
||||||
|
ClassHierarchy.RELAX = true; |
||||||
|
} else if (args[i].equals("-f")) { |
||||||
|
Main.FORCE = true; |
||||||
|
} else if (args[i].startsWith("-")) { |
||||||
|
Main.usage(); |
||||||
|
} else if (i == args.length - 1) { |
||||||
|
final File f = new File(args[i]); |
||||||
|
|
||||||
|
if (f.exists() && !f.isDirectory()) { |
||||||
|
System.err.println("No such directory: " + f.getPath()); |
||||||
|
System.exit(2); |
||||||
|
} |
||||||
|
|
||||||
|
if (!f.exists()) { |
||||||
|
f.mkdirs(); |
||||||
|
} |
||||||
|
|
||||||
|
if (!f.exists()) { |
||||||
|
System.err.println("Couldn't create directory: " |
||||||
|
+ f.getPath()); |
||||||
|
System.exit(2); |
||||||
|
} |
||||||
|
|
||||||
|
Main.loader.setOutputDir(f); |
||||||
|
gotdir = true; |
||||||
|
} else { |
||||||
|
classes.add(args[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!gotdir) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
if (classes.size() == 0) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
boolean errors = false; |
||||||
|
|
||||||
|
final Iterator iter = classes.iterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final String name = (String) iter.next(); |
||||||
|
|
||||||
|
try { |
||||||
|
Main.loader.loadClass(name); |
||||||
|
} catch (final ClassNotFoundException ex) { |
||||||
|
System.err.println("Couldn't find class: " |
||||||
|
+ ex.getMessage()); |
||||||
|
errors = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (errors) { |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
Main.context = new CachingBloatContext(Main.loader, classes, |
||||||
|
Main.CLOSURE); |
||||||
|
|
||||||
|
if (!Main.CLOSURE) { |
||||||
|
final Iterator e = classes.iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final String name = (String) e.next(); |
||||||
|
Main.editClass(name); |
||||||
|
} |
||||||
|
} else { |
||||||
|
classes = null; |
||||||
|
|
||||||
|
final Iterator e = Main.context.getHierarchy().classes() |
||||||
|
.iterator(); |
||||||
|
|
||||||
|
while (e.hasNext()) { |
||||||
|
final Type t = (Type) e.next(); |
||||||
|
|
||||||
|
if (t.isObject()) { |
||||||
|
Main.editClass(t.className()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (final ExceptionInInitializerError ex) { |
||||||
|
ex.printStackTrace(); |
||||||
|
System.out.println(ex.getException()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void usage() { |
||||||
|
System.err |
||||||
|
.println("Usage: java EDU.purdue.cs.bloat.optimize.Main" |
||||||
|
+ "\n [-options] classes dir" |
||||||
|
+ "\n" |
||||||
|
+ "\nwhere options include:" |
||||||
|
+ "\n -help print out this message" |
||||||
|
+ "\n -v -verbose turn on verbose mode" |
||||||
|
+ "\n -debug display a hideous amount of debug info" |
||||||
|
+ "\n -classpath <directories separated by colons>" |
||||||
|
+ "\n list directories in which to look for classes" |
||||||
|
+ "\n -f optimize files even if up-to-date" |
||||||
|
+ "\n -closure recursively optimize referenced classes" |
||||||
|
+ "\n -relax-loading don't report errors if a class is not found" |
||||||
|
+ "\n -skip <class|package.*>" |
||||||
|
+ "\n skip the given class or package" |
||||||
|
+ "\n -only <class|package.*>" |
||||||
|
+ "\n skip all but the given class or package" |
||||||
|
+ "\n -preserve-debug try to preserve debug information" |
||||||
|
+ "\n -[no]anno insert an annotation in the contant pool" |
||||||
|
+ "\n -[no]stack-alloc try to push locals onto the operand stack" |
||||||
|
+ "\n -peel-loops <n|all>" |
||||||
|
+ "\n peel innermost loops to enable code hoisting" |
||||||
|
+ "\n (n >= 0 is the maximum loop level to peel)" |
||||||
|
+ "\n -[no]pre perform partial redundency elimination" |
||||||
|
+ "\n -[no]dce perform dead code elimination" |
||||||
|
+ "\n -[no]prop perform copy and constant propagation" |
||||||
|
+ ""); |
||||||
|
System.exit(0); |
||||||
|
} |
||||||
|
|
||||||
|
private static void editClass(final String className) { |
||||||
|
ClassFile classFile; |
||||||
|
|
||||||
|
try { |
||||||
|
classFile = (ClassFile) Main.loader.loadClass(className); |
||||||
|
} catch (final ClassNotFoundException ex) { |
||||||
|
System.err.println("Couldn't find class: " + ex.getMessage()); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!Main.FORCE) { |
||||||
|
final File source = classFile.file(); |
||||||
|
final File target = classFile.outputFile(); |
||||||
|
|
||||||
|
if ((source != null) && (target != null) && source.exists() |
||||||
|
&& target.exists() |
||||||
|
&& (source.lastModified() < target.lastModified())) { |
||||||
|
|
||||||
|
if (Main.VERBOSE) { |
||||||
|
System.out.println(classFile.name() + " is up to date"); |
||||||
|
} |
||||||
|
|
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
classFile.print(System.out); |
||||||
|
} |
||||||
|
|
||||||
|
final ClassEditor c = Main.context.editClass(classFile); |
||||||
|
|
||||||
|
boolean skip = false; |
||||||
|
|
||||||
|
final String name = c.type().className(); |
||||||
|
final String qual = c.type().qualifier() + "/*"; |
||||||
|
|
||||||
|
// Edit only classes explicitly mentioned.
|
||||||
|
if (Main.ONLY.size() > 0) { |
||||||
|
skip = true; |
||||||
|
|
||||||
|
// Only edit classes we explicitly don't name.
|
||||||
|
for (int i = 0; i < Main.ONLY.size(); i++) { |
||||||
|
final String pkg = (String) Main.ONLY.get(i); |
||||||
|
|
||||||
|
if (name.equals(pkg) || qual.equals(pkg)) { |
||||||
|
skip = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Don't edit classes we explicitly skip.
|
||||||
|
if (!skip) { |
||||||
|
for (int i = 0; i < Main.SKIP.size(); i++) { |
||||||
|
final String pkg = (String) Main.SKIP.get(i); |
||||||
|
|
||||||
|
if (name.equals(pkg) || qual.equals(pkg)) { |
||||||
|
skip = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (skip) { |
||||||
|
if (Main.VERBOSE) { |
||||||
|
System.out.println("Skipping " + c.type().className()); |
||||||
|
} |
||||||
|
|
||||||
|
Main.context.release(classFile); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.VERBOSE) { |
||||||
|
System.out.println("Optimizing " + c.type().className()); |
||||||
|
} |
||||||
|
|
||||||
|
final MethodInfo[] methods = c.methods(); |
||||||
|
|
||||||
|
for (int j = 0; j < methods.length; j++) { |
||||||
|
final MethodEditor m; |
||||||
|
|
||||||
|
try { |
||||||
|
m = Main.context.editMethod(methods[j]); |
||||||
|
} catch (final ClassFormatException ex) { |
||||||
|
System.err.println(ex.getMessage()); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if ((Main.METHOD != null) && !m.name().equals(Main.METHOD)) { |
||||||
|
Main.context.release(methods[j]); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
m.print(System.out); |
||||||
|
} |
||||||
|
|
||||||
|
if (m.isNative() || m.isAbstract()) { |
||||||
|
Main.context.release(methods[j]); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.COMPACT_ARRAY_INIT) { |
||||||
|
CompactArrayInitializer.transform(m); |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("---------- After compaction:"); |
||||||
|
m.print(System.out); |
||||||
|
System.out.println("---------- end print"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
FlowGraph cfg; |
||||||
|
|
||||||
|
try { |
||||||
|
cfg = new FlowGraph(m); |
||||||
|
} catch (final ClassFormatException ex) { |
||||||
|
System.err.println(ex.getMessage()); |
||||||
|
Main.context.release(methods[j]); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
SSA.transform(cfg); |
||||||
|
|
||||||
|
if (FlowGraph.DEBUG) { |
||||||
|
System.out.println("---------- After SSA:"); |
||||||
|
cfg.print(System.out); |
||||||
|
System.out.println("---------- end print"); |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
cfg.visit(new VerifyCFG(false)); |
||||||
|
} |
||||||
|
|
||||||
|
// Do copy propagation first to get rid of all the extra copies
|
||||||
|
// inserted for dups. If they're left it, it really slows down
|
||||||
|
// value numbering.
|
||||||
|
if (Main.PROP) { |
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("-------Before Copy Propagation-------"); |
||||||
|
} |
||||||
|
|
||||||
|
final ExprPropagation copy = new ExprPropagation(cfg); |
||||||
|
copy.transform(); |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
cfg.visit(new VerifyCFG(false)); |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("--------After Copy Propagation-------"); |
||||||
|
cfg.print(System.out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.DCE) { |
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("-----Before Dead Code Elimination----"); |
||||||
|
} |
||||||
|
|
||||||
|
final DeadCodeElimination dce = new DeadCodeElimination(cfg); |
||||||
|
dce.transform(); |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
cfg.visit(new VerifyCFG(false)); |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("-----After Dead Code Elimination-----"); |
||||||
|
cfg.print(System.out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("---------Doing type inference--------"); |
||||||
|
} |
||||||
|
|
||||||
|
TypeInference.transform(cfg, Main.context.getHierarchy()); |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("--------Doing value numbering--------"); |
||||||
|
} |
||||||
|
|
||||||
|
(new ValueNumbering()).transform(cfg); |
||||||
|
|
||||||
|
if (Main.FOLD) { |
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("--------Before Value Folding---------"); |
||||||
|
} |
||||||
|
|
||||||
|
(new ValueFolding()).transform(cfg); |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
cfg.visit(new VerifyCFG()); |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("---------After Value Folding---------"); |
||||||
|
cfg.print(System.out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.PRE) { |
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("-------------Before SSAPRE-----------"); |
||||||
|
} |
||||||
|
|
||||||
|
final SSAPRE pre = new SSAPRE(cfg, Main.context); |
||||||
|
pre.transform(); |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
cfg.visit(new VerifyCFG()); |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("-------------After SSAPRE------------"); |
||||||
|
cfg.print(System.out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.FOLD) { |
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("--------Before Value Folding---------"); |
||||||
|
} |
||||||
|
|
||||||
|
(new ValueFolding()).transform(cfg); |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
cfg.visit(new VerifyCFG()); |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("---------After Value Folding---------"); |
||||||
|
cfg.print(System.out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.PROP) { |
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("-------Before Copy Propagation-------"); |
||||||
|
} |
||||||
|
|
||||||
|
final ExprPropagation copy = new ExprPropagation(cfg); |
||||||
|
copy.transform(); |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
cfg.visit(new VerifyCFG()); |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("--------After Copy Propagation-------"); |
||||||
|
cfg.print(System.out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.DCE) { |
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("-----Before Dead Code Elimination----"); |
||||||
|
} |
||||||
|
|
||||||
|
final DeadCodeElimination dce = new DeadCodeElimination(cfg); |
||||||
|
dce.transform(); |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
cfg.visit(new VerifyCFG()); |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.DEBUG) { |
||||||
|
System.out.println("-----After Dead Code Elimination-----"); |
||||||
|
cfg.print(System.out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
(new PersistentCheckElimination()).transform(cfg); |
||||||
|
(new InductionVarAnalyzer()).transform(cfg); |
||||||
|
|
||||||
|
/* |
||||||
|
* if (STACK_ALLOC) { if (DEBUG) { |
||||||
|
* System.out.println("------------Before StackPRE----------"); } |
||||||
|
* |
||||||
|
* StackPRE pre = new StackPRE(cfg); pre.transform(); |
||||||
|
* |
||||||
|
* if (DEBUG) { cfg.visit(new VerifyCFG()); } |
||||||
|
* |
||||||
|
* if (DEBUG) { System.out.println("------------After |
||||||
|
* StackPRE-----------"); cfg.print(System.out); } } |
||||||
|
*/ |
||||||
|
|
||||||
|
cfg.commit(); |
||||||
|
|
||||||
|
Peephole.transform(m); |
||||||
|
|
||||||
|
Main.context.commit(methods[j]); |
||||||
|
} |
||||||
|
|
||||||
|
if (Main.ANNO) { |
||||||
|
String s = "Optimized with: EDU.purdue.cs.bloat.diva.Main"; |
||||||
|
|
||||||
|
for (int i = 0; i < Main.ARGS.length; i++) { |
||||||
|
if ((Main.ARGS[i].indexOf(' ') >= 0) |
||||||
|
|| (Main.ARGS[i].indexOf('\t') >= 0) |
||||||
|
|| (Main.ARGS[i].indexOf('\r') >= 0) |
||||||
|
|| (Main.ARGS[i].indexOf('\n') >= 0)) { |
||||||
|
s += " '" + Main.ARGS[i] + "'"; |
||||||
|
} else { |
||||||
|
s += " " + Main.ARGS[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
System.out.println(s); |
||||||
|
// c.constants().addConstant(Constant.UTF8, s);
|
||||||
|
} |
||||||
|
|
||||||
|
Main.context.commit(classFile); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
# All files in the distribution of BLOAT (Bytecode Level Optimization and
|
||||||
|
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
||||||
|
# Research Foundation of Purdue University. All rights reserved.
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
|
||||||
|
CLASS = \
|
||||||
|
InductionVarAnalyzer.class\
|
||||||
|
Main.class
|
||||||
|
|
||||||
|
include ../class.mk |
@ -0,0 +1,8 @@ |
|||||||
|
<html> |
||||||
|
<body> |
||||||
|
|
||||||
|
<p>Performs demand-driven induction variable analysis on a Java |
||||||
|
classfile.</p> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1 @@ |
|||||||
|
*.class |
@ -0,0 +1,252 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.dump; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.context.*; |
||||||
|
import EDU.purdue.cs.bloat.editor.*; |
||||||
|
import EDU.purdue.cs.bloat.file.*; |
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Prints the contents of a Java classfile to the console. |
||||||
|
*/ |
||||||
|
public class Main implements Opcode { |
||||||
|
public static void main(final String[] args) { |
||||||
|
final ClassFileLoader loader = new ClassFileLoader(); |
||||||
|
String className = null; |
||||||
|
|
||||||
|
for (int i = 0; i < args.length; i++) { |
||||||
|
if (args[i].equals("-help")) { |
||||||
|
Main.usage(); |
||||||
|
} else if (args[i].equals("-classpath")) { |
||||||
|
if (++i >= args.length) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
final String classpath = args[i]; |
||||||
|
loader.setClassPath(classpath); |
||||||
|
} else if (args[i].startsWith("-")) { |
||||||
|
Main.usage(); |
||||||
|
} else { |
||||||
|
if (className != null) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
className = args[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (className == null) { |
||||||
|
Main.usage(); |
||||||
|
} |
||||||
|
|
||||||
|
ClassInfo info = null; |
||||||
|
|
||||||
|
try { |
||||||
|
info = loader.loadClass(className); |
||||||
|
} catch (final ClassNotFoundException ex) { |
||||||
|
System.err.println("Couldn't find class: " + ex.getMessage()); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
final Collection classes = new ArrayList(1); |
||||||
|
classes.add(className); |
||||||
|
|
||||||
|
final BloatContext context = new CachingBloatContext(loader, classes, |
||||||
|
true); |
||||||
|
|
||||||
|
if (info != null) { |
||||||
|
Main.printClass(context, info); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void usage() { |
||||||
|
System.err |
||||||
|
.println("Usage: java EDU.purdue.cs.bloat.dump.Main " |
||||||
|
+ "\n [-options] class" |
||||||
|
+ "\n" |
||||||
|
+ "\nwhere options include:" |
||||||
|
+ "\n -help print out this message" |
||||||
|
+ "\n -classpath <directories separated by colons>" |
||||||
|
+ "\n list directories in which to look for classes"); |
||||||
|
System.exit(0); |
||||||
|
} |
||||||
|
|
||||||
|
private static void printClass(final EditorContext context, |
||||||
|
final ClassInfo info) { |
||||||
|
final ClassEditor c = context.editClass(info); |
||||||
|
|
||||||
|
if (c.isPublic()) { |
||||||
|
System.out.print("public "); |
||||||
|
} else if (c.isPrivate()) { |
||||||
|
System.out.print("private "); |
||||||
|
} else if (c.isProtected()) { |
||||||
|
System.out.print("protected "); |
||||||
|
} |
||||||
|
|
||||||
|
if (c.isStatic()) { |
||||||
|
System.out.print("static "); |
||||||
|
} |
||||||
|
|
||||||
|
if (c.isFinal()) { |
||||||
|
System.out.print("final "); |
||||||
|
} |
||||||
|
|
||||||
|
if (c.isInterface()) { |
||||||
|
System.out.print("interface "); |
||||||
|
} else if (c.isAbstract()) { |
||||||
|
System.out.print("abstract class "); |
||||||
|
} else { |
||||||
|
System.out.print("class "); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.print(c.type().className()); |
||||||
|
|
||||||
|
if (c.superclass() != null) { |
||||||
|
System.out.print(" extends " + c.superclass().className()); |
||||||
|
} |
||||||
|
|
||||||
|
final Type[] interfaces = c.interfaces(); |
||||||
|
|
||||||
|
for (int i = 0; i < interfaces.length; i++) { |
||||||
|
if (i == 0) { |
||||||
|
System.out.print(" implements"); |
||||||
|
} else { |
||||||
|
System.out.print(","); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.print(" " + interfaces[i].className()); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.println(); |
||||||
|
System.out.println("{"); |
||||||
|
|
||||||
|
final FieldInfo[] fields = c.fields(); |
||||||
|
|
||||||
|
for (int i = 0; i < fields.length; i++) { |
||||||
|
FieldEditor f = null; |
||||||
|
|
||||||
|
try { |
||||||
|
f = context.editField(fields[i]); |
||||||
|
} catch (final ClassFormatException ex) { |
||||||
|
System.err.println(ex.getMessage()); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.print(" "); |
||||||
|
|
||||||
|
if (f.isPublic()) { |
||||||
|
System.out.print("public "); |
||||||
|
} else if (f.isPrivate()) { |
||||||
|
System.out.print("private "); |
||||||
|
} else if (f.isProtected()) { |
||||||
|
System.out.print("protected "); |
||||||
|
} |
||||||
|
|
||||||
|
if (f.isTransient()) { |
||||||
|
System.out.print("transient "); |
||||||
|
} |
||||||
|
|
||||||
|
if (f.isVolatile()) { |
||||||
|
System.out.print("volatile "); |
||||||
|
} |
||||||
|
|
||||||
|
if (f.isStatic()) { |
||||||
|
System.out.print("static "); |
||||||
|
} |
||||||
|
|
||||||
|
if (f.isFinal()) { |
||||||
|
System.out.print("final "); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.println(f.type() + " " + f.name()); |
||||||
|
|
||||||
|
context.release(fields[i]); |
||||||
|
} |
||||||
|
|
||||||
|
if (fields.length != 0) { |
||||||
|
System.out.println(); |
||||||
|
} |
||||||
|
|
||||||
|
final MethodInfo[] methods = c.methods(); |
||||||
|
|
||||||
|
for (int i = 0; i < methods.length; i++) { |
||||||
|
MethodEditor m = null; |
||||||
|
|
||||||
|
try { |
||||||
|
m = context.editMethod(methods[i]); |
||||||
|
} catch (final ClassFormatException ex) { |
||||||
|
System.err.println(ex.getMessage()); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
if (i != 0) { |
||||||
|
System.out.println(); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.print(" "); |
||||||
|
|
||||||
|
if (m.isPublic()) { |
||||||
|
System.out.print("public "); |
||||||
|
} else if (m.isPrivate()) { |
||||||
|
System.out.print("private "); |
||||||
|
} else if (m.isProtected()) { |
||||||
|
System.out.print("protected "); |
||||||
|
} |
||||||
|
|
||||||
|
if (m.isNative()) { |
||||||
|
System.out.print("native "); |
||||||
|
} |
||||||
|
|
||||||
|
if (m.isSynchronized()) { |
||||||
|
System.out.print("synchronized "); |
||||||
|
} |
||||||
|
|
||||||
|
if (m.isAbstract()) { |
||||||
|
System.out.print("abstract "); |
||||||
|
} |
||||||
|
|
||||||
|
if (m.isStatic()) { |
||||||
|
System.out.print("static "); |
||||||
|
} |
||||||
|
|
||||||
|
if (m.isFinal()) { |
||||||
|
System.out.print("final "); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.println(m.type() + " " + m.name()); |
||||||
|
|
||||||
|
final Iterator iter = m.code().iterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
final Object obj = iter.next(); |
||||||
|
System.out.println(" " + obj); |
||||||
|
} |
||||||
|
|
||||||
|
context.release(methods[i]); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.println("}"); |
||||||
|
|
||||||
|
context.release(info); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
# All files in the distribution of BLOAT (Bytecode Level Optimization and
|
||||||
|
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
||||||
|
# Research Foundation of Purdue University. All rights reserved.
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
|
||||||
|
CLASS = \
|
||||||
|
Main.class
|
||||||
|
|
||||||
|
include ../class.mk |
@ -0,0 +1,8 @@ |
|||||||
|
<html> |
||||||
|
<body> |
||||||
|
|
||||||
|
<p>Contains a program that prints the contents of a Java classfile to |
||||||
|
the console.</p> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1 @@ |
|||||||
|
*.class |
@ -0,0 +1,551 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
import EDU.purdue.cs.bloat.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* A ClassEditor provides finer-grain access to a class than a CLassInfo object |
||||||
|
* does. A ClassEditor takes a ClassInfo and extracts the class's constant pool, |
||||||
|
* type, super class type, and the types of its interfaces. When editing is |
||||||
|
* finished, changes are committed with the commit method. |
||||||
|
* |
||||||
|
* @see ClassInfo |
||||||
|
* @see MethodEditor |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class ClassEditor { |
||||||
|
public static boolean DEBUG = Boolean.getBoolean("ClassEditor.DEBUG"); |
||||||
|
|
||||||
|
private ConstantPool constants; // A copy of the constant pool of the class
|
||||||
|
|
||||||
|
// being edited.
|
||||||
|
private ClassInfo classInfo; // (A representation of) the class being
|
||||||
|
// edited
|
||||||
|
|
||||||
|
private Type type; // An index into constant pool (descriptors) that
|
||||||
|
|
||||||
|
private Type superclass; // specifies the class, superclass, and
|
||||||
|
// interfaces
|
||||||
|
|
||||||
|
private Type[] interfaces; // of the class being edited.
|
||||||
|
|
||||||
|
private EditorContext context; // Use to edit classes and methods
|
||||||
|
|
||||||
|
private boolean dirty; // Has the class been modified?
|
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Create a new ClassEditor based on information in a ClassInfo |
||||||
|
* object. It extracts the class's constant pool, and the types of the |
||||||
|
* class, its superclass, and any interfaces it implements. |
||||||
|
* |
||||||
|
* @param context |
||||||
|
* The <tt>EditorContext</tt> used to edit fields and methods. |
||||||
|
* @param classInfo |
||||||
|
* The ClassInfo structure of the class to edit. |
||||||
|
* |
||||||
|
* @see EDU.purdue.cs.bloat.reflect.ClassInfo |
||||||
|
* @see ConstantPool |
||||||
|
* @see Type |
||||||
|
*/ |
||||||
|
public ClassEditor(final EditorContext context, final ClassInfo classInfo) { |
||||||
|
this.context = context; |
||||||
|
this.classInfo = classInfo; |
||||||
|
this.dirty = false; |
||||||
|
|
||||||
|
// Extract the constant pool from the ClassInfo
|
||||||
|
constants = new ConstantPool(classInfo.constants()); |
||||||
|
|
||||||
|
int index; |
||||||
|
|
||||||
|
// Load information (such as the indices of the class, superclass,
|
||||||
|
// and the interfaces) about the class being edited from its
|
||||||
|
// constant pool.
|
||||||
|
|
||||||
|
index = classInfo.classIndex(); |
||||||
|
type = (Type) constants.constantAt(index); |
||||||
|
|
||||||
|
index = classInfo.superclassIndex(); |
||||||
|
superclass = (Type) constants.constantAt(index); |
||||||
|
|
||||||
|
final int ifs[] = classInfo.interfaceIndices(); |
||||||
|
interfaces = new Type[ifs.length]; |
||||||
|
|
||||||
|
for (int i = 0; i < ifs.length; i++) { |
||||||
|
interfaces[i] = (Type) constants.constantAt(ifs[i]); |
||||||
|
} |
||||||
|
|
||||||
|
if (ClassEditor.DEBUG) { |
||||||
|
System.out.println("Editing class " + type); |
||||||
|
} |
||||||
|
|
||||||
|
this.setDirty(false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new <code>ClassEditor</code> for editing a class (or |
||||||
|
* interface) from scratch. This constructor should not be invoked direcly. |
||||||
|
* Use {@link EditorContext#newClass(int, String, Type, Type[])} instead. |
||||||
|
*/ |
||||||
|
public ClassEditor(final EditorContext context, final int modifiers, |
||||||
|
final String className, Type superType, Type[] interfaces) { |
||||||
|
|
||||||
|
if (className == null) { |
||||||
|
final String s = "Cannot have a null class name"; |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
} |
||||||
|
|
||||||
|
if (superType == null) { |
||||||
|
superType = Type.OBJECT; |
||||||
|
} |
||||||
|
|
||||||
|
if (interfaces == null) { |
||||||
|
interfaces = new Type[0]; |
||||||
|
} |
||||||
|
|
||||||
|
if (ClassEditor.DEBUG) { |
||||||
|
System.out.println("Creating new class " + className + " extends " |
||||||
|
+ superType.className()); |
||||||
|
} |
||||||
|
|
||||||
|
this.context = context; |
||||||
|
this.superclass = superType; |
||||||
|
this.interfaces = interfaces; |
||||||
|
|
||||||
|
final ConstantPool cp = new ConstantPool(); |
||||||
|
this.constants = cp; |
||||||
|
|
||||||
|
this.type = Type.getType(Type.classDescriptor(className)); |
||||||
|
final int classNameIndex = cp.getClassIndex(this.type); |
||||||
|
final int superTypeIndex = cp.getClassIndex(superType); |
||||||
|
final int[] interfaceIndexes = new int[interfaces.length]; |
||||||
|
for (int i = 0; i < interfaces.length; i++) { |
||||||
|
interfaceIndexes[i] = cp.getClassIndex(interfaces[i]); |
||||||
|
} |
||||||
|
|
||||||
|
this.classInfo = context.newClassInfo(modifiers, classNameIndex, |
||||||
|
superTypeIndex, interfaceIndexes, cp.getConstantsList()); |
||||||
|
|
||||||
|
this.dirty = true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns <tt>true</tt> if the class has been modified. |
||||||
|
*/ |
||||||
|
public boolean isDirty() { |
||||||
|
return (this.dirty); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets this class's dirty flag. The dirty flag is <tt>true</tt> if the |
||||||
|
* class has been modified. |
||||||
|
*/ |
||||||
|
public void setDirty(final boolean dirty) { |
||||||
|
this.dirty = dirty; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the name of the class represented by this <tt>ClassEditor</tt>. |
||||||
|
*/ |
||||||
|
public String name() { |
||||||
|
return (this.classInfo().name()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Obtain the <tt>EditorContext</tt> for this ClassEditor. |
||||||
|
*/ |
||||||
|
public EditorContext context() { |
||||||
|
return context; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the ClassInfo object representing the class that being edited. |
||||||
|
*/ |
||||||
|
public ClassInfo classInfo() { |
||||||
|
return classInfo; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isPublic() { |
||||||
|
return (classInfo.modifiers() & Modifiers.PUBLIC) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isPrivate() { |
||||||
|
return (classInfo.modifiers() & Modifiers.PRIVATE) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isProtected() { |
||||||
|
return (classInfo.modifiers() & Modifiers.PROTECTED) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isStatic() { |
||||||
|
return (classInfo.modifiers() & Modifiers.STATIC) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isFinal() { |
||||||
|
return (classInfo.modifiers() & Modifiers.FINAL) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isSuper() { |
||||||
|
return (classInfo.modifiers() & Modifiers.SUPER) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isAbstract() { |
||||||
|
return (classInfo.modifiers() & Modifiers.ABSTRACT) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isInterface() { |
||||||
|
return (classInfo.modifiers() & Modifiers.INTERFACE) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public void setPublic(final boolean flag) { |
||||||
|
int modifiers = classInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.PUBLIC; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.PUBLIC; |
||||||
|
} |
||||||
|
|
||||||
|
classInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
public void setPrivate(final boolean flag) { |
||||||
|
int modifiers = classInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.PRIVATE; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.PRIVATE; |
||||||
|
} |
||||||
|
|
||||||
|
classInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
public void setProtected(final boolean flag) { |
||||||
|
int modifiers = classInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.PROTECTED; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.PROTECTED; |
||||||
|
} |
||||||
|
|
||||||
|
classInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
public void setStatic(final boolean flag) { |
||||||
|
int modifiers = classInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.STATIC; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.STATIC; |
||||||
|
} |
||||||
|
|
||||||
|
classInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
public void setFinal(final boolean flag) { |
||||||
|
int modifiers = classInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.FINAL; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.FINAL; |
||||||
|
} |
||||||
|
|
||||||
|
classInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
public void setSuper(final boolean flag) { |
||||||
|
int modifiers = classInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.SUPER; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.SUPER; |
||||||
|
} |
||||||
|
|
||||||
|
classInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
public void setAbstract(final boolean flag) { |
||||||
|
int modifiers = classInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.ABSTRACT; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.ABSTRACT; |
||||||
|
} |
||||||
|
|
||||||
|
classInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
public void setInterface(final boolean flag) { |
||||||
|
int modifiers = classInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.INTERFACE; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.INTERFACE; |
||||||
|
} |
||||||
|
|
||||||
|
classInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the Type (descriptor) object for the class. |
||||||
|
* |
||||||
|
* @param type |
||||||
|
* A Type. |
||||||
|
*/ |
||||||
|
public void setType(final Type type) { |
||||||
|
this.type = type; |
||||||
|
Assert.isTrue(type.isObject(), "Cannot set class type to " + type); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the Type (descriptor) for the class. |
||||||
|
*/ |
||||||
|
public Type type() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a Type object for the class's superclass. |
||||||
|
*/ |
||||||
|
public Type superclass() { |
||||||
|
return superclass; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds an interface of the given class to the set of interfaces that the |
||||||
|
* class implements. |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException <code>interfaceClass</code> is not an |
||||||
|
* interface
|
||||||
|
*/ |
||||||
|
public void addInterface(final Class interfaceClass) { |
||||||
|
if (!interfaceClass.isInterface()) { |
||||||
|
final String s = "Cannot add non-interface type: " |
||||||
|
+ interfaceClass.getName(); |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
} |
||||||
|
|
||||||
|
addInterface(Type.getType(interfaceClass)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds an interface of a given Type to the set of interfaces that the class
|
||||||
|
* implements. |
||||||
|
*/ |
||||||
|
public void addInterface(final Type interfaceType) { |
||||||
|
// // The interface must have an index in the constant pool
|
||||||
|
// this.constants().getClassIndex(interfaceType);
|
||||||
|
|
||||||
|
final Type[] interfaces = new Type[this.interfaces.length + 1]; |
||||||
|
for (int i = 0; i < this.interfaces.length; i++) { |
||||||
|
interfaces[i] = this.interfaces[i]; |
||||||
|
} |
||||||
|
interfaces[interfaces.length - 1] = interfaceType; |
||||||
|
this.setInterfaces(interfaces); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the interfaces the class implements. |
||||||
|
* |
||||||
|
* @param interfaces |
||||||
|
* An array of Types. |
||||||
|
*/ |
||||||
|
public void setInterfaces(final Type[] interfaces) { |
||||||
|
this.interfaces = interfaces; |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the interfaces the class implements. |
||||||
|
*/ |
||||||
|
public Type[] interfaces() { |
||||||
|
return interfaces; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the modifiers of the class. The values correspond to the |
||||||
|
* constants in the <tt>Modifiers</tt> class. |
||||||
|
* |
||||||
|
* @return A bit vector of modifier flags for the class. |
||||||
|
* @see Modifiers |
||||||
|
*/ |
||||||
|
public int modifiers() { |
||||||
|
return classInfo.modifiers(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an array of <tt>FieldInfo</tt> structures for each field in the |
||||||
|
* class. |
||||||
|
*/ |
||||||
|
public FieldInfo[] fields() { |
||||||
|
return classInfo.fields(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an array of MethodInfo structures for each method in the class. |
||||||
|
*/ |
||||||
|
public MethodInfo[] methods() { |
||||||
|
return classInfo.methods(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the constant pool for the class. |
||||||
|
*/ |
||||||
|
public ConstantPool constants() { |
||||||
|
return constants; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Commit any changes to the class since creation time. Note that committal |
||||||
|
* will occur regardless of whether or not the class is dirty. |
||||||
|
*/ |
||||||
|
public void commit() { |
||||||
|
commitOnly(null, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Commits only certain methods and fields. Note that committal will occur |
||||||
|
* regardless of whether or not the class is dirty. |
||||||
|
* |
||||||
|
* @param methods |
||||||
|
* Methods (<tt>MethodInfo</tt>s) to commit. If <tt>null</tt>, |
||||||
|
* all methods are committed. |
||||||
|
* @param fields |
||||||
|
* Fields (<tt>FieldInfo</tt>s) to commit. If <tt>null</tt>, |
||||||
|
* all fields are committed. |
||||||
|
*/ |
||||||
|
public void commitOnly(final Set methods, final Set fields) { |
||||||
|
classInfo.setClassIndex(constants.addConstant(Constant.CLASS, type)); |
||||||
|
classInfo.setSuperclassIndex(constants.addConstant(Constant.CLASS, |
||||||
|
superclass)); |
||||||
|
|
||||||
|
final int ifs[] = new int[interfaces.length]; |
||||||
|
|
||||||
|
for (int i = 0; i < ifs.length; i++) { |
||||||
|
ifs[i] = constants.addConstant(Constant.CLASS, interfaces[i]); |
||||||
|
} |
||||||
|
|
||||||
|
classInfo.setInterfaceIndices(ifs); |
||||||
|
|
||||||
|
classInfo.setConstants(constants.constants()); |
||||||
|
|
||||||
|
classInfo.commitOnly(methods, fields); |
||||||
|
|
||||||
|
// This class is no longer dirty
|
||||||
|
this.setDirty(false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This class is visited by an <tt>EditorVisitor</tt>. First, this |
||||||
|
* <tt>ClassEditor</tt> itself is visited. Then, all of this class's |
||||||
|
* fields (<tt>FieldEditor</tt>s) are visited. Finally, each of this |
||||||
|
* class's methods (<tt>MethodEditor</tt>s) are visited. Constructors |
||||||
|
* are visited before regular methods. |
||||||
|
*/ |
||||||
|
public void visit(final EditorVisitor visitor) { |
||||||
|
// First visit ourself
|
||||||
|
visitor.visitClassEditor(this); |
||||||
|
|
||||||
|
final EditorContext context = this.context(); |
||||||
|
|
||||||
|
// Visit each field
|
||||||
|
final FieldInfo[] fields = this.fields(); |
||||||
|
for (int i = 0; i < fields.length; i++) { |
||||||
|
final FieldEditor fieldEditor = context.editField(fields[i]); |
||||||
|
visitor.visitFieldEditor(fieldEditor); |
||||||
|
context.release(fields[i]); |
||||||
|
} |
||||||
|
|
||||||
|
// Visit each method
|
||||||
|
final ArrayList regularMethods = new ArrayList(); |
||||||
|
final MethodInfo[] methods = this.methods(); |
||||||
|
for (int i = 0; i < methods.length; i++) { |
||||||
|
final MethodEditor methodEditor = context.editMethod(methods[i]); |
||||||
|
|
||||||
|
if (methodEditor.name().charAt(0) != '<') { |
||||||
|
regularMethods.add(methods[i]); |
||||||
|
|
||||||
|
} else { |
||||||
|
visitor.visitMethodEditor(methodEditor); |
||||||
|
} |
||||||
|
|
||||||
|
context.release(methods[i]); |
||||||
|
} |
||||||
|
|
||||||
|
final Iterator iter = regularMethods.iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final MethodInfo info = (MethodInfo) iter.next(); |
||||||
|
final MethodEditor me = context.editMethod(info); |
||||||
|
visitor.visitMethodEditor(me); |
||||||
|
context.release(info); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Two <tt>ClassEditor</tt>s are equal if they edit the same class. |
||||||
|
*/ |
||||||
|
public boolean equals(final Object o) { |
||||||
|
if (o instanceof ClassEditor) { |
||||||
|
final ClassEditor other = (ClassEditor) o; |
||||||
|
if (!other.type().equals(this.type())) { |
||||||
|
return (false); |
||||||
|
} |
||||||
|
|
||||||
|
return (true); |
||||||
|
} |
||||||
|
|
||||||
|
return (false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A <tt>ClassEditor</tt>'s hash code is based upon the hash code of the |
||||||
|
* name of the class it edits. |
||||||
|
*/ |
||||||
|
public int hashCode() { |
||||||
|
return (this.name().hashCode()); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return (this.type().toString()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,459 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
import EDU.purdue.cs.bloat.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* ConstantPool models constants in the constant pool. Recall that the |
||||||
|
* reflection mechanism represents constants as a tag and a value. ConstantPool |
||||||
|
* consists of an array of <tt>reflect.Constant</tt>s that are resolved into |
||||||
|
* their appropriate value (e.g. Type, NameAndType, MemberRef, etc.) as they are |
||||||
|
* needed. |
||||||
|
* |
||||||
|
* @see EDU.purdue.cs.bloat.reflect.Constant |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class ConstantPool { |
||||||
|
/** |
||||||
|
* ConstantPool maintains some information about the constants in the |
||||||
|
* constant pool to make its life easier. |
||||||
|
* |
||||||
|
* constantIndices A mapping between constants and their indices in the |
||||||
|
* constant pool. Knowing this information makes adding constants to the |
||||||
|
* constant pool easier. |
||||||
|
* |
||||||
|
* constants A list of the reflect.Constant objects that are used to create |
||||||
|
* the ConstantPool. |
||||||
|
* |
||||||
|
* resolved A list of the constants in their resolved form (such as |
||||||
|
* NameAndType or String) |
||||||
|
*/ |
||||||
|
private Map constantIndices; |
||||||
|
|
||||||
|
ResizeableArrayList constants; |
||||||
|
|
||||||
|
ResizeableArrayList resolved; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Resolve the constants in the constant pool, converting from |
||||||
|
* the index-based representation of the class file to a more amenable |
||||||
|
* external representation. |
||||||
|
* |
||||||
|
* @param c |
||||||
|
* An array of Constant. |
||||||
|
*/ |
||||||
|
public ConstantPool(final Constant[] c) { |
||||||
|
constantIndices = new HashMap(); |
||||||
|
constants = new ResizeableArrayList(c.length); |
||||||
|
resolved = new ResizeableArrayList(c.length); |
||||||
|
|
||||||
|
/* |
||||||
|
* constants = new ResizeableArrayList(c.length, 256); resolved = new |
||||||
|
* ResizeableArrayList(c.length, 256); |
||||||
|
*/ |
||||||
|
for (int i = 0; i < c.length; i++) { |
||||||
|
constants.add(c[i]); |
||||||
|
resolved.add(null); |
||||||
|
if (c[i] != null) { |
||||||
|
constantIndices.put(c[i], new Integer(i)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new, empty <code>ConstantPool</code> |
||||||
|
*/ |
||||||
|
public ConstantPool() { |
||||||
|
this.constantIndices = new HashMap(); |
||||||
|
this.constants = new ResizeableArrayList(); |
||||||
|
this.resolved = new ResizeableArrayList(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Obtain the resolved value of a constant at given index in the constant |
||||||
|
* pool. |
||||||
|
* |
||||||
|
* @param idx |
||||||
|
* Index into the constant pool. |
||||||
|
* @return The value of the constant at index. |
||||||
|
*/ |
||||||
|
public Object constantAt(final int idx) { |
||||||
|
if (idx == 0) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
Object value = resolved.get(idx); |
||||||
|
|
||||||
|
if (value != null) { |
||||||
|
return value; |
||||||
|
} |
||||||
|
|
||||||
|
final Constant c = (Constant) constants.get(idx); |
||||||
|
|
||||||
|
if (c == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
value = c.value(); |
||||||
|
|
||||||
|
if (value == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
// Okay, we have to resolve the Constant.
|
||||||
|
|
||||||
|
switch (c.tag()) { |
||||||
|
case Constant.CLASS: { |
||||||
|
// Lookup the UTF8 at the index and use the class name
|
||||||
|
// to create a Type.
|
||||||
|
Assert.isTrue(value instanceof Integer, "Invalid constant: " + c); |
||||||
|
|
||||||
|
final int index = ((Integer) value).intValue(); |
||||||
|
Assert.isTrue(constantTag(index) == Constant.UTF8, |
||||||
|
"Invalid constant: " + c); |
||||||
|
|
||||||
|
final String className = (String) constantAt(index); |
||||||
|
|
||||||
|
value = Type.getType(Type.classDescriptor(className)); |
||||||
|
break; |
||||||
|
} |
||||||
|
case Constant.STRING: { |
||||||
|
// Lookup the UTF8 at the index and store the String directly.
|
||||||
|
Assert.isTrue(value instanceof Integer, "Invalid constant: " + c); |
||||||
|
|
||||||
|
final int index = ((Integer) value).intValue(); |
||||||
|
Assert.isTrue(constantTag(index) == Constant.UTF8, |
||||||
|
"Invalid constant: " + c); |
||||||
|
|
||||||
|
value = constantAt(index); |
||||||
|
break; |
||||||
|
} |
||||||
|
case Constant.FIELD_REF: |
||||||
|
case Constant.METHOD_REF: |
||||||
|
case Constant.INTERFACE_METHOD_REF: { |
||||||
|
// The constant at the first index should be a CLASS.
|
||||||
|
//
|
||||||
|
// The constant at the second should be a NAME_AND_TYPE.
|
||||||
|
//
|
||||||
|
// Resolve the two constants and then create a MemberRef
|
||||||
|
// for this constant.
|
||||||
|
//
|
||||||
|
Assert.isTrue(value instanceof int[], "Invalid constant: " + c); |
||||||
|
|
||||||
|
final int[] v = (int[]) value; |
||||||
|
|
||||||
|
Assert.isTrue(constantTag(v[0]) == Constant.CLASS, |
||||||
|
"Invalid constant: " + c); |
||||||
|
Assert.isTrue(constantTag(v[1]) == Constant.NAME_AND_TYPE, |
||||||
|
"Invalid constant: " + c); |
||||||
|
|
||||||
|
final Type clazz = (Type) constantAt(v[0]); |
||||||
|
final NameAndType nameAndType = (NameAndType) constantAt(v[1]); |
||||||
|
|
||||||
|
value = new MemberRef(clazz, nameAndType); |
||||||
|
break; |
||||||
|
} |
||||||
|
case Constant.NAME_AND_TYPE: { |
||||||
|
// The constant at the first index should be a UTF8 with the
|
||||||
|
// name of the field.
|
||||||
|
//
|
||||||
|
// The constant at the second should be a UTF8 with the type
|
||||||
|
// of the field.
|
||||||
|
//
|
||||||
|
// Resolve the two constants as a String and a Type and then
|
||||||
|
// create a NameAndType for this constant.
|
||||||
|
//
|
||||||
|
Assert.isTrue(value instanceof int[], "Invalid constant: " + c); |
||||||
|
|
||||||
|
final int[] v = (int[]) value; |
||||||
|
|
||||||
|
Assert.isTrue(constantTag(v[0]) == Constant.UTF8, |
||||||
|
"Invalid constant: " + c); |
||||||
|
Assert.isTrue(constantTag(v[1]) == Constant.UTF8, |
||||||
|
"Invalid constant: " + c); |
||||||
|
|
||||||
|
final String name = (String) constantAt(v[0]); |
||||||
|
final String type = (String) constantAt(v[1]); |
||||||
|
|
||||||
|
value = new NameAndType(name, Type.getType(type)); |
||||||
|
break; |
||||||
|
} |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
resolved.ensureSize(idx + 1); |
||||||
|
resolved.set(idx, value); |
||||||
|
|
||||||
|
return value; |
||||||
|
} |
||||||
|
|
||||||
|
public int numConstants() { |
||||||
|
return constants.size(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the tag of a constant. |
||||||
|
* |
||||||
|
* @param index |
||||||
|
* Index into the constant pool. |
||||||
|
* @return The tag of the constant at index, or Constant.UTF8. |
||||||
|
*/ |
||||||
|
public int constantTag(final int index) { |
||||||
|
if ((0 < index) && (index < constants.size())) { |
||||||
|
final Constant c = (Constant) constants.get(index); |
||||||
|
|
||||||
|
if (c != null) { |
||||||
|
return c.tag(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return Constant.UTF8; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the index of the constant with the given tag and value. |
||||||
|
* |
||||||
|
* @param tag |
||||||
|
* The constant's tag (for example, <tt>Constant.UTF8</tt>). |
||||||
|
* @param value |
||||||
|
* The constant's value (for example, a <tt>String</tt>). |
||||||
|
* @return The index of the constant. |
||||||
|
*/ |
||||||
|
public int constantIndex(final int tag, final Object value) { |
||||||
|
return addConstant(tag, value); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index of the constant pool entry for the given class
|
||||||
|
*/ |
||||||
|
public int getClassIndex(final Class c) { |
||||||
|
return (addConstant(Constant.CLASS, Type.getType(c))); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index of the constant pool entry for the given integer |
||||||
|
*/ |
||||||
|
public int getIntegerIndex(final Integer i) { |
||||||
|
return (addConstant(Constant.INTEGER, i)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index of the constant pool entry for the given float |
||||||
|
*/ |
||||||
|
public int getFloatIndex(final Float f) { |
||||||
|
return (addConstant(Constant.FLOAT, f)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index of the constant pool entry for the given long |
||||||
|
*/ |
||||||
|
public int getLongIndex(final Long l) { |
||||||
|
return (addConstant(Constant.LONG, l)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index of the constant pool entry for the given double |
||||||
|
*/ |
||||||
|
public int getDoubleIndex(final Double d) { |
||||||
|
return (addConstant(Constant.DOUBLE, d)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index of the constant pool entry for the given class. |
||||||
|
*/ |
||||||
|
public int getClassIndex(final Type type) { |
||||||
|
Assert |
||||||
|
.isTrue(type.isObject(), "Type " + type |
||||||
|
+ " is not an class type"); |
||||||
|
// Make sure that the descriptor constant is also there
|
||||||
|
getTypeIndex(type); |
||||||
|
return (addConstant(Constant.CLASS, type)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index of the constant pool entry for the given |
||||||
|
* <code>Type</code> |
||||||
|
*/ |
||||||
|
public int getTypeIndex(final Type type) { |
||||||
|
return (addConstant(Constant.UTF8, type.descriptor())); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index of the constant pool entry for the given String |
||||||
|
*/ |
||||||
|
public int getStringIndex(final String s) { |
||||||
|
return (addConstant(Constant.STRING, s)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index of the constant pool entry for the given |
||||||
|
* <code>MemberRef</code> |
||||||
|
*/ |
||||||
|
public int getMemberRefIndex(final MemberRef ref) { |
||||||
|
return (addConstant(Constant.FIELD_REF, ref)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index of the constant pool entry for the given |
||||||
|
* <code>NameAndType</code> |
||||||
|
*/ |
||||||
|
public int getNameAndTypeIndex(final NameAndType nat) { |
||||||
|
return (addConstant(Constant.NAME_AND_TYPE, nat)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index of the constant pool entry for the given UTF8 string |
||||||
|
*/ |
||||||
|
public int getUTF8Index(final String s) { |
||||||
|
return (addConstant(Constant.UTF8, s)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add a constant to the constant pool. Will not add the same constant |
||||||
|
* twice. |
||||||
|
* |
||||||
|
* @param tag |
||||||
|
* The constant's tag (for example, <tt>Constant.UTF8</tt>). |
||||||
|
* @param value |
||||||
|
* The constant's value (for example, a <tt>String</tt>). |
||||||
|
* @return The index of the constant. |
||||||
|
*/ |
||||||
|
public int addConstant(final int tag, final Object value) { |
||||||
|
if (value == null) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
Constant c; |
||||||
|
|
||||||
|
switch (tag) { |
||||||
|
case Constant.CLASS: { |
||||||
|
Assert.isTrue(value instanceof Type, "Invalid value: " + value); |
||||||
|
final int index = addConstant(Constant.UTF8, ((Type) value) |
||||||
|
.className()); |
||||||
|
c = new Constant(Constant.CLASS, new Integer(index)); |
||||||
|
break; |
||||||
|
} |
||||||
|
case Constant.STRING: { |
||||||
|
Assert.isTrue(value instanceof String, "Invalid value: " + value); |
||||||
|
final int index = addConstant(Constant.UTF8, value); |
||||||
|
c = new Constant(Constant.STRING, new Integer(index)); |
||||||
|
break; |
||||||
|
} |
||||||
|
case Constant.FIELD_REF: |
||||||
|
case Constant.METHOD_REF: |
||||||
|
case Constant.INTERFACE_METHOD_REF: { |
||||||
|
// The constant at the first index should be a CLASS.
|
||||||
|
//
|
||||||
|
// The constant at the second should be a NAME_AND_TYPE.
|
||||||
|
//
|
||||||
|
// Resolve the two constants and then create a MemberRef
|
||||||
|
// for this constant.
|
||||||
|
//
|
||||||
|
Assert |
||||||
|
.isTrue(value instanceof MemberRef, "Invalid value: " |
||||||
|
+ value); |
||||||
|
|
||||||
|
final int[] v = new int[2]; |
||||||
|
|
||||||
|
v[0] = addConstant(Constant.CLASS, ((MemberRef) value) |
||||||
|
.declaringClass()); |
||||||
|
v[1] = addConstant(Constant.NAME_AND_TYPE, ((MemberRef) value) |
||||||
|
.nameAndType()); |
||||||
|
|
||||||
|
c = new Constant(tag, v); |
||||||
|
break; |
||||||
|
} |
||||||
|
case Constant.NAME_AND_TYPE: { |
||||||
|
// The constant at the first index should be a UTF8 with the
|
||||||
|
// name of the field.
|
||||||
|
//
|
||||||
|
// The constant at the second should be a UTF8 with the type
|
||||||
|
// of the field.
|
||||||
|
//
|
||||||
|
// Resolve the two constants as a String and a Type and then
|
||||||
|
// create a NameAndType for this constant.
|
||||||
|
//
|
||||||
|
Assert.isTrue(value instanceof NameAndType, "Invalid value: " |
||||||
|
+ value); |
||||||
|
|
||||||
|
final int[] v = new int[2]; |
||||||
|
|
||||||
|
v[0] = addConstant(Constant.UTF8, ((NameAndType) value).name()); |
||||||
|
v[1] = addConstant(Constant.UTF8, ((NameAndType) value).type() |
||||||
|
.descriptor()); |
||||||
|
|
||||||
|
c = new Constant(tag, v); |
||||||
|
break; |
||||||
|
} |
||||||
|
case Constant.UTF8: { |
||||||
|
final String s = (String) value; |
||||||
|
c = new Constant(tag, s.intern()); |
||||||
|
break; |
||||||
|
} |
||||||
|
default: { |
||||||
|
c = new Constant(tag, value); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Integer index = (Integer) constantIndices.get(c); |
||||||
|
|
||||||
|
if (index == null) { |
||||||
|
index = new Integer(constants.size()); |
||||||
|
constantIndices.put(c, index); |
||||||
|
constants.add(c); |
||||||
|
resolved.add(value); |
||||||
|
|
||||||
|
if ((tag == Constant.LONG) || (tag == Constant.DOUBLE)) { |
||||||
|
constants.add(null); |
||||||
|
resolved.add(null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return index.intValue(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get an array of the constants in the pool. |
||||||
|
* |
||||||
|
* @return An array of the constants in the pool. |
||||||
|
*/ |
||||||
|
public Constant[] constants() { |
||||||
|
final Object[] a = constants.toArray(); |
||||||
|
final Constant[] array = new Constant[a.length]; |
||||||
|
System.arraycopy(a, 0, array, 0, a.length); |
||||||
|
return array; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an unmodifiable List of constants in this constant pool. |
||||||
|
*/ |
||||||
|
public List getConstantsList() { |
||||||
|
return Collections.unmodifiableList(this.constants); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,153 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* An <tt>EditorContext</tt> supplies a means of loading and editing classes. |
||||||
|
* Note that a number of these methods are identical to methods in |
||||||
|
* <tt>Editor</tt>. It is expected that an <tt>EditorContext</tt> will have |
||||||
|
* a different caching (of <tt>ClassEditor</tt>s, etc.) policy than |
||||||
|
* <tt>Editor</tt> does. Hence, the methods in <tt>EditorContext</tt> should |
||||||
|
* be used to edit classes, etc. |
||||||
|
*/ |
||||||
|
public interface EditorContext { |
||||||
|
|
||||||
|
/** |
||||||
|
* Loads a class into BLOAT |
||||||
|
*/ |
||||||
|
public ClassInfo loadClass(String className) throws ClassNotFoundException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new <code>ClassInfo</code> |
||||||
|
* |
||||||
|
* @param modifiers |
||||||
|
* The modifiers describing the newly-created class
|
||||||
|
* @param classIndex |
||||||
|
* The index of the name of the newly-created class in its |
||||||
|
* constant pool |
||||||
|
* @param superClassIndex |
||||||
|
* The index of the name of the newly-created class's superclass |
||||||
|
* in its constant pool |
||||||
|
* @param interfaceIndexes |
||||||
|
* The indexes of the names of the interfaces that the |
||||||
|
* newly-created class implements |
||||||
|
* @param constants |
||||||
|
* The constant pool for the newly created class (a list of |
||||||
|
* {@link Constant}s). |
||||||
|
*/ |
||||||
|
public ClassInfo newClassInfo(int modifiers, int classIndex, |
||||||
|
int superClassIndex, int[] interfaceIndexes, |
||||||
|
java.util.List constants); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the <tt>ClassHierarchy</tt> of all classes and interfaces known |
||||||
|
* to BLOAT. |
||||||
|
*/ |
||||||
|
public ClassHierarchy getHierarchy(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a <code>ClassEditor</code> for editing a new class with the |
||||||
|
* given name. It will override any class with the given name that is |
||||||
|
* already being edited. |
||||||
|
*/ |
||||||
|
public ClassEditor newClass(int modifiers, String className, |
||||||
|
Type superType, Type[] interfaces); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a <tt>ClassEditor</tt> used to edit a class of a given name. |
||||||
|
*/ |
||||||
|
public ClassEditor editClass(String className) |
||||||
|
throws ClassNotFoundException, ClassFormatException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a <tt>ClassEditor</tt> used to edit a class described by a |
||||||
|
* given <tt>Type</tt>. |
||||||
|
*/ |
||||||
|
public ClassEditor editClass(Type classType) throws ClassNotFoundException, |
||||||
|
ClassFormatException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a <tt>ClassEditor</tt> used to edit a class described by a |
||||||
|
* given <tt>ClassInfo</tt>. |
||||||
|
*/ |
||||||
|
public ClassEditor editClass(ClassInfo info); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a <tt>FieldEditor</tt> for editing a <tt>FieldInfo</tt>. |
||||||
|
*/ |
||||||
|
public FieldEditor editField(FieldInfo info); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a <tt>FieldEditor</tt> for editing a field. |
||||||
|
*/ |
||||||
|
public FieldEditor editField(MemberRef field) throws NoSuchFieldException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a <tt>MethodEditor</tt> for editing a method. |
||||||
|
*/ |
||||||
|
public MethodEditor editMethod(MethodInfo info); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a <tt>MethodEditor</tt> for editing a method. |
||||||
|
*/ |
||||||
|
public MethodEditor editMethod(MemberRef method) |
||||||
|
throws NoSuchMethodException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Signals that we are done editing a method. The object used to model it |
||||||
|
* may be reclaimed. |
||||||
|
*/ |
||||||
|
public void release(MethodInfo info); |
||||||
|
|
||||||
|
/** |
||||||
|
* Signals that we are done editing a field. The object used to model it may |
||||||
|
* be reclaimed. |
||||||
|
*/ |
||||||
|
public void release(FieldInfo info); |
||||||
|
|
||||||
|
/** |
||||||
|
* Signals that we are done editing a class. The object used to model it may |
||||||
|
* be reclaimed. |
||||||
|
*/ |
||||||
|
public void release(ClassInfo info); |
||||||
|
|
||||||
|
/** |
||||||
|
* Commits the changes made to a class. |
||||||
|
*/ |
||||||
|
public void commit(ClassInfo info); |
||||||
|
|
||||||
|
/** |
||||||
|
* Commits the changes made to a method. |
||||||
|
*/ |
||||||
|
public void commit(MethodInfo info); |
||||||
|
|
||||||
|
/** |
||||||
|
* Commits the changes made to a field. |
||||||
|
*/ |
||||||
|
public void commit(FieldInfo info); |
||||||
|
|
||||||
|
/** |
||||||
|
* Commits all changes made to classes, methods, and fields. |
||||||
|
*/ |
||||||
|
public void commit(); |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
/** |
||||||
|
* <tt>EditorVisitor</tt> "visits" the "nodes" in a class. Imagine that a |
||||||
|
* class is rooted by a <tt>ClassEditor</tt> that has <tt>FieldEditor</tt> |
||||||
|
* and <tt>MethodEditor</tt> children. |
||||||
|
*/ |
||||||
|
|
||||||
|
public interface EditorVisitor { |
||||||
|
public void visitClassEditor(ClassEditor editor); |
||||||
|
|
||||||
|
public void visitMethodEditor(MethodEditor editor); |
||||||
|
|
||||||
|
public void visitFieldEditor(FieldEditor editor); |
||||||
|
} |
@ -0,0 +1,562 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* <tt>FieldEditor</tt> provides a means to edit a field of a class. A |
||||||
|
* <tt>FieldEditor</tt> is created from a <tt>ClassEditor</tt> and a |
||||||
|
* <tt>reflect.FieldInfo</tt>. A <tt>FieldEditor</tt> knows its name, type |
||||||
|
* (descriptor), and its constant value (if it has one). |
||||||
|
* |
||||||
|
* @see EDU.purdue.cs.bloat.reflect.FieldInfo |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class FieldEditor { |
||||||
|
private ClassEditor editor; |
||||||
|
|
||||||
|
private FieldInfo fieldInfo; |
||||||
|
|
||||||
|
private String name; |
||||||
|
|
||||||
|
private Type type; |
||||||
|
|
||||||
|
private Object constantValue; |
||||||
|
|
||||||
|
private boolean isDirty; |
||||||
|
|
||||||
|
private boolean isDeleted = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new <code>FieldEditor</code> for editing a field in a given |
||||||
|
* class with the given modifiers, type and name |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException |
||||||
|
* If a field with the desired name already exists in the class
|
||||||
|
*/ |
||||||
|
public FieldEditor(final ClassEditor editor, final int modifiers, |
||||||
|
final Type type, final String name) { |
||||||
|
|
||||||
|
this(editor, modifiers, type, name, null); |
||||||
|
} |
||||||
|
|
||||||
|
public FieldEditor(final ClassEditor editor, final int modifiers, |
||||||
|
final Class type, final String name, final Object constantValue) { |
||||||
|
this(editor, modifiers, Type.getType(type), name, constantValue); |
||||||
|
} |
||||||
|
|
||||||
|
public FieldEditor(final ClassEditor editor, final int modifiers, |
||||||
|
final Class type, final String name) { |
||||||
|
this(editor, modifiers, Type.getType(type), name, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new <code>FieldEditor</code> for editing a field in a given |
||||||
|
* class with the given modifiers, type, name, and constant value. |
||||||
|
* |
||||||
|
* @param modifiers |
||||||
|
* Fields that have a constant value must be <code>static</code> |
||||||
|
* and <code>final</code> |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException |
||||||
|
* If a field with the desired name already exists in the class
|
||||||
|
* or if <code>constantValue</code> is non-null and neither a |
||||||
|
* <code>String</code>, <code>Integer</code>, |
||||||
|
* <code>Long</code>, <code>Float</code>, nor |
||||||
|
* <code>Double</code>. |
||||||
|
*/ |
||||||
|
public FieldEditor(final ClassEditor editor, final int modifiers, |
||||||
|
final Type type, final String name, final Object constantValue) { |
||||||
|
|
||||||
|
// Does the class already have a field with this name?
|
||||||
|
final FieldInfo[] fields = editor.fields(); |
||||||
|
for (int i = 0; i < fields.length; i++) { |
||||||
|
final FieldEditor fe = new FieldEditor(editor, fields[i]); |
||||||
|
if (fe.name().equals(name)) { |
||||||
|
final String s = "A field named " + name |
||||||
|
+ " already exists in " + editor.name(); |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
this.editor = editor; |
||||||
|
|
||||||
|
final ConstantPool cp = editor.constants(); |
||||||
|
this.name = name; |
||||||
|
this.type = type; |
||||||
|
|
||||||
|
final int typeIndex = cp.getUTF8Index(this.type.descriptor()); |
||||||
|
final int nameIndex = cp.getUTF8Index(name); |
||||||
|
|
||||||
|
final ClassInfo classInfo = editor.classInfo(); |
||||||
|
|
||||||
|
if (constantValue != null) { |
||||||
|
// Only static final field may have constant values
|
||||||
|
if (((modifiers & Modifiers.STATIC) == 0) |
||||||
|
|| ((modifiers & Modifiers.FINAL) == 0)) { |
||||||
|
final String s = "Field " + name |
||||||
|
+ " with a constant value must be static and final"; |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
} |
||||||
|
|
||||||
|
// Create an entry in the constant pool for the constant value
|
||||||
|
// of this field
|
||||||
|
int valueIndex; |
||||||
|
if (constantValue instanceof String) { |
||||||
|
if (!type.equals(Type.STRING)) { |
||||||
|
final String s = "Can't have field type of " |
||||||
|
+ type.className() + " with a constant value of \"" |
||||||
|
+ constantValue + "\""; |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
} |
||||||
|
valueIndex = cp.getStringIndex((String) constantValue); |
||||||
|
|
||||||
|
} else if (constantValue instanceof Integer) { |
||||||
|
if (!type.equals(Type.INTEGER)) { |
||||||
|
final String s = "Can't have field type of " |
||||||
|
+ type.className() + " with a constant value of \"" |
||||||
|
+ constantValue + "\""; |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
} |
||||||
|
valueIndex = cp.getIntegerIndex((Integer) constantValue); |
||||||
|
|
||||||
|
} else if (constantValue instanceof Long) { |
||||||
|
if (!type.equals(Type.LONG)) { |
||||||
|
final String s = "Can't have field type of " |
||||||
|
+ type.className() + " with a constant value of \"" |
||||||
|
+ constantValue + "\""; |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
} |
||||||
|
valueIndex = cp.getLongIndex((Long) constantValue); |
||||||
|
|
||||||
|
} else if (constantValue instanceof Float) { |
||||||
|
if (!type.equals(Type.FLOAT)) { |
||||||
|
final String s = "Can't have field type of " |
||||||
|
+ type.className() + " with a constant value of \"" |
||||||
|
+ constantValue + "\""; |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
} |
||||||
|
valueIndex = cp.getFloatIndex((Float) constantValue); |
||||||
|
|
||||||
|
} else if (constantValue instanceof Double) { |
||||||
|
if (!type.equals(Type.DOUBLE)) { |
||||||
|
final String s = "Can't have field type of " |
||||||
|
+ type.className() + " with a constant value of \"" |
||||||
|
+ constantValue + "\""; |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
} |
||||||
|
valueIndex = cp.getDoubleIndex((Double) constantValue); |
||||||
|
|
||||||
|
} else { |
||||||
|
final String s = "Cannot have a constant value of type " |
||||||
|
+ constantValue.getClass().getName(); |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
} |
||||||
|
|
||||||
|
this.constantValue = constantValue; |
||||||
|
|
||||||
|
final int cvNameIndex = cp.getUTF8Index("ConstantValue"); |
||||||
|
this.fieldInfo = classInfo.addNewField(modifiers, typeIndex, |
||||||
|
nameIndex, cvNameIndex, valueIndex); |
||||||
|
|
||||||
|
} else { |
||||||
|
this.fieldInfo = classInfo.addNewField(modifiers, typeIndex, |
||||||
|
nameIndex); |
||||||
|
} |
||||||
|
|
||||||
|
this.isDirty = true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param editor |
||||||
|
* The class containing the field. |
||||||
|
* @param fieldInfo |
||||||
|
* The field to edit. |
||||||
|
* |
||||||
|
* @see ClassEditor |
||||||
|
* @see FieldInfo |
||||||
|
*/ |
||||||
|
public FieldEditor(final ClassEditor editor, final FieldInfo fieldInfo) { |
||||||
|
final ConstantPool cp = editor.constants(); |
||||||
|
|
||||||
|
this.fieldInfo = fieldInfo; |
||||||
|
this.editor = editor; |
||||||
|
|
||||||
|
int index; |
||||||
|
|
||||||
|
index = fieldInfo.nameIndex(); |
||||||
|
name = (String) cp.constantAt(index); |
||||||
|
|
||||||
|
index = fieldInfo.typeIndex(); |
||||||
|
final String typeName = (String) cp.constantAt(index); |
||||||
|
type = Type.getType(typeName); |
||||||
|
|
||||||
|
index = fieldInfo.constantValue(); |
||||||
|
constantValue = cp.constantAt(index); |
||||||
|
this.isDirty = false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the <tt>ClassEditor</tt> used to edit the class in which this |
||||||
|
* field resides. |
||||||
|
*/ |
||||||
|
public ClassEditor declaringClass() { |
||||||
|
return editor; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns <tt>true</tt> if this field has been modified. |
||||||
|
*/ |
||||||
|
public boolean isDirty() { |
||||||
|
return (this.isDirty); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the dirty flag of this method. The dirty flag is <tt>true</tt> if |
||||||
|
* the method has been modified. |
||||||
|
* |
||||||
|
* @throws IllegalStateException This field has been marked for deletion |
||||||
|
*/ |
||||||
|
public void setDirty(final boolean isDirty) { |
||||||
|
if (this.isDeleted) { |
||||||
|
final String s = "Cannot change a field once it has been marked " |
||||||
|
+ "for deletion"; |
||||||
|
throw new IllegalStateException(s); |
||||||
|
} |
||||||
|
|
||||||
|
this.isDirty = isDirty; |
||||||
|
if (isDirty == true) { |
||||||
|
this.editor.setDirty(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Marks this field for deletion. Once a field has been marked for deletion |
||||||
|
* all attempts to change it will throw an |
||||||
|
* <code>IllegalStateException</code>. |
||||||
|
*/ |
||||||
|
public void delete() { |
||||||
|
this.setDirty(true); |
||||||
|
this.isDeleted = true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the raw FieldInfo of the field being edited. |
||||||
|
*/ |
||||||
|
public FieldInfo fieldInfo() { |
||||||
|
return fieldInfo; |
||||||
|
} |
||||||
|
|
||||||
|
public Object constantValue() { |
||||||
|
return constantValue; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isPublic() { |
||||||
|
return (fieldInfo.modifiers() & Modifiers.PUBLIC) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isPrivate() { |
||||||
|
return (fieldInfo.modifiers() & Modifiers.PRIVATE) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isProtected() { |
||||||
|
return (fieldInfo.modifiers() & Modifiers.PROTECTED) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true, if the field has package level visibility. |
||||||
|
*/ |
||||||
|
public boolean isPackage() { |
||||||
|
return (!isPublic() && !isPrivate() && !isProtected()); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isStatic() { |
||||||
|
return (fieldInfo.modifiers() & Modifiers.STATIC) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isFinal() { |
||||||
|
return (fieldInfo.modifiers() & Modifiers.FINAL) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isVolatile() { |
||||||
|
return (fieldInfo.modifiers() & Modifiers.VOLATILE) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isTransient() { |
||||||
|
return (fieldInfo.modifiers() & Modifiers.TRANSIENT) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @throws IllegalStateException This field has been marked for deletion |
||||||
|
*/ |
||||||
|
public void setPublic(final boolean flag) { |
||||||
|
if (this.isDeleted) { |
||||||
|
final String s = "Cannot change a field once it has been marked " |
||||||
|
+ "for deletion"; |
||||||
|
throw new IllegalStateException(s); |
||||||
|
} |
||||||
|
|
||||||
|
int modifiers = fieldInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.PUBLIC; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.PUBLIC; |
||||||
|
} |
||||||
|
|
||||||
|
fieldInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @throws IllegalStateException This field has been marked for deletion |
||||||
|
*/ |
||||||
|
public void setPrivate(final boolean flag) { |
||||||
|
if (this.isDeleted) { |
||||||
|
final String s = "Cannot change a field once it has been marked " |
||||||
|
+ "for deletion"; |
||||||
|
throw new IllegalStateException(s); |
||||||
|
} |
||||||
|
|
||||||
|
int modifiers = fieldInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.PRIVATE; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.PRIVATE; |
||||||
|
} |
||||||
|
|
||||||
|
fieldInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @throws IllegalStateException This field has been marked for deletion |
||||||
|
*/ |
||||||
|
public void setProtected(final boolean flag) { |
||||||
|
if (this.isDeleted) { |
||||||
|
final String s = "Cannot change a field once it has been marked " |
||||||
|
+ "for deletion"; |
||||||
|
throw new IllegalStateException(s); |
||||||
|
} |
||||||
|
|
||||||
|
int modifiers = fieldInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.PROTECTED; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.PROTECTED; |
||||||
|
} |
||||||
|
|
||||||
|
fieldInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @throws IllegalStateException This field has been marked for deletion |
||||||
|
*/ |
||||||
|
public void setStatic(final boolean flag) { |
||||||
|
if (this.isDeleted) { |
||||||
|
final String s = "Cannot change a field once it has been marked " |
||||||
|
+ "for deletion"; |
||||||
|
throw new IllegalStateException(s); |
||||||
|
} |
||||||
|
|
||||||
|
int modifiers = fieldInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.STATIC; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.STATIC; |
||||||
|
} |
||||||
|
|
||||||
|
fieldInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @throws IllegalStateException This field has been marked for deletion |
||||||
|
*/ |
||||||
|
public void setFinal(final boolean flag) { |
||||||
|
if (this.isDeleted) { |
||||||
|
final String s = "Cannot change a field once it has been marked " |
||||||
|
+ "for deletion"; |
||||||
|
throw new IllegalStateException(s); |
||||||
|
} |
||||||
|
|
||||||
|
int modifiers = fieldInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.FINAL; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.FINAL; |
||||||
|
} |
||||||
|
|
||||||
|
fieldInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @throws IllegalStateException This field has been marked for deletion |
||||||
|
*/ |
||||||
|
public void setTransient(final boolean flag) { |
||||||
|
if (this.isDeleted) { |
||||||
|
final String s = "Cannot change a field once it has been marked " |
||||||
|
+ "for deletion"; |
||||||
|
throw new IllegalStateException(s); |
||||||
|
} |
||||||
|
|
||||||
|
int modifiers = fieldInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.TRANSIENT; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.TRANSIENT; |
||||||
|
} |
||||||
|
|
||||||
|
fieldInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @throws IllegalStateException This field has been marked for deletion |
||||||
|
*/ |
||||||
|
public void setVolatile(final boolean flag) { |
||||||
|
if (this.isDeleted) { |
||||||
|
final String s = "Cannot change a field once it has been marked " |
||||||
|
+ "for deletion"; |
||||||
|
throw new IllegalStateException(s); |
||||||
|
} |
||||||
|
|
||||||
|
int modifiers = fieldInfo.modifiers(); |
||||||
|
|
||||||
|
if (flag) { |
||||||
|
modifiers |= Modifiers.VOLATILE; |
||||||
|
} else { |
||||||
|
modifiers &= ~Modifiers.VOLATILE; |
||||||
|
} |
||||||
|
|
||||||
|
fieldInfo.setModifiers(modifiers); |
||||||
|
this.setDirty(true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the name of the field. |
||||||
|
*/ |
||||||
|
public String name() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the type of the field. |
||||||
|
*/ |
||||||
|
public Type type() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a <tt>NameAndType</tt> of the field. |
||||||
|
*/ |
||||||
|
public NameAndType nameAndType() { |
||||||
|
return (new NameAndType(this.name(), this.type())); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a <code>MemberRef</code> for the field |
||||||
|
*/ |
||||||
|
public MemberRef memberRef() { |
||||||
|
return (new MemberRef(this.declaringClass().type(), this.nameAndType())); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Commit changes to the field back to the ClassEditor. Note that the field |
||||||
|
* is committed regardless of whether or not it is dirty. |
||||||
|
*/ |
||||||
|
public void commit() { |
||||||
|
if (this.isDeleted) { |
||||||
|
// Even if the field is newly-added, we can still delete it
|
||||||
|
// without problems because its FieldInfo was already noted with
|
||||||
|
// the ClassInfo.
|
||||||
|
|
||||||
|
final ConstantPool cp = editor.constants(); |
||||||
|
final int nameIndex = cp.getUTF8Index(name); |
||||||
|
this.editor.classInfo().deleteField(nameIndex); |
||||||
|
|
||||||
|
} else { |
||||||
|
final ConstantPool cp = editor.constants(); |
||||||
|
|
||||||
|
fieldInfo.setNameIndex(cp.addConstant(Constant.UTF8, name)); |
||||||
|
fieldInfo.setTypeIndex(cp.addConstant(Constant.UTF8, type |
||||||
|
.descriptor())); |
||||||
|
|
||||||
|
if (constantValue != null) { |
||||||
|
if (constantValue instanceof Long) { |
||||||
|
fieldInfo.setConstantValue(cp.addConstant(Constant.LONG, |
||||||
|
constantValue)); |
||||||
|
} else if (constantValue instanceof Float) { |
||||||
|
fieldInfo.setConstantValue(cp.addConstant(Constant.FLOAT, |
||||||
|
constantValue)); |
||||||
|
} else if (constantValue instanceof Double) { |
||||||
|
fieldInfo.setConstantValue(cp.addConstant(Constant.DOUBLE, |
||||||
|
constantValue)); |
||||||
|
} else if (constantValue instanceof Integer) { |
||||||
|
fieldInfo.setConstantValue(cp.addConstant(Constant.INTEGER, |
||||||
|
constantValue)); |
||||||
|
} else if (constantValue instanceof String) { |
||||||
|
fieldInfo.setConstantValue(cp.addConstant(Constant.STRING, |
||||||
|
constantValue)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// This field is no longer dirty
|
||||||
|
this.isDirty = false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Print the field. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* Stream to which to print. |
||||||
|
*/ |
||||||
|
public void print(final PrintStream out) { |
||||||
|
out.println("field " + name + " " + type); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a String that contains the declaring class name and the name of |
||||||
|
* the field |
||||||
|
*/ |
||||||
|
public String fullName() { |
||||||
|
return declaringClass().name() + "." + this.name(); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return ("[FieldEditor for " + this.name + this.type + "]"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,75 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
/** |
||||||
|
* IncOperand encapsulates the operands to the iinc instruction. It is necessary |
||||||
|
* because the <tt>iinc</tt> has two operands: a local variable and an integer |
||||||
|
* by which to increment the local variable. |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class IncOperand { |
||||||
|
private LocalVariable var; |
||||||
|
|
||||||
|
private int incr; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param var |
||||||
|
* The local variable to increment. |
||||||
|
* @param incr |
||||||
|
* The amount to increment by. |
||||||
|
*/ |
||||||
|
public IncOperand(final LocalVariable var, final int incr) { |
||||||
|
this.var = var; |
||||||
|
this.incr = incr; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the local variable to increment. |
||||||
|
* |
||||||
|
* @return The local variable to increment. |
||||||
|
*/ |
||||||
|
public LocalVariable var() { |
||||||
|
return var; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the amount to increment by. |
||||||
|
* |
||||||
|
* @return The amount to increment by. |
||||||
|
*/ |
||||||
|
public int incr() { |
||||||
|
return incr; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convert the operand to a string. |
||||||
|
* |
||||||
|
* @return A string representation of the operand. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
return "" + var + " by " + incr; |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,459 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
/** |
||||||
|
* This adapter provides a default implementation for every method in |
||||||
|
* InstructionVisitor. |
||||||
|
*/ |
||||||
|
public class InstructionAdapter implements InstructionVisitor { |
||||||
|
public void visit_nop(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ldc(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_iload(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lload(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_fload(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dload(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_aload(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_iaload(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_laload(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_faload(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_daload(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_aaload(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_baload(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_caload(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_saload(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_istore(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lstore(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_fstore(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dstore(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_astore(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_iastore(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lastore(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_fastore(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dastore(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_aastore(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_bastore(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_castore(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_sastore(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_pop(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_pop2(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dup(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dup_x1(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dup_x2(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dup2(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dup2_x1(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dup2_x2(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_swap(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_iadd(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ladd(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_fadd(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dadd(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_isub(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lsub(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_fsub(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dsub(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_imul(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lmul(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_fmul(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dmul(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_idiv(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ldiv(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_fdiv(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ddiv(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_irem(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lrem(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_frem(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_drem(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ineg(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lneg(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_fneg(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dneg(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ishl(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lshl(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ishr(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lshr(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_iushr(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lushr(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_iand(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_land(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ior(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lor(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ixor(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lxor(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_iinc(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_i2l(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_i2f(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_i2d(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_l2i(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_l2f(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_l2d(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_f2i(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_f2l(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_f2d(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_d2i(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_d2l(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_d2f(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_i2b(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_i2c(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_i2s(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lcmp(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_fcmpl(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_fcmpg(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dcmpl(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dcmpg(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ifeq(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ifne(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_iflt(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ifge(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ifgt(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ifle(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_if_icmpeq(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_if_icmpne(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_if_icmplt(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_if_icmpge(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_if_icmpgt(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_if_icmple(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_if_acmpeq(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_if_acmpne(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_goto(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_jsr(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ret(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_switch(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ireturn(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_lreturn(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_freturn(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_dreturn(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_areturn(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_return(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_getstatic(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_putstatic(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_putstatic_nowb(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_getfield(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_putfield(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_putfield_nowb(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_invokevirtual(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_invokespecial(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_invokestatic(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_invokeinterface(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_new(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_newarray(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_arraylength(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_athrow(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_checkcast(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_instanceof(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_monitorenter(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_monitorexit(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_multianewarray(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ifnull(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_ifnonnull(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_rc(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_aupdate(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_supdate(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_aswizzle(final Instruction inst) { |
||||||
|
} |
||||||
|
|
||||||
|
public void visit_aswrange(final Instruction inst) { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,330 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
/** |
||||||
|
* The visitor pattern allows functionality to be added to a number of classes |
||||||
|
* (or in this case one class, <tt>Instruction</tt>, that can vary in |
||||||
|
* behavior) without modifying the classes themselves. Additionally, the visitor |
||||||
|
* pattern simulates double dispatching. For instance <tt>visit</tt> method of |
||||||
|
* <tt>Instruction</tt> calls a particular method of |
||||||
|
* <tt>InstructionVisitor</tt> based on the <tt>Instruction</tt>'s opcode. |
||||||
|
* <p> |
||||||
|
* <tt>InstructionVisitor</tt> provides an interface for performing actions |
||||||
|
* based on the instruction type. Classes implementing this interface should not |
||||||
|
* be able to miss any of the instruction types. This interface was created as |
||||||
|
* an alternative to having 138 different subtypes of Instruction. |
||||||
|
* |
||||||
|
* @see Instruction#visit |
||||||
|
* @see EDU.purdue.cs.bloat.tree.Tree |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public interface InstructionVisitor { |
||||||
|
public void visit_nop(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ldc(Instruction inst); |
||||||
|
|
||||||
|
public void visit_iload(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lload(Instruction inst); |
||||||
|
|
||||||
|
public void visit_fload(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dload(Instruction inst); |
||||||
|
|
||||||
|
public void visit_aload(Instruction inst); |
||||||
|
|
||||||
|
public void visit_iaload(Instruction inst); |
||||||
|
|
||||||
|
public void visit_laload(Instruction inst); |
||||||
|
|
||||||
|
public void visit_faload(Instruction inst); |
||||||
|
|
||||||
|
public void visit_daload(Instruction inst); |
||||||
|
|
||||||
|
public void visit_aaload(Instruction inst); |
||||||
|
|
||||||
|
public void visit_baload(Instruction inst); |
||||||
|
|
||||||
|
public void visit_caload(Instruction inst); |
||||||
|
|
||||||
|
public void visit_saload(Instruction inst); |
||||||
|
|
||||||
|
public void visit_istore(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lstore(Instruction inst); |
||||||
|
|
||||||
|
public void visit_fstore(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dstore(Instruction inst); |
||||||
|
|
||||||
|
public void visit_astore(Instruction inst); |
||||||
|
|
||||||
|
public void visit_iastore(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lastore(Instruction inst); |
||||||
|
|
||||||
|
public void visit_fastore(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dastore(Instruction inst); |
||||||
|
|
||||||
|
public void visit_aastore(Instruction inst); |
||||||
|
|
||||||
|
public void visit_bastore(Instruction inst); |
||||||
|
|
||||||
|
public void visit_castore(Instruction inst); |
||||||
|
|
||||||
|
public void visit_sastore(Instruction inst); |
||||||
|
|
||||||
|
public void visit_pop(Instruction inst); |
||||||
|
|
||||||
|
public void visit_pop2(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dup(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dup_x1(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dup_x2(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dup2(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dup2_x1(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dup2_x2(Instruction inst); |
||||||
|
|
||||||
|
public void visit_swap(Instruction inst); |
||||||
|
|
||||||
|
public void visit_iadd(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ladd(Instruction inst); |
||||||
|
|
||||||
|
public void visit_fadd(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dadd(Instruction inst); |
||||||
|
|
||||||
|
public void visit_isub(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lsub(Instruction inst); |
||||||
|
|
||||||
|
public void visit_fsub(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dsub(Instruction inst); |
||||||
|
|
||||||
|
public void visit_imul(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lmul(Instruction inst); |
||||||
|
|
||||||
|
public void visit_fmul(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dmul(Instruction inst); |
||||||
|
|
||||||
|
public void visit_idiv(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ldiv(Instruction inst); |
||||||
|
|
||||||
|
public void visit_fdiv(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ddiv(Instruction inst); |
||||||
|
|
||||||
|
public void visit_irem(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lrem(Instruction inst); |
||||||
|
|
||||||
|
public void visit_frem(Instruction inst); |
||||||
|
|
||||||
|
public void visit_drem(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ineg(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lneg(Instruction inst); |
||||||
|
|
||||||
|
public void visit_fneg(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dneg(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ishl(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lshl(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ishr(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lshr(Instruction inst); |
||||||
|
|
||||||
|
public void visit_iushr(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lushr(Instruction inst); |
||||||
|
|
||||||
|
public void visit_iand(Instruction inst); |
||||||
|
|
||||||
|
public void visit_land(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ior(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lor(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ixor(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lxor(Instruction inst); |
||||||
|
|
||||||
|
public void visit_iinc(Instruction inst); |
||||||
|
|
||||||
|
public void visit_i2l(Instruction inst); |
||||||
|
|
||||||
|
public void visit_i2f(Instruction inst); |
||||||
|
|
||||||
|
public void visit_i2d(Instruction inst); |
||||||
|
|
||||||
|
public void visit_l2i(Instruction inst); |
||||||
|
|
||||||
|
public void visit_l2f(Instruction inst); |
||||||
|
|
||||||
|
public void visit_l2d(Instruction inst); |
||||||
|
|
||||||
|
public void visit_f2i(Instruction inst); |
||||||
|
|
||||||
|
public void visit_f2l(Instruction inst); |
||||||
|
|
||||||
|
public void visit_f2d(Instruction inst); |
||||||
|
|
||||||
|
public void visit_d2i(Instruction inst); |
||||||
|
|
||||||
|
public void visit_d2l(Instruction inst); |
||||||
|
|
||||||
|
public void visit_d2f(Instruction inst); |
||||||
|
|
||||||
|
public void visit_i2b(Instruction inst); |
||||||
|
|
||||||
|
public void visit_i2c(Instruction inst); |
||||||
|
|
||||||
|
public void visit_i2s(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lcmp(Instruction inst); |
||||||
|
|
||||||
|
public void visit_fcmpl(Instruction inst); |
||||||
|
|
||||||
|
public void visit_fcmpg(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dcmpl(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dcmpg(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ifeq(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ifne(Instruction inst); |
||||||
|
|
||||||
|
public void visit_iflt(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ifge(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ifgt(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ifle(Instruction inst); |
||||||
|
|
||||||
|
public void visit_if_icmpeq(Instruction inst); |
||||||
|
|
||||||
|
public void visit_if_icmpne(Instruction inst); |
||||||
|
|
||||||
|
public void visit_if_icmplt(Instruction inst); |
||||||
|
|
||||||
|
public void visit_if_icmpge(Instruction inst); |
||||||
|
|
||||||
|
public void visit_if_icmpgt(Instruction inst); |
||||||
|
|
||||||
|
public void visit_if_icmple(Instruction inst); |
||||||
|
|
||||||
|
public void visit_if_acmpeq(Instruction inst); |
||||||
|
|
||||||
|
public void visit_if_acmpne(Instruction inst); |
||||||
|
|
||||||
|
public void visit_goto(Instruction inst); |
||||||
|
|
||||||
|
public void visit_jsr(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ret(Instruction inst); |
||||||
|
|
||||||
|
public void visit_switch(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ireturn(Instruction inst); |
||||||
|
|
||||||
|
public void visit_lreturn(Instruction inst); |
||||||
|
|
||||||
|
public void visit_freturn(Instruction inst); |
||||||
|
|
||||||
|
public void visit_dreturn(Instruction inst); |
||||||
|
|
||||||
|
public void visit_areturn(Instruction inst); |
||||||
|
|
||||||
|
public void visit_return(Instruction inst); |
||||||
|
|
||||||
|
public void visit_getstatic(Instruction inst); |
||||||
|
|
||||||
|
public void visit_putstatic(Instruction inst); |
||||||
|
|
||||||
|
public void visit_putstatic_nowb(Instruction inst); |
||||||
|
|
||||||
|
public void visit_getfield(Instruction inst); |
||||||
|
|
||||||
|
public void visit_putfield(Instruction inst); |
||||||
|
|
||||||
|
public void visit_putfield_nowb(Instruction inst); |
||||||
|
|
||||||
|
public void visit_invokevirtual(Instruction inst); |
||||||
|
|
||||||
|
public void visit_invokespecial(Instruction inst); |
||||||
|
|
||||||
|
public void visit_invokestatic(Instruction inst); |
||||||
|
|
||||||
|
public void visit_invokeinterface(Instruction inst); |
||||||
|
|
||||||
|
public void visit_new(Instruction inst); |
||||||
|
|
||||||
|
public void visit_newarray(Instruction inst); |
||||||
|
|
||||||
|
public void visit_arraylength(Instruction inst); |
||||||
|
|
||||||
|
public void visit_athrow(Instruction inst); |
||||||
|
|
||||||
|
public void visit_checkcast(Instruction inst); |
||||||
|
|
||||||
|
public void visit_instanceof(Instruction inst); |
||||||
|
|
||||||
|
public void visit_monitorenter(Instruction inst); |
||||||
|
|
||||||
|
public void visit_monitorexit(Instruction inst); |
||||||
|
|
||||||
|
public void visit_multianewarray(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ifnull(Instruction inst); |
||||||
|
|
||||||
|
public void visit_ifnonnull(Instruction inst); |
||||||
|
|
||||||
|
public void visit_rc(Instruction inst); |
||||||
|
|
||||||
|
public void visit_aupdate(Instruction inst); |
||||||
|
|
||||||
|
public void visit_supdate(Instruction inst); |
||||||
|
|
||||||
|
public void visit_aswizzle(Instruction inst); |
||||||
|
|
||||||
|
public void visit_aswrange(Instruction inst); |
||||||
|
} |
@ -0,0 +1,141 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
/** |
||||||
|
* <tt>Label</tt> is used to label an instruction. <tt>Label</tt>s are used |
||||||
|
* to preserve the location of branch targets. A <tt>Label</tt> consists of an |
||||||
|
* index into the code array and a <tt>boolean</tt> that determines whether or |
||||||
|
* not it starts a basic block. |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class Label { |
||||||
|
public static boolean TRACE = false; |
||||||
|
|
||||||
|
private int index; |
||||||
|
|
||||||
|
private boolean startsBlock; |
||||||
|
|
||||||
|
private String comment; // Comment with Label
|
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param index |
||||||
|
* A unique index for the label. For instance, its offset in the |
||||||
|
* instruction array. |
||||||
|
*/ |
||||||
|
public Label(final int index) { |
||||||
|
this(index, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param index |
||||||
|
* The index of this label into the instruction array |
||||||
|
* @param startsBlock |
||||||
|
* True if the label is the first instruction in a basic block, |
||||||
|
* false if not. |
||||||
|
*/ |
||||||
|
public Label(final int index, final boolean startsBlock) { |
||||||
|
this.index = index; |
||||||
|
this.startsBlock = startsBlock; |
||||||
|
|
||||||
|
// if(Label.TRACE) {
|
||||||
|
// try {
|
||||||
|
// throw new Exception("Creating a new label: " + this);
|
||||||
|
// } catch(Exception ex) {
|
||||||
|
// ex.printStackTrace(System.out);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the comment associated with this <tt>Label</tt>. |
||||||
|
*/ |
||||||
|
public void setComment(final String comment) { |
||||||
|
this.comment = comment; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set if the label starts a block. |
||||||
|
* |
||||||
|
* @param startsBlock |
||||||
|
* True if the label starts a block, false if not. |
||||||
|
*/ |
||||||
|
public void setStartsBlock(final boolean startsBlock) { |
||||||
|
this.startsBlock = startsBlock; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Check if the label starts a block. |
||||||
|
* |
||||||
|
* @return True if the label starts a block, false if not. |
||||||
|
*/ |
||||||
|
public boolean startsBlock() { |
||||||
|
return startsBlock; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the index of the label. |
||||||
|
* |
||||||
|
* @return The index of the label. |
||||||
|
*/ |
||||||
|
public int index() { |
||||||
|
return index; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Hash the label. |
||||||
|
* |
||||||
|
* @return The hash code. |
||||||
|
*/ |
||||||
|
public int hashCode() { |
||||||
|
return index; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Check if an object is equal to this label. |
||||||
|
* |
||||||
|
* @param obj |
||||||
|
* The object to compare against. |
||||||
|
* @return true if equal, false if not. |
||||||
|
*/ |
||||||
|
public boolean equals(final Object obj) { |
||||||
|
return ((obj instanceof Label) && (((Label) obj).index == index)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convert the label to a string. |
||||||
|
* |
||||||
|
* @return A string representation of the label. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
if (comment != null) { |
||||||
|
return "label_" + index + " (" + comment + ")"; |
||||||
|
} else { |
||||||
|
return "label_" + index; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,134 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
/** |
||||||
|
* LocalVariable represents a local variable index operand to various |
||||||
|
* instructions. |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class LocalVariable { |
||||||
|
private String name; |
||||||
|
|
||||||
|
private Type type; |
||||||
|
|
||||||
|
private int index; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param index |
||||||
|
* The index of the local variable in the method's local variable |
||||||
|
* array. |
||||||
|
*/ |
||||||
|
public LocalVariable(final int index) { |
||||||
|
this.name = null; |
||||||
|
this.type = null; |
||||||
|
this.index = index; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param name |
||||||
|
* The name of the local variable. |
||||||
|
* @param type |
||||||
|
* The descriptor (or index into the constant pool) representing |
||||||
|
* the variable type. |
||||||
|
* @param index |
||||||
|
* The index of the local variable in the method's local variable |
||||||
|
* array. |
||||||
|
*/ |
||||||
|
public LocalVariable(final String name, final Type type, final int index) { |
||||||
|
this.name = name; |
||||||
|
this.type = type; |
||||||
|
this.index = index; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Hash the local variable. |
||||||
|
* |
||||||
|
* A stricter hashing than using the index will break Hashtable lookups |
||||||
|
* since a variable could have a name assigned to it after its first use. |
||||||
|
* |
||||||
|
* @return The hash code. |
||||||
|
*/ |
||||||
|
public int hashCode() { |
||||||
|
return index; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Check if an object is equal to this variable. |
||||||
|
* |
||||||
|
* A stricter comparison than comparing indices will break Hashtable lookups |
||||||
|
* since a variable could have a name assigned to it after its first use. |
||||||
|
* |
||||||
|
* @param obj |
||||||
|
* The object to compare against. |
||||||
|
* @return true if equal, false if not. |
||||||
|
*/ |
||||||
|
public boolean equals(final Object obj) { |
||||||
|
return (obj != null) && (obj instanceof LocalVariable) |
||||||
|
&& (((LocalVariable) obj).index == index); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the name of the local variable. |
||||||
|
* |
||||||
|
* @return The name of the local variable. |
||||||
|
*/ |
||||||
|
public String name() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the type of the local variable. |
||||||
|
* |
||||||
|
* @return The type of the local variable. |
||||||
|
*/ |
||||||
|
public Type type() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the index into the local variable array. |
||||||
|
* |
||||||
|
* @return The index into the local variable array. |
||||||
|
*/ |
||||||
|
public int index() { |
||||||
|
return index; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convert the variable to a string. |
||||||
|
* |
||||||
|
* @return A string representation of the variable. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
if (name == null) { |
||||||
|
return "Local$" + index; |
||||||
|
} |
||||||
|
|
||||||
|
return name + "$" + index; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
# All files in the distribution of BLOAT (Bytecode Level Optimization and
|
||||||
|
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
||||||
|
# Research Foundation of Purdue University. All rights reserved.
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
|
||||||
|
CLASS = \
|
||||||
|
ClassEditor.class\
|
||||||
|
ClassHierarchy.class\
|
||||||
|
CodeArray.class\
|
||||||
|
ConstantPool.class\
|
||||||
|
EditorContext.class\
|
||||||
|
FieldEditor.class\
|
||||||
|
IncOperand.class\
|
||||||
|
Instruction.class\
|
||||||
|
InstructionAdapter.class\
|
||||||
|
InstructionVisitor.class\
|
||||||
|
Label.class\
|
||||||
|
LocalVariable.class\
|
||||||
|
MemberRef.class\
|
||||||
|
MethodEditor.class\
|
||||||
|
MultiArrayOperand.class\
|
||||||
|
NameAndType.class\
|
||||||
|
Opcode.class\
|
||||||
|
SerialVersionUID.class\
|
||||||
|
Switch.class\
|
||||||
|
TryCatch.class\
|
||||||
|
Type.class\
|
||||||
|
TypeComparator.class
|
||||||
|
|
||||||
|
include ../class.mk |
@ -0,0 +1,117 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
/** |
||||||
|
* MemberRef represents a method or field (as a <tt>NameAndType</tt>) and the |
||||||
|
* class (as a <tt>Type</tt>) in which it is declared. |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class MemberRef { |
||||||
|
private Type declaringClass; |
||||||
|
|
||||||
|
private NameAndType nameAndType; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param declaringClass |
||||||
|
* The type of the class which declared the member. |
||||||
|
* @param nameAndType |
||||||
|
* The name and type of the member. |
||||||
|
*/ |
||||||
|
public MemberRef(final Type declaringClass, final NameAndType nameAndType) { |
||||||
|
this.declaringClass = declaringClass; |
||||||
|
this.nameAndType = nameAndType; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the type of the class which declared the member. |
||||||
|
* |
||||||
|
* @return The type of the class which declared the member. |
||||||
|
*/ |
||||||
|
public Type declaringClass() { |
||||||
|
return declaringClass; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the name of the member. |
||||||
|
* |
||||||
|
* @return The name of the member. |
||||||
|
*/ |
||||||
|
public String name() { |
||||||
|
return nameAndType.name(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the type of the member. |
||||||
|
* |
||||||
|
* @return The type of the member. |
||||||
|
*/ |
||||||
|
public Type type() { |
||||||
|
return nameAndType.type(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the name and type of the member. |
||||||
|
* |
||||||
|
* @return The name and type of the member. |
||||||
|
*/ |
||||||
|
public NameAndType nameAndType() { |
||||||
|
return nameAndType; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convert the reference to a string. |
||||||
|
* |
||||||
|
* @return A string representation of the reference. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
// Take advantage of PRINT_TRUNCATED in Type
|
||||||
|
final String className = declaringClass.toString(); |
||||||
|
return "<" + (type().isMethod() ? "Method" : "Field") + " " + className |
||||||
|
+ "." + name() + " " + type() + ">"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Check if an object is equal to this reference. |
||||||
|
* |
||||||
|
* @param obj |
||||||
|
* The object to compare against. |
||||||
|
* @return true if equal, false if not. |
||||||
|
*/ |
||||||
|
public boolean equals(final Object obj) { |
||||||
|
return (obj instanceof MemberRef) |
||||||
|
&& ((MemberRef) obj).declaringClass.equals(declaringClass) |
||||||
|
&& ((MemberRef) obj).nameAndType.equals(nameAndType); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Hash the member reference. |
||||||
|
* |
||||||
|
* @return The hash code. |
||||||
|
*/ |
||||||
|
public int hashCode() { |
||||||
|
return declaringClass.hashCode() ^ nameAndType.hashCode(); |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,76 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
/** |
||||||
|
* <tt>MultiArrayOperand</tt> encapsulates the operands to the |
||||||
|
* <tt>multianewarray</tt> instruction. Each <tt>MultiArrayOperand</tt> |
||||||
|
* contains the type descriptor of the new multidimensional array the |
||||||
|
* instruction creates, as well as the number of dimensions in the array. |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class MultiArrayOperand { |
||||||
|
private Type type; |
||||||
|
|
||||||
|
private int dim; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param type |
||||||
|
* The element type of the array. |
||||||
|
* @param dim |
||||||
|
* The number of dimensions of the array. |
||||||
|
*/ |
||||||
|
public MultiArrayOperand(final Type type, final int dim) { |
||||||
|
this.type = type; |
||||||
|
this.dim = dim; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the element type of the array. |
||||||
|
* |
||||||
|
* @return The element type of the array. |
||||||
|
*/ |
||||||
|
public Type type() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the number of dimensions of the array. |
||||||
|
* |
||||||
|
* @return The number of dimensions of the array. |
||||||
|
*/ |
||||||
|
public int dimensions() { |
||||||
|
return dim; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convert the operand to a string. |
||||||
|
* |
||||||
|
* @return A string representation of the operand. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
return type + " x " + dim; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
/** |
||||||
|
* Methods and fields are described by their name and type descriptor. |
||||||
|
* NameAndType represents exactly that. |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class NameAndType { |
||||||
|
private String name; |
||||||
|
|
||||||
|
private Type type; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
*/ |
||||||
|
public NameAndType(final String name, final Type type) { |
||||||
|
this.name = name; |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the name. |
||||||
|
*/ |
||||||
|
public String name() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the type. |
||||||
|
*/ |
||||||
|
public Type type() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a string representation of the name and type. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
return "<NameandType " + name + " " + type + ">"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Check if an object is equal to this name and type. |
||||||
|
* |
||||||
|
* @param obj |
||||||
|
* The object to compare against. |
||||||
|
* @return <tt>true</tt> if equal |
||||||
|
*/ |
||||||
|
public boolean equals(final Object obj) { |
||||||
|
return (obj instanceof NameAndType) |
||||||
|
&& ((NameAndType) obj).name.equals(name) |
||||||
|
&& ((NameAndType) obj).type.equals(type); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a hash of the name and type. |
||||||
|
*/ |
||||||
|
public int hashCode() { |
||||||
|
return name.hashCode() ^ type.hashCode(); |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,459 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.lang.reflect.*; |
||||||
|
import java.security.*; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* <P> |
||||||
|
* This class computes the serial version UID of a class modeled by a |
||||||
|
* <code>ClassEditor</code>. Otherwise, we would have to load the class in |
||||||
|
* order to compute its serial version UID. That would suck. |
||||||
|
* </P> |
||||||
|
* |
||||||
|
* <P> |
||||||
|
* The algorithm for computing the serial version UID can be found in the <A |
||||||
|
* href="http://java.sun.com/j2se/1.3/docs/guide/serialization/spec">serialization |
||||||
|
* spec</A> |
||||||
|
* </P> |
||||||
|
*/ |
||||||
|
public class SerialVersionUID { |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns <code>true</code> if the class modeled by the given |
||||||
|
* <code>ClassEditor</code> implements {@link java.io.Serializable |
||||||
|
* Serializable}. It checks superclasses. |
||||||
|
*/ |
||||||
|
public static boolean implementsSerializable(final ClassEditor ce) { |
||||||
|
if (ce.type().equals(Type.OBJECT)) { |
||||||
|
// Stop the recursion!
|
||||||
|
return (false); |
||||||
|
} |
||||||
|
|
||||||
|
final Type serializable = Type.getType("Ljava/io/Serializable;"); |
||||||
|
final Type[] interfaces = ce.interfaces(); |
||||||
|
for (int i = 0; i < interfaces.length; i++) { |
||||||
|
if (interfaces[i].equals(serializable)) { |
||||||
|
return (true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Does its superclass implement Serializable?
|
||||||
|
|
||||||
|
final Type superclass = ce.superclass(); |
||||||
|
final ClassInfoLoader loader = ce.classInfo().loader(); |
||||||
|
try { |
||||||
|
final ClassInfo ci = loader.loadClass(superclass.className()); |
||||||
|
final ClassEditor sce = new ClassEditor(ce.context(), ci); |
||||||
|
return (SerialVersionUID.implementsSerializable(sce)); |
||||||
|
|
||||||
|
} catch (final ClassNotFoundException ex) { |
||||||
|
System.err.println("Could not load class: " + superclass |
||||||
|
+ ", superclass of " + ce.name()); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
return (false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the serial version UID of the class modeled by the given |
||||||
|
* <code>ClassEditor</code>. |
||||||
|
* |
||||||
|
* @param ce |
||||||
|
* The class must implement {@link java.io.Serializable |
||||||
|
* Serializable} |
||||||
|
*/ |
||||||
|
public static long serialVersionUID(final ClassEditor ce) { |
||||||
|
// Make sure the class implements Serializable
|
||||||
|
if (!SerialVersionUID.implementsSerializable(ce)) { |
||||||
|
final String s = "Class " + ce.name() |
||||||
|
+ " does not implement java.io.Serializable"; |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
} |
||||||
|
|
||||||
|
// If the class already has a serialVersionUID, return that
|
||||||
|
final FieldInfo[] fields = ce.fields(); |
||||||
|
for (int i = 0; i < fields.length; i++) { |
||||||
|
final FieldEditor fe = new FieldEditor(ce, fields[i]); |
||||||
|
if (fe.name().equals("serialVersionUID")) { |
||||||
|
final Object value = fe.constantValue(); |
||||||
|
if (value != null) { |
||||||
|
if (value instanceof Long) { |
||||||
|
return (((Long) value).longValue()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Now, compute the digest of the bytes using SHA
|
||||||
|
MessageDigest algorithm = null; |
||||||
|
try { |
||||||
|
algorithm = MessageDigest.getInstance("SHA"); |
||||||
|
|
||||||
|
} catch (final NoSuchAlgorithmException ex) { |
||||||
|
final String s = "Can't use SHA-1 message digest algorith!"; |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
} |
||||||
|
|
||||||
|
final DataOutputStream dos = new DataOutputStream( |
||||||
|
new DigestOutputStream(new ByteArrayOutputStream(), algorithm)); |
||||||
|
|
||||||
|
try { |
||||||
|
// Write a bunch of information about the class to the output
|
||||||
|
// stream
|
||||||
|
SerialVersionUID.writeClassName(ce, dos); |
||||||
|
SerialVersionUID.writeClassModifiers(ce, dos); |
||||||
|
SerialVersionUID.writeInterfaceNames(ce, dos); |
||||||
|
SerialVersionUID.writeFields(ce, dos); |
||||||
|
SerialVersionUID.writeStaticInitializer(ce, dos); |
||||||
|
SerialVersionUID.writeConstructors(ce, dos); |
||||||
|
SerialVersionUID.writeMethods(ce, dos); |
||||||
|
|
||||||
|
dos.flush(); |
||||||
|
dos.close(); |
||||||
|
|
||||||
|
} catch (final IOException ex) { |
||||||
|
final String s = ("While computing serial version UID: " + ex); |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
} |
||||||
|
|
||||||
|
// Compute the hash value from the first 64 bites of the digest
|
||||||
|
final byte[] digest = algorithm.digest(); |
||||||
|
long uid = 0; |
||||||
|
for (int i = 0; i < Math.min(8, digest.length); i++) { |
||||||
|
uid += (long) (digest[i] & 255) << (i * 8); |
||||||
|
} |
||||||
|
return (uid); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes the name of the class to the data output stream |
||||||
|
*/ |
||||||
|
private static void writeClassName(final ClassEditor ce, |
||||||
|
final DataOutputStream dos) throws IOException { |
||||||
|
dos.writeUTF(ce.name().replace('/', '.')); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the Java reflection modifiers for a given class
|
||||||
|
*/ |
||||||
|
static int getModifiers(final ClassEditor ce) { |
||||||
|
// Translate BLOAT's class modifiers into Java's reflection
|
||||||
|
// modifiers
|
||||||
|
int modifiers = 0; |
||||||
|
|
||||||
|
if (ce.isPublic()) { |
||||||
|
modifiers |= Modifier.PUBLIC; |
||||||
|
} |
||||||
|
|
||||||
|
if (ce.isPrivate()) { |
||||||
|
modifiers |= Modifier.PRIVATE; |
||||||
|
} |
||||||
|
|
||||||
|
if (ce.isProtected()) { |
||||||
|
modifiers |= Modifier.PROTECTED; |
||||||
|
} |
||||||
|
|
||||||
|
if (ce.isStatic()) { |
||||||
|
modifiers |= Modifier.STATIC; |
||||||
|
} |
||||||
|
|
||||||
|
if (ce.isFinal()) { |
||||||
|
modifiers |= Modifier.FINAL; |
||||||
|
} |
||||||
|
|
||||||
|
if (ce.isAbstract()) { |
||||||
|
modifiers |= Modifier.ABSTRACT; |
||||||
|
} |
||||||
|
|
||||||
|
if (ce.isInterface()) { |
||||||
|
modifiers |= Modifier.INTERFACE; |
||||||
|
} |
||||||
|
|
||||||
|
return (modifiers); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes the class's modifiers to the output stream |
||||||
|
*/ |
||||||
|
private static void writeClassModifiers(final ClassEditor ce, |
||||||
|
final DataOutputStream dos) throws IOException { |
||||||
|
dos.writeInt(SerialVersionUID.getModifiers(ce)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes the names of the interfaces implemented by the class to the output |
||||||
|
* stream |
||||||
|
*/ |
||||||
|
private static void writeInterfaceNames(final ClassEditor ce, |
||||||
|
final DataOutputStream dos) throws IOException { |
||||||
|
|
||||||
|
// Sort interfaces by name
|
||||||
|
final SortedSet sorted = new TreeSet(); |
||||||
|
|
||||||
|
final Type[] interfaces = ce.interfaces(); |
||||||
|
for (int i = 0; i < interfaces.length; i++) { |
||||||
|
sorted.add(interfaces[i].className().replace('/', '.')); |
||||||
|
} |
||||||
|
|
||||||
|
final Iterator iter = sorted.iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final String name = (String) iter.next(); |
||||||
|
dos.writeUTF(name); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the Java reflection modifiers for a field |
||||||
|
*/ |
||||||
|
static int getModifiers(final FieldEditor fe) { |
||||||
|
int modifiers = 0; |
||||||
|
|
||||||
|
if (fe.isPublic()) { |
||||||
|
modifiers |= Modifier.PUBLIC; |
||||||
|
} |
||||||
|
|
||||||
|
if (fe.isPrivate()) { |
||||||
|
modifiers |= Modifier.PRIVATE; |
||||||
|
} |
||||||
|
|
||||||
|
if (fe.isProtected()) { |
||||||
|
modifiers |= Modifier.PROTECTED; |
||||||
|
} |
||||||
|
|
||||||
|
if (fe.isPackage()) { |
||||||
|
// Nothing
|
||||||
|
} |
||||||
|
|
||||||
|
if (fe.isStatic()) { |
||||||
|
modifiers |= Modifier.STATIC; |
||||||
|
} |
||||||
|
|
||||||
|
if (fe.isFinal()) { |
||||||
|
modifiers |= Modifier.FINAL; |
||||||
|
} |
||||||
|
|
||||||
|
if (fe.isVolatile()) { |
||||||
|
modifiers |= Modifier.VOLATILE; |
||||||
|
} |
||||||
|
|
||||||
|
if (fe.isTransient()) { |
||||||
|
// Kind of a moot point
|
||||||
|
modifiers |= Modifier.TRANSIENT; |
||||||
|
} |
||||||
|
|
||||||
|
return (modifiers); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes information about the class's fields to the output stream |
||||||
|
*/ |
||||||
|
private static void writeFields(final ClassEditor ce, |
||||||
|
final DataOutputStream dos) throws IOException { |
||||||
|
|
||||||
|
// Sort the fields by their names
|
||||||
|
final SortedSet sorted = new TreeSet(new Comparator() { |
||||||
|
public int compare(Object o1, Object o2) { |
||||||
|
FieldEditor fe1 = (FieldEditor) o1; |
||||||
|
FieldEditor fe2 = (FieldEditor) o2; |
||||||
|
return (fe1.name().compareTo(fe2.name())); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
final FieldInfo[] infos = ce.fields(); |
||||||
|
for (int i = 0; i < infos.length; i++) { |
||||||
|
final FieldEditor fe = new FieldEditor(ce, infos[i]); |
||||||
|
// Ignore private static and private transient fields
|
||||||
|
if (fe.isPrivate() && fe.isStatic()) { |
||||||
|
break; |
||||||
|
|
||||||
|
} else if (fe.isPrivate() && fe.isTransient()) { |
||||||
|
break; |
||||||
|
|
||||||
|
} else { |
||||||
|
sorted.add(fe); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final Iterator iter = sorted.iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final FieldEditor fe = (FieldEditor) iter.next(); |
||||||
|
dos.writeUTF(fe.name()); |
||||||
|
dos.writeInt(SerialVersionUID.getModifiers(fe)); |
||||||
|
dos.writeUTF(fe.type().descriptor()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the Java reflection descriptors for a method |
||||||
|
*/ |
||||||
|
static int getModifiers(final MethodEditor me) { |
||||||
|
int modifiers = 0; |
||||||
|
|
||||||
|
if (me.isPublic()) { |
||||||
|
modifiers |= Modifier.PUBLIC; |
||||||
|
} |
||||||
|
|
||||||
|
if (me.isPrivate()) { |
||||||
|
modifiers |= Modifier.PRIVATE; |
||||||
|
} |
||||||
|
|
||||||
|
if (me.isProtected()) { |
||||||
|
modifiers |= Modifier.PROTECTED; |
||||||
|
} |
||||||
|
|
||||||
|
if (me.isPackage()) { |
||||||
|
// Nothing
|
||||||
|
} |
||||||
|
|
||||||
|
if (me.isStatic()) { |
||||||
|
modifiers |= Modifier.STATIC; |
||||||
|
} |
||||||
|
|
||||||
|
if (me.isFinal()) { |
||||||
|
modifiers |= Modifier.FINAL; |
||||||
|
} |
||||||
|
|
||||||
|
if (me.isSynchronized()) { |
||||||
|
modifiers |= Modifier.SYNCHRONIZED; |
||||||
|
} |
||||||
|
|
||||||
|
if (me.isNative()) { |
||||||
|
modifiers |= Modifier.NATIVE; |
||||||
|
} |
||||||
|
|
||||||
|
if (me.isAbstract()) { |
||||||
|
modifiers |= Modifier.ABSTRACT; |
||||||
|
} |
||||||
|
|
||||||
|
if (me.isInterface()) { |
||||||
|
modifiers |= Modifier.INTERFACE; |
||||||
|
} |
||||||
|
|
||||||
|
return (modifiers); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes information about the classes static initializer if it has one |
||||||
|
*/ |
||||||
|
private static void writeStaticInitializer(final ClassEditor ce, |
||||||
|
final DataOutputStream dos) throws IOException { |
||||||
|
|
||||||
|
MethodEditor clinit = null; |
||||||
|
|
||||||
|
final MethodInfo[] methods = ce.methods(); |
||||||
|
for (int i = 0; i < methods.length; i++) { |
||||||
|
final MethodEditor me = new MethodEditor(ce, methods[i]); |
||||||
|
if (me.name().equals("<clinit>")) { |
||||||
|
clinit = me; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (clinit != null) { |
||||||
|
dos.writeUTF("<clinit>"); |
||||||
|
dos.writeInt(Modifier.STATIC); |
||||||
|
dos.writeUTF("()V"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes information about the class's constructors |
||||||
|
*/ |
||||||
|
private static void writeConstructors(final ClassEditor ce, |
||||||
|
final DataOutputStream dos) throws IOException { |
||||||
|
|
||||||
|
// Sort constructors by their signatures
|
||||||
|
final SortedSet sorted = new TreeSet(new Comparator() { |
||||||
|
public int compare(Object o1, Object o2) { |
||||||
|
MethodEditor me1 = (MethodEditor) o1; |
||||||
|
MethodEditor me2 = (MethodEditor) o2; |
||||||
|
return (me1.type().descriptor().compareTo(me2.type() |
||||||
|
.descriptor())); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
final MethodInfo[] methods = ce.methods(); |
||||||
|
for (int i = 0; i < methods.length; i++) { |
||||||
|
final MethodEditor me = new MethodEditor(ce, methods[i]); |
||||||
|
if (me.name().equals("<init>")) { |
||||||
|
if (!me.isPrivate()) { |
||||||
|
// Ignore private constructors
|
||||||
|
sorted.add(me); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final Iterator iter = sorted.iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final MethodEditor init = (MethodEditor) iter.next(); |
||||||
|
dos.writeUTF("<init>"); |
||||||
|
dos.writeInt(SerialVersionUID.getModifiers(init)); |
||||||
|
dos.writeUTF(init.type().descriptor()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write information about the class's methods |
||||||
|
*/ |
||||||
|
private static void writeMethods(final ClassEditor ce, |
||||||
|
final DataOutputStream dos) throws IOException { |
||||||
|
|
||||||
|
// Sort constructors by their names and signatures
|
||||||
|
final SortedSet sorted = new TreeSet(new Comparator() { |
||||||
|
public int compare(Object o1, Object o2) { |
||||||
|
MethodEditor me1 = (MethodEditor) o1; |
||||||
|
MethodEditor me2 = (MethodEditor) o2; |
||||||
|
|
||||||
|
String d1 = me1.name() + me1.type().descriptor(); |
||||||
|
String d2 = me2.name() + me2.type().descriptor(); |
||||||
|
return (d1.compareTo(d2)); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
final MethodInfo[] methods = ce.methods(); |
||||||
|
for (int i = 0; i < methods.length; i++) { |
||||||
|
final MethodEditor me = new MethodEditor(ce, methods[i]); |
||||||
|
if (!me.isPrivate() && !me.isConstructor() |
||||||
|
&& !me.name().equals("<clinit>")) { |
||||||
|
// Ignore private methods
|
||||||
|
sorted.add(me); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final Iterator iter = sorted.iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
final MethodEditor me = (MethodEditor) iter.next(); |
||||||
|
dos.writeUTF(me.name()); |
||||||
|
dos.writeInt(SerialVersionUID.getModifiers(me)); |
||||||
|
dos.writeUTF(me.type().descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,232 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
/** |
||||||
|
* Switch is used to hold the lookup values and branch targets of a tableswitch |
||||||
|
* or lookup switch instruction. |
||||||
|
* <p> |
||||||
|
* The tableswitch low-to-high range of values is represented by storing each |
||||||
|
* lookup value in the range. This allows the tableswitch to be replaced with a |
||||||
|
* lookupswitch if branches are deleted. |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class Switch { |
||||||
|
private Label defaultTarget; |
||||||
|
|
||||||
|
private Label[] targets; |
||||||
|
|
||||||
|
private int[] values; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param defaultTarget |
||||||
|
* The default target of the switch. |
||||||
|
* @param targets |
||||||
|
* The non-default branch targets of the switch. |
||||||
|
* @param values |
||||||
|
* The lookup values of the switch. This array must be the same |
||||||
|
* size as the targets array. |
||||||
|
*/ |
||||||
|
public Switch(final Label defaultTarget, final Label[] targets, |
||||||
|
final int[] values) { |
||||||
|
this.defaultTarget = defaultTarget; |
||||||
|
this.targets = targets; |
||||||
|
this.values = values; |
||||||
|
sort(); |
||||||
|
uniq(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the default target of the switch. |
||||||
|
* |
||||||
|
* @param target |
||||||
|
* The default target of the switch. |
||||||
|
*/ |
||||||
|
public void setDefaultTarget(final Label target) { |
||||||
|
defaultTarget = target; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the default target of the switch. |
||||||
|
* |
||||||
|
* @return The default target of the switch. |
||||||
|
*/ |
||||||
|
public Label defaultTarget() { |
||||||
|
return defaultTarget; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the non-default branch targets of the switch. The targets are sorted |
||||||
|
* by the corresponding lookup value. |
||||||
|
* |
||||||
|
* @return The non-default branch targets of the switch. |
||||||
|
*/ |
||||||
|
public Label[] targets() { |
||||||
|
return targets; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the lookup values of the switch, sorted low to high. |
||||||
|
* |
||||||
|
* @return The lookup values of the switch. |
||||||
|
*/ |
||||||
|
public int[] values() { |
||||||
|
return values; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Check if the all the values in the range of lookup values are contiguous. |
||||||
|
* If they are, a table switch can be used. If not a lookupswitch can be |
||||||
|
* used. |
||||||
|
* |
||||||
|
* @return true if contiguous, false if not. |
||||||
|
*/ |
||||||
|
public boolean hasContiguousValues() { |
||||||
|
return values.length == highValue() - lowValue() + 1; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the low value in the range the lookup values. |
||||||
|
* |
||||||
|
* @return The low value. |
||||||
|
*/ |
||||||
|
public int lowValue() { |
||||||
|
return values[0]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the high value in the range the lookup values. |
||||||
|
* |
||||||
|
* @return The high value. |
||||||
|
*/ |
||||||
|
public int highValue() { |
||||||
|
return values[values.length - 1]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sort the targets and values arrays so that values is sorted low to high. |
||||||
|
*/ |
||||||
|
private void sort() { |
||||||
|
quicksort(0, values.length - 1); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Utility function to sort the targets and values arrays so that values is |
||||||
|
* sorted low to high. |
||||||
|
* |
||||||
|
* @param p |
||||||
|
* The low index of the portion of the array to sort. |
||||||
|
* @param r |
||||||
|
* The high index of the portion of the array to sort. |
||||||
|
*/ |
||||||
|
private void quicksort(final int p, final int r) { |
||||||
|
if (p < r) { |
||||||
|
final int q = partition(p, r); |
||||||
|
quicksort(p, q); |
||||||
|
quicksort(q + 1, r); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Utility function to sort the targets and values arrays so that values is |
||||||
|
* sorted low to high. |
||||||
|
* <p> |
||||||
|
* Partition the arrays so that the values less than values[p] are to the |
||||||
|
* left. |
||||||
|
* |
||||||
|
* @param p |
||||||
|
* The low index of the portion of the array to sort. |
||||||
|
* @param r |
||||||
|
* The high index of the portion of the array to sort. |
||||||
|
* @return The index at which the partition finished. |
||||||
|
*/ |
||||||
|
private int partition(final int p, final int r) { |
||||||
|
final int x = values[p]; |
||||||
|
int i = p - 1; |
||||||
|
int j = r + 1; |
||||||
|
|
||||||
|
while (true) { |
||||||
|
do { |
||||||
|
j--; |
||||||
|
} while (values[j] > x); |
||||||
|
|
||||||
|
do { |
||||||
|
i++; |
||||||
|
} while (values[i] < x); |
||||||
|
|
||||||
|
if (i < j) { |
||||||
|
final int v = values[i]; |
||||||
|
values[i] = values[j]; |
||||||
|
values[j] = v; |
||||||
|
|
||||||
|
final Label t = targets[i]; |
||||||
|
targets[i] = targets[j]; |
||||||
|
targets[j] = t; |
||||||
|
} else { |
||||||
|
return j; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Remove duplicates from the values and targets arrays. |
||||||
|
*/ |
||||||
|
private void uniq() { |
||||||
|
if (values.length == 0) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
final int[] v = new int[values.length]; |
||||||
|
final Label[] t = new Label[values.length]; |
||||||
|
|
||||||
|
v[0] = values[0]; |
||||||
|
t[0] = targets[0]; |
||||||
|
|
||||||
|
int j = 1; |
||||||
|
|
||||||
|
for (int i = 1; i < values.length; i++) { |
||||||
|
if (v[j - 1] != values[i]) { |
||||||
|
v[j] = values[i]; |
||||||
|
t[j] = targets[i]; |
||||||
|
j++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
values = new int[j]; |
||||||
|
System.arraycopy(v, 0, values, 0, j); |
||||||
|
|
||||||
|
targets = new Label[j]; |
||||||
|
System.arraycopy(t, 0, targets, 0, j); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convert the operand to a string. |
||||||
|
* |
||||||
|
* @return A string representation of the operand. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
return "" + values.length + " pairs"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,106 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
/** |
||||||
|
* TryCatch holds the labels for the start and end of a protected block and the |
||||||
|
* beginning of a catch block and the type of the exception to catch. |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class TryCatch { |
||||||
|
private Label start; |
||||||
|
|
||||||
|
private Label end; |
||||||
|
|
||||||
|
private Label handler; |
||||||
|
|
||||||
|
private Type type; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param start |
||||||
|
* The start label of the protected block. |
||||||
|
* @param end |
||||||
|
* The label of the instruction after the end of the protected |
||||||
|
* block. |
||||||
|
* @param handler |
||||||
|
* The start label of the exception handler. |
||||||
|
* @param type |
||||||
|
* The type of exception to catch. |
||||||
|
*/ |
||||||
|
public TryCatch(final Label start, final Label end, final Label handler, |
||||||
|
final Type type) { |
||||||
|
this.start = start; |
||||||
|
this.end = end; |
||||||
|
this.handler = handler; |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the start label of the protected block. |
||||||
|
* |
||||||
|
* @return The start label. |
||||||
|
*/ |
||||||
|
public Label start() { |
||||||
|
return start; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the end label of the protected block. |
||||||
|
* |
||||||
|
* @return The end label. |
||||||
|
*/ |
||||||
|
public Label end() { |
||||||
|
return end; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the start label of the catch block. |
||||||
|
* |
||||||
|
* @return The handler label. |
||||||
|
*/ |
||||||
|
public Label handler() { |
||||||
|
return handler; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the start label of the catch block. |
||||||
|
*/ |
||||||
|
public void setHandler(final Label handler) { |
||||||
|
this.handler = handler; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the type of the exception to catch. |
||||||
|
* |
||||||
|
* @return The type of the exception to catch. |
||||||
|
*/ |
||||||
|
public Type type() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return "try " + start + ".." + end + " catch (" + type + ") " + handler; |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,120 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.util.*; |
||||||
|
|
||||||
|
// // For testing only
|
||||||
|
// import EDU.purdue.cs.bloat.file.*;
|
||||||
|
// import EDU.purdue.cs.bloat.context.*;
|
||||||
|
|
||||||
|
/** |
||||||
|
* A <tt>TypeComparator</tt> orders <tt>Type</tt>s such that a subclass |
||||||
|
* preceededs its superclass. Note that this doesn't really work with |
||||||
|
* interfaces. |
||||||
|
*/ |
||||||
|
public final class TypeComparator implements Comparator { |
||||||
|
|
||||||
|
public static boolean DEBUG = false; |
||||||
|
|
||||||
|
private EditorContext context; |
||||||
|
|
||||||
|
private static void db(final String s) { |
||||||
|
if (TypeComparator.DEBUG) { |
||||||
|
System.out.println(s); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
*/ |
||||||
|
public TypeComparator(final EditorContext context) { |
||||||
|
this.context = context; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a negative value if o1 < o2 (t1 is a subclass of t2). Otherwise, |
||||||
|
* it returns a positive value. |
||||||
|
*/ |
||||||
|
public int compare(final Object o1, final Object o2) { |
||||||
|
Assert.isTrue(o1 instanceof Type, o1 + " is not a Type"); |
||||||
|
Assert.isTrue(o2 instanceof Type, o2 + " is not a Type"); |
||||||
|
|
||||||
|
final Type t1 = (Type) o1; |
||||||
|
final Type t2 = (Type) o2; |
||||||
|
|
||||||
|
TypeComparator.db("Comparing " + t1 + " to " + t2); |
||||||
|
|
||||||
|
final ClassHierarchy hier = context.getHierarchy(); |
||||||
|
|
||||||
|
if (hier.subclassOf(t1, t2)) { |
||||||
|
TypeComparator.db(" " + t1 + " is a subclass of " + t2); |
||||||
|
return (-1); |
||||||
|
|
||||||
|
} else if (hier.subclassOf(t2, t1)) { |
||||||
|
TypeComparator.db(" " + t2 + " is a subclass of " + t1); |
||||||
|
return (1); |
||||||
|
|
||||||
|
} else { |
||||||
|
TypeComparator.db(" " + t1 + " and " + t2 + " are unrelated"); |
||||||
|
|
||||||
|
// Don't return 0. If you do, the type will not get included in
|
||||||
|
// the sorted set. Weird.
|
||||||
|
return (1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates whether some other object is "equal to" this Comparator. |
||||||
|
*/ |
||||||
|
public boolean equals(final Object other) { |
||||||
|
return (other instanceof TypeComparator); |
||||||
|
} |
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Test program. Reads class names from the command line.
|
||||||
|
// */
|
||||||
|
// public static void main(String[] args) {
|
||||||
|
// // Make a list of class names
|
||||||
|
// List names = new ArrayList();
|
||||||
|
// for(int i = 0; i < args.length; i++) {
|
||||||
|
// names.add(args[i]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Do some BLOAT magic
|
||||||
|
// EditorContext context =
|
||||||
|
// new CachingBloatContext(new ClassFileLoader(), names, false);
|
||||||
|
// Collection classes = context.getHierarchy().classes();
|
||||||
|
|
||||||
|
// TypeComparator.DEBUG = true;
|
||||||
|
// SortedSet sorted = new TreeSet(new TypeComparator(context));
|
||||||
|
// sorted.addAll(classes);
|
||||||
|
|
||||||
|
// System.out.println(classes.size() + " classes");
|
||||||
|
// System.out.println(sorted.size() + " sorted types:");
|
||||||
|
// Object[] array = sorted.toArray();
|
||||||
|
// for(int i = 0; i < array.length; i++) {
|
||||||
|
// System.out.println(" " + array[i]);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.editor; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.tree.*; |
||||||
|
|
||||||
|
public class UseMap { |
||||||
|
|
||||||
|
public Hashtable map; |
||||||
|
|
||||||
|
public UseMap() { |
||||||
|
map = new Hashtable(); |
||||||
|
} |
||||||
|
|
||||||
|
public void add(final LocalExpr use, final Instruction inst) { |
||||||
|
|
||||||
|
final Node def = use.def(); |
||||||
|
if (def != null) { |
||||||
|
map.put(inst, def); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public boolean hasDef(final Instruction inst) { |
||||||
|
|
||||||
|
return map.containsKey(inst); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean hasSameDef(final Instruction a, final Instruction b) { |
||||||
|
return map.containsKey(a) && map.containsKey(b) |
||||||
|
&& (map.get(a) == map.get(b)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
<html> |
||||||
|
<body> |
||||||
|
|
||||||
|
<p>Allows classes, methods, and fields to be edited. In addition, the |
||||||
|
representation of bytecodes (JVM instructions) is refined. Types, |
||||||
|
opcodes, local variables, and special instrcution operands are |
||||||
|
introduced.</p> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1 @@ |
|||||||
|
*.class |
@ -0,0 +1,89 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.file; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Attribute is an abstract class for an attribute defined for a method, field, |
||||||
|
* or class. An attribute consists of its name (represented as an index into the |
||||||
|
* constant pool) and its length. Attribute is extended to represent a constant |
||||||
|
* value, code, exceptions, etc. |
||||||
|
* |
||||||
|
* @see Code |
||||||
|
* @see ConstantValue |
||||||
|
* @see Exceptions |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public abstract class Attribute { |
||||||
|
protected int nameIndex; |
||||||
|
|
||||||
|
protected int length; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param nameIndex |
||||||
|
* The index into the constant pool of the name of the attribute. |
||||||
|
* @param length |
||||||
|
* The length of the attribute, excluding the header. |
||||||
|
*/ |
||||||
|
public Attribute(final int nameIndex, final int length) { |
||||||
|
this.nameIndex = nameIndex; |
||||||
|
this.length = length; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the attribute to a data stream. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The data stream of the class file. |
||||||
|
*/ |
||||||
|
public abstract void writeData(DataOutputStream out) throws IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a string representation of the attribute. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
return "(attribute " + nameIndex + " " + length + ")"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index into the constant pool of the name of the attribute. |
||||||
|
*/ |
||||||
|
public int nameIndex() { |
||||||
|
return nameIndex; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the length of the attribute, excluding the header. |
||||||
|
*/ |
||||||
|
public int length() { |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
public Object clone() { |
||||||
|
throw new UnsupportedOperationException("Cannot clone Attribute! " |
||||||
|
+ " (subclass: " + this.getClass() + ")"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,976 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.file; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
import EDU.purdue.cs.bloat.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* ClassFile basically represents a Java classfile as it is found on disk. The |
||||||
|
* classfile is modeled according to the Java Virtual Machine Specification. |
||||||
|
* Methods are provided to edit the classfile at a very low level. |
||||||
|
* |
||||||
|
* @see Attribute |
||||||
|
* @see EDU.purdue.cs.bloat.reflect.Constant |
||||||
|
* @see Field |
||||||
|
* @see Method |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class ClassFile implements ClassInfo { |
||||||
|
private ClassInfoLoader loader; // ClassInfoLoader that "owns" this class
|
||||||
|
|
||||||
|
private List constants; // The constant pool
|
||||||
|
|
||||||
|
private int modifiers; // This class's modifer bit field
|
||||||
|
|
||||||
|
private int thisClass; |
||||||
|
|
||||||
|
private int superClass; |
||||||
|
|
||||||
|
private int[] interfaces; |
||||||
|
|
||||||
|
private Field[] fields; |
||||||
|
|
||||||
|
private Method[] methods; |
||||||
|
|
||||||
|
private Attribute[] attrs; |
||||||
|
|
||||||
|
private File file; // (.class) File in which this class resides
|
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. This constructor parses the class file from the input |
||||||
|
* stream. |
||||||
|
* |
||||||
|
* @param file |
||||||
|
* The file in which the class resides. |
||||||
|
* @param loader |
||||||
|
* The class info loader which loaded the class. |
||||||
|
* @param in |
||||||
|
* The data stream containing the class. |
||||||
|
* @exception ClassFormatError |
||||||
|
* When the class could not be parsed. |
||||||
|
*/ |
||||||
|
public ClassFile(final File file, final ClassInfoLoader loader, |
||||||
|
final DataInputStream in) { |
||||||
|
this.loader = loader; |
||||||
|
this.file = file; |
||||||
|
// Assert.isTrue(file != null, "Null file for class file");
|
||||||
|
|
||||||
|
// Read in file contents from stream
|
||||||
|
try { |
||||||
|
if (ClassFileLoader.DEBUG) { |
||||||
|
System.out.println("ClassFile: Reading header"); |
||||||
|
} |
||||||
|
readHeader(in); |
||||||
|
|
||||||
|
if (ClassFileLoader.DEBUG) { |
||||||
|
System.out.println("ClassFile: Reading constant pool"); |
||||||
|
} |
||||||
|
readConstantPool(in); |
||||||
|
|
||||||
|
if (ClassFileLoader.DEBUG) { |
||||||
|
System.out.println("ClassFile: Reading access flags"); |
||||||
|
} |
||||||
|
readAccessFlags(in); |
||||||
|
|
||||||
|
if (ClassFileLoader.DEBUG) { |
||||||
|
System.out.println("ClassFile: Reading class info"); |
||||||
|
} |
||||||
|
readClassInfo(in); |
||||||
|
|
||||||
|
if (ClassFileLoader.DEBUG) { |
||||||
|
System.out.println("ClassFile: Reading fields"); |
||||||
|
} |
||||||
|
readFields(in); |
||||||
|
|
||||||
|
if (ClassFileLoader.DEBUG) { |
||||||
|
System.out.println("ClassFile: Reading methods"); |
||||||
|
} |
||||||
|
readMethods(in); |
||||||
|
|
||||||
|
if (ClassFileLoader.DEBUG) { |
||||||
|
System.out.println("ClassFile: Reading Attributes"); |
||||||
|
} |
||||||
|
readAttributes(in); |
||||||
|
in.close(); |
||||||
|
} catch (final IOException e) { |
||||||
|
throw new ClassFormatException(e.getMessage() + " (in file " + file |
||||||
|
+ ")"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new <code>ClassFile</code> from scratch. It has no fields or |
||||||
|
* methods. |
||||||
|
* |
||||||
|
* @param modifiers |
||||||
|
* The modifiers describing the newly-created class
|
||||||
|
* @param classIndex |
||||||
|
* The index of the type of the newly-created class in its |
||||||
|
* constant pool |
||||||
|
* @param superClassIndex |
||||||
|
* The index of the type of the newly-created class's superclass |
||||||
|
* in its constant pool |
||||||
|
* @param interfaceIndexes |
||||||
|
* The indexes of the types of the interfaces that the |
||||||
|
* newly-created class implements |
||||||
|
* @param constants |
||||||
|
* The constant pool for the newly created class (a list of |
||||||
|
* {@link Constant}s). |
||||||
|
*/ |
||||||
|
public ClassFile(final int modifiers, final int classIndex, |
||||||
|
final int superClassIndex, final int[] interfaceIndexes, |
||||||
|
final List constants, final ClassInfoLoader loader) { |
||||||
|
this.modifiers = modifiers; |
||||||
|
this.thisClass = classIndex; |
||||||
|
this.superClass = superClassIndex; |
||||||
|
this.interfaces = interfaceIndexes; |
||||||
|
this.constants = constants; |
||||||
|
this.loader = loader; |
||||||
|
|
||||||
|
this.fields = new Field[0]; |
||||||
|
this.methods = new Method[0]; |
||||||
|
this.attrs = new Attribute[0]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the class info loader for the class. |
||||||
|
* |
||||||
|
* @return The class info loader for the class. |
||||||
|
*/ |
||||||
|
public ClassInfoLoader loader() { |
||||||
|
return loader; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the name of the class, including the package name. |
||||||
|
* |
||||||
|
* @return The name of the class. |
||||||
|
*/ |
||||||
|
public String name() { |
||||||
|
Constant c = (Constant) constants.get(thisClass); |
||||||
|
Assert.isNotNull(c, "Null constant for class name"); |
||||||
|
if (c.tag() == Constant.CLASS) { |
||||||
|
final Integer nameIndex = (Integer) c.value(); |
||||||
|
if (nameIndex != null) { |
||||||
|
c = (Constant) constants.get(nameIndex.intValue()); |
||||||
|
if (c.tag() == Constant.UTF8) { |
||||||
|
return (String) c.value(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
throw new ClassFormatException("Couldn't find class name in file"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the index into the constant pool of the name of the class. |
||||||
|
* |
||||||
|
* @param index |
||||||
|
* The index of the name of the class. |
||||||
|
*/ |
||||||
|
public void setClassIndex(final int index) { |
||||||
|
this.thisClass = index; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the index into the constant pool of the name of the class's |
||||||
|
* superclass. |
||||||
|
* |
||||||
|
* @param index |
||||||
|
* The index of the name of the superclass. |
||||||
|
*/ |
||||||
|
public void setSuperclassIndex(final int index) { |
||||||
|
this.superClass = index; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the indices into the constant pool of the names of the class's |
||||||
|
* interfaces. |
||||||
|
* |
||||||
|
* @param indices |
||||||
|
* The indices of the names of the interfaces. |
||||||
|
*/ |
||||||
|
public void setInterfaceIndices(final int[] indices) { |
||||||
|
this.interfaces = indices; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the index into the constant pool of the name of the class. |
||||||
|
* |
||||||
|
* @return The index of the name of the class. |
||||||
|
*/ |
||||||
|
public int classIndex() { |
||||||
|
return thisClass; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the index into the constant pool of the name of the class's |
||||||
|
* superclass. |
||||||
|
* |
||||||
|
* @return The index of the name of the superclass. |
||||||
|
*/ |
||||||
|
public int superclassIndex() { |
||||||
|
return superClass; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the indices into the constant pool of the names of the class's |
||||||
|
* interfaces. |
||||||
|
* |
||||||
|
* @return The indices of the names of the interfaces. |
||||||
|
*/ |
||||||
|
public int[] interfaceIndices() { |
||||||
|
return interfaces; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the modifiers of the class. The values correspond to the constants in |
||||||
|
* the Modifiers class. |
||||||
|
* |
||||||
|
* @param modifiers |
||||||
|
* A bit vector of modifier flags for the class. |
||||||
|
* @see Modifiers |
||||||
|
*/ |
||||||
|
public void setModifiers(final int modifiers) { |
||||||
|
this.modifiers = modifiers; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the modifiers of the class. The values correspond to the constants in |
||||||
|
* the Modifiers class. |
||||||
|
* |
||||||
|
* @return A bit vector of modifier flags for the class. |
||||||
|
* @see Modifiers |
||||||
|
*/ |
||||||
|
public int modifiers() { |
||||||
|
return modifiers; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get an array of FieldInfo structures for each field in the class. |
||||||
|
* |
||||||
|
* @return An array of FieldInfo structures. |
||||||
|
*/ |
||||||
|
public FieldInfo[] fields() { |
||||||
|
return fields; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an array of MethodInfo structures for each method in the class. |
||||||
|
*/ |
||||||
|
public MethodInfo[] methods() { |
||||||
|
return methods; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the methods in this class. |
||||||
|
*/ |
||||||
|
public void setMethods(final MethodInfo[] methods) { |
||||||
|
this.methods = new Method[methods.length]; |
||||||
|
for (int i = 0; i < methods.length; i++) { |
||||||
|
this.methods[i] = (Method) methods[i]; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get an array of the constants in the constant pool. |
||||||
|
* |
||||||
|
* @return An array of Constants. |
||||||
|
*/ |
||||||
|
public Constant[] constants() { |
||||||
|
return (Constant[]) constants.toArray(new Constant[0]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set all the constants in the constant pool. |
||||||
|
* |
||||||
|
* @param constants |
||||||
|
* The array of Constants. |
||||||
|
*/ |
||||||
|
public void setConstants(final Constant[] constants) { |
||||||
|
this.constants = new ArrayList(constants.length); |
||||||
|
for (int i = 0; i < constants.length; i++) { |
||||||
|
this.constants.add(i, constants[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the File from which this <code>ClassFile</code> was created. If |
||||||
|
* this <code>ClassFile</code> was created from scratch, <code>null</code> |
||||||
|
* is returned. |
||||||
|
*/ |
||||||
|
public File file() { |
||||||
|
return file; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new File object to hold this class. It is placed in the output |
||||||
|
* directory and has the name of the class represented by this ClassFile |
||||||
|
* followed by the .class extension. |
||||||
|
*/ |
||||||
|
public File outputFile() { |
||||||
|
final File outputDir = ((ClassFileLoader) loader).outputDir(); |
||||||
|
final String fileName = this.name().replace('/', File.separatorChar); |
||||||
|
return new File(outputDir, fileName + ".class"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Commit any changes back to a file in the output directory. The output |
||||||
|
* directory is determined from the ClassFileLoader. |
||||||
|
*/ |
||||||
|
public void commit() { |
||||||
|
try { |
||||||
|
commitTo(loader.outputStreamFor(this)); |
||||||
|
|
||||||
|
} catch (final IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Commit changes made to this class. Write changes to an OutputStream. |
||||||
|
*/ |
||||||
|
void commitTo(final OutputStream outStream) { |
||||||
|
try { |
||||||
|
final DataOutputStream out = new DataOutputStream(outStream); |
||||||
|
|
||||||
|
writeHeader(out); |
||||||
|
writeConstantPool(out); |
||||||
|
writeAccessFlags(out); |
||||||
|
writeClassInfo(out); |
||||||
|
writeFields(out, null); |
||||||
|
writeMethods(out, null); |
||||||
|
writeAttributes(out); |
||||||
|
|
||||||
|
out.close(); |
||||||
|
|
||||||
|
} catch (final IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void commitOnly(final Set methods, final Set fields) { |
||||||
|
try { |
||||||
|
final OutputStream outStream = loader.outputStreamFor(this); |
||||||
|
final DataOutputStream out = new DataOutputStream(outStream); |
||||||
|
|
||||||
|
writeHeader(out); |
||||||
|
writeConstantPool(out); |
||||||
|
writeAccessFlags(out); |
||||||
|
writeClassInfo(out); |
||||||
|
writeFields(out, fields); |
||||||
|
writeMethods(out, methods); |
||||||
|
writeAttributes(out); |
||||||
|
|
||||||
|
out.close(); |
||||||
|
|
||||||
|
} catch (final IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the class file header. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The stream to which to write. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
private void writeHeader(final DataOutputStream out) throws IOException { |
||||||
|
out.writeInt(0xCAFEBABE); |
||||||
|
out.writeShort(3); |
||||||
|
out.writeShort(45); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the class's constant pool. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The stream to which to write. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
private void writeConstantPool(final DataOutputStream out) |
||||||
|
throws IOException { |
||||||
|
out.writeShort(constants.size()); |
||||||
|
|
||||||
|
// Write the constants. The first constant is reserved for
|
||||||
|
// internal use by the JVM, so start at 1.
|
||||||
|
for (int i = 1; i < constants.size(); i++) { |
||||||
|
writeConstant(out, (Constant) constants.get(i)); |
||||||
|
|
||||||
|
switch (((Constant) constants.get(i)).tag()) { |
||||||
|
case Constant.LONG: |
||||||
|
case Constant.DOUBLE: |
||||||
|
// Longs and doubles take up 2 constant pool entries.
|
||||||
|
i++; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Read a constant from the constant pool. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The stream from which to read. |
||||||
|
* @return The constant. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
private Constant readConstant(final DataInputStream in) throws IOException { |
||||||
|
final int tag = in.readUnsignedByte(); |
||||||
|
Object value; |
||||||
|
|
||||||
|
switch (tag) { |
||||||
|
case Constant.CLASS: |
||||||
|
case Constant.STRING: |
||||||
|
value = new Integer(in.readUnsignedShort()); |
||||||
|
break; |
||||||
|
case Constant.FIELD_REF: |
||||||
|
case Constant.METHOD_REF: |
||||||
|
case Constant.INTERFACE_METHOD_REF: |
||||||
|
case Constant.NAME_AND_TYPE: |
||||||
|
value = new int[2]; |
||||||
|
((int[]) value)[0] = in.readUnsignedShort(); |
||||||
|
((int[]) value)[1] = in.readUnsignedShort(); |
||||||
|
break; |
||||||
|
case Constant.INTEGER: |
||||||
|
value = new Integer(in.readInt()); |
||||||
|
break; |
||||||
|
case Constant.FLOAT: |
||||||
|
value = new Float(in.readFloat()); |
||||||
|
break; |
||||||
|
case Constant.LONG: |
||||||
|
// Longs take up 2 constant pool entries.
|
||||||
|
value = new Long(in.readLong()); |
||||||
|
break; |
||||||
|
case Constant.DOUBLE: |
||||||
|
// Doubles take up 2 constant pool entries.
|
||||||
|
value = new Double(in.readDouble()); |
||||||
|
break; |
||||||
|
case Constant.UTF8: |
||||||
|
value = in.readUTF(); |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new ClassFormatException(file.getPath() |
||||||
|
+ ": Invalid constant tag: " + tag); |
||||||
|
} |
||||||
|
|
||||||
|
return new Constant(tag, value); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write a constant in the constant pool. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The stream to which to write. |
||||||
|
* @param constant |
||||||
|
* The constant. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
private void writeConstant(final DataOutputStream out, |
||||||
|
final Constant constant) throws IOException { |
||||||
|
final int tag = constant.tag(); |
||||||
|
final Object value = constant.value(); |
||||||
|
|
||||||
|
out.writeByte(tag); |
||||||
|
|
||||||
|
switch (tag) { |
||||||
|
case Constant.CLASS: |
||||||
|
case Constant.STRING: |
||||||
|
out.writeShort(((Integer) value).intValue()); |
||||||
|
break; |
||||||
|
case Constant.INTEGER: |
||||||
|
out.writeInt(((Integer) value).intValue()); |
||||||
|
break; |
||||||
|
case Constant.FLOAT: |
||||||
|
out.writeFloat(((Float) value).floatValue()); |
||||||
|
break; |
||||||
|
case Constant.LONG: |
||||||
|
out.writeLong(((Long) value).longValue()); |
||||||
|
break; |
||||||
|
case Constant.DOUBLE: |
||||||
|
out.writeDouble(((Double) value).doubleValue()); |
||||||
|
break; |
||||||
|
case Constant.UTF8: |
||||||
|
out.writeUTF((String) value); |
||||||
|
break; |
||||||
|
case Constant.FIELD_REF: |
||||||
|
case Constant.METHOD_REF: |
||||||
|
case Constant.INTERFACE_METHOD_REF: |
||||||
|
case Constant.NAME_AND_TYPE: |
||||||
|
out.writeShort(((int[]) value)[0]); |
||||||
|
out.writeShort(((int[]) value)[1]); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the class's access flags. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The stream to which to write. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
private void writeAccessFlags(final DataOutputStream out) |
||||||
|
throws IOException { |
||||||
|
out.writeShort(modifiers); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the class's name, superclass, and interfaces. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The stream to which to write. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
private void writeClassInfo(final DataOutputStream out) throws IOException { |
||||||
|
out.writeShort(thisClass); |
||||||
|
out.writeShort(superClass); |
||||||
|
|
||||||
|
out.writeShort(interfaces.length); |
||||||
|
|
||||||
|
for (int i = 0; i < interfaces.length; i++) { |
||||||
|
out.writeShort(interfaces[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the class's fields. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The stream to which to write. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
private void writeFields(final DataOutputStream out, final Set onlyFields) |
||||||
|
throws IOException { |
||||||
|
out.writeShort(fields.length); |
||||||
|
|
||||||
|
for (int i = 0; i < fields.length; i++) { |
||||||
|
if ((onlyFields != null) && onlyFields.contains(fields[i])) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
fields[i].write(out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the class's methods. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The stream to which to write. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
private void writeMethods(final DataOutputStream out, final Set onlyMethods) |
||||||
|
throws IOException { |
||||||
|
if (onlyMethods != null) { |
||||||
|
out.writeShort(onlyMethods.size()); |
||||||
|
|
||||||
|
} else { |
||||||
|
if (Method.DEBUG) { |
||||||
|
System.out.println("Writing " + methods.length + " methods"); |
||||||
|
} |
||||||
|
out.writeShort(methods.length); |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < methods.length; i++) { |
||||||
|
if ((onlyMethods != null) && onlyMethods.contains(methods[i])) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
methods[i].write(out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the class's attributes. No attributes are written by this method |
||||||
|
* since none are required. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The stream to which to write. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
private void writeAttributes(final DataOutputStream out) throws IOException { |
||||||
|
out.writeShort(attrs.length); |
||||||
|
|
||||||
|
for (int i = 0; i < attrs.length; i++) { |
||||||
|
out.writeShort(attrs[i].nameIndex()); |
||||||
|
out.writeInt(attrs[i].length()); |
||||||
|
attrs[i].writeData(out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Read the class file header. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The stream from which to read. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
private void readHeader(final DataInputStream in) throws IOException { |
||||||
|
final int magic = in.readInt(); |
||||||
|
|
||||||
|
if (magic != 0xCAFEBABE) { |
||||||
|
throw new ClassFormatError("Bad magic number."); |
||||||
|
} |
||||||
|
|
||||||
|
in.readUnsignedShort(); // major
|
||||||
|
in.readUnsignedShort(); // minor
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Read the class's constant pool. Constants in the constant pool are |
||||||
|
* modeled by an array of <tt>reflect.Constant</tt>/ |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The stream from which to read. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
* |
||||||
|
* @see EDU.purdue.cs.bloat.reflect.Constant |
||||||
|
* @see #constants |
||||||
|
*/ |
||||||
|
private void readConstantPool(final DataInputStream in) throws IOException { |
||||||
|
final int count = in.readUnsignedShort(); |
||||||
|
|
||||||
|
constants = new ArrayList(count); |
||||||
|
|
||||||
|
// The first constant is reserved for internal use by the JVM.
|
||||||
|
constants.add(0, null); |
||||||
|
|
||||||
|
// Read the constants.
|
||||||
|
for (int i = 1; i < count; i++) { |
||||||
|
constants.add(i, readConstant(in)); |
||||||
|
|
||||||
|
switch (((Constant) constants.get(i)).tag()) { |
||||||
|
case Constant.LONG: |
||||||
|
case Constant.DOUBLE: |
||||||
|
// Longs and doubles take up 2 constant pool entries.
|
||||||
|
constants.add(++i, null); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Read the class's access flags. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The stream from which to read. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
private void readAccessFlags(final DataInputStream in) throws IOException { |
||||||
|
modifiers = in.readUnsignedShort(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Read the class's name, superclass, and interfaces. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The stream from which to read. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
private void readClassInfo(final DataInputStream in) throws IOException { |
||||||
|
thisClass = in.readUnsignedShort(); |
||||||
|
superClass = in.readUnsignedShort(); |
||||||
|
|
||||||
|
final int numInterfaces = in.readUnsignedShort(); |
||||||
|
|
||||||
|
interfaces = new int[numInterfaces]; |
||||||
|
|
||||||
|
for (int i = 0; i < numInterfaces; i++) { |
||||||
|
interfaces[i] = in.readUnsignedShort(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Read the class's fields. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The stream from which to read. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
private void readFields(final DataInputStream in) throws IOException { |
||||||
|
final int numFields = in.readUnsignedShort(); |
||||||
|
|
||||||
|
fields = new Field[numFields]; |
||||||
|
|
||||||
|
for (int i = 0; i < numFields; i++) { |
||||||
|
fields[i] = new Field(in, this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Read the class's methods. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The stream from which to read. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
private void readMethods(final DataInputStream in) throws IOException { |
||||||
|
final int numMethods = in.readUnsignedShort(); |
||||||
|
|
||||||
|
methods = new Method[numMethods]; |
||||||
|
|
||||||
|
for (int i = 0; i < numMethods; i++) { |
||||||
|
methods[i] = new Method(in, this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Read the class's attributes. Since none of the attributes are required, |
||||||
|
* just read the length of each attribute and skip that many bytes. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The stream from which to read. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
private void readAttributes(final DataInputStream in) throws IOException { |
||||||
|
final int numAttributes = in.readUnsignedShort(); |
||||||
|
|
||||||
|
attrs = new Attribute[numAttributes]; |
||||||
|
|
||||||
|
for (int i = 0; i < numAttributes; i++) { |
||||||
|
final int nameIndex = in.readUnsignedShort(); |
||||||
|
final int length = in.readInt(); |
||||||
|
attrs[i] = new GenericAttribute(in, nameIndex, length); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new field in this classfile |
||||||
|
*/ |
||||||
|
public FieldInfo addNewField(final int modifiers, final int typeIndex, |
||||||
|
final int nameIndex) { |
||||||
|
final Field field = new Field(this, modifiers, typeIndex, nameIndex); |
||||||
|
|
||||||
|
// Add the new field to the list of fields
|
||||||
|
final Field[] fields = new Field[this.fields.length + 1]; |
||||||
|
for (int i = 0; i < this.fields.length; i++) { |
||||||
|
fields[i] = this.fields[i]; |
||||||
|
} |
||||||
|
fields[this.fields.length] = field; |
||||||
|
this.fields = fields; |
||||||
|
|
||||||
|
return (field); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new field in this classfile |
||||||
|
*/ |
||||||
|
public FieldInfo addNewField(final int modifiers, final int typeIndex, |
||||||
|
final int nameIndex, final int cvNameIndex, |
||||||
|
final int constantValueIndex) { |
||||||
|
final Field field = new Field(this, modifiers, typeIndex, nameIndex, |
||||||
|
cvNameIndex, constantValueIndex); |
||||||
|
|
||||||
|
// Add the new field to the list of fields
|
||||||
|
final Field[] fields = new Field[this.fields.length + 1]; |
||||||
|
for (int i = 0; i < this.fields.length; i++) { |
||||||
|
fields[i] = this.fields[i]; |
||||||
|
} |
||||||
|
fields[this.fields.length] = field; |
||||||
|
this.fields = fields; |
||||||
|
|
||||||
|
return (field); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes the field whose name is at the given index in the constant pool. |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException The class does not contain a field whose |
||||||
|
* name is at the given index |
||||||
|
*/ |
||||||
|
public void deleteField(final int nameIndex) { |
||||||
|
final List newFields = new ArrayList(); |
||||||
|
|
||||||
|
boolean foundIt = false; |
||||||
|
for (int i = 0; i < this.fields.length; i++) { |
||||||
|
final Field field = this.fields[i]; |
||||||
|
if (field.nameIndex() == nameIndex) { |
||||||
|
foundIt = true; |
||||||
|
|
||||||
|
} else { |
||||||
|
newFields.add(field); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!foundIt) { |
||||||
|
final String s = "No field with name index " + nameIndex + " in " |
||||||
|
+ this.name(); |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
|
||||||
|
} else { |
||||||
|
this.fields = (Field[]) newFields.toArray(new Field[0]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Deletes a method from this class
|
||||||
|
* |
||||||
|
* @param nameIndex |
||||||
|
* Index in the constant pool of the name of the method to be |
||||||
|
* deleted |
||||||
|
* @param typeIndex |
||||||
|
* Index in the constant pool of the type of the method to be |
||||||
|
* deleted |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException The class modeled by this |
||||||
|
* <code>ClassInfo</code> does not contain a method whose name and |
||||||
|
* type are not at the given indices |
||||||
|
*/ |
||||||
|
public void deleteMethod(final int nameIndex, final int typeIndex) { |
||||||
|
final List newMethods = new ArrayList(); |
||||||
|
|
||||||
|
boolean foundIt = false; |
||||||
|
for (int i = 0; i < this.methods.length; i++) { |
||||||
|
final Method method = this.methods[i]; |
||||||
|
if ((method.nameIndex() == nameIndex) |
||||||
|
&& (method.typeIndex() == typeIndex)) { |
||||||
|
foundIt = true; |
||||||
|
|
||||||
|
} else { |
||||||
|
newMethods.add(method); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!foundIt) { |
||||||
|
final String s = "No method with name index " + nameIndex |
||||||
|
+ " and type index " + typeIndex + " in " + this.name(); |
||||||
|
throw new IllegalArgumentException(s); |
||||||
|
|
||||||
|
} else { |
||||||
|
this.methods = (Method[]) newMethods.toArray(new Method[0]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new method in this class
|
||||||
|
*/ |
||||||
|
public MethodInfo addNewMethod(final int modifiers, final int typeIndex, |
||||||
|
final int nameIndex, final int exceptionIndex, |
||||||
|
final int[] exceptionTypeIndices, final int codeIndex) { |
||||||
|
final Exceptions exceptions = new Exceptions(this, exceptionIndex, |
||||||
|
exceptionTypeIndices); |
||||||
|
final Code code = new Code(this, codeIndex); // code can't be null
|
||||||
|
final Attribute[] attributes = new Attribute[] { exceptions, code }; |
||||||
|
|
||||||
|
final Method method = new Method(this, modifiers, nameIndex, typeIndex, |
||||||
|
attributes, code, exceptions); |
||||||
|
|
||||||
|
// Add the new method to the list of method
|
||||||
|
final Method[] methods = new Method[this.methods.length + 1]; |
||||||
|
for (int i = 0; i < this.methods.length; i++) { |
||||||
|
methods[i] = this.methods[i]; |
||||||
|
} |
||||||
|
methods[this.methods.length] = method; |
||||||
|
this.methods = methods; |
||||||
|
|
||||||
|
return (method); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Prints a textual representation of this classfile to a PrintStream. The |
||||||
|
* text includes information such as the constants in the constant pool, the |
||||||
|
* name of the class's superclass, its modifier flags, its fields, and its |
||||||
|
* methods. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The stream to which to print. |
||||||
|
*/ |
||||||
|
public void print(final PrintStream out) { |
||||||
|
print(new PrintWriter(out, true)); |
||||||
|
} |
||||||
|
|
||||||
|
public void print(final PrintWriter out) { |
||||||
|
out.print("(constants"); |
||||||
|
for (int i = 0; i < constants.size(); i++) { |
||||||
|
out.print("\n " + i + ": " + constants.get(i)); |
||||||
|
} |
||||||
|
out.println(")"); |
||||||
|
|
||||||
|
out.println("(class " + classIndex() + ")"); |
||||||
|
out.println("(super " + superclassIndex() + ")"); |
||||||
|
|
||||||
|
out.print("(interfaces"); |
||||||
|
for (int i = 0; i < interfaces.length; i++) { |
||||||
|
out.print("\n " + i + ": " + interfaces[i]); |
||||||
|
} |
||||||
|
out.println(")"); |
||||||
|
|
||||||
|
out.print("(modifiers"); |
||||||
|
if ((modifiers & Modifiers.PUBLIC) != 0) { |
||||||
|
out.print(" PUBLIC"); |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.FINAL) != 0) { |
||||||
|
out.print(" FINAL"); |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.SUPER) != 0) { |
||||||
|
out.print(" SUPER"); |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.INTERFACE) != 0) { |
||||||
|
out.print(" INTERFACE"); |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.ABSTRACT) != 0) { |
||||||
|
out.print(" ABSTRACT"); |
||||||
|
} |
||||||
|
out.println(")"); |
||||||
|
|
||||||
|
out.print("(fields"); |
||||||
|
for (int i = 0; i < fields.length; i++) { |
||||||
|
out.print("\n " + i + ": " + fields[i]); |
||||||
|
} |
||||||
|
out.println(")"); |
||||||
|
|
||||||
|
out.print("(methods"); |
||||||
|
for (int i = 0; i < methods.length; i++) { |
||||||
|
out.print("\n " + i + ": " + methods[i]); |
||||||
|
} |
||||||
|
out.println(")"); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return "(ClassFile " + name() + ")"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,463 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.file; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.util.*; |
||||||
|
import java.util.zip.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* ClassFileLoder provides an interface for loading classes from files. The |
||||||
|
* actual loading is done by the ClassFile itself. |
||||||
|
* <p> |
||||||
|
* Classes may be specified by their full package name (<tt>java.lang.String</tt>), |
||||||
|
* or by the name of their class file (<tt>myclasses/Test.class</tt>). The |
||||||
|
* class path may contain directories or Zip or Jar files. Any classes that are |
||||||
|
* written back to disk ("committed") are placed in the output directory. |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class ClassFileLoader implements ClassInfoLoader { |
||||||
|
public static boolean DEBUG = false; |
||||||
|
|
||||||
|
public static boolean USE_SYSTEM_CLASSES = true; |
||||||
|
|
||||||
|
private File outputDir; // Directory in which to write committed class files
|
||||||
|
|
||||||
|
private String classpath; // Path to search for classes
|
||||||
|
|
||||||
|
private Map openZipFiles; // zip files to search for class files
|
||||||
|
|
||||||
|
private LinkedList cache; // We keep a cache of CACHE_LIMIT class files
|
||||||
|
|
||||||
|
private boolean verbose; |
||||||
|
|
||||||
|
private static final int CACHE_LIMIT = 10; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. The classpath initially consists of the contents of the |
||||||
|
* <tt>java.class.path</tt> and <tt>sun.boot.class.path</tt> system |
||||||
|
* properties. |
||||||
|
*/ |
||||||
|
public ClassFileLoader() { |
||||||
|
outputDir = new File("."); |
||||||
|
classpath = System.getProperty("java.class.path"); |
||||||
|
classpath += File.pathSeparator |
||||||
|
+ System.getProperty("sun.boot.class.path"); |
||||||
|
if (ClassFileLoader.USE_SYSTEM_CLASSES) { |
||||||
|
classpath += File.pathSeparator |
||||||
|
+ System.getProperty("java.sys.class.path"); |
||||||
|
} |
||||||
|
openZipFiles = new HashMap(); |
||||||
|
cache = new LinkedList(); |
||||||
|
verbose = false; |
||||||
|
} |
||||||
|
|
||||||
|
public void setVerbose(final boolean verbose) { |
||||||
|
this.verbose = verbose; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the classpath. |
||||||
|
*/ |
||||||
|
public void setClassPath(final String classpath) { |
||||||
|
this.classpath = classpath; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds to the classpath (CLASSPATH = CLASSPATH + morePath). |
||||||
|
*/ |
||||||
|
public void appendClassPath(final String morePath) { |
||||||
|
this.classpath += File.pathSeparator + morePath; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds to the classpath (CLASSPATH = morePath + CLASSPATH). |
||||||
|
*/ |
||||||
|
public void prependClassPath(final String morePath) { |
||||||
|
this.classpath = morePath + File.pathSeparator + this.classpath; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the path used to search for class files. |
||||||
|
*/ |
||||||
|
public String getClassPath() { |
||||||
|
return (this.classpath); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Load the class from a stream. |
||||||
|
* |
||||||
|
* @param inputFile |
||||||
|
* The file from which to load the class. |
||||||
|
* @param stream |
||||||
|
* The stream from which to load the class. |
||||||
|
* @return A ClassInfo for the class. |
||||||
|
* @exception ClassNotFoundException |
||||||
|
* The class cannot be found in the class path. |
||||||
|
*/ |
||||||
|
private ClassInfo loadClassFromStream(final File inputFile, |
||||||
|
final InputStream stream) throws ClassNotFoundException { |
||||||
|
|
||||||
|
final DataInputStream in = new DataInputStream(stream); |
||||||
|
final ClassFile file = new ClassFile(inputFile, this, in); |
||||||
|
|
||||||
|
return file; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Load the class from the file. |
||||||
|
* |
||||||
|
* @param file |
||||||
|
* The File from which to load a class. |
||||||
|
* @return A ClassInfo for the class. |
||||||
|
* @exception ClassNotFoundException |
||||||
|
* The class cannot be found in the class path. |
||||||
|
*/ |
||||||
|
private ClassInfo loadClassFromFile(final File file) |
||||||
|
throws ClassNotFoundException { |
||||||
|
try { |
||||||
|
final InputStream in = new FileInputStream(file); |
||||||
|
|
||||||
|
final ClassInfo info = loadClassFromStream(file, in); |
||||||
|
|
||||||
|
if (verbose) { |
||||||
|
System.out.println("[Loaded " + info.name() + " from " |
||||||
|
+ file.getPath() + "]"); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
in.close(); |
||||||
|
} catch (final IOException ex) { |
||||||
|
} |
||||||
|
|
||||||
|
return info; |
||||||
|
} catch (final FileNotFoundException e) { |
||||||
|
throw new ClassNotFoundException(file.getPath()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Loads all of the classes that are contained in a zip (or jar) file. |
||||||
|
* Returns an array of the <tt>ClassInfo</tt>s for the classes in the zip |
||||||
|
* file. |
||||||
|
*/ |
||||||
|
public ClassInfo[] loadClassesFromZipFile(final ZipFile zipFile) |
||||||
|
throws ClassNotFoundException { |
||||||
|
final ClassInfo[] infos = new ClassInfo[zipFile.size()]; |
||||||
|
|
||||||
|
// Examine each entry in the zip file
|
||||||
|
final Enumeration entries = zipFile.entries(); |
||||||
|
for (int i = 0; entries.hasMoreElements(); i++) { |
||||||
|
final ZipEntry entry = (ZipEntry) entries.nextElement(); |
||||||
|
if (entry.isDirectory() || !entry.getName().endsWith(".class")) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
final InputStream stream = zipFile.getInputStream(entry); |
||||||
|
final File file = new File(entry.getName()); |
||||||
|
|
||||||
|
infos[i] = loadClassFromStream(file, stream); |
||||||
|
|
||||||
|
} catch (final IOException ex) { |
||||||
|
System.err.println("IOException: " + ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return (infos); |
||||||
|
} |
||||||
|
|
||||||
|
public ClassInfo newClass(final int modifiers, final int classIndex, |
||||||
|
final int superClassIndex, final int[] interfaceIndexes, |
||||||
|
final List constants) { |
||||||
|
return new ClassFile(modifiers, classIndex, superClassIndex, |
||||||
|
interfaceIndexes, constants, this); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Loads the class with the given name. Searches the class path, including |
||||||
|
* zip files, for the class and then returns a data stream for the class
|
||||||
|
* file. |
||||||
|
* |
||||||
|
* @param name |
||||||
|
* The name of the class to load, including the package name. |
||||||
|
* @return A ClassInfo for the class. |
||||||
|
* @exception ClassNotFoundException |
||||||
|
* The class cannot be found in the class path. |
||||||
|
*/ |
||||||
|
public ClassInfo loadClass(String name) throws ClassNotFoundException { |
||||||
|
ClassInfo file = null; |
||||||
|
|
||||||
|
// Check to see if name ends with ".class". If so, load the class from
|
||||||
|
// that file. Note that this is okay because we can never have a class
|
||||||
|
// named "class" (i.e. a class named "class" with a lower-case 'c' can
|
||||||
|
// never be specified in a fully-specified java class name) because
|
||||||
|
// "class" is a reserved word.
|
||||||
|
|
||||||
|
if (name.endsWith(".class")) { |
||||||
|
final File nameFile = new File(name); |
||||||
|
|
||||||
|
if (!nameFile.exists()) { |
||||||
|
throw new ClassNotFoundException(name); |
||||||
|
|
||||||
|
} else { |
||||||
|
return (loadClassFromFile(nameFile)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Otherwise, we have a (possibly fully-specified) class name.
|
||||||
|
name = name.replace('.', '/'); |
||||||
|
|
||||||
|
// Check the cache for the class file.
|
||||||
|
if (ClassFileLoader.DEBUG) { |
||||||
|
System.out |
||||||
|
.println(" Looking for " + name + " in cache = " + cache); |
||||||
|
} |
||||||
|
|
||||||
|
final Iterator iter = cache.iterator(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
file = (ClassFile) iter.next(); |
||||||
|
|
||||||
|
if (name.equals(file.name())) { |
||||||
|
if (ClassFileLoader.DEBUG) { |
||||||
|
System.out.println(" Found " + file.name() + " in cache"); |
||||||
|
} |
||||||
|
|
||||||
|
// Move to the front of the cache.
|
||||||
|
iter.remove(); |
||||||
|
cache.addFirst(file); |
||||||
|
|
||||||
|
return file; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
file = null; |
||||||
|
|
||||||
|
final String classFile = name.replace('/', File.separatorChar) |
||||||
|
+ ".class"; |
||||||
|
|
||||||
|
// For each entry in the class path, search zip files and directories
|
||||||
|
// for classFile. When found, open an InputStream and break
|
||||||
|
// out of the loop to read the class file.
|
||||||
|
final String path = classpath + File.pathSeparator; |
||||||
|
|
||||||
|
if (ClassFileLoader.DEBUG) { |
||||||
|
System.out.println("CLASSPATH = " + path); |
||||||
|
} |
||||||
|
|
||||||
|
int index = 0; |
||||||
|
int end = path.indexOf(File.pathSeparator, index); |
||||||
|
|
||||||
|
SEARCH: while (end >= 0) { |
||||||
|
final String dir = path.substring(index, end); |
||||||
|
|
||||||
|
File f = new File(dir); |
||||||
|
|
||||||
|
if (f.isDirectory()) { |
||||||
|
// The directory is really a directory. If the class file
|
||||||
|
// exists, open a stream and return.
|
||||||
|
f = new File(dir, classFile); |
||||||
|
|
||||||
|
if (f.exists()) { |
||||||
|
try { |
||||||
|
final InputStream in = new FileInputStream(f); |
||||||
|
|
||||||
|
if (verbose) { |
||||||
|
System.out.println(" [Loaded " + name + " from " |
||||||
|
+ f.getPath() + "]"); |
||||||
|
} |
||||||
|
|
||||||
|
file = loadClassFromStream(f, in); |
||||||
|
|
||||||
|
try { |
||||||
|
in.close(); |
||||||
|
|
||||||
|
} catch (final IOException ex) { |
||||||
|
} |
||||||
|
|
||||||
|
break SEARCH; |
||||||
|
|
||||||
|
} catch (final FileNotFoundException ex) { |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} else if (dir.endsWith(".zip") || dir.endsWith(".jar")) { |
||||||
|
// Maybe a zip file?
|
||||||
|
try { |
||||||
|
ZipFile zip = (ZipFile) openZipFiles.get(dir); |
||||||
|
|
||||||
|
if (zip == null) { |
||||||
|
zip = new ZipFile(f); |
||||||
|
openZipFiles.put(dir, zip); |
||||||
|
} |
||||||
|
|
||||||
|
final String zipEntry = classFile.replace( |
||||||
|
File.separatorChar, '/'); |
||||||
|
|
||||||
|
final ZipEntry entry = zip.getEntry(zipEntry); |
||||||
|
|
||||||
|
if (entry != null) { |
||||||
|
// Found the class file in the zip file.
|
||||||
|
// Open a stream and return.
|
||||||
|
if (verbose) { |
||||||
|
System.out.println(" [Loaded " + name + " from " |
||||||
|
+ f.getPath() + "]"); |
||||||
|
} |
||||||
|
|
||||||
|
final InputStream in = zip.getInputStream(entry); |
||||||
|
file = loadClassFromStream(f, in); |
||||||
|
|
||||||
|
try { |
||||||
|
in.close(); |
||||||
|
|
||||||
|
} catch (final IOException ex) { |
||||||
|
} |
||||||
|
break SEARCH; |
||||||
|
} |
||||||
|
} catch (final ZipException ex) { |
||||||
|
} catch (final IOException ex) { |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
index = end + 1; |
||||||
|
end = path.indexOf(File.pathSeparator, index); |
||||||
|
} |
||||||
|
|
||||||
|
if (file == null) { |
||||||
|
// The class file wasn't in the class path. Try the currnet
|
||||||
|
// directory. If not there, give up.
|
||||||
|
final File f = new File(classFile); |
||||||
|
|
||||||
|
if (!f.exists()) { |
||||||
|
throw new ClassNotFoundException(name); |
||||||
|
} |
||||||
|
|
||||||
|
if (verbose) { |
||||||
|
System.out.println(" [Loaded " + name + " from " + f.getPath() |
||||||
|
+ "]"); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
final InputStream in = new FileInputStream(f); |
||||||
|
file = loadClassFromStream(f, in); |
||||||
|
|
||||||
|
try { |
||||||
|
in.close(); |
||||||
|
} catch (final IOException ex) { |
||||||
|
} |
||||||
|
} catch (final FileNotFoundException ex) { |
||||||
|
throw new ClassNotFoundException(name); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (file == null) { |
||||||
|
throw new ClassNotFoundException(name); |
||||||
|
} |
||||||
|
|
||||||
|
// If we've reached the cache size limit, remove the oldest file
|
||||||
|
// in the cache. Then add the new file.
|
||||||
|
if (cache.size() == ClassFileLoader.CACHE_LIMIT) { |
||||||
|
cache.removeLast(); |
||||||
|
} |
||||||
|
|
||||||
|
cache.addFirst(file); |
||||||
|
|
||||||
|
return file; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the directory into which commited class files should be written. |
||||||
|
* |
||||||
|
* @param dir |
||||||
|
* The directory. |
||||||
|
*/ |
||||||
|
public void setOutputDir(final File dir) { |
||||||
|
outputDir = dir; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the directory into which commited class files should be written. |
||||||
|
*/ |
||||||
|
public File outputDir() { |
||||||
|
return outputDir; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes a bunch of <code>byte</code>s to an output entry with the given |
||||||
|
* name. |
||||||
|
*/ |
||||||
|
public void writeEntry(final byte[] bytes, final String name) |
||||||
|
throws IOException { |
||||||
|
final OutputStream os = outputStreamFor(name); |
||||||
|
os.write(bytes); |
||||||
|
os.flush(); |
||||||
|
os.close(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an <tt>OutputStream</tt> to which a class file should be |
||||||
|
* written. |
||||||
|
*/ |
||||||
|
public OutputStream outputStreamFor(final ClassInfo info) |
||||||
|
throws IOException { |
||||||
|
// Format the name of the output file
|
||||||
|
final String name = info.name().replace('/', File.separatorChar) |
||||||
|
+ ".class"; |
||||||
|
return outputStreamFor(name); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an <code>OutputStream</code> to which somed named entity is |
||||||
|
* written. Any forward slashes in the name are replaced by |
||||||
|
* <code>File.separatorChar</code>. |
||||||
|
*/ |
||||||
|
protected OutputStream outputStreamFor(String name) throws IOException { |
||||||
|
|
||||||
|
name = name.replace('/', File.separatorChar); |
||||||
|
|
||||||
|
final File f = new File(outputDir, name); |
||||||
|
|
||||||
|
if (f.exists()) { |
||||||
|
f.delete(); |
||||||
|
} |
||||||
|
|
||||||
|
final File dir = new File(f.getParent()); |
||||||
|
dir.mkdirs(); |
||||||
|
|
||||||
|
if (!dir.exists()) { |
||||||
|
throw new RuntimeException("Couldn't create directory: " + dir); |
||||||
|
} |
||||||
|
|
||||||
|
return (new FileOutputStream(f)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Signifies that we are done with this <code>ClassFileLoader</code> |
||||||
|
*/ |
||||||
|
public void done() throws IOException { |
||||||
|
// Nothing for this guy
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,442 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.file; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Code is used to store the Code attribute of a method in a class file. The |
||||||
|
* Code attribute stores the raw bytecode of the method, the maximum stack |
||||||
|
* height and maximum number of locals used by the method, and the exception |
||||||
|
* handlers used in the method. Code may have several attributes. The local |
||||||
|
* variable table and the line number table are modeled explicitly. All other |
||||||
|
* attributes are modeled as generic attributes. |
||||||
|
* |
||||||
|
* @see EDU.purdue.cs.bloat.reflect.Catch Catch |
||||||
|
* @see GenericAttribute |
||||||
|
* @see EDU.purdue.cs.bloat.reflect.LineNumberDebugInfo |
||||||
|
* @see LocalVariableTable |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class Code extends Attribute { |
||||||
|
private ClassInfo classInfo; |
||||||
|
|
||||||
|
private int maxStack; |
||||||
|
|
||||||
|
private int maxLocals; |
||||||
|
|
||||||
|
private byte[] code; |
||||||
|
|
||||||
|
private Catch[] handlers; |
||||||
|
|
||||||
|
private LineNumberTable lineNumbers; |
||||||
|
|
||||||
|
private LocalVariableTable locals; |
||||||
|
|
||||||
|
private Attribute[] attrs; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor for creating a <code>Code</code> from scratch |
||||||
|
* |
||||||
|
* @param codeIndex |
||||||
|
* The index in the constant pool for the UTF8 "Code" |
||||||
|
*/ |
||||||
|
Code(final ClassInfo classInfo, final int codeIndex) { |
||||||
|
// Don't know length!
|
||||||
|
super(codeIndex, -1); |
||||||
|
|
||||||
|
this.classInfo = classInfo; |
||||||
|
|
||||||
|
// These should get set during a commit
|
||||||
|
this.maxStack = -1; |
||||||
|
this.maxLocals = -1; |
||||||
|
this.code = new byte[0]; |
||||||
|
this.handlers = new Catch[0]; |
||||||
|
this.lineNumbers = null; // It's okay for these to be null
|
||||||
|
this.locals = null; |
||||||
|
this.attrs = new Attribute[0]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Create a Code attribute from a data stream. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The data stream containing the class file. |
||||||
|
* @param index |
||||||
|
* The index into the constant pool of the name of the attribute. |
||||||
|
* @param len |
||||||
|
* The length of the attribute, excluding the header. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
public Code(final ClassInfo classInfo, final DataInputStream in, |
||||||
|
final int index, final int len) throws IOException { |
||||||
|
super(index, len); |
||||||
|
|
||||||
|
this.classInfo = classInfo; |
||||||
|
|
||||||
|
maxStack = in.readUnsignedShort(); |
||||||
|
maxLocals = in.readUnsignedShort(); |
||||||
|
|
||||||
|
final int codeLength = in.readInt(); |
||||||
|
|
||||||
|
code = new byte[codeLength]; |
||||||
|
|
||||||
|
for (int read = 0; read < codeLength;) { |
||||||
|
read += in.read(code, read, codeLength - read); |
||||||
|
} |
||||||
|
|
||||||
|
final int numHandlers = in.readUnsignedShort(); |
||||||
|
|
||||||
|
handlers = new Catch[numHandlers]; |
||||||
|
|
||||||
|
for (int i = 0; i < numHandlers; i++) { |
||||||
|
handlers[i] = readCatch(in); |
||||||
|
} |
||||||
|
|
||||||
|
final int numAttributes = in.readUnsignedShort(); |
||||||
|
|
||||||
|
attrs = new Attribute[numAttributes]; |
||||||
|
|
||||||
|
for (int i = 0; i < numAttributes; i++) { |
||||||
|
final int nameIndex = in.readUnsignedShort(); |
||||||
|
final int length = in.readInt(); |
||||||
|
|
||||||
|
final Constant name = classInfo.constants()[nameIndex]; |
||||||
|
|
||||||
|
if (name != null) { |
||||||
|
if ("LineNumberTable".equals(name.value())) { |
||||||
|
lineNumbers = new LineNumberTable(in, nameIndex, length); |
||||||
|
attrs[i] = lineNumbers; |
||||||
|
} else if ("LocalVariableTable".equals(name.value())) { |
||||||
|
locals = new LocalVariableTable(in, nameIndex, length); |
||||||
|
attrs[i] = locals; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (attrs[i] == null) { |
||||||
|
attrs[i] = new GenericAttribute(in, nameIndex, length); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the attribute to a data stream. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The data stream of the class file. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
public void writeData(final DataOutputStream out) throws IOException { |
||||||
|
out.writeShort(maxStack); |
||||||
|
out.writeShort(maxLocals); |
||||||
|
|
||||||
|
out.writeInt(code.length); |
||||||
|
out.write(code, 0, code.length); |
||||||
|
|
||||||
|
out.writeShort(handlers.length); |
||||||
|
|
||||||
|
for (int i = 0; i < handlers.length; i++) { |
||||||
|
writeCatch(out, handlers[i]); |
||||||
|
} |
||||||
|
|
||||||
|
out.writeShort(attrs.length); |
||||||
|
|
||||||
|
for (int i = 0; i < attrs.length; i++) { |
||||||
|
out.writeShort(attrs[i].nameIndex()); |
||||||
|
out.writeInt(attrs[i].length()); |
||||||
|
attrs[i].writeData(out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Read an exception handler attribute. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The data stream of the class file. |
||||||
|
* @return A Catch attribute for the handler. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
private Catch readCatch(final DataInputStream in) throws IOException { |
||||||
|
final int startPC = in.readUnsignedShort(); |
||||||
|
final int endPC = in.readUnsignedShort(); |
||||||
|
final int handlerPC = in.readUnsignedShort(); |
||||||
|
final int catchType = in.readUnsignedShort(); |
||||||
|
|
||||||
|
return new Catch(startPC, endPC, handlerPC, catchType); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write an exception handler attribute. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The data stream of the class file. |
||||||
|
* @param c |
||||||
|
* A Catch attribute for the handler. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
private void writeCatch(final DataOutputStream out, final Catch c) |
||||||
|
throws IOException { |
||||||
|
final int startPC = c.startPC(); |
||||||
|
final int endPC = c.endPC(); |
||||||
|
final int handlerPC = c.handlerPC(); |
||||||
|
final int catchType = c.catchTypeIndex(); |
||||||
|
|
||||||
|
out.writeShort(startPC); |
||||||
|
out.writeShort(endPC); |
||||||
|
out.writeShort(handlerPC); |
||||||
|
out.writeShort(catchType); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the maximum height of the operand stack used by the code. |
||||||
|
* |
||||||
|
* @param maxStack |
||||||
|
* The maximum height of the stack. |
||||||
|
*/ |
||||||
|
public void setMaxStack(final int maxStack) { |
||||||
|
this.maxStack = maxStack; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the maximum number of locals used by the code. |
||||||
|
* |
||||||
|
* @param maxLocals |
||||||
|
* The maximum number of locals. |
||||||
|
*/ |
||||||
|
public void setMaxLocals(final int maxLocals) { |
||||||
|
this.maxLocals = maxLocals; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the maximum height of the operand stack used by the code. |
||||||
|
* |
||||||
|
* @return The maximum number of locals. |
||||||
|
*/ |
||||||
|
public int maxStack() { |
||||||
|
return maxStack; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the maximum number of locals used by the code. |
||||||
|
* |
||||||
|
* @return The maximum number of locals. |
||||||
|
*/ |
||||||
|
public int maxLocals() { |
||||||
|
return maxLocals; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the exception handlers in the method. |
||||||
|
* |
||||||
|
* @param handlers |
||||||
|
* The handlers. |
||||||
|
*/ |
||||||
|
public void setExceptionHandlers(final Catch[] handlers) { |
||||||
|
this.handlers = handlers; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the length of the attribute. |
||||||
|
* |
||||||
|
* @return The length of the attribute. |
||||||
|
*/ |
||||||
|
public int length() { |
||||||
|
int length = 2 + 2 + 4 + code.length + 2 + handlers.length * 8 + 2; |
||||||
|
|
||||||
|
for (int i = 0; i < attrs.length; i++) { |
||||||
|
length += 2 + 4 + attrs[i].length(); |
||||||
|
} |
||||||
|
|
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the line number debug info for the code. |
||||||
|
* |
||||||
|
* @return The line number debug info for the code. |
||||||
|
*/ |
||||||
|
public LineNumberDebugInfo[] lineNumbers() { |
||||||
|
if (lineNumbers != null) { |
||||||
|
return lineNumbers.lineNumbers(); |
||||||
|
} |
||||||
|
|
||||||
|
return new LineNumberDebugInfo[0]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the local variable debug info for the code. |
||||||
|
* |
||||||
|
* @return The local variable debug info for the code. |
||||||
|
*/ |
||||||
|
public LocalDebugInfo[] locals() { |
||||||
|
if (locals != null) { |
||||||
|
return locals.locals(); |
||||||
|
} |
||||||
|
|
||||||
|
return new LocalDebugInfo[0]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the line number debug info for the code. |
||||||
|
* |
||||||
|
* @param lineNumbers |
||||||
|
* The line number debug info for the code. |
||||||
|
*/ |
||||||
|
public void setLineNumbers(final LineNumberDebugInfo[] lineNumbers) { |
||||||
|
if (lineNumbers == null) { |
||||||
|
for (int i = 0; i < attrs.length; i++) { |
||||||
|
if (this.lineNumbers == attrs[i]) { |
||||||
|
final Attribute[] a = attrs; |
||||||
|
attrs = new Attribute[a.length - 1]; |
||||||
|
System.arraycopy(a, 0, attrs, 0, i); |
||||||
|
System.arraycopy(a, i + 1, attrs, i, attrs.length - i); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
this.lineNumbers = null; |
||||||
|
} else if (this.lineNumbers != null) { |
||||||
|
this.lineNumbers.setLineNumbers(lineNumbers); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the local variable debug info for the code. |
||||||
|
* |
||||||
|
* @param locals |
||||||
|
* The local variable debug info for the code. |
||||||
|
*/ |
||||||
|
public void setLocals(final LocalDebugInfo[] locals) { |
||||||
|
if (locals == null) { |
||||||
|
for (int i = 0; i < attrs.length; i++) { |
||||||
|
if (this.locals == attrs[i]) { |
||||||
|
final Attribute[] a = attrs; |
||||||
|
attrs = new Attribute[a.length - 1]; |
||||||
|
System.arraycopy(a, 0, attrs, 0, i); |
||||||
|
System.arraycopy(a, i + 1, attrs, i, attrs.length - i); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
this.locals = null; |
||||||
|
} else if (this.locals != null) { |
||||||
|
this.locals.setLocals(locals); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the exception handlers in the method. |
||||||
|
* |
||||||
|
* @return The handlers. |
||||||
|
*/ |
||||||
|
public Catch[] exceptionHandlers() { |
||||||
|
return handlers; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the bytes of the code. |
||||||
|
* |
||||||
|
* @return The code. |
||||||
|
*/ |
||||||
|
public byte[] code() { |
||||||
|
return code; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return the length of the code array |
||||||
|
*/ |
||||||
|
public int codeLength() { |
||||||
|
return (code.length); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the bytes of the code. |
||||||
|
* |
||||||
|
* @param code |
||||||
|
* The code. |
||||||
|
*/ |
||||||
|
public void setCode(final byte[] code) { |
||||||
|
this.code = code; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Private constructor for cloning. |
||||||
|
*/ |
||||||
|
private Code(final Code other) { |
||||||
|
super(other.nameIndex, other.length); |
||||||
|
|
||||||
|
this.classInfo = other.classInfo; |
||||||
|
this.maxStack = other.maxStack; |
||||||
|
this.maxLocals = other.maxLocals; |
||||||
|
|
||||||
|
this.code = new byte[other.code.length]; |
||||||
|
System.arraycopy(other.code, 0, this.code, 0, other.code.length); |
||||||
|
this.handlers = new Catch[other.handlers.length]; |
||||||
|
for (int i = 0; i < other.handlers.length; i++) { |
||||||
|
this.handlers[i] = (Catch) other.handlers[i].clone(); |
||||||
|
} |
||||||
|
|
||||||
|
if (other.lineNumbers != null) { |
||||||
|
this.lineNumbers = (LineNumberTable) other.lineNumbers.clone(); |
||||||
|
} |
||||||
|
|
||||||
|
if (other.locals != null) { |
||||||
|
this.locals = (LocalVariableTable) other.locals.clone(); |
||||||
|
} |
||||||
|
|
||||||
|
this.attrs = new Attribute[other.attrs.length]; |
||||||
|
for (int i = 0; i < other.attrs.length; i++) { |
||||||
|
this.attrs[i] = other.attrs[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Object clone() { |
||||||
|
return (new Code(this)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a string representation of the attribute. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
String x = ""; |
||||||
|
|
||||||
|
if (handlers != null) { |
||||||
|
for (int i = 0; i < handlers.length; i++) { |
||||||
|
x += "\n " + handlers[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* for (int i = 0; i < attrs.length; i++) { x += "\n " + attrs[i]; } |
||||||
|
*/ |
||||||
|
|
||||||
|
return "(code " + maxStack + " " + maxLocals + " " + code.length + x |
||||||
|
+ ")"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,115 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.file; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* The ConstantValue attribute stores an index into the constant pool that |
||||||
|
* represents constant value. A class's static fields have constant value |
||||||
|
* attributes. |
||||||
|
* |
||||||
|
* @see Field |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class ConstantValue extends Attribute { |
||||||
|
private int constantValueIndex; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new <code>ConstantValue</code> from scratch |
||||||
|
* |
||||||
|
* @param nameIndex |
||||||
|
* The index in the constant pool of the UTF8 string |
||||||
|
* "ConstantValue" |
||||||
|
* @param constantValueIndex |
||||||
|
* The index in the constant pool of the Constant containing the |
||||||
|
* constant value |
||||||
|
*/ |
||||||
|
ConstantValue(final int nameIndex, final int length, |
||||||
|
final int constantValueIndex) { |
||||||
|
super(nameIndex, length); |
||||||
|
this.constantValueIndex = constantValueIndex; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Create a ConstantValue attribute from a data stream. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The data stream of the class file. |
||||||
|
* @param nameIndex |
||||||
|
* The index into the constant pool of the name of the attribute. |
||||||
|
* @param length |
||||||
|
* The length of the attribute, excluding the header. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
public ConstantValue(final DataInputStream in, final int nameIndex, |
||||||
|
final int length) throws IOException { |
||||||
|
super(nameIndex, length); |
||||||
|
constantValueIndex = in.readUnsignedShort(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the attribute to a data stream. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The data stream of the class file. |
||||||
|
*/ |
||||||
|
public void writeData(final DataOutputStream out) throws IOException { |
||||||
|
out.writeShort(constantValueIndex); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index into the constant pool of the constant value. |
||||||
|
*/ |
||||||
|
public int constantValueIndex() { |
||||||
|
return constantValueIndex; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the index into the constant pool of the constant value. |
||||||
|
*/ |
||||||
|
public void setConstantValueIndex(final int index) { |
||||||
|
this.constantValueIndex = index; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Private constructor used for cloning. |
||||||
|
*/ |
||||||
|
private ConstantValue(final ConstantValue other) { |
||||||
|
super(other.nameIndex, other.length); |
||||||
|
|
||||||
|
this.constantValueIndex = other.constantValueIndex; |
||||||
|
} |
||||||
|
|
||||||
|
public Object clone() { |
||||||
|
return (new ConstantValue(this)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Converts the attribute to a string. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
return "(constant-value " + constantValueIndex + ")"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,140 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.file; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Exceptions describes the types of exceptions that a method may throw. The |
||||||
|
* Exceptions attribute stores a list of indices into the constant pool of the |
||||||
|
* typs of exceptions thrown by the method. |
||||||
|
* |
||||||
|
* @see Method |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class Exceptions extends Attribute { |
||||||
|
private int[] exceptions; |
||||||
|
|
||||||
|
private ClassInfo classInfo; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor for create an <code>Exceptions</code> from scratch. |
||||||
|
* |
||||||
|
* @param nameIndex |
||||||
|
* The index of the UTF8 string "Exceptions" in the class's |
||||||
|
* constant pool |
||||||
|
* @param exceptions |
||||||
|
* A non-<code>null</code> array of indices into the constant |
||||||
|
* pool for the types of the exceptions |
||||||
|
*/ |
||||||
|
Exceptions(final ClassInfo info, final int nameIndex, final int[] exceptions) { |
||||||
|
super(nameIndex, (2 * exceptions.length) + 2); |
||||||
|
this.classInfo = info; |
||||||
|
this.exceptions = exceptions; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Create an Exceptions attribute from a data stream. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The data stream of the class file. |
||||||
|
* @param nameIndex |
||||||
|
* The index into the constant pool of the name of the attribute. |
||||||
|
* @param length |
||||||
|
* The length of the attribute, excluding the header. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
public Exceptions(final ClassInfo classInfo, final DataInputStream in, |
||||||
|
final int nameIndex, final int length) throws IOException { |
||||||
|
super(nameIndex, length); |
||||||
|
|
||||||
|
this.classInfo = classInfo; |
||||||
|
|
||||||
|
final int count = in.readUnsignedShort(); |
||||||
|
|
||||||
|
exceptions = new int[count]; |
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) { |
||||||
|
exceptions[i] = in.readUnsignedShort(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the attribute to a data stream. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The data stream of the class file. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
public void writeData(final DataOutputStream out) throws IOException { |
||||||
|
out.writeShort(exceptions.length); |
||||||
|
|
||||||
|
for (int i = 0; i < exceptions.length; i++) { |
||||||
|
out.writeShort(exceptions[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the indices into the constant pool of the types of the exceptions |
||||||
|
* thrown by this method. |
||||||
|
* |
||||||
|
* @return The indices of the types of the exceptions thrown. |
||||||
|
*/ |
||||||
|
public int[] exceptionTypes() { |
||||||
|
return exceptions; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the length of the attribute. |
||||||
|
*/ |
||||||
|
public int length() { |
||||||
|
return 2 + exceptions.length * 2; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Private constructor used for cloning. |
||||||
|
*/ |
||||||
|
private Exceptions(final Exceptions other) { |
||||||
|
super(other.nameIndex, other.length); |
||||||
|
|
||||||
|
this.exceptions = new int[other.exceptions.length]; |
||||||
|
System.arraycopy(other.exceptions, 0, this.exceptions, 0, |
||||||
|
other.exceptions.length); |
||||||
|
this.classInfo = other.classInfo; |
||||||
|
} |
||||||
|
|
||||||
|
public Object clone() { |
||||||
|
return (new Exceptions(this)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a string representation of the attribute. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
return "(exceptions)"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,293 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.file; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Field models a field (member variable) in a class. The Field class grants |
||||||
|
* access to information such as the field's modifiers, its name and type |
||||||
|
* descriptor (represented as indices into the constant pool), and any |
||||||
|
* attributes of the field. Static fields have a ConstantValue attribute. |
||||||
|
* |
||||||
|
* @see ConstantValue |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class Field implements FieldInfo { |
||||||
|
private ClassInfo classInfo; |
||||||
|
|
||||||
|
private int modifiers; |
||||||
|
|
||||||
|
private int name; |
||||||
|
|
||||||
|
private int type; |
||||||
|
|
||||||
|
private Attribute[] attrs; |
||||||
|
|
||||||
|
private ConstantValue constantValue; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor for creating a new field from scratch |
||||||
|
*/ |
||||||
|
Field(final ClassInfo classInfo, final int modifiers, final int typeIndex, |
||||||
|
final int nameIndex) { |
||||||
|
this.classInfo = classInfo; |
||||||
|
this.modifiers = modifiers; |
||||||
|
this.name = nameIndex; |
||||||
|
this.type = typeIndex; |
||||||
|
this.attrs = new Attribute[0]; |
||||||
|
this.constantValue = null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor for creating a new field that has a constant value from |
||||||
|
* scratch |
||||||
|
*/ |
||||||
|
Field(final ClassInfo classInfo, final int modifiers, final int typeIndex, |
||||||
|
final int nameIndex, final int cvNameIndex, |
||||||
|
final int constantValueIndex) { |
||||||
|
this.classInfo = classInfo; |
||||||
|
this.modifiers = modifiers; |
||||||
|
this.name = nameIndex; |
||||||
|
this.type = typeIndex; |
||||||
|
this.constantValue = new ConstantValue(cvNameIndex, 2, |
||||||
|
constantValueIndex); |
||||||
|
|
||||||
|
// The constant value is an attribute
|
||||||
|
this.attrs = new Attribute[1]; |
||||||
|
this.attrs[0] = constantValue; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Read a field from a class file. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The data stream of the class file. |
||||||
|
* @param classInfo |
||||||
|
* The class file containing the field. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
public Field(final DataInputStream in, final ClassInfo classInfo) |
||||||
|
throws IOException { |
||||||
|
this.classInfo = classInfo; |
||||||
|
|
||||||
|
modifiers = in.readUnsignedShort(); |
||||||
|
|
||||||
|
name = in.readUnsignedShort(); |
||||||
|
type = in.readUnsignedShort(); |
||||||
|
|
||||||
|
final int numAttributes = in.readUnsignedShort(); |
||||||
|
|
||||||
|
attrs = new Attribute[numAttributes]; |
||||||
|
|
||||||
|
for (int i = 0; i < numAttributes; i++) { |
||||||
|
final int nameIndex = in.readUnsignedShort(); |
||||||
|
final int length = in.readInt(); |
||||||
|
|
||||||
|
final Constant name = classInfo.constants()[nameIndex]; |
||||||
|
|
||||||
|
if (name != null) { |
||||||
|
if ("ConstantValue".equals(name.value())) { |
||||||
|
constantValue = new ConstantValue(in, nameIndex, length); |
||||||
|
attrs[i] = constantValue; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (attrs[i] == null) { |
||||||
|
attrs[i] = new GenericAttribute(in, nameIndex, length); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the class which declared the field. |
||||||
|
* |
||||||
|
* @return The ClassInfo of the class which declared the field. |
||||||
|
*/ |
||||||
|
public ClassInfo declaringClass() { |
||||||
|
return classInfo; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the index into the constant pool of the name of the field. |
||||||
|
* |
||||||
|
* @param name |
||||||
|
* The name of the field. |
||||||
|
*/ |
||||||
|
public void setNameIndex(final int name) { |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the index into the constant pool of the type of the field. |
||||||
|
* |
||||||
|
* @param type |
||||||
|
* The type of the field. |
||||||
|
*/ |
||||||
|
public void setTypeIndex(final int type) { |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the index into the constant pool of the name of the field. |
||||||
|
* |
||||||
|
* @return The index into the constant pool of the name of the field. |
||||||
|
*/ |
||||||
|
public int nameIndex() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the index into the constant pool of the type of the field. |
||||||
|
* |
||||||
|
* @return The index into the constant pool of the type of the field. |
||||||
|
*/ |
||||||
|
public int typeIndex() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the modifiers of the field. The values correspond to the constants in |
||||||
|
* the Modifiers class. |
||||||
|
* |
||||||
|
* @param modifiers |
||||||
|
* A bit vector of modifier flags for the field. |
||||||
|
* @see Modifiers |
||||||
|
*/ |
||||||
|
public void setModifiers(final int modifiers) { |
||||||
|
this.modifiers = modifiers; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the modifiers of the field. The values correspond to the constants in |
||||||
|
* the Modifiers class. |
||||||
|
* |
||||||
|
* @return A bit vector of modifier flags for the field. |
||||||
|
* @see Modifiers |
||||||
|
*/ |
||||||
|
public int modifiers() { |
||||||
|
return modifiers; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the index into the constant pool of the field's constant value, if |
||||||
|
* any. Returns 0 if the field does not have a constant value. |
||||||
|
* |
||||||
|
* @see ClassInfo#constants |
||||||
|
*/ |
||||||
|
public int constantValue() { |
||||||
|
if (constantValue != null) { |
||||||
|
return constantValue.constantValueIndex(); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the index into the constant pool of the field's constant value. |
||||||
|
* |
||||||
|
* @see ClassInfo#constants |
||||||
|
*/ |
||||||
|
public void setConstantValue(final int index) { |
||||||
|
if (constantValue != null) { |
||||||
|
constantValue.setConstantValueIndex(index); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the field to a class file. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The data stream of the class file. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
public void write(final DataOutputStream out) throws IOException { |
||||||
|
out.writeShort(modifiers); |
||||||
|
|
||||||
|
out.writeShort(name); |
||||||
|
out.writeShort(type); |
||||||
|
|
||||||
|
out.writeShort(attrs.length); |
||||||
|
|
||||||
|
for (int i = 0; i < attrs.length; i++) { |
||||||
|
out.writeShort(attrs[i].nameIndex()); |
||||||
|
out.writeInt(attrs[i].length()); |
||||||
|
attrs[i].writeData(out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convert the field to a string. |
||||||
|
* |
||||||
|
* @return A string representation of the field. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
String x = ""; |
||||||
|
|
||||||
|
x += " (modifiers"; |
||||||
|
|
||||||
|
if ((modifiers & Modifiers.PUBLIC) != 0) { |
||||||
|
x += " PUBLIC"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.PRIVATE) != 0) { |
||||||
|
x += " PRIVATE"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.PROTECTED) != 0) { |
||||||
|
x += " PROTECTED"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.STATIC) != 0) { |
||||||
|
x += " STATIC"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.FINAL) != 0) { |
||||||
|
x += " FINAL"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.SYNCHRONIZED) != 0) { |
||||||
|
x += " SYNCHRONIZED"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.VOLATILE) != 0) { |
||||||
|
x += " VOLATILE"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.TRANSIENT) != 0) { |
||||||
|
x += " TRANSIENT"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.NATIVE) != 0) { |
||||||
|
x += " NATIVE"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.INTERFACE) != 0) { |
||||||
|
x += " INTERFACE"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.ABSTRACT) != 0) { |
||||||
|
x += " ABSTRACT"; |
||||||
|
} |
||||||
|
x += ")"; |
||||||
|
|
||||||
|
if (constantValue != null) { |
||||||
|
x += " " + constantValue; |
||||||
|
} |
||||||
|
|
||||||
|
return "(field " + name + " " + type + x + ")"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.file; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* The Java Virtual Machine Specification allows implementors to invent their |
||||||
|
* own attributes. GenericAttribute models attributes whose name BLOAT does not |
||||||
|
* recognize. |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class GenericAttribute extends Attribute { |
||||||
|
private byte[] data; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Create an attribute from a data stream. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The data stream of the class file. |
||||||
|
* @param nameIndex |
||||||
|
* The index into the constant pool of the name of the attribute. |
||||||
|
* @param length |
||||||
|
* The length of the attribute, excluding the header. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
public GenericAttribute(final DataInputStream in, final int nameIndex, |
||||||
|
final int length) throws IOException { |
||||||
|
super(nameIndex, length); |
||||||
|
data = new byte[length]; |
||||||
|
for (int read = 0; read < length;) { |
||||||
|
read += in.read(data, read, length - read); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the attribute to a data stream. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The data stream of the class file. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
public void writeData(final DataOutputStream out) throws IOException { |
||||||
|
out.write(data, 0, data.length); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Private constructor used in cloning. |
||||||
|
*/ |
||||||
|
private GenericAttribute(final GenericAttribute other) { |
||||||
|
super(other.nameIndex, other.length); |
||||||
|
|
||||||
|
this.data = new byte[other.data.length]; |
||||||
|
System.arraycopy(other.data, 0, this.data, 0, other.data.length); |
||||||
|
} |
||||||
|
|
||||||
|
public Object clone() { |
||||||
|
return (new GenericAttribute(this)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,260 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.file; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.util.*; |
||||||
|
import java.util.jar.*; |
||||||
|
import java.util.zip.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Does a lot of the same stuff as <tt>ClassFileLoader</tt>, but classes are |
||||||
|
* committed to a JAR file instead of regular files. |
||||||
|
*/ |
||||||
|
public class JarFileCommitter extends ClassFileLoader { |
||||||
|
|
||||||
|
private FunkyJar funky; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param file |
||||||
|
* <tt>File</tt> representing JAR file |
||||||
|
* @param compress |
||||||
|
* If <tt>true</tt>, contents of JAR file is compressed |
||||||
|
* @param version |
||||||
|
* Version for the JAR file's manifest |
||||||
|
* @param author |
||||||
|
* Author string from JAR file's manifest |
||||||
|
*/ |
||||||
|
public JarFileCommitter(final File file, final boolean compress, |
||||||
|
final String version, final String author) throws IOException { |
||||||
|
|
||||||
|
funky = new FunkyJar(file, compress, version, author); |
||||||
|
} |
||||||
|
|
||||||
|
protected OutputStream outputStreamFor(final String name) |
||||||
|
throws IOException { |
||||||
|
|
||||||
|
funky.newEntry(name); |
||||||
|
return funky; |
||||||
|
} |
||||||
|
|
||||||
|
public OutputStream outputStreamFor(final ClassInfo info) |
||||||
|
throws IOException { |
||||||
|
// This is funky. Recall that a JarOutputStream is also an output
|
||||||
|
// stream. So, we just return it. This is why we have to
|
||||||
|
// override the write, etc. methods.
|
||||||
|
|
||||||
|
// Make a new entry based on the class name
|
||||||
|
final String name = info.name() + ".class"; |
||||||
|
return outputStreamFor(name); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Signifies that we are finished with this <tt>JarFileCommitter</tt>. |
||||||
|
*/ |
||||||
|
public void done() throws IOException { |
||||||
|
funky.done(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* We subclass JarOutputStream so that we can return an OutputStream to which a |
||||||
|
* BLOATed class file will be written. In order to accomodate non-compression, |
||||||
|
* we have to perform the checksum along the way. Bletch. |
||||||
|
*/ |
||||||
|
class FunkyJar extends JarOutputStream { |
||||||
|
|
||||||
|
private static final String MANIFEST = JarFile.MANIFEST_NAME; |
||||||
|
|
||||||
|
private static final String MANIFEST_DIR = "META-INF/"; |
||||||
|
|
||||||
|
private static final CRC32 crc32 = new CRC32(); |
||||||
|
|
||||||
|
private boolean compress; |
||||||
|
|
||||||
|
private JarEntry currEntry; |
||||||
|
|
||||||
|
private Size size; |
||||||
|
|
||||||
|
class Size { |
||||||
|
long value = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
*/ |
||||||
|
public FunkyJar(final File file, boolean compress, final String version, |
||||||
|
final String author) throws IOException { |
||||||
|
super(new FileOutputStream(file)); |
||||||
|
|
||||||
|
this.compress = compress; |
||||||
|
|
||||||
|
if (compress) { |
||||||
|
this.setMethod(ZipOutputStream.DEFLATED); |
||||||
|
} else { |
||||||
|
this.setMethod(ZipOutputStream.STORED); |
||||||
|
} |
||||||
|
|
||||||
|
final Manifest manifest = new Manifest(); |
||||||
|
final Attributes global = manifest.getMainAttributes(); |
||||||
|
if (global.getValue(Attributes.Name.MANIFEST_VERSION) == null) { |
||||||
|
global.put(Attributes.Name.MANIFEST_VERSION, version); |
||||||
|
} |
||||||
|
|
||||||
|
if (global.getValue(new Attributes.Name("Created-By")) == null) { |
||||||
|
global.put(new Attributes.Name("Created-By"), author); |
||||||
|
} |
||||||
|
|
||||||
|
// Add directory for manifest
|
||||||
|
JarEntry entry = new JarEntry(FunkyJar.MANIFEST_DIR); |
||||||
|
entry.setTime(System.currentTimeMillis()); |
||||||
|
entry.setSize(0); // Directories have size 0
|
||||||
|
entry.setCrc(0); // Checksum is 0
|
||||||
|
this.putNextEntry(entry); |
||||||
|
|
||||||
|
// Add manifest
|
||||||
|
entry = new JarEntry(FunkyJar.MANIFEST); |
||||||
|
entry.setTime(System.currentTimeMillis()); |
||||||
|
if (!compress) { |
||||||
|
// Have to compute checksum ourselves. Use an ugly anonymous
|
||||||
|
// inner class. Influenced by CRC32OutputStream in
|
||||||
|
// sun.tools.jar.Main. Please don't sue me. I have no money.
|
||||||
|
// Maybe you could give me a job instead. Of course, then I'd
|
||||||
|
// have money and you would sue me. Hmm.
|
||||||
|
final Size size = new Size(); |
||||||
|
FunkyJar.crc32.reset(); |
||||||
|
manifest.write(new OutputStream() { |
||||||
|
public void write(final int r) throws IOException { |
||||||
|
FunkyJar.crc32.update(r); |
||||||
|
size.value++; |
||||||
|
} |
||||||
|
|
||||||
|
public void write(final byte[] b) throws IOException { |
||||||
|
FunkyJar.crc32.update(b, 0, b.length); |
||||||
|
size.value += b.length; |
||||||
|
} |
||||||
|
|
||||||
|
public void write(final byte[] b, final int off, final int len) |
||||||
|
throws IOException { |
||||||
|
FunkyJar.crc32.update(b, off, len); |
||||||
|
size.value += len - off; |
||||||
|
} |
||||||
|
}); |
||||||
|
entry.setSize(size.value); |
||||||
|
entry.setCrc(FunkyJar.crc32.getValue()); |
||||||
|
} |
||||||
|
this.putNextEntry(entry); |
||||||
|
manifest.write(this); // Write the manifest to JAR file
|
||||||
|
this.closeEntry(); |
||||||
|
} |
||||||
|
|
||||||
|
public void newEntry(final String name) throws IOException { |
||||||
|
makeDirs(name); |
||||||
|
|
||||||
|
currEntry = new JarEntry(name); |
||||||
|
currEntry.setTime(System.currentTimeMillis()); |
||||||
|
if (compress) { |
||||||
|
currEntry.setMethod(ZipEntry.DEFLATED); |
||||||
|
} else { |
||||||
|
currEntry.setMethod(ZipEntry.STORED); |
||||||
|
} |
||||||
|
this.putNextEntry(currEntry); |
||||||
|
FunkyJar.crc32.reset(); |
||||||
|
this.size = new Size(); |
||||||
|
} |
||||||
|
|
||||||
|
private Set dirs; |
||||||
|
|
||||||
|
/** |
||||||
|
* look at the path name specified by key and create zip entries for each |
||||||
|
* directory level not already added. |
||||||
|
*/ |
||||||
|
private void makeDirs(final String key) throws IOException { |
||||||
|
if (dirs == null) { |
||||||
|
dirs = new HashSet(); |
||||||
|
} |
||||||
|
int idx = 0; |
||||||
|
int last = 0; |
||||||
|
while ((last = key.indexOf('/', idx + 1)) != -1) { |
||||||
|
final String aDir = key.substring(0, last + 1); |
||||||
|
if (!dirs.contains(aDir)) { |
||||||
|
dirs.add(aDir); |
||||||
|
this.putNextEntry(new ZipEntry(aDir)); |
||||||
|
this.closeEntry(); |
||||||
|
} |
||||||
|
idx = last; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void write(final int r) throws IOException { |
||||||
|
super.write(r); |
||||||
|
|
||||||
|
if (!compress && (size != null)) { |
||||||
|
FunkyJar.crc32.update(r); |
||||||
|
size.value++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void write(final byte[] b) throws IOException { |
||||||
|
super.write(b); |
||||||
|
|
||||||
|
if (!compress && (size != null)) { |
||||||
|
FunkyJar.crc32.update(b, 0, b.length); |
||||||
|
size.value += b.length; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void write(final byte[] b, final int off, final int len) |
||||||
|
throws IOException { |
||||||
|
super.write(b, off, len); |
||||||
|
|
||||||
|
if (!compress && (size != null)) { |
||||||
|
FunkyJar.crc32.update(b, off, len); |
||||||
|
size.value += len - off; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void close() throws IOException { |
||||||
|
// Okay, everythings is done. Set some values for the entry,
|
||||||
|
// cross your fingers, and run away.
|
||||||
|
if (!compress && (size != null)) { |
||||||
|
currEntry.setSize(size.value); |
||||||
|
currEntry.setCrc(FunkyJar.crc32.getValue()); |
||||||
|
} |
||||||
|
|
||||||
|
currEntry = null; |
||||||
|
size = null; |
||||||
|
this.closeEntry(); |
||||||
|
|
||||||
|
// Note that we don't invoke the super class method.
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Signifies that we are finished with this <tt>JarFileCommitter</tt>. |
||||||
|
*/ |
||||||
|
public void done() throws IOException { |
||||||
|
super.close(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,142 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.file; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* LineNumberTable is an attribute of a code attribute. A LineNumberTable stores |
||||||
|
* information that relates indices into the code array (instructions) to the |
||||||
|
* lines of code in the source file from which they were compiled. This optional |
||||||
|
* attribute is used with debuggers (<i>duh</i>) and consists of an array of |
||||||
|
* <tt>reflect.LineNumberDebugInfo</tt>. |
||||||
|
* |
||||||
|
* @see Code |
||||||
|
* @see EDU.purdue.cs.bloat.reflect.LineNumberDebugInfo |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class LineNumberTable extends Attribute { |
||||||
|
private LineNumberDebugInfo[] lineNumbers; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Create an attribute from a data stream. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The data stream of the class file. |
||||||
|
* @param nameIndex |
||||||
|
* The index into the constant pool of the name of the attribute. |
||||||
|
* @param length |
||||||
|
* The length of the attribute, excluding the header. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
public LineNumberTable(final DataInputStream in, final int nameIndex, |
||||||
|
final int length) throws IOException { |
||||||
|
super(nameIndex, length); |
||||||
|
|
||||||
|
final int numLines = in.readUnsignedShort(); |
||||||
|
|
||||||
|
lineNumbers = new LineNumberDebugInfo[numLines]; |
||||||
|
|
||||||
|
for (int i = 0; i < lineNumbers.length; i++) { |
||||||
|
final int startPC = in.readUnsignedShort(); |
||||||
|
final int lineNumber = in.readUnsignedShort(); |
||||||
|
lineNumbers[i] = new LineNumberDebugInfo(startPC, lineNumber); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the line number debug info for the code. |
||||||
|
* |
||||||
|
* @return The line number debug info for the code. |
||||||
|
*/ |
||||||
|
public LineNumberDebugInfo[] lineNumbers() { |
||||||
|
return lineNumbers; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the line number debug info for the code. |
||||||
|
* |
||||||
|
* @param lineNumbers |
||||||
|
* The line number debug info for the code. |
||||||
|
*/ |
||||||
|
public void setLineNumbers(final LineNumberDebugInfo[] lineNumbers) { |
||||||
|
this.lineNumbers = lineNumbers; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the length of the attribute. |
||||||
|
* |
||||||
|
* @return The length of the attribute. |
||||||
|
*/ |
||||||
|
public int length() { |
||||||
|
return 2 + lineNumbers.length * 4; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
String x = "(lines"; |
||||||
|
|
||||||
|
for (int i = 0; i < lineNumbers.length; i++) { |
||||||
|
x += "\n (line #" + lineNumbers[i].lineNumber() + " pc=" |
||||||
|
+ lineNumbers[i].startPC() + ")"; |
||||||
|
} |
||||||
|
|
||||||
|
return x + ")"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the attribute to a data stream. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The data stream of the class file. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
public void writeData(final DataOutputStream out) throws IOException { |
||||||
|
out.writeShort(lineNumbers.length); |
||||||
|
|
||||||
|
for (int i = 0; i < lineNumbers.length; i++) { |
||||||
|
out.writeShort(lineNumbers[i].startPC()); |
||||||
|
out.writeShort(lineNumbers[i].lineNumber()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Private constructor used in cloning. |
||||||
|
*/ |
||||||
|
private LineNumberTable(final LineNumberTable other) { |
||||||
|
super(other.nameIndex, other.length); |
||||||
|
|
||||||
|
this.lineNumbers = new LineNumberDebugInfo[other.lineNumbers.length]; |
||||||
|
for (int i = 0; i < other.lineNumbers.length; i++) { |
||||||
|
this.lineNumbers[i] = (LineNumberDebugInfo) other.lineNumbers[i] |
||||||
|
.clone(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Object clone() { |
||||||
|
return (new LineNumberTable(this)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,147 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.file; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* LocalVariableTable represents debugging information that may be used by a |
||||||
|
* debugger to determine the value of a given local variable during program |
||||||
|
* execution. It is essentially an array of <tt>reflect.LocalDebugInfo</tt>. |
||||||
|
* |
||||||
|
* @see EDU.purdue.cs.bloat.reflect.LocalDebugInfo |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class LocalVariableTable extends Attribute { |
||||||
|
private LocalDebugInfo[] locals; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Create an attribute from a data stream. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The data stream of the class file. |
||||||
|
* @param index |
||||||
|
* The index into the constant pool of the name of the attribute. |
||||||
|
* @param len |
||||||
|
* The length of the attribute, excluding the header. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
public LocalVariableTable(final DataInputStream in, final int index, |
||||||
|
final int len) throws IOException { |
||||||
|
super(index, len); |
||||||
|
|
||||||
|
final int numLocals = in.readUnsignedShort(); |
||||||
|
|
||||||
|
locals = new LocalDebugInfo[numLocals]; |
||||||
|
|
||||||
|
for (int i = 0; i < locals.length; i++) { |
||||||
|
final int startPC = in.readUnsignedShort(); |
||||||
|
final int length = in.readUnsignedShort(); |
||||||
|
final int nameIndex = in.readUnsignedShort(); |
||||||
|
final int typeIndex = in.readUnsignedShort(); |
||||||
|
final int varIndex = in.readUnsignedShort(); |
||||||
|
locals[i] = new LocalDebugInfo(startPC, length, nameIndex, |
||||||
|
typeIndex, varIndex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the local variable debug info for the code. |
||||||
|
* |
||||||
|
* @return The local variable debug info for the code. |
||||||
|
*/ |
||||||
|
public LocalDebugInfo[] locals() { |
||||||
|
return locals; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the local variable debug info for the code. |
||||||
|
* |
||||||
|
* @param locals |
||||||
|
* The local variable debug info for the code. |
||||||
|
*/ |
||||||
|
public void setLocals(final LocalDebugInfo[] locals) { |
||||||
|
this.locals = locals; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the length of the attribute. |
||||||
|
* |
||||||
|
* @return The length of the attribute. |
||||||
|
*/ |
||||||
|
public int length() { |
||||||
|
return 2 + locals.length * 10; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
String x = "(locals"; |
||||||
|
|
||||||
|
for (int i = 0; i < locals.length; i++) { |
||||||
|
x += "\n (local @" + locals[i].index() + " name=" |
||||||
|
+ locals[i].nameIndex() + " type=" + locals[i].typeIndex() |
||||||
|
+ " pc=" + locals[i].startPC() + ".." |
||||||
|
+ (locals[i].startPC() + locals[i].length()) + ")"; |
||||||
|
} |
||||||
|
|
||||||
|
return x + ")"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the attribute to a data stream. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The data stream of the class file. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
public void writeData(final DataOutputStream out) throws IOException { |
||||||
|
out.writeShort(locals.length); |
||||||
|
|
||||||
|
for (int i = 0; i < locals.length; i++) { |
||||||
|
out.writeShort(locals[i].startPC()); |
||||||
|
out.writeShort(locals[i].length()); |
||||||
|
out.writeShort(locals[i].nameIndex()); |
||||||
|
out.writeShort(locals[i].typeIndex()); |
||||||
|
out.writeShort(locals[i].index()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Private constructor used in cloning. |
||||||
|
*/ |
||||||
|
private LocalVariableTable(final LocalVariableTable other) { |
||||||
|
super(other.nameIndex, other.length); |
||||||
|
|
||||||
|
this.locals = new LocalDebugInfo[other.locals.length]; |
||||||
|
for (int i = 0; i < other.locals.length; i++) { |
||||||
|
this.locals[i] = (LocalDebugInfo) other.locals[i].clone(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Object clone() { |
||||||
|
return (new LocalVariableTable(this)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
# All files in the distribution of BLOAT (Bytecode Level Optimization and
|
||||||
|
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
||||||
|
# Research Foundation of Purdue University. All rights reserved.
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
CLASS = \
|
||||||
|
Attribute.class\
|
||||||
|
ClassFile.class\
|
||||||
|
ClassFileLoader.class\
|
||||||
|
Code.class\
|
||||||
|
ConstantValue.class\
|
||||||
|
Exceptions.class\
|
||||||
|
Field.class\
|
||||||
|
GenericAttribute.class\
|
||||||
|
JarFileCommitter.class\
|
||||||
|
LineNumberTable.class\
|
||||||
|
LocalVariableTable.class\
|
||||||
|
Method.class
|
||||||
|
|
||||||
|
include ../class.mk |
@ -0,0 +1,484 @@ |
|||||||
|
/** |
||||||
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and |
||||||
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue |
||||||
|
* Research Foundation of Purdue University. All rights reserved. |
||||||
|
* |
||||||
|
* This library is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU Lesser General Public |
||||||
|
* License as published by the Free Software Foundation; either |
||||||
|
* version 2.1 of the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This library is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
* Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public |
||||||
|
* License along with this library; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
package EDU.purdue.cs.bloat.file; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
import EDU.purdue.cs.bloat.reflect.*; |
||||||
|
import EDU.purdue.cs.bloat.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method represents a method in a Java classfile. A method's name and value |
||||||
|
* (the types of its parameters and its return type) are modeled as indices into |
||||||
|
* it class's constant pool. A method has modifiers that determine whether it is |
||||||
|
* public, private, static, final, etc. Methods have a number of attributes such |
||||||
|
* as their Code and any Exceptions they may throw. |
||||||
|
* |
||||||
|
* @see Code |
||||||
|
* @see Exceptions |
||||||
|
* |
||||||
|
* @author Nate Nystrom (<a |
||||||
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>) |
||||||
|
*/ |
||||||
|
public class Method implements MethodInfo { |
||||||
|
private ClassInfo classInfo; |
||||||
|
|
||||||
|
private int modifiers; |
||||||
|
|
||||||
|
private int name; |
||||||
|
|
||||||
|
private int type; |
||||||
|
|
||||||
|
private Attribute[] attrs; |
||||||
|
|
||||||
|
private Code code; |
||||||
|
|
||||||
|
private Exceptions exceptions; |
||||||
|
|
||||||
|
public static boolean DEBUG = Boolean.getBoolean("Method.DEBUG"); |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor for creating a <code>Method</code> from scratch |
||||||
|
* |
||||||
|
* @param attrs |
||||||
|
* Must include <code>code</code> and <code>exceptions</code> |
||||||
|
* which are themselves attributes of this method |
||||||
|
*/ |
||||||
|
Method(final ClassInfo classInfo, final int modifiers, final int name, |
||||||
|
final int type, final Attribute[] attrs, final Code code, |
||||||
|
final Exceptions exceptions) { |
||||||
|
this.classInfo = classInfo; |
||||||
|
this.modifiers = modifiers; |
||||||
|
this.name = name; |
||||||
|
this.type = type; |
||||||
|
|
||||||
|
Assert.isNotNull(attrs, "Every method must have at least a Code " |
||||||
|
+ "attribute"); |
||||||
|
this.attrs = attrs; |
||||||
|
this.code = code; |
||||||
|
this.exceptions = exceptions; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Read a method from a class file. |
||||||
|
* |
||||||
|
* @param in |
||||||
|
* The data stream of the class file. |
||||||
|
* @param classInfo |
||||||
|
* The class file containing the method. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while reading. |
||||||
|
*/ |
||||||
|
public Method(final DataInputStream in, final ClassInfo classInfo) |
||||||
|
throws IOException { |
||||||
|
this.classInfo = classInfo; |
||||||
|
|
||||||
|
modifiers = in.readUnsignedShort(); |
||||||
|
|
||||||
|
name = in.readUnsignedShort(); |
||||||
|
type = in.readUnsignedShort(); |
||||||
|
|
||||||
|
final int numAttributes = in.readUnsignedShort(); |
||||||
|
|
||||||
|
attrs = new Attribute[numAttributes]; |
||||||
|
|
||||||
|
for (int i = 0; i < numAttributes; i++) { |
||||||
|
final int nameIndex = in.readUnsignedShort(); |
||||||
|
final int length = in.readInt(); |
||||||
|
|
||||||
|
final Constant name = classInfo.constants()[nameIndex]; |
||||||
|
|
||||||
|
if (name != null) { |
||||||
|
if ("Code".equals(name.value())) { |
||||||
|
code = new Code(classInfo, in, nameIndex, length); |
||||||
|
attrs[i] = code; |
||||||
|
} else if ("Exceptions".equals(name.value())) { |
||||||
|
exceptions = new Exceptions(classInfo, in, nameIndex, |
||||||
|
length); |
||||||
|
attrs[i] = exceptions; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (attrs[i] == null) { |
||||||
|
attrs[i] = new GenericAttribute(in, nameIndex, length); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the class which declared the method. |
||||||
|
* |
||||||
|
* @return The ClassInfo of the class which declared the method. |
||||||
|
*/ |
||||||
|
public ClassInfo declaringClass() { |
||||||
|
return classInfo; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the index into the constant pool of the name of the method. |
||||||
|
* |
||||||
|
* @param name |
||||||
|
* The index into the constant pool of the name of the method. |
||||||
|
*/ |
||||||
|
public void setNameIndex(final int name) { |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the index into the constant pool of the type of the method. |
||||||
|
* |
||||||
|
* @param type |
||||||
|
* The index into the constant pool of the type of the method. |
||||||
|
*/ |
||||||
|
public void setTypeIndex(final int type) { |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the index into the constant pool of the name of the method. |
||||||
|
* |
||||||
|
* @return The index into the constant pool of the name of the method. |
||||||
|
*/ |
||||||
|
public int nameIndex() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the index into the constant pool of the type of the method. |
||||||
|
* |
||||||
|
* @return The index into the constant pool of the type of the method. |
||||||
|
*/ |
||||||
|
public int typeIndex() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the modifiers of the method. The values correspond to the constants |
||||||
|
* in the Modifiers class. |
||||||
|
* |
||||||
|
* @param modifiers |
||||||
|
* A bit vector of modifier flags for the method. |
||||||
|
* @see Modifiers |
||||||
|
*/ |
||||||
|
public void setModifiers(final int modifiers) { |
||||||
|
this.modifiers = modifiers; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the modifiers of the method. The values correspond to the constants |
||||||
|
* in the Modifiers class. |
||||||
|
* |
||||||
|
* @return A bit vector of modifier flags for the method. |
||||||
|
* @see Modifiers |
||||||
|
*/ |
||||||
|
public int modifiers() { |
||||||
|
return modifiers; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the maximum height of the operand stack. |
||||||
|
* |
||||||
|
* @return The maximum height of the operand stack. |
||||||
|
*/ |
||||||
|
public int maxStack() { |
||||||
|
if (code != null) { |
||||||
|
return code.maxStack(); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the maximum height of the operand stack. |
||||||
|
* |
||||||
|
* @param maxStack |
||||||
|
* The maximum height of the operand stack. |
||||||
|
*/ |
||||||
|
public void setMaxStack(final int maxStack) { |
||||||
|
if (code != null) { |
||||||
|
code.setMaxStack(maxStack); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the maximum number of locals used in the method. |
||||||
|
* |
||||||
|
* @return The maximum number of locals used in the method. |
||||||
|
*/ |
||||||
|
public int maxLocals() { |
||||||
|
if (code != null) { |
||||||
|
return code.maxLocals(); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the maximum number of locals used in the method. |
||||||
|
* |
||||||
|
* @param maxLocals |
||||||
|
* The maximum number of locals used in the method. |
||||||
|
*/ |
||||||
|
public void setMaxLocals(final int maxLocals) { |
||||||
|
if (code != null) { |
||||||
|
code.setMaxLocals(maxLocals); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the byte code array of the method. |
||||||
|
* |
||||||
|
* @return The byte code array of the method. |
||||||
|
*/ |
||||||
|
public byte[] code() { |
||||||
|
if (code != null) { |
||||||
|
return code.code(); |
||||||
|
} |
||||||
|
return new byte[0]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the length of the Code array. |
||||||
|
*/ |
||||||
|
public int codeLength() { |
||||||
|
if (code != null) { |
||||||
|
return (code.codeLength()); |
||||||
|
} else { |
||||||
|
return (0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the byte code array of the method. |
||||||
|
* |
||||||
|
* @param bytes |
||||||
|
* The byte code array of the method. |
||||||
|
*/ |
||||||
|
public void setCode(final byte[] bytes) { |
||||||
|
if (code != null) { |
||||||
|
code.setCode(bytes); |
||||||
|
if (Method.DEBUG) { |
||||||
|
System.out.println("Set code with " + bytes.length + " bytes"); |
||||||
|
// Thread.dumpStack();
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the indices into the constant pool of the types of the exceptions |
||||||
|
* thrown by the method. |
||||||
|
* |
||||||
|
* @return The indices into the constant pool of the types of the exceptions |
||||||
|
* thrown by the method. |
||||||
|
*/ |
||||||
|
public int[] exceptionTypes() { |
||||||
|
if (exceptions != null) { |
||||||
|
return exceptions.exceptionTypes(); |
||||||
|
} |
||||||
|
return new int[0]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the line numbers of the instructions in the method. |
||||||
|
* |
||||||
|
* @param lineNumbers |
||||||
|
* The line numbers of the instructions in the method. |
||||||
|
*/ |
||||||
|
public void setLineNumbers(final LineNumberDebugInfo[] lineNumbers) { |
||||||
|
if (code != null) { |
||||||
|
code.setLineNumbers(lineNumbers); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the local variable debug info for the method. |
||||||
|
* |
||||||
|
* @param locals |
||||||
|
* The local variable debug info for the method. |
||||||
|
*/ |
||||||
|
public void setLocals(final LocalDebugInfo[] locals) { |
||||||
|
if (code != null) { |
||||||
|
code.setLocals(locals); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the line numbers of the instructions in the method. |
||||||
|
* |
||||||
|
* @return The line numbers of the instructions in the method. |
||||||
|
*/ |
||||||
|
public LineNumberDebugInfo[] lineNumbers() { |
||||||
|
if (code != null) { |
||||||
|
return code.lineNumbers(); |
||||||
|
} |
||||||
|
return new LineNumberDebugInfo[0]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the local variable debug info for the method. |
||||||
|
* |
||||||
|
* @return The local variable debug info for the method. |
||||||
|
*/ |
||||||
|
public LocalDebugInfo[] locals() { |
||||||
|
if (code != null) { |
||||||
|
return code.locals(); |
||||||
|
} |
||||||
|
return new LocalDebugInfo[0]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the exception handlers in the method. |
||||||
|
* |
||||||
|
* @return The exception handlers in the method. |
||||||
|
*/ |
||||||
|
public Catch[] exceptionHandlers() { |
||||||
|
if (code != null) { |
||||||
|
return code.exceptionHandlers(); |
||||||
|
} |
||||||
|
return new Catch[0]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the exception handlers in the method. |
||||||
|
* |
||||||
|
* @param handlers |
||||||
|
* The exception handlers in the method. |
||||||
|
*/ |
||||||
|
public void setExceptionHandlers(final Catch[] handlers) { |
||||||
|
if (code != null) { |
||||||
|
code.setExceptionHandlers(handlers); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write the method to a class file. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* The data stream of the class file. |
||||||
|
* @exception IOException |
||||||
|
* If an error occurs while writing. |
||||||
|
*/ |
||||||
|
public void write(final DataOutputStream out) throws IOException { |
||||||
|
if (Method.DEBUG) { |
||||||
|
System.out.println("Writing method " + this); |
||||||
|
System.out.println(" Masked Modifiers: " + (modifiers & 0xf000)); |
||||||
|
} |
||||||
|
|
||||||
|
out.writeShort(modifiers); |
||||||
|
|
||||||
|
out.writeShort(name); |
||||||
|
out.writeShort(type); |
||||||
|
|
||||||
|
out.writeShort(attrs.length); |
||||||
|
|
||||||
|
for (int i = 0; i < attrs.length; i++) { |
||||||
|
if (Method.DEBUG) { |
||||||
|
System.out.println(" " + attrs[i]); |
||||||
|
} |
||||||
|
out.writeShort(attrs[i].nameIndex()); |
||||||
|
out.writeInt(attrs[i].length()); |
||||||
|
attrs[i].writeData(out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a string representation of the method. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
String x = ""; |
||||||
|
|
||||||
|
x += " (modifiers"; |
||||||
|
|
||||||
|
if ((modifiers & Modifiers.PUBLIC) != 0) { |
||||||
|
x += " PUBLIC"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.PRIVATE) != 0) { |
||||||
|
x += " PRIVATE"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.PROTECTED) != 0) { |
||||||
|
x += " PROTECTED"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.STATIC) != 0) { |
||||||
|
x += " STATIC"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.FINAL) != 0) { |
||||||
|
x += " FINAL"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.SYNCHRONIZED) != 0) { |
||||||
|
x += " SYNCHRONIZED"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.VOLATILE) != 0) { |
||||||
|
x += " VOLATILE"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.TRANSIENT) != 0) { |
||||||
|
x += " TRANSIENT"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.NATIVE) != 0) { |
||||||
|
x += " NATIVE"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.INTERFACE) != 0) { |
||||||
|
x += " INTERFACE"; |
||||||
|
} |
||||||
|
if ((modifiers & Modifiers.ABSTRACT) != 0) { |
||||||
|
x += " ABSTRACT"; |
||||||
|
} |
||||||
|
x += ")"; |
||||||
|
|
||||||
|
return "(method " + name + " " + type + x |
||||||
|
+ (code != null ? "\n " + code : "") |
||||||
|
+ (exceptions != null ? "\n " + exceptions : "") + ")"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor used for cloning a <code>Method</code> |
||||||
|
*/ |
||||||
|
private Method(final ClassInfo classInfo, final int modifiers, |
||||||
|
final int name, final int type, final Attribute[] attrs) { |
||||||
|
this.classInfo = classInfo; |
||||||
|
this.modifiers = modifiers; |
||||||
|
this.name = name; |
||||||
|
this.type = type; |
||||||
|
|
||||||
|
if (attrs != null) { |
||||||
|
this.attrs = new Attribute[attrs.length]; |
||||||
|
for (int i = 0; i < attrs.length; i++) { |
||||||
|
final Attribute attr = (Attribute) attrs[i].clone(); |
||||||
|
if (attr instanceof Code) { |
||||||
|
this.code = (Code) attr; |
||||||
|
|
||||||
|
} else if (attr instanceof Exceptions) { |
||||||
|
this.exceptions = (Exceptions) attr; |
||||||
|
} |
||||||
|
this.attrs[i] = attr; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Assert.isTrue(code != null, "No Code in attributes"); |
||||||
|
Assert.isTrue(exceptions != null, "No Exceptions in attributes"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a clone of this <tt>MethodInfo</tt> except that its declaring |
||||||
|
* class does not know about it. |
||||||
|
*/ |
||||||
|
public Object clone() { |
||||||
|
return (new Method(this.classInfo, this.modifiers, this.name, |
||||||
|
this.type, this.attrs)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
<html> |
||||||
|
<body> |
||||||
|
|
||||||
|
<p>Allows access to Java classes stored in files on disk. Many of |
||||||
|
these classes implement the interfaces found in the |
||||||
|
EDU.purdue.cs.bloat.reflect package. Classes are loaded from a file |
||||||
|
and things such as constant values, methods, code, debugging |
||||||
|
information, and exceptions are modeled.</p> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue