Compare commits

...

No commits in common. 'bcel' and 'master' have entirely different histories.
bcel ... master

  1. 6
      .classpath
  2. 1
      .cvsignore
  3. 17
      .project
  4. 12
      .settings/org.eclipse.jdt.core.prefs
  5. 24
      changes.txt
  6. 74
      src/EDU/purdue/cs/bloat/Makefile
  7. 2
      src/EDU/purdue/cs/bloat/benchmark/.cvsignore
  8. 143
      src/EDU/purdue/cs/bloat/benchmark/Benchmark.java
  9. 115
      src/EDU/purdue/cs/bloat/benchmark/BenchmarkSecurityManager.java
  10. 361
      src/EDU/purdue/cs/bloat/benchmark/CounterDecorate.java
  11. 129
      src/EDU/purdue/cs/bloat/benchmark/Makefile
  12. 101
      src/EDU/purdue/cs/bloat/benchmark/Nonstop.java
  13. 110
      src/EDU/purdue/cs/bloat/benchmark/Shade.java
  14. 105
      src/EDU/purdue/cs/bloat/benchmark/Stats.java
  15. 76
      src/EDU/purdue/cs/bloat/benchmark/Times.java
  16. 260
      src/EDU/purdue/cs/bloat/benchmark/benchmark.c
  17. 10
      src/EDU/purdue/cs/bloat/benchmark/package.html
  18. 39
      src/EDU/purdue/cs/bloat/benchmark/shade.c
  19. 35
      src/EDU/purdue/cs/bloat/benchmark/stats.c
  20. 1
      src/EDU/purdue/cs/bloat/cfg/.cvsignore
  21. 371
      src/EDU/purdue/cs/bloat/cfg/Block.java
  22. 158
      src/EDU/purdue/cs/bloat/cfg/DominanceFrontier.java
  23. 387
      src/EDU/purdue/cs/bloat/cfg/DominatorTree.java
  24. 3491
      src/EDU/purdue/cs/bloat/cfg/FlowGraph.java
  25. 79
      src/EDU/purdue/cs/bloat/cfg/Handler.java
  26. 28
      src/EDU/purdue/cs/bloat/cfg/Makefile
  27. 167
      src/EDU/purdue/cs/bloat/cfg/ReplaceTarget.java
  28. 259
      src/EDU/purdue/cs/bloat/cfg/Subroutine.java
  29. 384
      src/EDU/purdue/cs/bloat/cfg/VerifyCFG.java
  30. 0
      src/EDU/purdue/cs/bloat/cfg/package.html
  31. 43
      src/EDU/purdue/cs/bloat/class.mk
  32. 1
      src/EDU/purdue/cs/bloat/codegen/.cvsignore
  33. 2339
      src/EDU/purdue/cs/bloat/codegen/CodeGenerator.java
  34. 774
      src/EDU/purdue/cs/bloat/codegen/Liveness.java
  35. 24
      src/EDU/purdue/cs/bloat/codegen/Makefile
  36. 657
      src/EDU/purdue/cs/bloat/codegen/RegisterAllocator.java
  37. 0
      src/EDU/purdue/cs/bloat/codegen/package.html
  38. 1
      src/EDU/purdue/cs/bloat/context/.cvsignore
  39. 261
      src/EDU/purdue/cs/bloat/context/BloatContext.java
  40. 151
      src/EDU/purdue/cs/bloat/context/BloatingClassLoader.java
  41. 461
      src/EDU/purdue/cs/bloat/context/CachingBloatContext.java
  42. 25
      src/EDU/purdue/cs/bloat/context/Makefile
  43. 438
      src/EDU/purdue/cs/bloat/context/PersistentBloatContext.java
  44. 0
      src/EDU/purdue/cs/bloat/context/package.html
  45. 1
      src/EDU/purdue/cs/bloat/decorate/.cvsignore
  46. 727
      src/EDU/purdue/cs/bloat/decorate/Main.java
  47. 23
      src/EDU/purdue/cs/bloat/decorate/Makefile
  48. 8
      src/EDU/purdue/cs/bloat/decorate/package.html
  49. 1
      src/EDU/purdue/cs/bloat/diva/.cvsignore
  50. 514
      src/EDU/purdue/cs/bloat/diva/InductionVarAnalyzer.java
  51. 637
      src/EDU/purdue/cs/bloat/diva/Main.java
  52. 24
      src/EDU/purdue/cs/bloat/diva/Makefile
  53. 0
      src/EDU/purdue/cs/bloat/diva/package.html
  54. 1
      src/EDU/purdue/cs/bloat/dump/.cvsignore
  55. 252
      src/EDU/purdue/cs/bloat/dump/Main.java
  56. 23
      src/EDU/purdue/cs/bloat/dump/Makefile
  57. 8
      src/EDU/purdue/cs/bloat/dump/package.html
  58. 1
      src/EDU/purdue/cs/bloat/editor/.cvsignore
  59. 555
      src/EDU/purdue/cs/bloat/editor/ClassEditor.java
  60. 1270
      src/EDU/purdue/cs/bloat/editor/ClassHierarchy.java
  61. 2415
      src/EDU/purdue/cs/bloat/editor/CodeArray.java
  62. 459
      src/EDU/purdue/cs/bloat/editor/ConstantPool.java
  63. 153
      src/EDU/purdue/cs/bloat/editor/EditorContext.java
  64. 35
      src/EDU/purdue/cs/bloat/editor/EditorVisitor.java
  65. 562
      src/EDU/purdue/cs/bloat/editor/FieldEditor.java
  66. 75
      src/EDU/purdue/cs/bloat/editor/IncOperand.java
  67. 1258
      src/EDU/purdue/cs/bloat/editor/Instruction.java
  68. 459
      src/EDU/purdue/cs/bloat/editor/InstructionAdapter.java
  69. 330
      src/EDU/purdue/cs/bloat/editor/InstructionVisitor.java
  70. 141
      src/EDU/purdue/cs/bloat/editor/Label.java
  71. 134
      src/EDU/purdue/cs/bloat/editor/LocalVariable.java
  72. 44
      src/EDU/purdue/cs/bloat/editor/Makefile
  73. 117
      src/EDU/purdue/cs/bloat/editor/MemberRef.java
  74. 1757
      src/EDU/purdue/cs/bloat/editor/MethodEditor.java
  75. 76
      src/EDU/purdue/cs/bloat/editor/MultiArrayOperand.java
  76. 83
      src/EDU/purdue/cs/bloat/editor/NameAndType.java
  77. 1766
      src/EDU/purdue/cs/bloat/editor/Opcode.java
  78. 459
      src/EDU/purdue/cs/bloat/editor/SerialVersionUID.java
  79. 232
      src/EDU/purdue/cs/bloat/editor/Switch.java
  80. 106
      src/EDU/purdue/cs/bloat/editor/TryCatch.java
  81. 1035
      src/EDU/purdue/cs/bloat/editor/Type.java
  82. 120
      src/EDU/purdue/cs/bloat/editor/TypeComparator.java
  83. 53
      src/EDU/purdue/cs/bloat/editor/UseMap.java
  84. 0
      src/EDU/purdue/cs/bloat/editor/package.html
  85. 1
      src/EDU/purdue/cs/bloat/file/.cvsignore
  86. 89
      src/EDU/purdue/cs/bloat/file/Attribute.java
  87. 980
      src/EDU/purdue/cs/bloat/file/ClassFile.java
  88. 501
      src/EDU/purdue/cs/bloat/file/ClassFileLoader.java
  89. 5
      src/EDU/purdue/cs/bloat/file/ClassSource.java
  90. 451
      src/EDU/purdue/cs/bloat/file/Code.java
  91. 115
      src/EDU/purdue/cs/bloat/file/ConstantValue.java
  92. 9
      src/EDU/purdue/cs/bloat/file/DefaultClassSource.java
  93. 140
      src/EDU/purdue/cs/bloat/file/Exceptions.java
  94. 293
      src/EDU/purdue/cs/bloat/file/Field.java
  95. 82
      src/EDU/purdue/cs/bloat/file/GenericAttribute.java
  96. 260
      src/EDU/purdue/cs/bloat/file/JarFileCommitter.java
  97. 142
      src/EDU/purdue/cs/bloat/file/LineNumberTable.java
  98. 147
      src/EDU/purdue/cs/bloat/file/LocalVariableTable.java
  99. 33
      src/EDU/purdue/cs/bloat/file/Makefile
  100. 484
      src/EDU/purdue/cs/bloat/file/Method.java
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.3"/>
<classpathentry kind="output" path="bin"/>
</classpath>

@ -0,0 +1 @@
bin

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>bloat</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

@ -0,0 +1,12 @@
#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

@ -0,0 +1,24 @@
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

@ -0,0 +1,74 @@
# All files in the distribution of BLOAT (Bytecode Level Optimization and
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
# Research Foundation of Purdue University. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
SUBDIRS = util \
reflect \
file \
editor \
inline \
tree \
cfg \
ssa \
tbaa \
trans \
diva \
codegen \
context \
decorate \
optimize \
shrink \
strip \
dump \
tools \
benchmark
all:
@for i in $(SUBDIRS) ""; do \
if [ "x$$i" != "x" ]; then \
$(MAKE) -C $$i all; \
fi; \
done
clean:
@for i in $(SUBDIRS) ""; do \
if [ "x$$i" != "x" ]; then \
$(MAKE) -C $$i clean; \
fi; \
done
docs:
javadoc -d ../../../../../docs -sourcepath ../../../.. \
EDU.purdue.cs.bloat.util \
EDU.purdue.cs.bloat.reflect \
EDU.purdue.cs.bloat.file \
EDU.purdue.cs.bloat.editor \
EDU.purdue.cs.bloat.tree \
EDU.purdue.cs.bloat.cfg \
EDU.purdue.cs.bloat.ssa \
EDU.purdue.cs.bloat.tbaa \
EDU.purdue.cs.bloat.trans \
EDU.purdue.cs.bloat.diva \
EDU.purdue.cs.bloat.codegen \
EDU.purdue.cs.bloat.context \
EDU.purdue.cs.bloat.decorate \
EDU.purdue.cs.bloat.optimize \
EDU.purdue.cs.bloat.shrink \
EDU.purdue.cs.bloat.strip \
EDU.purdue.cs.bloat.dump \
EDU.purdue.cs.bloat.benchmark

@ -0,0 +1,2 @@
*.class
EDU_purdue_cs_bloat_benchmark_Benchmark.h

@ -0,0 +1,143 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.benchmark;
/**
* This class is used to run a benchmark Java program with Perfmon running in
* the background. Perfmon is a software package developed at Michigan State
* University that allows user-level programs to access the hardware counters on
* Sparc processors.
*
* <p>
*
* The <tt>main</tt> method of this class takes several arguments (note that
* the first four arguments are mutually exclusive):
*
* <pre>
* -inst-load-stall Count load interlock induced stalls
* -dcache Count data cache hit rate
* -cycle-ic-miss-stall Count I-cache miss induced stalls (and cycles)
* -inst-cycle Count instructions (and cycles)
*
* -run n How many times is the program run
*
* class Java class to run (the benchmark)
* args Arguments to benchmark class
* </pre>
*
* The real work is done by the native <tt>run</tt> method that is implemented
* in benchmark.c.
*
* @see BenchmarkSecurityManager
*/
public class Benchmark {
static {
// Load native code from libbenchmark.so
System.loadLibrary("benchmark");
}
public static native void init(Class main);
public static native void run(Class main, String[] args);
public static native void setMode(int mode);
public static void main(final String[] args) throws Exception {
int mode = 0;
int runs = 1;
int eat = 0;
if (args.length <= 1) {
Benchmark.usage();
}
for (eat = 0; eat < args.length; eat++) {
if (args[eat].equals("-inst-cycle")) {
mode = 3;
} else if (args[eat].equals("-inst-load-stall")) {
mode = 0;
} else if (args[eat].equals("-dcache")) {
mode = 1;
} else if (args[eat].equals("-cycle-ic-miss-stall")) {
mode = 2;
} else if (args[eat].equals("-run")) {
if (++eat >= args.length) {
Benchmark.usage();
}
runs = Integer.parseInt(args[eat]);
if (runs <= 0) {
Benchmark.usage();
}
} else {
// The main class
eat++;
break;
}
}
/* Got all the args. */
if (eat > args.length) {
Benchmark.usage();
}
final BenchmarkSecurityManager sec = new BenchmarkSecurityManager();
System.setSecurityManager(sec);
final String mainClassName = args[eat - 1];
final String[] a = new String[args.length - eat];
System.err.println("Running " + mainClassName + " in mode " + mode);
Benchmark.setMode(mode);
final Class mainClass = Class.forName(mainClassName);
Benchmark.init(mainClass);
for (int i = 0; i < runs; i++) {
try {
System.arraycopy(args, eat, a, 0, a.length);
Benchmark.run(mainClass, a);
} catch (final SecurityException e) {
continue;
} catch (final Exception e) {
e.printStackTrace(System.err);
sec.allowExit = true;
System.exit(1);
}
}
sec.allowExit = true;
}
private static void usage() {
System.err.print("usage: java EDU.purdue.cs.bloat.Benchmark ");
System.err.println("options class args...");
System.err.println("where options are one of:");
System.err.println(" -inst-load-stall");
System.err.println(" -inst-cycle");
System.err.println(" -cycle-ic-miss-stall");
System.err.println(" -dcache");
System.err.println(" -run n");
System.exit(1);
}
}

@ -0,0 +1,115 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.benchmark;
import java.io.*;
/**
* The <tt>BenchmarkSecurityManager</tt> allows us to execute a "main" method
* multiple times without the virtual machine exiting. If exit is not allowed,
* the <tt>checkExit</tt> method will throw a <tt>SecurityException</tt>
* that can be caught, thus allowing execution to continue.
*
* @see Shade
* @see Stats
*/
public class BenchmarkSecurityManager extends SecurityManager {
boolean allowExit = false;
/**
* A <tt>SecurityException</tt> is thrown if we do not allow the virtual
* machine to exit.
*/
public void checkExit(final int status) {
if (!allowExit) {
System.err.println("exit " + status);
throw new SecurityException("Tried to exit (status=" + status + ")");
}
}
public void checkCreateClassLoader() {
}
public void checkAccess(final Thread t) {
}
public void checkAccess(final ThreadGroup g) {
}
public void checkExec(final String cmd) {
}
public void checkLink(final String lib) {
}
public void checkRead(final FileDescriptor fd) {
}
public void checkRead(final String file) {
}
public void checkRead(final String file, final Object context) {
}
public void checkWrite(final FileDescriptor fd) {
}
public void checkWrite(final String file) {
}
public void checkDelete(final String file) {
}
public void checkConnect(final String host, final int port) {
}
public void checkConnect(final String host, final int port,
final Object context) {
}
public void checkListen(final int port) {
}
public void checkAccept(final String host, final int port) {
}
public void checkPropertiesAccess() {
}
public void checkPropertyAccess(final String key) {
}
public void checkPropertyAccess(final String key, final String val) {
}
public boolean checkTopLevelWindow(final Object window) {
return true;
}
public void checkPackageAccess(final String pkg) {
}
public void checkPackageDefinition(final String pkg) {
}
public void checkSetFactory() {
}
}

@ -0,0 +1,361 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.benchmark;
import java.io.*;
import java.util.*;
import EDU.purdue.cs.bloat.context.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.file.*;
import EDU.purdue.cs.bloat.reflect.*;
public class CounterDecorate implements Opcode {
private static final String COUNTER_TYPE = "I";
private static final String COUNTER_RCNAME = "rcCount";
private static final String COUNTER_AUNAME = "auCount";
private static final String COUNTER_SUNAME = "suCount";
private static final String COUNTER_MAIN = "LEDU/purdue/cs/bloat/benchmark/Counter;";
private static int VERBOSE = 0;
private static boolean FORCE = false;
private static boolean CLOSURE = false;
private static final List SKIP = new ArrayList();
private static final List ONLY = new ArrayList();
public static void main(final String[] args) {
final ClassFileLoader loader = new ClassFileLoader();
List classes = new ArrayList();
boolean gotdir = false;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-v") || args[i].equals("-verbose")) {
CounterDecorate.VERBOSE++;
} else if (args[i].equals("-help")) {
CounterDecorate.usage();
} else if (args[i].equals("-classpath")) {
if (++i >= args.length) {
CounterDecorate.usage();
}
final String classpath = args[i];
loader.setClassPath(classpath);
} else if (args[i].equals("-skip")) {
if (++i >= args.length) {
CounterDecorate.usage();
}
final String pkg = args[i].replace('.', '/');
CounterDecorate.SKIP.add(pkg);
} else if (args[i].equals("-only")) {
if (++i >= args.length) {
CounterDecorate.usage();
}
final String pkg = args[i].replace('.', '/');
CounterDecorate.ONLY.add(pkg);
} else if (args[i].equals("-closure")) {
CounterDecorate.CLOSURE = true;
} else if (args[i].equals("-relax-loading")) {
ClassHierarchy.RELAX = true;
} else if (args[i].equals("-f")) {
CounterDecorate.FORCE = true;
} else if (args[i].startsWith("-")) {
CounterDecorate.usage();
} else if (i == args.length - 1) {
final File f = new File(args[i]);
if (f.exists() && !f.isDirectory()) {
System.err.println("No such directory: " + f.getPath());
System.exit(2);
}
loader.setOutputDir(f);
gotdir = true;
} else {
classes.add(args[i]);
}
}
if (!gotdir) {
CounterDecorate.usage();
}
if (classes.size() == 0) {
CounterDecorate.usage();
}
if (CounterDecorate.VERBOSE > 3) {
ClassFileLoader.DEBUG = true;
ClassEditor.DEBUG = true;
}
boolean errors = false;
final Iterator iter = classes.iterator();
while (iter.hasNext()) {
final String name = (String) iter.next();
try {
loader.loadClass(name);
} catch (final ClassNotFoundException ex) {
System.err.println("Couldn't find class: " + ex.getMessage());
errors = true;
}
}
if (errors) {
System.exit(1);
}
final BloatContext context = new CachingBloatContext(loader, classes,
CounterDecorate.CLOSURE);
if (!CounterDecorate.CLOSURE) {
final Iterator e = classes.iterator();
while (e.hasNext()) {
final String name = (String) e.next();
try {
final ClassInfo info = loader.loadClass(name);
CounterDecorate.decorateClass(context, info);
} catch (final ClassNotFoundException ex) {
System.err.println("Couldn't find class: "
+ ex.getMessage());
System.exit(1);
}
}
} else {
classes = null;
final ClassHierarchy hier = context.getHierarchy();
final Iterator e = hier.classes().iterator();
while (e.hasNext()) {
final Type t = (Type) e.next();
if (t.isObject()) {
try {
final ClassInfo info = loader.loadClass(t.className());
CounterDecorate.decorateClass(context, info);
} catch (final ClassNotFoundException ex) {
System.err.println("Couldn't find class: "
+ ex.getMessage());
System.exit(1);
}
}
}
}
}
private static void usage() {
System.err
.println("Usage: java EDU.purdue.cs.bloat.count.Main "
+ "\n [-options] classes output_dir"
+ "\n"
+ "\nwhere options include:"
+ "\n -help print out this message"
+ "\n -v -verbose turn on verbose mode "
+ "(can be given multiple times)"
+ "\n -classpath <directories separated by colons>"
+ "\n list directories in which to look for classes"
+ "\n -f decorate files even if up-to-date"
+ "\n -closure recursively decorate referenced classes"
+ "\n -relax-loading don't report errors if a class is not found"
+ "\n -skip <class|package.*>"
+ "\n skip the given class or package"
+ "\n (this option can be given more than once)"
+ "\n -only <class|package.*>"
+ "\n skip all but the given class or package"
+ "\n (this option can be given more than once)");
System.exit(0);
}
private static void decorateClass(final EditorContext editor,
final ClassInfo info) {
final ClassFile classFile = (ClassFile) info;
if (!CounterDecorate.FORCE) {
final File source = classFile.file();
final File target = classFile.outputFile();
if ((source != null) && (target != null) && source.exists()
&& target.exists()
&& (source.lastModified() < target.lastModified())) {
if (CounterDecorate.VERBOSE > 1) {
System.out.println(classFile.name() + " is up to date");
}
return;
}
}
if (CounterDecorate.VERBOSE > 2) {
classFile.print(System.out);
}
final ClassEditor c = editor.editClass(info);
boolean skip = false;
final String name = c.type().className();
final String qual = c.type().qualifier() + "/*";
// Edit only classes explicitly mentioned.
if (CounterDecorate.ONLY.size() > 0) {
skip = true;
// Only edit classes we explicitly don't name.
for (int i = 0; i < CounterDecorate.ONLY.size(); i++) {
final String pkg = (String) CounterDecorate.ONLY.get(i);
if (name.equals(pkg) || qual.equals(pkg)) {
skip = false;
break;
}
}
}
// Don't edit classes we explicitly skip.
if (!skip) {
for (int i = 0; i < CounterDecorate.SKIP.size(); i++) {
final String pkg = (String) CounterDecorate.SKIP.get(i);
if (name.equals(pkg) || qual.equals(pkg)) {
skip = true;
break;
}
}
}
if (skip) {
if (CounterDecorate.VERBOSE > 0) {
System.out.println("Skipping " + c.type().className());
}
editor.release(info);
return;
}
if (CounterDecorate.VERBOSE > 0) {
System.out.println("Decorating class " + c.type().className());
}
if (CounterDecorate.VERBOSE > 2) {
((ClassFile) info).print(System.out);
}
final MethodInfo[] methods = c.methods();
for (int j = 0; j < methods.length; j++) {
MethodEditor m;
try {
m = editor.editMethod(methods[j]);
} catch (final ClassFormatException ex) {
System.err.println(ex.getMessage());
continue;
}
CounterDecorate.transform(m);
editor.commit(methods[j]);
}
editor.commit(info);
}
private static void transform(final MethodEditor method) {
if (CounterDecorate.VERBOSE > 1) {
System.out.println("Decorating method " + method);
}
final MemberRef rcfield = new MemberRef(Type
.getType(CounterDecorate.COUNTER_MAIN), new NameAndType(
CounterDecorate.COUNTER_RCNAME, Type
.getType(CounterDecorate.COUNTER_TYPE)));
final MemberRef aufield = new MemberRef(Type
.getType(CounterDecorate.COUNTER_MAIN), new NameAndType(
CounterDecorate.COUNTER_AUNAME, Type
.getType(CounterDecorate.COUNTER_TYPE)));
final MemberRef sufield = new MemberRef(Type
.getType(CounterDecorate.COUNTER_MAIN), new NameAndType(
CounterDecorate.COUNTER_SUNAME, Type
.getType(CounterDecorate.COUNTER_TYPE)));
final ListIterator iter = method.code().listIterator();
while (iter.hasNext()) {
final Object ce = iter.next();
if (CounterDecorate.VERBOSE > 2) {
System.out.println("Examining " + ce);
}
if (ce instanceof Instruction) {
final Instruction inst = (Instruction) ce;
if (inst.opcodeClass() == Opcode.opcx_aupdate) {
iter.remove();
iter.add(new Instruction(Opcode.opcx_getstatic, aufield));
iter.next();
iter.add(new Instruction(Opcode.opcx_ldc, new Integer(1)));
iter.next();
iter.add(new Instruction(Opcode.opcx_iadd));
iter.next();
iter.add(new Instruction(Opcode.opcx_putstatic, aufield));
iter.next();
}
if (inst.opcodeClass() == Opcode.opcx_supdate) {
iter.remove();
iter.add(new Instruction(Opcode.opcx_getstatic, sufield));
iter.next();
iter.add(new Instruction(Opcode.opcx_ldc, new Integer(1)));
iter.next();
iter.add(new Instruction(Opcode.opcx_iadd));
iter.next();
iter.add(new Instruction(Opcode.opcx_putstatic, sufield));
iter.next();
} else if (inst.opcodeClass() == Opcode.opcx_rc) {
iter.remove();
iter.add(new Instruction(Opcode.opcx_getstatic, rcfield));
iter.next();
iter.add(new Instruction(Opcode.opcx_ldc, new Integer(1)));
iter.next();
iter.add(new Instruction(Opcode.opcx_iadd));
iter.next();
iter.add(new Instruction(Opcode.opcx_putstatic, rcfield));
iter.next();
}
}
}
}
}

@ -0,0 +1,129 @@
# All files in the distribution of BLOAT (Bytecode Level Optimization and
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
# Research Foundation of Purdue University. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
CLASS = \
Benchmark.class\
Shade.class\
Stats.class\
BenchmarkSecurityManager.class\
Nonstop.class\
Times.class\
CounterDecorate.class
JNIH = EDU_purdue_cs_bloat_benchmark_Benchmark.h \
EDU_purdue_cs_bloat_benchmark_Shade.h \
EDU_purdue_cs_bloat_benchmark_Stats.h \
EDU_purdue_cs_bloat_benchmark_Times.h \
EDU_purdue_cs_bloat_benchmark_Nonstop.h
OBJ = benchmark.o shade.o stats.o nonstop.o times.o
LIB = libbenchmark_g.so libbenchmark.so libshade.so libstats.so libstats_g.so libnonstop.so libtimes.so libtimes_g.so
.SUFFIXES: .java .class
JAVA_HOME = /u/u83/pps/java
JAVAC = $(JAVA_HOME)/bin/javac
JAVAH = $(JAVA_HOME)/bin/javah
JFLAGS = -g
CLASSPATH = $(JAVA_HOME)/lib/classes.zip
CFLAGS = -K pic -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/solaris \
-I/u/u83/pps/perfmon/include
all: class $(LIB)
clean:
rm -f *.class *.o $(JNIH) $(OBJ) $(LIB)
class:
@files=`$(MAKE) -n _class | grep javac | cut -d' ' -f4`; \
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
if [ "x$$files" != "x" ]; then \
echo $(JAVAC) $(JFLAGS) -classpath $$cpath $$files; \
$(JAVAC) $(JFLAGS) -classpath $$cpath $$files; \
fi
_class: $(CLASS)
.java.class:
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
$(JAVAC) -classpath $$cpath $<
libbenchmark.so: benchmark.o
cc benchmark.o -o libbenchmark.so /u/u83/pps/perfmon/lib/libperfmon.a -G
libbenchmark_g.so: benchmark.o
cc benchmark.o -o libbenchmark_g.so /u/u83/pps/perfmon/lib/libperfmon.a -G
libshade.so: shade.o
cc shade.o -o libshade.so -G
libstats_g.so: stats.o
cc stats.o -o libstats_g.so -G
libstats.so: stats.o
cc stats.o -o libstats.so -G
libtimes.so: times.o
cc times.o -o libtimes.so -G
libtimes_g.so: times.o
cc times.o -o libtimes_g.so -G
libnonstop.so: nonstop.o
cc nonstop.o -o libnonstop.so -G
EDU_purdue_cs_bloat_benchmark_Benchmark.h: Benchmark.java
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
$(JAVAH) -jni -classpath $$cpath \
EDU.purdue.cs.bloat.benchmark.Benchmark
EDU_purdue_cs_bloat_benchmark_Shade.h: Shade.java
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
$(JAVAH) -jni -classpath $$cpath \
EDU.purdue.cs.bloat.benchmark.Shade
EDU_purdue_cs_bloat_benchmark_Stats.h: Stats.java
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
$(JAVAH) -jni -classpath $$cpath \
EDU.purdue.cs.bloat.benchmark.Stats
EDU_purdue_cs_bloat_benchmark_Times.h: Times.java
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
$(JAVAH) -jni -classpath $$cpath \
EDU.purdue.cs.bloat.benchmark.Times
EDU_purdue_cs_bloat_benchmark_Nonstop.h: Nonstop.java
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
$(JAVAH) -jni -classpath $$cpath \
EDU.purdue.cs.bloat.benchmark.Nonstop
nonstop.o: nonstop.c EDU_purdue_cs_bloat_benchmark_Nonstop.h
benchmark.o: benchmark.c EDU_purdue_cs_bloat_benchmark_Benchmark.h
times.o: times.c EDU_purdue_cs_bloat_benchmark_Times.h
shade.o: shade.c EDU_purdue_cs_bloat_benchmark_Shade.h
stats.o: stats.c EDU_purdue_cs_bloat_benchmark_Stats.h
.c.o:
cc -c $(CFLAGS) $<

@ -0,0 +1,101 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.benchmark;
/**
* Runs a Java program multiple times without the Virtual Machine exiting.
*
* @see BenchmarkSecurityManager
*/
public class Nonstop {
static {
// Load native code from libbenchmark.so
System.loadLibrary("nonstop");
}
public static native void run(Class main, String[] args);
public static void main(final String[] args) throws Exception {
int runs = 1;
int eat = 0;
if (args.length <= 1) {
Nonstop.usage();
}
for (eat = 0; eat < args.length; eat++) {
if (args[eat].equals("-run")) {
if (++eat >= args.length) {
Nonstop.usage();
}
runs = Integer.parseInt(args[eat]);
if (runs <= 0) {
Nonstop.usage();
}
} else {
// The main class
eat++;
break;
}
}
/* Got all the args. */
if (eat > args.length) {
Nonstop.usage();
}
final BenchmarkSecurityManager sec = new BenchmarkSecurityManager();
System.setSecurityManager(sec);
final String mainClassName = args[eat - 1];
final String[] a = new String[args.length - eat];
System.err.println("Running " + mainClassName);
for (int i = 0; i < runs; i++) {
try {
final Class mainClass = Class.forName(mainClassName);
System.arraycopy(args, eat, a, 0, a.length);
Benchmark.run(mainClass, a);
} catch (final SecurityException e) {
continue;
} catch (final Exception e) {
e.printStackTrace(System.err);
sec.allowExit = true;
System.exit(1);
}
}
sec.allowExit = true;
}
private static void usage() {
System.err.print("usage: java EDU.purdue.cs.bloat.Benchmark ");
System.err.println("options class args...");
System.err.println("where options are one of:");
System.exit(1);
}
}

@ -0,0 +1,110 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.benchmark;
/**
* This class is used to execute the BLOAT benchmarks while the Shade
* performanace monitoring software is running.
*/
public class Shade {
static {
System.loadLibrary("shade");
}
public static native void run(Class main, String[] args, boolean quit);
public static void main(final String[] args) throws Exception {
boolean quit = false;
int runs = 1;
int eat = 0;
if (args.length <= 1) {
Shade.usage();
}
for (eat = 0; eat < args.length; eat++) {
if (args[eat].equals("-quit")) {
quit = true;
} else if (args[eat].equals("-run")) {
if (++eat >= args.length) {
Shade.usage();
}
runs = Integer.parseInt(args[eat]);
if (runs <= 0) {
Shade.usage();
}
} else {
// The main class
eat++;
break;
}
}
/* Got all the args. */
if (eat > args.length) {
Shade.usage();
}
// Install a secutiry manager in which we can control whether or
// not the virtual machine is allowed to exit. We want to be able
// to make multiple runs of the main class without the VM exiting.
final BenchmarkSecurityManager sec = new BenchmarkSecurityManager();
System.setSecurityManager(sec);
final String mainClassName = args[eat - 1];
final String[] a = new String[args.length - eat];
System.err.println("Running " + mainClassName);
for (int i = 0; i < runs; i++) {
try {
final Class mainClass = Class.forName(mainClassName);
System.arraycopy(args, eat, a, 0, a.length);
Shade.run(mainClass, a, quit);
} catch (final SecurityException e) {
// An execution of the mainClass finished and the VM attempted
// to exit, thus causing a SecutiryException to be thrown by
// the BenchmarkSecurityManager.
continue;
} catch (final Exception e) {
e.printStackTrace(System.err);
sec.allowExit = true;
System.exit(1);
}
}
sec.allowExit = true;
}
private static void usage() {
System.err.print("usage: java EDU.purdue.cs.bloat.Shade ");
System.err.println("options class args...");
System.err.println("where options are one of:");
System.err.println(" -run n time n runs of the program");
System.exit(1);
}
}

@ -0,0 +1,105 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.benchmark;
/**
* <tt>Stats</tt> is used to run a benchmark using an instrumented Java
* Virtual machine that counts the number of each kind of bytecodes executed.
* The counts are maintained in two C variables, <tt>instruction_count</tt>
* and <tt>redundant_count</tt>.
*/
public class Stats {
static {
System.loadLibrary("stats");
}
public static native void run(Class main, String[] args);
public static void main(final String[] args) throws Exception {
int runs = 1;
int eat = 0;
if (args.length <= 1) {
Stats.usage();
}
for (eat = 0; eat < args.length; eat++) {
if (args[eat].equals("-run")) {
if (++eat >= args.length) {
Stats.usage();
}
runs = Integer.parseInt(args[eat]);
if (runs <= 0) {
Stats.usage();
}
} else {
// The main class
eat++;
break;
}
}
/* Got all the args. */
if (eat > args.length) {
Stats.usage();
}
final BenchmarkSecurityManager sec = new BenchmarkSecurityManager();
System.setSecurityManager(sec);
final String mainClassName = args[eat - 1];
final String[] a = new String[args.length - eat];
System.err.println("Running " + mainClassName);
for (int i = 0; i < runs; i++) {
try {
final Class mainClass = Class.forName(mainClassName);
System.arraycopy(args, eat, a, 0, a.length);
Stats.run(mainClass, a);
} catch (final SecurityException e) {
continue;
} catch (final Exception e) {
e.printStackTrace(System.err);
sec.allowExit = true;
System.exit(1);
}
}
sec.allowExit = true;
}
private static void usage() {
System.err.print("usage: java EDU.purdue.cs.bloat.Stats ");
System.err.println("options class args...");
System.err.println("where options are one of:");
System.err.println(" -run n time n runs of the program");
System.exit(1);
}
}

@ -0,0 +1,76 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.benchmark;
/**
* This class allows Java to access the information obtained by the UNIX system
* call <tt>times</tt>.
*/
public class Times {
static {
// Load native code from libbenchmark.so
System.loadLibrary("times");
}
static float userTime;
static float systemTime;
/**
* Takes a "snapshot" of the system. Reads various items from the result of
* <tt>times</tt>.
*
* @return <tt>true</tt> if everything is successful
*/
public static native boolean snapshot();
/**
* Returns the user time used by this process in seconds.
*/
public static float userTime() {
return (Times.userTime);
}
/**
* Returns the system time used by this process in seconds.
*/
public static float systemTime() {
return (Times.systemTime);
}
/**
* Test program.
*/
public static void main(final String[] args) throws Exception {
System.out.println("Starting Test");
if (Times.snapshot() == false) {
System.err.println("Error during snapshot");
System.exit(1);
}
System.out.println("System time: " + Times.systemTime());
System.out.println("User time: " + Times.userTime());
System.out.println("Ending Test");
}
}

@ -0,0 +1,260 @@
/*
* This C file provides the implementation of
* EDU.purdue.cs.bloat.Benchmark's native run method. It is loosely
* based on the "timeit" example found in:
* /u/u83/pps/perfmon/examples/
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/processor.h>
#include <sys/procset.h>
#include <sys/wait.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
#include <jni.h>
#include "EDU_purdue_cs_bloat_benchmark_Benchmark.h"
#include "perfmon.h"
#define MAIN_NAME "main"
#define MAIN_SIG "([Ljava/lang/String;)V"
#define FALSE 0
#define TRUE (!FALSE)
#define PIC_SNAPSHOT_INTERVAL 5
#define check_syscall(exp) \
do { if ((exp) == -1) die_with_errno(#exp, __FILE__, __LINE__); } while(0);
#ifndef U
#define U(a) a
#endif
/* Global locations for holding PIC values */
static unsigned long long pic0_counter; /* Cumulative PIC0 */
static unsigned long long pic1_counter; /* Cumulative PIC1 */
static unsigned long pic0_interval; /* Change since last alarm */
static unsigned long pic1_interval;
/* Other interesting global variables */
static int snapshotting = TRUE;
static int want_realtime = FALSE;
static int processor = 0; /* Processor we use */
static int mode; /* Determines what we count */
/* Stuff for running method */
static jmethodID method; /* Pointer to main method */
/* Error reporting function */
static void
die_with_errno(const char* s, const char* file, int line)
{
fprintf( stderr, "%s:%d:%s:%s\n", file, line, s, strerror(errno) );
exit(1);
}
/* Functions for reading the PIC registers and keeping a running total
* in memory. Called before the Java program is run and when the
* alarm goes off (i.e. when a "snapshot is taken").
*/
static void start_pic_snapshot() {
unsigned long long pic;
pic = get_pic();
pic0_interval -= extract_pic0( pic );
pic1_interval -= extract_pic1( pic );
}
/* Computes the difference in the PIC between now and the last
* "snapshot" (i.e. the "interval"). Adds increments the overall
* counts by these values. Resets the interval.
*/
static void end_pic_snapshot() {
unsigned long long pic;
pic = get_pic();
pic0_interval += extract_pic0( pic );
pic1_interval += extract_pic1( pic );
pic0_counter += (unsigned long long)pic0_interval;
pic1_counter += (unsigned long long)pic1_interval;
pic0_interval = pic1_interval = 0;
}
/* Function to catch interrupts and update the PIC values. This is
* called whenever the alarm "goes off".
*/
static void alarm_handler( int U(sig),
siginfo_t* U(info),
ucontext_t* U(ctxt) )
{
if ( snapshotting ) {
end_pic_snapshot();
start_pic_snapshot();
alarm(PIC_SNAPSHOT_INTERVAL);
}
}
/*
* Performs initialization necessary for running a Java program
* multiple times and monitoring what goes on using perfmon. For
* instance, we obtain a pointer to the main method, set up the alarm
* stuff, and initialize perfmon.
*
* Runs a Java program (a main method) with the perfmon counters
* turned on. We have to be careful to make sure that we correctly
* account for counters that may overflow. Thus, we set an alarm to
* go off every five seconds and record the counters. The code that
* handles all of the alarm stuff was contributed by Kevin Corry.
*
* Parameters:
* env JNI Environment
* clazz Reference to EDU.purdue.cs.bloat.benchmark.Benchmark
* main Class containing main method
* args Arguments to main method
*/
JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Benchmark_init(
JNIEnv *env, jclass clazz, jclass main)
{
unsigned long long pcr;
int fd;
struct sigaction act;
(*env)->ExceptionClear(env);
method = (*env)->GetStaticMethodID(env, main, MAIN_NAME, MAIN_SIG);
if ((*env)->ExceptionOccurred(env) != NULL) {
fprintf(stderr, "Method not found: %s%s\n", MAIN_NAME, MAIN_SIG);
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
return;
}
/* Determine which things we want to count */
if (mode == 0) {
/* Count load interlock induced stalls and instructions. */
pcr = PCR_USER_TRACE | PCR_S0_STALL_LOAD | PCR_S1_INSTR_CNT;
} else if (mode == 1) {
/* Count data cache hit rate. */
pcr = PCR_USER_TRACE | PCR_S0_DC_READ | PCR_S1_DC_READ_HIT;
} else if (mode == 2) {
/* Count icache miss induced stalls and cycles. */
pcr = PCR_USER_TRACE | PCR_S0_STALL_IC_MISS | PCR_S1_CYCLE_CNT;
} else if (mode == 3) {
/* Count instructions and cycles. */
pcr = PCR_USER_TRACE | PCR_S0_INSTR_CNT | PCR_S1_CYCLE_CNT;
} else {
fprintf(stderr, "invalid mode: %d, must be [012]\n", mode);
exit(1);
}
/* Set up perfmon. Bind this thread to a processor so that it can't
get away from us. Flush the cache for the heck of it. */
check_syscall(processor_bind(P_PID, P_MYID, processor, 0));
check_syscall(fd = open( "/dev/perfmon", O_RDONLY));
check_syscall(ioctl(fd, PERFMON_FLUSH_CACHE ));
check_syscall(ioctl(fd, PERFMON_SETPCR, &pcr));
check_syscall(close(fd));
/* Set up the periodic interrupts to make measurements. This way
we don't have to worry about the PIC counters wrapping around. */
act.sa_handler = 0;
act.sa_sigaction = (void(*)(int,siginfo_t*,void*))alarm_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
check_syscall(sigaction(SIGALRM, &act, 0));
}
/*
* Actually runs the program and takes measurements before and after.
* Prints the results out.
*/
JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Benchmark_run(
JNIEnv *env, jclass clazz, jclass main, jobjectArray args) {
pid_t pid;
hrtime_t starttime;
hrtime_t endtime;
unsigned long long starttick;
unsigned long long endtick;
/* Initialize counters and intervals */
snapshotting = TRUE;
pic0_counter = pic1_counter = 0LL;
pic0_interval = pic1_interval = 0;
/* Print this guy. I'm not too sure why. */
fprintf(stderr, "0x%x\n", &gethrtime);
/* Take an initial reading of the PICs */
fprintf(stderr, "reset\n");
cpu_sync();
clr_pic();
start_pic_snapshot();
starttick = get_tick(); /* Get number of cycles since power-on */
starttime = gethrtime(); /* Get the (high-resolution) time */
alarm(PIC_SNAPSHOT_INTERVAL); /* Set the alarm */
check_syscall(processor_bind(P_PID, P_MYID, processor, 0));
/* Run the Java program (benchmark). */
(*env)->CallStaticVoidMethod(env, main, method, args);
/* Get whatever data from the counters and report it. */
cpu_sync();
snapshotting = FALSE; /* No more snapshooting */
end_pic_snapshot();
alarm(0); /* Turn off alarm */
endtime = gethrtime(); /* Get time */
endtick = get_tick(); /* Get number of cycles since power-on */
fprintf(stderr, "wall time %llu ns\n", endtime-starttime);
fprintf(stderr, "ticks %llu\n", endtick-starttick);
if (mode == 0) {
/* Count load stalls and instructions. */
fprintf(stderr, "load stalls %llu\n", pic0_counter);
fprintf(stderr, "insts %llu\n", pic1_counter);
}
else if (mode == 1) {
/* Count data cache hit rate. */
fprintf(stderr, "D-cache reads %llu\n", pic0_counter);
fprintf(stderr, "D-cache hits %llu\n", pic1_counter);
}
else if (mode == 2) {
/* Count I-cache miss stalls and cycles. */
fprintf(stderr, "I-miss stalls %llu\n", pic0_counter);
fprintf(stderr, "cycles %llu\n", pic1_counter);
}
else if (mode == 3) {
/* Count instructions and cycles. */
fprintf(stderr, "insts %llu\n", pic0_counter);
fprintf(stderr, "cycles %llu\n", pic1_counter);
}
fflush(stderr);
}
/* Set the mode. The mode tells us which things to count.
*/
JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Benchmark_setMode(
JNIEnv *env, jclass clazz, jint m)
{
mode = m;
}

@ -0,0 +1,10 @@
<html>
<body>
<p>Contains classes that are used to run benchmarks that measure the
performance of BLOATed code. These classes allow us to do things like
run a program several times in the same virtual machine
invocation.</p>
</body>
</html>

@ -0,0 +1,39 @@
#include <jni.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/time.h>
#include "EDU_purdue_cs_bloat_benchmark_Shade.h"
#define MAIN_NAME "main"
#define MAIN_SIG "([Ljava/lang/String;)V"
JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Shade_run(
JNIEnv *env, jclass clazz, jclass main, jobjectArray args, jboolean quit)
{
jmethodID method;
(*env)->ExceptionClear(env);
method = (*env)->GetStaticMethodID(env, main, MAIN_NAME, MAIN_SIG);
if ((*env)->ExceptionOccurred(env) != NULL) {
fprintf(stderr, "Method not found: %s%s\n", MAIN_NAME, MAIN_SIG);
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
return;
}
system("sh ./run_pre");
if (quit) {
fprintf(stderr, "0x%x\n", &gethrtime);
fflush(stderr);
return;
}
gethrtime();
(*env)->CallStaticVoidMethod(env, main, method, args);
gethrtime();
}

@ -0,0 +1,35 @@
#include <jni.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/time.h>
#include <stdio.h>
#include "EDU_purdue_cs_bloat_benchmark_Stats.h"
extern int instruction_count[256][256];
extern int redundant_count[256];
#define MAIN_NAME "main"
#define MAIN_SIG "([Ljava/lang/String;)V"
JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Stats_run(
JNIEnv *env, jclass clazz, jclass main, jobjectArray args) {
jmethodID method;
(*env)->ExceptionClear(env);
method = (*env)->GetStaticMethodID(env, main, MAIN_NAME, MAIN_SIG);
if ((*env)->ExceptionOccurred(env) != NULL) {
fprintf(stderr, "Method not found: %s%s\n", MAIN_NAME, MAIN_SIG);
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
return;
}
system("sh ./run_pre");
memset(instruction_count, 0, sizeof(instruction_count));
memset(redundant_count, 0, sizeof(redundant_count));
(*env)->CallStaticVoidMethod(env, main, method, args);
}

@ -0,0 +1,371 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.cfg;
import java.util.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.tree.*;
import EDU.purdue.cs.bloat.util.*;
/**
* <tt>Block</tt> represents a basic block of code used in control flow
* graphs. A basic block is always entered at its beginning and exits at its
* end. That is, its first statement is a label and its last statement is a
* jump. There are no other labels or jumps in between.
* <p>
* Each <tt>Block</tt> knows its parent block and its children in the
* dominator and postdominator trees. It also knows which blocks are in its
* dominance frontier and its postdominance frontier.
*
* @see FlowGraph
* @see DominatorTree
* @see DominanceFrontier
*/
public class Block extends GraphNode {
// There are several "types" of Blocks. A NON_HEADER block is not the
// header of a loop. An IRREDUCIBLE block is one of the headers of an
// irreducible loop. An irriducible loop has more than one entry
// point. They are very rare and are really ugly. The loop
// transformer tries to fix up mutiple headers. A REDUCIBLE header is
// a header for a reducible loop.
public static final int NON_HEADER = 0;
public static final int IRREDUCIBLE = 1;
public static final int REDUCIBLE = 2;
FlowGraph graph; // CFG to which this Block belongs
Label label; // This Block's Label
Tree tree; // Expression tree for this block
Block domParent; // Block that (immediately) dominates this Block
Block pdomParent;
Set domChildren; // Blocks that this Block dominates
Set pdomChildren; // The postdominator children of this block
Set domFrontier; // This Block's dominance frontier
Set pdomFrontier; // This Block's postdominace frontier
int blockType; // NON_HEADER, IRREDUCIBLE, or REDUCIBLE
Block header; // The block's loop header
StackOptimizer stackOptimizer; // Stack Optimizer
/**
* Constructor.
*
* @param label
* The block's label. The label may be thought of as the line of
* code at which the block begins.
* @param graph
* The CFG containing the block.
*/
Block(final Label label, final FlowGraph graph) {
this.label = label;
this.graph = graph;
this.tree = null;
this.header = null;
this.blockType = Block.NON_HEADER;
label.setStartsBlock(true);
domParent = null;
pdomParent = null;
domChildren = new HashSet();
pdomChildren = new HashSet();
domFrontier = new HashSet();
pdomFrontier = new HashSet();
stackOptimizer = new StackOptimizer(this); // make StackOptimizer
// object
}
/**
* Returns the stack optimizer for this block.
*
* @return The stack optimizer.
*/
public StackOptimizer stackOptimizer() {
return stackOptimizer;
}
/**
* Returns the expression tree for this block.
*
* @return The tree.
*/
public Tree tree() {
return tree;
}
/**
* Sets the expression tree for this block.
*/
public void setTree(final Tree tree) {
this.tree = tree;
}
/**
* Returns the CFG containing the block.
*
* @return The CFG.
*/
public FlowGraph graph() {
return graph;
}
/**
* Returns the label associated with this block.
*/
public Label label() {
return label;
}
/**
* Visits the expression tree contained in this block.
*/
public void visitChildren(final TreeVisitor visitor) {
if (tree != null) {
tree.visit(visitor);
}
}
public void visit(final TreeVisitor visitor) {
visitor.visitBlock(this);
}
/**
* Sets the type of this Block. A Block may have one of three types:
*
* <ul>
* <li><tt>NON_HEADER</tt>: Not the header of any loop
* <li><tt>IRREDUCIBLE</tt>: Header of an irreducible loop
* <li><tt>REDUCIBLE</tt>: Header of a reducible loop
* </ul>
*
* A <i>loop</i> is a strongly connected component of a control flow graph.
* A loop's <i>header</i> is the block that dominates all other blocks in
* the loop. A loop is <i>reducible</i> if the only way to enter the loop
* is through the header.
*/
void setBlockType(final int blockType) {
this.blockType = blockType;
if (FlowGraph.DEBUG) {
System.out.println(" Set block type " + this);
}
}
/**
* Returns the type of this Block.
*/
int blockType() {
return blockType;
}
public void setHeader(final Block header) {
this.header = header;
if (FlowGraph.DEBUG) {
System.out.println(" Set header " + this);
}
}
public Block header() {
return header;
}
/**
* Returns a string representation of this block.
*/
public String toString() {
String s = "<block " + label + " hdr=";
if (header != null) {
s += header.label();
} else {
s += "null";
}
switch (blockType) {
case NON_HEADER:
break;
case IRREDUCIBLE:
s += " irred";
break;
case REDUCIBLE:
s += " red";
break;
}
if (this == graph.source()) {
return s + " source>";
} else if (this == graph.init()) {
return s + " init>";
} else if (this == graph.sink()) {
return s + " sink>";
} else {
return s + ">";
}
}
/**
* Returns the basic blocks that this Block immediately dominates. That is,
* it returns this Block's children in the dominator tree for the CFG.
*/
Collection domChildren() {
return domChildren;
}
/**
* Returns the immediate dominator of this Block. That is, it returns the
* Block's parent in the dominator tree, its immediate dominator.
*/
Block domParent() {
return domParent;
}
/**
* Specifies that Block dominates this Block (parent in the dominator tree,
* the immediate dominator).
*
* @param block
* Block that dominates this Block.
*/
void setDomParent(final Block block) {
// If this Block already had a dominator specified, remove
// it from its dominator's children.
if (domParent != null) {
domParent.domChildren.remove(this);
}
domParent = block;
// Add this Block to its new dominator's children.
if (domParent != null) {
domParent.domChildren.add(this);
}
}
/**
* Returns whether or this Block dominates another given Block. A node X
* dominates a node Y when every path from the first node in the CFG (Enter)
* to Y must pass through X.
*/
public boolean dominates(final Block block) {
Block p = block;
while (p != null) {
if (p == this) {
return true;
}
p = p.domParent();
}
return false;
}
/**
* Returns the children of this Block in the CFG's postdominator tree.
*/
Collection pdomChildren() {
return pdomChildren;
}
/**
* Returns the parent of this Block in the CFG's postdominator tree.
*/
Block pdomParent() {
return pdomParent;
}
/**
* Sets this Block's parent in the postdominator tree.
*/
void setPdomParent(final Block block) {
if (pdomParent != null) {
pdomParent.pdomChildren.remove(this);
}
pdomParent = block;
if (pdomParent != null) {
pdomParent.pdomChildren.add(this);
}
}
/**
* Determines whether or not this block postdominates a given block. A block
* X is said to postdominate a block Y when every path from Y to the last
* node in the CFG (Exit) passes through X. This relationship can be thought
* of as the reverse of dominance. That is, X dominates Y in the reverse
* CFG.
*
* @see DominatorTree
*/
public boolean postdominates(final Block block) {
Block p = block;
while (p != null) {
if (p == this) {
return true;
}
p = p.pdomParent();
}
return false;
}
/**
* Returns the blocks that are in this block's dominance frontier. The
* dominance frontier of a node X in a CFG is the set of all nodes Y such
* that X dominates a predacessor of Y, but does not strictly dominate Y.
* Nodes in the dominance frontier always have more than one parent (a
* join).
*
* @see DominanceFrontier
*/
Collection domFrontier() {
Assert.isTrue(domFrontier != null);
return domFrontier;
}
/**
* Returns the postdominance frontier for this node. A postdominace frontier
* is essentially the same as a dominace frontier, but the postdominance
* relationship is used instead of the dominance relationship.
*/
Collection pdomFrontier() {
Assert.isTrue(pdomFrontier != null);
return pdomFrontier;
}
}

@ -0,0 +1,158 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.cfg;
import java.util.*;
/**
* <tt>DominanceFrontier</tt> is used to calculate the <i>dominance frontier</i>
* of each node in a control flow graph.
* <p>
* The <i>dominance frontier</i> of a node x is the set of all nodes w such
* that x dominates a predacessor of w, but does not strictly dominate w.
* Basically, nodes in the dominance frontier have one parent that <b>is</b>
* dominated by x and at least one parent that <b>is not</b> dominated by x.
* <p>
* <tt>DominanceFrontier</tt> can be used to calculate both the dominance
* (forward) and the postdominance (reverse) frontiers for a control flow graph.
*
* @see FlowGraph
*/
public class DominanceFrontier {
/**
* Calculates the dominance frontier for a cfg and notifies the blocks in it
* appropriately.
*
* @param graph
* The cfg to operate on
* @param reverse
* Do we calculate the postdominance frontier?
*/
public static void buildFrontier(final FlowGraph graph, boolean reverse) {
if (!reverse) {
DominanceFrontier.calcFrontier(graph.source(), graph, reverse);
} else {
DominanceFrontier.calcFrontier(graph.sink(), graph, reverse);
}
}
/**
* Recursively traverses the cfg and builds up the dominance frontier.
* <p>
* A block n's dominance frontier is the union of two sets of nodes. The
* first set is the nodes in the dominance frontier of the nodes that n
* dominates that are not dominated by n's immediate dominator. The second
* set consists of the successors of n that are not strictly dominated by n.
*
* @param block
* The block to start from (either source or sink)
* @param graph
* The cfg from which to get blocks
* @param reverse
* Do we calculate the dominance or postdominance frontier?
*
* @return The blocks in the (post)dominance frontier of block
*/
private static LinkedList calcFrontier(final Block block,
final FlowGraph graph, boolean reverse) {
// local is an array of Blocks that are in block's dominance
// frontier. It is indexed by the block's pre-order index. I
// suppose an array is used so that no block is added to the
// dominance frontier twice.
final Block[] local = new Block[graph.size()];
Iterator children; // The blocks that are dominated by block
if (!reverse) {
children = block.domChildren().iterator();
} else {
children = block.pdomChildren().iterator();
}
// Recursively calculate the nodes in the dominance frontier of
// block that are not dominated by block's immediate dominator
while (children.hasNext()) {
final Block child = (Block) children.next();
final LinkedList df = DominanceFrontier.calcFrontier(child, graph,
reverse);
final Iterator e = df.iterator();
while (e.hasNext()) {
final Block dfChild = (Block) e.next();
if (!reverse) {
if (block != dfChild.domParent()) {
local[graph.preOrderIndex(dfChild)] = dfChild;
}
} else {
if (block != dfChild.pdomParent()) {
local[graph.preOrderIndex(dfChild)] = dfChild;
}
}
}
}
final Iterator succs = reverse ? graph.preds(block).iterator() : graph
.succs(block).iterator();
// Caculate the successors of block that are not strictly
// dominated by block.
while (succs.hasNext()) {
final Block succ = (Block) succs.next();
// If block is not the immediate (post)dominator of its
// successor, add it to block's dominance frontier.
if (!reverse) {
if (block != succ.domParent()) {
local[graph.preOrderIndex(succ)] = succ;
}
} else {
if (block != succ.pdomParent()) {
local[graph.preOrderIndex(succ)] = succ;
}
}
}
final LinkedList v = new LinkedList(); // The dominance frontier
for (int i = 0; i < local.length; i++) {
if (local[i] != null) {
v.add(local[i]);
}
}
// Set block's (post)dominance frontier
if (!reverse) {
block.domFrontier().clear();
block.domFrontier().addAll(v);
} else {
block.pdomFrontier().clear();
block.pdomFrontier().addAll(v);
}
return v;
}
}

@ -0,0 +1,387 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.cfg;
import java.util.*;
import EDU.purdue.cs.bloat.util.*;
/**
* DominatorTree finds the dominator tree of a FlowGraph.
* <p>
* The algorithm used is Purdum-Moore. It isn't as fast as Lengauer-Tarjan, but
* it's a lot simpler.
*
* @see FlowGraph
* @see Block
*/
public class DominatorTree {
public static boolean DEBUG = false;
/**
* Calculates what vertices dominate other verices and notify the basic
* Blocks as to who their dominator is.
*
* @param graph
* The cfg that is used to find the dominator tree.
* @param reverse
* Do we go in revsers? That is, are we computing the dominatance
* (false) or postdominance (true) tree.
* @see Block
*/
public static void buildTree(final FlowGraph graph, boolean reverse) {
final int size = graph.size(); // The number of vertices in the cfg
final Map snkPreds = new HashMap(); // The predacessor vertices from the
// sink
// Determine the predacessors of the cfg's sink node
DominatorTree.insertEdgesToSink(graph, snkPreds, reverse);
// Get the index of the root
final int root = reverse ? graph.preOrderIndex(graph.sink()) : graph
.preOrderIndex(graph.source());
Assert.isTrue((0 <= root) && (root < size));
// Bit matrix indicating the dominators of each vertex.
// If bit j of dom[i] is set, then node j dominates node i.
final BitSet[] dom = new BitSet[size];
// A bit vector of all 1's
final BitSet ALL = new BitSet(size);
for (int i = 0; i < size; i++) {
ALL.set(i);
}
// Initially, all the bits in the dominance matrix are set, except
// for the root node. The root node is initialized to have itself
// as an immediate dominator.
//
for (int i = 0; i < size; i++) {
final BitSet blockDoms = new BitSet(size);
dom[i] = blockDoms;
if (i != root) {
blockDoms.or(ALL);
} else {
blockDoms.set(root);
}
}
// Did the dominator bit vector array change?
boolean changed = true;
while (changed) {
changed = false;
// Get the basic blocks contained in the cfg
final Iterator blocks = reverse ? graph.postOrder().iterator()
: graph.preOrder().iterator();
// Compute the dominators of each node in the cfg. We iterate
// over every node in the cfg. The dominators of a node, x, are
// found by taking the intersection of the dominator bit vectors
// of each predacessor of x and unioning that with x. This
// process is repeated until no changes are made to any dominator
// bit vector.
while (blocks.hasNext()) {
final Block block = (Block) blocks.next();
final int i = graph.preOrderIndex(block);
Assert.isTrue((0 <= i) && (i < size), "Unreachable block "
+ block);
// We already know the dominators of the root, keep looking
if (i == root) {
continue;
}
final BitSet oldSet = dom[i];
final BitSet blockDoms = new BitSet(size);
blockDoms.or(oldSet);
// print(graph, reverse, "old set", i, blockDoms);
// blockDoms := intersection of dom(pred) for all pred(block).
Collection preds = reverse ? graph.succs(block) : graph
.preds(block);
Iterator e = preds.iterator();
// Find the intersection of the dominators of block's
// predacessors.
while (e.hasNext()) {
final Block pred = (Block) e.next();
final int j = graph.preOrderIndex(pred);
Assert.isTrue(j >= 0, "Unreachable block " + pred);
blockDoms.and(dom[j]);
}
// Don't forget to account for the sink node if block is a
// leaf node. Appearantly, there are not edges between
// leaf nodes and the sink node!
preds = (Collection) snkPreds.get(block);
if (preds != null) {
e = preds.iterator();
while (e.hasNext()) {
final Block pred = (Block) e.next();
final int j = graph.preOrderIndex(pred);
Assert.isTrue(j >= 0, "Unreachable block " + pred);
blockDoms.and(dom[j]);
}
}
// Include yourself in your dominators?!
blockDoms.set(i);
// print(graph, reverse, "intersecting " + preds, i, blockDoms);
// If the set changed, set the changed bit.
if (!blockDoms.equals(oldSet)) {
changed = true;
dom[i] = blockDoms;
}
}
}
// Once we have the predacessor bit vectors all squared away, we can
// determine which vertices dominate which vertices.
Iterator blocks = graph.nodes().iterator();
// Initialize each block's (post)dominator parent and children
while (blocks.hasNext()) {
final Block block = (Block) blocks.next();
if (!reverse) {
block.setDomParent(null);
block.domChildren().clear();
} else {
block.setPdomParent(null);
block.pdomChildren().clear();
}
}
blocks = graph.nodes().iterator();
// A block's immediate dominator is its closest dominator. So, we
// start with the dominators, dom(b), of a block, b. To find the
// imediate dominator of b, we remove all blocks from dom(b) that
// dominate any block in dom(b).
while (blocks.hasNext()) {
final Block block = (Block) blocks.next();
final int i = graph.preOrderIndex(block);
Assert.isTrue((0 <= i) && (i < size), "Unreachable block " + block);
if (i == root) {
if (!reverse) {
block.setDomParent(null);
} else {
block.setPdomParent(null);
}
} else {
// Find the immediate dominator
// idom := dom(block) - dom(dom(block)) - block
final BitSet blockDoms = dom[i];
// print(graph, reverse, "dom set", i, blockDoms);
final BitSet idom = new BitSet(size);
idom.or(blockDoms);
idom.clear(i);
for (int j = 0; j < size; j++) {
if ((i != j) && blockDoms.get(j)) {
final BitSet domDomBlocks = dom[j];
// idom = idom - (domDomBlocks - {j})
final BitSet b = new BitSet(size);
b.or(domDomBlocks);
b.xor(ALL);
b.set(j);
idom.and(b);
// print(graph, reverse,
// "removing dom(" + graph.preOrder().get(j) +")",
// i, idom);
}
}
Block parent = null;
// A block should only have one immediate dominator.
for (int j = 0; j < size; j++) {
if (idom.get(j)) {
final Block p = (Block) graph.preOrder().get(j);
Assert.isTrue(parent == null, block
+ " has more than one immediate dominator: "
+ parent + " and " + p);
parent = p;
}
}
Assert.isTrue(parent != null, block + " has 0 immediate "
+ (reverse ? "postdominators" : "dominators"));
if (!reverse) {
if (DominatorTree.DEBUG) {
System.out.println(parent + " dominates " + block);
}
block.setDomParent(parent);
} else {
if (DominatorTree.DEBUG) {
System.out.println(parent + " postdominates " + block);
}
block.setPdomParent(parent);
}
}
}
}
/**
* Determines which nodes are predacessors of a cfg's sink node. Creates a
* Map that maps the sink node to its predacessors (or the leaf nodes to the
* sink node, their predacessor, if we're going backwards).
*
* @param graph
* The cfg to operate on.
* @param preds
* A mapping from leaf nodes to their predacessors. The exact
* semantics depend on whether or not we are going forwards.
* @param reverse
* Are we computing the dominators or postdominators?
*/
private static void insertEdgesToSink(final FlowGraph graph,
final Map preds, final boolean reverse) {
final BitSet visited = new BitSet(); // see insertEdgesToSinkDFS
final BitSet returned = new BitSet();
visited.set(graph.preOrderIndex(graph.source()));
DominatorTree.insertEdgesToSinkDFS(graph, graph.source(), visited,
returned, preds, reverse);
}
/**
* This method determines which nodes are the predacessor of the sink node
* of a cfg. A depth-first traversal of the cfg is performed. When a leaf
* node (that is not the sink node) is encountered, add an entry to the
* preds Map.
*
* @param graph
* The cfg being operated on.
* @param block
* The basic Block to start at.
* @param visited
* Vertices that were visited
* @param returned
* Vertices that returned
* @param preds
* Maps a node to a HashSet representing its predacessors. In the
* case that we're determining the dominace tree, preds maps the
* sink node to its predacessors. In the case that we're
* determining the postdominance tree, preds maps the sink node's
* predacessors to the sink node.
* @param reverse
* Do we go in reverse?
*/
private static void insertEdgesToSinkDFS(final FlowGraph graph,
final Block block, final BitSet visited, final BitSet returned,
final Map preds, boolean reverse) {
boolean leaf = true; // Is a vertex a leaf node?
// Get the successors of block
final Iterator e = graph.succs(block).iterator();
while (e.hasNext()) {
final Block succ = (Block) e.next();
// Determine index of succ vertex in a pre-order traversal
final int index = graph.preOrderIndex(succ);
Assert.isTrue(index >= 0, "Unreachable block " + succ);
if (!visited.get(index)) {
// If the successor block hasn't been visited, visit it
visited.set(index);
DominatorTree.insertEdgesToSinkDFS(graph, succ, visited,
returned, preds, reverse);
returned.set(index);
leaf = false;
} else if (returned.get(index)) {
// Already visited and returned, so a descendent of succ
// has an edge to the sink.
leaf = false;
}
}
if (leaf && (block != graph.sink())) {
// If we're dealing with a leaf node that is not the sink, set
// up its predacessor set.
if (!reverse) {
// If we're going forwards (computing dominators), get the
// predacessor vertices from the sink
Set p = (Set) preds.get(graph.sink());
// If there are no (known) predacessors, make a new HashSet to
// store them and register it in the pred Map.
if (p == null) {
p = new HashSet();
preds.put(graph.sink(), p);
}
// The block is in the predacessors of the sink
p.add(block);
} else {
// If we're going backwards, get the block's predacessors
Set p = (Set) preds.get(block);
if (p == null) {
p = new HashSet();
preds.put(block, p);
}
// Add the sink vertex to the predacessors of the block
p.add(graph.sink());
}
}
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,79 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.cfg;
import java.util.*;
import EDU.purdue.cs.bloat.editor.*;
/**
* <tt>Handler</tt> represents a try-catch block. It containes a set of
* protected <tt>Block</tt>s (the "try" blocks), a catch <tt>Block</tt>,
* and the <tt>Type</tt> of exception that is caught by the catch block.
*
* @see Block
* @see EDU.purdue.cs.bloat.reflect.Catch
* @see EDU.purdue.cs.bloat.editor.TryCatch
*/
public class Handler {
Set protectedBlocks;
Block catchBlock;
Type type;
/**
* Constructor.
*
* @param catchBlock
* The block of code that handles an exception
* @param type
* The type of exception that is thrown
*/
public Handler(final Block catchBlock, final Type type) {
this.protectedBlocks = new HashSet();
this.catchBlock = catchBlock;
this.type = type;
}
/**
* Returns a <tt>Collection</tt> of the "try" blocks.
*/
public Collection protectedBlocks() {
return protectedBlocks;
}
public void setCatchBlock(final Block block) {
catchBlock = block;
}
public Block catchBlock() {
return catchBlock;
}
public Type catchType() {
return type;
}
public String toString() {
return "try -> catch (" + type + ") " + catchBlock;
}
}

@ -0,0 +1,28 @@
# All files in the distribution of BLOAT (Bytecode Level Optimization and
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
# Research Foundation of Purdue University. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
CLASS = \
Block.class\
DominanceFrontier.class\
DominatorTree.class\
FlowGraph.class\
ReplaceTarget.class\
Subroutine.class\
VerifyCFG.class
include ../class.mk

@ -0,0 +1,167 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.cfg;
import java.util.*;
import EDU.purdue.cs.bloat.tree.*;
/**
* <tt>ReplaceTarget</tt> replaces the block that is the target of a
* <tt>JumpStmt</tt>, <tt>JsrStmt</tt>, <tt>RetStmt</tt>,
* <tt>GotoStmt</tt>, <tt>SwitchStmt</tt>, or <tt>IfStmt</tt> with
* another <tt>Block</tt>.
*/
public class ReplaceTarget extends TreeVisitor {
Block oldDst;
Block newDst;
public ReplaceTarget(final Block oldDst, final Block newDst) {
this.oldDst = oldDst;
this.newDst = newDst;
}
public void visitTree(final Tree tree) {
final Stmt last = tree.lastStmt();
if (last instanceof JumpStmt) {
final JumpStmt stmt = (JumpStmt) last;
if (FlowGraph.DEBUG) {
System.out.println(" Replacing " + oldDst + " with " + newDst
+ " in " + stmt);
}
if (stmt.catchTargets().remove(oldDst)) {
stmt.catchTargets().add(newDst);
}
stmt.visit(this);
}
}
public void visitJsrStmt(final JsrStmt stmt) {
if (stmt.sub().entry() == oldDst) {
if (FlowGraph.DEBUG) {
System.out.print(" replacing " + stmt);
}
stmt.block().graph().setSubEntry(stmt.sub(), newDst);
if (FlowGraph.DEBUG) {
System.out.println(" with " + stmt);
}
}
}
public void visitRetStmt(final RetStmt stmt) {
final Iterator paths = stmt.sub().paths().iterator();
while (paths.hasNext()) {
final Block[] path = (Block[]) paths.next();
if (FlowGraph.DEBUG) {
System.out.println(" path = " + path[0] + " " + path[1]);
}
if (path[1] == oldDst) {
if (FlowGraph.DEBUG) {
System.out.println(" replacing ret to " + oldDst
+ " with ret to " + newDst);
}
path[1] = newDst;
((JsrStmt) path[0].tree().lastStmt()).setFollow(newDst);
}
}
}
public void visitGotoStmt(final GotoStmt stmt) {
if (stmt.target() == oldDst) {
if (FlowGraph.DEBUG) {
System.out.print(" replacing " + stmt);
}
stmt.setTarget(newDst);
if (FlowGraph.DEBUG) {
System.out.println(" with " + stmt);
}
}
}
public void visitSwitchStmt(final SwitchStmt stmt) {
if (stmt.defaultTarget() == oldDst) {
if (FlowGraph.DEBUG) {
System.out.print(" replacing " + stmt);
}
stmt.setDefaultTarget(newDst);
if (FlowGraph.DEBUG) {
System.out.println(" with " + stmt);
}
}
final Block[] targets = stmt.targets();
for (int i = 0; i < targets.length; i++) {
if (targets[i] == oldDst) {
if (FlowGraph.DEBUG) {
System.out.print(" replacing " + stmt);
}
targets[i] = newDst;
if (FlowGraph.DEBUG) {
System.out.println(" with " + stmt);
}
}
}
}
public void visitIfStmt(final IfStmt stmt) {
if (stmt.trueTarget() == oldDst) {
if (FlowGraph.DEBUG) {
System.out.print(" replacing " + stmt);
}
stmt.setTrueTarget(newDst);
if (FlowGraph.DEBUG) {
System.out.println(" with " + stmt);
}
}
if (stmt.falseTarget() == oldDst) {
if (FlowGraph.DEBUG) {
System.out.print(" replacing " + stmt);
}
stmt.setFalseTarget(newDst);
if (FlowGraph.DEBUG) {
System.out.println(" with " + stmt);
}
}
}
}

@ -0,0 +1,259 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.cfg;
import java.io.*;
import java.util.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.tree.*;
/**
* Subroutine represents a subroutine (target of a <i>jsr</i> instruction) in
* java bytecode. Subroutines are used to implement the finally part of a
* try-catch-finally block.
* <p>
* Each Subroutine belongs in a control flow graph, has an entry and exit block,
* and has a local variable that contains its return address. Additionally, it
* maintains a list of paths from blocks in which the subroutine is called to
* block that is executed after the subroutine returns.
* <p>
* Note that it is assumed that each subroutine ends with a <i>ret</i>. While
* this is true for bytecode generated by javac, it is not required.
*
* @see AddressStoreStmt
* @see Block
*/
// Important: I assume there is a ret statement for each jsr.
// This is true for javac code, but not in general.
public class Subroutine {
FlowGraph graph; // CFG containing this Subroutine
Block entry; // Basic Block at beginning of code
Block exit; // Basic Block ending code
ArrayList paths;
LocalVariable returnAddress; // This Subroutine's return address
/**
* Constructor.
*
* @param graph
* The CFG containing the block.
*/
public Subroutine(final FlowGraph graph) {
this.graph = graph;
this.entry = null;
this.exit = null;
this.paths = new ArrayList();
this.returnAddress = null;
}
/**
* Returns the local variable containing the return address of this
* subroutine.
*/
public LocalVariable returnAddress() {
return returnAddress;
}
/**
* Sets the address (stored in a LocalVariable) to which this subroutine
* will return once it is finished.
*
* @param returnAddress
* Local variable that stores the address to which the subroutine
* returns when it is completed.
*
* @see Tree#visit_astore
*/
public void setReturnAddress(final LocalVariable returnAddress) {
this.returnAddress = returnAddress;
}
/**
* Returns the number of places that this subroutine is called.
*/
public int numPaths() {
return paths.size();
}
/**
* Returns the paths (a Collection of two-element arrays of Blocks) that
* represent the Blocks that end in a call to this subroutine and the block
* that begin with the return address from this subroutine.
*/
public Collection paths() {
return paths;
}
/**
* Returns the CFG that contains this subroutine.
*/
public FlowGraph graph() {
return graph;
}
/**
* Removes all paths involving block regardless of whether it is a calling
* (source) block or a returning (target) block.
*/
public void removePathsContaining(final Block block) {
for (int i = paths.size() - 1; i >= 0; i--) {
final Block[] path = (Block[]) paths.get(i);
if ((path[0] == block) || (path[1] == block)) {
if (FlowGraph.DEBUG) {
System.out.println("removing path " + path[0] + " -> "
+ path[1]);
}
paths.remove(i);
}
}
}
/**
* Removes a path between a caller Block and a return Block.
*/
public void removePath(final Block callerBlock, final Block returnBlock) {
for (int i = 0; i < paths.size(); i++) {
final Block[] path = (Block[]) paths.get(i);
if ((path[0] == callerBlock) && (path[1] == returnBlock)) {
if (FlowGraph.DEBUG) {
System.out.println("removing path " + path[0] + " -> "
+ path[1]);
}
paths.remove(i);
return;
}
}
}
/**
* Removes all caller-return paths.
*/
public void removeAllPaths() {
paths = new ArrayList();
}
/**
* Adds a path from the block before a Subroutine is called to a block after
* the subroutine is called. If the callerBlock is already associated with a
* returnBlock, the old returnBlock is replaced.
*
* @param callerBlock
* The block in which the subroutine is called. This Block ends
* with a <i>jsr</i> to this subroutine.
* @param returnBlock
* The block to which the subroutine returns. This Block begins
* at the return address of this subroutine.
*/
public void addPath(final Block callerBlock, final Block returnBlock) {
for (int i = 0; i < paths.size(); i++) {
final Block[] path = (Block[]) paths.get(i);
if (path[0] == callerBlock) {
path[1] = returnBlock;
return;
}
}
paths.add(new Block[] { callerBlock, returnBlock });
}
/**
* Returns the "return block" for a given "caller block".
*/
public Block pathTarget(final Block block) {
for (int i = 0; i < paths.size(); i++) {
final Block[] path = (Block[]) paths.get(i);
if (path[0] == block) {
return path[1];
}
}
return null;
}
/**
* Returns the "caller block" for a given "return block".
*/
public Block pathSource(final Block block) {
for (int i = 0; i < paths.size(); i++) {
final Block[] path = (Block[]) paths.get(i);
if (path[1] == block) {
return path[0];
}
}
return null;
}
/**
* Sets the entry Block for this Subroutine.
*/
public void setEntry(final Block entry) {
this.entry = entry;
}
/**
* Sets the exit Block for this Subroutine.
*/
public void setExit(final Block exit) {
this.exit = exit;
}
/**
* Returns the first Block in the subroutine.
*/
public Block entry() {
return entry;
}
/**
* Returns the last Block in the subroutine.
*/
public Block exit() {
return exit;
}
/**
* Prints a textual representation of this Subroutine.
*
* @param out
* The PrintStream to which to print.
*/
public void print(final PrintStream out) {
out.println(" " + entry);
final Iterator e = paths().iterator();
while (e.hasNext()) {
final Block[] path = (Block[]) e.next();
out.println(" path: " + path[0] + " -> " + path[1]);
}
}
public String toString() {
return "sub " + entry;
}
}

@ -0,0 +1,384 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.cfg;
import java.util.*;
import EDU.purdue.cs.bloat.tree.*;
import EDU.purdue.cs.bloat.util.*;
/**
* VerifyCFG visits the nodes in a control flow graph and verifies that certain
* properties of the graph are true. For instance, value numbers of expressions
* are not equal to -1, node connections are consistent, exception handlers are
* set up correctly, etc. Mostly used for debugging purposes.
*/
public class VerifyCFG extends TreeVisitor {
Block block; // The Block containing the node being visited
Node parent; // The (expected) parent of the node being visited
FlowGraph cfg; // The CFG being visited
Set uses; // Expressions in which a definition is used
Set nodes; // (Visited) nodes in the CFG
boolean checkValueNumbers; // Do we check the value numbers of expressions?
/**
* Constructor. Don't check value numbers.
*/
public VerifyCFG() {
this(false);
}
/**
* Constructor. Since value numbers are not strictly part of the control
* flow graph, they may or may not be checked. For instance, if a CFG is
* being verfied before value numbers are assigned, we would not want to
* check them.
*
* @param checkValueNumbers
* Are the value numbers of expressions checked?
*/
public VerifyCFG(final boolean checkValueNumbers) {
this.checkValueNumbers = checkValueNumbers;
}
/**
* Visit the blocks and expression trees in a control flow graph. Examine
* the uses of a variable that is defined in the CFG. Make that all uses are
* reachable (i.e. are in the CFG).
*/
public void visitFlowGraph(final FlowGraph cfg) {
if (FlowGraph.DEBUG) {
System.out.println("Verifying CFG for " + cfg.method());
}
this.cfg = cfg;
uses = new HashSet();
nodes = new HashSet();
cfg.visitChildren(this);
final Iterator e = uses.iterator();
while (e.hasNext()) {
final Expr use = (Expr) e.next();
Assert.isTrue(nodes.contains(use), "use = " + use + " ("
+ System.identityHashCode(use) + ") is not in the CFG");
}
if (FlowGraph.DEBUG) {
System.out.println("Verification successful");
}
this.cfg = null;
uses = null;
nodes = null;
block = null;
parent = null;
}
/**
* First make sure that the <tt>Block</tt> indeed is in the CFG. If the
* block begins an exception handler, then make sure that all edges from
* protected blocks lead to the handler block. Also make sure that all of
* the handler block's predacessor lead to protected blocks. Finally, make
* sure that the successor/predacessor relationship holds.
*/
public void visitBlock(final Block block) {
Assert.isTrue(block.graph() == cfg, block + " is not in the CFG");
Iterator e;
final Handler handler = (Handler) cfg.handlersMap().get(block);
// If this block is the first block in an exception handler, make
// sure that the only predacessor edges the block has are
// protected blocks that may throw the exception handled by the
// block. Additionally, we check that there are edges from all
// protected blocks to the handler block.
//
// The predacessor to the first block in an exception handler may
// be the init block. However, it is not really a protected
// block, so just overlook it.
if (handler != null) {
final HashSet handlerPreds = new HashSet();
e = handler.protectedBlocks().iterator();
while (e.hasNext()) {
final Block prot = (Block) e.next();
handlerPreds.add(prot);
handlerPreds.addAll(cfg.preds(prot));
}
final HashSet extra = new HashSet(cfg.preds(block));
extra.removeAll(handlerPreds);
final HashSet missing = new HashSet(handlerPreds);
missing.removeAll(cfg.preds(block));
Assert.isTrue(((extra.size() == 0) && (missing.size() == 0))
|| ((missing.size() == 1) && missing.contains(cfg.init())),
"Handler prots = " + handlerPreds
+ " != handler block preds = " + cfg.preds(block)
+ " extra = " + extra + " missing = " + missing);
}
// Make sure that the predacessor has a successor and vice versa.
e = cfg.preds(block).iterator();
while (e.hasNext()) {
final Block pred = (Block) e.next();
Assert.isTrue(cfg.succs(pred).contains(block), pred
+ " has no succ " + block);
Assert.isTrue(cfg.preds(block).contains(pred), block
+ " has no pred " + pred);
}
e = cfg.succs(block).iterator();
while (e.hasNext()) {
final Block succ = (Block) e.next();
Assert.isTrue(cfg.succs(block).contains(succ), block
+ " has no succ " + succ);
Assert.isTrue(cfg.preds(succ).contains(block), succ
+ " has no pred " + block);
}
this.block = block;
parent = null;
block.visitChildren(this);
}
/**
* Make sure that all of targets of the <tt>ret</tt> are valid. The
* targets are the blocks to which the subroutine can return.
*/
public void visitRetStmt(final RetStmt stmt) {
final Set targets = new HashSet();
final Iterator iter = stmt.sub().paths().iterator();
while (iter.hasNext()) {
final Block[] path = (Block[]) iter.next();
targets.add(path[1]);
}
targets.addAll(stmt.catchTargets());
verifyTargets(stmt.block(), targets);
visitNode(stmt);
}
/**
* Make sure that all of the targets of the <tt>jsr</tt> are valid. The
* only target is the entry block of the subroutine.
*/
public void visitJsrStmt(final JsrStmt stmt) {
final Set targets = new HashSet();
targets.add(stmt.sub().entry());
targets.addAll(stmt.catchTargets());
verifyTargets(stmt.block(), targets);
visitNode(stmt);
}
/**
* Make sure that that all of the targets of the switch are valid.
*/
public void visitSwitchStmt(final SwitchStmt stmt) {
final Set targets = new HashSet();
targets.add(stmt.defaultTarget());
for (int i = 0; i < stmt.targets().length; i++) {
targets.add(stmt.targets()[i]);
}
targets.addAll(stmt.catchTargets());
verifyTargets(stmt.block(), targets);
visitNode(stmt);
}
/**
* Make sure that the targets of the if statement are valid. Targets consist
* of the true target, the false target, and the first blocks of any
* exceptions that may be thrown by the if statement.
*/
public void visitIfStmt(final IfStmt stmt) {
final Set targets = new HashSet();
targets.add(stmt.trueTarget());
targets.add(stmt.falseTarget());
targets.addAll(stmt.catchTargets());
verifyTargets(stmt.block(), targets);
visitNode(stmt);
}
/**
* Make sure that the target of <tt>goto</tt> is valid.
*/
public void visitGotoStmt(final GotoStmt stmt) {
final Set targets = new HashSet();
targets.add(stmt.target());
targets.addAll(stmt.catchTargets());
verifyTargets(stmt.block(), targets);
visitNode(stmt);
}
/**
* Verifies information about the targets of a jump in a given block. First,
* make sure that the number of targets is the same as the number of
* sucessor nodes to the block. Make sure that targets of all of the jumps
* are indeed in the CFG. Make sure that every target is a successor of the
* block.
*/
private void verifyTargets(final Block block, final Set targets) {
Assert.isTrue(targets.size() == cfg.succs(block).size(), block
+ " has succs " + cfg.succs(block) + " != " + targets + " in "
+ block.tree().lastStmt());
final Iterator iter = targets.iterator();
while (iter.hasNext()) {
final Block target = (Block) iter.next();
Assert.isTrue(block.graph().hasNode(target), target
+ " is not in the CFG");
Assert.isTrue(cfg.succs(block).contains(target), target
+ " is not a succ of " + block + " "
+ block.tree().lastStmt());
}
}
/**
* If desired, makes sure that the store expression's value number is not
* -1. Makes sure that the store expression's block and parent <tt>Node</tt>
* are what we expect them to be. If the type of the <tt>StoreExpr</tt> is
* void, then make sure that its parent is an <tt>ExprStmt</tt> (i.e. make
* sure it is not nested within another expression).
*/
public void visitStoreExpr(final StoreExpr node) {
nodes.add(node);
if (checkValueNumbers) {
Assert.isTrue(node.valueNumber() != -1, node
+ ".valueNumber() = -1");
}
Assert.isTrue(node.block() == block, node + ".block() = "
+ node.block() + " != block = " + block);
Assert.isTrue(node.parent() == parent, node + ".parent() = "
+ node.parent() + " != parent = " + parent);
// Visit the MemExpr into which node stores.
parent = node;
node.target().visit(this);
// Visit the expression whose value is being stored by node
parent = node;
node.expr().visit(this);
parent = node.parent();
if (node.type().isVoid()) {
Assert.isTrue(parent instanceof ExprStmt, "parent of " + node
+ " = " + parent + " is not an ExprStmt");
}
}
/**
* Make sure that the <tt>Node</tt> resides in the block that we expect it
* to and that it has the expected parent expression tree <tt>Node</tt>.
* Make sure that the children of this <tt>Node</tt> are also correct.
*/
public void visitNode(final Node node) {
nodes.add(node);
Assert.isTrue(node.block() == block, node + ".block() = "
+ node.block() + " != block = " + block);
Assert.isTrue(node.parent() == parent, node + ".parent() = "
+ node.parent() + " != parent = " + parent);
final ArrayList children = new ArrayList();
node.visitChildren(new TreeVisitor() {
public void visitNode(final Node n) {
children.add(n);
}
});
final Iterator e = children.iterator();
while (e.hasNext()) {
final Node child = (Node) e.next();
parent = node;
child.visit(this);
}
parent = node.parent();
}
/**
* If desired, make sure that the value number of the <tt>Expr</tt> is not
* -1.
*/
public void visitExpr(final Expr expr) {
if (checkValueNumbers) {
Assert.isTrue(expr.valueNumber() != -1, expr
+ ".valueNumber() = -1");
}
visitNode(expr);
}
/**
* Keep track of all the uses of the expression defined by the
* <tt>DefExpr</tt>. This information is used when verifying the
* <tt>FlowGraph</tt>.
*/
public void visitDefExpr(final DefExpr expr) {
uses.addAll(expr.uses());
visitExpr(expr);
}
/**
* Make sure that the <tt>VarExpr</tt> either defines a local variable, is
* defined by another expression, or is the child of a <tt>PhiStmt</tt>
* (therefore making the <tt>VarExpr</tt> a phi-variable).
*/
public void visitVarExpr(final VarExpr expr) {
Assert.isTrue(expr.isDef() || (expr.def() != null)
|| (expr.parent() instanceof PhiStmt), "Null def for variable "
+ expr);
visitDefExpr(expr);
}
}

@ -0,0 +1,43 @@
# All files in the distribution of BLOAT (Bytecode Level Optimization and
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
# Research Foundation of Purdue University. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
.SUFFIXES: .java .class
JAVA_HOME = /gcm/where/jdk/1.3/sparc.Solaris
JAVAC = $(JAVA_HOME)/bin/javac
JFLAGS = -g
CLASSPATH = $(JAVA_HOME)/lib/classes.zip
all: class
clean:
rm -f *.class
class:
@files=`$(MAKE) -n _class | grep $(JAVAC) | cut -d' ' -f4`; \
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
if [ "x$$files" != "x" ]; then \
echo $(JAVAC) $(JFLAGS) -classpath $$cpath $$files; \
$(JAVAC) $(JFLAGS) -classpath $$cpath $$files; \
fi
_class: $(CLASS)
.java.class:
cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \
$(JAVAC) -classpath $$cpath $<

File diff suppressed because it is too large Load Diff

@ -0,0 +1,774 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.codegen;
import java.util.*;
import EDU.purdue.cs.bloat.cfg.*;
import EDU.purdue.cs.bloat.tree.*;
import EDU.purdue.cs.bloat.util.*;
/**
* Liveness represents the interference graph of the local variables contained
* in a control flow graph.
*
* When the liveness of two variables overlap each other, the two variables are
* said to <i>interfere</i> with each other. The interference graph represents
* this relationship between variables. There is an (un-directed) edge between
* variables <tt>a</tt> and <tt>b</tt> in the interference graph if variable
* <tt>a</tt> interferes with variable <tt>b</tt>.
*/
public class Liveness {
public static boolean DEBUG = false;
public static boolean UNIQUE = false;
public static final boolean BEFORE = false;
public static final boolean AFTER = true;
FlowGraph cfg;
Graph ig;
/**
* Constructor.
*
* @param cfg
* Control flow graph on which to perform liveness analysis.
*/
public Liveness(final FlowGraph cfg) {
this.cfg = cfg;
computeIntersections();
}
/**
* Removes a local expression from the interference graph.
*/
public void removeVar(final LocalExpr expr) {
ig.removeNode(expr);
}
/**
* Should not be called.
*/
public boolean liveAtUse(final VarExpr isLive, final VarExpr at,
final boolean after) {
throw new RuntimeException();
}
/**
* Should not be called.
*/
public boolean liveAtStartOfBlock(final VarExpr isLive, final Block block) {
throw new RuntimeException();
}
/**
* Should not be called.
*/
public boolean liveAtEndOfBlock(final VarExpr isLive, final Block block) {
throw new RuntimeException();
}
/**
* Returns the <tt>LocalExpr</tt>s (variables) that occur in the CFG.
* They correspond to nodes in the interference graph.
*/
public Collection defs() {
return ig.keySet();
}
/**
* Returns an <tt>Iterator</tt> of <tt>LocalExpr</tt>s that interfere
* with a given <tt>VarExpr</tt>.
*/
public Iterator intersections(final VarExpr a) {
Assert.isTrue(a != null, "Cannot get intersections for null def");
Assert.isTrue(a.isDef(), "Cannot get intersections for variable uses");
final GraphNode node = ig.getNode(a);
Assert.isTrue(node != null, "Cannot find IG node for " + a);
return new Iterator() {
Iterator succs = ig.succs(node).iterator();
public boolean hasNext() {
return succs.hasNext();
}
public Object next() {
final IGNode next = (IGNode) succs.next();
return next.def;
}
public void remove() {
throw new RuntimeException();
}
};
}
/**
* Determines whether or not two variables interfere with one another.
*/
public boolean liveRangesIntersect(final VarExpr a, final VarExpr b) {
Assert.isTrue((a != null) && (b != null),
"Cannot get intersections for null def");
Assert.isTrue(a.isDef() && b.isDef(),
"Cannot get intersections for variable uses");
if (a == b) {
return false;
}
// If all locals should have unique colors, return true.
if (Liveness.UNIQUE) {
return true;
}
final IGNode na = (IGNode) ig.getNode(a);
final IGNode nb = (IGNode) ig.getNode(b);
Assert.isTrue((na != null) && (nb != null));
return ig.hasEdge(na, nb);
}
/**
* Constructs the interference graph.
*/
private void computeIntersections() {
ig = new Graph(); // The interference graph
if (Liveness.DEBUG) {
System.out.println("-----------Computing live ranges-----------");
}
// All of the nodes (IGNodes) in the IG
final List defNodes = new ArrayList();
// The IGNodes whose local variable is defined by a PhiCatchStmt
final List phiCatchNodes = new ArrayList();
// An array of NodeInfo for each node in the CFG (indexed by the
// node's pre-order index). Gives information about the local
// variables (nodes in the IG) that are defined in each block.
// The NodeInfos are stored in reverse order. That is, the
// NodeInfo for the final variable occurrence in the block is the
// first element in the list.
final List[] nodes = new ArrayList[cfg.size()];
// We need to keep track of the order of the statements in which
// variables occur. There is an entry in nodeIndices for each
// block in the CFG. Each entry consists of a mapping between a
// statement in which a variable occurs and the number of the
// statement (with respect to the other statements in which
// variables occur) of interest. This is hard to explain in
// words. This numbering comes into play in the liveOut method.
final Map[] nodeIndices = new HashMap[cfg.size()];
Iterator iter = cfg.nodes().iterator();
// Initialize nodes and nodeIndices
while (iter.hasNext()) {
final Block block = (Block) iter.next();
final int blockIndex = cfg.preOrderIndex(block);
nodes[blockIndex] = new ArrayList();
nodeIndices[blockIndex] = new HashMap();
}
// Go in trace order. Code generation for phis in the presence of
// critical edges depends on it!
iter = cfg.trace().iterator();
// When performing liveness analysis, we traverse the tree from
// the bottom up. That is, we do a REVERSE traversal.
while (iter.hasNext()) {
final Block block = (Block) iter.next();
block.visit(new TreeVisitor(TreeVisitor.REVERSE) {
public void visitPhiJoinStmt(final PhiJoinStmt stmt) {
if (!(stmt.target() instanceof LocalExpr)) {
return;
}
final LocalExpr target = (LocalExpr) stmt.target();
// Examine each predacessor and maintain some information
// about the definitions. Remember that we're dealing with
// a PhiJoinStmt. The predacessors of PhiJoinStmts are
// statements that define or use the local (SSA) variable.
final Iterator preds = cfg.preds(block).iterator();
while (preds.hasNext()) {
final Block pred = (Block) preds.next();
final int predIndex = cfg.preOrderIndex(pred);
final List n = nodes[predIndex];
final Map indices = nodeIndices[predIndex];
indices.put(stmt, new Integer(n.size()));
final NodeInfo info = new NodeInfo(stmt);
n.add(info);
// Make a new node in the interference graph for target,
// if one does not already exists
IGNode node = (IGNode) ig.getNode(target);
if (node == null) {
node = new IGNode(target);
ig.addNode(target, node);
defNodes.add(node);
}
info.defNodes.add(node);
}
}
public void visitPhiCatchStmt(final PhiCatchStmt stmt) {
}
public void visitStmt(final Stmt stmt) {
}
});
}
iter = cfg.trace().iterator();
while (iter.hasNext()) {
final Block block = (Block) iter.next();
final int blockIndex = cfg.preOrderIndex(block);
block.visit(new TreeVisitor(TreeVisitor.REVERSE) {
Node parent = null;
public void visitNode(final Node node) {
final Node p = parent;
parent = node;
node.visitChildren(this);
parent = p;
}
public void visitLocalExpr(final LocalExpr expr) {
Assert.isTrue(parent != null);
// Recall that a LocalExpr represents a use or a definition
// of a local variable. If the LocalExpr is defined by a
// PhiJoinStmt, the block in which it resides should already
// have some information about it.
NodeInfo info;
final List n = nodes[blockIndex];
final Map indices = nodeIndices[blockIndex];
final Integer i = (Integer) indices.get(parent);
if (i == null) {
if (Liveness.DEBUG) {
System.out.println("adding " + parent + " at "
+ n.size());
}
indices.put(parent, new Integer(n.size()));
info = new NodeInfo(parent);
n.add(info);
} else {
if (Liveness.DEBUG) {
System.out.println("found " + parent + " at " + i);
}
info = (NodeInfo) n.get(i.intValue());
Assert.isTrue(info != null);
}
if (expr.isDef()) {
IGNode node = (IGNode) ig.getNode(expr);
if (node == null) {
node = new IGNode(expr);
ig.addNode(expr, node);
defNodes.add(node);
}
info.defNodes.add(node);
}
}
public void visitPhiCatchStmt(final PhiCatchStmt stmt) {
NodeInfo info;
final List n = nodes[blockIndex];
final Map indices = nodeIndices[blockIndex];
final Integer i = (Integer) indices.get(stmt);
if (i == null) {
if (Liveness.DEBUG) {
System.out.println("adding " + stmt + " at "
+ n.size());
}
indices.put(stmt, new Integer(n.size()));
info = new NodeInfo(stmt);
n.add(info);
} else {
if (Liveness.DEBUG) {
System.out.println("found " + parent + " at " + i);
}
info = (NodeInfo) n.get(i.intValue());
Assert.isTrue(info != null);
}
final LocalExpr target = (LocalExpr) stmt.target();
IGNode node = (IGNode) ig.getNode(target);
if (node == null) {
node = new IGNode(target);
ig.addNode(target, node);
defNodes.add(node);
phiCatchNodes.add(node);
}
info.defNodes.add(node);
}
public void visitPhiJoinStmt(final PhiJoinStmt stmt) {
}
});
}
// Iterate over all of the nodes in the IG
final int numDefs = defNodes.size();
for (int i = 0; i < numDefs; i++) {
final IGNode node = (IGNode) defNodes.get(i);
final LocalExpr def = node.def;
// Set of blocks where this variable is live out (i.e. live on
// any of the block's outgoing edges).
final BitSet m = new BitSet(cfg.size());
final Iterator uses = def.uses().iterator();
// Look at each use of the local variable
while (uses.hasNext()) {
final LocalExpr use = (LocalExpr) uses.next();
Node parent = use.parent();
if ((parent instanceof MemRefExpr)
&& ((MemRefExpr) parent).isDef()) {
parent = parent.parent();
}
// Skip catch-phis. We handle this later.
if (parent instanceof PhiCatchStmt) {
// If we want to be less conservative:
// Need to search back from the operand from all
// points in the protected region where it is live
// back to the def of the operand. For each block
// in protected region, if the operand def is closest
// dominator of the block
continue;
}
if (Liveness.DEBUG) {
System.out.println("searching for " + def + " from "
+ parent);
}
final Block block = parent.block();
if (parent instanceof PhiJoinStmt) {
final PhiJoinStmt phi = (PhiJoinStmt) parent;
// The local variable (LocalExpr) occurs within a
// PhiJoinStmt. Look at the predacessors of the
// PhiJoinStmt. Recall that each predacessor defines one of
// the operands to the PhiJoinStmt. Locate the block that
// defines the LocalExpr in question. Call liveOut to
// determine for which nodes the LocalExpr is live out.
// Examine the predacessors of the block containing the
// LocalExpr
final Iterator preds = cfg.preds(block).iterator();
while (preds.hasNext()) {
final Block pred = (Block) preds.next();
if (phi.operandAt(pred) == use) {
final Map indices = nodeIndices[cfg
.preOrderIndex(pred)];
final Integer index = (Integer) indices.get(parent);
Assert.isTrue(index != null, "No index for "
+ parent);
liveOut(m, nodes, pred, index.intValue(), node,
phiCatchNodes);
break;
}
}
} else {
// The LocalExpr is define in a non-Phi statement. Figure
// out which number definition define the LocalExpr in quest
// and call liveOut to compute the set of block in which the
// LocalExpr is live out.
final Map indices = nodeIndices[cfg.preOrderIndex(block)];
final Integer index = (Integer) indices.get(parent);
Assert.isTrue(index != null, "No index for " + parent);
liveOut(m, nodes, block, index.intValue(), node,
phiCatchNodes);
}
}
}
// Go through all of the variables that are defined by
// PhiCatchStmts and make them (the variables) conflict with
// everything that the operands of the PhiCatchStmt conflict
// with. See liveOut for a discussion.
final int numPhiCatches = phiCatchNodes.size();
for (int i = 0; i < numPhiCatches; i++) {
final IGNode node = (IGNode) phiCatchNodes.get(i);
final PhiCatchStmt phi = (PhiCatchStmt) node.def.parent();
final Iterator operands = phi.operands().iterator();
while (operands.hasNext()) {
final LocalExpr operand = (LocalExpr) operands.next();
final LocalExpr def = (LocalExpr) operand.def();
if (def != null) {
final IGNode opNode = (IGNode) ig.getNode(def);
// Conflict with everything the operand conflicts with.
final Iterator edges = new ImmutableIterator(ig
.succs(opNode));
while (edges.hasNext()) {
final IGNode otherNode = (IGNode) edges.next();
if (otherNode != node) {
if (Liveness.DEBUG) {
System.out.println(otherNode.def
+ " conflicts with " + opNode.def
+ " and thus with " + node.def);
}
ig.addEdge(otherNode, node);
ig.addEdge(node, otherNode);
}
}
}
}
}
if (Liveness.DEBUG) {
System.out.println("Interference graph =");
System.out.println(ig);
}
}
/**
* Computes (a portion of) the "live out" set for a given local variable. If
* a variable is live on a block's outgoing edge in the CFG, then it is
* "live out" at that block.
*
* @param m
* Bit vector that indicates the block for which block the
* defNode is live out
* @param nodes
* The NodeInfo for the local variables used or defined in each
* block
* @param block
* The block in which the LocalExpr of interest is defined
* @param nodeIndex
* Which number definition in the defining block
* @param defNode
* The node in the IG whose live out set we are interested in
* @param phiCatchNodes
* The nodes in the interference graph that represent local
* variables defined by PhiCatchStmts
*/
// Nate sez:
//
// In a PhiJoin pred, add
// ...
// phi-target := phi-operand
// jump with throw succs
//
// Don't kill Phi targets in protected blocks
// The phi target and operand don't conflict
void liveOut(final BitSet m, final List[] nodes, Block block,
int nodeIndex, final IGNode defNode, final Collection phiCatchNodes) {
boolean firstNode = true;
int blockIndex = cfg.preOrderIndex(block);
final ArrayList stack = new ArrayList();
Pos pos = new Pos();
pos.block = block;
pos.blockIndex = blockIndex;
pos.nodeIndex = nodeIndex;
stack.add(pos);
while (!stack.isEmpty()) {
pos = (Pos) stack.remove(stack.size() - 1);
block = pos.block;
blockIndex = pos.blockIndex;
nodeIndex = pos.nodeIndex;
if (Liveness.DEBUG) {
System.out.println(defNode.def + " is live at position "
+ nodeIndex + " of " + block);
}
boolean stop = false;
// The nodes are sorted in reverse. So, the below gets all of
// the nodes defined at this block after nodeIndex. I believe
// this is an optimization so we don't calculate things twice.
// Or maybe its how we get things to terminate.
final ListIterator iter = nodes[blockIndex].listIterator(nodeIndex);
while (!stop && iter.hasNext()) {
final NodeInfo info = (NodeInfo) iter.next();
if (Liveness.DEBUG) {
System.out
.println(defNode.def + " is live at " + info.node);
}
if (firstNode) {
// We don't care about the definition in the block that
// defines the LocalExpr of interest.
firstNode = false;
continue;
}
// Look at all (?) of the definitions of the LocalExpr
final Iterator e = info.defNodes.iterator();
while (e.hasNext()) {
final IGNode node = (IGNode) e.next();
final Iterator catchPhis = phiCatchNodes.iterator();
// Calculating the live region of the target of a phi-catch
// node is a little tricky. The target (variable) must be
// live throughout the protected region as well as after the
// PhiCatchStmt (its definition). However, we do not want
// the phi-catch target to conflict (interfere) with any of
// its operands. So, we make the target conflict with all
// of the variables that its operand conflict with. See
// page 37 of Nate's Thesis.
PHIS: while (catchPhis.hasNext()) {
final IGNode catchNode = (IGNode) catchPhis.next();
final PhiCatchStmt phi = (PhiCatchStmt) catchNode.def
.parent();
final Handler handler = (Handler) cfg.handlersMap()
.get(phi.block());
Assert.isTrue(handler != null, "Null handler for "
+ phi.block());
if (handler.protectedBlocks().contains(block)) {
final Iterator operands = phi.operands().iterator();
// If the block containing the LocalExpr in question
// resides inside a protected region. Make sure that
// the LocalExpr is not one of the operands to the
// PhiCatchStmt associated with the protected
// region.
while (operands.hasNext()) {
final LocalExpr expr = (LocalExpr) operands
.next();
if (expr.def() == node.def) {
continue PHIS;
}
}
if (Liveness.DEBUG) {
System.out.println(defNode.def
+ " conflicts with " + node.def);
}
// Hey, wow. The variable defined in the phi-catch
// interferes with the variable from the worklist.
ig.addEdge(node, catchNode);
ig.addEdge(catchNode, node);
}
}
if (node != defNode) {
if (Liveness.DEBUG) {
System.out.println(defNode.def + " conflicts with "
+ node.def);
}
// If the node in the worklist is not the node we
// started
// with, then they conflict.
ig.addEdge(node, defNode);
ig.addEdge(defNode, node);
} else {
if (Liveness.DEBUG) {
System.out.println("def found stopping search");
}
// We've come across a definition of the LocalExpr in
// question, so we don't need to do any more.
stop = true;
}
}
}
if (!stop) {
// Propagate the liveness to each of the predacessors of the
// block in which the variable of interest is defined. This
// is accomplished by setting the appropriate bit in m. We
// also add another Pos to the worklist to work on the
// predacessor block.
final Iterator preds = cfg.preds(block).iterator();
while (preds.hasNext()) {
final Block pred = (Block) preds.next();
final int predIndex = cfg.preOrderIndex(pred);
if (Liveness.DEBUG) {
System.out.println(defNode.def + " is live at end of "
+ pred);
}
if (!m.get(predIndex)) {
pos = new Pos();
pos.block = pred;
pos.blockIndex = predIndex;
// Look at all of the statements in which a variable
// occur
pos.nodeIndex = 0;
m.set(predIndex);
stack.add(pos);
}
}
}
}
}
/**
* Represents a node in the interference graph. Connected nodes in the
* interference graph interfere with each other. That is, their live regions
*/
class IGNode extends GraphNode {
LocalExpr def;
/**
* Constructor.
*
* @param def
* The local variable represented by this node.
*/
public IGNode(final LocalExpr def) {
this.def = def;
}
public String toString() {
return def.toString();
}
}
/**
* Stores information about each Node in an expression tree (!) that defines
* a local variable (i.e. PhiJoinStmt, PhiCatchStmt, and the parent of a
* LocalExpr).
*/
class NodeInfo {
Node node; // Node in an expression tree in which a variable occurs
List defNodes; // node(s) in IG that define above Node
public NodeInfo(final Node node) {
this.node = node;
defNodes = new ArrayList();
}
}
class Key {
int blockIndex;
Node node;
public Key(final Node node, final int blockIndex) {
this.blockIndex = blockIndex;
this.node = node;
}
public int hashCode() {
return node.hashCode() ^ blockIndex;
}
public boolean equals(final Object obj) {
if (obj instanceof Key) {
final Key key = (Key) obj;
return (key.node == node) && (key.blockIndex == blockIndex);
}
return false;
}
}
/**
* A Pos is an element in the worklist used to determine the live out set of
* a given LocalExpr. It consists of the block in which a local variable
* definition occurs, the block's index (i.e. pre-order traversal number) in
* the CFG, and the number of the definition in the block that defines the
* LocalExpr of interest.
*/
class Pos {
Block block;
int blockIndex;
int nodeIndex;
}
}

@ -0,0 +1,24 @@
# All files in the distribution of BLOAT (Bytecode Level Optimization and
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
# Research Foundation of Purdue University. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
CLASS = \
CodeGenerator.class\
Liveness.class\
RegisterAllocator.class
include ../class.mk

@ -0,0 +1,657 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.codegen;
import java.util.*;
import EDU.purdue.cs.bloat.cfg.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.tree.*;
import EDU.purdue.cs.bloat.util.*;
/**
* RegisterAllocator performs analysis on a control flow graph and determines
* the minimum amount of local variables needed in a method.
*
* @see LocalVariable
*/
// Note that RegisterAllocator uses a different IGNode from Liveness!
public class RegisterAllocator {
FlowGraph cfg;
Liveness liveness;
Map colors;
int colorsUsed;
final static float MAX_WEIGHT = Float.MAX_VALUE;
final static float LOOP_FACTOR = 10.0F;
final static int MAX_DEPTH = (int) (Math.log(RegisterAllocator.MAX_WEIGHT) / Math
.log(RegisterAllocator.LOOP_FACTOR));
/**
* Constructor. Builds an interference graph based on the expression nodes
* found in liveness. Traverses the graph and determines which nodes needs
* to be precolored and which nodes can be coalesced (move statements).
* Nodes are coalesced and local variables are assigned to expressions.
*
* @see FlowGraph
* @see LocalVariable
*/
public RegisterAllocator(final FlowGraph cfg, final Liveness liveness) {
this.cfg = cfg;
this.liveness = liveness;
colorsUsed = 0;
colors = new HashMap();
// Construct the interference graph.
final Graph ig = new Graph();
Iterator iter = liveness.defs().iterator();
while (iter.hasNext()) {
final VarExpr def = (VarExpr) iter.next();
if (!(def instanceof LocalExpr)) {
// Ignore node in the Liveness IG that are not LocalExprs
continue;
}
// Create a new node in the IG, if one does not already exist
IGNode defNode = (IGNode) ig.getNode(def);
if (defNode == null) {
defNode = new IGNode((LocalExpr) def);
ig.addNode(def, defNode);
}
// Examine each variable that interferes with def
final Iterator intersections = liveness.intersections(def);
while (intersections.hasNext()) {
final VarExpr expr = (VarExpr) intersections.next();
if (expr == def) {
// If for some reason, def interferes with itself, ignore it
continue;
}
// Add an edge in RegisterAllocator's IG between the variables
// that interfere
if (expr instanceof LocalExpr) {
IGNode node = (IGNode) ig.getNode(expr);
if (node == null) {
node = new IGNode((LocalExpr) expr);
ig.addNode(expr, node);
}
ig.addEdge(defNode, node);
ig.addEdge(node, defNode);
}
}
}
// Arrays of expressions that invovle a copy of one local variable
// to another. Expressions invovled in copies (i.e. "moves") can
// be coalesced into one expression.
final ArrayList copies = new ArrayList();
// Nodes that are the targets of InitStmt are considered to be
// precolored.
final ArrayList precolor = new ArrayList();
cfg.visit(new TreeVisitor() {
public void visitBlock(final Block block) {
// Don't visit the sink block. There's nothing interesting
// there.
if (block != RegisterAllocator.this.cfg.sink()) {
block.visitChildren(this);
}
}
public void visitPhiStmt(final PhiStmt stmt) {
stmt.visitChildren(this);
if (!(stmt.target() instanceof LocalExpr)) {
return;
}
// A PhiStmt invovles an assignment (copy). So note the copy
// between the target and all of the PhiStmt's operands in the
// copies list.
final IGNode lnode = (IGNode) ig.getNode(stmt.target());
final HashSet set = new HashSet();
final Iterator e = stmt.operands().iterator();
while (e.hasNext()) {
final Expr op = (Expr) e.next();
if ((op instanceof LocalExpr) && (op.def() != null)) {
if (!set.contains(op.def())) {
set.add(op.def());
if (op.def() != stmt.target()) {
final IGNode rnode = (IGNode) ig.getNode(op
.def());
copies.add(new IGNode[] { lnode, rnode });
}
}
}
}
}
public void visitStoreExpr(final StoreExpr expr) {
expr.visitChildren(this);
if (!(expr.target() instanceof LocalExpr)) {
return;
}
final IGNode lnode = (IGNode) ig.getNode(expr.target());
if ((expr.expr() instanceof LocalExpr)
&& (expr.expr().def() != null)) {
// A store of a variable into another variable is a copy
final IGNode rnode = (IGNode) ig.getNode(expr.expr().def());
copies.add(new IGNode[] { lnode, rnode });
return;
}
// Treat L := L + k as a copy so that they get converted
// back to iincs.
if (expr.target().type().equals(Type.INTEGER)) {
if (!(expr.expr() instanceof ArithExpr)) {
return;
}
// We're dealing with integer arithmetic. Remember that an
// ArithExpr has a left and a right operand. If one of the
// operands is a variable and if the other is a constant and
// the operation is addition or substraction, we have an
// increment.
final ArithExpr rhs = (ArithExpr) expr.expr();
LocalExpr var = null;
Integer value = null;
if ((rhs.left() instanceof LocalExpr)
&& (rhs.right() instanceof ConstantExpr)) {
var = (LocalExpr) rhs.left();
final ConstantExpr c = (ConstantExpr) rhs.right();
if (c.value() instanceof Integer) {
value = (Integer) c.value();
}
} else if ((rhs.right() instanceof LocalExpr)
&& (rhs.left() instanceof ConstantExpr)) {
var = (LocalExpr) rhs.right();
final ConstantExpr c = (ConstantExpr) rhs.left();
if (c.value() instanceof Integer) {
value = (Integer) c.value();
}
}
if (rhs.operation() == ArithExpr.SUB) {
if (value != null) {
value = new Integer(-value.intValue());
}
} else if (rhs.operation() != ArithExpr.ADD) {
value = null;
}
if ((value != null) && (var.def() != null)) {
final int incr = value.intValue();
if ((short) incr == incr) {
// Only generate an iinc if the increment
// fits in a short
final IGNode rnode = (IGNode) ig.getNode(var.def());
copies.add(new IGNode[] { lnode, rnode });
}
}
}
}
public void visitInitStmt(final InitStmt stmt) {
stmt.visitChildren(this);
// The initialized variables are precolored.
final LocalExpr[] t = stmt.targets();
for (int i = 0; i < t.length; i++) {
precolor.add(t[i]);
}
}
});
// Coalesce move related nodes, maximum weight first.
while (copies.size() > 0) {
// We want the copy (v <- w) with the maximum:
// weight(v) + weight(w)
// ---------------------
// size(union)
// where union is the intersection of the nodes that conflict
// with v and the nodes that conflict with w. This equation
// appears to be in conflict with the one given on page 38 of
// Nate's thesis.
HashSet union; // The union of neighboring nodes
int max = 0;
IGNode[] copy = (IGNode[]) copies.get(max);
float maxWeight = copy[0].weight + copy[1].weight;
union = new HashSet();
union.addAll(ig.succs(copy[0]));
union.addAll(ig.succs(copy[1]));
maxWeight /= union.size();
for (int i = 1; i < copies.size(); i++) {
copy = (IGNode[]) copies.get(i);
float weight = copy[0].weight + copy[1].weight;
union.clear();
union.addAll(ig.succs(copy[0]));
union.addAll(ig.succs(copy[1]));
weight /= union.size();
if (weight > maxWeight) {
// The ith copy has the maximum weight
maxWeight = weight;
max = i;
}
}
// Remove the copy with the max weight from the copies list. He
// does it in a rather round-about way.
copy = (IGNode[]) copies.get(max);
copies.set(max, copies.get(copies.size() - 1));
copies.remove(copies.size() - 1);
if (!ig.hasEdge(copy[0], copy[1])) {
// If the variables involved in the copy do not interfere with
// each other, they are coalesced.
if (CodeGenerator.DEBUG) {
System.out.println("coalescing " + copy[0] + " " + copy[1]);
System.out.println(" 0 conflicts " + ig.succs(copy[0]));
System.out.println(" 1 conflicts " + ig.succs(copy[1]));
}
ig.succs(copy[0]).addAll(ig.succs(copy[1]));
ig.preds(copy[0]).addAll(ig.preds(copy[1]));
copy[0].coalesce(copy[1]);
if (CodeGenerator.DEBUG) {
System.out.println(" coalesced " + copy[0]);
System.out.println(" conflicts " + ig.succs(copy[0]));
}
// Remove coalesced node from the IG
ig.removeNode(copy[1].key);
iter = copies.iterator();
// Examine all copies. If the copy involves the node that was
// coalesced, the copy is no longer interesting. Remove it.
while (iter.hasNext()) {
final IGNode[] c = (IGNode[]) iter.next();
if ((c[0] == copy[1]) || (c[1] == copy[1])) {
iter.remove();
}
}
}
}
// Create a list of uncolored nodes.
final ArrayList uncoloredNodes = new ArrayList();
Iterator nodes = ig.nodes().iterator();
while (nodes.hasNext()) {
final IGNode node = (IGNode) nodes.next();
final ArrayList p = new ArrayList(precolor);
p.retainAll(node.defs);
// See if any node got coalesced with a precolored node.
if (p.size() == 1) {
// Precolored
node.color = ((LocalExpr) p.get(0)).index();
if (CodeGenerator.DEBUG) {
System.out.println("precolored " + node + " " + node.color);
}
} else if (p.size() == 0) {
// Uncolored (i.e. not coalesced with any of the pre-colored
// nodes.
node.color = -1;
uncoloredNodes.add(node);
} else {
// If two or more pre-colored nodes were coalesced, we have a
// problem.
throw new RuntimeException("coalesced pre-colored defs " + p);
}
}
// Sort the uncolored nodes, by decreasing weight. Wide nodes
// have half their original weight since they take up two indices
// and we want to put color nodes with the lower indices.
Collections.sort(uncoloredNodes, new Comparator() {
public int compare(final Object a, final Object b) {
final IGNode na = (IGNode) a;
final IGNode nb = (IGNode) b;
float wa = na.weight / ig.succs(na).size();
float wb = nb.weight / ig.succs(nb).size();
if (na.wide) {
wa /= 2;
}
if (nb.wide) {
wb /= 2;
}
if (wb > wa) {
return 1;
}
if (wb < wa) {
return -1;
}
return 0;
}
});
nodes = uncoloredNodes.iterator();
while (nodes.hasNext()) {
final IGNode node = (IGNode) nodes.next();
if (CodeGenerator.DEBUG) {
System.out.println("coloring " + node);
System.out.println(" conflicts " + ig.succs(node));
}
// Make sure node has not been colored
Assert.isTrue(node.color == -1);
// Determine which colors have been assigned to the nodes
// conflicting with the node of interest
final BitSet used = new BitSet();
final Iterator succs = ig.succs(node).iterator();
while (succs.hasNext()) {
final IGNode succ = (IGNode) succs.next();
if (succ.color != -1) {
used.set(succ.color);
if (succ.wide) {
used.set(succ.color + 1);
}
}
}
// Find the next available color
for (int i = 0; node.color == -1; i++) {
if (!used.get(i)) {
if (node.wide) {
// Wide variables need two colors
if (!used.get(i + 1)) {
node.color = i;
if (CodeGenerator.DEBUG) {
System.out.println(" assigning color " + i
+ " to " + node);
}
if (i + 1 >= colorsUsed) {
colorsUsed = i + 2;
}
}
} else {
node.color = i;
if (CodeGenerator.DEBUG) {
System.out.println(" assigning color " + i
+ " to " + node);
}
if (i >= colorsUsed) {
colorsUsed = i + 1;
}
}
}
}
}
nodes = ig.nodes().iterator();
while (nodes.hasNext()) {
final IGNode node = (IGNode) nodes.next();
// Make sure each node has been colored
Assert.isTrue(node.color != -1, "No color for " + node);
iter = node.defs.iterator();
// Set the index of the variable and all of its uses to be the
// chosen color.
while (iter.hasNext()) {
final LocalExpr def = (LocalExpr) iter.next();
def.setIndex(node.color);
final Iterator uses = def.uses().iterator();
while (uses.hasNext()) {
final LocalExpr use = (LocalExpr) uses.next();
use.setIndex(node.color);
}
}
}
if (CodeGenerator.DEBUG) {
System.out.println("After allocating locals--------------------");
cfg.print(System.out);
System.out.println("End print----------------------------------");
}
}
/**
* Returns the maximum number of local variables used by the cfg after its
* "registers" (local variables) have been allocated.
*/
public int maxLocals() {
return colorsUsed;
}
/**
* Creates a new local variable in this method (as modeled by the cfg).
* Updates the number of local variables appropriately.
*/
public LocalVariable newLocal(final Type type) {
// Why don't we add Type information to the LocalVariable? Are we
// assuming that type checking has already been done and so its a
// moot point?
final LocalVariable var = new LocalVariable(colorsUsed);
colorsUsed += type.stackHeight();
return var;
}
/**
* IGNode is a node in the interference graph. Note that this node is
* different from the one in Liveness. For instance, this one stores
* information about a node's color, its weight, etc. Because nodes may be
* coalesced, an IGNode may represent more than one LocalExpr. That's why
* there is a list of definitions.
*/
class IGNode extends GraphNode {
Set defs;
LocalExpr key;
int color;
boolean wide; // Is the variable wide?
float weight;
public IGNode(final LocalExpr def) {
color = -1;
key = def;
defs = new HashSet();
defs.add(def);
wide = def.type().isWide();
computeWeight();
}
/**
* Coalesce two nodes in the interference graph. The weight of the other
* node is added to that of this node. This node also inherits all of
* the definitions of the other node.
*/
void coalesce(final IGNode node) {
Assert.isTrue(wide == node.wide);
weight += node.weight;
final Iterator iter = node.defs.iterator();
while (iter.hasNext()) {
final LocalExpr def = (LocalExpr) iter.next();
defs.add(def);
}
}
public String toString() {
return "(color=" + color + " weight=" + weight + " "
+ defs.toString() + ")";
}
/**
* Calculates the weight of a Block based on its loop depth. If the
* block does not exceed the MAX_DEPTH, then the weight is LOOP_FACTOR
* raised to the depth.
*/
private float blockWeight(final Block block) {
int depth = cfg.loopDepth(block);
if (depth > RegisterAllocator.MAX_DEPTH) {
return RegisterAllocator.MAX_WEIGHT;
}
float w = 1.0F;
while (depth-- > 0) {
w *= RegisterAllocator.LOOP_FACTOR;
}
return w;
}
/**
* Computes the weight of a node in the interference graph. The weight
* is based on where the variable represented by this node is used. The
* method blockWeight is used to determine the weight of a variable used
* in a block based on the loop depth of the block. Special care must be
* taken if the variable is used in a PhiStmt.
*/
private void computeWeight() {
weight = 0.0F;
final Iterator iter = defs.iterator();
// Look at all(?) of the definitions of the IGNode
while (iter.hasNext()) {
final LocalExpr def = (LocalExpr) iter.next();
weight += blockWeight(def.block());
final Iterator uses = def.uses().iterator();
// If the variable is used as an operand to a PhiJoinStmt,
// find the predacessor block to the PhiJoinStmt in which the
// variable occurs and add the weight of that block to the
// running total weight.
while (uses.hasNext()) {
final LocalExpr use = (LocalExpr) uses.next();
if (use.parent() instanceof PhiJoinStmt) {
final PhiJoinStmt phi = (PhiJoinStmt) use.parent();
final Iterator preds = cfg.preds(phi.block())
.iterator();
while (preds.hasNext()) {
final Block pred = (Block) preds.next();
final Expr op = phi.operandAt(pred);
if (use == op) {
weight += blockWeight(pred);
break;
}
}
} else if (use.parent() instanceof PhiCatchStmt) {
// If the variable is used in a PhiCatchStmt, add the
// weight of the block in which the variable is defined
// to
// the running total.
weight += blockWeight(use.def().block());
} else {
// Just add in the weight of the block in which the
// variable is used.
weight += blockWeight(use.block());
}
}
}
}
}
}

@ -0,0 +1,261 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.context;
import java.io.*;
import java.util.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.file.*;
import EDU.purdue.cs.bloat.inline.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* This abstract class is a central repository for all things that are necessary
* for a BLOATing sessions. Its subclasses implement certain schemes for
* managing BLOAT data structures such as editors and control flow graphs.
*/
public abstract class BloatContext implements InlineContext {
public static boolean DEBUG = Boolean.getBoolean("BloatContext.DEBUG");
protected InlineStats inlineStats;
// Ignore stuff for inlining
protected Set ignorePackages = new HashSet();
protected Set ignoreClasses = new HashSet();
protected Set ignoreMethods = new HashSet();
protected Set ignoreFields = new HashSet();
protected boolean ignoreSystem = false;
protected CallGraph callGraph;
protected Set roots; // Root methods of call graph
protected static void db(final String s) {
if (BloatContext.DEBUG) {
System.out.println(s);
}
}
protected ClassInfoLoader loader;
/**
* Constructor. Each <tt>BloatContext</tt> needs to know about a
* <tt>ClassInfoLoader</tt>.
*/
public BloatContext(final ClassInfoLoader loader) {
this.loader = loader;
}
private static ClassLoader systemCL;
static {
final String s = "";
BloatContext.systemCL = s.getClass().getClassLoader();
}
/**
* Returns <tt>true</tt> if the give type is a system class (that is, has
* the same class loader as java.lang.String).
*/
public static boolean isSystem(final Type type) {
Class c = null;
try {
c = Class.forName(type.className().replace('/', '.'));
} catch (final ClassNotFoundException ex) {
System.err.println("** Could not find class " + type.className());
ex.printStackTrace(System.err);
System.exit(1);
}
// Have to use == because class loader might be null
return (c.getClassLoader() == BloatContext.systemCL);
}
public void setRootMethods(final Set roots) {
if (this.callGraph != null) {
// Can't set the call graph roots after its been created
throw new IllegalStateException("Cannot set roots after "
+ "call graph has been created");
}
this.roots = roots;
}
public CallGraph getCallGraph() {
if (this.callGraph == null) {
// Create a new CallGraph
this.callGraph = new CallGraph(this, this.roots);
}
return (this.callGraph);
}
public InlineStats getInlineStats() {
if (inlineStats == null) {
inlineStats = new InlineStats();
}
return (inlineStats);
}
public void addIgnorePackage(String name) {
name = name.replace('.', '/');
ignorePackages.add(name);
}
public void addIgnoreClass(final Type type) {
ignoreClasses.add(type);
}
public void addIgnoreMethod(final MemberRef method) {
ignoreMethods.add(method);
}
public void addIgnoreField(final MemberRef field) {
ignoreFields.add(field);
}
public void setIgnoreSystem(final boolean ignore) {
this.ignoreSystem = ignore;
}
public boolean ignoreClass(final Type type) {
// First, check to see if we explicitly ignore it. If not, check
// to see if we ignore its package. The ladies always seem to
// ignore my package.
if (ignoreClasses.contains(type)) {
return (true);
} else if (type.isPrimitive()) {
addIgnoreClass(type);
return (true);
} else {
if (this.ignoreSystem) {
if (BloatContext.isSystem(type)) {
addIgnoreClass(type);
return (true);
}
}
String packageName = type.className();
final int lastSlash = packageName.lastIndexOf('/');
if (lastSlash == -1) {
return (false);
}
packageName = packageName.substring(0, lastSlash);
// If any ignore package is a prefix of the class's package,
// then ignore it. This makes our lives easier.
final Iterator packages = ignorePackages.iterator();
while (packages.hasNext()) {
final String s = (String) packages.next();
if (type.className().startsWith(s)) {
addIgnoreClass(type);
return (true);
}
}
return (false);
}
}
public boolean ignoreMethod(final MemberRef method) {
if (ignoreMethods.contains(method)) {
return (true);
} else if (ignoreClass(method.declaringClass())) {
addIgnoreMethod(method);
return (true);
}
return (false);
}
public boolean ignoreField(final MemberRef field) {
if (ignoreMethods.contains(field)) {
return (true);
} else if (ignoreClass(field.declaringClass())) {
addIgnoreField(field);
return (true);
}
return (false);
}
/**
* Commits all classes, methods, and fields, that have been modified.
*/
public abstract void commitDirty();
/**
* Test the ignore stuff.
*/
public static void main(final String[] args) {
final PrintWriter out = new PrintWriter(System.out, true);
final PrintWriter err = new PrintWriter(System.err, true);
final BloatContext context = new CachingBloatContext(
new ClassFileLoader(), new ArrayList(), false);
final List types = new ArrayList();
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-ip")) {
if (++i >= args.length) {
err.println("** Missing package name");
System.exit(1);
}
out.println("Ignoring package " + args[i]);
context.addIgnorePackage(args[i]);
} else if (args[i].equals("-ic")) {
if (++i >= args.length) {
err.println("** Missing class name");
System.exit(1);
}
out.println("Ignoring class " + args[i]);
final String type = args[i].replace('.', '/');
context.addIgnoreClass(Type.getType("L" + type + ";"));
} else {
// A type
final String type = args[i].replace('.', '/');
types.add(Type.getType("L" + type + ";"));
}
}
out.println("");
final Iterator iter = types.iterator();
while (iter.hasNext()) {
final Type type = (Type) iter.next();
out.println("Ignore " + type + "? " + context.ignoreClass(type));
}
}
}

@ -0,0 +1,151 @@
/**
* 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);
}
}
}

@ -0,0 +1,461 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.context;
import java.io.*;
import java.util.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* Does a lot of the same stuff as <tt>PersistentBloatContext</tt> except that
* it manages the chaches of BLOAT objects. For example, when a
* <tt>MethodEditor</tt> is no longer needed, it is removed from the cache if
* it is not dirty. This context is meant to used in volatile memory.
*/
public class CachingBloatContext extends PersistentBloatContext {
// Keep track of reference counts in a manner reminiscent of the old
// Editor class.
protected Map classRC;
protected Map methodRC;
protected Map fieldRC;
/**
* Constructor.
*
* @param loader
* Used to load classes
* @param classes
* Some initial classes in the context
* @param closure
* Do we look for the maximum number of classes?
*/
public CachingBloatContext(final ClassInfoLoader loader,
final Collection classes, final boolean closure) {
super(loader, closure);
classRC = new HashMap();
methodRC = new HashMap();
fieldRC = new HashMap();
addClasses(classes);
}
public ClassEditor newClass(final int modifiers, final String className,
final Type superType, final Type[] interfaces) {
final ClassEditor ce = super.newClass(modifiers, className, superType,
interfaces);
final ClassInfo info = ce.classInfo();
classRC.put(info, new Integer(1));
return ce;
}
public ClassEditor editClass(final ClassInfo info) {
// Check the cache
ClassEditor ce = (ClassEditor) classEditors.get(info);
if (ce == null) {
ce = new ClassEditor(this, info);
classEditors.put(info, ce);
classRC.put(info, new Integer(1));
if (!classInfos.containsValue(info)) {
final String className = ce.name().intern();
BloatContext.db("editClass(ClassInfo): " + className + " -> "
+ info);
classInfos.put(className, info);
}
} else {
final Integer rc = (Integer) classRC.get(info);
classRC.put(info, new Integer(rc.intValue() + 1));
}
return (ce);
}
public MethodEditor editMethod(final MemberRef method)
throws NoSuchMethodException {
// Check the MethodInfo cache
final MethodInfo info = (MethodInfo) methodInfos.get(method);
if (info == null) {
// Groan, we have to do this the HARD way.
BloatContext.db("Creating a new MethodEditor for " + method);
final NameAndType nat = method.nameAndType();
final String name = nat.name();
final Type type = nat.type();
try {
final ClassEditor ce = editClass(method.declaringClass());
final MethodInfo[] methods = ce.methods();
for (int i = 0; i < methods.length; i++) {
final MethodEditor me = editMethod(methods[i]);
if (me.name().equals(name) && me.type().equals(type)) {
// The call to editMethod should have already handled
// the
// methodEditors mapping, but we still need to do
// methodInfos.
methodInfos.put(method, methods[i]);
release(ce.classInfo());
return (me);
}
}
release(ce.classInfo());
} catch (final ClassNotFoundException ex1) {
throw new NoSuchMethodException(method.toString() + "("
+ ex1.getMessage() + ")");
} catch (final ClassFormatException ex2) {
throw new NoSuchMethodException(method.toString() + "("
+ ex2.getMessage() + ")");
}
throw new NoSuchMethodException(method.toString());
}
return (editMethod(info));
}
public MethodEditor editMethod(final MethodInfo info) {
// Check methodEditors cache
MethodEditor me = (MethodEditor) methodEditors.get(info);
if (me == null) {
final ClassInfo classInfo = info.declaringClass();
me = new MethodEditor(editClass(classInfo), info);
release(classInfo);
methodEditors.put(info, me);
methodRC.put(info, new Integer(1));
BloatContext
.db("Creating a new MethodEditor for " + me.memberRef());
} else {
final Integer rc = (Integer) methodRC.get(info);
methodRC.put(info, new Integer(rc.intValue() + 1));
}
return (me);
}
public FieldEditor editField(final MemberRef field)
throws NoSuchFieldException {
// Just like we had to do with methods
final FieldInfo info = (FieldInfo) fieldInfos.get(field);
if (info == null) {
final NameAndType nat = field.nameAndType();
final String name = nat.name();
final Type type = nat.type();
try {
final ClassEditor ce = editClass(field.declaringClass());
final FieldInfo[] fields = ce.fields();
for (int i = 0; i < fields.length; i++) {
final FieldEditor fe = editField(fields[i]);
if (fe.name().equals(name) && fe.type().equals(type)) {
fieldInfos.put(field, fields[i]);
release(ce.classInfo());
return (fe);
}
release(fields[i]);
}
release(ce.classInfo());
} catch (final ClassNotFoundException ex1) {
} catch (final ClassFormatException ex2) {
}
throw new NoSuchFieldException(field.toString());
}
return (editField(info));
}
public FieldEditor editField(final FieldInfo info) {
// Check the cache
FieldEditor fe = (FieldEditor) fieldEditors.get(info);
BloatContext.db("Editing " + info);
if (fe == null) {
final ClassInfo classInfo = info.declaringClass();
fe = new FieldEditor(editClass(classInfo), info);
release(classInfo);
fieldEditors.put(info, fe);
fieldRC.put(info, new Integer(0));
BloatContext.db("Creating a new FieldEditor for "
+ fe.nameAndType());
} else {
final Integer rc = (Integer) fieldRC.get(info);
fieldRC.put(info, new Integer(rc.intValue() + 1));
}
return (fe);
}
public void release(final ClassInfo info) {
final Integer rc = (Integer) classRC.get(info);
if ((rc != null) && (rc.intValue() > 1)) {
// Not done yet;
classRC.put(info, new Integer(rc.intValue() - 1));
return;
}
ClassEditor ce = (ClassEditor) classEditors.get(info);
if ((ce != null) && ce.isDirty()) {
return;
}
// We're done with this class, remove all traces of it
ce = (ClassEditor) classEditors.remove(info);
classRC.remove(info);
classEditors.remove(info);
final Iterator iter = classInfos.keySet().iterator();
while (iter.hasNext()) {
final String name = (String) iter.next();
final ClassInfo info2 = (ClassInfo) classInfos.get(name);
if (info2 == info) {
BloatContext.db("Removing ClassInfo: " + name + " -> " + info2);
classInfos.remove(name);
break;
}
}
if (ce != null) {
// Remove all of the class's fields and methods also
final MethodInfo[] methods = ce.methods();
for (int i = 0; i < methods.length; i++) {
release(methods[i]);
}
final FieldInfo[] fields = ce.fields();
for (int i = 0; i < fields.length; i++) {
release(fields[i]);
}
}
}
public void release(final MethodInfo info) {
final Integer rc = (Integer) classRC.get(info);
if ((rc != null) && (rc.intValue() > 1)) {
methodRC.put(info, new Integer(rc.intValue() - 1));
return;
}
final MethodEditor me = (MethodEditor) methodEditors.get(info);
// We should keep dirty methods around. My original thought was
// that if we committed dirty methods when they were released, we
// risk having MethodEditors editing different versions of the
// same method. So, if we don't release dirty methods, we'll only
// have ONE MethodEditor.
if ((me != null) && me.isDirty()) {
return;
}
// We're done with this method, remove all traces of it
methodRC.remove(info);
methodEditors.remove(info);
final Iterator iter = methodInfos.keySet().iterator();
while (iter.hasNext()) {
final MemberRef ref = (MemberRef) iter.next();
final MethodInfo info2 = (MethodInfo) methodInfos.get(ref);
if (info2 == info) {
methodInfos.remove(ref);
break;
}
}
}
public void release(final FieldInfo info) {
final Integer rc = (Integer) fieldRC.get(info);
BloatContext.db("Releasing " + info);
if ((rc != null) && (rc.intValue() > 1)) {
fieldRC.put(info, new Integer(rc.intValue() - 1));
return;
}
final FieldEditor fe = (FieldEditor) fieldEditors.get(info);
if ((fe != null) && fe.isDirty()) {
return;
}
// We're done with this field, remove all traces of it
fieldRC.remove(info);
fieldEditors.remove(info);
final Iterator iter = fieldInfos.keySet().iterator();
while (iter.hasNext()) {
final MemberRef ref = (MemberRef) iter.next();
final FieldInfo info2 = (FieldInfo) fieldInfos.get(ref);
if (info2 == info) {
fieldInfos.remove(ref);
break;
}
}
}
public void commit(final ClassInfo info) {
super.commit(info);
classEditors.remove(info);
classRC.remove(info);
}
public void commit(final MethodInfo info) {
super.commit(info);
methodEditors.remove(info);
methodRC.remove(info);
}
public void commit(final FieldInfo info) {
super.commit(info);
fieldEditors.remove(info);
fieldRC.remove(info);
}
public void commit() {
Iterator iter = fieldEditors.values().iterator();
while (iter.hasNext()) {
final FieldEditor fe = (FieldEditor) iter.next();
commit(fe.fieldInfo());
}
iter = methodEditors.values().iterator();
while (iter.hasNext()) {
final MethodEditor me = (MethodEditor) iter.next();
commit(me.methodInfo());
}
iter = classEditors.values().iterator();
while (iter.hasNext()) {
final ClassEditor ce = (ClassEditor) iter.next();
commit(ce.classInfo());
}
}
/**
* Return a textual description of all of the caches. Useful if we run out
* of memory.
*/
public String toString() {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw, true);
pw.println("Context of caches in CachingBloatContext...");
pw.println(" Class Infos");
Iterator iter = classInfos.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + classInfos.get(key));
}
pw.println(" Class Editors");
iter = classEditors.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + classEditors.get(key));
}
pw.println(" Class RC");
iter = classRC.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + classRC.get(key));
}
pw.println(" Method Infos");
iter = methodInfos.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + methodInfos.get(key));
}
pw.println(" Method Editors");
iter = methodEditors.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + methodEditors.get(key));
}
pw.println(" Method RC");
iter = methodRC.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + methodRC.get(key));
}
pw.println(" Field Infos");
iter = fieldInfos.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + fieldInfos.get(key));
}
pw.println(" Field Editors");
iter = fieldEditors.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + fieldEditors.get(key));
}
pw.println(" Field RC");
iter = fieldRC.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + fieldRC.get(key));
}
return (sw.toString());
}
}

@ -0,0 +1,25 @@
# All files in the distribution of BLOAT (Bytecode Level Optimization and
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
# Research Foundation of Purdue University. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
CLASS = \
BloatContext.class\
CachingBloatContext.class\
PersistentBloatContext.class
include ../class.mk

@ -0,0 +1,438 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.context;
import java.util.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* Maintains all BLOAT data structures as if they were meant to reside in a
* persistent store. As a result, it keeps every piece of BLOAT data around
* because it might be needed in the future. No fancing cache maintainence is
* performed. Because we are going for maximum information we take the closure
* of classes when working with the class hierarchy.
*/
public class PersistentBloatContext extends BloatContext {
protected final ClassHierarchy hierarchy;
protected Map classInfos; // Maps Strings to ClassInfos
protected Map methodInfos; // Maps MemberRefs to MethodInfos
protected Map fieldInfos; // Maps MemberRefs to FieldInfos
protected Map classEditors; // Maps ClassInfos to ClassEditors
protected Map methodEditors; // Maps MethodInfos to MethodEditors
protected Map fieldEditors; // Maps MethodInfos to FieldEditors
public static boolean DB_COMMIT = false;
protected static void comm(final String s) {
if (PersistentBloatContext.DB_COMMIT || BloatContext.DEBUG) {
System.out.println(s);
}
}
/**
* Constructor. Each <tt>BloatContext</tt> stems from a
* <tt>ClassInfoLoader</tt>. Using the loader it can create an
* <tt>Editor</tt> and such. Initially, no classes are loaded.
*/
public PersistentBloatContext(final ClassInfoLoader loader) {
this(loader, true);
}
/**
* Constructor. It is the responsibility of the subclasses to add classes to
* the hierarchy by calling <tt>addClasses</tt>.
*
* @param loader
* Used to load classes
* @param closure
* Do we look for the maximum number of classes?
*/
protected PersistentBloatContext(final ClassInfoLoader loader,
final boolean closure) {
super(loader);
BloatContext.db("Creating a new BloatContext");
// Create a bunch of the mappings we maintain. Make sure to do
// this before anything else!
classInfos = new HashMap();
methodInfos = new HashMap();
fieldInfos = new HashMap();
classEditors = new HashMap();
methodEditors = new HashMap();
fieldEditors = new HashMap();
// Have to create an empty class hierarchy then add the classes.
// There is a strange circular dependence between the hierarchy
// and the context.
this.hierarchy = new ClassHierarchy(this, new ArrayList(), closure);
}
/**
* Adds a bunch of (names of) classes to the hierarchy.
*/
protected void addClasses(final Collection classes) {
final Iterator iter = classes.iterator();
while (iter.hasNext()) {
final String className = (String) iter.next();
this.hierarchy.addClassNamed(className);
}
}
public ClassInfo loadClass(String className) throws ClassNotFoundException {
// Lots of interesting stuff to do here. For the moment, just
// load the class from the ClassInfoLoader and add it to the
// hierarchy.
className = className.replace('.', '/').intern();
// Check the cache of ClassInfos
ClassInfo info = (ClassInfo) classInfos.get(className);
if (info == null) {
BloatContext.db("BloatContext: Loading class " + className);
info = loader.loadClass(className);
hierarchy.addClassNamed(className);
BloatContext.db("loadClass: " + className + " -> " + info);
classInfos.put(className, info);
}
return (info);
}
public ClassInfo newClassInfo(final int modifiers, final int classIndex,
final int superClassIndex, final int[] interfaceIndexes,
final List constants) {
return this.loader.newClass(modifiers, classIndex, superClassIndex,
interfaceIndexes, constants);
}
public ClassHierarchy getHierarchy() {
return (this.hierarchy);
}
public ClassEditor newClass(final int modifiers, String className,
final Type superType, final Type[] interfaces) {
final ClassEditor ce = new ClassEditor(this, modifiers, className,
superType, interfaces);
final ClassInfo info = ce.classInfo();
className = ce.name().intern();
BloatContext.db("editClass(ClassInfo): " + className + " -> " + info);
classInfos.put(className, info);
classEditors.put(info, ce);
return ce;
}
public ClassEditor editClass(String className)
throws ClassNotFoundException, ClassFormatException {
// Only make the name -> classInfo mapping if we edit the class,
// this way the mapping will be deleted when the ClassEditor is
// released.
className = className.intern();
ClassInfo info = (ClassInfo) classInfos.get(className);
if (info == null) {
info = loadClass(className);
// db("editClass(String): " + className + " -> " + info);
// classInfos.put(className, info);
}
return (editClass(info));
}
public ClassEditor editClass(final Type classType)
throws ClassNotFoundException, ClassFormatException {
return (editClass(classType.className()));
}
public ClassEditor editClass(final ClassInfo info) {
// Check the cache
ClassEditor ce = (ClassEditor) classEditors.get(info);
if (ce == null) {
ce = new ClassEditor(this, info);
classEditors.put(info, ce);
if (!classInfos.containsValue(info)) {
final String className = ce.name().intern();
BloatContext.db("editClass(ClassInfo): " + className + " -> "
+ info);
classInfos.put(className, info);
}
}
return (ce);
}
public MethodEditor editMethod(final MemberRef method)
throws NoSuchMethodException {
// Check the MethodInfo cache
final MethodInfo info = (MethodInfo) methodInfos.get(method);
if (info == null) {
// Groan, we have to do this the HARD way.
BloatContext.db("Creating a new MethodEditor for " + method);
final NameAndType nat = method.nameAndType();
final String name = nat.name();
final Type type = nat.type();
try {
final ClassEditor ce = editClass(method.declaringClass());
final MethodInfo[] methods = ce.methods();
for (int i = 0; i < methods.length; i++) {
final MethodEditor me = editMethod(methods[i]);
if (me.name().equals(name) && me.type().equals(type)) {
// The call to editMethod should have already handled
// the
// methodEditors mapping, but we still need to do
// methodInfos.
methodInfos.put(method, methods[i]);
release(ce.classInfo());
return (me);
}
release(methods[i]);
}
} catch (final ClassNotFoundException ex1) {
} catch (final ClassFormatException ex2) {
}
throw new NoSuchMethodException(method.toString());
}
return (editMethod(info));
}
public MethodEditor editMethod(final MethodInfo info) {
// Check methodEditors cache
MethodEditor me = (MethodEditor) methodEditors.get(info);
if (me == null) {
me = new MethodEditor(editClass(info.declaringClass()), info);
methodEditors.put(info, me);
BloatContext
.db("Creating a new MethodEditor for " + me.memberRef());
}
return (me);
}
public FieldEditor editField(final MemberRef field)
throws NoSuchFieldException {
// Just like we had to do with methods
final FieldInfo info = (FieldInfo) fieldInfos.get(field);
if (info == null) {
final NameAndType nat = field.nameAndType();
final String name = nat.name();
final Type type = nat.type();
try {
final ClassEditor ce = editClass(field.declaringClass());
final FieldInfo[] fields = ce.fields();
for (int i = 0; i < fields.length; i++) {
final FieldEditor fe = editField(fields[i]);
if (fe.name().equals(name) && fe.type().equals(type)) {
fieldInfos.put(field, fields[i]);
release(ce.classInfo());
return (fe);
}
release(fields[i]);
}
} catch (final ClassNotFoundException ex1) {
} catch (final ClassFormatException ex2) {
}
throw new NoSuchFieldException(field.toString());
}
return (editField(info));
}
public FieldEditor editField(final FieldInfo info) {
// Check the cache
FieldEditor fe = (FieldEditor) fieldEditors.get(info);
if (fe == null) {
fe = new FieldEditor(editClass(info.declaringClass()), info);
fieldEditors.put(info, fe);
BloatContext.db("Creating a new FieldEditor for "
+ fe.nameAndType());
}
return (fe);
}
public void release(final ClassInfo info) {
// Since we keep around all data, do nothing
}
public void release(final ClassEditor ce) {
// Since we keep around all data, do nothing
}
public void release(final MethodInfo info) {
// Since we keep around all data, do nothing
}
public void release(final FieldInfo info) {
// Since we keep around all data, do nothing
}
/**
* Classes that are ignored are not committed.
*
* @see #ignoreClass(Type)
*/
public void commit(final ClassInfo info) {
final Type type = Type.getType("L" + info.name() + ";");
if (ignoreClass(type)) {
return;
}
final ClassEditor ce = editClass(info);
// Commit all of the class's methods and fields
final MethodInfo[] methods = ce.methods();
for (int i = 0; i < methods.length; i++) {
commit(methods[i]);
}
final FieldInfo[] fields = ce.fields();
for (int i = 0; i < fields.length; i++) {
commit(fields[i]);
}
ce.commit();
ce.setDirty(false);
release(info);
}
public void commit(final MethodInfo info) {
final MethodEditor me = editMethod(info);
me.commit();
// We make the method's class dirty so it, too, will be committed
me.declaringClass().setDirty(true);
me.setDirty(false);
release(info);
}
public void commit(final FieldInfo info) {
final FieldEditor fe = editField(info);
fe.commit();
// We make the method's class dirty so it, too, will be committed
fe.declaringClass().setDirty(true);
fe.setDirty(false);
release(info);
}
public void commit() {
Object[] array = fieldEditors.values().toArray();
for (int i = 0; i < array.length; i++) {
final FieldEditor fe = (FieldEditor) array[i];
if (!ignoreField(fe.memberRef())) {
commit(fe.fieldInfo());
}
}
array = methodEditors.values().toArray();
for (int i = 0; i < array.length; i++) {
final MethodEditor me = (MethodEditor) array[i];
if (!ignoreMethod(me.memberRef())) {
commit(me.methodInfo());
}
}
array = classEditors.values().toArray();
for (int i = 0; i < array.length; i++) {
final ClassEditor ce = (ClassEditor) array[i];
if (!ignoreClass(ce.type())) {
commit(ce.classInfo());
}
}
}
public void commitDirty() {
PersistentBloatContext.comm("Committing dirty data");
// Commit all dirty fields
Object[] array = this.fieldEditors.values().toArray();
for (int i = 0; i < array.length; i++) {
final FieldEditor fe = (FieldEditor) array[i];
if (fe.isDirty() && !ignoreField(fe.memberRef())) {
PersistentBloatContext.comm(" Committing field: "
+ fe.declaringClass().name() + "." + fe.name());
commit(fe.fieldInfo());
}
}
// Commit all dirty methods
array = this.methodEditors.values().toArray();
for (int i = 0; i < array.length; i++) {
final MethodEditor me = (MethodEditor) array[i];
if (me.isDirty() && !ignoreMethod(me.memberRef())) {
PersistentBloatContext.comm(" Committing method: "
+ me.declaringClass().name() + "." + me.name()
+ me.type());
commit(me.methodInfo());
}
}
// Commit all dirty classes
array = this.classEditors.values().toArray();
for (int i = 0; i < array.length; i++) {
final ClassEditor ce = (ClassEditor) array[i];
if (ce.isDirty() && !ignoreClass(ce.type())) {
PersistentBloatContext.comm(" Committing class: " + ce.name());
commit(ce.classInfo());
}
}
}
}

