Compare commits
No commits in common. 'master' and 'bcel' have entirely different histories.
@ -1,6 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<classpath> |
|
||||||
<classpathentry kind="src" path="src"/> |
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.3"/> |
|
||||||
<classpathentry kind="output" path="bin"/> |
|
||||||
</classpath> |
|
@ -1 +0,0 @@ |
|||||||
bin |
|
@ -1,17 +0,0 @@ |
|||||||
<?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> |
|
@ -1,12 +0,0 @@ |
|||||||
#Fri May 06 14:29:34 CEST 2011 |
|
||||||
eclipse.preferences.version=1 |
|
||||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled |
|
||||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.1 |
|
||||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve |
|
||||||
org.eclipse.jdt.core.compiler.compliance=1.3 |
|
||||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate |
|
||||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate |
|
||||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate |
|
||||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=ignore |
|
||||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=ignore |
|
||||||
org.eclipse.jdt.core.compiler.source=1.3 |
|
@ -1,24 +0,0 @@ |
|||||||
2009-02-24 |
|
||||||
|
|
||||||
- experimental: add superclass setter to class editor |
|
||||||
|
|
||||||
2007-10-27 |
|
||||||
|
|
||||||
- Ignore/purge JDK5 LVTT attribute in Code constructor |
|
||||||
|
|
||||||
2007-10-25 |
|
||||||
|
|
||||||
- ClassFileLoader#loadClassFromRessource(): String#replaceAll() -> String#replace() |
|
||||||
- Added BloatingClassLoader#getEditorContext() |
|
||||||
- CodeArray#visit_ldc: fix for JDK5 class literal constant references |
|
||||||
- ClassFile: maintain original major/minor class format version information |
|
||||||
|
|
||||||
--- |
|
||||||
|
|
||||||
- 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 |
|
@ -1,74 +0,0 @@ |
|||||||
# 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
|
|
@ -1,2 +0,0 @@ |
|||||||
*.class |
|
||||||
EDU_purdue_cs_bloat_benchmark_Benchmark.h |
|
@ -1,143 +0,0 @@ |
|||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,115 +0,0 @@ |
|||||||
/** |
|
||||||
* 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() { |
|
||||||
} |
|
||||||
} |
|
@ -1,361 +0,0 @@ |
|||||||
/** |
|
||||||
* 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(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,129 +0,0 @@ |
|||||||
# 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) $<
|
|
||||||
|
|
@ -1,101 +0,0 @@ |
|||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,110 +0,0 @@ |
|||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,105 +0,0 @@ |
|||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,76 +0,0 @@ |
|||||||
/** |
|
||||||
* 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"); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,260 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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; |
|
||||||
} |
|
@ -1,10 +0,0 @@ |
|||||||
<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> |
|
@ -1,39 +0,0 @@ |
|||||||
#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(); |
|
||||||
} |
|
@ -1,35 +0,0 @@ |
|||||||
#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); |
|
||||||
} |
|
@ -1 +0,0 @@ |
|||||||
*.class |
|
@ -1,371 +0,0 @@ |
|||||||
/** |
|
||||||
* 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; |
|
||||||
} |
|
||||||
} |
|
@ -1,158 +0,0 @@ |
|||||||
/** |
|
||||||
* 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; |
|
||||||
} |
|
||||||
} |
|
@ -1,387 +0,0 @@ |
|||||||
/** |
|
||||||
* 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
@ -1,79 +0,0 @@ |
|||||||
/** |
|
||||||
* 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; |
|
||||||
} |
|
||||||
} |
|
@ -1,28 +0,0 @@ |
|||||||
# 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 |
|
@ -1,167 +0,0 @@ |
|||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,259 +0,0 @@ |
|||||||
/** |
|
||||||
* 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; |
|
||||||
} |
|
||||||
} |
|
@ -1,384 +0,0 @@ |
|||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,43 +0,0 @@ |
|||||||
# 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 $<
|
|
@ -1 +0,0 @@ |
|||||||
*.class |
|
File diff suppressed because it is too large
Load Diff
@ -1,774 +0,0 @@ |
|||||||
/** |
|
||||||
* 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; |
|
||||||
} |
|
||||||
} |
|
@ -1,24 +0,0 @@ |
|||||||
# 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 |
|
@ -1,657 +0,0 @@ |
|||||||
/** |
|
||||||
* 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()); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1 +0,0 @@ |
|||||||
*.class |
|
@ -1,261 +0,0 @@ |
|||||||
/** |
|
||||||
* 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)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,151 +0,0 @@ |
|||||||
/** |
|
||||||
* 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, parent); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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; |
|
||||||
} |
|
||||||
|
|
||||||
protected EditorContext getEditorContext() { |
|
||||||
return context; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,461 +0,0 @@ |
|||||||
/** |
|
||||||
* 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()); |
|
||||||
} |
|
||||||
} |
|
@ -1,25 +0,0 @@ |
|||||||
# 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 |
|
@ -1,438 +0,0 @@ |
|||||||
/** |
|
||||||
* 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()); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1 +0,0 @@ |
|||||||
*.class |
|
@ -1,727 +0,0 @@ |
|||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,23 +0,0 @@ |
|||||||
# 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 |
|
@ -1,8 +0,0 @@ |
|||||||
<html> |
|
||||||
<body> |
|
||||||
|
|
||||||
<p>Contains a program that decorates a Java classfile with opcodes |
|
||||||
used in a persistent Java virtual machine.</p> |
|
||||||
|
|
||||||
</body> |
|
||||||
</html> |
|
@ -1 +0,0 @@ |
|||||||
*.class |
|
@ -1,514 +0,0 @@ |
|||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,637 +0,0 @@ |
|||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,24 +0,0 @@ |
|||||||
# 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 |
|
@ -1 +0,0 @@ |
|||||||
*.class |
|
@ -1,252 +0,0 @@ |
|||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,23 +0,0 @@ |
|||||||
# 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 |
|
@ -1,8 +0,0 @@ |
|||||||
<html> |
|
||||||
<body> |
|
||||||
|
|
||||||
<p>Contains a program that prints the contents of a Java classfile to |
|
||||||
the console.</p> |
|
||||||
|
|
||||||
</body> |
|
||||||
</html> |
|
@ -1 +0,0 @@ |
|||||||
*.class |
|
@ -1,555 +0,0 @@ |
|||||||
/** |
|
||||||
* 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()); |
|
||||||
} |
|
||||||
|
|
||||||
public void setSuperclass(Type newSuperclass) { |
|
||||||
superclass = newSuperclass; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,459 +0,0 @@ |
|||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,153 +0,0 @@ |
|||||||
/** |
|
||||||
* 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(); |
|
||||||
} |
|
@ -1,35 +0,0 @@ |
|||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
@ -1,562 +0,0 @@ |
|||||||
/** |
|
||||||
* 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 + "]"); |
|
||||||
} |
|
||||||
} |
|
@ -1,75 +0,0 @@ |
|||||||
/** |
|
||||||
* 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
@ -1,459 +0,0 @@ |
|||||||
/** |
|
||||||
* 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) { |
|
||||||
} |
|
||||||
} |
|
@ -1,330 +0,0 @@ |
|||||||
/** |
|
||||||
* 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); |
|
||||||
} |
|
@ -1,141 +0,0 @@ |
|||||||
/** |
|
||||||
* 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; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,134 +0,0 @@ |
|||||||
/** |
|
||||||
* 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; |
|
||||||
} |
|
||||||
} |
|
@ -1,44 +0,0 @@ |
|||||||
# 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 |
|
@ -1,117 +0,0 @@ |
|||||||
/** |
|
||||||
* 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
@ -1,76 +0,0 @@ |
|||||||
/** |
|
||||||
* 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; |
|
||||||
} |
|
||||||
} |
|
@ -1,83 +0,0 @@ |
|||||||
/** |
|
||||||
* 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
@ -1,459 +0,0 @@ |
|||||||
/** |
|
||||||
* 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()); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,232 +0,0 @@ |
|||||||
/** |
|
||||||
* 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"; |
|
||||||
} |
|
||||||
} |
|
@ -1,106 +0,0 @@ |
|||||||
/** |
|
||||||
* 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
@ -1,120 +0,0 @@ |
|||||||
/** |
|
||||||
* 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]);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
} |
|
@ -1,53 +0,0 @@ |
|||||||
/** |
|
||||||
* 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)); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1 +0,0 @@ |
|||||||
*.class |
|
@ -1,89 +0,0 @@ |
|||||||
/** |
|
||||||
* 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() + ")"); |
|
||||||
} |
|
||||||
} |
|
@ -1,980 +0,0 @@ |
|||||||
/** |
|
||||||
* 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
|
|
||||||
|
|
||||||
private int major = 45; |
|
||||||
|
|
||||||
private int minor = 3; |
|
||||||
|
|
||||||
/** |
|
||||||
* 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(major); |
|
||||||
out.writeShort(minor); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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."); |
|
||||||
} |
|
||||||
|
|
||||||
this.major = in.readUnsignedShort(); // major
|
|
||||||
this.minor = 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() + ")"; |
|
||||||
} |
|
||||||
} |
|
@ -1,501 +0,0 @@ |
|||||||
/** |
|
||||||
* 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 java.net.URL; |
|
||||||
|
|
||||||
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; |
|
||||||
|
|
||||||
private ClassSource _classSource; |
|
||||||
|
|
||||||
public ClassFileLoader(ClassSource classSource) { |
|
||||||
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; |
|
||||||
_classSource = classSource; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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() { |
|
||||||
this(new DefaultClassSource()); |
|
||||||
} |
|
||||||
|
|
||||||
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); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Thhis method tries to load a Class by its ressource. |
|
||||||
* @param name the Name of the Class |
|
||||||
* @return the ClassInfo |
|
||||||
*/ |
|
||||||
private ClassInfo loadClassFromRessource(String name){ |
|
||||||
name = name.replace('/','.'); |
|
||||||
try { |
|
||||||
Class clazz = _classSource.loadClass(name); |
|
||||||
int i = name.lastIndexOf('.'); |
|
||||||
if (i >= 0 && i < name.length()){ |
|
||||||
name = name.substring(i+1); |
|
||||||
} |
|
||||||
URL url = clazz.getResource(name + ".class"); |
|
||||||
if (url != null){ |
|
||||||
return loadClassFromStream(new File(url.getFile()), url.openStream()); |
|
||||||
} |
|
||||||
} catch (Exception e) {} |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if ((file = loadClassFromRessource(name)) != null){ |
|
||||||
addToCache(file); |
|
||||||
return file; |
|
||||||
} |
|
||||||
|
|
||||||
// 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); |
|
||||||
} |
|
||||||
|
|
||||||
addToCache(file); |
|
||||||
|
|
||||||
return file; |
|
||||||
} |
|
||||||
|
|
||||||
private void addToCache(ClassInfo file) { |
|
||||||
// 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); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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
|
|
||||||
} |
|
||||||
} |
|
@ -1,5 +0,0 @@ |
|||||||
package EDU.purdue.cs.bloat.file; |
|
||||||
|
|
||||||
public interface ClassSource { |
|
||||||
Class loadClass(String name) throws ClassNotFoundException ; |
|
||||||
} |
|
@ -1,451 +0,0 @@ |
|||||||
/** |
|
||||||
* 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.*; |
|
||||||
|
|
||||||
/** |
|
||||||
* 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(); |
|
||||||
|
|
||||||
List attrList = new ArrayList(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); |
|
||||||
attrList.add(lineNumbers); |
|
||||||
} |
|
||||||
else if ("LocalVariableTable".equals(name.value())) { |
|
||||||
locals = new LocalVariableTable(in, nameIndex, length); |
|
||||||
attrList.add(locals); |
|
||||||
} |
|
||||||
else if ("LocalVariableTypeTable".equals(name.value())) { |
|
||||||
// just read and ignore
|
|
||||||
new GenericAttribute(in, nameIndex, length); |
|
||||||
} |
|
||||||
else { |
|
||||||
attrList.add(new GenericAttribute(in, nameIndex, length)); |
|
||||||
} |
|
||||||
} |
|
||||||
else { |
|
||||||
attrList.add(new GenericAttribute(in, nameIndex, length)); |
|
||||||
} |
|
||||||
} |
|
||||||
attrs = (Attribute[]) attrList.toArray(new Attribute[attrList.size()]); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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 |
|
||||||
+ ")"; |
|
||||||
} |
|
||||||
} |
|
@ -1,115 +0,0 @@ |
|||||||
/** |
|
||||||
* 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 + ")"; |
|
||||||
} |
|
||||||
} |
|
@ -1,9 +0,0 @@ |
|||||||
package EDU.purdue.cs.bloat.file; |
|
||||||
|
|
||||||
public class DefaultClassSource implements ClassSource { |
|
||||||
|
|
||||||
public Class loadClass(String name) throws ClassNotFoundException { |
|
||||||
return Class.forName(name); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,140 +0,0 @@ |
|||||||
/** |
|
||||||
* 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)"; |
|
||||||
} |
|
||||||
} |
|
@ -1,293 +0,0 @@ |
|||||||
/** |
|
||||||
* 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 + ")"; |
|
||||||
} |
|
||||||
} |
|
@ -1,82 +0,0 @@ |
|||||||
/** |
|
||||||
* 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)); |
|
||||||
} |
|
||||||
} |
|
@ -1,260 +0,0 @@ |
|||||||
/** |
|
||||||
* 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(); |
|
||||||
} |
|
||||||
} |
|
@ -1,142 +0,0 @@ |
|||||||
/** |
|
||||||
* 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)); |
|
||||||
} |
|
||||||
} |
|
@ -1,147 +0,0 @@ |
|||||||
/** |
|
||||||
* 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)); |
|
||||||
} |
|
||||||
} |
|
@ -1,33 +0,0 @@ |
|||||||
# 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 |
|
@ -1,484 +0,0 @@ |
|||||||
/** |
|
||||||
* 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)); |
|
||||||
} |
|
||||||
} |
|
@ -1,11 +0,0 @@ |
|||||||
<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> |
|
@ -1 +0,0 @@ |
|||||||
*.class |
|
@ -1,763 +0,0 @@ |
|||||||
/** |
|
||||||
* 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.inline; |
|
||||||
|
|
||||||
import java.io.*; |
|
||||||
import java.util.*; |
|
||||||
|
|
||||||
import EDU.purdue.cs.bloat.editor.*; |
|
||||||
import EDU.purdue.cs.bloat.reflect.*; |
|
||||||
import EDU.purdue.cs.bloat.util.*; |
|
||||||
|
|
||||||
/** |
|
||||||
* Grants access to certain information about a Java program. At least one root |
|
||||||
* method must be specified. From these root methods, the call graph and |
|
||||||
* information such as the classes that are instantiated during the Java program |
|
||||||
* is computed. |
|
||||||
* |
|
||||||
* <p> |
|
||||||
* |
|
||||||
* The construction of the call graph is in the spirit of the "Program |
|
||||||
* Virtual-call Graph" presented in [Bacon97]. However, certain changes have |
|
||||||
* been made to tailor it to BLOAT and Java and to make the overall |
|
||||||
* representation smaller. |
|
||||||
* |
|
||||||
* <p> |
|
||||||
* |
|
||||||
* Rapid type analysis is integrated into the construction of the call graph. A |
|
||||||
* virtual method is not examined until we know that its declaring class has |
|
||||||
* been instantiated. |
|
||||||
* |
|
||||||
* <p> |
|
||||||
* |
|
||||||
* Some classes are created internally by the VM and are missed by our analysis. |
|
||||||
* So, we maintain a set of "pre-live" classes. We consider all of their |
|
||||||
* constructors to be live. |
|
||||||
*/ |
|
||||||
public class CallGraph { |
|
||||||
public static boolean DEBUG = false; |
|
||||||
|
|
||||||
private static Set preLive; // "Pre-live" classes
|
|
||||||
|
|
||||||
public static boolean USEPRELIVE = true; |
|
||||||
|
|
||||||
public static boolean USE1_2 = true; |
|
||||||
|
|
||||||
private Set roots; // Root methods (MethodRefs)
|
|
||||||
|
|
||||||
private Map calls; // Maps methods to the methods they
|
|
||||||
|
|
||||||
// call (virtual calls are not resolved)
|
|
||||||
private Set liveClasses; // Classes (Types) that have been instantiated
|
|
||||||
|
|
||||||
private Map resolvesTo; // Maps methods to the methods they resolve to
|
|
||||||
|
|
||||||
private Map blocked; // Maps types to methods blocked on those types
|
|
||||||
|
|
||||||
List worklist; // Methods to process
|
|
||||||
|
|
||||||
Set liveMethods; // Methods that may be executed
|
|
||||||
|
|
||||||
InlineContext context; |
|
||||||
|
|
||||||
private ClassHierarchy hier; |
|
||||||
|
|
||||||
static void db(final String s) { |
|
||||||
if (CallGraph.DEBUG) { |
|
||||||
System.out.println(s); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Initialize the set of classes that are "pre-live" |
|
||||||
*/ |
|
||||||
private static void init() { |
|
||||||
// We can't do this in the static initializer because USE1_2 might
|
|
||||||
// not have the desired value.
|
|
||||||
|
|
||||||
CallGraph.preLive = new HashSet(); |
|
||||||
|
|
||||||
CallGraph.preLive.add("java.lang.Boolean"); |
|
||||||
CallGraph.preLive.add("java.lang.Class"); |
|
||||||
CallGraph.preLive.add("java.lang.ClassLoader"); |
|
||||||
CallGraph.preLive.add("java.lang.Compiler"); |
|
||||||
CallGraph.preLive.add("java.lang.Integer"); |
|
||||||
CallGraph.preLive.add("java.lang.SecurityManager"); |
|
||||||
CallGraph.preLive.add("java.lang.String"); |
|
||||||
CallGraph.preLive.add("java.lang.StringBuffer"); |
|
||||||
CallGraph.preLive.add("java.lang.System"); |
|
||||||
CallGraph.preLive.add("java.lang.StackOverflowError"); |
|
||||||
CallGraph.preLive.add("java.lang.Thread"); |
|
||||||
CallGraph.preLive.add("java.lang.ThreadGroup"); |
|
||||||
|
|
||||||
CallGraph.preLive.add("java.io.BufferedInputStream"); |
|
||||||
CallGraph.preLive.add("java.io.BufferedReader"); |
|
||||||
CallGraph.preLive.add("java.io.BufferedOutputStream"); |
|
||||||
CallGraph.preLive.add("java.io.BufferedWriter"); |
|
||||||
CallGraph.preLive.add("java.io.File"); |
|
||||||
CallGraph.preLive.add("java.io.FileDescriptor"); |
|
||||||
CallGraph.preLive.add("java.io.InputStreamReader"); |
|
||||||
CallGraph.preLive.add("java.io.ObjectStreamClass"); |
|
||||||
CallGraph.preLive.add("java.io.OutputStreamWriter"); |
|
||||||
CallGraph.preLive.add("java.io.PrintStream"); |
|
||||||
CallGraph.preLive.add("java.io.PrintWriter"); |
|
||||||
|
|
||||||
CallGraph.preLive.add("java.net.URL"); |
|
||||||
|
|
||||||
CallGraph.preLive.add("java.security.Provider"); |
|
||||||
CallGraph.preLive.add("java.security.Security"); |
|
||||||
|
|
||||||
CallGraph.preLive.add("java.util.Hashtable"); |
|
||||||
CallGraph.preLive.add("java.util.ListResourceBundle"); |
|
||||||
CallGraph.preLive.add("java.util.Locale"); |
|
||||||
CallGraph.preLive.add("java.util.Properties"); |
|
||||||
CallGraph.preLive.add("java.util.Stack"); |
|
||||||
CallGraph.preLive.add("java.util.Vector"); |
|
||||||
|
|
||||||
CallGraph.preLive.add("java.util.zip.ZipFile"); |
|
||||||
|
|
||||||
// Some pre-live classes are only available on JDK1.2.
|
|
||||||
if (CallGraph.USE1_2) { |
|
||||||
CallGraph.preLive.add("java.lang.Package"); |
|
||||||
|
|
||||||
CallGraph.preLive.add("java.lang.ref.Finalizer"); |
|
||||||
CallGraph.preLive.add("java.lang.ref.ReferenceQueue"); |
|
||||||
|
|
||||||
CallGraph.preLive.add("java.io.FilePermission"); |
|
||||||
CallGraph.preLive.add("java.io.UnixFileSystem"); |
|
||||||
|
|
||||||
CallGraph.preLive.add("java.net.URLClassLoader"); |
|
||||||
|
|
||||||
CallGraph.preLive.add("java.security.SecureClassLoader"); |
|
||||||
CallGraph.preLive.add("java.security.AccessController"); |
|
||||||
|
|
||||||
CallGraph.preLive.add("java.text.resources.LocaleElements"); |
|
||||||
CallGraph.preLive.add("java.text.resources.LocaleElements_en"); |
|
||||||
|
|
||||||
CallGraph.preLive.add("java.util.HashMap"); |
|
||||||
|
|
||||||
CallGraph.preLive.add("java.util.jar.JarFile"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Adds (the name of) a class to the set of classes that are considered to |
|
||||||
* be "pre-live" |
|
||||||
*/ |
|
||||||
public static void addPreLive(final String name) { |
|
||||||
if (CallGraph.preLive == null) { |
|
||||||
CallGraph.init(); |
|
||||||
} |
|
||||||
CallGraph.preLive.add(name); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Removes a class from the set of "pre-live" classes |
|
||||||
* |
|
||||||
* @return <tt>true</tt> if the class was "pre-live" |
|
||||||
*/ |
|
||||||
public static boolean removePreLive(final String name) { |
|
||||||
if (CallGraph.preLive == null) { |
|
||||||
CallGraph.init(); |
|
||||||
} |
|
||||||
return (CallGraph.preLive.remove(name)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Constructor. |
|
||||||
* |
|
||||||
* @param context |
|
||||||
* <Tt>InlineContext</tt> used to examine classes and methods. |
|
||||||
* |
|
||||||
* @param roots |
|
||||||
* The methods (represented as <tt>MemberRef</tt>s) considered |
|
||||||
* to the roots (that is, the "main" methods) of the call graph. |
|
||||||
* Presumably, only static methods or constructors can be root |
|
||||||
* methods. |
|
||||||
*/ |
|
||||||
public CallGraph(final InlineContext context, final Set roots) { |
|
||||||
Assert.isTrue(roots != null, "A call graph must have roots"); |
|
||||||
Assert.isTrue(roots.size() > 0, "A call graph must have roots"); |
|
||||||
|
|
||||||
if (CallGraph.preLive == null) { |
|
||||||
CallGraph.init(); |
|
||||||
} |
|
||||||
|
|
||||||
this.context = context; |
|
||||||
this.hier = context.getHierarchy(); |
|
||||||
this.roots = roots; |
|
||||||
|
|
||||||
this.liveClasses = new HashSet(); |
|
||||||
this.resolvesTo = new HashMap(); |
|
||||||
this.calls = new HashMap(); |
|
||||||
this.blocked = new HashMap(); |
|
||||||
this.worklist = new LinkedList(this.roots); |
|
||||||
this.liveMethods = new HashSet(); |
|
||||||
|
|
||||||
// To save space, make one InstructionVisitor and use it on every
|
|
||||||
// Instruction.
|
|
||||||
final CallVisitor visitor = new CallVisitor(this); |
|
||||||
|
|
||||||
CallGraph.db("Adding pre-live classes"); |
|
||||||
doPreLive(); |
|
||||||
|
|
||||||
CallGraph.db("Constructing call graph"); |
|
||||||
|
|
||||||
// Examine each method in the worklist. At each constructor
|
|
||||||
// invocation make note of the type that was created. At each
|
|
||||||
// method call determine all possible methods that it can resolve
|
|
||||||
// to. Add the methods of classes that have been instantiated to
|
|
||||||
// the worklist.
|
|
||||||
while (!worklist.isEmpty()) { |
|
||||||
final MemberRef caller = (MemberRef) worklist.remove(0); |
|
||||||
|
|
||||||
if (liveMethods.contains(caller)) { |
|
||||||
// We've already handled this method
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
MethodEditor callerMethod = null; |
|
||||||
try { |
|
||||||
callerMethod = context.editMethod(caller); |
|
||||||
|
|
||||||
} catch (final NoSuchMethodException ex1) { |
|
||||||
System.err.println("** Could not find method: " + caller); |
|
||||||
ex1.printStackTrace(System.err); |
|
||||||
System.exit(1); |
|
||||||
} |
|
||||||
|
|
||||||
// If the method is abstract or native, we can't do anything
|
|
||||||
// with it.
|
|
||||||
if (callerMethod.isAbstract()) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
liveMethods.add(caller); |
|
||||||
|
|
||||||
if (callerMethod.isNative()) { |
|
||||||
// We still want native methods to be live
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
CallGraph.db("\n Examining method " + caller); |
|
||||||
|
|
||||||
final Set callees = new HashSet(); // Methods called by caller
|
|
||||||
calls.put(caller, callees); |
|
||||||
|
|
||||||
// If the method is static or is a constructor, the classes
|
|
||||||
// static initializer method must have been called. Make note
|
|
||||||
// of this.
|
|
||||||
if (callerMethod.isStatic() || callerMethod.isConstructor()) { |
|
||||||
addClinit(callerMethod.declaringClass().type()); |
|
||||||
} |
|
||||||
|
|
||||||
// Examine the instructions in the caller method.
|
|
||||||
final Iterator code = callerMethod.code().iterator(); |
|
||||||
visitor.setCaller(callerMethod); |
|
||||||
while (code.hasNext()) { |
|
||||||
final Object o = code.next(); |
|
||||||
if (o instanceof Instruction) { |
|
||||||
final Instruction inst = (Instruction) o; |
|
||||||
inst.visit(visitor); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// We're done constructing the call graph. Try to free up some
|
|
||||||
// memory.
|
|
||||||
blocked = null; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Helper method to add the static initializers and all constructors of the |
|
||||||
* pre-live classes to the worklist, etc. |
|
||||||
*/ |
|
||||||
private void doPreLive() { |
|
||||||
if (!CallGraph.USEPRELIVE) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
CallGraph.db("Making constructors of pre-live classes live"); |
|
||||||
|
|
||||||
final Iterator iter = CallGraph.preLive.iterator(); |
|
||||||
while (iter.hasNext()) { |
|
||||||
String name = (String) iter.next(); |
|
||||||
CallGraph.db(" " + name + " is pre-live"); |
|
||||||
name = name.replace('.', '/'); |
|
||||||
|
|
||||||
ClassEditor ce = null; |
|
||||||
try { |
|
||||||
ce = context.editClass(name); |
|
||||||
|
|
||||||
} catch (final ClassNotFoundException ex1) { |
|
||||||
System.err.println("** Cannot find pre-live class: " + name); |
|
||||||
ex1.printStackTrace(System.err); |
|
||||||
System.exit(1); |
|
||||||
} |
|
||||||
|
|
||||||
// Make class and static initializer live
|
|
||||||
liveClasses.add(ce.type()); |
|
||||||
addClinit(ce.type()); |
|
||||||
|
|
||||||
// Make all constructors live
|
|
||||||
final MethodInfo[] methods = ce.methods(); |
|
||||||
for (int i = 0; i < methods.length; i++) { |
|
||||||
final MethodEditor method = context.editMethod(methods[i]); |
|
||||||
if (method.name().equals("<init>")) { |
|
||||||
CallGraph.db(" " + method); |
|
||||||
worklist.add(method.memberRef()); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Adds the static initializer for a given <tt>Type</tt> to the worklist. |
|
||||||
*/ |
|
||||||
void addClinit(final Type type) { |
|
||||||
try { |
|
||||||
final ClassEditor ce = context.editClass(type); |
|
||||||
|
|
||||||
final MethodInfo[] methods = ce.methods(); |
|
||||||
for (int i = 0; i < methods.length; i++) { |
|
||||||
final MethodEditor clinit = context.editMethod(methods[i]); |
|
||||||
if (clinit.name().equals("<clinit>")) { |
|
||||||
worklist.add(clinit.memberRef()); |
|
||||||
context.release(clinit.methodInfo()); |
|
||||||
break; |
|
||||||
} |
|
||||||
context.release(clinit.methodInfo()); |
|
||||||
} |
|
||||||
context.release(ce.classInfo()); |
|
||||||
|
|
||||||
} catch (final ClassNotFoundException ex1) { |
|
||||||
System.err.println("** Could not find class for " + type); |
|
||||||
ex1.printStackTrace(System.err); |
|
||||||
System.exit(1); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Handles a virtual call. Determines all possible methods the call could |
|
||||||
* resolve to. Adds the method whose declaring classes are live to the |
|
||||||
* worklist. Blocks the rest on their declaring types. |
|
||||||
*/ |
|
||||||
void doVirtual(final MethodEditor caller, final MemberRef callee) { |
|
||||||
// Figure out which methods the callee can resolve to.
|
|
||||||
final Iterator resolvesToWith = hier.resolvesToWith(callee).iterator(); |
|
||||||
|
|
||||||
while (resolvesToWith.hasNext()) { |
|
||||||
final ClassHierarchy.ResolvesToWith rtw = (ClassHierarchy.ResolvesToWith) resolvesToWith |
|
||||||
.next(); |
|
||||||
CallGraph.db(" resolves to " + rtw.method); |
|
||||||
|
|
||||||
// Add all possible non-abstract methods to the call graph.
|
|
||||||
// This way, when a blocked method becomes unblocked, it will
|
|
||||||
// still be in the call graph.
|
|
||||||
addCall(caller, rtw.method); |
|
||||||
|
|
||||||
Iterator rTypes = rtw.rTypes.iterator(); |
|
||||||
boolean isLive = false; // Is one of the rTypes live?
|
|
||||||
while (rTypes.hasNext()) { |
|
||||||
final Type rType = (Type) rTypes.next(); |
|
||||||
if (liveClasses.contains(rType)) { |
|
||||||
isLive = true; |
|
||||||
CallGraph.db(" Method " + rtw.method + " is live"); |
|
||||||
worklist.add(rtw.method); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (!isLive) { |
|
||||||
// If none of the receiver types is live, then the method is
|
|
||||||
// blocked on all possible receiver types.
|
|
||||||
rTypes = rtw.rTypes.iterator(); |
|
||||||
final StringBuffer sb = new StringBuffer(); |
|
||||||
while (rTypes.hasNext()) { |
|
||||||
final Type rType = (Type) rTypes.next(); |
|
||||||
Set blockedMethods = (Set) blocked.get(rType); |
|
||||||
if (blockedMethods == null) { |
|
||||||
blockedMethods = new HashSet(); |
|
||||||
blocked.put(rType, blockedMethods); |
|
||||||
} |
|
||||||
blockedMethods.add(rtw.method); |
|
||||||
sb.append(rType.toString()); |
|
||||||
if (rTypes.hasNext()) { |
|
||||||
sb.append(','); |
|
||||||
} |
|
||||||
} |
|
||||||
CallGraph.db(" Blocked " + rtw.method + " on " + sb); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Makes note of one method calling another. This does not make the method |
|
||||||
* live. |
|
||||||
*/ |
|
||||||
void addCall(final MethodEditor callerMethod, final MemberRef callee) { |
|
||||||
// Just maintain the calls mapping
|
|
||||||
final MemberRef caller = callerMethod.memberRef(); |
|
||||||
Set callees = (Set) this.calls.get(caller); |
|
||||||
if (callees == null) { |
|
||||||
callees = new HashSet(); |
|
||||||
this.calls.put(caller, callees); |
|
||||||
} |
|
||||||
callees.add(callee); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Marks a <tt>Type</tt> as being lives. It also unblocks any methods that |
|
||||||
* were blocked on the type. |
|
||||||
*/ |
|
||||||
void makeLive(final Type type) { |
|
||||||
if (this.liveClasses.contains(type)) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
// Make type live and unblock all methods blocked on it
|
|
||||||
CallGraph.db(" Making " + type + " live"); |
|
||||||
liveClasses.add(type); |
|
||||||
final Set blockedMethods = (Set) blocked.remove(type); |
|
||||||
if (blockedMethods != null) { |
|
||||||
final Iterator iter = blockedMethods.iterator(); |
|
||||||
while (iter.hasNext()) { |
|
||||||
final MemberRef method = (MemberRef) iter.next(); |
|
||||||
CallGraph.db(" Unblocking " + method); |
|
||||||
worklist.add(method); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the methods (<tt>MemberRef</tt>s) to which a given method |
|
||||||
* could resolve. Only live methods are taken into account. The methods are |
|
||||||
* sorted such that overriding methods appear before overriden methods. |
|
||||||
*/ |
|
||||||
public Set resolvesTo(final MemberRef method) { |
|
||||||
TreeSet resolvesTo = (TreeSet) this.resolvesTo.get(method); |
|
||||||
|
|
||||||
if (resolvesTo == null) { |
|
||||||
resolvesTo = new TreeSet(new MemberRefComparator(context)); |
|
||||||
this.resolvesTo.put(method, resolvesTo); |
|
||||||
|
|
||||||
final Set liveMethods = this.liveMethods(); |
|
||||||
final Iterator rtws = hier.resolvesToWith(method).iterator(); |
|
||||||
while (rtws.hasNext()) { |
|
||||||
final ClassHierarchy.ResolvesToWith rtw = (ClassHierarchy.ResolvesToWith) rtws |
|
||||||
.next(); |
|
||||||
if (liveMethods.contains(rtw.method)) { |
|
||||||
resolvesTo.add(rtw.method); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Return a clone so that the set may safely be modified
|
|
||||||
return ((Set) resolvesTo.clone()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the methods (<tt>MemberRef</tt>s) to which a given method |
|
||||||
* could resolve given that the receiver is in a certain set of types. Only |
|
||||||
* live methods are taken into account. The methods are sorted such that |
|
||||||
* overriding methods appear before overriden methods. |
|
||||||
*/ |
|
||||||
public Set resolvesTo(final MemberRef method, final Set rTypes) { |
|
||||||
if (rTypes.isEmpty()) { |
|
||||||
return (resolvesTo(method)); |
|
||||||
} |
|
||||||
|
|
||||||
// Since we're only dealing with a subset of types, don't bother
|
|
||||||
// with the caching stuff.
|
|
||||||
final TreeSet resolvesTo = new TreeSet(new MemberRefComparator(context)); |
|
||||||
|
|
||||||
final Set liveMethods = this.liveMethods(); |
|
||||||
final Iterator rtws = hier.resolvesToWith(method).iterator(); |
|
||||||
while (rtws.hasNext()) { |
|
||||||
final ClassHierarchy.ResolvesToWith rtw = (ClassHierarchy.ResolvesToWith) rtws |
|
||||||
.next(); |
|
||||||
if (liveMethods.contains(rtw.method)) { |
|
||||||
final HashSet clone = (HashSet) rtw.rTypes.clone(); |
|
||||||
|
|
||||||
clone.retainAll(rTypes); |
|
||||||
if (!clone.isEmpty()) { |
|
||||||
// Only keep method that have at least one possible
|
|
||||||
// receiver type in rTypes
|
|
||||||
resolvesTo.add(rtw.method); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Return a clone so that the set may safely be modified
|
|
||||||
return ((Set) resolvesTo.clone()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the set of methods (<tt>MemberRef</tt>s) that the |
|
||||||
* construction algorithm has deemed to be live. |
|
||||||
*/ |
|
||||||
public Set liveMethods() { |
|
||||||
// Not all of the methods in the calls mapping are necessarily
|
|
||||||
// live. So, we have to maintain a separate set.
|
|
||||||
return (this.liveMethods); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the root methods (<tt>MemberRef</tt>s) of the call graph. |
|
||||||
*/ |
|
||||||
public Set roots() { |
|
||||||
return (this.roots); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the set of classes (<tt>Type</tt>s) that are instantiated in |
|
||||||
* the program. |
|
||||||
*/ |
|
||||||
public Set liveClasses() { |
|
||||||
return (this.liveClasses); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Prints a textual prepresentation of the <tt>CallGraph</tt> to a |
|
||||||
* <tt>PrintWriter</tt>. |
|
||||||
* |
|
||||||
* @param out |
|
||||||
* To where we print |
|
||||||
* @param printLeaves |
|
||||||
* If <tt>true</tt>, leaf methods (methods that do not call |
|
||||||
* any other methods) are printed |
|
||||||
*/ |
|
||||||
public void print(final PrintWriter out, boolean printLeaves) { |
|
||||||
|
|
||||||
final Iterator callers = calls.keySet().iterator(); |
|
||||||
while (callers.hasNext()) { |
|
||||||
final MemberRef caller = (MemberRef) callers.next(); |
|
||||||
|
|
||||||
final Iterator callees = ((Set) calls.get(caller)).iterator(); |
|
||||||
|
|
||||||
if (!printLeaves && !callees.hasNext()) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
out.print(caller.declaringClass() + "." + caller.name() |
|
||||||
+ caller.type()); |
|
||||||
if (roots.contains(caller)) { |
|
||||||
out.print(" (root)"); |
|
||||||
} |
|
||||||
out.println(""); |
|
||||||
|
|
||||||
while (callees.hasNext()) { |
|
||||||
final MemberRef callee = (MemberRef) callees.next(); |
|
||||||
|
|
||||||
// Only print live methods
|
|
||||||
if (!calls.containsKey(callee)) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
out.println(" " + callee.declaringClass() + "." |
|
||||||
+ callee.name() + callee.type()); |
|
||||||
} |
|
||||||
|
|
||||||
out.println(""); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Prints a summary of the call graph. Including the classes that are live |
|
||||||
* and which methods are blocked. |
|
||||||
*/ |
|
||||||
public void printSummary(final PrintWriter out) { |
|
||||||
out.println("Instantiated classes:"); |
|
||||||
final Iterator instantiated = this.liveClasses.iterator(); |
|
||||||
while (instantiated.hasNext()) { |
|
||||||
final Type type = (Type) instantiated.next(); |
|
||||||
out.println(" " + type.toString()); |
|
||||||
} |
|
||||||
|
|
||||||
out.println("\nBlocked methods:"); |
|
||||||
if (blocked != null) { |
|
||||||
final Iterator types = blocked.keySet().iterator(); |
|
||||||
while (types.hasNext()) { |
|
||||||
final Type type = (Type) types.next(); |
|
||||||
out.println(" " + type); |
|
||||||
|
|
||||||
final Set set = (Set) blocked.get(type); |
|
||||||
if (set != null) { |
|
||||||
final Iterator methods = set.iterator(); |
|
||||||
while (methods.hasNext()) { |
|
||||||
final MemberRef method = (MemberRef) methods.next(); |
|
||||||
out.println(" " + method); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
out.println("\nCall graph:"); |
|
||||||
this.print(out, false); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* <tt>CallVisitor</tt> examines the instructions in a method and notices what |
|
||||||
* methods are called and which classes are created. |
|
||||||
*/ |
|
||||||
class CallVisitor extends InstructionAdapter { |
|
||||||
MethodEditor caller; |
|
||||||
|
|
||||||
CallGraph cg; |
|
||||||
|
|
||||||
boolean firstSpecial; // Are we dealing with the first invokespecial?
|
|
||||||
|
|
||||||
private static void db(final String s) { |
|
||||||
CallGraph.db(s); |
|
||||||
} |
|
||||||
|
|
||||||
public CallVisitor(final CallGraph cg) { |
|
||||||
this.cg = cg; |
|
||||||
} |
|
||||||
|
|
||||||
public void setCaller(final MethodEditor caller) { |
|
||||||
this.caller = caller; |
|
||||||
if (caller.isConstructor()) { |
|
||||||
this.firstSpecial = true; |
|
||||||
} else { |
|
||||||
this.firstSpecial = false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void visit_invokevirtual(final Instruction inst) { |
|
||||||
CallVisitor.db("\n Visiting Call: " + inst); |
|
||||||
|
|
||||||
this.firstSpecial = false; |
|
||||||
|
|
||||||
// Call doVirtual to determine which methods this call may resolve
|
|
||||||
// to are live.
|
|
||||||
final MemberRef callee = (MemberRef) inst.operand(); |
|
||||||
cg.doVirtual(caller, callee); |
|
||||||
} |
|
||||||
|
|
||||||
public void visit_invokeinterface(final Instruction inst) { |
|
||||||
CallVisitor.db("\n Visiting Call: " + inst); |
|
||||||
|
|
||||||
this.firstSpecial = false; |
|
||||||
|
|
||||||
// Pretty much the same as invokevirtual
|
|
||||||
final MemberRef callee = (MemberRef) inst.operand(); |
|
||||||
cg.doVirtual(caller, callee); |
|
||||||
} |
|
||||||
|
|
||||||
public void visit_invokestatic(final Instruction inst) { |
|
||||||
CallVisitor.db("\n Visiting call: " + inst); |
|
||||||
|
|
||||||
this.firstSpecial = false; |
|
||||||
|
|
||||||
// There's not a lot to do with static methods since there is no
|
|
||||||
// dynamic dispatch.
|
|
||||||
final MemberRef callee = (MemberRef) inst.operand(); |
|
||||||
cg.addCall(caller, callee); |
|
||||||
cg.worklist.add(callee); |
|
||||||
} |
|
||||||
|
|
||||||
public void visit_invokespecial(final Instruction inst) { |
|
||||||
CallVisitor.db("\n Visiting call: " + inst); |
|
||||||
|
|
||||||
// Recall that invokespecial is used to call constructors, private
|
|
||||||
// methods, and "super" methods. There is no dynamic dispatch for
|
|
||||||
// special methods.
|
|
||||||
final MemberRef callee = (MemberRef) inst.operand(); |
|
||||||
|
|
||||||
MethodEditor calleeMethod = null; |
|
||||||
|
|
||||||
try { |
|
||||||
calleeMethod = cg.context.editMethod(callee); |
|
||||||
|
|
||||||
} catch (final NoSuchMethodException ex1) { |
|
||||||
System.err.println("** Couldn't find method: " + callee); |
|
||||||
System.exit(1); |
|
||||||
} |
|
||||||
|
|
||||||
if (calleeMethod.isSynchronized() || calleeMethod.isNative()) { |
|
||||||
// Calls to synchronized and native methods are virtual
|
|
||||||
cg.doVirtual(caller, callee); |
|
||||||
|
|
||||||
} else { |
|
||||||
// Calls to everything else (superclass methods, private
|
|
||||||
// methods, etc.) do not involve a dynamic dispatch and can be
|
|
||||||
// treated like a static method.
|
|
||||||
cg.addCall(caller, callee); |
|
||||||
cg.worklist.add(callee); |
|
||||||
} |
|
||||||
|
|
||||||
cg.context.release(calleeMethod.methodInfo()); |
|
||||||
} |
|
||||||
|
|
||||||
public void visit_getstatic(final Instruction inst) { |
|
||||||
// Referencing a static fields implies that its class's static
|
|
||||||
// initializer has been invoked.
|
|
||||||
CallVisitor.db("\n Referencing static field " + inst); |
|
||||||
final MemberRef field = (MemberRef) inst.operand(); |
|
||||||
cg.addClinit(field.declaringClass()); |
|
||||||
} |
|
||||||
|
|
||||||
public void visit_putstatic(final Instruction inst) { |
|
||||||
// Referencing a static field implies that its class's static
|
|
||||||
// initializer has been invoked.
|
|
||||||
CallVisitor.db("\n Referencing static field " + inst); |
|
||||||
final MemberRef field = (MemberRef) inst.operand(); |
|
||||||
cg.addClinit(field.declaringClass()); |
|
||||||
} |
|
||||||
|
|
||||||
public void visit_new(final Instruction inst) { |
|
||||||
// The new instruction instantiates a type and thus makes it live.
|
|
||||||
final Type type = (Type) inst.operand(); |
|
||||||
cg.makeLive(type); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Compares <tt>MemberRef</tt>s such that overriding methods are less than |
|
||||||
* overridden methods. |
|
||||||
*/ |
|
||||||
class MemberRefComparator implements Comparator { |
|
||||||
TypeComparator c; |
|
||||||
|
|
||||||
public MemberRefComparator(final InlineContext context) { |
|
||||||
c = new TypeComparator(context); |
|
||||||
} |
|
||||||
|
|
||||||
public int compare(final Object o1, final Object o2) { |
|
||||||
Assert.isTrue(o1 instanceof MemberRef, o1 + " is not a MemberRef"); |
|
||||||
Assert.isTrue(o2 instanceof MemberRef, o2 + " is not a MemberRef"); |
|
||||||
|
|
||||||
final MemberRef ref1 = (MemberRef) o1; |
|
||||||
final MemberRef ref2 = (MemberRef) o2; |
|
||||||
|
|
||||||
final Type type1 = ref1.declaringClass(); |
|
||||||
final Type type2 = ref2.declaringClass(); |
|
||||||
|
|
||||||
return (c.compare(type1, type2)); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean compareTo(final Object other) { |
|
||||||
return (other instanceof MemberRefComparator); |
|
||||||
} |
|
||||||
} |
|
@ -1,733 +0,0 @@ |
|||||||
/** |
|
||||||
* 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.inline; |
|
||||||
|
|
||||||
import java.util.*; |
|
||||||
|
|
||||||
import EDU.purdue.cs.bloat.editor.*; |
|
||||||
import EDU.purdue.cs.bloat.util.*; |
|
||||||
|
|
||||||
/** |
|
||||||
* Inlines the code of non-virtual method call sites. These sites include calls |
|
||||||
* to static methods and certain uses of the <tt>invokespecial</tt> method. |
|
||||||
* There are certain metrics that can be set to effect where and how inlining is |
|
||||||
* performed. |
|
||||||
*/ |
|
||||||
public class Inline { |
|
||||||
public static boolean DEBUG = false; |
|
||||||
|
|
||||||
private int maxCodeSize; // Max number of instructions in method
|
|
||||||
|
|
||||||
private int maxCallDepth; // Max of height of call stack
|
|
||||||
|
|
||||||
private boolean inlineExceptions; // Inline methods that throw exceptions
|
|
||||||
|
|
||||||
private InlineContext context; |
|
||||||
|
|
||||||
private Map editors; // Maps MemberRefs to their MethodEditors
|
|
||||||
|
|
||||||
/** |
|
||||||
* Size of the largest method that can be inlined |
|
||||||
*/ |
|
||||||
public static int CALLEE_SIZE = 100000; |
|
||||||
|
|
||||||
private static void db(final String s) { |
|
||||||
if (Inline.DEBUG) { |
|
||||||
System.out.println(s); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Constructor. By default the first-level calls are only inlined one level |
|
||||||
* deep, there is no max size on methods to inline, and methods that may |
|
||||||
* throw exceptions are inlined. |
|
||||||
* |
|
||||||
* @param maxCodeSize |
|
||||||
* The maximum number of instructions a method can grow to. |
|
||||||
*/ |
|
||||||
public Inline(final InlineContext context, final int maxCodeSize) { |
|
||||||
this.context = context; |
|
||||||
this.maxCodeSize = maxCodeSize; |
|
||||||
this.maxCallDepth = 1; |
|
||||||
this.inlineExceptions = true; |
|
||||||
|
|
||||||
editors = new HashMap(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Sets the maximum of size of a method that will be inlined. No method |
|
||||||
* larger than this will be inlined. |
|
||||||
*/ |
|
||||||
public void setMaxInlineSize(final int maxInlineSize) { |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Sets the maximum number of nested calls we inline. |
|
||||||
*/ |
|
||||||
public void setMaxCallDepth(final int maxCallDepth) { |
|
||||||
this.maxCallDepth = maxCallDepth; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Sets whether or not methods that may throw exceptions (that is, have a |
|
||||||
* non-empty "throws" declaration) are inlined. |
|
||||||
*/ |
|
||||||
public void setInlineExceptions(final boolean inlineExceptions) { |
|
||||||
this.inlineExceptions = inlineExceptions; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Scans a method and inlines non-virtual method calls according to this |
|
||||||
* <tt>Inline</tt>'s metrics. |
|
||||||
*/ |
|
||||||
public void inline(final MethodEditor method) { |
|
||||||
// Go through the method and look for calls to inline
|
|
||||||
StackHeightCounter stackHeight = new StackHeightCounter(method); |
|
||||||
List code = method.code(); |
|
||||||
boolean firstCall = true; |
|
||||||
for (int i = 0; i < code.size(); i++) { |
|
||||||
final Object o = code.get(i); |
|
||||||
if (o instanceof Instruction) { |
|
||||||
final Instruction inst = (Instruction) o; |
|
||||||
if ((inst.opcodeClass() == Opcode.opcx_invokestatic) |
|
||||||
|| (inst.opcodeClass() == Opcode.opcx_invokespecial)) { |
|
||||||
final MemberRef callee = (MemberRef) inst.operand(); |
|
||||||
final Stack callStack = new Stack(); |
|
||||||
callStack.add(method.memberRef()); |
|
||||||
|
|
||||||
Inline.db(" Call: " + inst); |
|
||||||
|
|
||||||
stackHeight.handle(inst); |
|
||||||
final int expectedHeight = stackHeight.height(); |
|
||||||
stackHeight.unhandle(inst); |
|
||||||
|
|
||||||
final int j = i; |
|
||||||
i = inline(method, callee, i, callStack, stackHeight, |
|
||||||
firstCall); |
|
||||||
|
|
||||||
if (j == i) { |
|
||||||
// Call was not inlined, add it to the stack
|
|
||||||
stackHeight.handle(inst); |
|
||||||
Inline.db(" " + i + "." + stackHeight.height() + ") " |
|
||||||
+ inst); |
|
||||||
} |
|
||||||
|
|
||||||
final int newHeight = stackHeight.height(); |
|
||||||
// If an exception is thrown as the last thing in a method
|
|
||||||
// newHeight will equal 0. Let's let this one slide.
|
|
||||||
Assert.isTrue((newHeight == 0) |
|
||||||
|| (newHeight == expectedHeight), |
|
||||||
"Inlining did not get the stack heights right: " |
|
||||||
+ "Expected " + expectedHeight + ", got " |
|
||||||
+ newHeight); |
|
||||||
|
|
||||||
} else { |
|
||||||
stackHeight.handle(inst); |
|
||||||
Inline.db(" " + i + "." + stackHeight.height() + ") " |
|
||||||
+ inst); |
|
||||||
} |
|
||||||
|
|
||||||
if (inst.isInvoke()) { |
|
||||||
firstCall = false; |
|
||||||
} |
|
||||||
|
|
||||||
} else if (o instanceof Label) { |
|
||||||
final Label label = (Label) o; |
|
||||||
stackHeight.handle(label); |
|
||||||
Inline.db(" " + i + "." + stackHeight.height() + ") " + label |
|
||||||
+ (label.startsBlock() ? " (starts block)" : "")); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
method.setCode(code); |
|
||||||
|
|
||||||
if (Inline.DEBUG) { |
|
||||||
stackHeight = new StackHeightCounter(method); |
|
||||||
Inline.db("\nNew Code for " + method.declaringClass().name() + "." |
|
||||||
+ method.name() + method.type()); |
|
||||||
code = method.code(); |
|
||||||
for (int j = 0; j < code.size(); j++) { |
|
||||||
if (code.get(j) instanceof Label) { |
|
||||||
final Label label = (Label) code.get(j); |
|
||||||
|
|
||||||
stackHeight.handle(label); |
|
||||||
|
|
||||||
final Iterator tryCatches = method.tryCatches().iterator(); |
|
||||||
while (tryCatches.hasNext()) { |
|
||||||
final TryCatch tryCatch = (TryCatch) tryCatches.next(); |
|
||||||
if (tryCatch.start().equals(label)) { |
|
||||||
System.out.println(" Begin protected region"); |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
if (tryCatch.end().equals(label)) { |
|
||||||
System.out.println(" End protected region"); |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
// A Label can both end a protected region and begin
|
|
||||||
// catch
|
|
||||||
// block
|
|
||||||
|
|
||||||
if (tryCatch.handler().equals(label)) { |
|
||||||
System.out.println(" Catch " + tryCatch.type()); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
System.out.println(" " + j + "." + stackHeight.height() |
|
||||||
+ ") " + label |
|
||||||
+ (label.startsBlock() ? " (starts block)" : "")); |
|
||||||
} else { |
|
||||||
final Instruction inst = (Instruction) code.get(j); |
|
||||||
stackHeight.handle(inst); |
|
||||||
System.out.println(" " + j + "." + stackHeight.height() |
|
||||||
+ ") " + code.get(j)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Print try-catch information
|
|
||||||
final Iterator tryCatches = method.tryCatches().iterator(); |
|
||||||
System.out.println("Exception information:"); |
|
||||||
while (tryCatches.hasNext()) { |
|
||||||
final TryCatch tryCatch = (TryCatch) tryCatches.next(); |
|
||||||
System.out.println(" " + tryCatch); |
|
||||||
} |
|
||||||
System.out.println(""); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Helper method that does most of the work. By calling this method |
|
||||||
* recursively, we can inline more than one call deep. |
|
||||||
* |
|
||||||
* @param caller |
|
||||||
* The original caller that got all of this started. Into this |
|
||||||
* method we insert the code. |
|
||||||
* @param callee |
|
||||||
* The method to be inlined |
|
||||||
* @param index |
|
||||||
* Where in caller we insert inlined code |
|
||||||
* @param callStack |
|
||||||
* A stack of <tt>MemberRef</tt>s that represent the inlined |
|
||||||
* methods that call other methods. It is used to detect |
|
||||||
* recursion. |
|
||||||
* |
|
||||||
* @return The index into the caller's code array of the instruction |
|
||||||
* following the last inlinined instruction. Start looking here |
|
||||||
* after inline returns. |
|
||||||
*/ |
|
||||||
private int inline(final MethodEditor caller, final MemberRef callee, |
|
||||||
int index, final Stack callStack, |
|
||||||
final StackHeightCounter stackHeight, boolean firstCall) { |
|
||||||
|
|
||||||
Instruction newInst = null; |
|
||||||
|
|
||||||
// Do we ignore the method being inlined?
|
|
||||||
if (context.ignoreMethod(callee)) { |
|
||||||
Inline.db(" Can't inline " + callee + ": it's ignored"); |
|
||||||
return (index++); |
|
||||||
} |
|
||||||
|
|
||||||
// Can we inline this method
|
|
||||||
if (callStack.size() > maxCallDepth) { |
|
||||||
Inline.db(" Can't inline " + callee + ": max call depth (" |
|
||||||
+ maxCallDepth + ") reached"); |
|
||||||
return (index++); |
|
||||||
|
|
||||||
} else if (callStack.contains(callee)) { |
|
||||||
Inline.db(" Can't inline recursive call to " + callee); |
|
||||||
return (index++); |
|
||||||
} |
|
||||||
|
|
||||||
// Make sure we're not inlining the static-ized version of a
|
|
||||||
// method in the call stack.
|
|
||||||
String name = callee.name(); |
|
||||||
final int b = name.indexOf("$$BLOAT"); |
|
||||||
if (b != -1) { |
|
||||||
name = name.substring(0, b); |
|
||||||
|
|
||||||
// Get rid of first parameter
|
|
||||||
final Type[] oldParams = callee.type().paramTypes(); |
|
||||||
final StringBuffer sb = new StringBuffer("("); |
|
||||||
for (int p = 1; p < oldParams.length; p++) { |
|
||||||
sb.append(oldParams[p].descriptor()); |
|
||||||
} |
|
||||||
sb.append(")" + callee.type().returnType()); |
|
||||||
final Type newType = Type.getType(sb.toString()); |
|
||||||
|
|
||||||
final MemberRef unBloated = new MemberRef(callee.declaringClass(), |
|
||||||
new NameAndType(name, newType)); |
|
||||||
|
|
||||||
if (callStack.contains(unBloated)) { |
|
||||||
Inline.db(" Can't inline recursive call to " + callee); |
|
||||||
return (index++); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
final List code = caller.code(); |
|
||||||
if (code.size() > maxCodeSize) { |
|
||||||
Inline.db(" Can't inline " + callee + ": max code size (" |
|
||||||
+ maxCodeSize + ") reached"); |
|
||||||
return (index++); |
|
||||||
} |
|
||||||
|
|
||||||
MethodEditor calleeMethod = null; |
|
||||||
try { |
|
||||||
calleeMethod = context.editMethod(callee); |
|
||||||
|
|
||||||
} catch (final NoSuchMethodException ex) { |
|
||||||
System.err.println("Couldn't find method " + callee); |
|
||||||
ex.printStackTrace(System.err); |
|
||||||
System.exit(1); |
|
||||||
} |
|
||||||
|
|
||||||
if (calleeMethod.isNative()) { |
|
||||||
Inline.db(" Can't inline " + callee + ": it's a native method"); |
|
||||||
return (index++); |
|
||||||
} |
|
||||||
|
|
||||||
if (calleeMethod.isSynchronized()) { |
|
||||||
Inline.db(" Can't inline " + callee + ": it's synchronized"); |
|
||||||
return (index++); |
|
||||||
} |
|
||||||
|
|
||||||
if (!inlineExceptions |
|
||||||
&& (calleeMethod.methodInfo().exceptionTypes().length > 0)) { |
|
||||||
Inline.db(" Can't inline " + callee |
|
||||||
+ ": it may throw an exception"); |
|
||||||
return (index++); |
|
||||||
} |
|
||||||
|
|
||||||
if (calleeMethod.code().size() > Inline.CALLEE_SIZE) { |
|
||||||
Inline.db(" Can't inline " + callee + ": it's too big"); |
|
||||||
return (index++); |
|
||||||
} |
|
||||||
|
|
||||||
// Methods that catch exceptions are problematic. When an
|
|
||||||
// exception is thrown, it clears the stack. Ordinarily this
|
|
||||||
// isn't a problem. However, now the stack of the caller is
|
|
||||||
// cleared in addition to the stack of the callee. This is bad.
|
|
||||||
// The callee might catch the exception and deal with it.
|
|
||||||
// However, the stack has still been cleared. This really messes
|
|
||||||
// things up for the code that appears after the inlined method.
|
|
||||||
// So, if a method catches an exception, we can only inline it if
|
|
||||||
// the stack contains nothing but the parameters to the call.
|
|
||||||
if (calleeMethod.tryCatches().size() > 0) { |
|
||||||
if (stackHeight.height() > callee.type().stackHeight()) { |
|
||||||
Inline.db(" Can't inline " + callee |
|
||||||
+ ": It catches an exception and there's stuff on the " |
|
||||||
+ "stack"); |
|
||||||
return (index++); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// If the callee method catches any of the same exceptions as the
|
|
||||||
// protected region that we are currently in, then we can't inline
|
|
||||||
// the method.
|
|
||||||
final Iterator tryCatches0 = calleeMethod.tryCatches().iterator(); |
|
||||||
while (tryCatches0.hasNext()) { |
|
||||||
final TryCatch tc1 = (TryCatch) tryCatches0.next(); |
|
||||||
final Iterator iter = stackHeight.tryCatches().iterator(); |
|
||||||
while (iter.hasNext()) { |
|
||||||
final TryCatch tc2 = (TryCatch) iter.next(); |
|
||||||
final Type t1 = tc1.type(); |
|
||||||
final Type t2 = tc2.type(); |
|
||||||
if ((t1 != null) && (t2 != null) && t1.equals(t2)) { |
|
||||||
Inline.db(" Can't inline " + callee |
|
||||||
+ ": It catches the same type " |
|
||||||
+ tc1.type().className() |
|
||||||
+ " as the current protected region"); |
|
||||||
return (index++); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// If the caller is a constructor and this is the first
|
|
||||||
// invokespecial we've seen in this callee method, we can inline
|
|
||||||
// calls to the constructors of superclasses and other
|
|
||||||
// constructors in this method. So, if this IS the first call in
|
|
||||||
// a method which IS a constructor, we can inline it.
|
|
||||||
if (calleeMethod.isConstructor() |
|
||||||
&& (!firstCall || !caller.isConstructor())) { |
|
||||||
|
|
||||||
Inline.db(" Can't inline " + callee |
|
||||||
+ ": It calls a normal constructor"); |
|
||||||
return (index++); |
|
||||||
} |
|
||||||
|
|
||||||
// Local variables are problematic. We cannot simply map the
|
|
||||||
// callee's variables to new variables in the caller because we
|
|
||||||
// cannot precisely determine the width of the variable the first
|
|
||||||
// time we see it. (For instance, Nate's generated code might use
|
|
||||||
// a local variable as a non-wide initially and then use it as a
|
|
||||||
// wide later.)
|
|
||||||
|
|
||||||
// Okay, we going to inline. Remove the calling instruction.
|
|
||||||
final Instruction call = (Instruction) code.remove(index--); |
|
||||||
Inline.db(" Removing call: " + call); |
|
||||||
Assert.isTrue((call.opcodeClass() == Opcode.opcx_invokestatic) |
|
||||||
|| (call.opcodeClass() == Opcode.opcx_invokespecial), |
|
||||||
"Removing the wrong call instruction:" + call); |
|
||||||
callStack.push(callee); |
|
||||||
|
|
||||||
Inline |
|
||||||
.db(" Inlining call (" + callStack.size() + ") to " |
|
||||||
+ callee.declaringClass() + "." + callee.name() |
|
||||||
+ callee.type()); |
|
||||||
context.getInlineStats().noteInlined(); |
|
||||||
|
|
||||||
// First we have to pop the arguments off the stack and store them
|
|
||||||
// into the local variables. Remember that wide types occupy two
|
|
||||||
// local variables.
|
|
||||||
final Mapper mapper = new Mapper(caller); |
|
||||||
Type[] paramTypes = callee.type().indexedParamTypes(); |
|
||||||
if (!calleeMethod.isStatic()) { |
|
||||||
// Constructors (and any other special methods we're inlining)
|
|
||||||
// have a "this" pointer where static methods do not.
|
|
||||||
final Type[] newParams = new Type[paramTypes.length + 1]; |
|
||||||
newParams[0] = callee.declaringClass(); |
|
||||||
|
|
||||||
for (int i = 0; i < paramTypes.length; i++) { |
|
||||||
newParams[i + 1] = paramTypes[i]; |
|
||||||
} |
|
||||||
paramTypes = newParams; |
|
||||||
} |
|
||||||
|
|
||||||
final LocalVariable[] params = new LocalVariable[paramTypes.length]; |
|
||||||
|
|
||||||
Inline.db(" Indexed params:"); |
|
||||||
for (int i = 0; i < params.length; i++) { |
|
||||||
params[i] = calleeMethod.paramAt(i); |
|
||||||
Inline.db(" " + i + ": " + params[i] |
|
||||||
+ (params[i] != null ? " " + params[i].type() + " " : "")); |
|
||||||
} |
|
||||||
|
|
||||||
for (int i = params.length - 1; i >= 0; i--) { |
|
||||||
// Map the local variables containing the arguments to new
|
|
||||||
// local variables.
|
|
||||||
final LocalVariable param = params[i]; |
|
||||||
final Type paramType = params[i].type(); |
|
||||||
|
|
||||||
if (param.type() == null) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
Inline.db(" Param " + i + ": " + param + " of type " + paramType); |
|
||||||
|
|
||||||
final LocalVariable newVar = mapper.map(param, paramType); |
|
||||||
|
|
||||||
int opcode; |
|
||||||
|
|
||||||
if (paramType.isReference()) { |
|
||||||
opcode = Opcode.opcx_astore; |
|
||||||
|
|
||||||
} else { |
|
||||||
switch (paramType.typeCode()) { |
|
||||||
case Type.BOOLEAN_CODE: |
|
||||||
case Type.BYTE_CODE: |
|
||||||
case Type.CHARACTER_CODE: |
|
||||||
case Type.SHORT_CODE: |
|
||||||
opcode = Opcode.opcx_istore; |
|
||||||
break; |
|
||||||
|
|
||||||
case Type.DOUBLE_CODE: |
|
||||||
opcode = Opcode.opcx_dstore; |
|
||||||
break; |
|
||||||
|
|
||||||
case Type.LONG_CODE: |
|
||||||
opcode = Opcode.opcx_lstore; |
|
||||||
break; |
|
||||||
|
|
||||||
case Type.FLOAT_CODE: |
|
||||||
opcode = Opcode.opcx_fstore; |
|
||||||
break; |
|
||||||
|
|
||||||
case Type.INTEGER_CODE: |
|
||||||
opcode = Opcode.opcx_istore; |
|
||||||
break; |
|
||||||
|
|
||||||
default: |
|
||||||
throw new IllegalArgumentException("What's a " + paramType |
|
||||||
+ "doing as a method " + "parameter"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
newInst = new Instruction(opcode, newVar); |
|
||||||
code.add(++index, newInst); |
|
||||||
stackHeight.handle(newInst); |
|
||||||
Inline.db(" " + index + "." + stackHeight.height() + "> " |
|
||||||
+ newInst); |
|
||||||
} |
|
||||||
|
|
||||||
// Before we mess with the code, we have to patch up the try-catch
|
|
||||||
// information from the inlined method to the caller method.
|
|
||||||
final Iterator tryCatches = calleeMethod.tryCatches().iterator(); |
|
||||||
while (tryCatches.hasNext()) { |
|
||||||
final TryCatch tryCatch = (TryCatch) tryCatches.next(); |
|
||||||
|
|
||||||
final Label start = mapper.map(tryCatch.start()); |
|
||||||
final Label end = mapper.map(tryCatch.end()); |
|
||||||
final Label handler = mapper.map(tryCatch.handler()); |
|
||||||
|
|
||||||
final TryCatch newTryCatch = new TryCatch(start, end, handler, |
|
||||||
tryCatch.type()); |
|
||||||
caller.addTryCatch(newTryCatch); |
|
||||||
|
|
||||||
// db("Try-catch");
|
|
||||||
// db(" Before: " + tryCatch.start() + "\t" + tryCatch.end() +
|
|
||||||
// "\t" + tryCatch.handler());
|
|
||||||
// db(" After: " + newTryCatch.start() + "\t" + newTryCatch.end()
|
|
||||||
// + "\t" + newTryCatch.handler());
|
|
||||||
} |
|
||||||
|
|
||||||
// Go through the code in the callee method and inline it. Handle
|
|
||||||
// any calls by making a recursive call to this method. Copy each
|
|
||||||
// instruction to the method in which it is being inlined. Along
|
|
||||||
// the way convert references to local variables to their mapped
|
|
||||||
// values. Also remove return instructions. Replace them with
|
|
||||||
// loads as necessary.
|
|
||||||
|
|
||||||
final List inlineCode = calleeMethod.code(); |
|
||||||
|
|
||||||
// We don't want to introduce a new end label because it confuses
|
|
||||||
// BLOAT during CFG construction. We designate the end label as
|
|
||||||
// starting a new block in hopes that it will solve problems with
|
|
||||||
// CFG construction.
|
|
||||||
final Object last = inlineCode.get(inlineCode.size() - 1); |
|
||||||
boolean addEndLabel; |
|
||||||
Label endLabel; |
|
||||||
if (last instanceof Label) { |
|
||||||
endLabel = mapper.map((Label) last); |
|
||||||
addEndLabel = false; |
|
||||||
|
|
||||||
} else { |
|
||||||
endLabel = caller.newLabel(); |
|
||||||
addEndLabel = true; |
|
||||||
} |
|
||||||
endLabel.setStartsBlock(true); |
|
||||||
|
|
||||||
firstCall = true; |
|
||||||
|
|
||||||
for (int j = 0; j < inlineCode.size(); j++) { |
|
||||||
final Object o = inlineCode.get(j); |
|
||||||
|
|
||||||
if (o instanceof Label) { |
|
||||||
final Label label = (Label) o; |
|
||||||
final Label newLabel = mapper.map(label); |
|
||||||
|
|
||||||
code.add(++index, newLabel); |
|
||||||
stackHeight.handle(newLabel); |
|
||||||
Inline.db(" " + index + "." + stackHeight.height() + "> " |
|
||||||
+ newLabel |
|
||||||
+ (newLabel.startsBlock() ? " (starts block)" : "")); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
Assert.isTrue(o instanceof Instruction, "What is a " + o |
|
||||||
+ " doing in the instruction stream?"); |
|
||||||
|
|
||||||
final Instruction inst = (Instruction) inlineCode.get(j); |
|
||||||
Object operand = inst.operand(); |
|
||||||
final int opcode = inst.opcodeClass(); |
|
||||||
|
|
||||||
if (operand instanceof LocalVariable) { |
|
||||||
// Map local variable in the callee method to local
|
|
||||||
// variables in the caller method.
|
|
||||||
final LocalVariable local = mapper.map((LocalVariable) operand, |
|
||||||
(inst.category() == 2 ? true : false)); |
|
||||||
operand = local; |
|
||||||
|
|
||||||
} else if (operand instanceof Label) { |
|
||||||
// Map labels in the callee method to labels in the caller
|
|
||||||
// method.
|
|
||||||
final Label label = mapper.map((Label) operand); |
|
||||||
operand = label; |
|
||||||
|
|
||||||
} else if (operand instanceof IncOperand) { |
|
||||||
// Map the local being incremented
|
|
||||||
final IncOperand inc = (IncOperand) operand; |
|
||||||
final LocalVariable newLocal = mapper.map(inc.var(), |
|
||||||
Type.INTEGER); |
|
||||||
operand = new IncOperand(newLocal, inc.incr()); |
|
||||||
|
|
||||||
} else if (operand instanceof Switch) { |
|
||||||
// We have to patch up the Labels involved with the Switch
|
|
||||||
final Switch oldSwitch = (Switch) operand; |
|
||||||
|
|
||||||
final Label newDefault = mapper.map(oldSwitch.defaultTarget()); |
|
||||||
|
|
||||||
final Label[] oldTargets = oldSwitch.targets(); |
|
||||||
final Label[] newTargets = new Label[oldTargets.length]; |
|
||||||
for (int i = 0; i < newTargets.length; i++) { |
|
||||||
final Label newTarget = mapper.map(oldTargets[i]); |
|
||||||
newTargets[i] = newTarget; |
|
||||||
} |
|
||||||
|
|
||||||
operand = new Switch(newDefault, newTargets, oldSwitch.values()); |
|
||||||
} |
|
||||||
|
|
||||||
if (inst.isReturn()) { |
|
||||||
// Insert a jump to the end of the inlined method. Any
|
|
||||||
// return value will be on top of the stack. This is where
|
|
||||||
// we want it.
|
|
||||||
newInst = new Instruction(Opcode.opcx_goto, endLabel); |
|
||||||
code.add(++index, newInst); |
|
||||||
stackHeight.handle(newInst); |
|
||||||
Inline.db(" " + index + "." + stackHeight.height() + "> " |
|
||||||
+ newInst); |
|
||||||
|
|
||||||
} else if ((inst.opcodeClass() == Opcode.opcx_invokestatic) |
|
||||||
|| (inst.opcodeClass() == Opcode.opcx_invokespecial)) { |
|
||||||
// Make a recursive call. Note that this must be done after
|
|
||||||
// we add the call instruction above. But we only want to
|
|
||||||
// visit the instruction with the stackHeight if the call was
|
|
||||||
// not inlined.
|
|
||||||
newInst = new Instruction(opcode, operand); |
|
||||||
code.add(++index, newInst); |
|
||||||
|
|
||||||
stackHeight.handle(newInst); |
|
||||||
final int expectedHeight = stackHeight.height(); |
|
||||||
stackHeight.unhandle(newInst); |
|
||||||
|
|
||||||
final MemberRef nestedCall = (MemberRef) inst.operand(); |
|
||||||
final int oldIndex = index; |
|
||||||
index = inline(caller, nestedCall, index, callStack, |
|
||||||
stackHeight, firstCall); |
|
||||||
|
|
||||||
if (index == oldIndex) { |
|
||||||
stackHeight.handle(newInst); |
|
||||||
Inline.db(" " + index + "." + stackHeight.height() + "> " |
|
||||||
+ newInst); |
|
||||||
} |
|
||||||
|
|
||||||
final int newHeight = stackHeight.height(); |
|
||||||
Assert.isTrue( |
|
||||||
(newHeight == 0) || (newHeight == expectedHeight), |
|
||||||
"Inlining did not get the stack heights right: " |
|
||||||
+ "Expected " + expectedHeight + ", got " |
|
||||||
+ newHeight); |
|
||||||
|
|
||||||
} else { |
|
||||||
// Add the instruction
|
|
||||||
newInst = new Instruction(opcode, operand); |
|
||||||
code.add(++index, newInst); |
|
||||||
stackHeight.handle(newInst); |
|
||||||
Inline.db(" " + index + "." + stackHeight.height() + "> " |
|
||||||
+ newInst); |
|
||||||
} |
|
||||||
|
|
||||||
// We want to do this after we've made any recursive calls to
|
|
||||||
// inline.
|
|
||||||
if (inst.isInvoke()) { |
|
||||||
firstCall = false; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
if (addEndLabel) { |
|
||||||
// Done inlining. Add end label.
|
|
||||||
code.add(++index, endLabel); |
|
||||||
stackHeight.handle(endLabel); |
|
||||||
Inline.db(" " + index + "." + stackHeight.height() + "> " |
|
||||||
+ endLabel |
|
||||||
+ (endLabel.startsBlock() ? " (starts block)" : "")); |
|
||||||
} |
|
||||||
|
|
||||||
caller.setDirty(true); |
|
||||||
callStack.pop(); |
|
||||||
return (index); |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Utility class for mapping local variables and labels. Note that when mapping |
|
||||||
* local variables we have to be careful. We can't assume that a variable will |
|
||||||
* retain its "wideness" throughout the method. I learned this one the hard way. |
|
||||||
* So, we have to keep a constant difference between the mapped variables. |
|
||||||
*/ |
|
||||||
class Mapper { |
|
||||||
private Map varsMap; // Maps local variables
|
|
||||||
|
|
||||||
private Map labelsMap; // Maps labels
|
|
||||||
|
|
||||||
private MethodEditor method; // Method into which things are mapped
|
|
||||||
|
|
||||||
private int offset; // Start numbering new locals here
|
|
||||||
|
|
||||||
private static void db(final String s) { |
|
||||||
if (Inline.DEBUG) { |
|
||||||
System.out.println(s); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Constructor. |
|
||||||
*/ |
|
||||||
public Mapper(final MethodEditor method) { |
|
||||||
this.method = method; |
|
||||||
varsMap = new HashMap(); |
|
||||||
labelsMap = new HashMap(); |
|
||||||
offset = method.maxLocals() + 1; |
|
||||||
} |
|
||||||
|
|
||||||
public Label map(final Label label) { |
|
||||||
Label newLabel = (Label) labelsMap.get(label); |
|
||||||
if (newLabel == null) { |
|
||||||
newLabel = this.method.newLabel(); |
|
||||||
newLabel.setStartsBlock(label.startsBlock()); |
|
||||||
labelsMap.put(label, newLabel); |
|
||||||
Mapper.db(" " + label + " -> " + newLabel |
|
||||||
+ (newLabel.startsBlock() ? " (starts block)" : "")); |
|
||||||
} |
|
||||||
return (newLabel); |
|
||||||
} |
|
||||||
|
|
||||||
public LocalVariable map(final LocalVariable var, final Type type) { |
|
||||||
LocalVariable newVar = (LocalVariable) varsMap.get(var); |
|
||||||
if (newVar == null) { |
|
||||||
newVar = this.method.localAt(var.index() + offset); |
|
||||||
// newVar = this.method.newLocal(type);
|
|
||||||
varsMap.put(var, newVar); |
|
||||||
Mapper.db(" " + var + " (" + var.index() + ") -> " + newVar |
|
||||||
+ "(" + var.index() + "+" + offset + ")" |
|
||||||
+ (type.isWide() ? " (" + type + ")" : "")); |
|
||||||
} |
|
||||||
return (newVar); |
|
||||||
} |
|
||||||
|
|
||||||
public LocalVariable map(final LocalVariable var, final boolean isWide) { |
|
||||||
LocalVariable newVar = (LocalVariable) varsMap.get(var); |
|
||||||
if (newVar == null) { |
|
||||||
newVar = this.method.localAt(var.index() + offset); |
|
||||||
// newVar = this.method.newLocal(isWide);
|
|
||||||
varsMap.put(var, newVar); |
|
||||||
Mapper.db(" " + var + " (" + var.index() + ") -> " + newVar |
|
||||||
+ "(" + var.index() + "+" + offset + ")" |
|
||||||
+ (isWide ? " (wide)" : "")); |
|
||||||
} |
|
||||||
return (newVar); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,99 +0,0 @@ |
|||||||
/** |
|
||||||
* 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.inline; |
|
||||||
|
|
||||||
import java.util.*; |
|
||||||
|
|
||||||
import EDU.purdue.cs.bloat.editor.*; |
|
||||||
|
|
||||||
/** |
|
||||||
* An <Tt>InlineContext</tt> gives access to the <Tt>CallGraph</tt> for the |
|
||||||
* program whose classes are being operated on by BLOAT. |
|
||||||
*/ |
|
||||||
public interface InlineContext extends EditorContext { |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the call graph for the program. |
|
||||||
*/ |
|
||||||
public CallGraph getCallGraph(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Sets the root methods for this <tt>InlineContext</tt>. |
|
||||||
* |
|
||||||
* @param roots |
|
||||||
* The root methods (<tt>MemberRef</tt>s) of the program |
|
||||||
* @throws IllegalStateException |
|
||||||
* Call graph has already been created with different roots. |
|
||||||
*/ |
|
||||||
public void setRootMethods(Set roots); |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns an <tt>InlineStats</tt> object for getting statistics about |
|
||||||
* inlining. |
|
||||||
*/ |
|
||||||
public InlineStats getInlineStats(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Notes that all classes, methods, and fields in a package should be |
|
||||||
* "ignored" by inlining. That is, methods won't be inlined, classes won't |
|
||||||
* be involved in specialization, etc. Note that it is exceptable to just |
|
||||||
* add a prefix of a package name. For instance, adding "java" will ignore |
|
||||||
* java.lang.Object, java.io.File, etc. |
|
||||||
*/ |
|
||||||
public void addIgnorePackage(String name); |
|
||||||
|
|
||||||
/** |
|
||||||
* Notes that a class should be ignored by inlining. That is, none of its |
|
||||||
* methods will be inlined and it won't be involved in specialization. |
|
||||||
*/ |
|
||||||
public void addIgnoreClass(Type type); |
|
||||||
|
|
||||||
/** |
|
||||||
* Notes that a method should be ignored by inlining. That is, it will not |
|
||||||
* be inlined. |
|
||||||
*/ |
|
||||||
public void addIgnoreMethod(MemberRef method); |
|
||||||
|
|
||||||
/** |
|
||||||
* Notes that a field should be ignored by inlining. |
|
||||||
*/ |
|
||||||
public void addIgnoreField(MemberRef field); |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns <tt>true</tt> if a class should be ignored by inlining. |
|
||||||
*/ |
|
||||||
public boolean ignoreClass(Type type); |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns <tt>true</tt> if a method should be ignored by inlining. |
|
||||||
*/ |
|
||||||
public boolean ignoreMethod(MemberRef method); |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns <tt>true</tt> if a field should be ignored by inlining. |
|
||||||
*/ |
|
||||||
public boolean ignoreField(MemberRef field); |
|
||||||
|
|
||||||
/** |
|
||||||
* Sets whether or not we ignore all system classes. |
|
||||||
*/ |
|
||||||
public void setIgnoreSystem(boolean ignoreSystem); |
|
||||||
} |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue