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