@ -0,0 +1,727 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.decorate;
import java.io.*;
import java.util.*;
import EDU.purdue.cs.bloat.context.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.file.*;
import EDU.purdue.cs.bloat.reflect.*;
import EDU.purdue.cs.bloat.trans.*;
import EDU.purdue.cs.bloat.util.*;
/**
* Inserts residency, update, or swizzle checks into the methods of the classes
* specified on the command line.
*
* Usage: java EDU.purdue.cs.bloat.decorate.Main [-options] classes output_dir
*
* where options include: -help print out this message -v -verbose turn on
* verbose mode (can be given multiple times) -classpath <directories separated
* by colons list directories in which to look for classes -f decorate files
* even if up-to-date -closure recursively decorate referenced classes
* -relax-loading don't report errors if a class is not found -skip
* <class|package.*> skip the given class or package (this option can be given
* more than once) -only <class|package.*> skip all but the given class or
* package (this option can be given more than once) -rc insert residency checks
* (default) -norc don't insert residency checks -uc insert update checks
* (default) -sc insert array swizzle checks (default) -nosc don't insert array
* swizzle checkso
*
*/
public class Main implements Opcode {
private static int VERBOSE = 0; // The level of verbosity
private static boolean FORCE = false;
private static boolean CLOSURE = false;
private static boolean RC = true; // Insert residency checks?
private static boolean UC = true; // Insert update checks?
private static boolean SC = true; // Insert swizzle checks?
private static final List SKIP = new ArrayList();
private static final List ONLY = new ArrayList();
private static final int NONE = 0;
private static final int POINTER = 1;
private static final int SCALAR = 2;
/**
* Parse the command line. Inserts residency, update, and swizzle checks
* into the bytecode of the methods of the specified classes.
*/
public static void main(final String[] args) {
final ClassFileLoader loader = new ClassFileLoader();
List classes = new ArrayList(); // Names of classes from command line
boolean gotdir = false; // Did user specify an output dir?
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-v") || args[i].equals("-verbose")) {
Main.VERBOSE++;
} else if (args[i].equals("-help")) {
Main.usage();
} else if (args[i].equals("-classpath")) {
if (++i >= args.length) {
Main.usage();
}
final String classpath = args[i];
loader.setClassPath(classpath);
} else if (args[i].equals("-skip")) {
if (++i >= args.length) {
Main.usage();
}
final String pkg = args[i].replace('.', '/');
Main.SKIP.add(pkg);
} else if (args[i].equals("-only")) {
if (++i >= args.length) {
Main.usage();
}
final String pkg = args[i].replace('.', '/');
Main.ONLY.add(pkg);
} else if (args[i].equals("-closure")) {
Main.CLOSURE = true;
} else if (args[i].equals("-relax-loading")) {
ClassHierarchy.RELAX = true;
} else if (args[i].equals("-f")) {
Main.FORCE = true;
} else if (args[i].equals("-norc")) {
Main.RC = false;
} else if (args[i].equals("-rc")) {
Main.RC = true;
} else if (args[i].equals("-nouc")) {
Main.UC = false;
} else if (args[i].equals("-uc")) {
Main.UC = true;
} else if (args[i].equals("-nosc")) {
Main.SC = false;
} else if (args[i].equals("-sc")) {
Main.SC = true;
} else if (args[i].startsWith("-")) {
Main.usage();
} else if (i == args.length - 1) {
// Last argument is the name of the outpu directory
final File f = new File(args[i]);
if (f.exists() && !f.isDirectory()) {
System.err.println("No such directory: " + f.getPath());
System.exit(2);
}
loader.setOutputDir(f);
gotdir = true;
} else {
classes.add(args[i]);
}
}
if (!gotdir) {
Main.usage();
}
if (classes.size() == 0) {
Main.usage();
}
if (Main.VERBOSE > 3) {
ClassFileLoader.DEBUG = true;
ClassEditor.DEBUG = true;
}
boolean errors = false;
final Iterator iter = classes.iterator();
// Load each class specified on the command line
while (iter.hasNext()) {
final String name = (String) iter.next();
try {
loader.loadClass(name);
} catch (final ClassNotFoundException ex) {
System.err.println("Couldn't find class: " + ex.getMessage());
errors = true;
}
}
if (errors) {
System.exit(1);
}
final BloatContext context = new CachingBloatContext(loader, classes,
Main.CLOSURE);
if (!Main.CLOSURE) {
final Iterator e = classes.iterator();
while (e.hasNext()) {
final String name = (String) e.next();
try {
final ClassInfo info = loader.loadClass(name);
Main.decorateClass(context, info);
} catch (final ClassNotFoundException ex) {
System.err.println("Couldn't find class: "
+ ex.getMessage());
System.exit(1);
}
}
} else {
classes = null;
final ClassHierarchy hier = context.getHierarchy();
final Iterator e = hier.classes().iterator();
while (e.hasNext()) {
final Type t = (Type) e.next();
if (t.isObject()) {
try {
final ClassInfo info = loader.loadClass(t.className());
Main.decorateClass(context, info);
} catch (final ClassNotFoundException ex) {
System.err.println("Couldn't find class: "
+ ex.getMessage());
System.exit(1);
}
}
}
}
}
private static void usage() {
System.err
.println("Usage: java EDU.purdue.cs.bloat.decorate.Main "
+ "\n [-options] classes output_dir"
+ "\n"
+ "\nwhere options include:"
+ "\n -help print out this message"
+ "\n -v -verbose turn on verbose mode "
+ "(can be given multiple times)"
+ "\n -classpath <directories separated by colons>"
+ "\n list directories in which to look for classes"
+ "\n -f decorate files even if up-to-date"
+ "\n -closure recursively decorate referenced classes"
+ "\n -relax-loading don't report errors if a class is not found"
+ "\n -skip <class|package.*>"
+ "\n skip the given class or package"
+ "\n (this option can be given more than once)"
+ "\n -only <class|package.*>"
+ "\n skip all but the given class or package"
+ "\n (this option can be given more than once)"
+ "\n -rc insert residency checks (default)"
+ "\n -norc don't insert residency checks"
+ "\n -uc insert update checks (default)"
+ "\n -sc insert array swizzle checks (default)"
+ "\n -nosc don't insert array swizzle checks");
System.exit(0);
}
/**
* Adds residency/update/swizzle checks to all of the methods in a given
* class.
*
* @param context
* Information about all the classes we're dealing with
* @param info
* Information about the class we're decorating
*/
private static void decorateClass(final EditorContext context,
final ClassInfo info) {
final ClassFile classFile = (ClassFile) info;
// Check to see if the class file is up-to-date
if (!Main.FORCE) {
final File source = classFile.file();
final File target = classFile.outputFile();
if ((source != null) && (target != null) && source.exists()
&& target.exists()
&& (source.lastModified() < target.lastModified())) {
if (Main.VERBOSE > 1) {
System.out.println(classFile.name() + " is up to date");
}
return;
}
}
if (Main.VERBOSE > 2) {
classFile.print(System.out);
}
final ClassEditor c = context.editClass(info);
boolean skip = false;
final String name = c.type().className();
final String qual = c.type().qualifier() + "/*";
// Edit only classes explicitly mentioned.
if (Main.ONLY.size() > 0) {
skip = true;
// Only edit classes we explicitly don't name.
for (int i = 0; i < Main.ONLY.size(); i++) {
final String pkg = (String) Main.ONLY.get(i);
if (name.equals(pkg) || qual.equals(pkg)) {
skip = false;
break;
}
}
}
// Don't edit classes we explicitly skip.
if (!skip) {
for (int i = 0; i < Main.SKIP.size(); i++) {
final String pkg = (String) Main.SKIP.get(i);
if (name.equals(pkg) || qual.equals(pkg)) {
skip = true;
break;
}
}
}
if (skip) {
if (Main.VERBOSE > 0) {
System.out.println("Skipping " + c.type().className());
}
context.release(info);
return;
}
if (Main.VERBOSE > 0) {
System.out.println("Decorating class " + c.type().className());
}
if (Main.VERBOSE > 2) {
((ClassFile) info).print(System.out);
}
final MethodInfo[] methods = c.methods();
// Add residency checks (via transform()) to each method in the class
for (int j = 0; j < methods.length; j++) {
MethodEditor m;
try {
m = context.editMethod(methods[j]);
} catch (final ClassFormatException ex) {
System.err.println(ex.getMessage());
continue;
}
Main.transform(m);
context.commit(methods[j]);
}
context.commit(info);
}
/**
* Inserts residency/update/swizzle checks into a method. Iterates over the
* bytecodes in the method and inserts the appropriate residency opcode.
*
* @param method
* The method to which to add checks.
*
* @see MethodEditor#code
*/
private static void transform(final MethodEditor method) {
if (Main.VERBOSE > 1) {
System.out.println("Decorating method " + method);
}
// Optimize initialization of arrays to speed things up.
CompactArrayInitializer.transform(method);
final ListIterator iter = method.code().listIterator();
// Go through the code (Instructions and Labels) in the method
INST: while (iter.hasNext()) {
final Object ce = iter.next();
if (Main.VERBOSE > 2) {
System.out.println("Examining " + ce);
}
if (ce instanceof Instruction) {
final Instruction inst = (Instruction) ce;
int uctype = Main.NONE; // Type of update check (POINTER or
// SCALAR)
boolean insert_sc = false; // Insert swizzle check (aaload
// only)?
final int opc = inst.opcodeClass();
int depth;
switch (opc) {
case opcx_arraylength:
case opcx_athrow:
case opcx_getfield:
case opcx_instanceof: {
depth = 0;
break;
}
case opcx_iaload:
case opcx_laload:
case opcx_faload:
case opcx_daload:
case opcx_baload:
case opcx_caload:
case opcx_saload: {
depth = 1;
break;
}
case opcx_aaload: {
depth = 1;
insert_sc = true;
break;
}
case opcx_iastore:
case opcx_fastore:
case opcx_aastore:
case opcx_bastore:
case opcx_castore:
case opcx_sastore: {
depth = 2;
break;
}
case opcx_lastore:
case opcx_dastore: {
depth = 3;
break;
}
case opcx_putfield: {
final MemberRef ref = (MemberRef) inst.operand();
depth = ref.type().stackHeight();
if (ref.type().isReference()) {
uctype = Main.POINTER;
} else {
uctype = Main.SCALAR;
}
break;
}
case opcx_invokevirtual:
case opcx_invokespecial:
case opcx_invokeinterface: {
final MemberRef ref = (MemberRef) inst.operand();
depth = ref.type().stackHeight();
break;
}
case opcx_rc: {
// Skip any existing residency checks.
iter.remove();
continue INST;
}
case opcx_aupdate: {
// Skip any existing update checks.
iter.remove();
continue INST;
}
case opcx_supdate: {
// Skip any existing update checks.
iter.remove();
continue INST;
}
default: {
continue INST;
}
}
Instruction addInst;
// Insert a residency check...
if (Main.RC) {
Object t;
// //////////////////////////////////
// Before...
// +-----+------+-----------+
// | ... | inst | afterInst |
// +-----+------+-----------+
// ^prev ^next
//
// After...
// +-----+----+------+-----------+
// | ... | RC | inst | afterInst |
// +-----+----+------+-----------+
// ^prev ^next
// //////////////////////////////////
// +-----+------+-----------+
// | ... | inst | afterInst |
// +-----+------+-----------+
// ^prev ^next
t = iter.previous();
Assert.isTrue(t == inst, t + " != " + inst);
// +-----+------+-----------+
// | ... | inst | afterInst |
// +-----+------+-----------+
// ^prev ^next
addInst = new Instruction(Opcode.opcx_rc,
new Integer(depth));
iter.add(addInst);
// +-----+----+------+-----------+
// | ... | RC | inst | afterInst |
// +-----+----+------+-----------+
// ^prev ^next
t = iter.previous();
Assert.isTrue(t == addInst, t + " != " + addInst);
// +-----+----+------+-----------+
// | ... | RC | inst | afterInst |
// +-----+----+------+-----------+
// ^prev ^next
t = iter.next();
Assert.isTrue(t == addInst, t + " != " + addInst);
// +-----+----+------+-----------+
// | ... | RC | inst | afterInst |
// +-----+----+------+-----------+
// ^prev ^next
t = iter.next();
Assert.isTrue(t == inst, t + " != " + inst);
// +-----+----+------+-----------+
// | ... | RC | inst | afterInst |
// +-----+----+------+-----------+
// ^prev ^next
if (Main.VERBOSE > 2) {
System.out.println("Inserting " + addInst + " before "
+ inst);
}
} else {
if (Main.VERBOSE > 2) {
System.out.println("Not inserting rc before " + inst);
}
}
// Insert a swizzle check...
if (insert_sc) {
if (Main.SC) {
Object t;
// ////////////////////////////////////////////
// Before...
// +-----+------+-----------+
// | ... | inst | afterInst |
// +-----+------+-----------+
// ^prev ^next
//
// After...
// +-----+------+----------+------+-----------+
// | ... | dup2 | aswizzle | inst | afterInst |
// +-----+------+----------+------+-----------+
// ^prev ^next
// /////////////////////////////////////////////
// +-----+------+-----------+
// | ... | inst | afterInst |
// +-----+------+-----------+
// ^prev ^next
t = iter.previous();
Assert.isTrue(t == inst, t + " != " + inst);
// +-----+------+-----------+
// | ... | inst | afterInst |
// +-----+------+-----------+
// ^prev ^next
addInst = new Instruction(Opcode.opcx_dup2);
iter.add(addInst);
// +-----+------+------+-----------+
// | ... | dup2 | inst | afterInst |
// +-----+------+------+-----------+
// ^prev ^next
t = iter.previous();
Assert.isTrue(t == addInst, t + " != " + addInst);
// +-----+------+------+-----------+
// | ... | dup2 | inst | afterInst |
// +-----+------+------+-----------+
// ^prev ^next
t = iter.next();
Assert.isTrue(t == addInst, t + " != " + addInst);
// +-----+------+------+-----------+
// | ... | dup2 | inst | afterInst |
// +-----+------+------+-----------+
// ^prev ^next
addInst = new Instruction(Opcode.opcx_aswizzle);
iter.add(addInst);
// +-----+------+----------+------+-----------+
// | ... | dup2 | aswizzle | inst | afterInst |
// +-----+------+----------+------+-----------+
// ^prev ^next
t = iter.previous();
Assert.isTrue(t == addInst, t + " != " + addInst);
// +-----+------+----------+------+-----------+
// | ... | dup2 | aswizzle | inst | afterInst |
// +-----+------+----------+------+-----------+
// ^prev ^next
t = iter.next();
Assert.isTrue(t == addInst, t + " != " + addInst);
// +-----+------+----------+------+-----------+
// | ... | dup2 | aswizzle | inst | afterInst |
// +-----+------+----------+------+-----------+
// ^prev ^next
t = iter.next();
Assert.isTrue(t == inst, t + " != " + inst);
// +-----+------+----------+------+-----------+
// | ... | dup2 | aswizzle | inst | afterInst |
// +-----+------+----------+------+-----------+
// ^prev ^next
if (Main.VERBOSE > 2) {
System.out
.println("Inserting dup2,aswizzle before "
+ inst);
}
}
else {
if (Main.VERBOSE > 2) {
System.out.println("Not inserting aswizzle before "
+ inst);
}
}
}
// Insert an update check...
if (uctype != Main.NONE) {
if (Main.UC) {
Object t;
// ////////////////////////////////////////////
// Before...
// +-----+------+-----------+
// | ... | inst | afterInst |
// +-----+------+-----------+
// ^prev ^next
//
// After...
// +-----+---------+------+-----------+
// | ... | aupdate | inst | afterInst |
// +-----+---------+------+-----------+
// ^prev ^next
// /////////////////////////////////////////////
// +-----+------+-----------+
// | ... | inst | afterInst |
// +-----+------+-----------+
// ^prev ^next
t = iter.previous();
Assert.isTrue(t == inst, t + " != " + inst);
// +-----+------+-----------+
// | ... | inst | afterInst |
// +-----+------+-----------+
// ^prev ^next
addInst = new Instruction(Opcode.opcx_aupdate,
new Integer(depth));
/*
* if (uctype == POINTER) { addInst = new
* Instruction(opcx_aupdate, new Integer(depth)); } else {
* addInst = new Instruction(opcx_supdate, new
* Integer(depth)); }
*/
iter.add(addInst);
// +-----+---------+------+-----------+
// | ... | aupdate | inst | afterInst |
// +-----+---------+------+-----------+
// ^prev ^next
t = iter.previous();
Assert.isTrue(t == addInst, t + " != " + addInst);
// +-----+---------+------+-----------+
// | ... | aupdate | inst | afterInst |
// +-----+---------+------+-----------+
// ^prev ^next
t = iter.next();
Assert.isTrue(t == addInst, t + " != " + addInst);
// +-----+---------+------+-----------+
// | ... | aupdate | inst | afterInst |
// +-----+---------+------+-----------+
// ^prev ^next
t = iter.next();
Assert.isTrue(t == inst, t + " != " + inst);
// +-----+---------+------+-----------+
// | ... | aupdate | inst | afterInst |
// +-----+---------+------+-----------+
// ^prev ^next
if (Main.VERBOSE > 2) {
System.out.println("Inserting " + addInst
+ " before " + inst);
}
} else if (Main.VERBOSE > 2) {
System.out.println("Not inserting uc before " + inst);
}
}
}
}
}
}

@ -0,0 +1,23 @@
# All files in the distribution of BLOAT (Bytecode Level Optimization and
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
# Research Foundation of Purdue University. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
CLASS = \
Main.class
include ../class.mk

@ -0,0 +1,8 @@
<html>
<body>
<p>Contains a program that decorates a Java classfile with opcodes
used in a persistent Java virtual machine.</p>
</body>
</html>

@ -0,0 +1,514 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Demand-driven Induction Variable Analysis (diva)*/
package EDU.purdue.cs.bloat.diva;
import java.util.*;
import EDU.purdue.cs.bloat.cfg.*;
import EDU.purdue.cs.bloat.ssa.*;
import EDU.purdue.cs.bloat.tree.*;
/**
* InductionVarAnalyzer traverses a control flow graph and looks for array
* element swizzle operations inside loops. If possible, these swizzle
* operations are hoisted out of the loop and are replaced with range swizzle
* operations. The technique used is Demand-driven Induction Variable Analysis
* (DIVA).
* <p>
* To accomplish its tasks, InductionVarAnalyzer keeps track of a number of
* induction variables (represented by Swizzler objects) and local variables.
*
* @see Swizzler
* @see LocalExpr
*/
public class InductionVarAnalyzer {
public static boolean DEBUG = false;
SSAGraph ssaGraph;
FlowGraph CFG; // Control flow graph being operated on
HashMap IndStore; // Stores induction variables and
// associated swizzlers
HashMap LocalStore; // Stores local variables
Expr ind_var = null; // An induction variable
Expr ind_init = null; // Initial value of an induction variable
Expr ind_term = null; // Not used???
Expr ind_inc = null; // Expression used to increment induction
// variable (all uses commented out)
Expr tgt = null; // Target of the phi statement that defines
// an induction var
boolean changed = false; // Was the cfg changed?
/**
* Searches the list of induction variables for an induction variable with a
* given value number.
*
* @param vn
* Value number to search for.
*
* @return Swizzler object whose target has the desired value number or
* null, if value number is not found.
*/
public Object get_swizzler(final int vn) {
final Iterator iter = IndStore.values().iterator();
while (iter.hasNext()) {
final Swizzler swz = (Swizzler) iter.next();
if ((swz.target().valueNumber() == vn)
|| (swz.ind_var().valueNumber() == vn)) {
return swz;
}
}
return null;
}
/**
* Searchs the stored list of local variables for a local variable with a
* given value number.
*
* @param vn
* The value number to search for.
*
* @return The local variable with the given value number, or null, if not
* found.
*/
public MemExpr get_local(final int vn) {
final Iterator iter = LocalStore.keySet().iterator();
while (iter.hasNext()) {
final MemExpr le = (MemExpr) iter.next();
if (le.valueNumber() == vn) {
return le;
}
}
return null;
}
/**
* Displays (to System.out) all of the Swizzlers stored in the induction
* variable store.
*
* @see Swizzler
*/
public void Display_store() {
final Iterator iter = IndStore.values().iterator();
while (iter.hasNext()) {
final Swizzler indswz = (Swizzler) iter.next();
System.out.println("\nIV: " + indswz.ind_var() + " tgt: "
+ indswz.target() + "\narray: " + indswz.array()
+ " init: " + indswz.init_val() + " end: "
+ indswz.end_val());
}
}
/**
* Displays the contents of a Swizzler object to System.out.
*
* @param indswz
* The Swizzler to display.
*/
public void displaySwizzler(final Swizzler indswz) {
System.out.println("\nIV: " + indswz.ind_var() + "vn:"
+ indswz.ind_var().valueNumber() + " tgt: " + indswz.target()
+ "vn:" + indswz.target().valueNumber() + "\narray: "
+ indswz.array() + " init: " + indswz.init_val() + " end: "
+ indswz.end_val());
}
/**
* Adds a swizzle range statement (SRStmt) to the end of each predacessor
* block of the block containing the phi statement that defines an induction
* variable provided that the phi block does not dominate its predacessor.
* Phew.
*
* @param indswz
* Swizzler representing the induction variable on which the
* range swizzle statement is added.
*/
public void insert_aswrange(final Swizzler indswz) {
final Iterator iter = CFG.preds(indswz.phi_block()).iterator();
while (iter.hasNext()) {
final Block blk = (Block) iter.next();
if (!indswz.phi_block().dominates(blk)) {
final SRStmt aswrange = new SRStmt((Expr) indswz.array()
.clone(), (Expr) indswz.init_val().clone(),
(Expr) indswz.end_val().clone());
blk.tree().addStmtBeforeJump(aswrange);
changed = true;
if (InductionVarAnalyzer.DEBUG) {
System.out.println("Inserted ASWR: " + aswrange
+ "\nin block: " + blk);
System.out.println("$$$ can insert aswrange now\n"
+ "array: " + indswz.array() + "\nIV: "
+ indswz.ind_var() + "\ninit: " + indswz.init_val()
+ "\nend: " + indswz.end_val());
}
}
}
}
/* To determine if a phi statement is a mu */
/* Returns null if not a MU and sets ind_var & ind_init */
/* to refer to the IV & its initial value otherwise */
/**
* Determines whether or not a phi statement is a mu function. A mu function
* is a phi function that merges values at loop headers, as opposed to those
* that occur as a result of forward branching. Mu functions always have two
* arguments.
*
* @param phi
* phi statement that may be a mu function
* @param cfg
* CFG to look through <font color="ff0000">Get rid of this
* parameter</font>
*
* @return The block containing the mu functions external (that is, outside
* the loop) argument, also known as the external ssalink. If the
* phi statement is not a mu function, null is returned.
*/
public Block isMu(final PhiJoinStmt phi, final FlowGraph cfg) {
// Does it contain two operands?
if (phi.numOperands() != 2) {
return null;
}
// Is it REDUCIBLE?
if ((cfg.blockType(phi.block()) == Block.IRREDUCIBLE)
|| (cfg.blockType(phi.block()) == Block.NON_HEADER)) {
return null;
}
/*
* Does one of them dominate the phi and the other dominated by the phi?
*/
final Iterator iter = cfg.preds(phi.block()).iterator();
final Block pred1 = (Block) iter.next();
final Block pred2 = (Block) iter.next();
if (pred1.dominates(phi.block()) && phi.block().dominates(pred2)
&& (pred1 != phi.block())) {
if (InductionVarAnalyzer.DEBUG) {
System.out.println("Extlink = 1 pred1:" + pred1 + " pred2:"
+ pred2);
}
ind_var = phi.operandAt(pred2);
ind_init = phi.operandAt(pred1);
return pred1;
}
if (pred2.dominates(phi.block()) && phi.block().dominates(pred1)
&& (pred2 != phi.block())) {
if (InductionVarAnalyzer.DEBUG) {
System.out.println("Extlink = 2 pred1:" + pred1 + " pred2:"
+ pred2);
}
ind_var = phi.operandAt(pred1);
ind_init = phi.operandAt(pred2);
return pred2;
}
return null;
}
/**
* Performs DIVA on a CFG public static void transform(FlowGraph cfg) { //
* Create a new instance to allow multiple threads. InductionVarAnalyzer me =
* new InductionVarAnalyzer(); me.transform(cfg); }
*/
/**
* Performs DIVA on a CFG.
*/
public void transform(final FlowGraph cfg) {
ssaGraph = new SSAGraph(cfg);
CFG = cfg;
IndStore = new HashMap();
LocalStore = new HashMap();
changed = false;
if (InductionVarAnalyzer.DEBUG) {
System.out
.println("----------Before visitComponents--------------");
cfg.print(System.out);
}
// Visit each strongly connected component (SCC) in the CFG. SCCs
// correspond to sequences in the program. Visit each node in the
// SCC and build the local variable store. Create Swizzlers at
// PhiStmts, if approproate, and store them in the induction
// variable store. If it can be determined that an array element
// swizzle can be hoisted out of a loop, it is hoisted.
ssaGraph.visitComponents(new ComponentVisitor() {
public void visitComponent(final List scc) {
if (InductionVarAnalyzer.DEBUG) {
System.out.println("SCC =");
}
final Iterator e = scc.iterator();
while (e.hasNext()) {
final Node v = (Node) e.next();
if (InductionVarAnalyzer.DEBUG) {
System.out.println(" " + v + "{" + v.key() + "} "
+ v.getClass());
}
v.visit(new TreeVisitor() {
public void visitPhiJoinStmt(final PhiJoinStmt phi) {
if (isMu(phi, CFG) != null) {
tgt = phi.target();
if (InductionVarAnalyzer.DEBUG) {
System.out.println("IV:" + ind_var + " VN:"
+ ind_var.valueNumber() + "\ninit:"
+ ind_init + " target: " + tgt
+ " VN: " + tgt.valueNumber());
}
final Swizzler swz = new Swizzler(ind_var, tgt,
ind_init, phi.block());
if (InductionVarAnalyzer.DEBUG) {
System.out
.println("store swizzler for "
+ ind_var.def() + " & "
+ tgt.def());
displaySwizzler(swz);
}
if (ind_var.def() != null) {
IndStore.put(ind_var.def(), swz);
}
if (tgt.def() != null) {
IndStore.put(tgt.def(), swz);
}
if (InductionVarAnalyzer.DEBUG) {
System.out.println(" Mu: " + phi + "{"
+ phi.key() + "}");
}
} else {
if (InductionVarAnalyzer.DEBUG) {
System.out.println("Phi: " + phi + "{"
+ phi.key() + "}");
}
}
}
public void visitLocalExpr(final LocalExpr me) {
if (me.def() != null) {
if (LocalStore.get(me.def()) == null) {
LocalStore.put(me.def(), me);
}
}
if (LocalStore.get(me) == null) {
LocalStore.put(me, me);
}
if (InductionVarAnalyzer.DEBUG) {
System.out.println("stored ME: " + me
+ " vn: " + me.valueNumber());
}
}
public void visitStaticFieldExpr(
final StaticFieldExpr me) {
if (me.def() != null) {
if (LocalStore.get(me.def()) == null) {
LocalStore.put(me.def(), me);
}
}
if (LocalStore.get(me) == null) {
LocalStore.put(me, me);
}
if (InductionVarAnalyzer.DEBUG) {
System.out.println("stored ME: " + me
+ " vn: " + me.valueNumber());
}
}
public void visitIfCmpStmt(final IfCmpStmt cmp) {
Swizzler indswz = null;
boolean set_term = false;
if (cmp.left().def() != null) {
indswz = (Swizzler) IndStore.get(cmp.left()
.def());
}
if (indswz != null) {
if (InductionVarAnalyzer.DEBUG) {
displaySwizzler(indswz);
}
if (indswz.end_val() == null) {
indswz.set_end_val(cmp.right());
set_term = true;
if (InductionVarAnalyzer.DEBUG) {
System.out.println("Set end_val of "
+ indswz.ind_var() + " to "
+ cmp.right());
}
}
} else {
if (cmp.right().def() != null) {
indswz = (Swizzler) IndStore.get(cmp
.right().def());
}
if (indswz != null) {
if (InductionVarAnalyzer.DEBUG) {
displaySwizzler(indswz);
}
if (indswz.end_val() == null) {
indswz.set_end_val(cmp.left());
set_term = true;
if (InductionVarAnalyzer.DEBUG) {
System.out
.println("Set end_val of "
+ indswz.ind_var()
+ " to "
+ cmp.left());
}
}
}
}
if (set_term && (indswz != null)
&& (indswz.array() != null)) {
indswz.aswizzle().set_redundant(true);
insert_aswrange(indswz);
}
}
public void visitSCStmt(final SCStmt sc) {
Swizzler indswz;
MemExpr le = null;
if (InductionVarAnalyzer.DEBUG) {
System.out.println("SC: array= " + sc.array()
+ " VN:" + sc.array().valueNumber()
+ "\nindex=" + sc.index() + " VN:"
+ sc.index().valueNumber());
}
indswz = (Swizzler) get_swizzler(sc.index()
.valueNumber());
if (indswz != null) {
if (InductionVarAnalyzer.DEBUG) {
displaySwizzler(indswz);
}
if (indswz.array() == null) {
le = get_local(sc.array().valueNumber());
if ((le == null)
&& (sc.array().def() != null)) {
le = get_local(sc.array().def()
.valueNumber());
}
if (le != null) {
if (InductionVarAnalyzer.DEBUG) {
System.out.println("Le: " + le);
}
indswz.set_array(le);
indswz.set_aswizzle(sc);
} else {
return;
}
}
if (indswz.end_val() != null) {
sc.set_redundant(true);
insert_aswrange(indswz);
}
}
}
/*
* public void visitStoreExpr(StoreExpr ind_store) {
* if(ind_var != null) {
* if(ind_var.equalsExpr(ind_store.target())) { if (tgt !=
* null && ind_store.expr() instanceof ArithExpr){
* ArithExpr ind_exp = (ArithExpr)ind_store.expr();
* if(tgt.equalsExpr(ind_exp.left())) ind_inc =
* ind_exp.right(); else if(tgt.equals(ind_exp.right()))
* ind_inc = ind_exp.left(); else { ind_inc = null;
* return; } System.out.println("Ind_inc: "+ind_inc); } } } }
*/
});
}
}
});
if (InductionVarAnalyzer.DEBUG) {
System.out.println("------------After visitComponents---------");
cfg.print(System.out);
}
// If the CFG changed (i.e. if an array range swizzle was added),
// traverse
// the graph and remove redundent swizzle statements.
if (changed) {
cfg.visit(new TreeVisitor() {
ListIterator iter;
public void visitTree(final Tree tree) {
iter = tree.stmts().listIterator();
while (iter.hasNext()) {
final Stmt stmt = (Stmt) iter.next();
stmt.visit(this);
}
}
public void visitSCStmt(final SCStmt sc) {
Object dup2stmt;
if (sc.redundant()) {
iter.remove();
dup2stmt = iter.previous();
iter.remove();
if (InductionVarAnalyzer.DEBUG) {
System.out.println("Removed Redundant ASW: " + sc
+ "\nand " + dup2stmt);
}
}
}
});
}
if (InductionVarAnalyzer.DEBUG) {
System.out.println("----------------After cfg.visit--------------");
cfg.print(System.out);
}
}
}

@ -0,0 +1,637 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.diva;
import java.io.*;
import java.util.*;
import EDU.purdue.cs.bloat.cfg.*;
import EDU.purdue.cs.bloat.codegen.*;
import EDU.purdue.cs.bloat.context.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.file.*;
import EDU.purdue.cs.bloat.reflect.*;
import EDU.purdue.cs.bloat.ssa.*;
import EDU.purdue.cs.bloat.tbaa.*;
import EDU.purdue.cs.bloat.trans.*;
import EDU.purdue.cs.bloat.tree.*;
/**
* Performs a number of analyses on the methods of some specified classes.
* However, it does not perform some of the optimizations that optimize.Main
* does.
*
* @see EDU.purdue.cs.bloat.optimize.Main
*/
public class Main {
static boolean DEBUG = false;
static boolean VERBOSE = false;
static boolean FORCE = false;
static boolean CLOSURE = false;
static boolean PRE = true;
static boolean DCE = true;
static boolean PROP = true;
static boolean FOLD = true;
static boolean STACK_ALLOC = false;
static boolean COMPACT_ARRAY_INIT = true;
static boolean ANNO = true;
static String[] ARGS = null;
static List SKIP = new ArrayList();
static List ONLY = new ArrayList();
static String METHOD = null;
static BloatContext context = null;
static ClassFileLoader loader = null;
public static void main(final String[] args) {
try {
Main.loader = new ClassFileLoader();
List classes = new ArrayList(args.length);
boolean gotdir = false;
Main.ARGS = args;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-v") || args[i].equals("-verbose")) {
Main.VERBOSE = true;
Main.loader.setVerbose(true);
} else if (args[i].equals("-debug")) {
Main.DEBUG = true;
Main.loader.setVerbose(true);
ClassFileLoader.DEBUG = true;
CompactArrayInitializer.DEBUG = true;
ClassEditor.DEBUG = true;
FlowGraph.DEBUG = true;
DominatorTree.DEBUG = true;
Tree.DEBUG = true;
CodeGenerator.DEBUG = true;
Liveness.DEBUG = true;
SSA.DEBUG = true;
SSAGraph.DEBUG = true;
PersistentCheckElimination.DEBUG = true;
ValueNumbering.DEBUG = true;
ValueFolding.DEBUG = true;
ClassHierarchy.DEBUG = true;
TypeInference.DEBUG = true;
SSAPRE.DEBUG = true;
StackPRE.DEBUG = true;
ExprPropagation.DEBUG = true;
DeadCodeElimination.DEBUG = true;
} else if (args[i].equals("-help")) {
Main.usage();
} else if (args[i].equals("-noanno")) {
Main.ANNO = false;
} else if (args[i].equals("-anno")) {
Main.ANNO = true;
} else if (args[i].equals("-preserve-debug")) {
MethodEditor.PRESERVE_DEBUG = true;
} else if (args[i].equals("-nouse-stack-vars")) {
Tree.USE_STACK = false;
} else if (args[i].equals("-use-stack-vars")) {
Tree.USE_STACK = true;
} else if (args[i].equals("-nocompact-array-init")) {
Main.COMPACT_ARRAY_INIT = false;
} else if (args[i].equals("-compact-array-init")) {
Main.COMPACT_ARRAY_INIT = true;
} else if (args[i].equals("-nostack-alloc")) {
Main.STACK_ALLOC = false;
} else if (args[i].equals("-stack-alloc")) {
Main.STACK_ALLOC = true;
} else if (args[i].equals("-peel-loops")) {
if (++i >= args.length) {
Main.usage();
}
final String n = args[i];
if (n.equals("all")) {
FlowGraph.PEEL_LOOPS_LEVEL = FlowGraph.PEEL_ALL_LOOPS;
} else {
try {
FlowGraph.PEEL_LOOPS_LEVEL = Integer.parseInt(n);
if (FlowGraph.PEEL_LOOPS_LEVEL < 0) {
Main.usage();
}
} catch (final NumberFormatException ex) {
Main.usage();
}
}
} else if (args[i].equals("-color")) {
Liveness.UNIQUE = false;
} else if (args[i].equals("-nocolor")) {
Liveness.UNIQUE = true;
} else if (args[i].equals("-only-method")) {
if (++i >= args.length) {
Main.usage();
}
Main.METHOD = args[i];
} else if (args[i].equals("-print-flow-graph")) {
FlowGraph.PRINT_GRAPH = true;
} else if (args[i].equals("-classpath")) {
if (++i >= args.length) {
Main.usage();
}
final String classpath = args[i];
Main.loader.setClassPath(classpath);
} else if (args[i].equals("-skip")) {
if (++i >= args.length) {
Main.usage();
}
final String pkg = args[i].replace('.', '/');
Main.SKIP.add(pkg);
} else if (args[i].equals("-only")) {
if (++i >= args.length) {
Main.usage();
}
final String pkg = args[i].replace('.', '/');
Main.ONLY.add(pkg);
} else if (args[i].equals("-nodce")) {
Main.DCE = false;
} else if (args[i].equals("-noprop")) {
Main.PROP = false;
} else if (args[i].equals("-nopre")) {
Main.PRE = false;
} else if (args[i].equals("-dce")) {
Main.DCE = true;
} else if (args[i].equals("-prop")) {
Main.PROP = true;
} else if (args[i].equals("-pre")) {
Main.PRE = true;
} else if (args[i].equals("-closure")) {
Main.CLOSURE = true;
} else if (args[i].equals("-relax-loading")) {
ClassHierarchy.RELAX = true;
} else if (args[i].equals("-f")) {
Main.FORCE = true;
} else if (args[i].startsWith("-")) {
Main.usage();
} else if (i == args.length - 1) {
final File f = new File(args[i]);
if (f.exists() && !f.isDirectory()) {
System.err.println("No such directory: " + f.getPath());
System.exit(2);
}
if (!f.exists()) {
f.mkdirs();
}
if (!f.exists()) {
System.err.println("Couldn't create directory: "
+ f.getPath());
System.exit(2);
}
Main.loader.setOutputDir(f);
gotdir = true;
} else {
classes.add(args[i]);
}
}
if (!gotdir) {
Main.usage();
}
if (classes.size() == 0) {
Main.usage();
}
boolean errors = false;
final Iterator iter = classes.iterator();
while (iter.hasNext()) {
final String name = (String) iter.next();
try {
Main.loader.loadClass(name);
} catch (final ClassNotFoundException ex) {
System.err.println("Couldn't find class: "
+ ex.getMessage());
errors = true;
}
}
if (errors) {
System.exit(1);
}
Main.context = new CachingBloatContext(Main.loader, classes,
Main.CLOSURE);
if (!Main.CLOSURE) {
final Iterator e = classes.iterator();
while (e.hasNext()) {
final String name = (String) e.next();
Main.editClass(name);
}
} else {
classes = null;
final Iterator e = Main.context.getHierarchy().classes()
.iterator();
while (e.hasNext()) {
final Type t = (Type) e.next();
if (t.isObject()) {
Main.editClass(t.className());
}
}
}
} catch (final ExceptionInInitializerError ex) {
ex.printStackTrace();
System.out.println(ex.getException());
}
}
private static void usage() {
System.err
.println("Usage: java EDU.purdue.cs.bloat.optimize.Main"
+ "\n [-options] classes dir"
+ "\n"
+ "\nwhere options include:"
+ "\n -help print out this message"
+ "\n -v -verbose turn on verbose mode"
+ "\n -debug display a hideous amount of debug info"
+ "\n -classpath <directories separated by colons>"
+ "\n list directories in which to look for classes"
+ "\n -f optimize files even if up-to-date"
+ "\n -closure recursively optimize referenced classes"
+ "\n -relax-loading don't report errors if a class is not found"
+ "\n -skip <class|package.*>"
+ "\n skip the given class or package"
+ "\n -only <class|package.*>"
+ "\n skip all but the given class or package"
+ "\n -preserve-debug try to preserve debug information"
+ "\n -[no]anno insert an annotation in the contant pool"
+ "\n -[no]stack-alloc try to push locals onto the operand stack"
+ "\n -peel-loops <n|all>"
+ "\n peel innermost loops to enable code hoisting"
+ "\n (n >= 0 is the maximum loop level to peel)"
+ "\n -[no]pre perform partial redundency elimination"
+ "\n -[no]dce perform dead code elimination"
+ "\n -[no]prop perform copy and constant propagation"
+ "");
System.exit(0);
}
private static void editClass(final String className) {
ClassFile classFile;
try {
classFile = (ClassFile) Main.loader.loadClass(className);
} catch (final ClassNotFoundException ex) {
System.err.println("Couldn't find class: " + ex.getMessage());
return;
}
if (!Main.FORCE) {
final File source = classFile.file();
final File target = classFile.outputFile();
if ((source != null) && (target != null) && source.exists()
&& target.exists()
&& (source.lastModified() < target.lastModified())) {
if (Main.VERBOSE) {
System.out.println(classFile.name() + " is up to date");
}
return;
}
}
if (Main.DEBUG) {
classFile.print(System.out);
}
final ClassEditor c = Main.context.editClass(classFile);
boolean skip = false;
final String name = c.type().className();
final String qual = c.type().qualifier() + "/*";
// Edit only classes explicitly mentioned.
if (Main.ONLY.size() > 0) {
skip = true;
// Only edit classes we explicitly don't name.
for (int i = 0; i < Main.ONLY.size(); i++) {
final String pkg = (String) Main.ONLY.get(i);
if (name.equals(pkg) || qual.equals(pkg)) {
skip = false;
break;
}
}
}
// Don't edit classes we explicitly skip.
if (!skip) {
for (int i = 0; i < Main.SKIP.size(); i++) {
final String pkg = (String) Main.SKIP.get(i);
if (name.equals(pkg) || qual.equals(pkg)) {
skip = true;
break;
}
}
}
if (skip) {
if (Main.VERBOSE) {
System.out.println("Skipping " + c.type().className());
}
Main.context.release(classFile);
return;
}
if (Main.VERBOSE) {
System.out.println("Optimizing " + c.type().className());
}
final MethodInfo[] methods = c.methods();
for (int j = 0; j < methods.length; j++) {
final MethodEditor m;
try {
m = Main.context.editMethod(methods[j]);
} catch (final ClassFormatException ex) {
System.err.println(ex.getMessage());
continue;
}
if ((Main.METHOD != null) && !m.name().equals(Main.METHOD)) {
Main.context.release(methods[j]);
continue;
}
if (Main.DEBUG) {
m.print(System.out);
}
if (m.isNative() || m.isAbstract()) {
Main.context.release(methods[j]);
continue;
}
if (Main.COMPACT_ARRAY_INIT) {
CompactArrayInitializer.transform(m);
if (Main.DEBUG) {
System.out.println("---------- After compaction:");
m.print(System.out);
System.out.println("---------- end print");
}
}
FlowGraph cfg;
try {
cfg = new FlowGraph(m);
} catch (final ClassFormatException ex) {
System.err.println(ex.getMessage());
Main.context.release(methods[j]);
continue;
}
SSA.transform(cfg);
if (FlowGraph.DEBUG) {
System.out.println("---------- After SSA:");
cfg.print(System.out);
System.out.println("---------- end print");
}
if (Main.DEBUG) {
cfg.visit(new VerifyCFG(false));
}
// Do copy propagation first to get rid of all the extra copies
// inserted for dups. If they're left it, it really slows down
// value numbering.
if (Main.PROP) {
if (Main.DEBUG) {
System.out.println("-------Before Copy Propagation-------");
}
final ExprPropagation copy = new ExprPropagation(cfg);
copy.transform();
if (Main.DEBUG) {
cfg.visit(new VerifyCFG(false));
}
if (Main.DEBUG) {
System.out.println("--------After Copy Propagation-------");
cfg.print(System.out);
}
}
if (Main.DCE) {
if (Main.DEBUG) {
System.out.println("-----Before Dead Code Elimination----");
}
final DeadCodeElimination dce = new DeadCodeElimination(cfg);
dce.transform();
if (Main.DEBUG) {
cfg.visit(new VerifyCFG(false));
}
if (Main.DEBUG) {
System.out.println("-----After Dead Code Elimination-----");
cfg.print(System.out);
}
}
if (Main.DEBUG) {
System.out.println("---------Doing type inference--------");
}
TypeInference.transform(cfg, Main.context.getHierarchy());
if (Main.DEBUG) {
System.out.println("--------Doing value numbering--------");
}
(new ValueNumbering()).transform(cfg);
if (Main.FOLD) {
if (Main.DEBUG) {
System.out.println("--------Before Value Folding---------");
}
(new ValueFolding()).transform(cfg);
if (Main.DEBUG) {
cfg.visit(new VerifyCFG());
}
if (Main.DEBUG) {
System.out.println("---------After Value Folding---------");
cfg.print(System.out);
}
}
if (Main.PRE) {
if (Main.DEBUG) {
System.out.println("-------------Before SSAPRE-----------");
}
final SSAPRE pre = new SSAPRE(cfg, Main.context);
pre.transform();
if (Main.DEBUG) {
cfg.visit(new VerifyCFG());
}
if (Main.DEBUG) {
System.out.println("-------------After SSAPRE------------");
cfg.print(System.out);
}
}
if (Main.FOLD) {
if (Main.DEBUG) {
System.out.println("--------Before Value Folding---------");
}
(new ValueFolding()).transform(cfg);
if (Main.DEBUG) {
cfg.visit(new VerifyCFG());
}
if (Main.DEBUG) {
System.out.println("---------After Value Folding---------");
cfg.print(System.out);
}
}
if (Main.PROP) {
if (Main.DEBUG) {
System.out.println("-------Before Copy Propagation-------");
}
final ExprPropagation copy = new ExprPropagation(cfg);
copy.transform();
if (Main.DEBUG) {
cfg.visit(new VerifyCFG());
}
if (Main.DEBUG) {
System.out.println("--------After Copy Propagation-------");
cfg.print(System.out);
}
}
if (Main.DCE) {
if (Main.DEBUG) {
System.out.println("-----Before Dead Code Elimination----");
}
final DeadCodeElimination dce = new DeadCodeElimination(cfg);
dce.transform();
if (Main.DEBUG) {
cfg.visit(new VerifyCFG());
}
if (Main.DEBUG) {
System.out.println("-----After Dead Code Elimination-----");
cfg.print(System.out);
}
}
(new PersistentCheckElimination()).transform(cfg);
(new InductionVarAnalyzer()).transform(cfg);
/*
* if (STACK_ALLOC) { if (DEBUG) {
* System.out.println("------------Before StackPRE----------"); }
*
* StackPRE pre = new StackPRE(cfg); pre.transform();
*
* if (DEBUG) { cfg.visit(new VerifyCFG()); }
*
* if (DEBUG) { System.out.println("------------After
* StackPRE-----------"); cfg.print(System.out); } }
*/
cfg.commit();
Peephole.transform(m);
Main.context.commit(methods[j]);
}
if (Main.ANNO) {
String s = "Optimized with: EDU.purdue.cs.bloat.diva.Main";
for (int i = 0; i < Main.ARGS.length; i++) {
if ((Main.ARGS[i].indexOf(' ') >= 0)
|| (Main.ARGS[i].indexOf('\t') >= 0)
|| (Main.ARGS[i].indexOf('\r') >= 0)
|| (Main.ARGS[i].indexOf('\n') >= 0)) {
s += " '" + Main.ARGS[i] + "'";
} else {
s += " " + Main.ARGS[i];
}
}
System.out.println(s);
// c.constants().addConstant(Constant.UTF8, s);
}
Main.context.commit(classFile);
}
}

