initial upload Bloat 1.0

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

@ -0,0 +1 @@
bin

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

@ -0,0 +1,7 @@
#Tue Oct 18 04:56:05 CEST 2005
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.1
org.eclipse.jdt.core.compiler.compliance=1.3
org.eclipse.jdt.core.compiler.problem.assertIdentifier=ignore
org.eclipse.jdt.core.compiler.problem.enumIdentifier=ignore
org.eclipse.jdt.core.compiler.source=1.3

@ -0,0 +1,7 @@
- FlowGraph#addHandlerEdges: added visited parameter to keep track of
blocks already visited to break 'infinite loop' in compiler-generated
'self-handling' exception handlers since JDK 1.4
- Type#getType(Class): small fix for array type descriptors
- Tree#visit_ldc: broken hack for JDK5 class constants - check usages and fix

@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

@ -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,14 @@
<html>
<body>
<p>Classes to represent a Java method as a control flow graph of basic
blocks. A <em>basic block</em> is a sequence of code that is entered
only in once place (e.g. the target of a branch statement) and is
exited at only one place (e.g. a branch statement). Each basic block
consists of an expression tree. There are also classes to represent
try-catch blocks, Java subroutines (<tt>finally</tt> blocks), certain
properties of the control flow graph such as its dominator tree, and
to visualize a control flow graph.</p>
</body>
</html>

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

File diff suppressed because it is too large Load Diff

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

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

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

@ -0,0 +1,9 @@
<html>
<body>
<p>Generates Java bytecodes from the contents of a control flow graph.
It also performs "register" allocation of the local variables inside
the virtual machine.</p>
</body>
</html>

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

@ -0,0 +1,147 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.context;
import java.io.*;
import java.net.*;
import java.util.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.file.*;
import EDU.purdue.cs.bloat.reflect.*;
import EDU.purdue.cs.bloat.util.*;
/**
* <code>BloatingClassLoader</code> is a Java class loader that BLOATs a class
* before it is loader into a Java Virtual Machine. It loads its classes from a
* set of {@link URL}s.
*/
public abstract class BloatingClassLoader extends URLClassLoader {
/**
* A ClassInfoLoader that loads classes from the same locations as this
* class loader.
*/
ClassInfoLoader loader = new BloatingClassInfoLoader();
/** The context that is used to edit classes, etc. */
private final EditorContext context = new PersistentBloatContext(loader,
false);
/**
* Maps ClassInfos to their committed bytes (as a ByteArrayOutputStream)
*/
private final Map classBytes = new HashMap();
// //////////////////// Constructors /////////////////////////
/**
* Creates a new <code>BloatingClassLoader</code> that loads its classes
* from a given set of URLs.
*/
public BloatingClassLoader(final URL[] urls) {
super(urls);
}
/**
* Creates a new <code>BloatingClassLoader</code> that loads its classes
* from a given set of URLs. Before attempting to load a class, this
* <code>BloatingClassLoader</code> will delegate to its parent class
* loader.
*/
public BloatingClassLoader(final URL[] urls, final ClassLoader parent) {
super(urls);
}
/**
* Before the <code>Class</code> is created, invoke {@link
* #bloat(ClassEditor)}.
*/
protected Class findClass(final String name) throws ClassNotFoundException {
final ClassInfo info = this.loader.loadClass(name);
final ClassEditor ce = this.context.editClass(info);
this.bloat(ce);
ce.commit();
final ByteArrayOutputStream baos = (ByteArrayOutputStream) this.classBytes
.get(info);
Assert.isNotNull(baos, "No bytes for " + name);
final byte[] bytes = baos.toByteArray();
return super.defineClass(name, bytes, 0, bytes.length);
}
/**
* Returns a <code>ClassInfoLoader</code> that loads classes from the same
* place as this <code>ClassLoader</code>.
*/
public ClassInfoLoader getClassInfoLoader() {
return this.loader;
}
/**
* This method is invoked as a class is being loaded.
*/
protected abstract void bloat(ClassEditor ce);
/**
* This inner class is a ClassInfoLoader that loads classes from the same
* locations as the outer BloatClassLoader. The primary reason that we have
* this class is because the loadClass method of ClassInfoLoader has a
* different signature from ClassLoader. Hence, a ClassLoader cannot be a
* ClassInfoLoader.
*/
class BloatingClassInfoLoader implements ClassInfoLoader {
public ClassInfo loadClass(final String name)
throws ClassNotFoundException {
final String classFileName = name.replace('.', '/') + ".class";
final InputStream is = BloatingClassLoader.this
.getResourceAsStream(classFileName);
if (is == null) {
throw new ClassNotFoundException("Could not find class " + name);
}
final DataInputStream dis = new DataInputStream(is);
return new ClassFile(null, this, dis);
}
public ClassInfo newClass(final int modifiers, final int classIndex,
final int superClassIndex, final int[] interfaceIndexes,
final java.util.List constants) {
return new ClassFile(modifiers, classIndex, superClassIndex,
interfaceIndexes, constants, this);
}
public OutputStream outputStreamFor(final ClassInfo info)
throws IOException {
// Maintain a mapping between ClassInfos and their committed bytes
final OutputStream os = new ByteArrayOutputStream();
classBytes.put(info, os);
return (os);
}
}
}

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

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

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

@ -0,0 +1,17 @@
<html>
<body>
<p>Repositories for important BLOAT objects such as the
<tt>Editor</tt>, <tt>ClassHierarchy</tt>, and <tt>FlowGraph</tt>s. In
order to avoid build dependencies with other packages we took the
novel approach of having a "context" interface in several packages that
the classes in this package then implement.</p>
<p>The reason we implement all of the methods in one class is so that
if the user wants to have different contexts with different attributes
(for instance, caching policies), all one has to do is subclass
<tt>BloatContext</tt> instead of subclassing other contextes which may
become ackward.</p>
</body>
</html>

@ -0,0 +1,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,8 @@
<html>
<body>
<p>Performs demand-driven induction variable analysis on a Java
classfile.</p>
</body>
</html>

@ -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,551 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
import java.util.*;
import EDU.purdue.cs.bloat.reflect.*;
import EDU.purdue.cs.bloat.util.*;
/**
* A ClassEditor provides finer-grain access to a class than a CLassInfo object
* does. A ClassEditor takes a ClassInfo and extracts the class's constant pool,
* type, super class type, and the types of its interfaces. When editing is
* finished, changes are committed with the commit method.
*
* @see ClassInfo
* @see MethodEditor
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class ClassEditor {
public static boolean DEBUG = Boolean.getBoolean("ClassEditor.DEBUG");
private ConstantPool constants; // A copy of the constant pool of the class
// being edited.
private ClassInfo classInfo; // (A representation of) the class being
// edited
private Type type; // An index into constant pool (descriptors) that
private Type superclass; // specifies the class, superclass, and
// interfaces
private Type[] interfaces; // of the class being edited.
private EditorContext context; // Use to edit classes and methods
private boolean dirty; // Has the class been modified?
/**
* Constructor. Create a new ClassEditor based on information in a ClassInfo
* object. It extracts the class's constant pool, and the types of the
* class, its superclass, and any interfaces it implements.
*
* @param context
* The <tt>EditorContext</tt> used to edit fields and methods.
* @param classInfo
* The ClassInfo structure of the class to edit.
*
* @see EDU.purdue.cs.bloat.reflect.ClassInfo
* @see ConstantPool
* @see Type
*/
public ClassEditor(final EditorContext context, final ClassInfo classInfo) {
this.context = context;
this.classInfo = classInfo;
this.dirty = false;
// Extract the constant pool from the ClassInfo
constants = new ConstantPool(classInfo.constants());
int index;
// Load information (such as the indices of the class, superclass,
// and the interfaces) about the class being edited from its
// constant pool.
index = classInfo.classIndex();
type = (Type) constants.constantAt(index);
index = classInfo.superclassIndex();
superclass = (Type) constants.constantAt(index);
final int ifs[] = classInfo.interfaceIndices();
interfaces = new Type[ifs.length];
for (int i = 0; i < ifs.length; i++) {
interfaces[i] = (Type) constants.constantAt(ifs[i]);
}
if (ClassEditor.DEBUG) {
System.out.println("Editing class " + type);
}
this.setDirty(false);
}
/**
* Creates a new <code>ClassEditor</code> for editing a class (or
* interface) from scratch. This constructor should not be invoked direcly.
* Use {@link EditorContext#newClass(int, String, Type, Type[])} instead.
*/
public ClassEditor(final EditorContext context, final int modifiers,
final String className, Type superType, Type[] interfaces) {
if (className == null) {
final String s = "Cannot have a null class name";
throw new IllegalArgumentException(s);
}
if (superType == null) {
superType = Type.OBJECT;
}
if (interfaces == null) {
interfaces = new Type[0];
}
if (ClassEditor.DEBUG) {
System.out.println("Creating new class " + className + " extends "
+ superType.className());
}
this.context = context;
this.superclass = superType;
this.interfaces = interfaces;
final ConstantPool cp = new ConstantPool();
this.constants = cp;
this.type = Type.getType(Type.classDescriptor(className));
final int classNameIndex = cp.getClassIndex(this.type);
final int superTypeIndex = cp.getClassIndex(superType);
final int[] interfaceIndexes = new int[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
interfaceIndexes[i] = cp.getClassIndex(interfaces[i]);
}
this.classInfo = context.newClassInfo(modifiers, classNameIndex,
superTypeIndex, interfaceIndexes, cp.getConstantsList());
this.dirty = true;
}
/**
* Returns <tt>true</tt> if the class has been modified.
*/
public boolean isDirty() {
return (this.dirty);
}
/**
* Sets this class's dirty flag. The dirty flag is <tt>true</tt> if the
* class has been modified.
*/
public void setDirty(final boolean dirty) {
this.dirty = dirty;
}
/**
* Returns the name of the class represented by this <tt>ClassEditor</tt>.
*/
public String name() {
return (this.classInfo().name());
}
/**
* Obtain the <tt>EditorContext</tt> for this ClassEditor.
*/
public EditorContext context() {
return context;
}
/**
* Get the ClassInfo object representing the class that being edited.
*/
public ClassInfo classInfo() {
return classInfo;
}
public boolean isPublic() {
return (classInfo.modifiers() & Modifiers.PUBLIC) != 0;
}
public boolean isPrivate() {
return (classInfo.modifiers() & Modifiers.PRIVATE) != 0;
}
public boolean isProtected() {
return (classInfo.modifiers() & Modifiers.PROTECTED) != 0;
}
public boolean isStatic() {
return (classInfo.modifiers() & Modifiers.STATIC) != 0;
}
public boolean isFinal() {
return (classInfo.modifiers() & Modifiers.FINAL) != 0;
}
public boolean isSuper() {
return (classInfo.modifiers() & Modifiers.SUPER) != 0;
}
public boolean isAbstract() {
return (classInfo.modifiers() & Modifiers.ABSTRACT) != 0;
}
public boolean isInterface() {
return (classInfo.modifiers() & Modifiers.INTERFACE) != 0;
}
public void setPublic(final boolean flag) {
int modifiers = classInfo.modifiers();
if (flag) {
modifiers |= Modifiers.PUBLIC;
} else {
modifiers &= ~Modifiers.PUBLIC;
}
classInfo.setModifiers(modifiers);
this.setDirty(true);
}
public void setPrivate(final boolean flag) {
int modifiers = classInfo.modifiers();
if (flag) {
modifiers |= Modifiers.PRIVATE;
} else {
modifiers &= ~Modifiers.PRIVATE;
}
classInfo.setModifiers(modifiers);
this.setDirty(true);
}
public void setProtected(final boolean flag) {
int modifiers = classInfo.modifiers();
if (flag) {
modifiers |= Modifiers.PROTECTED;
} else {
modifiers &= ~Modifiers.PROTECTED;
}
classInfo.setModifiers(modifiers);
this.setDirty(true);
}
public void setStatic(final boolean flag) {
int modifiers = classInfo.modifiers();
if (flag) {
modifiers |= Modifiers.STATIC;
} else {
modifiers &= ~Modifiers.STATIC;
}
classInfo.setModifiers(modifiers);
this.setDirty(true);
}
public void setFinal(final boolean flag) {
int modifiers = classInfo.modifiers();
if (flag) {
modifiers |= Modifiers.FINAL;
} else {
modifiers &= ~Modifiers.FINAL;
}
classInfo.setModifiers(modifiers);
this.setDirty(true);
}
public void setSuper(final boolean flag) {
int modifiers = classInfo.modifiers();
if (flag) {
modifiers |= Modifiers.SUPER;
} else {
modifiers &= ~Modifiers.SUPER;
}
classInfo.setModifiers(modifiers);
this.setDirty(true);
}
public void setAbstract(final boolean flag) {
int modifiers = classInfo.modifiers();
if (flag) {
modifiers |= Modifiers.ABSTRACT;
} else {
modifiers &= ~Modifiers.ABSTRACT;
}
classInfo.setModifiers(modifiers);
this.setDirty(true);
}
public void setInterface(final boolean flag) {
int modifiers = classInfo.modifiers();
if (flag) {
modifiers |= Modifiers.INTERFACE;
} else {
modifiers &= ~Modifiers.INTERFACE;
}
classInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* Sets the Type (descriptor) object for the class.
*
* @param type
* A Type.
*/
public void setType(final Type type) {
this.type = type;
Assert.isTrue(type.isObject(), "Cannot set class type to " + type);
this.setDirty(true);
}
/**
* Returns the Type (descriptor) for the class.
*/
public Type type() {
return type;
}
/**
* Returns a Type object for the class's superclass.
*/
public Type superclass() {
return superclass;
}
/**
* Adds an interface of the given class to the set of interfaces that the
* class implements.
*
* @throws IllegalArgumentException <code>interfaceClass</code> is not an
* interface
*/
public void addInterface(final Class interfaceClass) {
if (!interfaceClass.isInterface()) {
final String s = "Cannot add non-interface type: "
+ interfaceClass.getName();
throw new IllegalArgumentException(s);
}
addInterface(Type.getType(interfaceClass));
}
/**
* Adds an interface of a given Type to the set of interfaces that the class
* implements.
*/
public void addInterface(final Type interfaceType) {
// // The interface must have an index in the constant pool
// this.constants().getClassIndex(interfaceType);
final Type[] interfaces = new Type[this.interfaces.length + 1];
for (int i = 0; i < this.interfaces.length; i++) {
interfaces[i] = this.interfaces[i];
}
interfaces[interfaces.length - 1] = interfaceType;
this.setInterfaces(interfaces);
}
/**
* Returns the interfaces the class implements.
*
* @param interfaces
* An array of Types.
*/
public void setInterfaces(final Type[] interfaces) {
this.interfaces = interfaces;
this.setDirty(true);
}
/**
* Returns the interfaces the class implements.
*/
public Type[] interfaces() {
return interfaces;
}
/**
* Returns the modifiers of the class. The values correspond to the
* constants in the <tt>Modifiers</tt> class.
*
* @return A bit vector of modifier flags for the class.
* @see Modifiers
*/
public int modifiers() {
return classInfo.modifiers();
}
/**
* Returns an array of <tt>FieldInfo</tt> structures for each field in the
* class.
*/
public FieldInfo[] fields() {
return classInfo.fields();
}
/**
* Returns an array of MethodInfo structures for each method in the class.
*/
public MethodInfo[] methods() {
return classInfo.methods();
}
/**
* Returns the constant pool for the class.
*/
public ConstantPool constants() {
return constants;
}
/**
* Commit any changes to the class since creation time. Note that committal
* will occur regardless of whether or not the class is dirty.
*/
public void commit() {
commitOnly(null, null);
}
/**
* Commits only certain methods and fields. Note that committal will occur
* regardless of whether or not the class is dirty.
*
* @param methods
* Methods (<tt>MethodInfo</tt>s) to commit. If <tt>null</tt>,
* all methods are committed.
* @param fields
* Fields (<tt>FieldInfo</tt>s) to commit. If <tt>null</tt>,
* all fields are committed.
*/
public void commitOnly(final Set methods, final Set fields) {
classInfo.setClassIndex(constants.addConstant(Constant.CLASS, type));
classInfo.setSuperclassIndex(constants.addConstant(Constant.CLASS,
superclass));
final int ifs[] = new int[interfaces.length];
for (int i = 0; i < ifs.length; i++) {
ifs[i] = constants.addConstant(Constant.CLASS, interfaces[i]);
}
classInfo.setInterfaceIndices(ifs);
classInfo.setConstants(constants.constants());
classInfo.commitOnly(methods, fields);
// This class is no longer dirty
this.setDirty(false);
}
/**
* This class is visited by an <tt>EditorVisitor</tt>. First, this
* <tt>ClassEditor</tt> itself is visited. Then, all of this class's
* fields (<tt>FieldEditor</tt>s) are visited. Finally, each of this
* class's methods (<tt>MethodEditor</tt>s) are visited. Constructors
* are visited before regular methods.
*/
public void visit(final EditorVisitor visitor) {
// First visit ourself
visitor.visitClassEditor(this);
final EditorContext context = this.context();
// Visit each field
final FieldInfo[] fields = this.fields();
for (int i = 0; i < fields.length; i++) {
final FieldEditor fieldEditor = context.editField(fields[i]);
visitor.visitFieldEditor(fieldEditor);
context.release(fields[i]);
}
// Visit each method
final ArrayList regularMethods = new ArrayList();
final MethodInfo[] methods = this.methods();
for (int i = 0; i < methods.length; i++) {
final MethodEditor methodEditor = context.editMethod(methods[i]);
if (methodEditor.name().charAt(0) != '<') {
regularMethods.add(methods[i]);
} else {
visitor.visitMethodEditor(methodEditor);
}
context.release(methods[i]);
}
final Iterator iter = regularMethods.iterator();
while (iter.hasNext()) {
final MethodInfo info = (MethodInfo) iter.next();
final MethodEditor me = context.editMethod(info);
visitor.visitMethodEditor(me);
context.release(info);
}
}
/**
* Two <tt>ClassEditor</tt>s are equal if they edit the same class.
*/
public boolean equals(final Object o) {
if (o instanceof ClassEditor) {
final ClassEditor other = (ClassEditor) o;
if (!other.type().equals(this.type())) {
return (false);
}
return (true);
}
return (false);
}
/**
* A <tt>ClassEditor</tt>'s hash code is based upon the hash code of the
* name of the class it edits.
*/
public int hashCode() {
return (this.name().hashCode());
}
public String toString() {
return (this.type().toString());
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

File diff suppressed because it is too large Load Diff

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

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

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

File diff suppressed because it is too large Load Diff

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

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

@ -0,0 +1,10 @@
<html>
<body>
<p>Allows classes, methods, and fields to be edited. In addition, the
representation of bytecodes (JVM instructions) is refined. Types,
opcodes, local variables, and special instrcution operands are
introduced.</p>
</body>
</html>

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

@ -0,0 +1,976 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.file;
import java.io.*;
import java.util.*;
import EDU.purdue.cs.bloat.reflect.*;
import EDU.purdue.cs.bloat.util.*;
/**
* ClassFile basically represents a Java classfile as it is found on disk. The
* classfile is modeled according to the Java Virtual Machine Specification.
* Methods are provided to edit the classfile at a very low level.
*
* @see Attribute
* @see EDU.purdue.cs.bloat.reflect.Constant
* @see Field
* @see Method
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class ClassFile implements ClassInfo {
private ClassInfoLoader loader; // ClassInfoLoader that "owns" this class
private List constants; // The constant pool
private int modifiers; // This class's modifer bit field
private int thisClass;
private int superClass;
private int[] interfaces;
private Field[] fields;
private Method[] methods;
private Attribute[] attrs;
private File file; // (.class) File in which this class resides
/**
* Constructor. This constructor parses the class file from the input
* stream.
*
* @param file
* The file in which the class resides.
* @param loader
* The class info loader which loaded the class.
* @param in
* The data stream containing the class.
* @exception ClassFormatError
* When the class could not be parsed.
*/
public ClassFile(final File file, final ClassInfoLoader loader,
final DataInputStream in) {
this.loader = loader;
this.file = file;
// Assert.isTrue(file != null, "Null file for class file");
// Read in file contents from stream
try {
if (ClassFileLoader.DEBUG) {
System.out.println("ClassFile: Reading header");
}
readHeader(in);
if (ClassFileLoader.DEBUG) {
System.out.println("ClassFile: Reading constant pool");
}
readConstantPool(in);
if (ClassFileLoader.DEBUG) {
System.out.println("ClassFile: Reading access flags");
}
readAccessFlags(in);
if (ClassFileLoader.DEBUG) {
System.out.println("ClassFile: Reading class info");
}
readClassInfo(in);
if (ClassFileLoader.DEBUG) {
System.out.println("ClassFile: Reading fields");
}
readFields(in);
if (ClassFileLoader.DEBUG) {
System.out.println("ClassFile: Reading methods");
}
readMethods(in);
if (ClassFileLoader.DEBUG) {
System.out.println("ClassFile: Reading Attributes");
}
readAttributes(in);
in.close();
} catch (final IOException e) {
throw new ClassFormatException(e.getMessage() + " (in file " + file
+ ")");
}
}
/**
* Creates a new <code>ClassFile</code> from scratch. It has no fields or
* methods.
*
* @param modifiers
* The modifiers describing the newly-created class
* @param classIndex
* The index of the type of the newly-created class in its
* constant pool
* @param superClassIndex
* The index of the type of the newly-created class's superclass
* in its constant pool
* @param interfaceIndexes
* The indexes of the types of the interfaces that the
* newly-created class implements
* @param constants
* The constant pool for the newly created class (a list of
* {@link Constant}s).
*/
public ClassFile(final int modifiers, final int classIndex,
final int superClassIndex, final int[] interfaceIndexes,
final List constants, final ClassInfoLoader loader) {
this.modifiers = modifiers;
this.thisClass = classIndex;
this.superClass = superClassIndex;
this.interfaces = interfaceIndexes;
this.constants = constants;
this.loader = loader;
this.fields = new Field[0];
this.methods = new Method[0];
this.attrs = new Attribute[0];
}
/**
* Get the class info loader for the class.
*
* @return The class info loader for the class.
*/
public ClassInfoLoader loader() {
return loader;
}
/**
* Get the name of the class, including the package name.
*
* @return The name of the class.
*/
public String name() {
Constant c = (Constant) constants.get(thisClass);
Assert.isNotNull(c, "Null constant for class name");
if (c.tag() == Constant.CLASS) {
final Integer nameIndex = (Integer) c.value();
if (nameIndex != null) {
c = (Constant) constants.get(nameIndex.intValue());
if (c.tag() == Constant.UTF8) {
return (String) c.value();
}
}
}
throw new ClassFormatException("Couldn't find class name in file");
}
/**
* Set the index into the constant pool of the name of the class.
*
* @param index
* The index of the name of the class.
*/
public void setClassIndex(final int index) {
this.thisClass = index;
}
/**
* Set the index into the constant pool of the name of the class's
* superclass.
*
* @param index
* The index of the name of the superclass.
*/
public void setSuperclassIndex(final int index) {
this.superClass = index;
}
/**
* Set the indices into the constant pool of the names of the class's
* interfaces.
*
* @param indices
* The indices of the names of the interfaces.
*/
public void setInterfaceIndices(final int[] indices) {
this.interfaces = indices;
}
/**
* Get the index into the constant pool of the name of the class.
*
* @return The index of the name of the class.
*/
public int classIndex() {
return thisClass;
}
/**
* Get the index into the constant pool of the name of the class's
* superclass.
*
* @return The index of the name of the superclass.
*/
public int superclassIndex() {
return superClass;
}
/**
* Get the indices into the constant pool of the names of the class's
* interfaces.
*
* @return The indices of the names of the interfaces.
*/
public int[] interfaceIndices() {
return interfaces;
}
/**
* Set the modifiers of the class. The values correspond to the constants in
* the Modifiers class.
*
* @param modifiers
* A bit vector of modifier flags for the class.
* @see Modifiers
*/
public void setModifiers(final int modifiers) {
this.modifiers = modifiers;
}
/**
* Get the modifiers of the class. The values correspond to the constants in
* the Modifiers class.
*
* @return A bit vector of modifier flags for the class.
* @see Modifiers
*/
public int modifiers() {
return modifiers;
}
/**
* Get an array of FieldInfo structures for each field in the class.
*
* @return An array of FieldInfo structures.
*/
public FieldInfo[] fields() {
return fields;
}
/**
* Returns an array of MethodInfo structures for each method in the class.
*/
public MethodInfo[] methods() {
return methods;
}
/**
* Sets the methods in this class.
*/
public void setMethods(final MethodInfo[] methods) {
this.methods = new Method[methods.length];
for (int i = 0; i < methods.length; i++) {
this.methods[i] = (Method) methods[i];
}
}
/**
* Get an array of the constants in the constant pool.
*
* @return An array of Constants.
*/
public Constant[] constants() {
return (Constant[]) constants.toArray(new Constant[0]);
}
/**
* Set all the constants in the constant pool.
*
* @param constants
* The array of Constants.
*/
public void setConstants(final Constant[] constants) {
this.constants = new ArrayList(constants.length);
for (int i = 0; i < constants.length; i++) {
this.constants.add(i, constants[i]);
}
}
/**
* Returns the File from which this <code>ClassFile</code> was created. If
* this <code>ClassFile</code> was created from scratch, <code>null</code>
* is returned.
*/
public File file() {
return file;
}
/**
* Creates a new File object to hold this class. It is placed in the output
* directory and has the name of the class represented by this ClassFile
* followed by the .class extension.
*/
public File outputFile() {
final File outputDir = ((ClassFileLoader) loader).outputDir();
final String fileName = this.name().replace('/', File.separatorChar);
return new File(outputDir, fileName + ".class");
}
/**
* Commit any changes back to a file in the output directory. The output
* directory is determined from the ClassFileLoader.
*/
public void commit() {
try {
commitTo(loader.outputStreamFor(this));
} catch (final IOException e) {
e.printStackTrace();
System.exit(1);
}
}
/**
* Commit changes made to this class. Write changes to an OutputStream.
*/
void commitTo(final OutputStream outStream) {
try {
final DataOutputStream out = new DataOutputStream(outStream);
writeHeader(out);
writeConstantPool(out);
writeAccessFlags(out);
writeClassInfo(out);
writeFields(out, null);
writeMethods(out, null);
writeAttributes(out);
out.close();
} catch (final IOException e) {
e.printStackTrace();
System.exit(1);
}
}
public void commitOnly(final Set methods, final Set fields) {
try {
final OutputStream outStream = loader.outputStreamFor(this);
final DataOutputStream out = new DataOutputStream(outStream);
writeHeader(out);
writeConstantPool(out);
writeAccessFlags(out);
writeClassInfo(out);
writeFields(out, fields);
writeMethods(out, methods);
writeAttributes(out);
out.close();
} catch (final IOException e) {
e.printStackTrace();
System.exit(1);
}
}
/**
* Write the class file header.
*
* @param out
* The stream to which to write.
* @exception IOException
* If an error occurs while writing.
*/
private void writeHeader(final DataOutputStream out) throws IOException {
out.writeInt(0xCAFEBABE);
out.writeShort(3);
out.writeShort(45);
}
/**
* Write the class's constant pool.
*
* @param out
* The stream to which to write.
* @exception IOException
* If an error occurs while writing.
*/
private void writeConstantPool(final DataOutputStream out)
throws IOException {
out.writeShort(constants.size());
// Write the constants. The first constant is reserved for
// internal use by the JVM, so start at 1.
for (int i = 1; i < constants.size(); i++) {
writeConstant(out, (Constant) constants.get(i));
switch (((Constant) constants.get(i)).tag()) {
case Constant.LONG:
case Constant.DOUBLE:
// Longs and doubles take up 2 constant pool entries.
i++;
break;
}
}
}
/**
* Read a constant from the constant pool.
*
* @param in
* The stream from which to read.
* @return The constant.
* @exception IOException
* If an error occurs while reading.
*/
private Constant readConstant(final DataInputStream in) throws IOException {
final int tag = in.readUnsignedByte();
Object value;
switch (tag) {
case Constant.CLASS:
case Constant.STRING:
value = new Integer(in.readUnsignedShort());
break;
case Constant.FIELD_REF:
case Constant.METHOD_REF:
case Constant.INTERFACE_METHOD_REF:
case Constant.NAME_AND_TYPE:
value = new int[2];
((int[]) value)[0] = in.readUnsignedShort();
((int[]) value)[1] = in.readUnsignedShort();
break;
case Constant.INTEGER:
value = new Integer(in.readInt());
break;
case Constant.FLOAT:
value = new Float(in.readFloat());
break;
case Constant.LONG:
// Longs take up 2 constant pool entries.
value = new Long(in.readLong());
break;
case Constant.DOUBLE:
// Doubles take up 2 constant pool entries.
value = new Double(in.readDouble());
break;
case Constant.UTF8:
value = in.readUTF();
break;
default:
throw new ClassFormatException(file.getPath()
+ ": Invalid constant tag: " + tag);
}
return new Constant(tag, value);
}
/**
* Write a constant in the constant pool.
*
* @param out
* The stream to which to write.
* @param constant
* The constant.
* @exception IOException
* If an error occurs while writing.
*/
private void writeConstant(final DataOutputStream out,
final Constant constant) throws IOException {
final int tag = constant.tag();
final Object value = constant.value();
out.writeByte(tag);
switch (tag) {
case Constant.CLASS:
case Constant.STRING:
out.writeShort(((Integer) value).intValue());
break;
case Constant.INTEGER:
out.writeInt(((Integer) value).intValue());
break;
case Constant.FLOAT:
out.writeFloat(((Float) value).floatValue());
break;
case Constant.LONG:
out.writeLong(((Long) value).longValue());
break;
case Constant.DOUBLE:
out.writeDouble(((Double) value).doubleValue());
break;
case Constant.UTF8:
out.writeUTF((String) value);
break;
case Constant.FIELD_REF:
case Constant.METHOD_REF:
case Constant.INTERFACE_METHOD_REF:
case Constant.NAME_AND_TYPE:
out.writeShort(((int[]) value)[0]);
out.writeShort(((int[]) value)[1]);
break;
}
}
/**
* Write the class's access flags.
*
* @param out
* The stream to which to write.
* @exception IOException
* If an error occurs while writing.
*/
private void writeAccessFlags(final DataOutputStream out)
throws IOException {
out.writeShort(modifiers);
}
/**
* Write the class's name, superclass, and interfaces.
*
* @param out
* The stream to which to write.
* @exception IOException
* If an error occurs while writing.
*/
private void writeClassInfo(final DataOutputStream out) throws IOException {
out.writeShort(thisClass);
out.writeShort(superClass);
out.writeShort(interfaces.length);
for (int i = 0; i < interfaces.length; i++) {
out.writeShort(interfaces[i]);
}
}
/**
* Write the class's fields.
*
* @param out
* The stream to which to write.
* @exception IOException
* If an error occurs while writing.
*/
private void writeFields(final DataOutputStream out, final Set onlyFields)
throws IOException {
out.writeShort(fields.length);
for (int i = 0; i < fields.length; i++) {
if ((onlyFields != null) && onlyFields.contains(fields[i])) {
continue;
}
fields[i].write(out);
}
}
/**
* Write the class's methods.
*
* @param out
* The stream to which to write.
* @exception IOException
* If an error occurs while writing.
*/
private void writeMethods(final DataOutputStream out, final Set onlyMethods)
throws IOException {
if (onlyMethods != null) {
out.writeShort(onlyMethods.size());
} else {
if (Method.DEBUG) {
System.out.println("Writing " + methods.length + " methods");
}
out.writeShort(methods.length);
}
for (int i = 0; i < methods.length; i++) {
if ((onlyMethods != null) && onlyMethods.contains(methods[i])) {
continue;
}
methods[i].write(out);
}
}
/**
* Write the class's attributes. No attributes are written by this method
* since none are required.
*
* @param out
* The stream to which to write.
* @exception IOException
* If an error occurs while writing.
*/
private void writeAttributes(final DataOutputStream out) throws IOException {
out.writeShort(attrs.length);
for (int i = 0; i < attrs.length; i++) {
out.writeShort(attrs[i].nameIndex());
out.writeInt(attrs[i].length());
attrs[i].writeData(out);
}
}
/**
* Read the class file header.
*
* @param in
* The stream from which to read.
* @exception IOException
* If an error occurs while reading.
*/
private void readHeader(final DataInputStream in) throws IOException {
final int magic = in.readInt();
if (magic != 0xCAFEBABE) {
throw new ClassFormatError("Bad magic number.");
}
in.readUnsignedShort(); // major
in.readUnsignedShort(); // minor
}
/**
* Read the class's constant pool. Constants in the constant pool are
* modeled by an array of <tt>reflect.Constant</tt>/
*
* @param in
* The stream from which to read.
* @exception IOException
* If an error occurs while reading.
*
* @see EDU.purdue.cs.bloat.reflect.Constant
* @see #constants
*/
private void readConstantPool(final DataInputStream in) throws IOException {
final int count = in.readUnsignedShort();
constants = new ArrayList(count);
// The first constant is reserved for internal use by the JVM.
constants.add(0, null);
// Read the constants.
for (int i = 1; i < count; i++) {
constants.add(i, readConstant(in));
switch (((Constant) constants.get(i)).tag()) {
case Constant.LONG:
case Constant.DOUBLE:
// Longs and doubles take up 2 constant pool entries.
constants.add(++i, null);
break;
}
}
}
/**
* Read the class's access flags.
*
* @param in
* The stream from which to read.
* @exception IOException
* If an error occurs while reading.
*/
private void readAccessFlags(final DataInputStream in) throws IOException {
modifiers = in.readUnsignedShort();
}
/**
* Read the class's name, superclass, and interfaces.
*
* @param in
* The stream from which to read.
* @exception IOException
* If an error occurs while reading.
*/
private void readClassInfo(final DataInputStream in) throws IOException {
thisClass = in.readUnsignedShort();
superClass = in.readUnsignedShort();
final int numInterfaces = in.readUnsignedShort();
interfaces = new int[numInterfaces];
for (int i = 0; i < numInterfaces; i++) {
interfaces[i] = in.readUnsignedShort();
}
}
/**
* Read the class's fields.
*
* @param in
* The stream from which to read.
* @exception IOException
* If an error occurs while reading.
*/
private void readFields(final DataInputStream in) throws IOException {
final int numFields = in.readUnsignedShort();
fields = new Field[numFields];
for (int i = 0; i < numFields; i++) {
fields[i] = new Field(in, this);
}
}
/**
* Read the class's methods.
*
* @param in
* The stream from which to read.
* @exception IOException
* If an error occurs while reading.
*/
private void readMethods(final DataInputStream in) throws IOException {
final int numMethods = in.readUnsignedShort();
methods = new Method[numMethods];
for (int i = 0; i < numMethods; i++) {
methods[i] = new Method(in, this);
}
}
/**
* Read the class's attributes. Since none of the attributes are required,
* just read the length of each attribute and skip that many bytes.
*
* @param in
* The stream from which to read.
* @exception IOException
* If an error occurs while reading.
*/
private void readAttributes(final DataInputStream in) throws IOException {
final int numAttributes = in.readUnsignedShort();
attrs = new Attribute[numAttributes];
for (int i = 0; i < numAttributes; i++) {
final int nameIndex = in.readUnsignedShort();
final int length = in.readInt();
attrs[i] = new GenericAttribute(in, nameIndex, length);
}
}
/**
* Creates a new field in this classfile
*/
public FieldInfo addNewField(final int modifiers, final int typeIndex,
final int nameIndex) {
final Field field = new Field(this, modifiers, typeIndex, nameIndex);
// Add the new field to the list of fields
final Field[] fields = new Field[this.fields.length + 1];
for (int i = 0; i < this.fields.length; i++) {
fields[i] = this.fields[i];
}
fields[this.fields.length] = field;
this.fields = fields;
return (field);
}
/**
* Creates a new field in this classfile
*/
public FieldInfo addNewField(final int modifiers, final int typeIndex,
final int nameIndex, final int cvNameIndex,
final int constantValueIndex) {
final Field field = new Field(this, modifiers, typeIndex, nameIndex,
cvNameIndex, constantValueIndex);
// Add the new field to the list of fields
final Field[] fields = new Field[this.fields.length + 1];
for (int i = 0; i < this.fields.length; i++) {
fields[i] = this.fields[i];
}
fields[this.fields.length] = field;
this.fields = fields;
return (field);
}
/**
* Removes the field whose name is at the given index in the constant pool.
*
* @throws IllegalArgumentException The class does not contain a field whose
* name is at the given index
*/
public void deleteField(final int nameIndex) {
final List newFields = new ArrayList();
boolean foundIt = false;
for (int i = 0; i < this.fields.length; i++) {
final Field field = this.fields[i];
if (field.nameIndex() == nameIndex) {
foundIt = true;
} else {
newFields.add(field);
}
}
if (!foundIt) {
final String s = "No field with name index " + nameIndex + " in "
+ this.name();
throw new IllegalArgumentException(s);
} else {
this.fields = (Field[]) newFields.toArray(new Field[0]);
}
}
/**
* Deletes a method from this class
*
* @param nameIndex
* Index in the constant pool of the name of the method to be
* deleted
* @param typeIndex
* Index in the constant pool of the type of the method to be
* deleted
*
* @throws IllegalArgumentException The class modeled by this
* <code>ClassInfo</code> does not contain a method whose name and
* type are not at the given indices
*/
public void deleteMethod(final int nameIndex, final int typeIndex) {
final List newMethods = new ArrayList();
boolean foundIt = false;
for (int i = 0; i < this.methods.length; i++) {
final Method method = this.methods[i];
if ((method.nameIndex() == nameIndex)
&& (method.typeIndex() == typeIndex)) {
foundIt = true;
} else {
newMethods.add(method);
}
}
if (!foundIt) {
final String s = "No method with name index " + nameIndex
+ " and type index " + typeIndex + " in " + this.name();
throw new IllegalArgumentException(s);
} else {
this.methods = (Method[]) newMethods.toArray(new Method[0]);
}
}
/**
* Creates a new method in this class
*/
public MethodInfo addNewMethod(final int modifiers, final int typeIndex,
final int nameIndex, final int exceptionIndex,
final int[] exceptionTypeIndices, final int codeIndex) {
final Exceptions exceptions = new Exceptions(this, exceptionIndex,
exceptionTypeIndices);
final Code code = new Code(this, codeIndex); // code can't be null
final Attribute[] attributes = new Attribute[] { exceptions, code };
final Method method = new Method(this, modifiers, nameIndex, typeIndex,
attributes, code, exceptions);
// Add the new method to the list of method
final Method[] methods = new Method[this.methods.length + 1];
for (int i = 0; i < this.methods.length; i++) {
methods[i] = this.methods[i];
}
methods[this.methods.length] = method;
this.methods = methods;
return (method);
}
/**
* Prints a textual representation of this classfile to a PrintStream. The
* text includes information such as the constants in the constant pool, the
* name of the class's superclass, its modifier flags, its fields, and its
* methods.
*
* @param out
* The stream to which to print.
*/
public void print(final PrintStream out) {
print(new PrintWriter(out, true));
}
public void print(final PrintWriter out) {
out.print("(constants");
for (int i = 0; i < constants.size(); i++) {
out.print("\n " + i + ": " + constants.get(i));
}
out.println(")");
out.println("(class " + classIndex() + ")");
out.println("(super " + superclassIndex() + ")");
out.print("(interfaces");
for (int i = 0; i < interfaces.length; i++) {
out.print("\n " + i + ": " + interfaces[i]);
}
out.println(")");
out.print("(modifiers");
if ((modifiers & Modifiers.PUBLIC) != 0) {
out.print(" PUBLIC");
}
if ((modifiers & Modifiers.FINAL) != 0) {
out.print(" FINAL");
}
if ((modifiers & Modifiers.SUPER) != 0) {
out.print(" SUPER");
}
if ((modifiers & Modifiers.INTERFACE) != 0) {
out.print(" INTERFACE");
}
if ((modifiers & Modifiers.ABSTRACT) != 0) {
out.print(" ABSTRACT");
}
out.println(")");
out.print("(fields");
for (int i = 0; i < fields.length; i++) {
out.print("\n " + i + ": " + fields[i]);
}
out.println(")");
out.print("(methods");
for (int i = 0; i < methods.length; i++) {
out.print("\n " + i + ": " + methods[i]);
}
out.println(")");
}
public String toString() {
return "(ClassFile " + name() + ")";
}
}

@ -0,0 +1,463 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.file;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* ClassFileLoder provides an interface for loading classes from files. The
* actual loading is done by the ClassFile itself.
* <p>
* Classes may be specified by their full package name (<tt>java.lang.String</tt>),
* or by the name of their class file (<tt>myclasses/Test.class</tt>). The
* class path may contain directories or Zip or Jar files. Any classes that are
* written back to disk ("committed") are placed in the output directory.
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class ClassFileLoader implements ClassInfoLoader {
public static boolean DEBUG = false;
public static boolean USE_SYSTEM_CLASSES = true;
private File outputDir; // Directory in which to write committed class files
private String classpath; // Path to search for classes
private Map openZipFiles; // zip files to search for class files
private LinkedList cache; // We keep a cache of CACHE_LIMIT class files
private boolean verbose;
private static final int CACHE_LIMIT = 10;
/**
* Constructor. The classpath initially consists of the contents of the
* <tt>java.class.path</tt> and <tt>sun.boot.class.path</tt> system
* properties.
*/
public ClassFileLoader() {
outputDir = new File(".");
classpath = System.getProperty("java.class.path");
classpath += File.pathSeparator
+ System.getProperty("sun.boot.class.path");
if (ClassFileLoader.USE_SYSTEM_CLASSES) {
classpath += File.pathSeparator
+ System.getProperty("java.sys.class.path");
}
openZipFiles = new HashMap();
cache = new LinkedList();
verbose = false;
}
public void setVerbose(final boolean verbose) {
this.verbose = verbose;
}
/**
* Sets the classpath.
*/
public void setClassPath(final String classpath) {
this.classpath = classpath;
}
/**
* Adds to the classpath (CLASSPATH = CLASSPATH + morePath).
*/
public void appendClassPath(final String morePath) {
this.classpath += File.pathSeparator + morePath;
}
/**
* Adds to the classpath (CLASSPATH = morePath + CLASSPATH).
*/
public void prependClassPath(final String morePath) {
this.classpath = morePath + File.pathSeparator + this.classpath;
}
/**
* Returns the path used to search for class files.
*/
public String getClassPath() {
return (this.classpath);
}
/**
* Load the class from a stream.
*
* @param inputFile
* The file from which to load the class.
* @param stream
* The stream from which to load the class.
* @return A ClassInfo for the class.
* @exception ClassNotFoundException
* The class cannot be found in the class path.
*/
private ClassInfo loadClassFromStream(final File inputFile,
final InputStream stream) throws ClassNotFoundException {
final DataInputStream in = new DataInputStream(stream);
final ClassFile file = new ClassFile(inputFile, this, in);
return file;
}
/**
* Load the class from the file.
*
* @param file
* The File from which to load a class.
* @return A ClassInfo for the class.
* @exception ClassNotFoundException
* The class cannot be found in the class path.
*/
private ClassInfo loadClassFromFile(final File file)
throws ClassNotFoundException {
try {
final InputStream in = new FileInputStream(file);
final ClassInfo info = loadClassFromStream(file, in);
if (verbose) {
System.out.println("[Loaded " + info.name() + " from "
+ file.getPath() + "]");
}
try {
in.close();
} catch (final IOException ex) {
}
return info;
} catch (final FileNotFoundException e) {
throw new ClassNotFoundException(file.getPath());
}
}
/**
* Loads all of the classes that are contained in a zip (or jar) file.
* Returns an array of the <tt>ClassInfo</tt>s for the classes in the zip
* file.
*/
public ClassInfo[] loadClassesFromZipFile(final ZipFile zipFile)
throws ClassNotFoundException {
final ClassInfo[] infos = new ClassInfo[zipFile.size()];
// Examine each entry in the zip file
final Enumeration entries = zipFile.entries();
for (int i = 0; entries.hasMoreElements(); i++) {
final ZipEntry entry = (ZipEntry) entries.nextElement();
if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
continue;
}
try {
final InputStream stream = zipFile.getInputStream(entry);
final File file = new File(entry.getName());
infos[i] = loadClassFromStream(file, stream);
} catch (final IOException ex) {
System.err.println("IOException: " + ex);
}
}
return (infos);
}
public ClassInfo newClass(final int modifiers, final int classIndex,
final int superClassIndex, final int[] interfaceIndexes,
final List constants) {
return new ClassFile(modifiers, classIndex, superClassIndex,
interfaceIndexes, constants, this);
}
/**
* Loads the class with the given name. Searches the class path, including
* zip files, for the class and then returns a data stream for the class
* file.
*
* @param name
* The name of the class to load, including the package name.
* @return A ClassInfo for the class.
* @exception ClassNotFoundException
* The class cannot be found in the class path.
*/
public ClassInfo loadClass(String name) throws ClassNotFoundException {
ClassInfo file = null;
// Check to see if name ends with ".class". If so, load the class from
// that file. Note that this is okay because we can never have a class
// named "class" (i.e. a class named "class" with a lower-case 'c' can
// never be specified in a fully-specified java class name) because
// "class" is a reserved word.
if (name.endsWith(".class")) {
final File nameFile = new File(name);
if (!nameFile.exists()) {
throw new ClassNotFoundException(name);
} else {
return (loadClassFromFile(nameFile));
}
}
// Otherwise, we have a (possibly fully-specified) class name.
name = name.replace('.', '/');
// Check the cache for the class file.
if (ClassFileLoader.DEBUG) {
System.out
.println(" Looking for " + name + " in cache = " + cache);
}
final Iterator iter = cache.iterator();
while (iter.hasNext()) {
file = (ClassFile) iter.next();
if (name.equals(file.name())) {
if (ClassFileLoader.DEBUG) {
System.out.println(" Found " + file.name() + " in cache");
}
// Move to the front of the cache.
iter.remove();
cache.addFirst(file);
return file;
}
}
file = null;
final String classFile = name.replace('/', File.separatorChar)
+ ".class";
// For each entry in the class path, search zip files and directories
// for classFile. When found, open an InputStream and break
// out of the loop to read the class file.
final String path = classpath + File.pathSeparator;
if (ClassFileLoader.DEBUG) {
System.out.println("CLASSPATH = " + path);
}
int index = 0;
int end = path.indexOf(File.pathSeparator, index);
SEARCH: while (end >= 0) {
final String dir = path.substring(index, end);
File f = new File(dir);
if (f.isDirectory()) {
// The directory is really a directory. If the class file
// exists, open a stream and return.
f = new File(dir, classFile);
if (f.exists()) {
try {
final InputStream in = new FileInputStream(f);
if (verbose) {
System.out.println(" [Loaded " + name + " from "
+ f.getPath() + "]");
}
file = loadClassFromStream(f, in);
try {
in.close();
} catch (final IOException ex) {
}
break SEARCH;
} catch (final FileNotFoundException ex) {
}
}
} else if (dir.endsWith(".zip") || dir.endsWith(".jar")) {
// Maybe a zip file?
try {
ZipFile zip = (ZipFile) openZipFiles.get(dir);
if (zip == null) {
zip = new ZipFile(f);
openZipFiles.put(dir, zip);
}
final String zipEntry = classFile.replace(
File.separatorChar, '/');
final ZipEntry entry = zip.getEntry(zipEntry);
if (entry != null) {
// Found the class file in the zip file.
// Open a stream and return.
if (verbose) {
System.out.println(" [Loaded " + name + " from "
+ f.getPath() + "]");
}
final InputStream in = zip.getInputStream(entry);
file = loadClassFromStream(f, in);
try {
in.close();
} catch (final IOException ex) {
}
break SEARCH;
}
} catch (final ZipException ex) {
} catch (final IOException ex) {
}
}
index = end + 1;
end = path.indexOf(File.pathSeparator, index);
}
if (file == null) {
// The class file wasn't in the class path. Try the currnet
// directory. If not there, give up.
final File f = new File(classFile);
if (!f.exists()) {
throw new ClassNotFoundException(name);
}
if (verbose) {
System.out.println(" [Loaded " + name + " from " + f.getPath()
+ "]");
}
try {
final InputStream in = new FileInputStream(f);
file = loadClassFromStream(f, in);
try {
in.close();
} catch (final IOException ex) {
}
} catch (final FileNotFoundException ex) {
throw new ClassNotFoundException(name);
}
}
if (file == null) {
throw new ClassNotFoundException(name);
}
// If we've reached the cache size limit, remove the oldest file
// in the cache. Then add the new file.
if (cache.size() == ClassFileLoader.CACHE_LIMIT) {
cache.removeLast();
}
cache.addFirst(file);
return file;
}
/**
* Set the directory into which commited class files should be written.
*
* @param dir
* The directory.
*/
public void setOutputDir(final File dir) {
outputDir = dir;
}
/**
* Get the directory into which commited class files should be written.
*/
public File outputDir() {
return outputDir;
}
/**
* Writes a bunch of <code>byte</code>s to an output entry with the given
* name.
*/
public void writeEntry(final byte[] bytes, final String name)
throws IOException {
final OutputStream os = outputStreamFor(name);
os.write(bytes);
os.flush();
os.close();
}
/**
* Returns an <tt>OutputStream</tt> to which a class file should be
* written.
*/
public OutputStream outputStreamFor(final ClassInfo info)
throws IOException {
// Format the name of the output file
final String name = info.name().replace('/', File.separatorChar)
+ ".class";
return outputStreamFor(name);
}
/**
* Returns an <code>OutputStream</code> to which somed named entity is
* written. Any forward slashes in the name are replaced by
* <code>File.separatorChar</code>.
*/
protected OutputStream outputStreamFor(String name) throws IOException {
name = name.replace('/', File.separatorChar);
final File f = new File(outputDir, name);
if (f.exists()) {
f.delete();
}
final File dir = new File(f.getParent());
dir.mkdirs();
if (!dir.exists()) {
throw new RuntimeException("Couldn't create directory: " + dir);
}
return (new FileOutputStream(f));
}
/**
* Signifies that we are done with this <code>ClassFileLoader</code>
*/
public void done() throws IOException {
// Nothing for this guy
}
}

@ -0,0 +1,442 @@
/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.file;
import java.io.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* Code is used to store the Code attribute of a method in a class file. The
* Code attribute stores the raw bytecode of the method, the maximum stack
* height and maximum number of locals used by the method, and the exception
* handlers used in the method. Code may have several attributes. The local
* variable table and the line number table are modeled explicitly. All other
* attributes are modeled as generic attributes.
*
* @see EDU.purdue.cs.bloat.reflect.Catch Catch
* @see GenericAttribute
* @see EDU.purdue.cs.bloat.reflect.LineNumberDebugInfo
* @see LocalVariableTable
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class Code extends Attribute {
private ClassInfo classInfo;
private int maxStack;
private int maxLocals;
private byte[] code;
private Catch[] handlers;
private LineNumberTable lineNumbers;
private LocalVariableTable locals;
private Attribute[] attrs;
/**
* Constructor for creating a <code>Code</code> from scratch
*
* @param codeIndex
* The index in the constant pool for the UTF8 "Code"
*/
Code(final ClassInfo classInfo, final int codeIndex) {
// Don't know length!
super(codeIndex, -1);
this.classInfo = classInfo;
// These should get set during a commit
this.maxStack = -1;
this.maxLocals = -1;
this.code = new byte[0];
this.handlers = new Catch[0];
this.lineNumbers = null; // It's okay for these to be null
this.locals = null;
this.attrs = new Attribute[0];
}
/**
* Constructor. Create a Code attribute from a data stream.
*
* @param in
* The data stream containing the class file.
* @param index
* The index into the constant pool of the name of the attribute.
* @param len
* The length of the attribute, excluding the header.
* @exception IOException
* If an error occurs while reading.
*/
public Code(final ClassInfo classInfo, final DataInputStream in,
final int index, final int len) throws IOException {
super(index, len);
this.classInfo = classInfo;
maxStack = in.readUnsignedShort();
maxLocals = in.readUnsignedShort();
final int codeLength = in.readInt();
code = new byte[codeLength];
for (int read = 0; read < codeLength;) {
read += in.read(code, read, codeLength - read);
}
final int numHandlers = in.readUnsignedShort();
handlers = new Catch[numHandlers];
for (int i = 0; i < numHandlers; i++) {
handlers[i] = readCatch(in);
}
final int numAttributes = in.readUnsignedShort();
attrs = new Attribute[numAttributes];
for (int i = 0; i < numAttributes; i++) {
final int nameIndex = in.readUnsignedShort();
final int length = in.readInt();
final Constant name = classInfo.constants()[nameIndex];
if (name != null) {
if ("LineNumberTable".equals(name.value())) {
lineNumbers = new LineNumberTable(in, nameIndex, length);
attrs[i] = lineNumbers;
} else if ("LocalVariableTable".equals(name.value())) {
locals = new LocalVariableTable(in, nameIndex, length);
attrs[i] = locals;
}
}
if (attrs[i] == null) {
attrs[i] = new GenericAttribute(in, nameIndex, length);
}
}
}
/**
* Write the attribute to a data stream.
*
* @param out
* The data stream of the class file.
* @exception IOException
* If an error occurs while writing.
*/
public void writeData(final DataOutputStream out) throws IOException {
out.writeShort(maxStack);
out.writeShort(maxLocals);
out.writeInt(code.length);
out.write(code, 0, code.length);
out.writeShort(handlers.length);
for (int i = 0; i < handlers.length; i++) {
writeCatch(out, handlers[i]);
}
out.writeShort(attrs.length);
for (int i = 0; i < attrs.length; i++) {
out.writeShort(attrs[i].nameIndex());
out.writeInt(attrs[i].length());
attrs[i].writeData(out);
}
}
/**
* Read an exception handler attribute.
*
* @param in
* The data stream of the class file.
* @return A Catch attribute for the handler.
* @exception IOException
* If an error occurs while reading.
*/
private Catch readCatch(final DataInputStream in) throws IOException {
final int startPC = in.readUnsignedShort();
final int endPC = in.readUnsignedShort();
final int handlerPC = in.readUnsignedShort();
final int catchType = in.readUnsignedShort();
return new Catch(startPC, endPC, handlerPC, catchType);
}
/**
* Write an exception handler attribute.
*
* @param out
* The data stream of the class file.
* @param c
* A Catch attribute for the handler.
* @exception IOException
* If an error occurs while writing.
*/
private void writeCatch(final DataOutputStream out, final Catch c)
throws IOException {
final int startPC = c.startPC();
final int endPC = c.endPC();
final int handlerPC = c.handlerPC();
final int catchType = c.catchTypeIndex();
out.writeShort(startPC);
out.writeShort(endPC);
out.writeShort(handlerPC);
out.writeShort(catchType);
}
/**
* Set the maximum height of the operand stack used by the code.
*
* @param maxStack
* The maximum height of the stack.
*/
public void setMaxStack(final int maxStack) {
this.maxStack = maxStack;
}
/**
* Set the maximum number of locals used by the code.
*
* @param maxLocals
* The maximum number of locals.
*/
public void setMaxLocals(final int maxLocals) {
this.maxLocals = maxLocals;
}
/**
* Get the maximum height of the operand stack used by the code.
*
* @return The maximum number of locals.
*/
public int maxStack() {
return maxStack;
}
/**
* Get the maximum number of locals used by the code.
*
* @return The maximum number of locals.
*/
public int maxLocals() {
return maxLocals;
}
/**
* Set the exception handlers in the method.
*
* @param handlers
* The handlers.
*/
public void setExceptionHandlers(final Catch[] handlers) {
this.handlers = handlers;
}
/**
* Get the length of the attribute.
*
* @return The length of the attribute.
*/
public int length() {
int length = 2 + 2 + 4 + code.length + 2 + handlers.length * 8 + 2;
for (int i = 0; i < attrs.length; i++) {
length += 2 + 4 + attrs[i].length();
}
return length;
}
/**
* Get the line number debug info for the code.
*
* @return The line number debug info for the code.
*/
public LineNumberDebugInfo[] lineNumbers() {
if (lineNumbers != null) {
return lineNumbers.lineNumbers();
}
return new LineNumberDebugInfo[0];
}
/**
* Get the local variable debug info for the code.
*
* @return The local variable debug info for the code.
*/
public LocalDebugInfo[] locals() {
if (locals != null) {
return locals.locals();
}
return new LocalDebugInfo[0];
}
/**
* Set the line number debug info for the code.
*
* @param lineNumbers
* The line number debug info for the code.
*/
public void setLineNumbers(final LineNumberDebugInfo[] lineNumbers) {
if (lineNumbers == null) {
for (int i = 0; i < attrs.length; i++) {
if (this.lineNumbers == attrs[i]) {
final Attribute[] a = attrs;
attrs = new Attribute[a.length - 1];
System.arraycopy(a, 0, attrs, 0, i);
System.arraycopy(a, i + 1, attrs, i, attrs.length - i);
break;
}
}
this.lineNumbers = null;
} else if (this.lineNumbers != null) {
this.lineNumbers.setLineNumbers(lineNumbers);
}
}
/**
* Set the local variable debug info for the code.
*
* @param locals
* The local variable debug info for the code.
*/
public void setLocals(final LocalDebugInfo[] locals) {
if (locals == null) {
for (int i = 0; i < attrs.length; i++) {
if (this.locals == attrs[i]) {
final Attribute[] a = attrs;
attrs = new Attribute[a.length - 1];
System.arraycopy(a, 0, attrs, 0, i);
System.arraycopy(a, i + 1, attrs, i, attrs.length - i);
break;
}
}
this.locals = null;
} else if (this.locals != null) {
this.locals.setLocals(locals);
}
}
/**
* Get the exception handlers in the method.
*
* @return The handlers.
*/
public Catch[] exceptionHandlers() {
return handlers;
}
/**
* Get the bytes of the code.
*
* @return The code.
*/
public byte[] code() {
return code;
}
/**
* Return the length of the code array
*/
public int codeLength() {
return (code.length);
}
/**
* Set the bytes of the code.
*
* @param code
* The code.
*/
public void setCode(final byte[] code) {
this.code = code;
}
/**
* Private constructor for cloning.
*/
private Code(final Code other) {
super(other.nameIndex, other.length);
this.classInfo = other.classInfo;
this.maxStack = other.maxStack;
this.maxLocals = other.maxLocals;
this.code = new byte[other.code.length];
System.arraycopy(other.code, 0, this.code, 0, other.code.length);
this.handlers = new Catch[other.handlers.length];
for (int i = 0; i < other.handlers.length; i++) {
this.handlers[i] = (Catch) other.handlers[i].clone();
}
if (other.lineNumbers != null) {
this.lineNumbers = (LineNumberTable) other.lineNumbers.clone();
}
if (other.locals != null) {
this.locals = (LocalVariableTable) other.locals.clone();
}
this.attrs = new Attribute[other.attrs.length];
for (int i = 0; i < other.attrs.length; i++) {
this.attrs[i] = other.attrs[i];
}
}
public Object clone() {
return (new Code(this));
}
/**
* Returns a string representation of the attribute.
*/
public String toString() {
String x = "";
if (handlers != null) {
for (int i = 0; i < handlers.length; i++) {
x += "\n " + handlers[i];
}
}
/*
* for (int i = 0; i < attrs.length; i++) { x += "\n " + attrs[i]; }
*/
return "(code " + maxStack + " " + maxLocals + " " + code.length + x
+ ")";
}
}

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

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

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

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

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

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

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

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

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

@ -0,0 +1,11 @@
<html>
<body>
<p>Allows access to Java classes stored in files on disk. Many of
these classes implement the interfaces found in the
EDU.purdue.cs.bloat.reflect package. Classes are loaded from a file
and things such as constant values, methods, code, debugging
information, and exceptions are modeled.</p>
</body>
</html>

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

Loading…
Cancel
Save