@ -0,0 +1,24 @@
# All files in the distribution of BLOAT (Bytecode Level Optimization and
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
# Research Foundation of Purdue University. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
CLASS = \
InductionVarAnalyzer.class\
Main.class
include ../class.mk

@ -0,0 +1,252 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.dump;
import java.util.*;
import EDU.purdue.cs.bloat.context.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.file.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* Prints the contents of a Java classfile to the console.
*/
public class Main implements Opcode {
public static void main(final String[] args) {
final ClassFileLoader loader = new ClassFileLoader();
String className = null;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-help")) {
Main.usage();
} else if (args[i].equals("-classpath")) {
if (++i >= args.length) {
Main.usage();
}
final String classpath = args[i];
loader.setClassPath(classpath);
} else if (args[i].startsWith("-")) {
Main.usage();
} else {
if (className != null) {
Main.usage();
}
className = args[i];
}
}
if (className == null) {
Main.usage();
}
ClassInfo info = null;
try {
info = loader.loadClass(className);
} catch (final ClassNotFoundException ex) {
System.err.println("Couldn't find class: " + ex.getMessage());
System.exit(1);
}
final Collection classes = new ArrayList(1);
classes.add(className);
final BloatContext context = new CachingBloatContext(loader, classes,
true);
if (info != null) {
Main.printClass(context, info);
}
}
private static void usage() {
System.err
.println("Usage: java EDU.purdue.cs.bloat.dump.Main "
+ "\n [-options] class"
+ "\n"
+ "\nwhere options include:"
+ "\n -help print out this message"
+ "\n -classpath <directories separated by colons>"
+ "\n list directories in which to look for classes");
System.exit(0);
}
private static void printClass(final EditorContext context,
final ClassInfo info) {
final ClassEditor c = context.editClass(info);
if (c.isPublic()) {
System.out.print("public ");
} else if (c.isPrivate()) {
System.out.print("private ");
} else if (c.isProtected()) {
System.out.print("protected ");
}
if (c.isStatic()) {
System.out.print("static ");
}
if (c.isFinal()) {
System.out.print("final ");
}
if (c.isInterface()) {
System.out.print("interface ");
} else if (c.isAbstract()) {
System.out.print("abstract class ");
} else {
System.out.print("class ");
}
System.out.print(c.type().className());
if (c.superclass() != null) {
System.out.print(" extends " + c.superclass().className());
}
final Type[] interfaces = c.interfaces();
for (int i = 0; i < interfaces.length; i++) {
if (i == 0) {
System.out.print(" implements");
} else {
System.out.print(",");
}
System.out.print(" " + interfaces[i].className());
}
System.out.println();
System.out.println("{");
final FieldInfo[] fields = c.fields();
for (int i = 0; i < fields.length; i++) {
FieldEditor f = null;
try {
f = context.editField(fields[i]);
} catch (final ClassFormatException ex) {
System.err.println(ex.getMessage());
System.exit(1);
}
System.out.print(" ");
if (f.isPublic()) {
System.out.print("public ");
} else if (f.isPrivate()) {
System.out.print("private ");
} else if (f.isProtected()) {
System.out.print("protected ");
}
if (f.isTransient()) {
System.out.print("transient ");
}
if (f.isVolatile()) {
System.out.print("volatile ");
}
if (f.isStatic()) {
System.out.print("static ");
}
if (f.isFinal()) {
System.out.print("final ");
}
System.out.println(f.type() + " " + f.name());
context.release(fields[i]);
}
if (fields.length != 0) {
System.out.println();
}
final MethodInfo[] methods = c.methods();
for (int i = 0; i < methods.length; i++) {
MethodEditor m = null;
try {
m = context.editMethod(methods[i]);
} catch (final ClassFormatException ex) {
System.err.println(ex.getMessage());
System.exit(1);
}
if (i != 0) {
System.out.println();
}
System.out.print(" ");
if (m.isPublic()) {
System.out.print("public ");
} else if (m.isPrivate()) {
System.out.print("private ");
} else if (m.isProtected()) {
System.out.print("protected ");
}
if (m.isNative()) {
System.out.print("native ");
}
if (m.isSynchronized()) {
System.out.print("synchronized ");
}
if (m.isAbstract()) {
System.out.print("abstract ");
}
if (m.isStatic()) {
System.out.print("static ");
}
if (m.isFinal()) {
System.out.print("final ");
}
System.out.println(m.type() + " " + m.name());
final Iterator iter = m.code().iterator();
while (iter.hasNext()) {
final Object obj = iter.next();
System.out.println(" " + obj);
}
context.release(methods[i]);
}
System.out.println("}");
context.release(info);
}
}

@ -0,0 +1,23 @@
# All files in the distribution of BLOAT (Bytecode Level Optimization and
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
# Research Foundation of Purdue University. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
CLASS = \
Main.class
include ../class.mk

@ -0,0 +1,8 @@
<html>
<body>
<p>Contains a program that prints the contents of a Java classfile to
the console.</p>
</body>
</html>

@ -0,0 +1,555 @@
/**
* 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

@ -0,0 +1,459 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
import java.util.*;
import EDU.purdue.cs.bloat.reflect.*;
import EDU.purdue.cs.bloat.util.*;
/**
* ConstantPool models constants in the constant pool. Recall that the
* reflection mechanism represents constants as a tag and a value. ConstantPool
* consists of an array of <tt>reflect.Constant</tt>s that are resolved into
* their appropriate value (e.g. Type, NameAndType, MemberRef, etc.) as they are
* needed.
*
* @see EDU.purdue.cs.bloat.reflect.Constant
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class ConstantPool {
/**
* ConstantPool maintains some information about the constants in the
* constant pool to make its life easier.
*
* constantIndices A mapping between constants and their indices in the
* constant pool. Knowing this information makes adding constants to the
* constant pool easier.
*
* constants A list of the reflect.Constant objects that are used to create
* the ConstantPool.
*
* resolved A list of the constants in their resolved form (such as
* NameAndType or String)
*/
private Map constantIndices;
ResizeableArrayList constants;
ResizeableArrayList resolved;
/**
* Constructor. Resolve the constants in the constant pool, converting from
* the index-based representation of the class file to a more amenable
* external representation.
*
* @param c
* An array of Constant.
*/
public ConstantPool(final Constant[] c) {
constantIndices = new HashMap();
constants = new ResizeableArrayList(c.length);
resolved = new ResizeableArrayList(c.length);
/*
* constants = new ResizeableArrayList(c.length, 256); resolved = new
* ResizeableArrayList(c.length, 256);
*/
for (int i = 0; i < c.length; i++) {
constants.add(c[i]);
resolved.add(null);
if (c[i] != null) {
constantIndices.put(c[i], new Integer(i));
}
}
}
/**
* Creates a new, empty <code>ConstantPool</code>
*/
public ConstantPool() {
this.constantIndices = new HashMap();
this.constants = new ResizeableArrayList();
this.resolved = new ResizeableArrayList();
}
/**
* Obtain the resolved value of a constant at given index in the constant
* pool.
*
* @param idx
* Index into the constant pool.
* @return The value of the constant at index.
*/
public Object constantAt(final int idx) {
if (idx == 0) {
return null;
}
Object value = resolved.get(idx);
if (value != null) {
return value;
}
final Constant c = (Constant) constants.get(idx);
if (c == null) {
return null;
}
value = c.value();
if (value == null) {
return null;
}
// Okay, we have to resolve the Constant.
switch (c.tag()) {
case Constant.CLASS: {
// Lookup the UTF8 at the index and use the class name
// to create a Type.
Assert.isTrue(value instanceof Integer, "Invalid constant: " + c);
final int index = ((Integer) value).intValue();
Assert.isTrue(constantTag(index) == Constant.UTF8,
"Invalid constant: " + c);
final String className = (String) constantAt(index);
value = Type.getType(Type.classDescriptor(className));
break;
}
case Constant.STRING: {
// Lookup the UTF8 at the index and store the String directly.
Assert.isTrue(value instanceof Integer, "Invalid constant: " + c);
final int index = ((Integer) value).intValue();
Assert.isTrue(constantTag(index) == Constant.UTF8,
"Invalid constant: " + c);
value = constantAt(index);
break;
}
case Constant.FIELD_REF:
case Constant.METHOD_REF:
case Constant.INTERFACE_METHOD_REF: {
// The constant at the first index should be a CLASS.
//
// The constant at the second should be a NAME_AND_TYPE.
//
// Resolve the two constants and then create a MemberRef
// for this constant.
//
Assert.isTrue(value instanceof int[], "Invalid constant: " + c);
final int[] v = (int[]) value;
Assert.isTrue(constantTag(v[0]) == Constant.CLASS,
"Invalid constant: " + c);
Assert.isTrue(constantTag(v[1]) == Constant.NAME_AND_TYPE,
"Invalid constant: " + c);
final Type clazz = (Type) constantAt(v[0]);
final NameAndType nameAndType = (NameAndType) constantAt(v[1]);
value = new MemberRef(clazz, nameAndType);
break;
}
case Constant.NAME_AND_TYPE: {
// The constant at the first index should be a UTF8 with the
// name of the field.
//
// The constant at the second should be a UTF8 with the type
// of the field.
//
// Resolve the two constants as a String and a Type and then
// create a NameAndType for this constant.
//
Assert.isTrue(value instanceof int[], "Invalid constant: " + c);
final int[] v = (int[]) value;
Assert.isTrue(constantTag(v[0]) == Constant.UTF8,
"Invalid constant: " + c);
Assert.isTrue(constantTag(v[1]) == Constant.UTF8,
"Invalid constant: " + c);
final String name = (String) constantAt(v[0]);
final String type = (String) constantAt(v[1]);
value = new NameAndType(name, Type.getType(type));
break;
}
default:
break;
}
resolved.ensureSize(idx + 1);
resolved.set(idx, value);
return value;
}
public int numConstants() {
return constants.size();
}
/**
* Get the tag of a constant.
*
* @param index
* Index into the constant pool.
* @return The tag of the constant at index, or Constant.UTF8.
*/
public int constantTag(final int index) {
if ((0 < index) && (index < constants.size())) {
final Constant c = (Constant) constants.get(index);
if (c != null) {
return c.tag();
}
}
return Constant.UTF8;
}
/**
* Get the index of the constant with the given tag and value.
*
* @param tag
* The constant's tag (for example, <tt>Constant.UTF8</tt>).
* @param value
* The constant's value (for example, a <tt>String</tt>).
* @return The index of the constant.
*/
public int constantIndex(final int tag, final Object value) {
return addConstant(tag, value);
}
/**
* Returns the index of the constant pool entry for the given class
*/
public int getClassIndex(final Class c) {
return (addConstant(Constant.CLASS, Type.getType(c)));
}
/**
* Returns the index of the constant pool entry for the given integer
*/
public int getIntegerIndex(final Integer i) {
return (addConstant(Constant.INTEGER, i));
}
/**
* Returns the index of the constant pool entry for the given float
*/
public int getFloatIndex(final Float f) {
return (addConstant(Constant.FLOAT, f));
}
/**
* Returns the index of the constant pool entry for the given long
*/
public int getLongIndex(final Long l) {
return (addConstant(Constant.LONG, l));
}
/**
* Returns the index of the constant pool entry for the given double
*/
public int getDoubleIndex(final Double d) {
return (addConstant(Constant.DOUBLE, d));
}
/**
* Returns the index of the constant pool entry for the given class.
*/
public int getClassIndex(final Type type) {
Assert
.isTrue(type.isObject(), "Type " + type
+ " is not an class type");
// Make sure that the descriptor constant is also there
getTypeIndex(type);
return (addConstant(Constant.CLASS, type));
}
/**
* Returns the index of the constant pool entry for the given
* <code>Type</code>
*/
public int getTypeIndex(final Type type) {
return (addConstant(Constant.UTF8, type.descriptor()));
}
/**
* Returns the index of the constant pool entry for the given String
*/
public int getStringIndex(final String s) {
return (addConstant(Constant.STRING, s));
}
/**
* Returns the index of the constant pool entry for the given
* <code>MemberRef</code>
*/
public int getMemberRefIndex(final MemberRef ref) {
return (addConstant(Constant.FIELD_REF, ref));
}
/**
* Returns the index of the constant pool entry for the given
* <code>NameAndType</code>
*/
public int getNameAndTypeIndex(final NameAndType nat) {
return (addConstant(Constant.NAME_AND_TYPE, nat));
}
/**
* Returns the index of the constant pool entry for the given UTF8 string
*/
public int getUTF8Index(final String s) {
return (addConstant(Constant.UTF8, s));
}
/**
* Add a constant to the constant pool. Will not add the same constant
* twice.
*
* @param tag
* The constant's tag (for example, <tt>Constant.UTF8</tt>).
* @param value
* The constant's value (for example, a <tt>String</tt>).
* @return The index of the constant.
*/
public int addConstant(final int tag, final Object value) {
if (value == null) {
return 0;
}
Constant c;
switch (tag) {
case Constant.CLASS: {
Assert.isTrue(value instanceof Type, "Invalid value: " + value);
final int index = addConstant(Constant.UTF8, ((Type) value)
.className());
c = new Constant(Constant.CLASS, new Integer(index));
break;
}
case Constant.STRING: {
Assert.isTrue(value instanceof String, "Invalid value: " + value);
final int index = addConstant(Constant.UTF8, value);
c = new Constant(Constant.STRING, new Integer(index));
break;
}
case Constant.FIELD_REF:
case Constant.METHOD_REF:
case Constant.INTERFACE_METHOD_REF: {
// The constant at the first index should be a CLASS.
//
// The constant at the second should be a NAME_AND_TYPE.
//
// Resolve the two constants and then create a MemberRef
// for this constant.
//
Assert
.isTrue(value instanceof MemberRef, "Invalid value: "
+ value);
final int[] v = new int[2];
v[0] = addConstant(Constant.CLASS, ((MemberRef) value)
.declaringClass());
v[1] = addConstant(Constant.NAME_AND_TYPE, ((MemberRef) value)
.nameAndType());
c = new Constant(tag, v);
break;
}
case Constant.NAME_AND_TYPE: {
// The constant at the first index should be a UTF8 with the
// name of the field.
//
// The constant at the second should be a UTF8 with the type
// of the field.
//
// Resolve the two constants as a String and a Type and then
// create a NameAndType for this constant.
//
Assert.isTrue(value instanceof NameAndType, "Invalid value: "
+ value);
final int[] v = new int[2];
v[0] = addConstant(Constant.UTF8, ((NameAndType) value).name());
v[1] = addConstant(Constant.UTF8, ((NameAndType) value).type()
.descriptor());
c = new Constant(tag, v);
break;
}
case Constant.UTF8: {
final String s = (String) value;
c = new Constant(tag, s.intern());
break;
}
default: {
c = new Constant(tag, value);
break;
}
}
Integer index = (Integer) constantIndices.get(c);
if (index == null) {
index = new Integer(constants.size());
constantIndices.put(c, index);
constants.add(c);
resolved.add(value);
if ((tag == Constant.LONG) || (tag == Constant.DOUBLE)) {
constants.add(null);
resolved.add(null);
}
}
return index.intValue();
}
/**
* Get an array of the constants in the pool.
*
* @return An array of the constants in the pool.
*/
public Constant[] constants() {
final Object[] a = constants.toArray();
final Constant[] array = new Constant[a.length];
System.arraycopy(a, 0, array, 0, a.length);
return array;
}
/**
* Returns an unmodifiable List of constants in this constant pool.
*/
public List getConstantsList() {
return Collections.unmodifiableList(this.constants);
}
}

@ -0,0 +1,153 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
import EDU.purdue.cs.bloat.reflect.*;
/**
* An <tt>EditorContext</tt> supplies a means of loading and editing classes.
* Note that a number of these methods are identical to methods in
* <tt>Editor</tt>. It is expected that an <tt>EditorContext</tt> will have
* a different caching (of <tt>ClassEditor</tt>s, etc.) policy than
* <tt>Editor</tt> does. Hence, the methods in <tt>EditorContext</tt> should
* be used to edit classes, etc.
*/
public interface EditorContext {
/**
* Loads a class into BLOAT
*/
public ClassInfo loadClass(String className) throws ClassNotFoundException;
/**
* Creates a new <code>ClassInfo</code>
*
* @param modifiers
* The modifiers describing the newly-created class
* @param classIndex
* The index of the name of the newly-created class in its
* constant pool
* @param superClassIndex
* The index of the name of the newly-created class's superclass
* in its constant pool
* @param interfaceIndexes
* The indexes of the names of the interfaces that the
* newly-created class implements
* @param constants
* The constant pool for the newly created class (a list of
* {@link Constant}s).
*/
public ClassInfo newClassInfo(int modifiers, int classIndex,
int superClassIndex, int[] interfaceIndexes,
java.util.List constants);
/**
* Returns the <tt>ClassHierarchy</tt> of all classes and interfaces known
* to BLOAT.
*/
public ClassHierarchy getHierarchy();
/**
* Returns a <code>ClassEditor</code> for editing a new class with the
* given name. It will override any class with the given name that is
* already being edited.
*/
public ClassEditor newClass(int modifiers, String className,
Type superType, Type[] interfaces);
/**
* Returns a <tt>ClassEditor</tt> used to edit a class of a given name.
*/
public ClassEditor editClass(String className)
throws ClassNotFoundException, ClassFormatException;
/**
* Returns a <tt>ClassEditor</tt> used to edit a class described by a
* given <tt>Type</tt>.
*/
public ClassEditor editClass(Type classType) throws ClassNotFoundException,
ClassFormatException;
/**
* Returns a <tt>ClassEditor</tt> used to edit a class described by a
* given <tt>ClassInfo</tt>.
*/
public ClassEditor editClass(ClassInfo info);
/**
* Returns a <tt>FieldEditor</tt> for editing a <tt>FieldInfo</tt>.
*/
public FieldEditor editField(FieldInfo info);
/**
* Returns a <tt>FieldEditor</tt> for editing a field.
*/
public FieldEditor editField(MemberRef field) throws NoSuchFieldException;
/**
* Returns a <tt>MethodEditor</tt> for editing a method.
*/
public MethodEditor editMethod(MethodInfo info);
/**
* Returns a <tt>MethodEditor</tt> for editing a method.
*/
public MethodEditor editMethod(MemberRef method)
throws NoSuchMethodException;
/**
* Signals that we are done editing a method. The object used to model it
* may be reclaimed.
*/
public void release(MethodInfo info);
/**
* Signals that we are done editing a field. The object used to model it may
* be reclaimed.
*/
public void release(FieldInfo info);
/**
* Signals that we are done editing a class. The object used to model it may
* be reclaimed.
*/
public void release(ClassInfo info);
/**
* Commits the changes made to a class.
*/
public void commit(ClassInfo info);
/**
* Commits the changes made to a method.
*/
public void commit(MethodInfo info);
/**
* Commits the changes made to a field.
*/
public void commit(FieldInfo info);
/**
* Commits all changes made to classes, methods, and fields.
*/
public void commit();
}

@ -0,0 +1,35 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
/**
* <tt>EditorVisitor</tt> "visits" the "nodes" in a class. Imagine that a
* class is rooted by a <tt>ClassEditor</tt> that has <tt>FieldEditor</tt>
* and <tt>MethodEditor</tt> children.
*/
public interface EditorVisitor {
public void visitClassEditor(ClassEditor editor);
public void visitMethodEditor(MethodEditor editor);
public void visitFieldEditor(FieldEditor editor);
}

@ -0,0 +1,562 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
import java.io.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* <tt>FieldEditor</tt> provides a means to edit a field of a class. A
* <tt>FieldEditor</tt> is created from a <tt>ClassEditor</tt> and a
* <tt>reflect.FieldInfo</tt>. A <tt>FieldEditor</tt> knows its name, type
* (descriptor), and its constant value (if it has one).
*
* @see EDU.purdue.cs.bloat.reflect.FieldInfo
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class FieldEditor {
private ClassEditor editor;
private FieldInfo fieldInfo;
private String name;
private Type type;
private Object constantValue;
private boolean isDirty;
private boolean isDeleted = false;
/**
* Creates a new <code>FieldEditor</code> for editing a field in a given
* class with the given modifiers, type and name
*
* @throws IllegalArgumentException
* If a field with the desired name already exists in the class
*/
public FieldEditor(final ClassEditor editor, final int modifiers,
final Type type, final String name) {
this(editor, modifiers, type, name, null);
}
public FieldEditor(final ClassEditor editor, final int modifiers,
final Class type, final String name, final Object constantValue) {
this(editor, modifiers, Type.getType(type), name, constantValue);
}
public FieldEditor(final ClassEditor editor, final int modifiers,
final Class type, final String name) {
this(editor, modifiers, Type.getType(type), name, null);
}
/**
* Creates a new <code>FieldEditor</code> for editing a field in a given
* class with the given modifiers, type, name, and constant value.
*
* @param modifiers
* Fields that have a constant value must be <code>static</code>
* and <code>final</code>
*
* @throws IllegalArgumentException
* If a field with the desired name already exists in the class
* or if <code>constantValue</code> is non-null and neither a
* <code>String</code>, <code>Integer</code>,
* <code>Long</code>, <code>Float</code>, nor
* <code>Double</code>.
*/
public FieldEditor(final ClassEditor editor, final int modifiers,
final Type type, final String name, final Object constantValue) {
// Does the class already have a field with this name?
final FieldInfo[] fields = editor.fields();
for (int i = 0; i < fields.length; i++) {
final FieldEditor fe = new FieldEditor(editor, fields[i]);
if (fe.name().equals(name)) {
final String s = "A field named " + name
+ " already exists in " + editor.name();
throw new IllegalArgumentException(s);
}
}
this.editor = editor;
final ConstantPool cp = editor.constants();
this.name = name;
this.type = type;
final int typeIndex = cp.getUTF8Index(this.type.descriptor());
final int nameIndex = cp.getUTF8Index(name);
final ClassInfo classInfo = editor.classInfo();
if (constantValue != null) {
// Only static final field may have constant values
if (((modifiers & Modifiers.STATIC) == 0)
|| ((modifiers & Modifiers.FINAL) == 0)) {
final String s = "Field " + name
+ " with a constant value must be static and final";
throw new IllegalArgumentException(s);
}
// Create an entry in the constant pool for the constant value
// of this field
int valueIndex;
if (constantValue instanceof String) {
if (!type.equals(Type.STRING)) {
final String s = "Can't have field type of "
+ type.className() + " with a constant value of \""
+ constantValue + "\"";
throw new IllegalArgumentException(s);
}
valueIndex = cp.getStringIndex((String) constantValue);
} else if (constantValue instanceof Integer) {
if (!type.equals(Type.INTEGER)) {
final String s = "Can't have field type of "
+ type.className() + " with a constant value of \""
+ constantValue + "\"";
throw new IllegalArgumentException(s);
}
valueIndex = cp.getIntegerIndex((Integer) constantValue);
} else if (constantValue instanceof Long) {
if (!type.equals(Type.LONG)) {
final String s = "Can't have field type of "
+ type.className() + " with a constant value of \""
+ constantValue + "\"";
throw new IllegalArgumentException(s);
}
valueIndex = cp.getLongIndex((Long) constantValue);
} else if (constantValue instanceof Float) {
if (!type.equals(Type.FLOAT)) {
final String s = "Can't have field type of "
+ type.className() + " with a constant value of \""
+ constantValue + "\"";
throw new IllegalArgumentException(s);
}
valueIndex = cp.getFloatIndex((Float) constantValue);
} else if (constantValue instanceof Double) {
if (!type.equals(Type.DOUBLE)) {
final String s = "Can't have field type of "
+ type.className() + " with a constant value of \""
+ constantValue + "\"";
throw new IllegalArgumentException(s);
}
valueIndex = cp.getDoubleIndex((Double) constantValue);
} else {
final String s = "Cannot have a constant value of type "
+ constantValue.getClass().getName();
throw new IllegalArgumentException(s);
}
this.constantValue = constantValue;
final int cvNameIndex = cp.getUTF8Index("ConstantValue");
this.fieldInfo = classInfo.addNewField(modifiers, typeIndex,
nameIndex, cvNameIndex, valueIndex);
} else {
this.fieldInfo = classInfo.addNewField(modifiers, typeIndex,
nameIndex);
}
this.isDirty = true;
}
/**
* Constructor.
*
* @param editor
* The class containing the field.
* @param fieldInfo
* The field to edit.
*
* @see ClassEditor
* @see FieldInfo
*/
public FieldEditor(final ClassEditor editor, final FieldInfo fieldInfo) {
final ConstantPool cp = editor.constants();
this.fieldInfo = fieldInfo;
this.editor = editor;
int index;
index = fieldInfo.nameIndex();
name = (String) cp.constantAt(index);
index = fieldInfo.typeIndex();
final String typeName = (String) cp.constantAt(index);
type = Type.getType(typeName);
index = fieldInfo.constantValue();
constantValue = cp.constantAt(index);
this.isDirty = false;
}
/**
* Returns the <tt>ClassEditor</tt> used to edit the class in which this
* field resides.
*/
public ClassEditor declaringClass() {
return editor;
}
/**
* Returns <tt>true</tt> if this field has been modified.
*/
public boolean isDirty() {
return (this.isDirty);
}
/**
* Sets the dirty flag of this method. The dirty flag is <tt>true</tt> if
* the method has been modified.
*
* @throws IllegalStateException This field has been marked for deletion
*/
public void setDirty(final boolean isDirty) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
this.isDirty = isDirty;
if (isDirty == true) {
this.editor.setDirty(true);
}
}
/**
* Marks this field for deletion. Once a field has been marked for deletion
* all attempts to change it will throw an
* <code>IllegalStateException</code>.
*/
public void delete() {
this.setDirty(true);
this.isDeleted = true;
}
/**
* Returns the raw FieldInfo of the field being edited.
*/
public FieldInfo fieldInfo() {
return fieldInfo;
}
public Object constantValue() {
return constantValue;
}
public boolean isPublic() {
return (fieldInfo.modifiers() & Modifiers.PUBLIC) != 0;
}
public boolean isPrivate() {
return (fieldInfo.modifiers() & Modifiers.PRIVATE) != 0;
}
public boolean isProtected() {
return (fieldInfo.modifiers() & Modifiers.PROTECTED) != 0;
}
/**
* Returns true, if the field has package level visibility.
*/
public boolean isPackage() {
return (!isPublic() && !isPrivate() && !isProtected());
}
public boolean isStatic() {
return (fieldInfo.modifiers() & Modifiers.STATIC) != 0;
}
public boolean isFinal() {
return (fieldInfo.modifiers() & Modifiers.FINAL) != 0;
}
public boolean isVolatile() {
return (fieldInfo.modifiers() & Modifiers.VOLATILE) != 0;
}
public boolean isTransient() {
return (fieldInfo.modifiers() & Modifiers.TRANSIENT) != 0;
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setPublic(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.PUBLIC;
} else {
modifiers &= ~Modifiers.PUBLIC;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setPrivate(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.PRIVATE;
} else {
modifiers &= ~Modifiers.PRIVATE;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setProtected(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.PROTECTED;
} else {
modifiers &= ~Modifiers.PROTECTED;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setStatic(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.STATIC;
} else {
modifiers &= ~Modifiers.STATIC;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setFinal(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.FINAL;
} else {
modifiers &= ~Modifiers.FINAL;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setTransient(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.TRANSIENT;
} else {
modifiers &= ~Modifiers.TRANSIENT;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setVolatile(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.VOLATILE;
} else {
modifiers &= ~Modifiers.VOLATILE;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* Returns the name of the field.
*/
public String name() {
return name;
}
/**
* Returns the type of the field.
*/
public Type type() {
return type;
}
/**
* Returns a <tt>NameAndType</tt> of the field.
*/
public NameAndType nameAndType() {
return (new NameAndType(this.name(), this.type()));
}
/**
* Returns a <code>MemberRef</code> for the field
*/
public MemberRef memberRef() {
return (new MemberRef(this.declaringClass().type(), this.nameAndType()));
}
/**
* Commit changes to the field back to the ClassEditor. Note that the field
* is committed regardless of whether or not it is dirty.
*/
public void commit() {
if (this.isDeleted) {
// Even if the field is newly-added, we can still delete it
// without problems because its FieldInfo was already noted with
// the ClassInfo.
final ConstantPool cp = editor.constants();
final int nameIndex = cp.getUTF8Index(name);
this.editor.classInfo().deleteField(nameIndex);
} else {
final ConstantPool cp = editor.constants();
fieldInfo.setNameIndex(cp.addConstant(Constant.UTF8, name));
fieldInfo.setTypeIndex(cp.addConstant(Constant.UTF8, type
.descriptor()));
if (constantValue != null) {
if (constantValue instanceof Long) {
fieldInfo.setConstantValue(cp.addConstant(Constant.LONG,
constantValue));
} else if (constantValue instanceof Float) {
fieldInfo.setConstantValue(cp.addConstant(Constant.FLOAT,
constantValue));
} else if (constantValue instanceof Double) {
fieldInfo.setConstantValue(cp.addConstant(Constant.DOUBLE,
constantValue));
} else if (constantValue instanceof Integer) {
fieldInfo.setConstantValue(cp.addConstant(Constant.INTEGER,
constantValue));
} else if (constantValue instanceof String) {
fieldInfo.setConstantValue(cp.addConstant(Constant.STRING,
constantValue));
}
}
}
// This field is no longer dirty
this.isDirty = false;
}
/**
* Print the field.
*
* @param out
* Stream to which to print.
*/
public void print(final PrintStream out) {
out.println("field " + name + " " + type);
}
/**
* Returns a String that contains the declaring class name and the name of
* the field
*/
public String fullName() {
return declaringClass().name() + "." + this.name();
}
public String toString() {
return ("[FieldEditor for " + this.name + this.type + "]");
}
}

@ -0,0 +1,75 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
/**
* IncOperand encapsulates the operands to the iinc instruction. It is necessary
* because the <tt>iinc</tt> has two operands: a local variable and an integer
* by which to increment the local variable.
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class IncOperand {
private LocalVariable var;
private int incr;
/**
* Constructor.
*
* @param var
* The local variable to increment.
* @param incr
* The amount to increment by.
*/
public IncOperand(final LocalVariable var, final int incr) {
this.var = var;
this.incr = incr;
}
/**
* Get the local variable to increment.
*
* @return The local variable to increment.
*/
public LocalVariable var() {
return var;
}
/**
* Get the amount to increment by.
*
* @return The amount to increment by.
*/
public int incr() {
return incr;
}
/**
* Convert the operand to a string.
*
* @return A string representation of the operand.
*/
public String toString() {
return "" + var + " by " + incr;
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,459 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
/**
* This adapter provides a default implementation for every method in
* InstructionVisitor.
*/
public class InstructionAdapter implements InstructionVisitor {
public void visit_nop(final Instruction inst) {
}
public void visit_ldc(final Instruction inst) {
}
public void visit_iload(final Instruction inst) {
}
public void visit_lload(final Instruction inst) {
}
public void visit_fload(final Instruction inst) {
}
public void visit_dload(final Instruction inst) {
}
public void visit_aload(final Instruction inst) {
}
public void visit_iaload(final Instruction inst) {
}
public void visit_laload(final Instruction inst) {
}
public void visit_faload(final Instruction inst) {
}
public void visit_daload(final Instruction inst) {
}
public void visit_aaload(final Instruction inst) {
}
public void visit_baload(final Instruction inst) {
}
public void visit_caload(final Instruction inst) {
}
public void visit_saload(final Instruction inst) {
}
public void visit_istore(final Instruction inst) {
}
public void visit_lstore(final Instruction inst) {
}
public void visit_fstore(final Instruction inst) {
}
public void visit_dstore(final Instruction inst) {
}
public void visit_astore(final Instruction inst) {
}
public void visit_iastore(final Instruction inst) {
}
public void visit_lastore(final Instruction inst) {
}
public void visit_fastore(final Instruction inst) {
}
public void visit_dastore(final Instruction inst) {
}
public void visit_aastore(final Instruction inst) {
}
public void visit_bastore(final Instruction inst) {
}
public void visit_castore(final Instruction inst) {
}
public void visit_sastore(final Instruction inst) {
}
public void visit_pop(final Instruction inst) {
}
public void visit_pop2(final Instruction inst) {
}
public void visit_dup(final Instruction inst) {
}
public void visit_dup_x1(final Instruction inst) {
}
public void visit_dup_x2(final Instruction inst) {
}
public void visit_dup2(final Instruction inst) {
}
public void visit_dup2_x1(final Instruction inst) {
}
public void visit_dup2_x2(final Instruction inst) {
}
public void visit_swap(final Instruction inst) {
}
public void visit_iadd(final Instruction inst) {
}
public void visit_ladd(final Instruction inst) {
}
public void visit_fadd(final Instruction inst) {
}
public void visit_dadd(final Instruction inst) {
}
public void visit_isub(final Instruction inst) {
}
public void visit_lsub(final Instruction inst) {
}
public void visit_fsub(final Instruction inst) {
}
public void visit_dsub(final Instruction inst) {
}
public void visit_imul(final Instruction inst) {
}
public void visit_lmul(final Instruction inst) {
}
public void visit_fmul(final Instruction inst) {
}
public void visit_dmul(final Instruction inst) {
}
public void visit_idiv(final Instruction inst) {
}
public void visit_ldiv(final Instruction inst) {
}
public void visit_fdiv(final Instruction inst) {
}
public void visit_ddiv(final Instruction inst) {
}
public void visit_irem(final Instruction inst) {
}
public void visit_lrem(final Instruction inst) {
}
public void visit_frem(final Instruction inst) {
}
public void visit_drem(final Instruction inst) {
}
public void visit_ineg(final Instruction inst) {
}
public void visit_lneg(final Instruction inst) {
}
public void visit_fneg(final Instruction inst) {
}
public void visit_dneg(final Instruction inst) {
}
public void visit_ishl(final Instruction inst) {
}
public void visit_lshl(final Instruction inst) {
}
public void visit_ishr(final Instruction inst) {
}
public void visit_lshr(final Instruction inst) {
}
public void visit_iushr(final Instruction inst) {
}
public void visit_lushr(final Instruction inst) {
}
public void visit_iand(final Instruction inst) {
}
public void visit_land(final Instruction inst) {
}
public void visit_ior(final Instruction inst) {
}
public void visit_lor(final Instruction inst) {
}
public void visit_ixor(final Instruction inst) {
}
public void visit_lxor(final Instruction inst) {
}
public void visit_iinc(final Instruction inst) {
}
public void visit_i2l(final Instruction inst) {
}
public void visit_i2f(final Instruction inst) {
}
public void visit_i2d(final Instruction inst) {
}
public void visit_l2i(final Instruction inst) {
}
public void visit_l2f(final Instruction inst) {
}
public void visit_l2d(final Instruction inst) {
}
public void visit_f2i(final Instruction inst) {
}
public void visit_f2l(final Instruction inst) {
}
public void visit_f2d(final Instruction inst) {
}
public void visit_d2i(final Instruction inst) {
}
public void visit_d2l(final Instruction inst) {
}
public void visit_d2f(final Instruction inst) {
}
public void visit_i2b(final Instruction inst) {
}
public void visit_i2c(final Instruction inst) {
}
public void visit_i2s(final Instruction inst) {
}
public void visit_lcmp(final Instruction inst) {
}
public void visit_fcmpl(final Instruction inst) {
}
public void visit_fcmpg(final Instruction inst) {
}
public void visit_dcmpl(final Instruction inst) {
}
public void visit_dcmpg(final Instruction inst) {
}
public void visit_ifeq(final Instruction inst) {
}
public void visit_ifne(final Instruction inst) {
}
public void visit_iflt(final Instruction inst) {
}
public void visit_ifge(final Instruction inst) {
}
public void visit_ifgt(final Instruction inst) {
}
public void visit_ifle(final Instruction inst) {
}
public void visit_if_icmpeq(final Instruction inst) {
}
public void visit_if_icmpne(final Instruction inst) {
}
public void visit_if_icmplt(final Instruction inst) {
}
public void visit_if_icmpge(final Instruction inst) {
}
public void visit_if_icmpgt(final Instruction inst) {
}
public void visit_if_icmple(final Instruction inst) {
}
public void visit_if_acmpeq(final Instruction inst) {
}
public void visit_if_acmpne(final Instruction inst) {
}
public void visit_goto(final Instruction inst) {
}
public void visit_jsr(final Instruction inst) {
}
public void visit_ret(final Instruction inst) {
}
public void visit_switch(final Instruction inst) {
}
public void visit_ireturn(final Instruction inst) {
}
public void visit_lreturn(final Instruction inst) {
}
public void visit_freturn(final Instruction inst) {
}
public void visit_dreturn(final Instruction inst) {
}
public void visit_areturn(final Instruction inst) {
}
public void visit_return(final Instruction inst) {
}
public void visit_getstatic(final Instruction inst) {
}
public void visit_putstatic(final Instruction inst) {
}
public void visit_putstatic_nowb(final Instruction inst) {
}
public void visit_getfield(final Instruction inst) {
}
public void visit_putfield(final Instruction inst) {
}
public void visit_putfield_nowb(final Instruction inst) {
}
public void visit_invokevirtual(final Instruction inst) {
}
public void visit_invokespecial(final Instruction inst) {
}
public void visit_invokestatic(final Instruction inst) {
}
public void visit_invokeinterface(final Instruction inst) {
}
public void visit_new(final Instruction inst) {
}
public void visit_newarray(final Instruction inst) {
}
public void visit_arraylength(final Instruction inst) {
}
public void visit_athrow(final Instruction inst) {
}
public void visit_checkcast(final Instruction inst) {
}
public void visit_instanceof(final Instruction inst) {
}
public void visit_monitorenter(final Instruction inst) {
}
public void visit_monitorexit(final Instruction inst) {
}
public void visit_multianewarray(final Instruction inst) {
}
public void visit_ifnull(final Instruction inst) {
}
public void visit_ifnonnull(final Instruction inst) {
}
public void visit_rc(final Instruction inst) {
}
public void visit_aupdate(final Instruction inst) {
}
public void visit_supdate(final Instruction inst) {
}
public void visit_aswizzle(final Instruction inst) {
}
public void visit_aswrange(final Instruction inst) {
}
}

@ -0,0 +1,330 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
/**
* The visitor pattern allows functionality to be added to a number of classes
* (or in this case one class, <tt>Instruction</tt>, that can vary in
* behavior) without modifying the classes themselves. Additionally, the visitor
* pattern simulates double dispatching. For instance <tt>visit</tt> method of
* <tt>Instruction</tt> calls a particular method of
* <tt>InstructionVisitor</tt> based on the <tt>Instruction</tt>'s opcode.
* <p>
* <tt>InstructionVisitor</tt> provides an interface for performing actions
* based on the instruction type. Classes implementing this interface should not
* be able to miss any of the instruction types. This interface was created as
* an alternative to having 138 different subtypes of Instruction.
*
* @see Instruction#visit
* @see EDU.purdue.cs.bloat.tree.Tree
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public interface InstructionVisitor {
public void visit_nop(Instruction inst);
public void visit_ldc(Instruction inst);
public void visit_iload(Instruction inst);
public void visit_lload(Instruction inst);
public void visit_fload(Instruction inst);
public void visit_dload(Instruction inst);
public void visit_aload(Instruction inst);
public void visit_iaload(Instruction inst);
public void visit_laload(Instruction inst);
public void visit_faload(Instruction inst);
public void visit_daload(Instruction inst);
public void visit_aaload(Instruction inst);
public void visit_baload(Instruction inst);
public void visit_caload(Instruction inst);
public void visit_saload(Instruction inst);
public void visit_istore(Instruction inst);
public void visit_lstore(Instruction inst);
public void visit_fstore(Instruction inst);
public void visit_dstore(Instruction inst);
public void visit_astore(Instruction inst);
public void visit_iastore(Instruction inst);
public void visit_lastore(Instruction inst);
public void visit_fastore(Instruction inst);
public void visit_dastore(Instruction inst);
public void visit_aastore(Instruction inst);
public void visit_bastore(Instruction inst);
public void visit_castore(Instruction inst);
public void visit_sastore(Instruction inst);
public void visit_pop(Instruction inst);
public void visit_pop2(Instruction inst);
public void visit_dup(Instruction inst);
public void visit_dup_x1(Instruction inst);
public void visit_dup_x2(Instruction inst);
public void visit_dup2(Instruction inst);
public void visit_dup2_x1(Instruction inst);
public void visit_dup2_x2(Instruction inst);
public void visit_swap(Instruction inst);
public void visit_iadd(Instruction inst);
public void visit_ladd(Instruction inst);
public void visit_fadd(Instruction inst);
public void visit_dadd(Instruction inst);
public void visit_isub(Instruction inst);
public void visit_lsub(Instruction inst);
public void visit_fsub(Instruction inst);
public void visit_dsub(Instruction inst);
public void visit_imul(Instruction inst);
public void visit_lmul(Instruction inst);
public void visit_fmul(Instruction inst);
public void visit_dmul(Instruction inst);
public void visit_idiv(Instruction inst);
public void visit_ldiv(Instruction inst);
public void visit_fdiv(Instruction inst);
public void visit_ddiv(Instruction inst);
public void visit_irem(Instruction inst);
public void visit_lrem(Instruction inst);
public void visit_frem(Instruction inst);
public void visit_drem(Instruction inst);
public void visit_ineg(Instruction inst);
public void visit_lneg(Instruction inst);
public void visit_fneg(Instruction inst);
public void visit_dneg(Instruction inst);
public void visit_ishl(Instruction inst);
public void visit_lshl(Instruction inst);
public void visit_ishr(Instruction inst);
public void visit_lshr(Instruction inst);
public void visit_iushr(Instruction inst);
public void visit_lushr(Instruction inst);
public void visit_iand(Instruction inst);
public void visit_land(Instruction inst);
public void visit_ior(Instruction inst);
public void visit_lor(Instruction inst);
public void visit_ixor(Instruction inst);
public void visit_lxor(Instruction inst);
public void visit_iinc(Instruction inst);
public void visit_i2l(Instruction inst);
public void visit_i2f(Instruction inst);
public void visit_i2d(Instruction inst);
public void visit_l2i(Instruction inst);
public void visit_l2f(Instruction inst);
public void visit_l2d(Instruction inst);
public void visit_f2i(Instruction inst);
public void visit_f2l(Instruction inst);
public void visit_f2d(Instruction inst);
public void visit_d2i(Instruction inst);
public void visit_d2l(Instruction inst);
public void visit_d2f(Instruction inst);
public void visit_i2b(Instruction inst);
public void visit_i2c(Instruction inst);
public void visit_i2s(Instruction inst);
public void visit_lcmp(Instruction inst);
public void visit_fcmpl(Instruction inst);
public void visit_fcmpg(Instruction inst);
public void visit_dcmpl(Instruction inst);
public void visit_dcmpg(Instruction inst);
public void visit_ifeq(Instruction inst);
public void visit_ifne(Instruction inst);
public void visit_iflt(Instruction inst);
public void visit_ifge(Instruction inst);
public void visit_ifgt(Instruction inst);
public void visit_ifle(Instruction inst);
public void visit_if_icmpeq(Instruction inst);
public void visit_if_icmpne(Instruction inst);
public void visit_if_icmplt(Instruction inst);
public void visit_if_icmpge(Instruction inst);
public void visit_if_icmpgt(Instruction inst);
public void visit_if_icmple(Instruction inst);
public void visit_if_acmpeq(Instruction inst);
public void visit_if_acmpne(Instruction inst);
public void visit_goto(Instruction inst);
public void visit_jsr(Instruction inst);
public void visit_ret(Instruction inst);
public void visit_switch(Instruction inst);
public void visit_ireturn(Instruction inst);
public void visit_lreturn(Instruction inst);
public void visit_freturn(Instruction inst);
public void visit_dreturn(Instruction inst);
public void visit_areturn(Instruction inst);
public void visit_return(Instruction inst);
public void visit_getstatic(Instruction inst);
public void visit_putstatic(Instruction inst);
public void visit_putstatic_nowb(Instruction inst);
public void visit_getfield(Instruction inst);
public void visit_putfield(Instruction inst);
public void visit_putfield_nowb(Instruction inst);
public void visit_invokevirtual(Instruction inst);
public void visit_invokespecial(Instruction inst);
public void visit_invokestatic(Instruction inst);
public void visit_invokeinterface(Instruction inst);
public void visit_new(Instruction inst);
public void visit_newarray(Instruction inst);
public void visit_arraylength(Instruction inst);
public void visit_athrow(Instruction inst);
public void visit_checkcast(Instruction inst);
public void visit_instanceof(Instruction inst);
public void visit_monitorenter(Instruction inst);
public void visit_monitorexit(Instruction inst);
public void visit_multianewarray(Instruction inst);
public void visit_ifnull(Instruction inst);
public void visit_ifnonnull(Instruction inst);
public void visit_rc(Instruction inst);
public void visit_aupdate(Instruction inst);
public void visit_supdate(Instruction inst);
public void visit_aswizzle(Instruction inst);
public void visit_aswrange(Instruction inst);
}

@ -0,0 +1,141 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
/**
* <tt>Label</tt> is used to label an instruction. <tt>Label</tt>s are used
* to preserve the location of branch targets. A <tt>Label</tt> consists of an
* index into the code array and a <tt>boolean</tt> that determines whether or
* not it starts a basic block.
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class Label {
public static boolean TRACE = false;
private int index;
private boolean startsBlock;
private String comment; // Comment with Label
/**
* Constructor.
*
* @param index
* A unique index for the label. For instance, its offset in the
* instruction array.
*/
public Label(final int index) {
this(index, false);
}
/**
* Constructor.
*
* @param index
* The index of this label into the instruction array
* @param startsBlock
* True if the label is the first instruction in a basic block,
* false if not.
*/
public Label(final int index, final boolean startsBlock) {
this.index = index;
this.startsBlock = startsBlock;
// if(Label.TRACE) {
// try {
// throw new Exception("Creating a new label: " + this);
// } catch(Exception ex) {
// ex.printStackTrace(System.out);
// }
// }
}
/**
* Sets the comment associated with this <tt>Label</tt>.
*/
public void setComment(final String comment) {
this.comment = comment;
}
/**
* Set if the label starts a block.
*
* @param startsBlock
* True if the label starts a block, false if not.
*/
public void setStartsBlock(final boolean startsBlock) {
this.startsBlock = startsBlock;
}
/**
* Check if the label starts a block.
*
* @return True if the label starts a block, false if not.
*/
public boolean startsBlock() {
return startsBlock;
}
/**
* Get the index of the label.
*
* @return The index of the label.
*/
public int index() {
return index;
}
/**
* Hash the label.
*
* @return The hash code.
*/
public int hashCode() {
return index;
}
/**
* Check if an object is equal to this label.
*
* @param obj
* The object to compare against.
* @return true if equal, false if not.
*/
public boolean equals(final Object obj) {
return ((obj instanceof Label) && (((Label) obj).index == index));
}
/**
* Convert the label to a string.
*
* @return A string representation of the label.
*/
public String toString() {
if (comment != null) {
return "label_" + index + " (" + comment + ")";
} else {
return "label_" + index;
}
}
}

@ -0,0 +1,134 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
/**
* LocalVariable represents a local variable index operand to various
* instructions.
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class LocalVariable {
private String name;
private Type type;
private int index;
/**
* Constructor.
*
* @param index
* The index of the local variable in the method's local variable
* array.
*/
public LocalVariable(final int index) {
this.name = null;
this.type = null;
this.index = index;
}
/**
* Constructor.
*
* @param name
* The name of the local variable.
* @param type
* The descriptor (or index into the constant pool) representing
* the variable type.
* @param index
* The index of the local variable in the method's local variable
* array.
*/
public LocalVariable(final String name, final Type type, final int index) {
this.name = name;
this.type = type;
this.index = index;
}
/**
* Hash the local variable.
*
* A stricter hashing than using the index will break Hashtable lookups
* since a variable could have a name assigned to it after its first use.
*
* @return The hash code.
*/
public int hashCode() {
return index;
}
/**
* Check if an object is equal to this variable.
*
* A stricter comparison than comparing indices will break Hashtable lookups
* since a variable could have a name assigned to it after its first use.
*
* @param obj
* The object to compare against.
* @return true if equal, false if not.
*/
public boolean equals(final Object obj) {
return (obj != null) && (obj instanceof LocalVariable)
&& (((LocalVariable) obj).index == index);
}
/**
* Get the name of the local variable.
*
* @return The name of the local variable.
*/
public String name() {
return name;
}
/**
* Get the type of the local variable.
*
* @return The type of the local variable.
*/
public Type type() {
return type;
}
/**
* Get the index into the local variable array.
*
* @return The index into the local variable array.
*/
public int index() {
return index;
}
/**
* Convert the variable to a string.
*
* @return A string representation of the variable.
*/
public String toString() {
if (name == null) {
return "Local$" + index;
}
return name + "$" + index;
}
}

@ -0,0 +1,44 @@
# All files in the distribution of BLOAT (Bytecode Level Optimization and
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
# Research Foundation of Purdue University. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
CLASS = \
ClassEditor.class\
ClassHierarchy.class\
CodeArray.class\
ConstantPool.class\
EditorContext.class\
FieldEditor.class\
IncOperand.class\
Instruction.class\
InstructionAdapter.class\
InstructionVisitor.class\
Label.class\
LocalVariable.class\
MemberRef.class\
MethodEditor.class\
MultiArrayOperand.class\
NameAndType.class\
Opcode.class\
SerialVersionUID.class\
Switch.class\
TryCatch.class\
Type.class\
TypeComparator.class
include ../class.mk

@ -0,0 +1,117 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
/**
* MemberRef represents a method or field (as a <tt>NameAndType</tt>) and the
* class (as a <tt>Type</tt>) in which it is declared.
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class MemberRef {
private Type declaringClass;
private NameAndType nameAndType;
/**
* Constructor.
*
* @param declaringClass
* The type of the class which declared the member.
* @param nameAndType
* The name and type of the member.
*/
public MemberRef(final Type declaringClass, final NameAndType nameAndType) {
this.declaringClass = declaringClass;
this.nameAndType = nameAndType;
}
/**
* Get the type of the class which declared the member.
*
* @return The type of the class which declared the member.
*/
public Type declaringClass() {
return declaringClass;
}
/**
* Get the name of the member.
*
* @return The name of the member.
*/
public String name() {
return nameAndType.name();
}
/**
* Get the type of the member.
*
* @return The type of the member.
*/
public Type type() {
return nameAndType.type();
}
/**
* Get the name and type of the member.
*
* @return The name and type of the member.
*/
public NameAndType nameAndType() {
return nameAndType;
}
/**
* Convert the reference to a string.
*
* @return A string representation of the reference.
*/
public String toString() {
// Take advantage of PRINT_TRUNCATED in Type
final String className = declaringClass.toString();
return "<" + (type().isMethod() ? "Method" : "Field") + " " + className
+ "." + name() + " " + type() + ">";
}
/**
* Check if an object is equal to this reference.
*
* @param obj
* The object to compare against.
* @return true if equal, false if not.
*/
public boolean equals(final Object obj) {
return (obj instanceof MemberRef)
&& ((MemberRef) obj).declaringClass.equals(declaringClass)
&& ((MemberRef) obj).nameAndType.equals(nameAndType);
}
/**
* Hash the member reference.
*
* @return The hash code.
*/
public int hashCode() {
return declaringClass.hashCode() ^ nameAndType.hashCode();
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,76 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
/**
* <tt>MultiArrayOperand</tt> encapsulates the operands to the
* <tt>multianewarray</tt> instruction. Each <tt>MultiArrayOperand</tt>
* contains the type descriptor of the new multidimensional array the
* instruction creates, as well as the number of dimensions in the array.
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class MultiArrayOperand {
private Type type;
private int dim;
/**
* Constructor.
*
* @param type
* The element type of the array.
* @param dim
* The number of dimensions of the array.
*/
public MultiArrayOperand(final Type type, final int dim) {
this.type = type;
this.dim = dim;
}
/**
* Get the element type of the array.
*
* @return The element type of the array.
*/
public Type type() {
return type;
}
/**
* Get the number of dimensions of the array.
*
* @return The number of dimensions of the array.
*/
public int dimensions() {
return dim;
}
/**
* Convert the operand to a string.
*
* @return A string representation of the operand.
*/
public String toString() {
return type + " x " + dim;
}
}

@ -0,0 +1,83 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
/**
* Methods and fields are described by their name and type descriptor.
* NameAndType represents exactly that.
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class NameAndType {
private String name;
private Type type;
/**
* Constructor.
*/
public NameAndType(final String name, final Type type) {
this.name = name;
this.type = type;
}
/**
* Returns the name.
*/
public String name() {
return name;
}
/**
* Returns the type.
*/
public Type type() {
return type;
}
/**
* Returns a string representation of the name and type.
*/
public String toString() {
return "<NameandType " + name + " " + type + ">";
}
/**
* Check if an object is equal to this name and type.
*
* @param obj
* The object to compare against.
* @return <tt>true</tt> if equal
*/
public boolean equals(final Object obj) {
return (obj instanceof NameAndType)
&& ((NameAndType) obj).name.equals(name)
&& ((NameAndType) obj).type.equals(type);
}
/**
* Returns a hash of the name and type.
*/
public int hashCode() {
return name.hashCode() ^ type.hashCode();
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,459 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
import java.io.*;
import java.lang.reflect.*;
import java.security.*;
import java.util.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* <P>
* This class computes the serial version UID of a class modeled by a
* <code>ClassEditor</code>. Otherwise, we would have to load the class in
* order to compute its serial version UID. That would suck.
* </P>
*
* <P>
* The algorithm for computing the serial version UID can be found in the <A
* href="http://java.sun.com/j2se/1.3/docs/guide/serialization/spec">serialization
* spec</A>
* </P>
*/
public class SerialVersionUID {
/**
* Returns <code>true</code> if the class modeled by the given
* <code>ClassEditor</code> implements {@link java.io.Serializable
* Serializable}. It checks superclasses.
*/
public static boolean implementsSerializable(final ClassEditor ce) {
if (ce.type().equals(Type.OBJECT)) {
// Stop the recursion!
return (false);
}
final Type serializable = Type.getType("Ljava/io/Serializable;");
final Type[] interfaces = ce.interfaces();
for (int i = 0; i < interfaces.length; i++) {
if (interfaces[i].equals(serializable)) {
return (true);
}
}
// Does its superclass implement Serializable?
final Type superclass = ce.superclass();
final ClassInfoLoader loader = ce.classInfo().loader();
try {
final ClassInfo ci = loader.loadClass(superclass.className());
final ClassEditor sce = new ClassEditor(ce.context(), ci);
return (SerialVersionUID.implementsSerializable(sce));
} catch (final ClassNotFoundException ex) {
System.err.println("Could not load class: " + superclass
+ ", superclass of " + ce.name());
System.exit(1);
}
return (false);
}
/**
* Returns the serial version UID of the class modeled by the given
* <code>ClassEditor</code>.
*
* @param ce
* The class must implement {@link java.io.Serializable
* Serializable}
*/
public static long serialVersionUID(final ClassEditor ce) {
// Make sure the class implements Serializable
if (!SerialVersionUID.implementsSerializable(ce)) {
final String s = "Class " + ce.name()
+ " does not implement java.io.Serializable";
throw new IllegalArgumentException(s);
}
// If the class already has a serialVersionUID, return that
final FieldInfo[] fields = ce.fields();
for (int i = 0; i < fields.length; i++) {
final FieldEditor fe = new FieldEditor(ce, fields[i]);
if (fe.name().equals("serialVersionUID")) {
final Object value = fe.constantValue();
if (value != null) {
if (value instanceof Long) {
return (((Long) value).longValue());
}
}
}
}
// Now, compute the digest of the bytes using SHA
MessageDigest algorithm = null;
try {
algorithm = MessageDigest.getInstance("SHA");
} catch (final NoSuchAlgorithmException ex) {
final String s = "Can't use SHA-1 message digest algorith!";
throw new IllegalArgumentException(s);
}
final DataOutputStream dos = new DataOutputStream(
new DigestOutputStream(new ByteArrayOutputStream(), algorithm));
try {
// Write a bunch of information about the class to the output
// stream
SerialVersionUID.writeClassName(ce, dos);
SerialVersionUID.writeClassModifiers(ce, dos);
SerialVersionUID.writeInterfaceNames(ce, dos);
SerialVersionUID.writeFields(ce, dos);
SerialVersionUID.writeStaticInitializer(ce, dos);
SerialVersionUID.writeConstructors(ce, dos);
SerialVersionUID.writeMethods(ce, dos);
dos.flush();
dos.close();
} catch (final IOException ex) {
final String s = ("While computing serial version UID: " + ex);
throw new IllegalArgumentException(s);
}
// Compute the hash value from the first 64 bites of the digest
final byte[] digest = algorithm.digest();
long uid = 0;
for (int i = 0; i < Math.min(8, digest.length); i++) {
uid += (long) (digest[i] & 255) << (i * 8);
}
return (uid);
}
/**
* Writes the name of the class to the data output stream
*/
private static void writeClassName(final ClassEditor ce,
final DataOutputStream dos) throws IOException {
dos.writeUTF(ce.name().replace('/', '.'));
}
/**
* Returns the Java reflection modifiers for a given class
*/
static int getModifiers(final ClassEditor ce) {
// Translate BLOAT's class modifiers into Java's reflection
// modifiers
int modifiers = 0;
if (ce.isPublic()) {
modifiers |= Modifier.PUBLIC;
}
if (ce.isPrivate()) {
modifiers |= Modifier.PRIVATE;
}
if (ce.isProtected()) {
modifiers |= Modifier.PROTECTED;
}
if (ce.isStatic()) {
modifiers |= Modifier.STATIC;
}
if (ce.isFinal()) {
modifiers |= Modifier.FINAL;
}
if (ce.isAbstract()) {
modifiers |= Modifier.ABSTRACT;
}
if (ce.isInterface()) {
modifiers |= Modifier.INTERFACE;
}
return (modifiers);
}
/**
* Writes the class's modifiers to the output stream
*/
private static void writeClassModifiers(final ClassEditor ce,
final DataOutputStream dos) throws IOException {
dos.writeInt(SerialVersionUID.getModifiers(ce));
}
/**
* Writes the names of the interfaces implemented by the class to the output
* stream
*/
private static void writeInterfaceNames(final ClassEditor ce,
final DataOutputStream dos) throws IOException {
// Sort interfaces by name
final SortedSet sorted = new TreeSet();
final Type[] interfaces = ce.interfaces();
for (int i = 0; i < interfaces.length; i++) {
sorted.add(interfaces[i].className().replace('/', '.'));
}
final Iterator iter = sorted.iterator();
while (iter.hasNext()) {
final String name = (String) iter.next();
dos.writeUTF(name);
}
}
/**
* Returns the Java reflection modifiers for a field
*/
static int getModifiers(final FieldEditor fe) {
int modifiers = 0;
if (fe.isPublic()) {
modifiers |= Modifier.PUBLIC;
}
if (fe.isPrivate()) {
modifiers |= Modifier.PRIVATE;
}
if (fe.isProtected()) {
modifiers |= Modifier.PROTECTED;
}
if (fe.isPackage()) {
// Nothing
}
if (fe.isStatic()) {
modifiers |= Modifier.STATIC;
}
if (fe.isFinal()) {
modifiers |= Modifier.FINAL;
}
if (fe.isVolatile()) {
modifiers |= Modifier.VOLATILE;
}
if (fe.isTransient()) {
// Kind of a moot point
modifiers |= Modifier.TRANSIENT;
}
return (modifiers);
}
/**
* Writes information about the class's fields to the output stream
*/
private static void writeFields(final ClassEditor ce,
final DataOutputStream dos) throws IOException {
// Sort the fields by their names
final SortedSet sorted = new TreeSet(new Comparator() {
public int compare(Object o1, Object o2) {
FieldEditor fe1 = (FieldEditor) o1;
FieldEditor fe2 = (FieldEditor) o2;
return (fe1.name().compareTo(fe2.name()));
}
});
final FieldInfo[] infos = ce.fields();
for (int i = 0; i < infos.length; i++) {
final FieldEditor fe = new FieldEditor(ce, infos[i]);
// Ignore private static and private transient fields
if (fe.isPrivate() && fe.isStatic()) {
break;
} else if (fe.isPrivate() && fe.isTransient()) {
break;
} else {
sorted.add(fe);
}
}
final Iterator iter = sorted.iterator();
while (iter.hasNext()) {
final FieldEditor fe = (FieldEditor) iter.next();
dos.writeUTF(fe.name());
dos.writeInt(SerialVersionUID.getModifiers(fe));
dos.writeUTF(fe.type().descriptor());
}
}
/**
* Returns the Java reflection descriptors for a method
*/
static int getModifiers(final MethodEditor me) {
int modifiers = 0;
if (me.isPublic()) {
modifiers |= Modifier.PUBLIC;
}
if (me.isPrivate()) {
modifiers |= Modifier.PRIVATE;
}
if (me.isProtected()) {
modifiers |= Modifier.PROTECTED;
}
if (me.isPackage()) {
// Nothing
}
if (me.isStatic()) {
modifiers |= Modifier.STATIC;
}
if (me.isFinal()) {
modifiers |= Modifier.FINAL;
}
if (me.isSynchronized()) {
modifiers |= Modifier.SYNCHRONIZED;
}
if (me.isNative()) {
modifiers |= Modifier.NATIVE;
}
if (me.isAbstract()) {
modifiers |= Modifier.ABSTRACT;
}
if (me.isInterface()) {
modifiers |= Modifier.INTERFACE;
}
return (modifiers);
}
/**
* Writes information about the classes static initializer if it has one
*/
private static void writeStaticInitializer(final ClassEditor ce,
final DataOutputStream dos) throws IOException {
MethodEditor clinit = null;
final MethodInfo[] methods = ce.methods();
for (int i = 0; i < methods.length; i++) {
final MethodEditor me = new MethodEditor(ce, methods[i]);
if (me.name().equals("<clinit>")) {
clinit = me;
break;
}
}
if (clinit != null) {
dos.writeUTF("<clinit>");
dos.writeInt(Modifier.STATIC);
dos.writeUTF("()V");
}
}
/**
* Writes information about the class's constructors
*/
private static void writeConstructors(final ClassEditor ce,
final DataOutputStream dos) throws IOException {
// Sort constructors by their signatures
final SortedSet sorted = new TreeSet(new Comparator() {
public int compare(Object o1, Object o2) {
MethodEditor me1 = (MethodEditor) o1;
MethodEditor me2 = (MethodEditor) o2;
return (me1.type().descriptor().compareTo(me2.type()
.descriptor()));
}
});
final MethodInfo[] methods = ce.methods();
for (int i = 0; i < methods.length; i++) {
final MethodEditor me = new MethodEditor(ce, methods[i]);
if (me.name().equals("<init>")) {
if (!me.isPrivate()) {
// Ignore private constructors
sorted.add(me);
}
}
}
final Iterator iter = sorted.iterator();
while (iter.hasNext()) {
final MethodEditor init = (MethodEditor) iter.next();
dos.writeUTF("<init>");
dos.writeInt(SerialVersionUID.getModifiers(init));
dos.writeUTF(init.type().descriptor());
}
}
/**
* Write information about the class's methods
*/
private static void writeMethods(final ClassEditor ce,
final DataOutputStream dos) throws IOException {
// Sort constructors by their names and signatures
final SortedSet sorted = new TreeSet(new Comparator() {
public int compare(Object o1, Object o2) {
MethodEditor me1 = (MethodEditor) o1;
MethodEditor me2 = (MethodEditor) o2;
String d1 = me1.name() + me1.type().descriptor();
String d2 = me2.name() + me2.type().descriptor();
return (d1.compareTo(d2));
}
});
final MethodInfo[] methods = ce.methods();
for (int i = 0; i < methods.length; i++) {
final MethodEditor me = new MethodEditor(ce, methods[i]);
if (!me.isPrivate() && !me.isConstructor()
&& !me.name().equals("<clinit>")) {
// Ignore private methods
sorted.add(me);
}
}
final Iterator iter = sorted.iterator();
while (iter.hasNext()) {
final MethodEditor me = (MethodEditor) iter.next();
dos.writeUTF(me.name());
dos.writeInt(SerialVersionUID.getModifiers(me));
dos.writeUTF(me.type().descriptor());
}
}
}

@ -0,0 +1,232 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
/**
* Switch is used to hold the lookup values and branch targets of a tableswitch
* or lookup switch instruction.
* <p>
* The tableswitch low-to-high range of values is represented by storing each
* lookup value in the range. This allows the tableswitch to be replaced with a
* lookupswitch if branches are deleted.
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class Switch {
private Label defaultTarget;
private Label[] targets;
private int[] values;
/**
* Constructor.
*
* @param defaultTarget
* The default target of the switch.
* @param targets
* The non-default branch targets of the switch.
* @param values
* The lookup values of the switch. This array must be the same
* size as the targets array.
*/
public Switch(final Label defaultTarget, final Label[] targets,
final int[] values) {
this.defaultTarget = defaultTarget;
this.targets = targets;
this.values = values;
sort();
uniq();
}
/**
* Set the default target of the switch.
*
* @param target
* The default target of the switch.
*/
public void setDefaultTarget(final Label target) {
defaultTarget = target;
}
/**
* Get the default target of the switch.
*
* @return The default target of the switch.
*/
public Label defaultTarget() {
return defaultTarget;
}
/**
* Get the non-default branch targets of the switch. The targets are sorted
* by the corresponding lookup value.
*
* @return The non-default branch targets of the switch.
*/
public Label[] targets() {
return targets;
}
/**
* Get the lookup values of the switch, sorted low to high.
*
* @return The lookup values of the switch.
*/
public int[] values() {
return values;
}
/**
* Check if the all the values in the range of lookup values are contiguous.
* If they are, a table switch can be used. If not a lookupswitch can be
* used.
*
* @return true if contiguous, false if not.
*/
public boolean hasContiguousValues() {
return values.length == highValue() - lowValue() + 1;
}
/**
* Get the low value in the range the lookup values.
*
* @return The low value.
*/
public int lowValue() {
return values[0];
}
/**
* Get the high value in the range the lookup values.
*
* @return The high value.
*/
public int highValue() {
return values[values.length - 1];
}
/**
* Sort the targets and values arrays so that values is sorted low to high.
*/
private void sort() {
quicksort(0, values.length - 1);
}
/**
* Utility function to sort the targets and values arrays so that values is
* sorted low to high.
*
* @param p
* The low index of the portion of the array to sort.
* @param r
* The high index of the portion of the array to sort.
*/
private void quicksort(final int p, final int r) {
if (p < r) {
final int q = partition(p, r);
quicksort(p, q);
quicksort(q + 1, r);
}
}
/**
* Utility function to sort the targets and values arrays so that values is
* sorted low to high.
* <p>
* Partition the arrays so that the values less than values[p] are to the
* left.
*
* @param p
* The low index of the portion of the array to sort.
* @param r
* The high index of the portion of the array to sort.
* @return The index at which the partition finished.
*/
private int partition(final int p, final int r) {
final int x = values[p];
int i = p - 1;
int j = r + 1;
while (true) {
do {
j--;
} while (values[j] > x);
do {
i++;
} while (values[i] < x);
if (i < j) {
final int v = values[i];
values[i] = values[j];
values[j] = v;
final Label t = targets[i];
targets[i] = targets[j];
targets[j] = t;
} else {
return j;
}
}
}
/**
* Remove duplicates from the values and targets arrays.
*/
private void uniq() {
if (values.length == 0) {
return;
}
final int[] v = new int[values.length];
final Label[] t = new Label[values.length];
v[0] = values[0];
t[0] = targets[0];
int j = 1;
for (int i = 1; i < values.length; i++) {
if (v[j - 1] != values[i]) {
v[j] = values[i];
t[j] = targets[i];
j++;
}
}
values = new int[j];
System.arraycopy(v, 0, values, 0, j);
targets = new Label[j];
System.arraycopy(t, 0, targets, 0, j);
}
/**
* Convert the operand to a string.
*
* @return A string representation of the operand.
*/
public String toString() {
return "" + values.length + " pairs";
}
}

@ -0,0 +1,106 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
/**
* TryCatch holds the labels for the start and end of a protected block and the
* beginning of a catch block and the type of the exception to catch.
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class TryCatch {
private Label start;
private Label end;
private Label handler;
private Type type;
/**
* Constructor.
*
* @param start
* The start label of the protected block.
* @param end
* The label of the instruction after the end of the protected
* block.
* @param handler
* The start label of the exception handler.
* @param type
* The type of exception to catch.
*/
public TryCatch(final Label start, final Label end, final Label handler,
final Type type) {
this.start = start;
this.end = end;
this.handler = handler;
this.type = type;
}
/**
* Get the start label of the protected block.
*
* @return The start label.
*/
public Label start() {
return start;
}
/**
* Get the end label of the protected block.
*
* @return The end label.
*/
public Label end() {
return end;
}
/**
* Get the start label of the catch block.
*
* @return The handler label.
*/
public Label handler() {
return handler;
}
/**
* Set the start label of the catch block.
*/
public void setHandler(final Label handler) {
this.handler = handler;
}
/**
* Get the type of the exception to catch.
*
* @return The type of the exception to catch.
*/
public Type type() {
return type;
}
public String toString() {
return "try " + start + ".." + end + " catch (" + type + ") " + handler;
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,120 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
import java.util.*;
import EDU.purdue.cs.bloat.util.*;
// // For testing only
// import EDU.purdue.cs.bloat.file.*;
// import EDU.purdue.cs.bloat.context.*;
/**
* A <tt>TypeComparator</tt> orders <tt>Type</tt>s such that a subclass
* preceededs its superclass. Note that this doesn't really work with
* interfaces.
*/
public final class TypeComparator implements Comparator {
public static boolean DEBUG = false;
private EditorContext context;
private static void db(final String s) {
if (TypeComparator.DEBUG) {
System.out.println(s);
}
}
/**
* Constructor.
*/
public TypeComparator(final EditorContext context) {
this.context = context;
}
/**
* Returns a negative value if o1 < o2 (t1 is a subclass of t2). Otherwise,
* it returns a positive value.
*/
public int compare(final Object o1, final Object o2) {
Assert.isTrue(o1 instanceof Type, o1 + " is not a Type");
Assert.isTrue(o2 instanceof Type, o2 + " is not a Type");
final Type t1 = (Type) o1;
final Type t2 = (Type) o2;
TypeComparator.db("Comparing " + t1 + " to " + t2);
final ClassHierarchy hier = context.getHierarchy();
if (hier.subclassOf(t1, t2)) {
TypeComparator.db(" " + t1 + " is a subclass of " + t2);
return (-1);
} else if (hier.subclassOf(t2, t1)) {
TypeComparator.db(" " + t2 + " is a subclass of " + t1);
return (1);
} else {
TypeComparator.db(" " + t1 + " and " + t2 + " are unrelated");
// Don't return 0. If you do, the type will not get included in
// the sorted set. Weird.
return (1);
}
}
/**
* Indicates whether some other object is "equal to" this Comparator.
*/
public boolean equals(final Object other) {
return (other instanceof TypeComparator);
}
// /**
// * Test program. Reads class names from the command line.
// */
// public static void main(String[] args) {
// // Make a list of class names
// List names = new ArrayList();
// for(int i = 0; i < args.length; i++) {
// names.add(args[i]);
// }
// // Do some BLOAT magic
// EditorContext context =
// new CachingBloatContext(new ClassFileLoader(), names, false);
// Collection classes = context.getHierarchy().classes();
// TypeComparator.DEBUG = true;
// SortedSet sorted = new TreeSet(new TypeComparator(context));
// sorted.addAll(classes);
// System.out.println(classes.size() + " classes");
// System.out.println(sorted.size() + " sorted types:");
// Object[] array = sorted.toArray();
// for(int i = 0; i < array.length; i++) {
// System.out.println(" " + array[i]);
// }
// }
}

@ -0,0 +1,53 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
import java.util.*;
import EDU.purdue.cs.bloat.tree.*;
public class UseMap {
public Hashtable map;
public UseMap() {
map = new Hashtable();
}
public void add(final LocalExpr use, final Instruction inst) {
final Node def = use.def();
if (def != null) {
map.put(inst, def);
}
}
public boolean hasDef(final Instruction inst) {
return map.containsKey(inst);
}
public boolean hasSameDef(final Instruction a, final Instruction b) {
return map.containsKey(a) && map.containsKey(b)
&& (map.get(a) == map.get(b));
}
}

@ -0,0 +1,89 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.file;
import java.io.*;
/**
* Attribute is an abstract class for an attribute defined for a method, field,
* or class. An attribute consists of its name (represented as an index into the
* constant pool) and its length. Attribute is extended to represent a constant
* value, code, exceptions, etc.
*
* @see Code
* @see ConstantValue
* @see Exceptions
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public abstract class Attribute {
protected int nameIndex;
protected int length;
/**
* Constructor.
*
* @param nameIndex
* The index into the constant pool of the name of the attribute.
* @param length
* The length of the attribute, excluding the header.
*/
public Attribute(final int nameIndex, final int length) {
this.nameIndex = nameIndex;
this.length = length;
}
/**
* Write the attribute to a data stream.
*
* @param out
* The data stream of the class file.
*/
public abstract void writeData(DataOutputStream out) throws IOException;
/**
* Returns a string representation of the attribute.
*/
public String toString() {
return "(attribute " + nameIndex + " " + length + ")";
}
/**
* Returns the index into the constant pool of the name of the attribute.
*/
public int nameIndex() {
return nameIndex;
}
/**
* Returns the length of the attribute, excluding the header.
*/
public int length() {
return length;
}
public Object clone() {
throw new UnsupportedOperationException("Cannot clone Attribute! "
+ " (subclass: " + this.getClass() + ")");
}
}

@ -0,0 +1,980 @@
/**
* 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() + ")";
}
}

@ -0,0 +1,501 @@
/**
* 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
}
}

@ -0,0 +1,5 @@
package EDU.purdue.cs.bloat.file;
public interface ClassSource {
Class loadClass(String name) throws ClassNotFoundException ;
}

@ -0,0 +1,451 @@
/**
* 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
+ ")";
}
}

@ -0,0 +1,115 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.file;
import java.io.*;
/**
* The ConstantValue attribute stores an index into the constant pool that
* represents constant value. A class's static fields have constant value
* attributes.
*
* @see Field
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class ConstantValue extends Attribute {
private int constantValueIndex;
/**
* Creates a new <code>ConstantValue</code> from scratch
*
* @param nameIndex
* The index in the constant pool of the UTF8 string
* "ConstantValue"
* @param constantValueIndex
* The index in the constant pool of the Constant containing the
* constant value
*/
ConstantValue(final int nameIndex, final int length,
final int constantValueIndex) {
super(nameIndex, length);
this.constantValueIndex = constantValueIndex;
}
/**
* Constructor. Create a ConstantValue attribute from a data stream.
*
* @param in
* The data stream of the class file.
* @param nameIndex
* The index into the constant pool of the name of the attribute.
* @param length
* The length of the attribute, excluding the header.
* @exception IOException
* If an error occurs while reading.
*/
public ConstantValue(final DataInputStream in, final int nameIndex,
final int length) throws IOException {
super(nameIndex, length);
constantValueIndex = in.readUnsignedShort();
}
/**
* Write the attribute to a data stream.
*
* @param out
* The data stream of the class file.
*/
public void writeData(final DataOutputStream out) throws IOException {
out.writeShort(constantValueIndex);
}
/**
* Returns the index into the constant pool of the constant value.
*/
public int constantValueIndex() {
return constantValueIndex;
}
/**
* Set the index into the constant pool of the constant value.
*/
public void setConstantValueIndex(final int index) {
this.constantValueIndex = index;
}
/**
* Private constructor used for cloning.
*/
private ConstantValue(final ConstantValue other) {
super(other.nameIndex, other.length);
this.constantValueIndex = other.constantValueIndex;
}
public Object clone() {
return (new ConstantValue(this));
}
/**
* Converts the attribute to a string.
*/
public String toString() {
return "(constant-value " + constantValueIndex + ")";
}
}

@ -0,0 +1,9 @@
package EDU.purdue.cs.bloat.file;
public class DefaultClassSource implements ClassSource {
public Class loadClass(String name) throws ClassNotFoundException {
return Class.forName(name);
}
}

@ -0,0 +1,140 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.file;
import java.io.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* Exceptions describes the types of exceptions that a method may throw. The
* Exceptions attribute stores a list of indices into the constant pool of the
* typs of exceptions thrown by the method.
*
* @see Method
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class Exceptions extends Attribute {
private int[] exceptions;
private ClassInfo classInfo;
/**
* Constructor for create an <code>Exceptions</code> from scratch.
*
* @param nameIndex
* The index of the UTF8 string "Exceptions" in the class's
* constant pool
* @param exceptions
* A non-<code>null</code> array of indices into the constant
* pool for the types of the exceptions
*/
Exceptions(final ClassInfo info, final int nameIndex, final int[] exceptions) {
super(nameIndex, (2 * exceptions.length) + 2);
this.classInfo = info;
this.exceptions = exceptions;
}
/**
* Constructor. Create an Exceptions attribute from a data stream.
*
* @param in
* The data stream of the class file.
* @param nameIndex
* The index into the constant pool of the name of the attribute.
* @param length
* The length of the attribute, excluding the header.
* @exception IOException
* If an error occurs while reading.
*/
public Exceptions(final ClassInfo classInfo, final DataInputStream in,
final int nameIndex, final int length) throws IOException {
super(nameIndex, length);
this.classInfo = classInfo;
final int count = in.readUnsignedShort();
exceptions = new int[count];
for (int i = 0; i < count; i++) {
exceptions[i] = in.readUnsignedShort();
}
}
/**
* Write the attribute to a data stream.
*
* @param out
* The data stream of the class file.
* @exception IOException
* If an error occurs while writing.
*/
public void writeData(final DataOutputStream out) throws IOException {
out.writeShort(exceptions.length);
for (int i = 0; i < exceptions.length; i++) {
out.writeShort(exceptions[i]);
}
}
/**
* Get the indices into the constant pool of the types of the exceptions
* thrown by this method.
*
* @return The indices of the types of the exceptions thrown.
*/
public int[] exceptionTypes() {
return exceptions;
}
/**
* Get the length of the attribute.
*/
public int length() {
return 2 + exceptions.length * 2;
}
/**
* Private constructor used for cloning.
*/
private Exceptions(final Exceptions other) {
super(other.nameIndex, other.length);
this.exceptions = new int[other.exceptions.length];
System.arraycopy(other.exceptions, 0, this.exceptions, 0,
other.exceptions.length);
this.classInfo = other.classInfo;
}
public Object clone() {
return (new Exceptions(this));
}
/**
* Returns a string representation of the attribute.
*/
public String toString() {
return "(exceptions)";
}
}

@ -0,0 +1,293 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.file;
import java.io.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* Field models a field (member variable) in a class. The Field class grants
* access to information such as the field's modifiers, its name and type
* descriptor (represented as indices into the constant pool), and any
* attributes of the field. Static fields have a ConstantValue attribute.
*
* @see ConstantValue
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class Field implements FieldInfo {
private ClassInfo classInfo;
private int modifiers;
private int name;
private int type;
private Attribute[] attrs;
private ConstantValue constantValue;
/**
* Constructor for creating a new field from scratch
*/
Field(final ClassInfo classInfo, final int modifiers, final int typeIndex,
final int nameIndex) {
this.classInfo = classInfo;
this.modifiers = modifiers;
this.name = nameIndex;
this.type = typeIndex;
this.attrs = new Attribute[0];
this.constantValue = null;
}
/**
* Constructor for creating a new field that has a constant value from
* scratch
*/
Field(final ClassInfo classInfo, final int modifiers, final int typeIndex,
final int nameIndex, final int cvNameIndex,
final int constantValueIndex) {
this.classInfo = classInfo;
this.modifiers = modifiers;
this.name = nameIndex;
this.type = typeIndex;
this.constantValue = new ConstantValue(cvNameIndex, 2,
constantValueIndex);
// The constant value is an attribute
this.attrs = new Attribute[1];
this.attrs[0] = constantValue;
}
/**
* Constructor. Read a field from a class file.
*
* @param in
* The data stream of the class file.
* @param classInfo
* The class file containing the field.
* @exception IOException
* If an error occurs while reading.
*/
public Field(final DataInputStream in, final ClassInfo classInfo)
throws IOException {
this.classInfo = classInfo;
modifiers = in.readUnsignedShort();
name = in.readUnsignedShort();
type = in.readUnsignedShort();
final int numAttributes = in.readUnsignedShort();
attrs = new Attribute[numAttributes];
for (int i = 0; i < numAttributes; i++) {
final int nameIndex = in.readUnsignedShort();
final int length = in.readInt();
final Constant name = classInfo.constants()[nameIndex];
if (name != null) {
if ("ConstantValue".equals(name.value())) {
constantValue = new ConstantValue(in, nameIndex, length);
attrs[i] = constantValue;
}
}
if (attrs[i] == null) {
attrs[i] = new GenericAttribute(in, nameIndex, length);
}
}
}
/**
* Get the class which declared the field.
*
* @return The ClassInfo of the class which declared the field.
*/
public ClassInfo declaringClass() {
return classInfo;
}
/**
* Set the index into the constant pool of the name of the field.
*
* @param name
* The name of the field.
*/
public void setNameIndex(final int name) {
this.name = name;
}
/**
* Set the index into the constant pool of the type of the field.
*
* @param type
* The type of the field.
*/
public void setTypeIndex(final int type) {
this.type = type;
}
/**
* Get the index into the constant pool of the name of the field.
*
* @return The index into the constant pool of the name of the field.
*/
public int nameIndex() {
return name;
}
/**
* Get the index into the constant pool of the type of the field.
*
* @return The index into the constant pool of the type of the field.
*/
public int typeIndex() {
return type;
}
/**
* Set the modifiers of the field. The values correspond to the constants in
* the Modifiers class.
*
* @param modifiers
* A bit vector of modifier flags for the field.
* @see Modifiers
*/
public void setModifiers(final int modifiers) {
this.modifiers = modifiers;
}
/**
* Get the modifiers of the field. The values correspond to the constants in
* the Modifiers class.
*
* @return A bit vector of modifier flags for the field.
* @see Modifiers
*/
public int modifiers() {
return modifiers;
}
/**
* Get the index into the constant pool of the field's constant value, if
* any. Returns 0 if the field does not have a constant value.
*
* @see ClassInfo#constants
*/
public int constantValue() {
if (constantValue != null) {
return constantValue.constantValueIndex();
}
return 0;
}
/**
* Set the index into the constant pool of the field's constant value.
*
* @see ClassInfo#constants
*/
public void setConstantValue(final int index) {
if (constantValue != null) {
constantValue.setConstantValueIndex(index);
}
}
/**
* Write the field to a class file.
*
* @param out
* The data stream of the class file.
* @exception IOException
* If an error occurs while writing.
*/
public void write(final DataOutputStream out) throws IOException {
out.writeShort(modifiers);
out.writeShort(name);
out.writeShort(type);
out.writeShort(attrs.length);
for (int i = 0; i < attrs.length; i++) {
out.writeShort(attrs[i].nameIndex());
out.writeInt(attrs[i].length());
attrs[i].writeData(out);
}
}
/**
* Convert the field to a string.
*
* @return A string representation of the field.
*/
public String toString() {
String x = "";
x += " (modifiers";
if ((modifiers & Modifiers.PUBLIC) != 0) {
x += " PUBLIC";
}
if ((modifiers & Modifiers.PRIVATE) != 0) {
x += " PRIVATE";
}
if ((modifiers & Modifiers.PROTECTED) != 0) {
x += " PROTECTED";
}
if ((modifiers & Modifiers.STATIC) != 0) {
x += " STATIC";
}
if ((modifiers & Modifiers.FINAL) != 0) {
x += " FINAL";
}
if ((modifiers & Modifiers.SYNCHRONIZED) != 0) {
x += " SYNCHRONIZED";
}
if ((modifiers & Modifiers.VOLATILE) != 0) {
x += " VOLATILE";
}
if ((modifiers & Modifiers.TRANSIENT) != 0) {
x += " TRANSIENT";
}
if ((modifiers & Modifiers.NATIVE) != 0) {
x += " NATIVE";
}
if ((modifiers & Modifiers.INTERFACE) != 0) {
x += " INTERFACE";
}
if ((modifiers & Modifiers.ABSTRACT) != 0) {
x += " ABSTRACT";
}
x += ")";
if (constantValue != null) {
x += " " + constantValue;
}
return "(field " + name + " " + type + x + ")";
}
}

@ -0,0 +1,82 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.file;
import java.io.*;
/**
* The Java Virtual Machine Specification allows implementors to invent their
* own attributes. GenericAttribute models attributes whose name BLOAT does not
* recognize.
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class GenericAttribute extends Attribute {
private byte[] data;
/**
* Constructor. Create an attribute from a data stream.
*
* @param in
* The data stream of the class file.
* @param nameIndex
* The index into the constant pool of the name of the attribute.
* @param length
* The length of the attribute, excluding the header.
* @exception IOException
* If an error occurs while reading.
*/
public GenericAttribute(final DataInputStream in, final int nameIndex,
final int length) throws IOException {
super(nameIndex, length);
data = new byte[length];
for (int read = 0; read < length;) {
read += in.read(data, read, length - read);
}
}
/**
* Write the attribute to a data stream.
*
* @param out
* The data stream of the class file.
* @exception IOException
* If an error occurs while writing.
*/
public void writeData(final DataOutputStream out) throws IOException {
out.write(data, 0, data.length);
}
/**
* Private constructor used in cloning.
*/
private GenericAttribute(final GenericAttribute other) {
super(other.nameIndex, other.length);
this.data = new byte[other.data.length];
System.arraycopy(other.data, 0, this.data, 0, other.data.length);
}
public Object clone() {
return (new GenericAttribute(this));
}
}

@ -0,0 +1,260 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.file;
import java.io.*;
import java.util.*;
import java.util.jar.*;
import java.util.zip.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* Does a lot of the same stuff as <tt>ClassFileLoader</tt>, but classes are
* committed to a JAR file instead of regular files.
*/
public class JarFileCommitter extends ClassFileLoader {
private FunkyJar funky;
/**
* Constructor.
*
* @param file
* <tt>File</tt> representing JAR file
* @param compress
* If <tt>true</tt>, contents of JAR file is compressed
* @param version
* Version for the JAR file's manifest
* @param author
* Author string from JAR file's manifest
*/
public JarFileCommitter(final File file, final boolean compress,
final String version, final String author) throws IOException {
funky = new FunkyJar(file, compress, version, author);
}
protected OutputStream outputStreamFor(final String name)
throws IOException {
funky.newEntry(name);
return funky;
}
public OutputStream outputStreamFor(final ClassInfo info)
throws IOException {
// This is funky. Recall that a JarOutputStream is also an output
// stream. So, we just return it. This is why we have to
// override the write, etc. methods.
// Make a new entry based on the class name
final String name = info.name() + ".class";
return outputStreamFor(name);
}
/**
* Signifies that we are finished with this <tt>JarFileCommitter</tt>.
*/
public void done() throws IOException {
funky.done();
}
}
/**
* We subclass JarOutputStream so that we can return an OutputStream to which a
* BLOATed class file will be written. In order to accomodate non-compression,
* we have to perform the checksum along the way. Bletch.
*/
class FunkyJar extends JarOutputStream {
private static final String MANIFEST = JarFile.MANIFEST_NAME;
private static final String MANIFEST_DIR = "META-INF/";
private static final CRC32 crc32 = new CRC32();
private boolean compress;
private JarEntry currEntry;
private Size size;
class Size {
long value = 0;
}
/**
* Constructor.
*/
public FunkyJar(final File file, boolean compress, final String version,
final String author) throws IOException {
super(new FileOutputStream(file));
this.compress = compress;
if (compress) {
this.setMethod(ZipOutputStream.DEFLATED);
} else {
this.setMethod(ZipOutputStream.STORED);
}
final Manifest manifest = new Manifest();
final Attributes global = manifest.getMainAttributes();
if (global.getValue(Attributes.Name.MANIFEST_VERSION) == null) {
global.put(Attributes.Name.MANIFEST_VERSION, version);
}
if (global.getValue(new Attributes.Name("Created-By")) == null) {
global.put(new Attributes.Name("Created-By"), author);
}
// Add directory for manifest
JarEntry entry = new JarEntry(FunkyJar.MANIFEST_DIR);
entry.setTime(System.currentTimeMillis());
entry.setSize(0); // Directories have size 0
entry.setCrc(0); // Checksum is 0
this.putNextEntry(entry);
// Add manifest
entry = new JarEntry(FunkyJar.MANIFEST);
entry.setTime(System.currentTimeMillis());
if (!compress) {
// Have to compute checksum ourselves. Use an ugly anonymous
// inner class. Influenced by CRC32OutputStream in
// sun.tools.jar.Main. Please don't sue me. I have no money.
// Maybe you could give me a job instead. Of course, then I'd
// have money and you would sue me. Hmm.
final Size size = new Size();
FunkyJar.crc32.reset();
manifest.write(new OutputStream() {
public void write(final int r) throws IOException {
FunkyJar.crc32.update(r);
size.value++;
}
public void write(final byte[] b) throws IOException {
FunkyJar.crc32.update(b, 0, b.length);
size.value += b.length;
}
public void write(final byte[] b, final int off, final int len)
throws IOException {
FunkyJar.crc32.update(b, off, len);
size.value += len - off;
}
});
entry.setSize(size.value);
entry.setCrc(FunkyJar.crc32.getValue());
}
this.putNextEntry(entry);
manifest.write(this); // Write the manifest to JAR file
this.closeEntry();
}
public void newEntry(final String name) throws IOException {
makeDirs(name);
currEntry = new JarEntry(name);
currEntry.setTime(System.currentTimeMillis());
if (compress) {
currEntry.setMethod(ZipEntry.DEFLATED);
} else {
currEntry.setMethod(ZipEntry.STORED);
}
this.putNextEntry(currEntry);
FunkyJar.crc32.reset();
this.size = new Size();
}
private Set dirs;
/**
* look at the path name specified by key and create zip entries for each
* directory level not already added.
*/
private void makeDirs(final String key) throws IOException {
if (dirs == null) {
dirs = new HashSet();
}
int idx = 0;
int last = 0;
while ((last = key.indexOf('/', idx + 1)) != -1) {
final String aDir = key.substring(0, last + 1);
if (!dirs.contains(aDir)) {
dirs.add(aDir);
this.putNextEntry(new ZipEntry(aDir));
this.closeEntry();
}
idx = last;
}
}
public void write(final int r) throws IOException {
super.write(r);
if (!compress && (size != null)) {
FunkyJar.crc32.update(r);
size.value++;
}
}
public void write(final byte[] b) throws IOException {
super.write(b);
if (!compress && (size != null)) {
FunkyJar.crc32.update(b, 0, b.length);
size.value += b.length;
}
}
public void write(final byte[] b, final int off, final int len)
throws IOException {
super.write(b, off, len);
if (!compress && (size != null)) {
FunkyJar.crc32.update(b, off, len);
size.value += len - off;
}
}
public void close() throws IOException {
// Okay, everythings is done. Set some values for the entry,
// cross your fingers, and run away.
if (!compress && (size != null)) {
currEntry.setSize(size.value);
currEntry.setCrc(FunkyJar.crc32.getValue());
}
currEntry = null;
size = null;
this.closeEntry();
// Note that we don't invoke the super class method.
}
/**
* Signifies that we are finished with this <tt>JarFileCommitter</tt>.
*/
public void done() throws IOException {
super.close();
}
}

@ -0,0 +1,142 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.file;
import java.io.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* LineNumberTable is an attribute of a code attribute. A LineNumberTable stores
* information that relates indices into the code array (instructions) to the
* lines of code in the source file from which they were compiled. This optional
* attribute is used with debuggers (<i>duh</i>) and consists of an array of
* <tt>reflect.LineNumberDebugInfo</tt>.
*
* @see Code
* @see EDU.purdue.cs.bloat.reflect.LineNumberDebugInfo
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class LineNumberTable extends Attribute {
private LineNumberDebugInfo[] lineNumbers;
/**
* Constructor. Create an attribute from a data stream.
*
* @param in
* The data stream of the class file.
* @param nameIndex
* The index into the constant pool of the name of the attribute.
* @param length
* The length of the attribute, excluding the header.
* @exception IOException
* If an error occurs while reading.
*/
public LineNumberTable(final DataInputStream in, final int nameIndex,
final int length) throws IOException {
super(nameIndex, length);
final int numLines = in.readUnsignedShort();
lineNumbers = new LineNumberDebugInfo[numLines];
for (int i = 0; i < lineNumbers.length; i++) {
final int startPC = in.readUnsignedShort();
final int lineNumber = in.readUnsignedShort();
lineNumbers[i] = new LineNumberDebugInfo(startPC, lineNumber);
}
}
/**
* Get the line number debug info for the code.
*
* @return The line number debug info for the code.
*/
public LineNumberDebugInfo[] lineNumbers() {
return lineNumbers;
}
/**
* Set the line number debug info for the code.
*
* @param lineNumbers
* The line number debug info for the code.
*/
public void setLineNumbers(final LineNumberDebugInfo[] lineNumbers) {
this.lineNumbers = lineNumbers;
}
/**
* Get the length of the attribute.
*
* @return The length of the attribute.
*/
public int length() {
return 2 + lineNumbers.length * 4;
}
public String toString() {
String x = "(lines";
for (int i = 0; i < lineNumbers.length; i++) {
x += "\n (line #" + lineNumbers[i].lineNumber() + " pc="
+ lineNumbers[i].startPC() + ")";
}
return x + ")";
}
/**
* Write the attribute to a data stream.
*
* @param out
* The data stream of the class file.
* @exception IOException
* If an error occurs while writing.
*/
public void writeData(final DataOutputStream out) throws IOException {
out.writeShort(lineNumbers.length);
for (int i = 0; i < lineNumbers.length; i++) {
out.writeShort(lineNumbers[i].startPC());
out.writeShort(lineNumbers[i].lineNumber());
}
}
/**
* Private constructor used in cloning.
*/
private LineNumberTable(final LineNumberTable other) {
super(other.nameIndex, other.length);
this.lineNumbers = new LineNumberDebugInfo[other.lineNumbers.length];
for (int i = 0; i < other.lineNumbers.length; i++) {
this.lineNumbers[i] = (LineNumberDebugInfo) other.lineNumbers[i]
.clone();
}
}
public Object clone() {
return (new LineNumberTable(this));
}
}

@ -0,0 +1,147 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.file;
import java.io.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* LocalVariableTable represents debugging information that may be used by a
* debugger to determine the value of a given local variable during program
* execution. It is essentially an array of <tt>reflect.LocalDebugInfo</tt>.
*
* @see EDU.purdue.cs.bloat.reflect.LocalDebugInfo
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class LocalVariableTable extends Attribute {
private LocalDebugInfo[] locals;
/**
* Constructor. Create an attribute from a data stream.
*
* @param in
* The data stream of the class file.
* @param index
* The index into the constant pool of the name of the attribute.
* @param len
* The length of the attribute, excluding the header.
* @exception IOException
* If an error occurs while reading.
*/
public LocalVariableTable(final DataInputStream in, final int index,
final int len) throws IOException {
super(index, len);
final int numLocals = in.readUnsignedShort();
locals = new LocalDebugInfo[numLocals];
for (int i = 0; i < locals.length; i++) {
final int startPC = in.readUnsignedShort();
final int length = in.readUnsignedShort();
final int nameIndex = in.readUnsignedShort();
final int typeIndex = in.readUnsignedShort();
final int varIndex = in.readUnsignedShort();
locals[i] = new LocalDebugInfo(startPC, length, nameIndex,
typeIndex, varIndex);
}
}
/**
* Get the local variable debug info for the code.
*
* @return The local variable debug info for the code.
*/
public LocalDebugInfo[] locals() {
return locals;
}
/**
* Set the local variable debug info for the code.
*
* @param locals
* The local variable debug info for the code.
*/
public void setLocals(final LocalDebugInfo[] locals) {
this.locals = locals;
}
/**
* Get the length of the attribute.
*
* @return The length of the attribute.
*/
public int length() {
return 2 + locals.length * 10;
}
public String toString() {
String x = "(locals";
for (int i = 0; i < locals.length; i++) {
x += "\n (local @" + locals[i].index() + " name="
+ locals[i].nameIndex() + " type=" + locals[i].typeIndex()
+ " pc=" + locals[i].startPC() + ".."
+ (locals[i].startPC() + locals[i].length()) + ")";
}
return x + ")";
}
/**
* Write the attribute to a data stream.
*
* @param out
* The data stream of the class file.
* @exception IOException
* If an error occurs while writing.
*/
public void writeData(final DataOutputStream out) throws IOException {
out.writeShort(locals.length);
for (int i = 0; i < locals.length; i++) {
out.writeShort(locals[i].startPC());
out.writeShort(locals[i].length());
out.writeShort(locals[i].nameIndex());
out.writeShort(locals[i].typeIndex());
out.writeShort(locals[i].index());
}
}
/**
* Private constructor used in cloning.
*/
private LocalVariableTable(final LocalVariableTable other) {
super(other.nameIndex, other.length);
this.locals = new LocalDebugInfo[other.locals.length];
for (int i = 0; i < other.locals.length; i++) {
this.locals[i] = (LocalDebugInfo) other.locals[i].clone();
}
}
public Object clone() {
return (new LocalVariableTable(this));
}
}

@ -0,0 +1,33 @@
# All files in the distribution of BLOAT (Bytecode Level Optimization and
# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
# Research Foundation of Purdue University. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
CLASS = \
Attribute.class\
ClassFile.class\
ClassFileLoader.class\
Code.class\
ConstantValue.class\
Exceptions.class\
Field.class\
GenericAttribute.class\
JarFileCommitter.class\
LineNumberTable.class\
LocalVariableTable.class\
Method.class
include ../class.mk

@ -0,0 +1,484 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.file;
import java.io.*;
import EDU.purdue.cs.bloat.reflect.*;
import EDU.purdue.cs.bloat.util.*;
/**
* Method represents a method in a Java classfile. A method's name and value
* (the types of its parameters and its return type) are modeled as indices into
* it class's constant pool. A method has modifiers that determine whether it is
* public, private, static, final, etc. Methods have a number of attributes such
* as their Code and any Exceptions they may throw.
*
* @see Code
* @see Exceptions
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class Method implements MethodInfo {
private ClassInfo classInfo;
private int modifiers;
private int name;
private int type;
private Attribute[] attrs;
private Code code;
private Exceptions exceptions;
public static boolean DEBUG = Boolean.getBoolean("Method.DEBUG");
/**
* Constructor for creating a <code>Method</code> from scratch
*
* @param attrs
* Must include <code>code</code> and <code>exceptions</code>
* which are themselves attributes of this method
*/
Method(final ClassInfo classInfo, final int modifiers, final int name,
final int type, final Attribute[] attrs, final Code code,
final Exceptions exceptions) {
this.classInfo = classInfo;
this.modifiers = modifiers;
this.name = name;
this.type = type;
Assert.isNotNull(attrs, "Every method must have at least a Code "
+ "attribute");
this.attrs = attrs;
this.code = code;
this.exceptions = exceptions;
}
/**
* Constructor. Read a method from a class file.
*
* @param in
* The data stream of the class file.
* @param classInfo
* The class file containing the method.
* @exception IOException
* If an error occurs while reading.
*/
public Method(final DataInputStream in, final ClassInfo classInfo)
throws IOException {
this.classInfo = classInfo;
modifiers = in.readUnsignedShort();
name = in.readUnsignedShort();
type = in.readUnsignedShort();
final int numAttributes = in.readUnsignedShort();
attrs = new Attribute[numAttributes];
for (int i = 0; i < numAttributes; i++) {
final int nameIndex = in.readUnsignedShort();
final int length = in.readInt();
final Constant name = classInfo.constants()[nameIndex];
if (name != null) {
if ("Code".equals(name.value())) {
code = new Code(classInfo, in, nameIndex, length);
attrs[i] = code;
} else if ("Exceptions".equals(name.value())) {
exceptions = new Exceptions(classInfo, in, nameIndex,
length);
attrs[i] = exceptions;
}
}
if (attrs[i] == null) {
attrs[i] = new GenericAttribute(in, nameIndex, length);
}
}
}
/**
* Get the class which declared the method.
*
* @return The ClassInfo of the class which declared the method.
*/
public ClassInfo declaringClass() {
return classInfo;
}
/**
* Set the index into the constant pool of the name of the method.
*
* @param name
* The index into the constant pool of the name of the method.
*/
public void setNameIndex(final int name) {
this.name = name;
}
/**
* Set the index into the constant pool of the type of the method.
*
* @param type
* The index into the constant pool of the type of the method.
*/
public void setTypeIndex(final int type) {
this.type = type;
}
/**
* Get the index into the constant pool of the name of the method.
*
* @return The index into the constant pool of the name of the method.
*/
public int nameIndex() {
return name;
}
/**
* Get the index into the constant pool of the type of the method.
*
* @return The index into the constant pool of the type of the method.
*/
public int typeIndex() {
return type;
}
/**
* Set the modifiers of the method. The values correspond to the constants
* in the Modifiers class.
*
* @param modifiers
* A bit vector of modifier flags for the method.
* @see Modifiers
*/
public void setModifiers(final int modifiers) {
this.modifiers = modifiers;
}
/**
* Get the modifiers of the method. The values correspond to the constants
* in the Modifiers class.
*
* @return A bit vector of modifier flags for the method.
* @see Modifiers
*/
public int modifiers() {
return modifiers;
}
/**
* Get the maximum height of the operand stack.
*
* @return The maximum height of the operand stack.
*/
public int maxStack() {
if (code != null) {
return code.maxStack();
}
return 0;
}
/**
* Set the maximum height of the operand stack.
*
* @param maxStack
* The maximum height of the operand stack.
*/
public void setMaxStack(final int maxStack) {
if (code != null) {
code.setMaxStack(maxStack);
}
}
/**
* Get the maximum number of locals used in the method.
*
* @return The maximum number of locals used in the method.
*/
public int maxLocals() {
if (code != null) {
return code.maxLocals();
}
return 0;
}
/**
* Set the maximum number of locals used in the method.
*
* @param maxLocals
* The maximum number of locals used in the method.
*/
public void setMaxLocals(final int maxLocals) {
if (code != null) {
code.setMaxLocals(maxLocals);
}
}
/**
* Get the byte code array of the method.
*
* @return The byte code array of the method.
*/
public byte[] code() {
if (code != null) {
return code.code();
}
return new byte[0];
}
/**
* Returns the length of the Code array.
*/
public int codeLength() {
if (code != null) {
return (code.codeLength());
} else {
return (0);
}
}
/**
* Set the byte code array of the method.
*
* @param bytes
* The byte code array of the method.
*/
public void setCode(final byte[] bytes) {
if (code != null) {
code.setCode(bytes);
if (Method.DEBUG) {
System.out.println("Set code with " + bytes.length + " bytes");
// Thread.dumpStack();
}
}
}
/**
* Get the indices into the constant pool of the types of the exceptions
* thrown by the method.
*
* @return The indices into the constant pool of the types of the exceptions
* thrown by the method.
*/
public int[] exceptionTypes() {
if (exceptions != null) {
return exceptions.exceptionTypes();
}
return new int[0];
}
/**
* Set the line numbers of the instructions in the method.
*
* @param lineNumbers
* The line numbers of the instructions in the method.
*/
public void setLineNumbers(final LineNumberDebugInfo[] lineNumbers) {
if (code != null) {
code.setLineNumbers(lineNumbers);
}
}
/**
* Set the local variable debug info for the method.
*
* @param locals
* The local variable debug info for the method.
*/
public void setLocals(final LocalDebugInfo[] locals) {
if (code != null) {
code.setLocals(locals);
}
}
/**
* Get the line numbers of the instructions in the method.
*
* @return The line numbers of the instructions in the method.
*/
public LineNumberDebugInfo[] lineNumbers() {
if (code != null) {
return code.lineNumbers();
}
return new LineNumberDebugInfo[0];
}
/**
* Get the local variable debug info for the method.
*
* @return The local variable debug info for the method.
*/
public LocalDebugInfo[] locals() {
if (code != null) {
return code.locals();
}
return new LocalDebugInfo[0];
}
/**
* Get the exception handlers in the method.
*
* @return The exception handlers in the method.
*/
public Catch[] exceptionHandlers() {
if (code != null) {
return code.exceptionHandlers();
}
return new Catch[0];
}
/**
* Set the exception handlers in the method.
*
* @param handlers
* The exception handlers in the method.
*/
public void setExceptionHandlers(final Catch[] handlers) {
if (code != null) {
code.setExceptionHandlers(handlers);
}
}
/**
* Write the method to a class file.
*
* @param out
* The data stream of the class file.
* @exception IOException
* If an error occurs while writing.
*/
public void write(final DataOutputStream out) throws IOException {
if (Method.DEBUG) {
System.out.println("Writing method " + this);
System.out.println(" Masked Modifiers: " + (modifiers & 0xf000));
}
out.writeShort(modifiers);
out.writeShort(name);
out.writeShort(type);
out.writeShort(attrs.length);
for (int i = 0; i < attrs.length; i++) {
if (Method.DEBUG) {
System.out.println(" " + attrs[i]);
}
out.writeShort(attrs[i].nameIndex());
out.writeInt(attrs[i].length());
attrs[i].writeData(out);
}
}
/**
* Returns a string representation of the method.
*/
public String toString() {
String x = "";
x += " (modifiers";
if ((modifiers & Modifiers.PUBLIC) != 0) {
x += " PUBLIC";
}
if ((modifiers & Modifiers.PRIVATE) != 0) {
x += " PRIVATE";
}
if ((modifiers & Modifiers.PROTECTED) != 0) {
x += " PROTECTED";
}
if ((modifiers & Modifiers.STATIC) != 0) {
x += " STATIC";
}
if ((modifiers & Modifiers.FINAL) != 0) {
x += " FINAL";
}
if ((modifiers & Modifiers.SYNCHRONIZED) != 0) {
x += " SYNCHRONIZED";
}
if ((modifiers & Modifiers.VOLATILE) != 0) {
x += " VOLATILE";
}
if ((modifiers & Modifiers.TRANSIENT) != 0) {
x += " TRANSIENT";
}
if ((modifiers & Modifiers.NATIVE) != 0) {
x += " NATIVE";
}
if ((modifiers & Modifiers.INTERFACE) != 0) {
x += " INTERFACE";
}
if ((modifiers & Modifiers.ABSTRACT) != 0) {
x += " ABSTRACT";
}
x += ")";
return "(method " + name + " " + type + x
+ (code != null ? "\n " + code : "")
+ (exceptions != null ? "\n " + exceptions : "") + ")";
}
/**
* Constructor used for cloning a <code>Method</code>
*/
private Method(final ClassInfo classInfo, final int modifiers,
final int name, final int type, final Attribute[] attrs) {
this.classInfo = classInfo;
this.modifiers = modifiers;
this.name = name;
this.type = type;
if (attrs != null) {
this.attrs = new Attribute[attrs.length];
for (int i = 0; i < attrs.length; i++) {
final Attribute attr = (Attribute) attrs[i].clone();
if (attr instanceof Code) {
this.code = (Code) attr;
} else if (attr instanceof Exceptions) {
this.exceptions = (Exceptions) attr;
}
this.attrs[i] = attr;
}
}
Assert.isTrue(code != null, "No Code in attributes");
Assert.isTrue(exceptions != null, "No Exceptions in attributes");
}
/**
* Creates a clone of this <tt>MethodInfo</tt> except that its declaring
* class does not know about it.
*/
public Object clone() {
return (new Method(this.classInfo, this.modifiers, this.name,
this.type, this.attrs));
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save