Compare commits

..

64 Commits

Author SHA1 Message Date
hoenicke 12e82bea53 Set copyright to LGPL for jode.type package 15 years ago
hoenicke e10bfec001 Minor fixes 18 years ago
hoenicke 240de78e81 jode/bytecode/ClassInfo.java.in, jode/bytecode/MethodInfo.java, 18 years ago
hoenicke fca129b90a enum -> enumeration (jdk1.5) 19 years ago
hoenicke 26933573bb Ignore minor(major?) number 19 years ago
hoenicke 190eefee2c Check for NullPointer in SyntheticAnalyzer. Based on 19 years ago
hoenicke 90aa079d11 Added patch from Peter Klauser (klp at users.sf.net), to support 19 years ago
hoenicke aae07e1491 A continue was missing that could produce ArrayIndexOutOfBounds 20 years ago
hoenicke 6604df3248 Added patch from Thomas Oldervoll, to continue decompiling 22 years ago
hoenicke 97b5c5bb0f deprecated flags for classinfo 22 years ago
hoenicke f38c62b1cd Set version number to 1.1.2. 23 years ago
hoenicke d1f22a62b4 Fix the exception handlers that Javac 1.4 produces. 23 years ago
hoenicke c761c52679 Changed license of util,bytecode,jvm,expr,flow and decompiler packages 23 years ago
hoenicke a12e86ea8f Don't check for a maximum version anymore. Sun changes it with every 23 years ago
hoenicke 111d5046ea * jode/obfuscator/ClassIdentifier.java.in (transformInnerClasses): 23 years ago
hoenicke a0caad2c2c * jode/flow/CreateAssignExpression.java (createAssignOp): 23 years ago
hoenicke 910456c0b0 Fixes by anonymous for SerializePreserver: 23 years ago
hoenicke 686154fca2 * jode/obfuscator/modules/MultiIdentifierMatcher.java.in 23 years ago
hoenicke 068e85a76d Fixed major/minor naming. Allow minor to be bigger than 45 for newer java 23 years ago
hoenicke ea5e834abe * configure.in: Set version number to 1.1.1. 24 years ago
hoenicke 03497ae1a2 Fixed Java-1.1 and finally/synchronized blocks. 24 years ago
hoenicke c79f6122f5 * configure.in: Changed bash syntax to more compatible (but 24 years ago
hoenicke 805ab8613d * jode/expr/InvokeOperator.java.in (dumpExpression): Fixed the 24 years ago
hoenicke 15e7ec9dc0 More debugging outputs. 24 years ago
hoenicke a018796a04 * jode/obfuscator/Main.java.in (stripping): Initialize to 0 which 24 years ago
hoenicke aca789beed * jode/flow/CatchBlock.java.in (combineLocal): Added more checks 24 years ago
hoenicke a65fd35ceb * jode/obfuscator/modules/SimpleAnalyzer.java.in: 24 years ago
hoenicke fa005e09ee * jode/bytecode/BytecodeInfo.java.in (calculateMaxStack): Handle 24 years ago
hoenicke dd5112302f Added more links. 24 years ago
hoenicke fbcfb06d90 * jode/jvm/SyntheticAnalyzer.java.in: 24 years ago
hoenicke 5a4dbe0b02 Fixed a bug in the obfuscator. 24 years ago
hoenicke af086625d8 More documentation updates. 24 years ago
hoenicke 98bf83e95d Fixed link to sourceforge logo. 24 years ago
hoenicke 7fca8d09e9 Remove .html files, added a-logo.gif 24 years ago
hoenicke 0246d1e5c3 lots of changes, see ChangeLog 24 years ago
hoenicke 23adc4ce78 * jode/jvm/CodeVerifier.java.in (doVerify): Don't check for 24 years ago
hoenicke 736e9595d2 * jode/obfuscator/modules/ConstantAnalyzer.java.in (handleOpcode): 24 years ago
hoenicke d093988656 Reworked usage message. 24 years ago
hoenicke 95b75fa705 Bug fixes from Joe Bronkema. 24 years ago
hoenicke b355dc3395 * acinclude.m4 (JODE_CHECK_CLASS): Changed "test -e" to "-f" since 24 years ago
hoenicke 5e972183a9 * jode/swingui/Main.java.in (AreaWriter): Convert all kinds of 24 years ago
hoenicke c5fae0c7ee Some more bug fixes, see changelog. 24 years ago
hoenicke f1cee9fca4 InvokeOperator (method interpreter): Fixed call to ClassInfo.forName. 24 years ago
hoenicke dbacfa1c7e updated list 24 years ago
hoenicke 1f87c3817c Changed version to 1.2 24 years ago
hoenicke 4aebd22542 Fixed previous patch. 24 years ago
hoenicke 344faa9265 CodeVerifier, ArrayType: merge array types whose elemTypes are incompatible 24 years ago
hoenicke d0a70c3626 SyntheticAnalyzer: Allow the special unifyParam to be the last parameter. 24 years ago
hoenicke cb12d3ead2 Pascal style. 24 years ago
hoenicke 816bca302d Distinguish method scoped classes from inner classes in a better way. 24 years ago
hoenicke 0785a7cd21 Support decompiling complete Zip files 24 years ago
hoenicke 4888c5f98c Set fields to not editable. 24 years ago
hoenicke 3506e82033 Ported a fix (this class could throw an ArrayIndexOutOfBoundsException due, to 24 years ago
hoenicke fb5d918702 Another fix: The fall through for opc_dadd and co. didn't work 24 years ago
hoenicke 7eb1d3f8b5 Forgot to calc stacksize for exception handlers. 24 years ago
hoenicke 7548f53a75 Fixed another stupid bug. 24 years ago
hoenicke ef0a0f214b Reserve locals for method arguments. Important if they are not accessed 24 years ago
hoenicke 0250b87fe1 Removed some old local variables, that were never accessed. 24 years ago
hoenicke f26d8a21e6 Calculate number of stack elements and local variables in prepareWriting. 24 years ago
hoenicke a56a7f0e98 Fixed a bug in configure.in, reported by zzzeek 24 years ago
hoenicke 075db18fcf Accept class version 46.0 (jdk1.2) 25 years ago
hoenicke 356a8c6a61 New implementation to get the alias of a class getClassAlias(). 25 years ago
hoenicke f0392e9371 Don't create doc/download.html anymore 25 years ago
(no author) 6466af0fea This commit was manufactured by cvs2svn to create branch 'branch_1_1'. 25 years ago
  1. 14
      CVSROOT/checkoutlist
  2. 15
      CVSROOT/commitinfo
  3. 11
      CVSROOT/config
  4. 1
      CVSROOT/cvsignore
  5. 24
      CVSROOT/cvswrappers
  6. 21
      CVSROOT/editinfo
  7. 27
      CVSROOT/loginfo
  8. 1
      CVSROOT/modules
  9. 12
      CVSROOT/notify
  10. 13
      CVSROOT/rcsinfo
  11. 206
      CVSROOT/syncmail
  12. 20
      CVSROOT/taginfo
  13. 21
      CVSROOT/verifymsg
  14. 7
      jode/.classpath
  15. 17
      jode/.project
  16. 353
      jode/.settings/org.eclipse.jdt.core.prefs
  17. 4
      jode/.settings/org.eclipse.jdt.ui.prefs
  18. 3
      jode/.settings/org.eclipse.ltk.core.refactoring.prefs
  19. 504
      jode/COPYING.LGPL
  20. 628
      jode/ChangeLog
  21. 62
      jode/INSTALL
  22. 3
      jode/MANIFEST.MF
  23. 5
      jode/Makefile.am
  24. 5
      jode/NEWS
  25. 47
      jode/TODO
  26. 44
      jode/acinclude.m4
  27. 0
      jode/addHeader.pl
  28. 380
      jode/build.xml
  29. 28
      jode/config.props
  30. 244
      jode/configure.in
  31. 46
      jode/doc/Makefile.am
  32. BIN
      jode/doc/a-logo.gif
  33. 25
      jode/doc/applet.htp
  34. 38
      jode/doc/applet.php
  35. 17
      jode/doc/bluesky.php
  36. 39
      jode/doc/download.htp
  37. 40
      jode/doc/download.php
  38. 36
      jode/doc/faq.php
  39. 106
      jode/doc/favicon.xpm
  40. 7
      jode/doc/feedback.php
  41. 4
      jode/doc/footer.inc
  42. BIN
      jode/doc/gimp/jode-logo.xcf
  43. 6
      jode/doc/header.inc
  44. 7
      jode/doc/history.php
  45. 52
      jode/doc/htp.def
  46. 35
      jode/doc/index.php
  47. BIN
      jode/doc/jode-logo.gif
  48. BIN
      jode/doc/jode-logo.png
  49. 67
      jode/doc/jode.htt
  50. 13
      jode/doc/license.php
  51. 10
      jode/doc/links.php
  52. 2
      jode/doc/menu.inc
  53. 32
      jode/doc/myproject.jos
  54. BIN
      jode/doc/poweredbyhtp.png
  55. 62
      jode/doc/technical.texi
  56. 77
      jode/doc/usage.php
  57. BIN
      jode/doc/w3c_ab.png
  58. 7
      jode/javaDependencies.pl.in
  59. 99
      jode/jcpp
  60. 4
      jode/jode/.cvsignore
  61. 29
      jode/jode/AssertError.java
  62. 13
      jode/jode/GlobalOptions.java.in
  63. 41
      jode/jode/Makefile.am
  64. 2
      jode/jode/bytecode/.cvsignore
  65. 229
      jode/jode/bytecode/BinaryInfo.java.in
  66. 1635
      jode/jode/bytecode/BytecodeInfo.java.in
  67. 16
      jode/jode/bytecode/ClassFormatException.java
  68. 903
      jode/jode/bytecode/ClassInfo.java.in
  69. 91
      jode/jode/bytecode/ConstantPool.java
  70. 206
      jode/jode/bytecode/FieldInfo.java
  71. 106
      jode/jode/bytecode/GrowableConstantPool.java
  72. 29
      jode/jode/bytecode/Handler.java
  73. 38
      jode/jode/bytecode/InnerClassInfo.java
  74. 830
      jode/jode/bytecode/Instruction.java
  75. 29
      jode/jode/bytecode/LineNumber.java
  76. 30
      jode/jode/bytecode/LocalVariableInfo.java
  77. 45
      jode/jode/bytecode/Makefile.am
  78. 241
      jode/jode/bytecode/MethodInfo.java
  79. 12
      jode/jode/bytecode/Opcodes.java
  80. 17
      jode/jode/bytecode/Reference.java.in
  81. 581
      jode/jode/bytecode/SearchPath.java
  82. 175
      jode/jode/bytecode/TypeSignature.java
  83. 2
      jode/jode/decompiler/.cvsignore
  84. 2
      jode/jode/decompiler/Analyzer.java
  85. 2
      jode/jode/decompiler/Applet.java
  86. 199
      jode/jode/decompiler/ClassAnalyzer.java.in
  87. 4
      jode/jode/decompiler/ClassDeclarer.java
  88. 116
      jode/jode/decompiler/DeadCodeAnalysis.java.in
  89. 2
      jode/jode/decompiler/Declarable.java
  90. 99
      jode/jode/decompiler/Decompiler.java
  91. 26
      jode/jode/decompiler/FieldAnalyzer.java.in
  92. 117
      jode/jode/decompiler/ImportHandler.java.in
  93. 35
      jode/jode/decompiler/LocalInfo.java
  94. 45
      jode/jode/decompiler/LocalVarEntry.java
  95. 79
      jode/jode/decompiler/LocalVariableRangeList.java
  96. 44
      jode/jode/decompiler/LocalVariableTable.java
  97. 238
      jode/jode/decompiler/Main.java
  98. 51
      jode/jode/decompiler/Makefile.am
  99. 398
      jode/jode/decompiler/MethodAnalyzer.java.in
  100. 365
      jode/jode/decompiler/Opcodes.java
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,14 +0,0 @@
# The "checkoutlist" file is used to support additional version controlled
# administrative files in $CVSROOT/CVSROOT, such as template files.
#
# The first entry on a line is a filename which will be checked out from
# the corresponding RCS file in the $CVSROOT/CVSROOT directory.
# The remainder of the line is an error message to use if the file cannot
# be checked out.
#
# File format:
#
# [<whitespace>]<filename><whitespace><error message><end-of-line>
#
# comment lines begin with '#'
syncmail

@ -1,15 +0,0 @@
# The "commitinfo" file is used to control pre-commit checks.
# The filter on the right is invoked with the repository and a list
# of files to check. A non-zero exit of the filter program will
# cause the commit to be aborted.
#
# The first entry on a line is a regular expression which is tested
# against the directory that the change is being committed to, relative
# to the $CVSROOT. For the first match that is found, then the remainder
# of the line is the name of the filter to run.
#
# If the repository name does not match any of the regular expressions in this
# file, the "DEFAULT" line is used, if it is specified.
#
# If the name "ALL" appears as a regular expression it is always used
# in addition to the first matching regex or "DEFAULT".

@ -1,11 +0,0 @@
# Set this to "no" if pserver shouldn't check system users/passwords
#SystemAuth=no
# Set `PreservePermissions' to `yes' to save file status information
# in the repository.
#PreservePermissions=no
# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top
# level of the new working directory when using the `cvs checkout'
# command.
#TopLevelAdmin=no

@ -1,24 +0,0 @@
# This file affects handling of files based on their names.
#
# The -t/-f options allow one to treat directories of files
# as a single file, or to transform a file in other ways on
# its way in and out of CVS.
#
# The -m option specifies whether CVS attempts to merge files.
#
# The -k option specifies keyword expansion (e.g. -kb for binary).
#
# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)
#
# wildcard [option value][option value]...
#
# where option is one of
# -f from cvs filter value: path to filter
# -t to cvs filter value: path to filter
# -m update methodology value: MERGE or COPY
# -k expansion mode value: b, o, kkv, &c
#
# and value is a single-quote delimited value.
# For example:
*.gif -k 'b'
*.xcf -k 'b'

@ -1,21 +0,0 @@
# The "editinfo" file is used to allow verification of logging
# information. It works best when a template (as specified in the
# rcsinfo file) is provided for the logging procedure. Given a
# template with locations for, a bug-id number, a list of people who
# reviewed the code before it can be checked in, and an external
# process to catalog the differences that were code reviewed, the
# following test can be applied to the code:
#
# Making sure that the entered bug-id number is correct.
# Validating that the code that was reviewed is indeed the code being
# checked in (using the bug-id number or a seperate review
# number to identify this particular code set.).
#
# If any of the above test failed, then the commit would be aborted.
#
# Actions such as mailing a copy of the report to each reviewer are
# better handled by an entry in the loginfo file.
#
# One thing that should be noted is the the ALL keyword is not
# supported. There can be only one entry that matches a given
# repository.

@ -1,27 +0,0 @@
# The "loginfo" file controls where "cvs commit" log information
# is sent. The first entry on a line is a regular expression which must match
# the directory that the change is being made to, relative to the
# $CVSROOT. If a match is found, then the remainder of the line is a filter
# program that should expect log information on its standard input.
#
# If the repository name does not match any of the regular expressions in this
# file, the "DEFAULT" line is used, if it is specified.
#
# If the name ALL appears as a regular expression it is always used
# in addition to the first matching regex or DEFAULT.
#
# You may specify a format string as part of the
# filter. The string is composed of a `%' followed
# by a single format character, or followed by a set of format
# characters surrounded by `{' and `}' as separators. The format
# characters are:
#
# s = file name
# V = old version number (pre-checkin)
# v = new version number (post-checkin)
#
# For example:
#DEFAULT (echo ""; id; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog
# or
#DEFAULT (echo ""; id; echo %{sVv}; date; cat) >> $CVSROOT/CVSROOT/commitlog
DEFAULT $CVSROOT/CVSROOT/syncmail %{sVv} jode-commit@lists.sourceforge.net

@ -1 +0,0 @@
jode jode

@ -1,12 +0,0 @@
# The "notify" file controls where notifications from watches set by
# "cvs watch add" or "cvs edit" are sent. The first entry on a line is
# a regular expression which is tested against the directory that the
# change is being made to, relative to the $CVSROOT. If it matches,
# then the remainder of the line is a filter program that should contain
# one occurrence of %s for the user to notify, and information on its
# standard input.
#
# "ALL" or "DEFAULT" can be used in place of the regular expression.
#
# For example:
#ALL mail %s -s "CVS notification"

@ -1,13 +0,0 @@
# The "rcsinfo" file is used to control templates with which the editor
# is invoked on commit and import.
#
# The first entry on a line is a regular expression which is tested
# against the directory that the change is being made to, relative to the
# $CVSROOT. For the first match that is found, then the remainder of the
# line is the name of the file that contains the template.
#
# If the repository name does not match any of the regular expressions in this
# file, the "DEFAULT" line is used, if it is specified.
#
# If the name "ALL" appears as a regular expression it is always used
# in addition to the first matching regex or "DEFAULT".

@ -1,206 +0,0 @@
#! /usr/bin/python
# -*- Python -*-
"""Complicated notification for CVS checkins.
This script is used to provide email notifications of changes to the CVS
repository. These email changes will include context diffs of the changes.
Really big diffs will be trimmed.
This script is run from a CVS loginfo file (see $CVSROOT/CVSROOT/loginfo). To
set this up, create a loginfo entry that looks something like this:
mymodule /path/to/this/script %%s some-email-addr@your.domain
In this example, whenever a checkin that matches `mymodule' is made, this
script is invoked, which will generate the diff containing email, and send it
to some-email-addr@your.domain.
Note: This module used to also do repository synchronizations via
rsync-over-ssh, but since the repository has been moved to SourceForge,
this is no longer necessary. The syncing functionality has been ripped
out in the 3.0, which simplifies it considerably. Access the 2.x versions
to refer to this functionality. Because of this, the script is misnamed.
It no longer makes sense to run this script from the command line. Doing so
will only print out this usage information.
Usage:
%(PROGRAM)s [options] <%%S> email-addr [email-addr ...]
Where options is:
--cvsroot=<path>
Use <path> as the environment variable CVSROOT. Otherwise this
variable must exist in the environment.
--help
-h
Print this text.
<%%S>
CVS %%s loginfo expansion. When invoked by CVS, this will be a single
string containing the directory the checkin is being made in, relative
to $CVSROOT, followed by the list of files that are changing. If the
%%s in the loginfo file is %%{sVv}, context diffs for each of the
modified files are included in any email messages that are generated.
email-addrs
At least one email address.
"""
import os
import sys
import string
import time
import getopt
# Notification command
MAILCMD = '/bin/mail -s "%(SUBJECT)s" %(PEOPLE)s 2>&1 > /dev/null'
# Diff trimming stuff
DIFF_HEAD_LINES = 20
DIFF_TAIL_LINES = 20
DIFF_TRUNCATE_IF_LARGER = 1000
PROGRAM = sys.argv[0]
def usage(code, msg=''):
print __doc__ % globals()
if msg:
print msg
sys.exit(code)
def calculate_diff(filespec):
try:
file, oldrev, newrev = string.split(filespec, ',')
except ValueError:
# No diff to report
return '***** Bogus filespec: %s' % filespec
if oldrev == 'NONE':
try:
if os.path.exists(file):
fp = open(file)
else:
update_cmd = 'cvs -fn update -r %s -p %s' % (newrev, file)
fp = os.popen(update_cmd)
lines = fp.readlines()
fp.close()
lines.insert(0, '--- NEW FILE ---\n')
except IOError, e:
lines = ['***** Error reading new file: ',
str(e), '\n***** file: ', file, ' cwd: ', os.getcwd()]
elif newrev == 'NONE':
lines = ['--- %s DELETED ---\n' % file]
else:
# This /has/ to happen in the background, otherwise we'll run into CVS
# lock contention. What a crock.
diffcmd = '/usr/bin/cvs -f diff -kk -C 2 -r %s -r %s %s' % (
oldrev, newrev, file)
fp = os.popen(diffcmd)
lines = fp.readlines()
sts = fp.close()
# ignore the error code, it always seems to be 1 :(
## if sts:
## return 'Error code %d occurred during diff\n' % (sts >> 8)
if len(lines) > DIFF_TRUNCATE_IF_LARGER:
removedlines = len(lines) - DIFF_HEAD_LINES - DIFF_TAIL_LINES
del lines[DIFF_HEAD_LINES:-DIFF_TAIL_LINES]
lines.insert(DIFF_HEAD_LINES,
'[...%d lines suppressed...]\n' % removedlines)
return string.join(lines, '')
def calculate_url(dir, filespec):
try:
file, oldrev, newrev = string.split(filespec, ',')
except ValueError:
# No diff to report
return '***** Bogus filespec: %s' % filespec
if oldrev == 'NONE':
lines = [ 'http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/jode/%s/%s?rev=%s' % (dir, file, newrev) ]
elif newrev == 'NONE':
lines = [ 'DELETED: http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/jode/%s/%s?rev=%s' % (dir, file, oldrev) ]
else:
lines = [ 'http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/jode/%s/%s.diff?r1=%s&r2=%s&diff_format=u' % (dir, file, oldrev, newrev) ]
return string.join(lines, '')
def blast_mail(mailcmd, dir, filestodiff):
## cannot wait for child process or that will cause parent to retain cvs
## lock for too long. Urg!
#if not os.fork():
# in the child
# give up the lock you cvs thang!
time.sleep(2)
fp = os.popen(mailcmd, 'w')
fp.write(sys.stdin.read())
fp.write('\n')
# append the cvsweb urls if available
fp.write('CVSWeb URLs:\n');
for file in filestodiff:
# fp.write(calculate_diff(file))
fp.write(calculate_url(dir, file))
fp.write('\n')
fp.close()
## doesn't matter what code we return, it isn't waited on
# os._exit(0)
# scan args for options
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], 'h', ['cvsroot=', 'help'])
except getopt.error, msg:
usage(1, msg)
# parse the options
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
elif opt == '--cvsroot':
os.environ['CVSROOT'] = arg
# What follows is the specification containing the files that were
# modified. The argument actually must be split, with the first component
# containing the directory the checkin is being made in, relative to
# $CVSROOT, followed by the list of files that are changing.
if not args:
usage(1, 'No CVS module specified')
specs = string.split(args[0])
changedfiles = [ specs[0] ];
for filespec in specs[1:]:
try:
file, oldrev, newrev = string.split(filespec, ',')
changedfiles.append(file)
except ValueError:
changedfiles.append(filespec)
SUBJECT = string.join(changedfiles, ' ')
del args[0]
# The remaining args should be the email addresses
if not args:
usage(1, 'No recipients specified')
# Now do the mail command
PEOPLE = string.join(args)
mailcmd = MAILCMD % vars()
if specs == ['-', 'Imported', 'sources']:
return
if specs[-3:] == ['-', 'New', 'directory']:
del specs[-3:]
blast_mail(mailcmd, specs[0], specs[1:])
if __name__ == '__main__':
main()
sys.exit(0)

@ -1,20 +0,0 @@
# The "taginfo" file is used to control pre-tag checks.
# The filter on the right is invoked with the following arguments:
#
# $1 -- tagname
# $2 -- operation "add" for tag, "mov" for tag -F, and "del" for tag -d
# $3 -- repository
# $4-> file revision [file revision ...]
#
# A non-zero exit of the filter program will cause the tag to be aborted.
#
# The first entry on a line is a regular expression which is tested
# against the directory that the change is being committed to, relative
# to the $CVSROOT. For the first match that is found, then the remainder
# of the line is the name of the filter to run.
#
# If the repository name does not match any of the regular expressions in this
# file, the "DEFAULT" line is used, if it is specified.
#
# If the name "ALL" appears as a regular expression it is always used
# in addition to the first matching regex or "DEFAULT".

@ -1,21 +0,0 @@
# The "verifymsg" file is used to allow verification of logging
# information. It works best when a template (as specified in the
# rcsinfo file) is provided for the logging procedure. Given a
# template with locations for, a bug-id number, a list of people who
# reviewed the code before it can be checked in, and an external
# process to catalog the differences that were code reviewed, the
# following test can be applied to the code:
#
# Making sure that the entered bug-id number is correct.
# Validating that the code that was reviewed is indeed the code being
# checked in (using the bug-id number or a seperate review
# number to identify this particular code set.).
#
# If any of the above test failed, then the commit would be aborted.
#
# Actions such as mailing a copy of the report to each reviewer are
# better handled by an entry in the loginfo file.
#
# One thing that should be noted is the the ALL keyword is not
# supported. There can be only one entry that matches a given
# repository.

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry excluding="net/sf/jode/obfuscator/modules/LocalOptimizer.java|net/sf/jode/obfuscator/modules/LocalizeFieldTransformer.java|net/sf/jode/obfuscator/modules/RemovePopAnalyzer.java" kind="src" output="build" path="src"/>
<classpathentry kind="lib" path="lib/java-getopt-1.0.8.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="build"/>
</classpath>

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

@ -1,353 +0,0 @@
#Thu Mar 01 23:39:18 CET 2012
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.4
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
org.eclipse.jdt.core.compiler.problem.deadCode=warning
org.eclipse.jdt.core.compiler.problem.deprecation=warning
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
org.eclipse.jdt.core.compiler.problem.nullReference=warning
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
org.eclipse.jdt.core.compiler.problem.unusedImport=warning
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.source=1.3
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
org.eclipse.jdt.core.formatter.comment.format_header=false
org.eclipse.jdt.core.formatter.comment.format_html=true
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
org.eclipse.jdt.core.formatter.comment.format_source_code=true
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
org.eclipse.jdt.core.formatter.comment.line_length=80
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
org.eclipse.jdt.core.formatter.compact_else_if=true
org.eclipse.jdt.core.formatter.continuation_indentation=2
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_empty_lines=false
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
org.eclipse.jdt.core.formatter.indentation.size=4
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
org.eclipse.jdt.core.formatter.lineSplit=80
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
org.eclipse.jdt.core.formatter.tabulation.char=mixed
org.eclipse.jdt.core.formatter.tabulation.size=8
org.eclipse.jdt.core.formatter.use_on_off_tags=false
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true

@ -1,4 +0,0 @@
#Thu Mar 01 22:21:28 CET 2012
eclipse.preferences.version=1
formatter_profile=org.eclipse.jdt.ui.default.sun_profile
formatter_settings_version=12

@ -1,3 +0,0 @@
#Thu Mar 01 22:15:48 CET 2012
eclipse.preferences.version=1
org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false

@ -1,504 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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!

@ -1,133 +1,75 @@
2005-10-14 Jochen Hoenicke <jochen@gnu.org> 2007-07-31 Jochen Hoenicke <hoenicke@gmail.com>
* jode/bytecode/ClassInfo.java.in:
(readAttribute): Never read in known attributes as unknown
attributes. This could happen before when class was first read with
known info and then again with all info.
* jode/bytecode/MethodInfo.java: (readAttribute): Likewise.
* jode/bytecode/FieldInfo.java: (readAttribute): Likewise.
* jode/bytecode/BytecodeInfo.java.in: (readAttribute): Likewise.
* jode/obfuscator/ClassIdentifier.java: (doTransformation): Remove
all unknown attributes. They may contain references to
nonexisting constant pool entries.
* jode/obfuscator/PackageIdentifier.java: (loadClass): Fix a
compile time bug in the last patch.
* src/net/sf/jode/flow/TransformConstructor.java:
(lookForConstructorCall) Check for isStatic before setting
outer $this reference
(reported by Andreas Salathé, bug #1306688)
2005-09-13 Jochen Hoenicke <jochen@gnu.org> 2005-09-13 Jochen Hoenicke <jochen@gnu.org>
Check for NullPointer in SyntheticAnalyzer. Based on Check for NullPointer in SyntheticAnalyzer. Based on
patch suggessted by Peter Klauser (klp at users.sf.net). patch suggessted by Peter Klauser (klp at users.sf.net).
* src/net/sf/jode/jvm/SyntheticAnalyzer.java: * jode/jvm/SyntheticAnalyzer.java.in:
(checkStaticAccess): Check refField for null pointer. (checkStaticAccess): Check refField for null pointer.
(checkAccess): Likewise. (checkAccess): Likewise.
2004-08-06 Jochen Hoenicke <hoenicke@marge.Informatik.Uni-Oldenburg.DE> 2005-09-13 Jochen Hoenicke <jochen@gnu.org>
* src/net/sf/jode/bytecode/BinaryInfo.java (ACC_*): added
constants describing modifier attributes.
* src/net/sf/jode/bytecode/BasicBlockReader.java
(convertHandlers): remove empty handlers.
(readCode): merge adjacent try-blocks (splitted by javac-1.4
return rule).
* src/net/sf/jode/bytecode/FieldInfo.java (syntheticFlag):
removed, use modifier and ACC_SYNTHETIC (new in java 5) instead.
Changed all usages. When writing it currently writes out both
old and new synthetic format.
(getSignature): New method to return full generic signature.
* src/net/sf/jode/bytecode/MethodInfo.java
(syntheticFlag, getSignature): likewise.
* src/net/sf/jode/bytecode/ClassInfo.java (getSignature):
New method to return full generic signature.
* src/net/sf/jode/decompiler/MethodAnalyzer.java (skipWriting):
Skip java 5 bridge methods.
* src/net/sf/jode/expr/InvokeOperator.java (getClassAnalyzer):
Check for null callee.
* src/net/sf/jode/expr/FlowBlock.java (analyze): New order for
T1,T2 analysis: Do not do T1 analysis when the block has more
than one real successor and the next block can be easily merged.
See comment for more information.
2004-08-05 Jochen Hoenicke <hoenicke@marge.Informatik.Uni-Oldenburg.DE>
* build.xml: replace execon with apply.
* src/net/sf/jode/bytecode/ClassInfo.java (readAttributes):
read in signature attribute (not yet published, though).
* src/net/sf/jode/bytecode/MethodInfo.java (readAttributes):
likewise.
* src/net/sf/jode/bytecode/FieldInfo.java (readAttributes):
likewise.
* src/net/sf/jode/bytecode/ClassInfo.java (mergeModifiers):
only check the traditional modifiers for equality.
* src/net/sf/jode/bytecode/ConstantPool.java (getConstant):
Support for CLASS constants (jdk1.5) added.
* src/net/sf/jode/bytecode/BasicBlockReader.java (readCode):
opc_ldc, opc_ldc_w: Support for CLASS constants added.
* src/net/sf/jode/decompiler/Opcodes.java (addOpcode):
likewise.
* src/net/sf/jode/expr/InvokeOperator.java
(simplifyStringBuffer, simplifyString):
Also handle StringBuilder (jdk1.5).
* src/net/sf/jode/type/Type.java (tStringBuilder): new field.
* src/net/sf/jode/swingui/Main.java (main): handle debug
options.
2004-01-31 Jochen Hoenicke <hoenicke@informatik.uni-oldenburg.de>
* src/net/sf/jode/jvm/SyntheticAnalyzer.java (checkGetClass):
Handle jdk1.4 class$ methods.
* src/net/sf/jode/jvm/RuntimeEnvironment.java: Fixed some javadocs.
* src/net/sf/jode/flow/CompleteSynchronized.java: likewise.
* src/net/sf/jode/flow/CreateExpression.java: likewise.
* src/net/sf/jode/flow/CreateIfThenElseOperator.java: likewise.
Added changes (except obfuscator changes) from jode-1.1 tree up to
2001-07-08
* src/net/sf/jode/bytecode/ClassInfo.java (deprecatedFlag): Added
flag for deprecated classes. Stuart Ballard noticed that this was
missing.
(readAttribute): Read deprecated attribute.
(prepareWriting): Prepare deprecated attribute.
(writeKnownAttributes): Write deprecated attribute.
(isDeprected): New function.
(setDeprecated): Likewise.
* src/net/sf/jode/bytecode/BasicBlockReader.java (readCode): Fix
the exception handlers that javac 1.4 produces: I simply shorten
the start/end interval, so that the catcher is not in the end
interval.
* src/net/sf/jode/flow/CreateAssignExpression.java
(createAssignOp): Bug fix: Check whether store is already a
op-assign and break out.
* src/net/sf/jode/expr/StoreInstruction.java (isOpAssign): New
function to check whether this is an op-assign.
* src/net/sf/jode/flow/CatchBlock.java (combineLocal): Added more
checks if LocalStoreOperator is of the right form.
* net/sf/jode/flow/TransformConstructors.java (Constructor): Ignore
OuterValues for static constructor.
* src/net/sf/jode/expr/CompareToIntOperator.java (dumpExpression): Added patch from Peter Klauser (klp at users.sf.net), to support
Added a missing breakOp. packages with same name as classes in the obfuscator.
2004-01-22 Jochen Hoenicke <hoenicke@informatik.uni-oldenburg.de> * jode/obfuscator/PackageIdentifier.java.in
(loadedPackages): New field, to store packages in separate map.
(getIdentifier): Renamed to...
(getClassIdentifier): ...this (and return only ClassIdentifier).
(setLoadOnDemand): Use loadedPackages.
(loadClass): Likewise.
(loadMatchingClass): Likewise.
(readTable): Likewise.
(getChilds): Likewise.
(contains): Likewise.
* net/sf/jode/jvm/CodeVerifier.java (modelEffect): Allow assigning 2003-06-11 Jochen Hoenicke <jochen@gnu.org>
fields in an uninitialized class as some synthetic code does this.
2003-06-11 Mark Morschhäuser <mark.morschhaeuser@firemail.de> Added patch from Thomas Oldervoll, to continue decompiling
* net/sf/jode/decompiler/Main.java: New MenuItem to save a decompiled file. after errors.
* net/sf/jode/decompiler/Main.java: Main-window will be centered on startup * jode/decompiler/Main.java (decompileClass): Handle all
exceptions and add names of failed classes to a vector.
(printSummary): New function.
(main): call printSummary.
* build.xml: 2002-11-24 Jochen Hoenicke <jochen@gnu.org>
(release): Added MANIFEST.MF to target and enabled compressed jar-file
* MANIFEST.MF: Added this file to be able to create an executable jar-file * jode/bytecode/ClassInfo.java.in (deprecatedFlag): Added flag
for deprecated classes. Stuart Ballard noticed that this was
missing.
(readAttribute): Read deprecated attribute.
(prepareWriting): Prepare deprecated attribute.
(writeKnownAttributes): Write deprecated attribute.
(isDeprected): New function.
(setDeprecated): Likewise.
2002-06-11 Jochen Hoenicke <jochen@gnu.org> 2002-06-11 Jochen Hoenicke <jochen@gnu.org>
* net/sf/jode/decompiler/Main.java: New option keep-alive. With * configure.in: Set version number to 1.1.2.
this option jode won't stop after an error but will continue with
the next class. * jode/bytecode/BytecodeInfo.java.in (readAttribute): Fix the
Patch suggested by Francis Devereux, francis at hc.eclipse.co.uk exception handlers that javac 1.4 produces: I simply shorten
the start/end interval, so that the catcher is not in the end
interval.
2002-02-25 Jochen Hoenicke <jochen@gnu.org> 2002-02-25 Jochen Hoenicke <jochen@gnu.org>
@ -135,44 +77,60 @@
maximum version anymore. Sun changes it with every release without maximum version anymore. Sun changes it with every release without
changing the bytecode format. changing the bytecode format.
2002-02-15 Jochen Hoenicke <jochen@gnu.org> 2002-02-08 Jochen Hoenicke <jochen@gnu.org>
* net/sf/jode/bytecode/BasicBlockReader.java: handle empty loops. * jode/obfuscator/ClassIdentifier.java.in (transformInnerClasses):
(IS_NULL): new constant to tag empty blocks. Bug fix: Added missing checks for STRIP_UNREACH.
(markReachableBlocks): check for empty loops.
(convertBlock): Handle empty blocks.
(convert): Handle IS_NULL.
* net/sf/jode/decompiler/MethodAnalyzer.java: * jode/obfuscator/MethodIdentifier.java.in (doTransformation):
(analyzeCode): handle empty blocks. Don't run analyzer on unreachable methods. Previously the
ConstantAnalyzer assumed that the instructions are not reachable
and removed them all leading to illegal bytecode.
2001-08-14 Jochen Hoenicke <jochen@gnu.org> 2002-02-01 Jochen Hoenicke <jochen@gnu.org>
* build.xml: test is default. * jode/flow/CreateAssignExpression.java (createAssignOp):
(release-javadoc): New target. Bug fix: Check whether store is already a op-assign and break out.
(release-src): Get from dir test only source files. * jode/expr/StoreInstruction.java (isOpAssign): New function to
(doc-javadoc): More parameters for nicer docu. check whether this is an op-assign.
2001-08-12 Jochen Hoenicke <jochen@gnu.org> 2002-01-16 Jochen Hoenicke <jochen@gnu.org>
Fixes by anonymous for SerializePreserver:
* jode/obfuscator/ClassIdentifier.java.in (isSerializable):
The Serializable interface lives in java.io not in java.lang.
* jode/obfuscator/modules/SerializePreserver.java.in:
This class needs to implement OptionHandler.
* net/sf/jode/bytecode/TypeSignature.java: 2001-11-29 Jochen Hoenicke <jochen@gnu.org>
(getArgumentSize): Renamed to ...
(getParameterSize): ... this. Changed all callers.
(skipType): Made private.
* net/sf/jode/jvm/CodeVerifier.java: * jode/obfuscator/modules/MultiIdentifierMatcher.java.in
(initInfo): Use TypeSignature.getParameterTypes instead of skipType. (getNextComponent): Fixed a (noncritical) bug, submitted
by Sergio (koker at users.sf.net).
2001-08-12 Jochen Hoenicke <jochen@gnu.org>
* net/sf/jode/jvm/SyntheticAnalyzer.java: * configure.in: Set version number to 1.1.1.
(checkGetClass): Be more lenient with the types, they are already
checked by the CodeVerifier. This is to support jdk-1.4.
* net/sf/jode/expr/InvokeOperator.java 2001-08-12 Jochen Hoenicke <jochen@gnu.org>
(dumpExpression): Fixed the check for null outerExpr.
* net/sf/jode/flow/FlowBlock.java: * jode/obfuscator/Identifier.java.in (writeTable): New boolean
parameter specifying if reversed or not.
* jode/obfuscator/ClassBundle.java.in: Renamed the table file
variables, added outTableFile.
(setOption): Support outtable, intable and outrevtable.
Support verbose.
(writeTable): Writes table unreversed.
(writeRevTable): New method, Writes table reversed.
* jode/obfuscator/Main.java.in: (rand): New variable.
* jode/obfuscator/PackageIdentifier.java.in: Use Main.rand instead
of definining its own random.
* jode/obfuscator/ClassIdentifier.java.in: likewise.
* jode/flow/FlowBlock.java.in:
(checkConsistent): Allow lastModified in a finally block. (checkConsistent): Allow lastModified in a finally block.
* net/sf/jode/flow/TransformExceptionHandlers.java: Reworked exception
* jode/flow/TransformExceptionHandlers.java.in: Reworked exception
handlers again. This time checked with javac 1.3, javac 1.1 and handlers again. This time checked with javac 1.3, javac 1.1 and
jikes. jikes.
(checkTryCatchOrder): New method that was previously part of (checkTryCatchOrder): New method that was previously part of
@ -199,160 +157,84 @@
(analyzeSpecialFinally): Simplified, after checking it only handles (analyzeSpecialFinally): Simplified, after checking it only handles
the jumps in the try part and then call mergeFinallyBlocks. the jumps in the try part and then call mergeFinallyBlocks.
2001-08-08 Jochen Hoenicke <jochen@gnu.org> 2001-08-10 Jochen Hoenicke <jochen@gnu.org>
More Documentation updates.
* build.xml: Release rules. * configure.in: Changed bash syntax to more compatible (but
* scripts/jcpp.pl: Don't make backups of original. slower) syntax. Fixes bug #448909.
* net/sf/jode/bytecode/BasicBlocks.java (setBlocks): Check that
successors are inside method. 2001-08-09 Jochen Hoenicke <jochen@gnu.org>
* net/sf/jode/bytecode/Block.java (getStackHeight): New Method.
* net/sf/jode/bytecode/ClassPath.java (Location): public class to * jode/expr/InvokeOperator.java.in (dumpExpression): Fixed the
model a component of the class path. Previously it was Path. check for null outerExpr.
(ClassPath): New constructors added that take Location objects.
* net/sf/jode/bytecode/ConstantPool.java (getClassName): Cache 2001-07-27 Jochen Hoenicke <jochen@gnu.org>
constants.
* net/sf/jode/bytecode/GrowableConstantPool.java: Made public. * jode/obfuscator/Main.java.in (stripping): Initialize to 0 which
(grow): Check that not too many constants are added. means strip nothing. This is necessary because there is no way
(reserveLongConstants): Removed (not used). to turn off stripping.
(copyConstant): Removed (not used).
* net/sf/jode/jvm/NewObject.java: Made package protected. 2001-07-11 Jochen Hoenicke <jochen@gnu.org>
* net/sf/jode/obfuscator/modules/RemovePopAnalyzer.java:
Big updates (almost rewrote from scratch). Still doesn't compile. * jode/flow/CatchBlock.java.in (combineLocal): Added more checks
if LocalStoreOperator is of the right form.
2001-08-05 Jochen Hoenicke <jochen@gnu.org>
2001-07-10 Jochen Hoenicke <jochen@gnu.org>
Documentation updates (INSTALL, javadoc).
Added JUnit Test cases. * jode/obfuscator/modules/SimpleAnalyzer.java.in:
* build.xml: Big update. Ported fix from ConstantAnalyzer:
* net/sf/jode/bytecode/BasicBlock.java: (canonizeReference): for interfaces call canonizeIfaceReference.
(updateMaxStackLocals): new method to calculate maxStack and (canonizeIfaceReference): new method.
maxLocals. * jode/obfuscator/modules/ConstantAnalyzer.java.in:
(setBlocks): fixed calculation of handlers, call updateMaxLocals. made sub class of SimpleAnalyzer.
* net/sf/jode/bytecode/BasicBlockReader.java: (canonizeReference): Removed, since its inherited.
(maxLocals, maxStack): new fields. (canonizeIfaceReference): dito.
(readCode): read maxStack/Locals into private fields.
(convert): check that maxStack/Locals match what we calculate. 2001-07-08 Jochen Hoenicke <jochen@gnu.org>
* net/sf/jode/bytecode/BinaryInfo.java:
(getKnownAttributeCount): renamed to... * jode/bytecode/BytecodeInfo.java.in (calculateMaxStack): Handle special case for empty method. Previous code would just crash.
(getAttributeCount): ... this, and also count internal attributes.
Made it protected. 2001-06-15 Jochen Hoenicke <jochen@gnu.org>
(readAttribute): made protected.
(drop): made protected. * jode/jvm/SyntheticAnalyzer.java.in:
(prepareAttributes): made protected. (checkGetClass): Ignore nop opcodes.
(writeKnownAttributes): removed. (checkStaticAccess): Likewise.
(writeAttributes): made protected, use getAttributeCount. (checkAccess): Likewise.
Changed policy: it doesn't call writeKnownAttribute, but instead (checkConstructorAccess): Likewise.
it expects sub classes to override this method.
(getAttributeSize): made protected, subclasses should override it. * jode/flow/TransformConstructors.java (Constructor): Ignore
Changed all subclasses to new policy. OuterValues for static constructor.
* net/sf/jode/bytecode/Block.java:
(lineNr): Removed, it wasn't used. * jode/expr/NewArrayOperator.java (dumpExpression): Added
(pop,push): Removed, replaced by ... a missing breakOp.
(maxpop,maxpush,delta): ... these, with slightly changed semantics. * jode/expr/CompareToIntOperator.java (dumpExpression): Likewise.
(stackHeight): New variable.
(Block): Default Constructor doesn't initialize fields now. 2001-06-01 Jochen Hoenicke <jochen@gnu.org>
(getCatchers): Renamed to ...
(getHandlers): ... this, changed all callers. * jode/obfuscator/modules/ConstantAnalyzer.java.in:
(initCode): Calculate maxpop, maxpush, delta correctly. (analyzeCode): set field listener of RuntimeEnvironment.
(getStackPopPush): Changed accordingly to new fields. (replaceWith): Fixed the invoke_xxx case.
(setCode): Removed debugging output for illegal contents.
* net/sf/jode/bytecode/Classes.java: Reworked handling of inner * jode/obfuscator/ConstantRuntimeEnvironment.java.in:
classes. (getField): add current field listener to field if constant value
(innerClasses): Field mustn't be null anymore when loaded. was used.
(setName): Update class in classpath. (currentFieldAnalyzer): New field.
* net/sf/jode/bytecode/ClassPath.java: (setFieldAnalyzer): New method.
(renameClassInfo): new function, should only used by ClassInfo.
* net/sf/jode/bytecode/ConstantPool.java: made public. 2001-05-27 Jochen Hoenicke <jochen@gnu.org>
(getUTF8,getRef,getClassType,getClassName): Don't allow the 0 index.
(iterateClassNames): New method.
* net/sf/jode/decompiler/Main.java:
(decompileClass): Catch ClassFormatExceptions and decompile
remaining classes.
* net/sf/jode/obfuscator/ClassIdentifier.java:
Updated handling of inner/extra classes to new ClassInfo behaviour.
(initSuperClasses): Load DECLARATION of super classes.
* net/sf/jode/obfuscator/PackageIdentifier.java:
Replace deprecated methods of ClassInfo with corresponding classpath
calls.
(loadMatchingClasses): Initialize packages loaded on demand if we
are initialize.
* net/sf/jode/obfuscator/modules/ConstantAnalyzer.java:
Now extends SimpleAnalyzer.
(canonizeIfaceRef): Removed; it is now inherited.
(canonizeRef): likewise.
Big updates to handle jsr correctly.
(handleOpcode): Moved method to BlockInfo.
* net/sf/jode/obfuscator/modules/SimpleAnalyzer.java:
(canonizeIfaceRef): New method, copied from ConstantAnalyzer.
(canonizeRef): call canonizeIfaceRef for interfaces.
* net/sf/jode/util/UnifyHash.java
(iterateHashCode): iterator now supports remove().
(remove): New method.
2001-07-30 Jochen Hoenicke <jochen@gnu.org>
Changed compilation procedure to ant.
2001-07-30 Jochen Hoenicke <jochen@gnu.org>
* jode/bytecode/BasicBlockReader.java: Fixed import of non
collection java.util classes.
* jode/bytecode/BasicBlockWriter.java: likewise.
2001-07-28 Jochen Hoenicke <jochen@gnu.org>
* jode/AssertError.java: removed, all uses are now replaced
by java.lang.InternalError.
* jode/Makefile.am: removed AssertError.java
* jode/bytecode/ClassInfo.java: reworked handling of inner
classes.
(extraClasses): removed, they are calculated automatically.
(hasInnerClassesAttr): new variable.
(readInnerClassesAttribute): Mark all classes in the constant
pool as having OUTERCLASS info filled. Don't handle extraClasses
specially.
(prepareWriting): Change for automatically generating outer
class info.
(getKnownAttributes): dito.
(writeKnownAttributes): dito.
(getExtraClasses): removed.
(setExtraClasses): removed.
* jode/bytecode/ClassAnalyzer.java (conflicts): load or guess
declarations of info before getting inner classes.
* jode/decompiler/TabbedPrintWriter.java (BreakPoint.endOp):
Set options correctly.
* jode/expr/InvokeOperator.java (getMethodInfo): load or guess
declarations before accessing methods.
* jode/flow/FlowBlock.java (resolveSomeJumps): When creating a
if-then-else move the jump from the then branch to the if, before
restarting analysis.
(doT1): handle the case when lastModified.jump is null. Throw
statements have no jump now.
* jode/jvm/SyntheticAnalyzer (checkAccess): Fix the detection for
PUTDUPSTATIC/FIELD.
* jode/type/ClassType.java (getCastHelper): More checks when
cast is not needed: interfaces and null pointer.
2001-07-15 Jochen Hoenicke <jochen@gnu.org>
* jode/decompiler/Decompiler.java (decompile): removed
setClassPath call. ClassInfo.forName() is no longer used.
* jode/decompiler/Main.java (decompile): likewise.
2001-07-15 Jochen Hoenicke <jochen@gnu.org>
Applied patches from 2001-05-26 of Jode 1.1 tree:
* configure.in: Set version to 1.1. * configure.in: Set version to 1.1.
* jode/swingui/Main.java (main): Also use bootclasspath if no * jode/decompiler/Main.java (main): Also use bootclasspath if no
classpath given. classpath given.
* jode/swingui/Main.java (main): Likewise.
* jode/decompiler/MethodAnalyzer.java (skipWriting): Don't skip * jode/decompiler/MethodAnalyzer.java.in (skipWriting): Don't skip
empty constructor that have a throws clause. empty constructor that have a throws clause.
* configure.in: Determine whether jdk1.1 resp. jdk1.2. Call jcpp * configure.in: Determine whether jdk1.1 resp. jdk1.2. Call jcpp
in config.status. in config.status.
* jode/expr/Expression.java (makeInitializer): Now takes the * jode/expr/Expression.java.in (makeInitializer): Now takes the
type of the initialization. Changed all callers. type of the initialization. Changed all callers.
* jode/expr/ConstantArrayOperator.java (makeInitializer): Check * jode/expr/ConstantArrayOperator.java (makeInitializer): Check
that type is our array type, otherwise we can't omit new Array[]. that type is our array type, otherwise we can't omit new Array[].
@ -366,11 +248,11 @@
int[]. int[].
(getSuperType): Likewise. (getSuperType): Likewise.
* jode/expr/FieldOperator.java (getFieldInfo): New function. * jode/expr/FieldOperator.java.in (getFieldInfo): New function.
(needsCast): A cast is also needed if the field is private or (needsCast): A cast is also needed if the field is private or
package scope and the current type can't access the field. package scope and the current type can't access the field.
* jode/expr/InvokeOperator.java (getMethodInfo): New function. * jode/expr/InvokeOperator.java.in (getMethodInfo): New function.
(needsCast): A cast is also needed if the method is private or (needsCast): A cast is also needed if the method is private or
package scope and the current type can't access the method. package scope and the current type can't access the method.
@ -385,27 +267,29 @@
attribute even when not all attributes should be read. They are attribute even when not all attributes should be read. They are
needed by TransformConstructors, see above. needed by TransformConstructors, see above.
2001-05-26 Jochen Hoenicke <jochen@gnu.org>
* jode/decompiler/TabbedPrintWriter.java (saveOps): Don't allow * jode/decompiler/TabbedPrintWriter.java (saveOps): Don't allow
line breaks in not completed expressions since implicit parentheses line breaks in not completed expressions since implicit parentheses
would destroy the syntax. No need to put line break option on stack. would destroy the syntax. No need to put line break option on stack.
(restoreOps): Adapted Stack format. (restoreOps): Adapted Stack format.
* jode/decompiler/ClassAnalyzer.java (dumpDeclaration): Moved * jode/decompiler/ClassAnalyzer.java.in (dumpDeclaration): Moved
Code from dumpSource here. Don't put a line break after closing Code from dumpSource here. Don't put a line break after closing
brace. brace.
(dumpSource): call dumpDeclaration and add a line break. (dumpSource): call dumpDeclaration and add a line break.
(dumpBlock): Moved dropInfo(ATTRIBS) here. (dumpBlock): Moved dropInfo(ATTRIBS) here.
* jode/decompiler/ClassAnalyzer.java (STRICTFP): New Constant. * jode/decompiler/ClassAnalyzer.java.in (STRICTFP): New Constant.
(isStrictFP): New function. (isStrictFP): New function.
(initialize): Set strictfp modifier if a constructor has it set. (initialize): Set strictfp modifier if a constructor has it set.
(dumpSource): Handle strictfp modifier. (dumpSource): Handle strictfp modifier.
* jode/decompiler/MethodAnalyzer.java (STRICTFP): New Constant. * jode/decompiler/MethodAnalyzer.java.in (STRICTFP): New Constant.
(isStrictFP): New function. (isStrictFP): New function.
(dumpSource): Handle strictfp modifier. (dumpSource): Handle strictfp modifier.
* jode/jvm/SyntheticAnalyzer.java (checkAccess): Check for a * jode/jvm/SyntheticAnalyzer.java.in (checkAccess): Check for a
special putfield access, where the set value is returned. Allow special putfield access, where the set value is returned. Allow
the modifier of field/method to be protected and the class to be the modifier of field/method to be protected and the class to be
a superclass. a superclass.
@ -413,19 +297,19 @@
(ACCESSDUPPUTFIELD): New Constant. (ACCESSDUPPUTFIELD): New Constant.
(ACCESSDUPPUTSTATIC): New Constant. (ACCESSDUPPUTSTATIC): New Constant.
* jode/expr/InvokeOperator.java (simplifyAccess): Handle new * jode/expr/InvokeOperator.java.in (simplifyAccess): Handle new
synthetics. synthetics.
* jode/flow/SpecialBlock.java (removePop): Remove pop also for * jode/flow/SpecialBlock.java (removePop): Remove pop also for
non void store instructions. non void store instructions.
* jode/decompiler/MethodAnalyzer.java (skipWriting): Also skip * jode/decompiler/MethodAnalyzer.java.in (skipWriting): Also skip
the new synthetics. the new synthetics.
* jode/decompiler/Main.java (main): Call System.exit() after * jode/decompiler/Main.java (main): Call System.exit() after
everything was compiled. everything was compiled.
* jode/flow/TransformExceptionHandlers.java (removeJSR): * jode/flow/TransformExceptionHandlers.java.in (removeJSR):
Renamed back from removeBadJSR (see patch from 2001-02-04). The Renamed back from removeBadJSR (see patch from 2001-02-04). The
checkAndRemove* functions mustn't change the successors while they checkAndRemove* functions mustn't change the successors while they
iterate over them. Instead of removing good jsr they mark them as iterate over them. Instead of removing good jsr they mark them as
@ -437,39 +321,98 @@
(setGood): New method. (setGood): New method.
(isGood): New method. (isGood): New method.
2001-07-15 Jochen Hoenicke <jochen@gnu.org> 2001-05-08 Jochen Hoenicke <jochen@gnu.org>
Applied patch from 2001-05-08 of Jode 1.1 tree:
* jode/jvm/CodeVerifier.java (doVerify): Don't check for * jode/jvm/CodeVerifier.java.in (doVerify): Don't check for
uninitialized objects in local or stack slots on backwards jump or uninitialized objects in local or stack slots on backwards jump or
exception blocks. Sun's jdk also doesn't check it, and I never exception blocks. Sun's jdk also doesn't check it, and I never
understood why it is necessary. But see JVM Spec 4.9.4. understood why it is necessary. But see JVM Spec 4.9.4.
2001-07-15 Jochen Hoenicke <jochen@gnu.org> 2001-05-02 Jochen Hoenicke <jochen@gnu.org>
Applied patch from 2001-05-02 of Jode 1.1 tree:
* jode/obfuscator/modules/ConstantAnalyzer.java (handleOpcode): * jode/obfuscator/modules/ConstantAnalyzer.java.in (handleOpcode):
Added divide by zero checks for opc_irem and opc_lrem. Added divide by zero checks for opc_irem and opc_lrem.
2001-07-15 Jochen Hoenicke <jochen@gnu.org> 2001-04-11 Jochen Hoenicke <jochen@gnu.org>
Applied patches from 2001-02-27 of Jode 1.1 tree:
* jode/type/ClassInterfacesType.java (keywords): Reworked keyword
list.
* jode/decompiler/OuterValues.java (implicitOuterClass): New field.
(isImplicitOuterClass): new Method.
(setImplicitOuterClass): new Method.
* jode/flow/TransformConstructors.java (checkAnonymousConstructor):
Check for implicitOuterClass, a new javac 1.3 construct.
* jode/expr/FieldOperator.java.in (dumpSource): Removed this
simplification nonesense. Now Outer.this is never printed as
this.
* jode/expr/InvokeOperator.java.in (dumpSource): Removed this
simplification nonesense. Now Outer.this is never printed as
this.
Handle implicitOuterClass.
2001-04-10 Jochen Hoenicke <jochen@gnu.org>
* jode/decompiler/Main.java (usage): Reworked usage message.
2001-04-09 Jochen Hoenicke <jochen@gnu.org>
* jode/bytecode/SearchPath.java: Bug fixes from Joe Bronkema:
(exists): Don't replace '/' with fileSeparator in original
filename; make a copy instead.
(getFile): likewise.
(isDirectory): likewise.
(listFiles): Reset fileNr when a new directory is read.
2001-02-28 Jochen Hoenicke <jochen@gnu.org>
* acinclude.m4 (JODE_CHECK_CLASS): Changed "test -e" to "-f" since * acinclude.m4 (JODE_CHECK_CLASS): Changed "test -e" to "-f" since
-e is not supported on all architectures (Solaris) and -f is more -e is not supported on all architectures (Solaris) and -f is more
correct anyway. correct anyway.
Reported by Erik Modén. Reported by Erik Modén.
* jode/swingui/Main.java (AreaWriter): Convert all kinds of 2001-02-27 Jochen Hoenicke <jochen@gnu.org>
* jode/swingui/Main.java.in (AreaWriter): Convert all kinds of
line breaks (CR+LF, CR, LF) to a LF character, which a JTextArea line breaks (CR+LF, CR, LF) to a LF character, which a JTextArea
understands. understands.
2001-07-15 Jochen Hoenicke <jochen@gnu.org> 2001-02-08 Jochen Hoenicke <jochen@gnu.org>
Applied patch from 2001-02-04 of Jode 1.1 tree:
* jode/expr/StoreInstruction.java (dumpExpression): Java doesn't
allow parenthesis around left hand side, so use NO_PAREN and don't
call lhs.dumpExpression() with priority.
* jode/expr/PrePostFixOperator.java (dumpExpression): likewise.
* jode/expr/IIncOperator.java (dumpExpression): likewise.
* jode/flow/TransformConstructors.java (jikesAnonInner): Removed,
since it wasn't used: This information is stored in OuterValues
now.
* jode/decompiler/LocalInfo.java (isConstant): Always return true
so that inner classes that access final locals work, even if we
can't decide that the local can be final.
2001-02-05 Jochen Hoenicke <jochen@gnu.org>
* jode/expr/InvokeOperator.java.in (Environment.invokeMethod):
Fixed the call to ClassInfo.forName: it expects a class name, while
ref.getClazz() gives a type signature.
* jode/flow/TransformExceptionHandlers.java.in:
(checkAndRemoveJSR): Only invoke removeBadJSR, if there are
successors left.
(checkAndRemoveMonitorExit): likewise.
2001-02-04 Jochen Hoenicke <jochen@gnu.org>
* jode/expr/IfThenElseOperator.java (simplify): Allow in the class$ * jode/expr/IfThenElseOperator.java (simplify): Allow in the class$
simplification the then and else part to be swapped. simplification the then and else part to be swapped.
* jode/type/ClassType.java (keywords): Added the package * jode/bytecode/ClassInfo.java.in (read): Accept class version
1.3.
* jode/type/ClassInterfacesType.java (keywords): Added the package
and import keywords. and import keywords.
* jode/flow/TransformExceptionHandlers.java.in:
* jode/flow/TransformExceptionHandlers.java:
(getPredecessor): New function. (getPredecessor): New function.
(getMonitorExitSlot): New function. (getMonitorExitSlot): New function.
(skipFinExitChain): New function. (skipFinExitChain): New function.
@ -481,79 +424,98 @@
skipFinExitChain, then checks and remove the first JSR skipFinExitChain, then checks and remove the first JSR
resp. monitorexit. JSR jumps are simply ignored now. resp. monitorexit. JSR jumps are simply ignored now.
(checkAndRemoveMonitorExit): likewise. (checkAndRemoveMonitorExit): likewise.
* jode/flow/StructuredBlock.java (prependBlock): New function. * jode/flow/StructuredBlock.java.in (prependBlock): New function.
* jode/flow/CatchBlock.java (makeDeclaration): Generate name * jode/flow/CatchBlock.java.in (makeDeclaration): Generate name
of dummyLocal, since nobody else will generate it. of dummyLocal, since nobody else will generate it.
* jode/bytecode/BasicBlockReader.java (readCode): Remove bogus 2001-02-03 Jochen Hoenicke <jochen@gnu.org>
* jode/bytecode/BytecodeInfo.java.in (read): Remove bogus
exceptionHandlers, whose catchers just throw the exception again. exceptionHandlers, whose catchers just throw the exception again.
This kind of entries are inserted by an obfuscator and would break This kind of entries are inserted by an obfuscator and would break
JODE. JODE.
* jode/util/UnifyHash.java (iterateHashCode): Call cleanUp, * jode/util/UnifyHash.java.in (iterateHashCode): Call cleanUp,
to clean unneeded references. to clean unneeded references.
* jode/flow/TransformConstructors.java (transformOneField): * jode/flow/TransformConstructors.java (transformOneField):
Changed to private. Take field number as parameter. Check that Changed to private. Take field number as parameter. Check that
expression doesn't contain a FieldOperator for a later field of expression doesn't contain a FieldOperator for a later field of
the same class or a PutFieldOperator. Changed all callers. the same class or a PutFieldOperator. Changed all callers.
2001-07-15 Jochen Hoenicke <jochen@gnu.org> 2001-02-01 Jochen Hoenicke <jochen@gnu.org>
Applied patch from 2001-02-01 of Jode 1.1 tree: * jode/jvm/CodeVerifier.java.in (Type.mergeType): If array elem
* jode/jvm/CodeVerifier.java (Type.mergeType): If array elem
types can't be merged, return tObject as common super type. types can't be merged, return tObject as common super type.
* jode/type/ArrayType.java (getGeneralizedType): If array elem * jode/type/ArrayType.java (getGeneralizedType): If array elem
type can't be intersected, return tObject as common super type. type can't be intersected, return tObject as common super type.
* jode/flow/TransformExceptionHandlers.java.in: Merged patch from
the 1.2 tree to support javac 1.3 synchronized blocks. Doesn't
handle nested synchronized blocks yet.
* jode/expr/Expression.java.in (dumpExpression): Show runtime
exception when it occurs.
2001-07-15 Jochen Hoenicke <jochen@gnu.org> 2001-01-31 Jochen Hoenicke <jochen@gnu.org>
Applied patch from Jode 1.1 tree:
* jode/expr/Expression.java (updateParentTypes): Call setType, * jode/expr/Expression.java.in (updateParentTypes): Call setType,
instead of merging the types. Other childs want to know about the instead of merging the types. Other childs want to know about the
type change as well. type change as well.
* jode/decompiler/LocalInfo.java (combineWith): Reorganized a bit, * jode/decompiler/LocalInfo.java (combineWith): Reorganized a bit,
but no changes. but no changes.
* jode/expr/InvokeOperator.java (dumpExpression): Always print * jode/expr/InvokeOperator.java.in (dumpExpression): Always print
the ThisOperator if a field is from a parent class of an outer the ThisOperator if a field is from a parent class of an outer
class is used. And always qualify the this operator if not class is used. And always qualify the this operator if not
innermost. innermost.
2001-07-14 Jochen Hoenicke <jochen@gnu.org> 2001-01-30 Jochen Hoenicke <jochen@gnu.org>
Applied patches from the Jode 1.1 tree:
* jode/jvm/SyntheticAnalyzer.java.in (checkConstructorAccess):
Allow the special unifyParam to be the last parameter.
2001-01-30 Jochen Hoenicke <jochen@gnu.org>
* jode/decompiler/TabbedPrintWriter.java: Better gnu style handling: * jode/decompiler/TabbedPrintWriter.java: Better gnu style handling:
(openBraceClass) (closeBraceClass) (openBraceClass) (closeBraceClass)
(openBraceNoIndent) (closeBraceNoIndent): new functions. (openBraceNoIndent) (closeBraceNoIndent): new functions.
(closeBraceNoSpace): Removed. (closeBraceNoSpace): Removed.
* jode/decompiler/TabbedPrintWriter.java (GNU_SPACING): new constant. * jode/decompiler/Options.java (GNU_SPACING): new constant.
(printOptionalSpace): Print space for GNU_SPACING. (GNU_STYLE): changed to include GNU_SPACING.
* jode/decompiler/Options.java (setOptions): changed gnu style * jode/decompiler/ClassAnalyzer.java.in (dumpSource): Use
to include GNU_SPACING.
* jode/decompiler/ClassAnalyzer.java (dumpSource): Use
open/closeBraceClass. open/closeBraceClass.
* jode/decompiler/MethodAnalyzer.java (dumpSource): Use * jode/decompiler/MethodAnalyzer.java.in (dumpSource): Use
open/closeBraceNoIndent. Call printOptionalSpace. open/closeBraceNoIndent. Insert a space for GNU_SPACING.
* jode/decompiler/InvokeOperator.java (dumpExpression): * jode/decompiler/InvokeOperator.java.in (dumpExpression): Insert
Call printOptionalSpace, use open/closeBraceClass for inner a space for GNU_SPACING, use open/closeBraceClass for inner
classes. classes.
* jode/decompiler/UnaryOperator.java (dumpExpression): Call * jode/decompiler/UnaryOperator.java (dumpExpression): Insert
printOptionalSpace. a space for GNU_SPACING.
2001-01-30 Jochen Hoenicke <jochen@gnu.org>
Added pascal style from Rolf Howarth <rolf@squarebox.co.uk> Added pascal style from Rolf Howarth <rolf@squarebox.co.uk>
* jode/decompiler/Options.java (PASCAL_STYLE): new constant.
(BRACE_FLUSH_LEFT): dito.
* jode/decompiler/Decompiler.java (setOption): detect pascal option. * jode/decompiler/Decompiler.java (setOption): detect pascal option.
* jode/decompiler/TabbedPrintWriter.java (BRACE_FLUSH_LEFT): * jode/decompiler/Main.java (main): dito.
new constant. * jode/decompiler/TabbedPrintWriter.java (openBrace,
(openBrace, openBraceContinue, closeBrace, closeBraceNoSpace, openBraceContinue, closeBrace, closeBraceNoSpace,
closeBraceContinue): handle flush left. closeBraceContinue): handle flush left.
2001-01-30 Jochen Hoenicke <jochen@gnu.org>
* jode/type/NullType.java (intersection): Removed, since the * jode/type/NullType.java (intersection): Removed, since the
version in ReferenceType is more correct. Before version in ReferenceType is more correct. Before
tNull.isOfType(tRange(X,tNull)) returned false, which lead to tNull.isOfType(tRange(X,tNull)) returned false, which lead to
incorrect behaviour in InvokeOperator.needsCast. incorrect behaviour in InvokeOperator.needsCast.
* jode/decompiler/FieldAnalyzer.java (dumpSource): Removed the * jode/decompiler/FieldAnalyzer.java.in (dumpSource): Removed the
"= null" hack for final fields; it was not correct, since the "= null" hack for final fields; it was not correct, since the
field could be initialized in a constructor. field could be initialized in a constructor.
* jode/decompiler/TabbedPrintWriter.java (BreakPoint.endOp): * jode/decompiler/TabbedPrintWriter.java (BreakPoint.endOp):
Simplified the code, copy options always from child. Simplified the code, copy options always from child.
* jode/jvm/SyntheticAnalyzer.java.in (unifyParam): new field.
(checkConstructorAccess): Allow the special Parameter, whose
purpose is to distinguish the wrapper from the real constructor
and give him a "$" in the type signature, to appear at every
position. It doesn't appear at position 1 for inner classes.
Store the position in unifyParam.
* jode/expr/InvokeOperator.java (isGetClass): Allow the method to * jode/expr/InvokeOperator.java (isGetClass): Allow the method to
be declared inside an outer class: We simply check if we can get be declared inside an outer class: We simply check if we can get
the method analyzer. the method analyzer.

@ -1,23 +1,45 @@
Before installing, make sure you have at least version 1.1 of the java Before installing, make sure you have at least version 1.1 of the java
developement kit installed. If you want to run this program you only developement kit installed. If you want to run this program you only
need the java runtime environment. Version 1.1 is quite old, I need the java runtime environment. Version 1.1 is quite old, I
recommend using Java 2 (jdk1.2 or above). You need perl if you want recommend using Java 2 (jdk1.2 or above).
to compile a 1.1 version.
This package was designed to use the GNU standard for configuration
This package was designed to use the ANT from the jakarta.apache.org and makefiles. To build and install do the following:
tools. I assume you have installed it correctly.
0). If you have downloaded the code from the CVS repository create
Take some time to edit config.props. There are a few options you need configure and Makefile.in with autoconf-2.13 and automake-1.4. Type
to take care of. (Unfortunately ant can't test for executables). "aclocal; automake -a; autoconf".
Now you are ready to invoke ant. There are many possible targets, here 1). You need a java development kit (at least version 1.1), some unix
are the most useful ones: tools and some java packages. Make sure that you have all java
packages that are needed in your classpath. This are gnu.getopt, and
all builds class files and documentation. if you have JDK 1.1 you also need the collection classes and swing for
build builds class files only (autodetects java version). 1.1. These packages are accessible from the following urls:
build-1.1 builds JDK1.1 class files.
doc builds documentation. CYGWIN (unix tools for win95/NT):
dist creates all release files. http://sourceware.cygnus.com/cygwin/
test does some self tests. You need to have junit installed for this.
clean cleans everything that doesn't belong to the source distribution. JDK 1.1:
cvsclean cleans everything that doesn't belong into the cvs repository. http://java.sun.com/products/jdk/1.1/index.htm
Collection classes and Swing for JDK 1.1:
http://java.sun.com/beans/infobus/#DOWNLOAD_COLLECTIONS
http://java.sun.com/products/jfc/index.html#download-swing
JDK 1.2:
http://java.sun.com/products/jdk/1.2/index.html
Getopt:
http://www.urbanophile.com/arenn/hacking/download.html#getopt
2). Run the "configure" script to configure the package. There are
various options you might want to pass to configure to control how the
package is built. "configure --help" will give a complete list.
Use the --with-jdk option to specify the install directory of the jdk.
If you have jikes, you should specify it with --with-jikes. You can
give a path to the directory where it resides, otherwise it is
searched in the path.
3). Type "make" to build the package.

@ -1,3 +0,0 @@
Manifest-Version: 1.0
Main-Class: net.sf.jode.swingui.Main
Created-By: SeeksTheMoon

@ -0,0 +1,5 @@
## Input file for automake to generate the Makefile.in used by configure
SUBDIRS = bin doc jode test
EXTRA_DIST = jcpp

@ -1,6 +1,5 @@
New in 1.2 New in 1.1.1
* New bytecode interface * Only bug fixes
* Faster and better flow analyzation
New in 1.1 New in 1.1
* break long lines * break long lines

@ -1,60 +1,31 @@
This is a list of features, that would be nice to have: This is a list of features, that would be nice to have:
Decompiler: Decompiler:
- BUG: public final static null fields aren't initialized (leads to compile error)
- outline inlined methods. - outline inlined methods.
- remove string decrypt method. - remove string decrypt method.
- remove synthetic methods if and only if all calls to them are resolved. - remove synthetic methods if and only if all calls to them are resolved.
- rename keywords to safe names.
~ handle try catch more thouroughly/safely.
~ decompile jode.jvm.Interpreter (hand optimized bytecode)
Obfuscator: Obfuscator:
- Detect Class.forName() calls with constant parameters and rename - flow obfuscation/optimization.
these constants. Detect class$ methods with constant parameters. - warn about Class.forName and list occurences.
Warn about all other occurences of Class.forName() - detect Class.forName on constant strings and handle appropriately.
- work around Class.forName, by creating a new version using a hash - work around Class.forName, by creating a new version using a hash
table that maps md5 sums of old names to obfuscated names. table that maps md5 sums of old names to obfuscated names.
This should be put into the constant analyzer. The simple
analyzer should only do the warnings.
- Transforming the class hierarchy, e.g. combining two totally
unrelated classes together into one class or make some class
to implement some interfaces, that it previously didn't.
- Doing flow obfuscation, i.e. do some tests, that one knows to
succeed always, and jump to funny position if the test fails.
The tests should use undecidable properties, so that a
deobfuscator cannot remove them again.
DeObfuscator: DeObfuscator:
- Deobfuscator should detect inner/anonymous classes and mark them - generate nice names:
as such. It should be possible with the renaming table to mark - classes: derive name from super.
inner classes as well. Inner classes are easy to detect; there - fields: derive name from type.
constructor has a special form. And the information is very - give synthetic methods the right attribute and name (e.g. class$)
useful for the decompiler. - detect inner classes and give suitable names.
This should be done with some generalize interface similar to (or
instead of) Transformer
- Deobfuscator should generate nicer names. This should be a
special Renamer. The renamer should analyze short methods and
call them getXXX, isXXX, setXXX if apropriate, detect synthetic
methods and similar. Class names should be derived from super
class or interface (e.g. Enumeration), fields should be derived
from their type, maybe also from their assignments.
One can build more renamer, each handles some special cases and
calls the next one, if it can't handle an identifier.
User Interface: User Interface:
- make a nice user interface: - make a nice user interface:
~ list classnames: toggable between class hierarchie/package hierarchie.
- list fields/method of selected class. - list fields/method of selected class.
- show decompilation of selected method. - show only decompilation of selected method.
- show usage of method/fields. - show usage of method/fields.
- syntax highlighting, hyper links etc. - syntax highlighting, hyper links etc.
(look at java.swing.JEditorPane or at Java Insight) (look at java.swing.JEditorPane or at Java Insight)
- as a first approximation use HTML code and a JHTMLPane
- visual obfuscation/deobfuscation (like klassmaster?, better?) - visual obfuscation/deobfuscation (like klassmaster?, better?)
Internal: Internal:

@ -0,0 +1,44 @@
dnl
dnl Add macros
dnl JODE_CHECK_JAVA
dnl
dnl JODE_CHECK_JAVA(path)
AC_DEFUN(JODE_CHECK_JAVA,
[
AC_PATH_PROG(JAVA, java, "", $1/bin:$1/jre/bin:$PATH)
AC_PATH_PROG(JAVAC, javac, "", $1/bin:$PATH)
AC_PATH_PROG(JAR, jar, "", $1/bin:$PATH)
for path in $1/lib $1/jre/lib $1/shared; do
for classlib in classes.zip rt.jar; do
AC_CHECK_FILES($path/$classlib,
[ CLASSLIB=$path/$classlib
break 3
], [ true ])
done
done
AC_SUBST(CLASSPATH)
AC_SUBST(CLASSLIB)
])
AC_DEFUN(JODE_CHECK_CLASS,
[
if (IFS=":"
clazz=`echo $1 | sed -e 's/\./\//g' -e 's/\(.*\)/\1.class/'`
myclasspath=$2;
for path in $myclasspath; do
if test -d $path; then
if test -f $path/$clazz; then
exit 0
fi
elif CLASS_CHECK $path $clazz ; then
exit 0
fi
done;
exit 1)
then
$3
else
$4
fi
])

@ -1,380 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Jakarta-Ant build file for jode, Copyright (C) 1999-2004 Jochen Hoenicke.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
$Id$
-->
<!DOCTYPE project PUBLIC "-//ANT//DTD project//EN" "project.dtd">
<project name="jode" default="test" basedir=".">
<!-- set global properties for this build -->
<property name="version" value="1.90-CVS"/>
<property name="build" value="${basedir}/build"/>
<property name="props" value="${basedir}/props"/>
<property name="doc" value="${basedir}/doc"/>
<property name="lib" value="${basedir}/lib"/>
<property name="src" value="${basedir}/src"/>
<property name="release" value="${basedir}/release"/>
<property name="distdir" value="${release}/jode-${version}"/>
<property name="scripts" value="${basedir}/scripts"/>
<property name="api.doc" value="${doc}/api"/>
<property name="test" value="${basedir}/test"/>
<property name="test.src" value="${test}/src"/>
<property name="test.build" value="${test}/build"/>
<property name="test.log" value="${test}/log"/>
<property name="jcpp" value="${scripts}/jcpp.pl"/>
<property name="versionfile" value="${src}/jode/GlobalOptions.java"/>
<property file="config.props"/>
<path id="project.classpath">
<pathelement path="${classpath}"/>
<fileset dir="lib" includes="*.jar"/>
</path>
<!-- ********* General targets ******* -->
<!-- compiles jode and creates its javadoc-files -->
<target name="all" depends="build,doc"/>
<!-- clean all -->
<target name="clean" depends="clean-jcpp,clean-build,clean-doc,clean-test"/>
<target name="cvsclean" depends="clean,clean-html,clean-release"/>
<!-- ********* jcpp targets ******* -->
<target name="check-jcpp" unless="perl.present">
<fail message="need perl to configure for JDK 1.1"/>
</target>
<target name="run-jcpp" depends="check-packages,check-jcpp">
<apply dir="." executable="perl" parallel="true">
<arg file="${jcpp}"/>
<arg value="-DJDK11"/>
<arg value="-DCOLLECTIONS=${collections.package}"/>
<arg value="-DCOLLECTIONEXTRA=${collections.package}"/>
<arg value="-DJAVAX_SWING=${swing.package}"/>
<fileset dir="${src}" includes="**/*.java"/>
</apply>
</target>
<target name="clean-jcpp" if="perl.present">
<apply dir="." executable="perl" parallel="true">
<arg file="${jcpp}"/>
<arg value="-DJDK12"/>
<arg value="-DCOLLECTIONS=java.util"/>
<arg value="-DCOLLECTIONEXTRA=java.lang"/>
<arg value="-DJAVAX_SWING=javax.swing"/>
<fileset dir="${src}" includes="**/*.java"/>
</apply>
</target>
<!-- ********* Check Environment ******* -->
<target name="check-jdk" unless="jdk1.1.forced">
<available property="jdk1.2+" classname="java.lang.ThreadLocal" />
<available property="jdk1.3+" classname="java.lang.StrictMath" />
</target>
<target name="fail-getopt" unless="getopt.present">
<fail message="Package gnu.getopt not found!"/>
</target>
<target name="check-getopt">
<available property="getopt.present"
classname="gnu.getopt.Getopt"
classpathref="project.classpath" />
<antcall target="fail-getopt"/>
</target>
<target name="check-packages">
<available property="collections.package"
value="gnu.java.util.collections"
classname="gnu.java.util.collections.Set"
classpathref="project.classpath" />
<available property="collections.package"
value="org.gnu.java.util.collections"
classname="org.gnu.java.util.collections.Set"
classpathref="project.classpath" />
<available property="collections.package"
value="com.sun.java.util.collections"
classname="com.sun.java.util.collections.Set"
classpathref="project.classpath" />
<available property="swing.package" value="com.sun.java.swing"
classname="com.sun.java.swing.JFrame"
classpathref="project.classpath" />
<available property="swing.package" value="javax.swing"
classname="javax.swing.JFrame"
classpathref="project.classpath" />
</target>
<!-- ********* Build targets ******* -->
<target name="preconfig" depends="check-jdk,check-getopt,preconfig.11"/>
<target name="preconfig.11" unless="jdk1.2+">
<antcall target="run-jcpp"/>
</target>
<target name="preconfig.12" if="jdk1.2+">
<antcall target="clean-jcpp"/>
</target>
<target name="build-1.1">
<antcall target="build">
<param name="jdk1.1.forced" value="on"/>
</antcall>
</target>
<target name="build" depends="check-jdk,preconfig">
<mkdir dir="${build}"/>
<javac srcdir="${src}"
destdir="${build}"
debug="true"
classpathref="project.classpath"
deprecation="on">
<exclude name="net/sf/jode/obfuscator/modules/RemovePopAnalyzer.java"/>
<exclude name="net/sf/jode/obfuscator/modules/LocalOptimizer.java"/>
<exclude name="net/sf/jode/obfuscator/modules/LocalizeFieldTransformer.java"/>
<!--
<exclude name="net/sf/jode/bytecode/*Subroutine*" />
-->
</javac>
</target>
<!-- clean the class files -->
<target name="clean-build">
<delete dir="${build}"/>
</target>
<!-- ********* Create Release files ******* -->
<target name="release" depends="release-bin,release-bin11,release-src,release-javadoc"/>
<target name="release-bindist" depends="build">
<jar jarfile="${distdir}/jode.jar" compress="true" manifest="${basedir}/MANIFEST.MF">
<fileset dir="${build}" includes="**/*.class"/>
<fileset dir="${props}" includes="**/*.properties"/>
</jar>
<copy todir="${distdir}">
<fileset dir="${lib}">
<include name="*getopt*.jar" />
<include name="*collection*.jar" unless="jdk1.2+" />
</fileset>
<fileset dir="${basedir}"
includes="AUTHORS,COPYING,NEWS,README,THANKS,TODO">
<include name="doc/*.html" />
<include name="doc/*.gif" />
<include name="doc/*.jos" />
<include name="doc/*.perl" />
</fileset>
</copy>
</target>
<target name="release-bin" depends="doc-html">
<antcall target="clean"/>
<mkdir dir="${release}"/>
<mkdir dir="${distdir}"/>
<antcall target="release-bindist"/>
<jar jarfile="${release}/jode-${version}.jar"
basedir="${release}" includes="jode-${version}/**"/>
<delete dir="${distdir}"/>
<antcall target="clean"/>
</target>
<target name="release-bin11" depends="doc-html">
<antcall target="clean"/>
<mkdir dir="${release}"/>
<mkdir dir="${distdir}"/>
<antcall target="release-bindist">
<param name="jdk1.1.forced" value="on"/>
</antcall>
<jar jarfile="${release}/jode-${version}-JDK1.1.jar"
basedir="${release}" includes="jode-${version}/**"/>
<delete dir="${distdir}"/>
<antcall target="clean"/>
</target>
<target name="release-src" depends="doc-html">
<antcall target="clean"/>
<mkdir dir="${release}"/>
<mkdir dir="${distdir}"/>
<copy todir="${distdir}">
<fileset dir="${basedir}"
includes="AUTHORS,COPYING,INSTALL,NEWS,README,THANKS,TODO,ChangeLog">
<include name="build.xml,config.props,project*.dtd"/>
<include name="doc/**"/>
<include name="scripts/**"/>
<include name="src/**"/>
<include name="test/*.java"/>
<include name="test/*.j"/>
<include name="test/src/**"/>
<include name="props/**"/>
<include name="lib/**"/>
</fileset>
</copy>
<jar jarfile="${release}/jode-${version}-src.jar"
basedir="${release}" includes="jode-${version}/**"/>
<delete dir="${distdir}"/>
</target>
<target name="release-javadoc">
<antcall target="doc-javadoc"/>
<mkdir dir="${release}"/>
<jar jarfile="${release}/jode-${version}-API.jar"
basedir="${doc}" includes="api/**"/>
<antcall target="clean-doc"/>
</target>
<target name="clean-release">
<delete dir="${release}"/>
</target>
<!-- ********* Javadoc targets ********** -->
<target name="doc" depends="doc-javadoc,doc-html"/>
<target name="doc-html" if="htp.present">
<apply executable="htp" dir="${doc}" dest="${doc}" parallel="false" relative="yes">
<arg value="-NODEPEND" />
<srcfile />
<targetfile />
<fileset dir="${doc}" includes="*.htp"/>
<mapper type="glob" from="*.htp" to="*.html"/>
</apply>
</target>
<target name="doc-javadoc">
<tstamp>
<format property="date" pattern="MMM d, yyyy"/>
</tstamp>
<mkdir dir="${api.doc}"/>
<javadoc packagenames="net.sf.jode.*"
windowtitle="Jode ${version} API Specification"
header='&lt;b&gt;&lt;a href="http://jode.sourceforge.net/"&gt;Jode&lt;/a&gt; ${version}&lt;/b&gt;&lt;br&gt;&lt;font size="-2"&gt;Build ${date}&lt;/font&gt;'
overview="${src}/net/sf/jode/overview.html"
bottom='Copyright &amp;copy; 1998-2004 by Jochen Hoenicke.'
sourcepath="${src}"
destdir="${api.doc}"
use="yes">
<link offline="${javadoc.offline}"
href="${javadoc.href}"
packagelistLoc="${javadoc.packagelistLoc}"/>
</javadoc>
</target>
<target name="clean-doc">
<delete dir="${api.doc}"/>
</target>
<target name="clean-html">
<delete>
<fileset dir="${doc}" includes="*.html"/>
</delete>
</target>
<!-- ********* test targets ************* -->
<target name="build-test" depends="build">
<mkdir dir="${test.build}"/>
<javac srcdir="${test.src}"
destdir="${test.build}"
debug="true"
classpathref="project.classpath"
classpath="${build}"
deprecation="on">
</javac>
</target>
<target name="test" depends="build-test">
<mkdir dir="${test.log}"/>
<junit printsummary="yes" fork="yes" haltonfailure="yes">
<classpath>
<pathelement path="${test.build}"/>
<pathelement path="${build}"/>
<path refid="project.classpath"/>
</classpath>
<formatter type="plain" />
<batchtest fork="no" todir="${test.log}">
<fileset dir="${test.src}">
<include name="**/*.java"/>
</fileset>
</batchtest>
</junit>
</target>
<target name="test-cvs" depends="build-test">
<mkdir dir="${test.log}"/>
<junit printsummary="yes" fork="yes" haltonfailure="yes">
<classpath>
<pathelement path="${test.build}"/>
<pathelement path="${build}"/>
<fileset dir="lib" includes="*.jar"/>
<fileset dir="/usr/local/ant/lib" includes="*.jar"/>
</classpath>
<formatter type="plain" />
<batchtest fork="no" todir="${test.log}">
<fileset dir="${test.src}">
<include name="**/*.java"/>
</fileset>
</batchtest>
</junit>
</target>
<target name="clean-test">
<delete dir="${test.build}"/>
<delete dir="${test.log}"/>
</target>
<!-- ********* version targets ************* -->
<target name="setversion" if="version">
<echo message="updating version in ${versionfile} ..."/>
<exec executable="perl">
<arg value="-i"/>
<arg value="-pe"/>
<arg value='s/(String\s*version\s*=\s*")[^"]*/$1${version}/' />
<arg value="${versionfile}"/>
</exec>
</target>
<target name="commit" depends="setversion,test-cvs" if="version">
<antcall target="cvsclean"/>
<echo message="---------------------------------------------------"/>
<echo message=' Commiting new Jode version: ${version} !!!'/>
<echo message="==================================================="/>
<!--
search the old version information and replace it with the new version
we will search the $(mainclass) for 'String VERSION = "..."' and
replace the contents of the String with the new version.
-->
<!-- commit the new $(VERSIONFILE) to the CVS
<echo message="commiting updated file to CVS..."/>
<cvs command='ci -m"new version ${version}" ${versionfile}'/>
-->
<!-- commit the new $(VERSIONFILE) to the CVS
<echo message="tagging files in CVS..."/>
<property
<cvs command="tag ${cvstag}"/>
-->
<echo message="...done!"/>
<echo message="---------------------------------------------------"/>
</target>
</project>

@ -1,28 +0,0 @@
# Do you have online access for generating javadoc?
# If not, where are your local files.
javadoc.offline=false
javadoc.packagelistLoc=
javadoc.href=http://java.sun.com/products/jdk/1.2/docs/api/
#javadoc.href=file:/usr/doc/inet/java/jdk1.2/docs/api
#javadoc.offline=true
#javadoc.packagelistLoc=/usr/doc/inet/java/jdk1.2/docs/api
# Is Perl installed on your system?
#
# perl is needed to reconfigure Jode for JDK-1.1. If you haven't
# installed it you can only configure for JDK-1.2 and you should
# comment out the next line.
#
# perl is also used for things that are relevant for the maintainer.
#
# Remove the next line if perl is not installed.
perl.present=true
# Is HTP installed on your system?
#
# htp is needed to generate html files from htp files.
# see http://htp.sourceforge.net/
#
# Remove the next line if either htp is not installed.
htp.present=true

@ -0,0 +1,244 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT()
AM_INIT_AUTOMAKE(jode, 1.1.2)
dnl Checks for programs.
AC_PROG_MAKE_SET
AC_PATH_PROG(PERL, perl)
AC_PATH_PROG(CYGPATH, cygpath)
dnl Checks for libraries.
dnl Checks for header files.
dnl Checks for typedefs, structures, and compiler characteristics.
dnl Checks for library functions.
dnl hack to quote Makefile lines
QUOTE=""
AC_SUBST(QUOTE)
AC_SUBST(SHELL)
if test -n "$CYGPATH"; then
# Extra check if java take windows path?
CLASSPATH=`$CYGPATH --unix --path $CLASSPATH`
SUBSTCP="$CYGPATH --windows --path"
else
SUBSTCP="echo"
fi
AC_SUBST(SUBSTCP)
AC_ARG_WITH(java,
[ --with-java specify path to a java-like program ],
[
if test ${withval} != "" || test ${withval} != "yes" || test ${withval} != "no"; then
# set javac to user input value
JODE_CHECK_JAVA(${withval})
else
JODE_CHECK_JAVA(/usr/lib/java)
fi
],
[
JODE_CHECK_JAVA(/usr/lib/java)
])
dnl jikes can also handle dependancies.
AC_ARG_WITH(jikes,
[ --with-jikes specify location of jikes ],
[
USER_SPECIFIED_JIKES=true
if test "${withval}" = "yes" || test "${withval}" = ""; then
AC_PATH_PROG(JIKES, jikes, "", $PATH)
else
echo "searching jikes in ${withval}:$PATH"
AC_PATH_PROG(JIKES, jikes, "", ${withval}:$PATH)
fi
if test -n "$JIKES"; then
JAVAC=$JIKES
fi
],
[
USER_SPECIFIED_JIKES=
AC_PATH_PROG(JIKES, jikes, "", $PATH)
])
AM_CONDITIONAL(HAVE_JIKES, test x"$JIKES" != x)
AC_ARG_WITH(javac,
[ --with-javac specify location of javac ],
[
if test x$USER_SPECIFIED_JIKES = xtrue; then
AC_MSG_ERROR(You must only give one option --with-javac or --with-jikes)
fi
if test ${withval} != "" || test ${withval} != "yes" || test ${withval} != "no"; then
AC_CHECK_FILES(${withval},
[ JAVAC=${withval} ],
[ AC_MSG_ERROR(${withval} does not exists) ])
fi
], [
dnl use jikes as default, if javac option not given.
if test -n "$JIKES"; then
JAVAC=$JIKES
fi
])
AC_PATH_PROG(UNZIP, unzip)
if test -n "$UNZIP"; then
CLASS_CHECK () {
$UNZIP -v -C `$SUBSTCP $1` $2 >/dev/null 2>&1
}
else
if test -n "$JAR"; then
CLASS_CHECK () {
$JAR -tf `$SUBSTCP $1` 2>&1 | grep $2 >/dev/null
}
else
AC_MSG_ERROR(You need either unzip or jar.)
fi
fi
AC_MSG_CHECKING(for java.lang.Object)
JODE_CHECK_CLASS(java.lang.Object, $CLASSLIB,
[ AC_MSG_RESULT(yes) ],
[ AC_MSG_RESULT(no)
AC_MSG_ERROR(Please specify location of core java class library) ])
AC_MSG_CHECKING(for java.lang.ref.WeakReference)
JODE_CHECK_CLASS(java.lang.ref.WeakReference, $CLASSLIB,
[ AC_MSG_RESULT(yes)
JCPPFLAGS="-DJDK12" ],
[ AC_MSG_RESULT(no)
JCPPFLAGS="-DJDK11" ])
AC_MSG_CHECKING(for collection classes)
JODE_CHECK_CLASS(java.util.Set, $CLASSPATH:$CLASSLIB,
[ COLLECTIONS="java.util"
COLLECTIONEXTRA="java.lang" ],
[ JODE_CHECK_CLASS(gnu.java.util.collections.Set, $CLASSPATH:$CLASSLIB,
[ COLLECTIONS="gnu.java.util.collections"
COLLECTIONEXTRA="gnu.java.util.collections" ],
[ JODE_CHECK_CLASS(com.sun.java.util.collections.Set, $CLASSPATH:$CLASSLIB,
[ COLLECTIONS="com.sun.java.util.collections"
COLLECTIONEXTRA="com.sun.java.util.collections" ],
[ AC_MSG_RESULT(no)
AC_MSG_ERROR(You need the Java 1.2 collection classes in your classpath)
])
])
])
AC_MSG_RESULT($COLLECTIONS)
AC_SUBST(COLLECTIONS)
AC_SUBST(COLLECTIONEXTRA)
AC_MSG_CHECKING(for gnu.getopt)
JODE_CHECK_CLASS(gnu.getopt.Getopt, $CLASSPATH:$CLASSLIB,
[ AC_MSG_RESULT(yes) ],
[ AC_MSG_RESULT(no)
AC_MSG_ERROR(You need gnu getopt for java.) ])
AC_MSG_CHECKING(for swing)
JODE_CHECK_CLASS(javax.swing.JFrame, $CLASSPATH:$CLASSLIB,
[ JAVAX_SWING="javax.swing" ],
[ JODE_CHECK_CLASS(com.sun.java.swing.JFrame, $CLASSPATH:$CLASSLIB,
[ JAVAX_SWING="com.sun.java.swing" ],
[ JAVAX_SWING="no" ]) ] )
AC_MSG_RESULT($JAVAX_SWING)
AC_SUBST(JAVAX_SWING)
if test "$JAVAX_SWING" != "no"; then
SWINGUI="swingui"
else
AC_MSG_WARN(Swing is not in classpath ... skipping swingui)
SWINGUI=""
fi
AC_SUBST(SWINGUI)
AC_SUBST(CLASSPATH)
AC_SUBST(JAVAC)
AC_OUTPUT(Makefile
javaDependencies.pl
jode/Makefile
jode/bytecode/Makefile
jode/decompiler/Makefile
jode/expr/Makefile
jode/flow/Makefile
jode/jvm/Makefile
jode/obfuscator/Makefile
jode/obfuscator/modules/Makefile
jode/swingui/Makefile
jode/type/Makefile
jode/util/Makefile
jode/bytecode/BinaryInfo.java
jode/bytecode/BytecodeInfo.java
jode/bytecode/ClassInfo.java
jode/bytecode/Reference.java
jode/decompiler/ImportHandler.java
jode/decompiler/ClassAnalyzer.java
jode/decompiler/FieldAnalyzer.java
jode/decompiler/MethodAnalyzer.java
jode/decompiler/DeadCodeAnalysis.java
jode/expr/Expression.java
jode/expr/FieldOperator.java
jode/expr/InvokeOperator.java
jode/expr/LocalVarOperator.java
jode/expr/Operator.java
jode/expr/CheckNullOperator.java
jode/flow/RetBlock.java
jode/flow/InstructionContainer.java
jode/flow/LoopBlock.java
jode/flow/SequentialBlock.java
jode/flow/SlotSet.java
jode/flow/StructuredBlock.java
jode/flow/SynchronizedBlock.java
jode/flow/VariableSet.java
jode/flow/CatchBlock.java
jode/flow/IfThenElseBlock.java
jode/flow/InstructionBlock.java
jode/flow/FlowBlock.java
jode/flow/TransformExceptionHandlers.java
jode/jvm/CodeVerifier.java
jode/jvm/Interpreter.java
jode/jvm/SyntheticAnalyzer.java
jode/obfuscator/ClassBundle.java
jode/obfuscator/ClassIdentifier.java
jode/obfuscator/ConstantRuntimeEnvironment.java
jode/obfuscator/FieldIdentifier.java
jode/obfuscator/Identifier.java
jode/obfuscator/LocalIdentifier.java
jode/obfuscator/Main.java
jode/obfuscator/MethodIdentifier.java
jode/obfuscator/OptionHandler.java
jode/obfuscator/PackageIdentifier.java
jode/obfuscator/Renamer.java
jode/obfuscator/ScriptParser.java
jode/obfuscator/TranslationTable.java
jode/obfuscator/modules/ConstantAnalyzer.java
jode/obfuscator/modules/KeywordRenamer.java
jode/obfuscator/modules/LocalOptimizer.java
jode/obfuscator/modules/LocalizeFieldTransformer.java
jode/obfuscator/modules/ModifierMatcher.java
jode/obfuscator/modules/MultiIdentifierMatcher.java
jode/obfuscator/modules/NameSwapper.java
jode/obfuscator/modules/RemovePopAnalyzer.java
jode/obfuscator/modules/SerializePreserver.java
jode/obfuscator/modules/SimpleAnalyzer.java
jode/obfuscator/modules/StrongRenamer.java
jode/obfuscator/modules/UniqueRenamer.java
jode/obfuscator/modules/WildCard.java
jode/swingui/Main.java
jode/swingui/PackagesTreeModel.java
jode/swingui/HierarchyTreeModel.java
jode/type/Type.java
jode/util/SimpleSet.java
jode/util/SimpleMap.java
jode/util/UnifyHash.java
jode/GlobalOptions.java
bin/Makefile
bin/jode
bin/jode.bat
doc/Makefile
test/Makefile,
[chmod 755 javaDependencies.pl bin/jode],
[$PERL $srcdir/jcpp $JCPPFLAGS \`ls \$CONFIG_FILES | grep '.java\$'\`])

@ -0,0 +1,46 @@
## Input file for automake to generate the Makefile.in used by configure
PHP_FILES = \
applet.php \
bluesky.php \
download.php \
faq.php \
feedback.php \
history.php \
index.php \
license.php \
links.php \
usage.php
HTML_FILES = $(PHP_FILES:%.php=$(srcdir)/%.html)
# noinst_DATA = $(HTML_FILES)
EXTRA_DIST = $(PHP_FILES) $(notdir $(HTML_FILES)) \
header.inc \
menu.inc \
footer.inc \
a-logo.gif \
myproject.jos \
dasm_to_java.perl \
gimp/jode-logo.xcf \
jode-logo.gif
.PHONY: public_html_symlink
# The following rules require that you have an apache with php on
# localhost with standard user public_html directories and
# FollowSymLink enabled.
PUBLIC_HTML=$(HOME)/public_html
JODE_PHP_DIR=jode_php
public_html_symlink:
rm -f $(PUBLIC_HTML)/$(JODE_PHP_DIR)
@RELDIR=`pwd | sed s!^$(HOME)!..!`; \
ln -sf $$RELDIR/$(srcdir) $(PUBLIC_HTML)/$(JODE_PHP_DIR); \
echo Created symlink to $$RELDIR/$(srcdir).
footer.inc: public_html_symlink
$(srcdir)/%.html: %.php footer.inc header.inc menu.inc
lynx -source http://localhost/~$(LOGNAME)/$(JODE_PHP_DIR)/$(notdir $<)?extension=html > $@

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

@ -1,25 +0,0 @@
<section title="The <i>JODE</i> Applet">
<p>Please be patience, loading the applet may take some time.</p>
<center>
<applet code="jode/Applet.class" archive="jode-applet.jar" width=540 height=400>
<param name=pagecolor value="ffffff">
<param name=classpath value="http://jode.sourceforge.net/plasma.jar">
<param name=class value="PlasmaApplet">
<p>Sorry you need a java enabled browser to test a java applet ;-)</p>
<p>Don't read the rest, it only contains information about the applet.</p>
</applet>
</center><br>
<p> Press the start button to decompile <a
href="http://www.informatik.uni-oldenburg.de/~mw/plasma.html">Michael's
Plasma applet</a> (and give the decompiler some time to download the
jar file). </p>
<p>You may change the classpath to point to a zip or jar file of your
choice. Unfortunately, your browser will most likely forbid URL's that
aren't located on jode.sourceforge.net.
Save probably doesn't work, because it is forbidden by your browser.</p>
</section>

@ -0,0 +1,38 @@
<?php require("header.inc"); ?>
<h1>The <i>JODE</i> Applet</h1>
<p>Please be patience, loading the applet may take some time.</p>
<center>
<applet code="jode/Applet.class" archive="jode-applet.jar" width=540 height=400>
<param name=pagecolor value="ffffff">
<param name=classpath value="http://jode.sourceforge.net/http/www.informatik.uni-oldenburg.de/~mw/plasma.jar">
<param name=class value="PlasmaApplet">
<p>Sorry you need a java enabled browser to test a java applet ;-)</p>
<p>Don't read the rest, it only contains information about the applet.</p>
</applet>
</center><br>
<p> Press the start button to decompile <a
href="http://www.informatik.uni-oldenburg.de/~mw/plasma.html">Michael's
Plasma applet</a> (and give the decompiler some time to download the
jar file). </p>
You may change the classpath to point to a zip or jar file of your
choice, using a similar syntax. Only http and ftp addresses are supported.
The file must be available from the world wide web. In fact you download it
from Sourceforge and Sourceforge gets it from the given address. This hack
is necessary, because Java's security policy doesn't allow applets to contact
a different server. You can also point the classpath to a directory containing
the class-files (include a slash `/' at the end in this case), but
this is not recommended, since it is <i>very</i> slow. You may give
multiple entries in the class path field separated by a comma.<br><br>
You can't use this applet for local files. You can try to give
local filenames directly without going through Sourceforge, but that is
probably forbidden by your browser. Most browser only allow loading
files from the same server as the applet, and this is the reason why
you have to use such a weird URL.<br><br>
Save probably doesn't work, because it is forbidden by your browser.<br><br>
<?php require("footer.inc"); ?>

@ -1,7 +1,10 @@
<section title="Wish List"> <?php require("header.inc"); ?>
<h1>Blue Sky</h1>
<p>This section contains features that I think would be great to have, <p>This section contains features that I think would be great to have,
but are also very hard to implement. </p> but are also very hard to implement. The name of the section is
inspired, by <a
href="http://www.mozilla.org/blue-sky/">Mozilla</a>.</p>
<p>Currently this are all my own ideas. But if you send me an idea <p>Currently this are all my own ideas. But if you send me an idea
for an interesting feature, I will add it to this list.</p> for an interesting feature, I will add it to this list.</p>
@ -26,18 +29,18 @@ method containing very much objects of the same type looks very
ugly. </p> ugly. </p>
<p>My plan is looking at the assignments. If we have locals in <p>My plan is looking at the assignments. If we have locals in
assignments</p> assignments
<pre> <pre>
int l_1 = array.length int l_1 = array.length
String l_2 = object.getName() String l_2 = object.getName()
</pre> </pre>
<p>we could name them "length" and "name". If we we could name them "length" and "name". If we
have assignments:</p> have assignments:
<pre> <pre>
MenuItem local_1 = new MenuItem("Open"); MenuItem local_1 = new MenuItem("Open");
MenuItem local_2 = new MenuItem("Save"); MenuItem local_2 = new MenuItem("Save");
</pre> </pre>
<p>good names would be <code>miOpen</code> and <code>miSave</code>. </p> good names would be <code>miOpen</code> and <code>miSave</code>. </p>
<p>It is currently possible to assign a <i>(hint name,type)</i> pair <p>It is currently possible to assign a <i>(hint name,type)</i> pair
to a local. If the type matches, the local will be named after to a local. If the type matches, the local will be named after
@ -66,4 +69,4 @@ copy them back into the java code. </p>
<p>This doesn't need to be built into the decompiler. A script that takes <p>This doesn't need to be built into the decompiler. A script that takes
the javadoc pages and the decompiled code can easily merge them.</p> the javadoc pages and the decompiled code can easily merge them.</p>
</section> <?php require("footer.inc"); ?>

@ -1,39 +0,0 @@
<section title="Download">
<p>Jode is available in the <sflink
href="project/showfiles.php">download area</a> in source or binary
form. For compiling the source code, you need several other packages,
check the <a href="links.html">links page</a>. You need a unix like
environment for compilation.</p>
<p>The simplest way to get it, especially for non unix users, is in
precompiled form, though. There are two jar archives in the download
area:</P>
<ul> <li>jode-1.1-JDK1.1.jar is for JDK&nbsp;1.1. If you want to use
the swing interface, you have to download swing separately, all other
packages are already included in the archive. </li>
<li>jode-1.1.jar is for JDK&nbsp;1.2 or better. It should run
without any other package.</li> </ul>
</section>
<section title="Subversion Repository">
<p>You can get the latest sources from the <a
href="http://sourceforge.net/svn/?group_id=3790">SVN repository</a>.
Follow the instruction on that page; use
<code>/svnroot/jode/trunk/jode</code> as last part of the URL. If you
want to checkout a specific version you can checkout the URL
<code>.../svnroot/jode/tags/xxx/jode</code>:</p>
<ul>
<li><code>../jode/tags/jode_1_0_93/jode</code>: checks out the version 1.0.93</li>
<li><code>../jode/branches/branch_1_1/jode</code>: checks out the latest version in the
1.1 series.</li> </ul>
<p>To build the sources from latest SVN change to the main directory and invoke ant.</p>
<p>To build the 1.1 versions of jode change to the main directory and run
<pre>aclocal && automake -a && autoconf</pre>
<p>Afterwards follow the instruction in the INSTALL file. </p>
</section>

@ -0,0 +1,40 @@
<?php require("header.inc"); ?>
<h1>Download</h1>
<p>Jode is available in the <?
sflink("project/showfiles.php")?>download area</a> in source or
binary form. For compiling the source code, you need several other
packages, check the <?php selflink("links") ?>links page</a>. You
need a unix like environment for compilation.</p>
<p>The simplest way to get it, especially for non unix users, is in
precompiled form, though. There are two jar archives in the download
area:</P>
<ul> <li>jode-1.1-JDK1.1.jar is for JDK&nbsp;1.1. If you want to use
the swing interface, you have to download swing separately, all other
packages are already included in the archive. </li>
<li>jode-1.1.jar is for JDK&nbsp;1.2 or better. It should run
without any other package.</li> </ul> </p>
<h1>CVS Repository</h1>
<p>You can get the latest sources from the <?php sflink("cvs/") ?> CVS
repository</a>. Follow the instruction on that page; use
<code>jode</code> as <i>modulename</i>. If you want to checkout a
specific version you can use the <code>-r</code> option:</p>
<ul>
<li><code>-r jode_1_0_93</code>: checks out the version 1.0.93</li>
<li><code>-r branch_1_1</code>: checks out the latest version in the
1.1 series.</li> </ul>
<p>To build the sources from CVS change to the main directory where
the <code>configure.in</code> file resides and run
<pre>aclocal && automake -a && autoconf</pre>
Afterwards follow the instruction in the INSTALL file. </p>
<?php require("footer.inc"); ?>

@ -1,26 +1,10 @@
<section title="FAQ - Frequently Asked Questions"> <?php require("header.inc"); ?>
This is a list of some questions that pop up from time to time.
</section>
<section title="Decompiler issues">
<h3>Does Jode support Java 5?</h3>
<p>It does not support generics/vararg method or the new for loop at
the moment. It produces readable code and I think it may even compile
again. But it is not compatible as the generics and varargs
information is not included.</p>
<h3>Jode crashes with ExceptionHandler order failed</h3> <h1>FAQ - Frequently Asked Questions</h1>
<p>Try jode-1.1.2pre1 or the latest CVS version. If it still does not This is a list of some questions that pop up from time to time.
work rewrite <code>jode.flow.TransformExceptionHandlers</code> and
send me the fix :) </p>
<p>Since Java 1.4 the format for finally and synchronized blocks <h2>Decompiler issues</h2>
changed again. It was always a very difficult task to reconstruct
<code>finally</code> blocks correctly and the code is huge and very
hard to maintain. With Java 5 it gets even worse.</p>
<h3>The decompiler crashes with a VerifyException, what can I do?</h3> <h3>The decompiler crashes with a VerifyException, what can I do?</h3>
@ -29,8 +13,7 @@ information about used classes. See the question about the
classpath.</p> classpath.</p>
<p>This could also be caused by malicious bytecode, or because there <p>This could also be caused by malicious bytecode, or because there
is a bug in Jode's verifier, or because Sun decided to change the is a bug in Jode's verifier.</p>
definition of correct bytecode, again.</p>
<h3>What should be included in the classpath?</h3> <h3>What should be included in the classpath?</h3>
@ -54,9 +37,7 @@ it.</p>
<p>You should decompile the outermost class (<code>MyClass</code> in <p>You should decompile the outermost class (<code>MyClass</code> in
this case). The produced code contains the inner class. </p> this case). The produced code contains the inner class. </p>
</section> <h2>Obfuscator issues</h2>
<section title="Obfuscator issues">
<h3>What should be included in the classpath?</h3> <h3>What should be included in the classpath?</h3>
@ -69,7 +50,7 @@ library even when you don't want to obfuscate it.</p>
is not what you want. This only makes sure the class won't be is not what you want. This only makes sure the class won't be
renamed, it doesn't prevent it from being stripped. Instead you renamed, it doesn't prevent it from being stripped. Instead you
should preserve methods and constructors. The constructor is just a should preserve methods and constructors. The constructor is just a
method with the special name <tt>&lt;init&gt;</tt>. </p> method with the special name <tt>&lt;init&rt;</tt>. </p>
<p> Another common mistake is to omit the type <p> Another common mistake is to omit the type
signature, e.g. to preserve <tt>Class.main</tt> instead of signature, e.g. to preserve <tt>Class.main</tt> instead of
@ -87,4 +68,5 @@ and methods of a class with their type signatures.</p>
<p> If you are interested in the format of type signatures read the <p> If you are interested in the format of type signatures read the
Java Virtual Machine Specification, Chapter 4.3 Descriptors</p> Java Virtual Machine Specification, Chapter 4.3 Descriptors</p>
</section> <?php require("footer.inc"); ?>

@ -1,106 +0,0 @@
/* XPM */
static char * favicon_xpm[] = {
"16 16 87 1",
" c None",
". c #C2C2C2",
"+ c #A1A1A1",
"@ c #BBBBBB",
"# c #D9D9D9",
"$ c #BABABA",
"% c #C1C1C1",
"& c #ECECEC",
"* c #A7A7A7",
"= c #636363",
"- c #989898",
"; c #C3C3C3",
"> c #C5C5C5",
", c #A6A6A6",
"' c #747474",
") c #646464",
"! c #6D6D6D",
"~ c #8C8C8C",
"{ c #ABABAB",
"] c #A5A5A5",
"^ c #787878",
"/ c #A8A8A8",
"( c #606060",
"_ c #FFFFFF",
": c #626262",
"< c #7A7A7A",
"[ c #FEFEFE",
"} c #949494",
"| c #535353",
"1 c #919191",
"2 c #F0F0F0",
"3 c #5B5B5B",
"4 c #B3B3B3",
"5 c #5A5A5A",
"6 c #3E3E3E",
"7 c #4C4C4C",
"8 c #666666",
"9 c #616161",
"0 c #939393",
"a c #F8F8F8",
"b c #1C1C1C",
"c c #999999",
"d c #DFDFDF",
"e c #0D0D0D",
"f c #B1B1B1",
"g c #343434",
"h c #5D5D5D",
"i c #676767",
"j c #6F6F6F",
"k c #9E9E9E",
"l c #4F4F4F",
"m c #F7F7F7",
"n c #1B1B1B",
"o c #E7E7E7",
"p c #1D1D1D",
"q c #7C7C7C",
"r c #9B9B9B",
"s c #525252",
"t c #EFEFEF",
"u c #9C9C9C",
"v c #434343",
"w c #414141",
"x c #3D3D3D",
"y c #3F3F3F",
"z c #BFBFBF",
"A c #3A3A3A",
"B c #686868",
"C c #6B6B6B",
"D c #C4C4C4",
"E c #F4F4F4",
"F c #FAFAFA",
"G c #D7D7D7",
"H c #AFAFAF",
"I c #828282",
"J c #737373",
"K c #818181",
"L c #DEDEDE",
"M c #E9E9E9",
"N c #696969",
"O c #9F9F9F",
"P c #A2A2A2",
"Q c #717171",
"R c #B4B4B4",
"S c #E8E8E8",
"T c #898989",
"U c #767676",
"V c #DBDBDB",
" ",
" ",
" ",
" ",
" .++@ #$%& ",
"*=-;>,')!~{]^)/ ",
"(_:<[}|123:42567",
"8_,90=abc|defg*h",
"i_jklfmnj1opq(r=",
"9sgtugvuwxyzwAB:",
"}CDEFGHIJK{L[MNO",
" #PQCKRd S@TiUV ",
" ",
" ",
" ",
" "};

@ -1,4 +1,6 @@
<section title="Feedback"> <?php require("header.inc"); ?>
<h1>Feedback</h1>
<p>You can report bugs to the <?php sflink("bugs/")?>bug forum</a>. </p> <p>You can report bugs to the <?php sflink("bugs/")?>bug forum</a>. </p>
@ -8,4 +10,5 @@ users.sourceforge.net</a>. Please mention <i>jode</i> in the
subject.</p> subject.</p>
<p>There is a mailing list. Check <a href="http://lists.sourceforge.net/mailman/listinfo/jode-users">this page</a> for subscription informations.</p> <p>There is a mailing list. Check <a href="http://lists.sourceforge.net/mailman/listinfo/jode-users">this page</a> for subscription informations.</p>
</section>
<?php require("footer.inc"); ?>

@ -2,8 +2,8 @@
<TR> <TR>
<TD align="center"><SPAN class=footer> <TD align="center"><SPAN class=footer>
All trademarks and copyrights on this page are properties of their respective owners. <br> All trademarks and copyrights on this page are properties of their respective owners. <br>
Last updated on 29-May-2002, Last updated on 29-May-2001,
Copyright &copy; 1998-2002 by Jochen Hoenicke. Copyright &copy; 1998-2001 by Jochen Hoenicke.
Canonic URL is <a class=boldlink href="http://jode.sourceforge.net/">http://jode.sourceforge.net/</a></SPAN> Canonic URL is <a class=boldlink href="http://jode.sourceforge.net/">http://jode.sourceforge.net/</a></SPAN>
</TD> </TD>
</TR> </TR>

Binary file not shown.

@ -21,7 +21,7 @@ function selflink($link) {
function sflink($link) { function sflink($link) {
echo "<a href=\"http://sourceforge.net/$link?group_id=3790\">"; echo "<a href=\"http://sourceforge.net/$link?group_id=3790\">";
} }
?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> ?><!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 3.0//EN">
<html> <html>
<head> <head>
<title>Java Optimize and Decompile Environment (JODE)</title> <title>Java Optimize and Decompile Environment (JODE)</title>
@ -40,7 +40,7 @@ body { color:#000000; background-color: #FFFFFF; }
</style> </style>
</head> </head>
<body text="#000000" bgcolor="#FFFFFF"> <body text=#000000 bgcolor=#FFFFFF>
<table cellpadding=4 cellspacing=1 width="100%" <table cellpadding=4 cellspacing=1 width="100%"
><tr ><tr
@ -49,7 +49,7 @@ body { color:#000000; background-color: #FFFFFF; }
></td ></td
><td align="right" ><td align="right"
>Powered by <a href="http://sourceforge.net"><img >Powered by <a href="http://sourceforge.net"><img
src="http://sourceforge.net/sflogo.php?group_id=3790&amp;type=1" src="http://sourceforge.net/sflogo.php?group_id=3790&type=1"
border=0 width=88 height=31 alt="SourceForge"></a><br border=0 width=88 height=31 alt="SourceForge"></a><br
>Best viewed with <a >Best viewed with <a
href="http://www.anybrowser.org/campaign/"><img href="http://www.anybrowser.org/campaign/"><img

@ -1,4 +1,6 @@
<section title="History"> <?php require("header.inc"); ?>
<h1>History</h1>
<p>Someday I found <code>guavad</code>, a disassembler for java byte <p>Someday I found <code>guavad</code>, a disassembler for java byte
code (it does similar things like <code>javap&nbsp;-c</code>). I used code (it does similar things like <code>javap&nbsp;-c</code>). I used
it on a class file, and found that it was possible to reconstruct the it on a class file, and found that it was possible to reconstruct the
@ -17,4 +19,5 @@ now, because it suited best.</p>
<p>Just for the records: the java code is now more than 50 times <p>Just for the records: the java code is now more than 50 times
bigger than the original perl script and is still growing.</p> bigger than the original perl script and is still growing.</p>
</section> <?php require("footer.inc"); ?>

@ -1,52 +0,0 @@
<opt quiet>
<file template="jode.htt">
<set version="1.1">
<set sfgroup="3790">
<def name="sflink" option="href">
<a href="http://sourceforge.net/${href}?group_id=${sfgroup}">
</def>
<def name="entry" option="name type href">
<if type="sflink">
<sflink href="$href"><use name></a>
<else>
<if _htpfile_out="${href}.html">
<use name>
<elseif $href="index">
<a href="."><use name></a>
<else>
<a href="${href}.html"><use name></a>
</if>
</if>
</def>
<block name=menu>
<entry name="<B>Home</B>" href="index">
<entry type=sflink name="Project page" href="project/">
<entry name="Applet" href="applet">
<entry name="Download" href="download">
<entry name="FAQ" href="faq">
<entry name="Feedback" href="feedback">
<entry name="Documentation" href="usage">
<entry name="License" href="license">
<entry name="History" href="history">
<entry name="Links" href="links">
<entry name="Blue Sky" href="bluesky">
</block>
<blockdef name=section option="title">
<if not sect_ctr><set sect_ctr="0" global></if>
<inc sect_ctr global>
<set title${sect_ctr}="$title" global>
<block name=section${sect_ctr} global expand>
<use block noexpand>
</block>
</blockdef>
<block name=everything>
<set i=1>
<while section$i>
<h1><use title$i></h1>
<use section$i>
<inc i>
</while>
</block>

@ -1,17 +1,15 @@
<section title="Introduction"> <?php require("header.inc"); ?>
<P><i>JODE</i> is a java package containing a decompiler and an <P><i>JODE</i> is a java package containing a decompiler and an
optimizer for java. This package is <a href="license.html">freely optimizer for java. This package is <?php selflink("license")
available</a> under the GNU GPL. The bytecode package and the core ?>freely available</a> under the GNU GPL.<p>
decompiler is now under GNU Lesser General Public License, so you can
integrate it in your project.</p>
<P>The decompiler reads in <tt>class</tt> files and produces something <P>The decompiler reads in <tt>class</tt> files and produces something
similar to the original <tt>java</tt> file. Of course this can't be similar to the original <tt>java</tt> file. Of course this can't be
perfect: There is no way to produce the comments or the names of local perfect: There is no way to produce the comments or the names of local
variables (except when compiled with debuging) and there are often variables (except when compiled with debuging) and there are often
more ways to write the same thing. However, <i>JODE</i> does its job quite more ways to write the same thing. However, <i>JODE</i> does its job quite
well, so you should give it a try and <a href="applet.html">start the well, so you should give it a try and <? selflink("applet") ?>start the
applet</a>.</P> applet</a>.</P>
<P>The optimizer transforms <tt>class</tt> files in various ways with <P>The optimizer transforms <tt>class</tt> files in various ways with
@ -27,21 +25,18 @@ fields</li>
<li>Optimizing local variable allocation</li> <li>Optimizing local variable allocation</li>
</ul> </ul>
</section> <h2>News</h2>
<section title="News">
<ul> <ul>
<li><i>JODE</i> 1.1.1 is out. With support for javac v8 (jdk 1.3). </li> <li><i>JODE</i> 1.1 is out. With support for javac v8 (jdk 1.3). </li>
<li>The license changed to LGPL for the bytecode interface and decompiler.</li> <li><i>JODE</i> is now hosted by <a href="http://sourceforge.net/">SourceForge</a>.</li>
<li>Now long lines are automatically broken.</li>
<li><b>Inner and anonymous</b> classes are automatically decompiled.</li>
<li>The optimizer (aka obfuscator) can be customized via a small
config file</li>
</ul> </ul>
</section>
<section title="Known Bugs">
<p>The current version has problems try/catch/finally code produced <h2>Known bugs of the decompiler</h2>
by java 1.4 compiler. You may try the latest CVS version or pre-release
instead.</p>
<p>Some jdk1.3 synthetic access functions aren't understood. The <p>Some jdk1.3 synthetic access functions aren't understood. The
produced source contains access$xxx functions, but it still compiles.</p> produced source contains access$xxx functions, but it still compiles.</p>
@ -50,9 +45,7 @@ fields</li>
If you have such a problems don't hesitate to issue a bug report. If you have such a problems don't hesitate to issue a bug report.
Please include the <code>class</code> file if possible.</p> Please include the <code>class</code> file if possible.</p>
</section> <h2>Limitations</h2>
<section title="Limits">
<p>If not all dependent classes can be found, the verifier (which is <p>If not all dependent classes can be found, the verifier (which is
run before decompilation starts) may exit with a type error. You run before decompilation starts) may exit with a type error. You
@ -71,4 +64,4 @@ the code should still be compileable. This does especially happen
when you compile with <tt>`-O'</tt> flag and javac has inlined some when you compile with <tt>`-O'</tt> flag and javac has inlined some
methods. </p> methods. </p>
</section> <?php require("footer.inc"); ?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

@ -1,67 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Java Optimize and Decompile Environment (JODE)</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="date" content="2001-05-29">
<meta name="description" content="JODE - Java Optimize and Decompile Environment.">
<meta name="author" content="Jochen Hoenicke">
<meta name="keywords" content="jode, java, decompiler, obfuscator, deobfuscator, reverse engineering, free, GPL">
<style type="text/css">
<!--
body { color:#000000; background-color: #FFFFFF; }
.nav { font-family: Helvetica, Arial, sans-serif; font-weight: bold;
color:#000000; background-color: #EEEEF8; }
.footer { color:#FFFFFF; background-color: #737B9C; }
.boldlink { font-weight:bold; text-decoration: none; color:#FFFFFF; }
//-->
</style>
</head>
<body text="#000000" bgcolor="#FFFFFF">
<table cellpadding=4 cellspacing=1 width="100%"
><tr
><td align="left"
><img src="jode-logo.png" alt="JODE"
></td
><td align="right"
>Powered by <a href="http://sourceforge.net"><img
src="http://sourceforge.net/sflogo.php?group_id=3790&amp;type=1"
border=0 width=88 height=31 alt="SourceForge"></a><br
>HTML coding <a href="http://htp.sourceforge.net"><img
src="poweredbyhtp.png" border=0 alt="Powered by htp"></a><br
>Best viewed with <a
href="http://www.anybrowser.org/campaign/"><img
src="w3c_ab.png" border=0 alt="Any Browser"></a><br
></td
></tr
></table>
<table cellspacing=0 cellpadding=3 border=0 bgcolor="#EEEEF8" class="nav">
<tr><td class="nav">
<use menu>
</td></tr>
</table><br>
<use everything>
<if _htpfile_out="index.html">
<set pageref="">
<else>
<set pageref="$_htpfile_out">
</if>
<TABLE class=footer width="100%" border="0" cellspacing="0" cellpadding="2">
<TR>
<TD align="center"><SPAN class=footer>
All trademarks and copyrights on this page are properties of their respective owners. <br>
Last updated on <file date>,
Copyright &copy; 1998-2004 by Jochen Hoenicke.
Canonic URL is <a class=boldlink href="http://jode.sourceforge.net/$pageref">http://jode.sourceforge.net/<use pageref></a></SPAN>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>

@ -1,5 +1,6 @@
<section title="License"> <?php require("header.inc"); ?>
<p><i>JODE</i> is Copyright &copy; 1998-2004 by Jochen Hoenicke. <br><br> <h1>License</h1>
<p><i>JODE</i> is Copyright &copy; 1998-2000 by Jochen Hoenicke. <br><br>
<p>This program is free software; you can redistribute it and/or modify <p>This program is free software; you can redistribute it and/or modify
it under the terms of the <a it under the terms of the <a
@ -7,15 +8,9 @@ href="http://www.gnu.org/copyleft/gpl.html">GNU General Public
License</a> as published by the Free Software Foundation; either License</a> as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.</p> version 2 of the License, or (at your option) any later version.</p>
<p>You can redistribute some of the packages under the
terms of the of the <a
href="http://www.gnu.org/copyleft/lesser.html">GNU Lesser General
Public License</a> as published by the Free Software Foundation. See
the copyright headers in the source code.</p>
<p>This program is distributed in the hope that it will be useful, <p>This program is distributed in the hope that it will be useful,
but <b>without any warranty</b>; without even the implied warranty of but <b>without any warranty</b>; without even the implied warranty of
<b>merchantability</b> or <b>fitness for a particular purpose</b>. See the <b>merchantability</b> or <b>fitness for a particular purpose</b>. See the
GNU General Public License for more details.</p> GNU General Public License for more details.</p>
</section> <?php require("footer.inc"); ?>

@ -1,7 +1,8 @@
<section title="<i>JODE</i> Links"> <?php require("header.inc") ?>
<h1><i>JODE</i> Links</h1>
<h3>Other decompilers</h3> <h3>Other decompilers</h3>
<ul> <ul>
<li><a href="http://dmoz.org/Computers/Programming/Languages/Java/Development_Tools/Translators/Decompilers_and_Disassemblers/">The Open Directory list</a></li> <li><a href="http://dmoz.org/Computers/Programming/Languages/Java/Decompilers_and_Disassemblers/">The Open Directory list</a></li>
<li>A list of decompilers can be found at <a href="http://www.meurrens.org/ip-Links/Java/CodeEngineering/#tocDecompilersToJava">Marc Meurren's list</a> <li>A list of decompilers can be found at <a href="http://www.meurrens.org/ip-Links/Java/CodeEngineering/#tocDecompilersToJava">Marc Meurren's list</a>
</li> </li>
<li>A very fast decompiler is <a <li>A very fast decompiler is <a
@ -14,7 +15,7 @@ Dyer.
</ul> </ul>
<h3>Other obfuscators</h3> <h3>Other obfuscators</h3>
<ul> <ul>
<li><a href="http://dmoz.org/Computers/Programming/Languages/Java/Development_Tools/Obfuscators/">The Open Directory list</a></li> <li><a href="http://dmoz.org/Computers/Programming/Languages/Java/Obfuscators/">The Open Directory list</a></li>
<li><a href="http://www.sbktech.org/hashjava_old.html">Hashjava</a> is another free obfuscator. It is no longer maintained, though, since its successor was commercialized.</li> <li><a href="http://www.sbktech.org/hashjava_old.html">Hashjava</a> is another free obfuscator. It is no longer maintained, though, since its successor was commercialized.</li>
<li><a href="http://www.zelix.com/klassmaster/index.html">Zelix <li><a href="http://www.zelix.com/klassmaster/index.html">Zelix
Klassmaster</a> does a very good flow optimization and also decrypts Klassmaster</a> does a very good flow optimization and also decrypts
@ -72,4 +73,5 @@ href="http://www.informatik.uni-oldenburg.de/~delwi/jode/collections.jar">jar
file</a> on this server. file</a> on this server.
</dd> </dd>
</dl> </dl>
</section> <?php require("footer.inc"); ?>

@ -13,7 +13,7 @@ $menu =
"Blue Sky" , "selflink", "bluesky"); "Blue Sky" , "selflink", "bluesky");
?> ?>
<table cellspacing=0 cellpadding=3 border=0 bgcolor="#EEEEF8" class="nav"> <table cellspacing=0 cellpadding=3 border=0 bgcolor=#EEEEF8 class="nav">
<tr><td class="nav"> <tr><td class="nav">
<?php <?php
reset($menu); reset($menu);

@ -50,35 +50,3 @@ analyzer = new ConstantAnalyzer
# The RemovePopAnalyzer will remove instructions that were optimized # The RemovePopAnalyzer will remove instructions that were optimized
# away by the ConstantAnalyzer and LocalOptimizer. # away by the ConstantAnalyzer and LocalOptimizer.
post = new LocalOptimizer, new RemovePopAnalyzer post = new LocalOptimizer, new RemovePopAnalyzer
################################################################
# The syntax for load and preserve is as follows
################################################################
#
# preserve ::= <list of IdentifierMatcher>
# // preserves everything that is matched by
# // at least one identifier matcher.
#
# IdentifierMatcher ::=
# MultiIdentifierMatcher { and = <list of IdentifierMatcher> }
# |
# MultiIdentifierMatcher { or = <list of IdentifierMatcher> }
# |
# WildCard { value = "<wildcard>" }
# |
# ModifierMatcher { access = "<AccessSpec>"
# [access = "<AccessSpec>" ...]
# modifier = "<ModifierSpec>"
# [modifier = "<ModifierSpec>" ...]
# // identifier must fulfill all constraints
# }
# |
# SerializedPreserver
#
# AccessSpec ::=
# <optional "<" or ">"> (PUBLIC|PROTECTED|PACKAGE|PRIVATE)
#
# ModifierSpec ::=
# <optional "!" (not)> (ABSTRACT|FINAL|INTERFACE|NATIVE|STATIC
# |STRICT|SYNCHRONIZED|TRANSIENT|VOLATILE)
#

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

@ -215,65 +215,3 @@ Zu betrachtende 32bit Typen:
(I,S,B,Z) (I,S,B,Z)
(I,S,B,cS,cB) (I,S,B,cS,cB)
@node Highlevel analysis, Technical Info, Solving unknown stack-ops, Technical Info
@section Highlevel analysis
@cindex passes
@section The passes
JODE works in three passes:
@subsection Pass 1: Initialize
In the initialize pass the methods, fields and inner classes are read in
and the inner classes are recursively initialized. In this pass the
complexity of the class is calculated. Anonymous and method scoped
classes aren't even considered yet.
@subsection Pass 2: Analyze
The analyze pass is the real decompilation pass: The code of the methods
is transformed into flow blocks and merged to one flow block as
described in a previous section. The in/out analysis for the local
variables is completed, and the locals are merged as necessary. The
parameter 0 for non static method is marked as ThisOperator in this
pass.
The constructors are analyzed first. If they initialize synthetic
fields, this is taken as clear sign that this are outer value
parameters. So afterwards, these synthetic fields know their value.
Then the methods are analyzed. Each method remembers the anonymous
classes it creates for Pass 3, but these classes are not yet
initialized. Inner classes aren't analyzed yet, either.
@subsection Pass 3: Analyze Inner
As the name of this pass makes clear the inner classes are initialized
in this pass, i.e. first Pass 2 and 3 are invoked for the inner classes.
After that the method scoped classes are analyzed: For each constructor
it is first check if one surrounding method knows about it. If not, a
new class analyzer is created for the method scoped class and Pass 1--3
are invoked. Every surrounding method is then told about this new class
analyzer.
After this pass, every anonymous constructor is analyzed, so we know
which constructor parameters can be outer values. The constructor
transformation may force some other outer values, though. It is also
known, in which method a method scoped class must be declared.
@subsection Pass 4: Make Declarations
The last pass begins with transforming the constructors of a class. Now
the outer values are fixed and the constructor parameters and synthetic
fields are told their values.
After that every method determines where to declare local variables and
method scoped classes. Local variables are declared as final if a
method scoped class uses it as outer value. The name of local
variables is guessed now.
This pass is done recursively for inner and method scoped classes.

@ -1,40 +1,38 @@
<if we_want_a_menu> <?php require("header.inc") ?>
<p>On this page:<br> <?php /* MOVE TO menu.inc
<a href="#decompiler">Decompiler</a><br> <a href="#decompiler">Decompiler</a><br>
&nbsp;&nbsp;&nbsp;<a href="#cmdline">Command&nbsp;Line</a><br> &nbsp;&nbsp;&nbsp;<a href="#cmdline">Command&nbsp;Line</a><br>
&nbsp;&nbsp;&nbsp;<a href="#awt">AWT&nbsp;Interface</a><br> &nbsp;&nbsp;&nbsp;<a href="#awt">AWT&nbsp;Interface</a><br>
&nbsp;&nbsp;&nbsp;<a href="#swing">Swing&nbsp;Interface</a><br> &nbsp;&nbsp;&nbsp;<a href="#swing">Swing&nbsp;Interface</a><br>
&nbsp;&nbsp;&nbsp;<a href="#java">Java&nbsp;Interface</a><br> &nbsp;&nbsp;&nbsp;<a href="#java">Java&nbsp;Interface</a><br>
<a href="#optimizer">Obfuscator</a><br> <a href="#optimizer">Obfuscator</a><br>
</p> */ ?>
</if> <a name="decompiler">
<h1>Using the Decompiler</h1></a>
<section title="Using the Decompiler"> <p>After you have <?php selflink("download") ?>downloaded</a> the jar archive
<p>After you have <a href="download.html">downloaded</a> the jar archive
put it into your <tt>CLASSPATH</tt>. The package put it into your <tt>CLASSPATH</tt>. The package
<tt>swingall.jar</tt> is also needed if you are using JDK 1.1.</p> <tt>swingall.jar</tt> is also needed if you are using JDK 1.1.</p>
<ul><li>Under Windows you have to start a MSDOS session and type <ul><li>Under Windows you have to start a MSDOS session and type
something like: something like:
<pre> <pre>
set CLASSPATH=C:\download\jode-<use version>.jar;C:\swing\swingall.jar set CLASSPATH=C:\download\jode-<?php echo "$version"?>.jar;C:\swing\swingall.jar
</pre> </pre>
<li>Under Unix you start a shell and type (for bourne shell): <li>Under Unix you start a shell and type (for bourne shell):
<pre>export CLASSPATH=/tmp/jode-<use version>.jar:/usr/local/swing/swingall.jar</pre> <pre>export CLASSPATH=/tmp/jode-<?php echo "$version"?>.jar:/usr/local/swing/swingall.jar</pre>
or for csh: or for csh:
<pre>setenv CLASSPATH /tmp/jode-<use version>.jar:/usr/local/swing/swingall.jar</pre> <pre>setenv CLASSPATH /tmp/jode-<?php echo "$version"?>.jar:/usr/local/swing/swingall.jar</pre>
</ul> </ul>
<br> <br>
There is also a batch file for windows and a script file for unix, There is also a batch file for windows and a script file for unix,
that you can use. You can extract it with the following command: that you can use. You can extract it with the following command:
<pre> <pre>
jar -xvf jode-<use version>".jar bin/jode.bat <i>resp.</i> bin/jode jar -xvf jode-<?php echo "$version-jdk1.1"?>.jar bin/jode.bat <i>resp.</i> bin/jode
</pre> </pre>
Edit the file to adapt it to your paths and put it to a convenient location. Edit the file to adapt it to your paths and put it to a convenient location.
</section>
<section title="Command Line Interface"> <a name="cmdline"><h3>Command Line Interface</h3></a>
The most powerful way to start <I>JODE</I>'s decompiler is the command The most powerful way to start <I>JODE</I>'s decompiler is the command
line interface. Some people don't like long command lines; they line interface. Some people don't like long command lines; they
@ -52,12 +50,12 @@ If you want to decompile a jar package you can do it this way:
If you have installed the batch file/script, you can use it like this: If you have installed the batch file/script, you can use it like this:
<pre>jode --dest srcdir program.jar</pre> <pre>jode --dest srcdir program.jar</pre>
<h3><a name="awt">AWT Interface</a></h3> <a name="awt"><h3>AWT Interface</h3></a>
The AWT Interface looks exactly like the <a href="applet.html"> The AWT Interface looks exactly like the <?php selflink("applet") ?>
applet</a>. In fact the applet uses the AWT Interface. You start it applet</a>. In fact the applet uses the AWT Interface. You start it
after setting the <tt>CLASSPATH</tt> (see <a after setting the <tt>CLASSPATH</tt> (see <?php
href="#decompiler">above</a>), with selflink("usage#decompiler") ?>above</a>), with
<pre>java jode.decompiler.Window</pre> <pre>java jode.decompiler.Window</pre>
@ -67,15 +65,11 @@ dot(<tt>.</tt>) separated name of the class you want to decompile.
Press the <code>start</code> button and the decompiled class should Press the <code>start</code> button and the decompiled class should
appear. You can save it via the <code>save</code> button. appear. You can save it via the <code>save</code> button.
<h3><a name="swing">Swing Interface</a></h3> <a name="swing"><h3>Swing Interface</h3></a>
For the swing interface you need java version 1.2 or the separately For the swing interface you need java version 1.2 or the separately
available swing package (see <a href="links.html#swing">link available swing package (see <?php selflink("links#swing") ?>link
page</a>. You can invoke it with the following command (JDK1.2 only): page</a>. You can invoke it with the following command:
<pre>
java -jar jode-<use version>.jar classes.jar
</pre>
or if you have set the classpath (see above)
<pre> <pre>
java jode.swingui.Main classes.jar java jode.swingui.Main classes.jar
<i>resp.</i> jode swi classes.jar <i>resp.</i> jode swi classes.jar
@ -92,27 +86,28 @@ you don't have the source code. You can also use it to trace bugs in
library code. It is not meant to generate <tt>java</tt> files and so library code. It is not meant to generate <tt>java</tt> files and so
you won't find a save option there.</p> you won't find a save option there.</p>
<h3><a name="java">Java Interface</a></h3> <a name="java"><h3>Java Interface</h3></a>
<p>If you want to integrate <i>JODE</i> into your own java program, <p>If you want to integrate <i>JODE</i> into your own java program,
you can use the <a you can use the <a
href="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/jode/jode/jode/decompiler/Decompiler.java?rev=jode_1_1&amp;content-type=text/vnd.viewcvs-markup" href="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/jode/jode/jode/decompiler/Decompiler.java?rev=jode_1_1&content-type=text/vnd.viewcvs-markup"
><code>jode.decompiler.Decompiler</code></a> ><code>jode.decompiler.Decompiler</code></a>
class. Note that the LGPL allows dynamic linking as long as you don't change class. Note that the GPL only allows you to integrate <i>JODE</i>
Jode itself. Please tell me if you use <i>JODE</i> in this way.</p> into GPL programs. Please tell me if you use <i>JODE</i> in this
way.</p>
<p>You should ship <code>jode-1.1-embedded.jar</code> with your program. This jar file is <p>You should ship <code>jode-1.1-embedded.jar</code> with your program. This jar file is
available in the <sflink href="project/showfiles.php">download area</a>. available in the <? sflink("project/showfiles.php") ?>download area</a>.
It works only under JDK&nbsp;1.2 and above.</p> It works only under JDK&nbsp;1.2 and above.</p>
</section>
<section title="Using the Obfuscator"> <a name="optimizer"><h1>Using the Obfuscator</h1>
<p>To use the obfuscator you should first create a script file, say <a <p>To use the obfuscator you should first create a script file, say <a
href="myproject.jos"><tt>myproject.jos</tt></a>. Then you can invoke the href="myproject.jos"><tt>myproject.jos</tt></a>. Then you can invoke the
obfuscator with:</p> obfuscator with:
<pre> <pre>
java jode.obfuscator.Main myproject.jos java jode.obfuscator.Main myproject.jos
</pre> </pre></p>
<p>The script file should contain the following options: </p> <p>The script file should contain the following options: </p>
@ -171,16 +166,12 @@ load = new WildCard { value = "org.myorg.myproject" },
<p>Select the methods and classes you want to preserve. This is <p>Select the methods and classes you want to preserve. This is
the <tt>main</tt> method for applications and the default constructor the <tt>main</tt> method for applications and the default constructor
<tt>&lt;init&gt;.()V</tt> for applets, resource bundles and other classes <tt>&lt;init&gt;.()V</tt> for applets, resource bundles and other classes
that you load manually at runtime. <br> that you load manually at runtime.<br> You have to give the method
You have to give the method
name and the type signature to identify your method. <tt>javap name and the type signature to identify your method. <tt>javap
-s</tt> will show you the type signatures for your classes, but you -s</tt> will show you the type signatures for your classes, but you
may also use <tt>*</tt>, to select all methods with that name. may also use <tt>*</tt>, to select all methods with that name.</p>
If you have serializable classes and want to preserve their serialized
form you can use the <tt>SerializePreserver</tt>. </p>
<pre> <pre>
preserve = new SerializePreserver, preserve = new WildCard { value = "org.myorg.ApplicationClass.main.*" },
new WildCard { value = "org.myorg.ApplicationClass.main.*" },
new WildCard { value = "org.myorg.AppletClass.&lt;init&gt;.()V" }, new WildCard { value = "org.myorg.AppletClass.&lt;init&gt;.()V" },
new WildCard { value = "org.resources.Bundle*.&lt;init&gt;.()V" }, new WildCard { value = "org.resources.Bundle*.&lt;init&gt;.()V" },
</pre> </pre>
@ -211,7 +202,7 @@ renamer = new StrongRenamer
<p>You can also create a renaming table with the same format as the <p>You can also create a renaming table with the same format as the
table written by revtable. The entries in the table get precedence table written by revtable. The entries in the table get precedence
over renamer. Entries not in the table will get renamed by the over renamer. Entries not in the table will get renamed by the
renamer.</p> renamer.<p>
<pre> <pre>
table = "translat.tbl" table = "translat.tbl"
</pre> </pre>
@ -221,7 +212,6 @@ analyzer is to mark all reachable methods, find out which methods
needs to get the same name (overloading), and which method names needs to get the same name (overloading), and which method names
mustn't change (overload of library methods, e.g. <tt>nextElement</tt> mustn't change (overload of library methods, e.g. <tt>nextElement</tt>
for <tt>Enumeration</tt>s). There are currently two analyzers. for <tt>Enumeration</tt>s). There are currently two analyzers.
</p>
<dl><dt>SimpleAnalyzer</dt> <dl><dt>SimpleAnalyzer</dt>
<dd>Straight forward analyzer. It is fast and will remove dead code <dd>Straight forward analyzer. It is fast and will remove dead code
on method basis.</dd> on method basis.</dd>
@ -233,6 +223,7 @@ and replace constant instruction with a load of the constant, or
remove them completely.<br> This analyzer is especially useful to remove them completely.<br> This analyzer is especially useful to
revert the flow obfuscation of some other obfuscators.</dd> revert the flow obfuscation of some other obfuscators.</dd>
</dl> </dl>
</p>
<pre> <pre>
analyzer = new ConstantAnalyzer analyzer = new ConstantAnalyzer
</pre> </pre>
@ -247,4 +238,4 @@ change the bytecode interface.</p>
<pre> <pre>
post = new LocalOptimizer, new RemovePopAnalyzer post = new LocalOptimizer, new RemovePopAnalyzer
</pre> </pre>
</section> <?php require("footer.inc") ?>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

@ -1,4 +1,4 @@
#!/usr/bin/perl -s -w #!@PERL@ -s -w
# #
# javaDependencies Copyright (C) 1999 Jochen Hoenicke. # javaDependencies Copyright (C) 1999 Jochen Hoenicke.
# #
@ -82,11 +82,10 @@ foreach $clazz (@ARGV) {
binmode FILE; binmode FILE;
readInBuff 8 or die "Can't read header"; readInBuff 8 or die "Can't read header";
my ($magic, $major, $minor) = unpack("Nnn", $buff); my ($magic, $minor, $major) = unpack("Nnn", $buff);
die "Wrong magic $magic" if $magic != 0xcafebabe; die "Wrong magic $magic" if $magic != 0xcafebabe;
die "Wrong major $major" if $major != 3; die "Wrong major $major" if $major < 45;
die "Wrong minor $minor" if $minor < 45;
readInBuff 2 or die "Can't read cpool length"; readInBuff 2 or die "Can't read cpool length";

@ -1,6 +1,6 @@
#!/usr/bin/perl -w #!/usr/bin/perl
# #
# jcpp Copyright (C) 1999-2001 Jochen Hoenicke. # jcpp Copyright (C) 1999 Jochen Hoenicke.
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -23,9 +23,8 @@
# preprocessor, but to modify the java files directly and make use of # preprocessor, but to modify the java files directly and make use of
# comments. # comments.
# #
# The comments all have the form /// and start at the beginning of the # The comments all have the form /// to distinguish them from normal
# line to distinguish them from normal comments. You must not use # comments. You should not use such comments yourself.
# such comments for other purposes.
# #
# Usage is simple: jcpp -Ddefine1 -Ddefine2 first.java second.java # Usage is simple: jcpp -Ddefine1 -Ddefine2 first.java second.java
# The files should contain comments of the form # The files should contain comments of the form
@ -38,23 +37,6 @@
# #
# After running jcpp the false branch is commented out. If the true # After running jcpp the false branch is commented out. If the true
# branch was commented out it will get commented in. # branch was commented out it will get commented in.
#
# jcpp can also change definitions, useful for package renaming. The
# java file should look like this:
#
# ///#def COLLECTIONS java.util
# import java.util.Vector
# import java.util.ArrayList
# ///#enddef
#
# If jcpp is then called with -DCOLLECTIONS=gnu.java.util.collections
# it will replace every occurence of java.util (the string in the #def
# line) with the new value:
#
# ///#def COLLECTIONS gnu.java.util.collections
# import gnu.java.util.collections.Vector
# import gnu.java.util.collections.ArrayList
# ///#enddef
my @files; my @files;
my %defs; my %defs;
@ -85,11 +67,6 @@ for (@files) {
my $error = 0; my $error = 0;
my $changes = 0; my $changes = 0;
# The list of #def replacements, @replold is the previous value,
# @replnew the new one.
my @replold = ();
my @replnew = ();
my $file = $_; my $file = $_;
open OLD, "<$file" or do { open OLD, "<$file" or do {
print STDERR "Can't open file $file\n"; print STDERR "Can't open file $file\n";
@ -105,9 +82,10 @@ for (@files) {
$linenr++; $linenr++;
if (m'^///#') { if (m'^///#') {
# This is a directive. First we print it out. # This is a directive. First we print it out.
if (m'^///#\s*if') { print NEW $_;
if (m'^///# *if') {
$level++; $level++;
if (m'^///#\s*ifdef\s+(\w+)\s*$') { if (m'^///# *ifdef (\S*)$') {
# If there was an outer false #if directive, we ignore the # If there was an outer false #if directive, we ignore the
# condition. # condition.
next LINE if ($falselevel); next LINE if ($falselevel);
@ -117,7 +95,7 @@ for (@files) {
# An ifdef directive, look if -D is defined. # An ifdef directive, look if -D is defined.
$falselevel = $level $falselevel = $level
unless (defined $defs{$label}); unless (defined $defs{$label});
} elsif (m'^///#\s*ifndef\s+(\w+)\s*$') { } elsif (m'^///# *ifndef (\S*)$') {
# If there was an outer false #if directive, we ignore the # If there was an outer false #if directive, we ignore the
# condition. # condition.
next LINE if ($falselevel); next LINE if ($falselevel);
@ -126,34 +104,8 @@ for (@files) {
# An ifndef directive, look if -D is defined # An ifndef directive, look if -D is defined
$falselevel = $level $falselevel = $level
if (defined $defs{$label}); if (defined $defs{$label});
} elsif (m'^///#\s*if\s+(\w+)\s*(==|!=)\s*(\S+)\s*$') {
# If there was an outer false #if directive, we ignore the
# condition.
next LINE if ($falselevel);
my $label=$1;
my $value=$3;
# An ifdef directive, look if -D is defined.
$falselevel = $level
unless ($2 eq "==" ? $defs{$label} eq $value
: $defs{$label} ne $value);
} elsif (m'^///#\s*if\s+(\w+)\s*(>=|<=|>|<)\s*(\S+)\s*$') {
# If there was an outer false #if directive, we ignore the
# condition.
next LINE if ($falselevel);
my $label=$1;
my $value=$3;
# An ifdef directive, look if -D is defined.
$falselevel = $level
unless ($2 eq ">=" ? $defs{$label} >= $value
: $2 eq "<=" ? $defs{$label} <= $value
: $2 eq ">" ? $defs{$label} > $value
: $defs{$label} < $value);
} }
} elsif (m'^///#\s*else\s*$') { } elsif (m'^///# *else') {
# An else directive. We switch from true to false and # An else directive. We switch from true to false and
# if level is falselevel we switch from false to true # if level is falselevel we switch from false to true
if ($level == 0) { if ($level == 0) {
@ -165,7 +117,7 @@ for (@files) {
} elsif ($falselevel == 0) { } elsif ($falselevel == 0) {
$falselevel = $level; $falselevel = $level;
} }
} elsif (m'^///#\s*endif\s*$') { } elsif (m'^///# *endif') {
# set $falselevel to 0, if the false branch is over now. # set $falselevel to 0, if the false branch is over now.
$falselevel = 0 if ($falselevel == $level); $falselevel = 0 if ($falselevel == $level);
# decrease level. # decrease level.
@ -175,21 +127,6 @@ for (@files) {
} else { } else {
$level--; $level--;
} }
} elsif (m'^///#\s*def\s+(\w+)\s+(\S*)$') {
my $label = $1;
my $old = $2;
my $new = $defs{$label};
if (defined $new && $new ne $old) {
push @replold, "$old";
push @replnew, "$new";
$changes = 1;
} else {
push @replnew, "";
push @replold, "";
}
} elsif (m'^///#\s*enddef\s*$') {
pop @replold;
pop @replnew;
} else { } else {
print STDERR "$file: $linenr: ignoring unknown directive $_"; print STDERR "$file: $linenr: ignoring unknown directive $_";
$error = 1; $error = 1;
@ -198,20 +135,21 @@ for (@files) {
$line = $1; $line = $1;
if ($falselevel == 0 && $level > 0) { if ($falselevel == 0 && $level > 0) {
# remove comments in true branch, but not in outermost level. # remove comments in true branch, but not in outermost level.
$_ = "$line\n"; print NEW "$line\n";
$changes = 1; $changes = 1;
} else {
# print out the full line with comments:
print NEW $_;
} }
} else { } else {
if ($falselevel != 0) { if ($falselevel != 0) {
# add comments in false branch # add comments in false branch
$_ = "///$_"; print NEW "///$_";
$changes = 1; $changes = 1;
} else {
print NEW $_;
} }
} }
for ($i = 0; $i < @replold; $i++) {
$_ =~ s/\Q$replold[$i]\E/$replnew[$i]/ if ($replold[$i] ne "");
}
print NEW $_;
} }
if ($level != 0 || $falselevel != 0) { if ($level != 0 || $falselevel != 0) {
@ -225,7 +163,8 @@ for (@files) {
if ($changes == 0) { if ($changes == 0) {
unlink "$file.tmp"; unlink "$file.tmp";
} else { } else {
(unlink "$file" and rename "$file.tmp", "$file") (rename "$file", "$file.orig"
and rename "$file.tmp", "$file")
or print STDERR "$file: Couldn't rename files.\n"; or print STDERR "$file: Couldn't rename files.\n";
} }
} else { } else {

@ -0,0 +1,4 @@
Makefile
Makefile.in
.java.deps
jode.jar

@ -0,0 +1,29 @@
/* AssertError Copyright (C) 1998-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode;
public class AssertError extends Error {
public AssertError() {
}
public AssertError(String detail) {
super(detail);
}
}

@ -17,7 +17,7 @@
* $Id$ * $Id$
*/ */
package net.sf.jode; package jode;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.StringTokenizer; import java.util.StringTokenizer;
@ -25,7 +25,7 @@ public class GlobalOptions {
public final static String version = "@VERSION@"; public final static String version = "@VERSION@";
public final static String email = "jochen@gnu.org"; public final static String email = "jochen@gnu.org";
public final static String copyright = public final static String copyright =
"Jode (c) 1998-2004 Jochen Hoenicke <"+email+">"; "Jode (c) 1998-2001 Jochen Hoenicke <"+email+">";
public final static String URL = "http://jode.sourceforge.net/"; public final static String URL = "http://jode.sourceforge.net/";
public static PrintWriter err = new PrintWriter(System.err, true); public static PrintWriter err = new PrintWriter(System.err, true);
@ -80,13 +80,12 @@ public class GlobalOptions {
/** /**
* Parse the argument given to the debugging flag. * Parse the argument given to the debugging flag.
* @exception IllegalArgumentException * @return true, if the argument parsed without problems.
* if a problem occured while parsing the argument.
*/ */
public static boolean setDebugging(String debuggingString) { public static boolean setDebugging(String debuggingString) {
if (debuggingString.length() == 0 || debuggingString.equals("help")) { if (debuggingString.length() == 0 || debuggingString.equals("help")) {
usageDebugging(); usageDebugging();
throw new IllegalArgumentException(); return false;
} }
StringTokenizer st = new StringTokenizer(debuggingString, ","); StringTokenizer st = new StringTokenizer(debuggingString, ",");
@ -99,8 +98,8 @@ public class GlobalOptions {
continue next_token; continue next_token;
} }
} }
throw new IllegalArgumentException("Illegal debugging flag: " err.println("Illegal debugging flag: "+token);
+token); return false;
} }
return true; return true;
} }

@ -0,0 +1,41 @@
## Input file for automake to generate the Makefile.in used by configure
SUBDIRS = util bytecode type jvm expr flow decompiler obfuscator @SWINGUI@
JAR = @JAR@
JAVAC = @JAVAC@
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@
FULL_CLASSPATH := $(shell $(SUBSTCP) $(top_srcdir):$(top_builddir):$(CLASSPATH):$(CLASSLIB))
MY_JAVA_FILES = \
AssertError.java \
GlobalOptions.java
noinst_DATA = $(MY_JAVA_FILES:.java=.class)
EXTRA_DIST = $(MY_JAVA_FILES)
JARFILE = jode-@VERSION@.jar
#data_DATA = $(JARFILE)
@QUOTE@-include Makefile.dep
%.class: %.java
$(JAVAC) -classpath $(FULL_CLASSPATH) -d $(top_builddir) $<
Makefile.dep: $(MY_JAVA_FILES:.java=.class)
$(JAVADEP) $^
clean-local:
@rm -f *.class
@rm -f *.dep
$(JARFILE): $(noinst_DATA)
CLASSPATH=$(top_builddir):$(CLASSPATH) $(JAVA) -mx80m \
jode.obfuscator.Main --classpath=$(top_builddir) \
--dest=$(JARFILE) -v -v $(srcdir)/jode.jodescript

@ -0,0 +1,2 @@
Makefile
Makefile.in

@ -0,0 +1,229 @@
/* BinaryInfo Copyright (C) 1998-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import jode.util.SimpleMap;
import @COLLECTIONS@.Map;
import @COLLECTIONS@.Collections;
import @COLLECTIONS@.Iterator;
/**
*
* @author Jochen Hoenicke
*/
public class BinaryInfo {
public static final int HIERARCHY = 0x01;
public static final int FIELDS = 0x02;
public static final int METHODS = 0x04;
public static final int CONSTANTS = 0x08;
public static final int KNOWNATTRIBS = 0x10;
public static final int INNERCLASSES = 0x20;
public static final int OUTERCLASSES = 0x40;
public static final int UNKNOWNATTRIBS = 0x80;
public static final int FULLINFO = 0xff;
public static final int MOSTINFO = 0x7f;
public static final int REFLECTINFO = 0x6f;
private Map unknownAttributes = null;
protected void skipAttributes(DataInputStream input) throws IOException {
int count = input.readUnsignedShort();
for (int i=0; i< count; i++) {
input.readUnsignedShort(); // the name index
long length = input.readInt();
while (length > 0) {
long skipped = input.skip(length);
if (skipped == 0)
throw new EOFException("Can't skip. EOF?");
length -= skipped;
}
}
}
protected int getKnownAttributeCount() {
return 0;
}
protected void readAttribute(String name, int length,
ConstantPool constantPool,
DataInputStream input,
int howMuch) throws IOException {
byte[] data = new byte[length];
input.readFully(data);
if ((howMuch & UNKNOWNATTRIBS) != 0) {
if (unknownAttributes == null)
unknownAttributes = new SimpleMap();
unknownAttributes.put(name, data);
}
}
static class ConstrainedInputStream extends FilterInputStream {
int length;
public ConstrainedInputStream(int attrLength, InputStream input) {
super(input);
length = attrLength;
}
public int read() throws IOException {
if (length > 0) {
int data = super.read();
length--;
return data;
}
throw new EOFException();
}
public int read(byte[] b, int off, int len) throws IOException {
if (length < len) {
len = length;
}
if (len == 0)
return -1;
int count = super.read(b, off, len);
length -= count;
return count;
}
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public long skip(long count) throws IOException {
if (length < count) {
count = length;
}
count = super.skip(count);
length -= (int) count;
return count;
}
public void skipRemaining() throws IOException {
while (length > 0) {
int skipped = (int) skip(length);
if (skipped == 0)
throw new EOFException();
length -= skipped;
}
}
}
protected void readAttributes(ConstantPool constantPool,
DataInputStream input,
int howMuch) throws IOException {
int count = input.readUnsignedShort();
unknownAttributes = null;
for (int i=0; i< count; i++) {
String attrName =
constantPool.getUTF8(input.readUnsignedShort());
final int attrLength = input.readInt();
ConstrainedInputStream constrInput =
new ConstrainedInputStream(attrLength, input);
readAttribute(attrName, attrLength,
constantPool, new DataInputStream(constrInput),
howMuch);
constrInput.skipRemaining();
}
}
public void dropInfo(int howMuch) {
if ((howMuch & UNKNOWNATTRIBS) != 0)
unknownAttributes = null;
}
protected void prepareAttributes(GrowableConstantPool gcp) {
if (unknownAttributes == null)
return;
Iterator i = unknownAttributes.keySet().iterator();
while (i.hasNext())
gcp.putUTF8((String) i.next());
}
protected void writeKnownAttributes
(GrowableConstantPool constantPool,
DataOutputStream output) throws IOException {
}
protected void writeAttributes
(GrowableConstantPool constantPool,
DataOutputStream output) throws IOException {
int count = getKnownAttributeCount();
if (unknownAttributes != null)
count += unknownAttributes.size();
output.writeShort(count);
writeKnownAttributes(constantPool, output);
if (unknownAttributes != null) {
Iterator i = unknownAttributes.entrySet().iterator();
while (i.hasNext()) {
Map.Entry e = (Map.Entry) i.next();
String name = (String) e.getKey();
byte[] data = (byte[]) e.getValue();
output.writeShort(constantPool.putUTF8(name));
output.writeInt(data.length);
output.write(data);
}
}
}
public int getAttributeSize() {
int size = 2; /* attribute count */
if (unknownAttributes != null) {
Iterator i = unknownAttributes.values().iterator();
while (i.hasNext())
size += 2 + 4 + ((byte[]) i.next()).length;
}
return size;
}
public byte[] findAttribute(String name) {
if (unknownAttributes != null)
return (byte[]) unknownAttributes.get(name);
return null;
}
public Iterator getAttributes() {
if (unknownAttributes != null)
return unknownAttributes.values().iterator();
return Collections.EMPTY_SET.iterator();
}
public void setAttribute(String name, byte[] content) {
if (unknownAttributes == null)
unknownAttributes = new SimpleMap();
unknownAttributes.put(name, content);
}
public byte[] removeAttribute(String name) {
if (unknownAttributes != null)
return (byte[]) unknownAttributes.remove(name);
return null;
}
public void removeAllAttributes() {
unknownAttributes = null;
}
}

File diff suppressed because it is too large Load Diff

@ -17,26 +17,16 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.bytecode; package jode.bytecode;
/** /**
* Thrown when a class file with an unknown or illegal format is loaded. * This exception is thrown, if the class file has an unknown format.
*
* @author Jochen Hoenicke * @author Jochen Hoenicke
*/ */
public class ClassFormatException extends java.io.IOException { public class ClassFormatException extends java.io.IOException{
/**
* Constructs a new class format exception with the given detail
* message.
* @param detail the detail message.
*/
public ClassFormatException(String detail) { public ClassFormatException(String detail) {
super(detail); super(detail);
} }
/**
* Constructs a new class format exception.
*/
public ClassFormatException() { public ClassFormatException() {
super(); super();
} }

@ -0,0 +1,903 @@
/* ClassInfo Copyright (C) 1998-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import jode.GlobalOptions;
import jode.util.UnifyHash;
import java.io.DataInputStream;
import java.io.BufferedInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import @COLLECTIONS@.Iterator;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* This class does represent a class similar to java.lang.Class. You
* can get the super class and the interfaces. <br>
*
* The main difference to java.lang.Class is, that the objects are builded
* from a stream containing the .class file, and that it uses the
* <code>Type</code> to represent types instead of Class itself. <br>
*
* <h2>The InnerClasses attribute</h2>
*
* The InnerClasses attribute is transformed in a special way by this
* class so we want to taker a closer look. According to the <a
* href="http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc10.html#18814">inner
* class specification</a> there must be an InnerClass attribute for
* every non top-level class that is referenced somewhere in the
* bytecode. This implies that if this is an inner class, it must
* contain a inner class attribute for itself. Before a class is
* referenced as outer class in an InnerClass attribute, it must be
* described by another InnerClass attribute. <br>
*
* Since every class references itself, there must be informations
* about the outer class for each class scoped class. If that outer
* class is an outer class again, there must be information about it,
* too. This particular chain of InnerClassInfos is returned by the
* getOuterClasses() method; for convenience in reverse order, i.e.
* current class first, then the outer classes from innermost to
* outermost. <br>
*
* A valid bytecode must also contain InnerClass infos for each inner
* classes it declares. These information are returned by the
* getInnerClasses() method. The order of these classes is the same
* as in the bytecode attribute.
*
* All remaining attributes are returned by getExtraClasses() in the
* same order as in the bytecode attribute.
*
* @author Jochen Hoenicke
*/
public class ClassInfo extends BinaryInfo {
private static SearchPath classpath;
private static final UnifyHash classes = new UnifyHash();
private int status = 0;
private boolean modified = false;
private int modifiers = -1;
private boolean deprecatedFlag;
private String name;
private ClassInfo superclass;
private ClassInfo[] interfaces;
private FieldInfo[] fields;
private MethodInfo[] methods;
private InnerClassInfo[] outerClasses;
private InnerClassInfo[] innerClasses;
private InnerClassInfo[] extraClasses;
private String sourceFile;
public final static ClassInfo javaLangObject = forName("java.lang.Object");
public static void setClassPath(String path) {
setClassPath(new SearchPath(path));
}
public static void setClassPath(SearchPath path) {
if (classpath != path) {
classpath = path;
Iterator i = classes.iterator();
while (i.hasNext()) {
ClassInfo ci = (ClassInfo) i.next();
ci.status = 0;
ci.superclass = null;
ci.fields = null;
ci.interfaces = null;
ci.methods = null;
ci.removeAllAttributes();
}
}
}
public static boolean exists(String name) {
return classpath.exists(name.replace('.', '/') + ".class");
}
public static boolean isPackage(String name) {
return classpath.isDirectory(name.replace('.', '/'));
}
public static Enumeration getClassesAndPackages(final String packageName) {
final Enumeration enumeration =
classpath.listFiles(packageName.replace('.','/'));
return new Enumeration() {
public boolean hasMoreElements() {
return enumeration.hasMoreElements();
}
public Object nextElement() {
String name = (String) enumeration.nextElement();
if (!name.endsWith(".class"))
// This is a package
return name;
return name.substring(0, name.length()-6);
}
};
}
public static ClassInfo forName(String name) {
if (name == null
|| name.indexOf(';') != -1
|| name.indexOf('[') != -1
|| name.indexOf('/') != -1)
throw new IllegalArgumentException("Illegal class name: "+name);
int hash = name.hashCode();
Iterator iter = classes.iterateHashCode(hash);
while (iter.hasNext()) {
ClassInfo clazz = (ClassInfo) iter.next();
if (clazz.name.equals(name))
return clazz;
}
ClassInfo clazz = new ClassInfo(name);
classes.put(hash, clazz);
return clazz;
}
private ClassInfo(String name) {
this.name = name;
}
protected void readAttribute(String name, int length,
ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
if (name.equals("SourceFile")) {
if ((howMuch & KNOWNATTRIBS) != 0) {
if (length != 2)
throw new ClassFormatException("SourceFile attribute"
+ " has wrong length");
sourceFile = cp.getUTF8(input.readUnsignedShort());
} else
input.readFully(new byte[length]);
} else if (name.equals("InnerClasses")) {
if ((howMuch & (OUTERCLASSES | INNERCLASSES)) != 0) {
int count = input.readUnsignedShort();
if (length != 2 + 8 * count)
throw new ClassFormatException
("InnerClasses attribute has wrong length");
int innerCount = 0, outerCount = 0, extraCount = 0;
InnerClassInfo[] innerClassInfo = new InnerClassInfo[count];
for (int i=0; i< count; i++) {
int innerIndex = input.readUnsignedShort();
int outerIndex = input.readUnsignedShort();
int nameIndex = input.readUnsignedShort();
String inner = cp.getClassName(innerIndex);
String outer =
outerIndex != 0 ? cp.getClassName(outerIndex) : null;
String innername =
nameIndex != 0 ? cp.getUTF8(nameIndex) : null;
int access = input.readUnsignedShort();
if (innername != null && innername.length() == 0)
innername = null;
/* Some compilers give method scope classes a valid
* outer field, but we mustn't handle them as inner
* classes. The best way to distinguish this case
* is by the class name.
*/
if (outer != null && innername != null
&& inner.length() > outer.length() + 2 + innername.length()
&& inner.startsWith(outer+"$")
&& inner.endsWith("$"+innername)
&& Character.isDigit(inner.charAt(outer.length() + 1)))
outer = null;
InnerClassInfo ici = new InnerClassInfo
(inner, outer, innername, access);
if (outer != null && outer.equals(getName())
&& innername != null)
innerClassInfo[innerCount++] = ici;
else
innerClassInfo[count - (++extraCount)] = ici;
}
/* Now innerClasses are at the front of innerClassInfo array
* in correct order. The other InnerClassInfos are in reverse
* order in the rest of the innerClassInfo array.
*/
/* We now count the outerClasses. The reverse order is the
* right thing for us.
*/
{
String lastOuterName = getName();
for (int i = count - extraCount;
i < count && lastOuterName != null; i++) {
InnerClassInfo ici = innerClassInfo[i];
if (ici.inner.equals(lastOuterName)) {
outerCount++;
extraCount--;
lastOuterName = ici.outer;
}
}
}
if (innerCount > 0) {
innerClasses = new InnerClassInfo[innerCount];
System.arraycopy(innerClassInfo, 0,
innerClasses, 0, innerCount);
} else
innerClasses = null;
if (outerCount > 0) {
outerClasses = new InnerClassInfo[outerCount];
} else
outerClasses = null;
if (extraCount > 0) {
extraClasses = new InnerClassInfo[extraCount];
} else
extraClasses = null;
/* The last part: We split between outer and extra classes.
* In this step we will also revert the order of the extra
* classes.
*/
{
int outerPtr = 0;
String lastOuterName = getName();
for (int i = count - extraCount - outerCount;
i < count; i++) {
InnerClassInfo ici = innerClassInfo[i];
/* If we counted correctly there is no NullPointer
* or ArrayIndexOutOfBoundsException here
*/
if (ici.inner.equals(lastOuterName)) {
outerClasses[outerPtr++] = ici;
lastOuterName = ici.outer;
} else
extraClasses[--extraCount] = ici;
}
}
} else
input.readFully(new byte[length]);
} else if (name.equals("Deprecated")) {
deprecatedFlag = true;
if (length != 0)
throw new ClassFormatException
("Deprecated attribute has wrong length");
} else
super.readAttribute(name, length, cp, input, howMuch);
}
public void read(DataInputStream input, int howMuch) throws IOException {
/* Since we have to read the whole class anyway, we load all
* info, that we may need later and that does not take much memory.
*/
howMuch |= HIERARCHY | INNERCLASSES | OUTERCLASSES;
howMuch &= ~status;
/* header */
if (input.readInt() != 0xcafebabe)
throw new ClassFormatException("Wrong magic");
int version = input.readUnsignedShort();
version |= input.readUnsignedShort() << 16;
if (version < (45 << 16 | 0))
throw new ClassFormatException("Wrong class version");
/* constant pool */
ConstantPool cpool = new ConstantPool();
cpool.read(input);
/* always read modifiers, name, super, ifaces */
{
modifiers = input.readUnsignedShort();
String className = cpool.getClassName(input.readUnsignedShort());
if (!name.equals(className))
throw new ClassFormatException("wrong name " + className);
String superName = cpool.getClassName(input.readUnsignedShort());
superclass = superName != null ? ClassInfo.forName(superName) : null;
int count = input.readUnsignedShort();
interfaces = new ClassInfo[count];
for (int i=0; i< count; i++) {
interfaces[i] = ClassInfo.forName
(cpool.getClassName(input.readUnsignedShort()));
}
status |= HIERARCHY;
}
/* fields */
if ((howMuch & (FIELDS | KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) {
int count = input.readUnsignedShort();
if ((status & FIELDS) == 0)
fields = new FieldInfo[count];
for (int i=0; i< count; i++) {
if ((status & FIELDS) == 0)
fields[i] = new FieldInfo(this);
fields[i].read(cpool, input, howMuch);
}
} else {
byte[] skipBuf = new byte[6];
int count = input.readUnsignedShort();
for (int i=0; i< count; i++) {
input.readFully(skipBuf); // modifier, name, type
skipAttributes(input);
}
}
/* methods */
if ((howMuch & (METHODS | KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) {
int count = input.readUnsignedShort();
if ((status & METHODS) == 0)
methods = new MethodInfo[count];
for (int i=0; i< count; i++) {
if ((status & METHODS) == 0)
methods[i] = new MethodInfo(this);
methods[i].read(cpool, input, howMuch);
}
} else {
byte[] skipBuf = new byte[6];
int count = input.readUnsignedShort();
for (int i=0; i< count; i++) {
input.readFully(skipBuf); // modifier, name, type
skipAttributes(input);
}
}
/* attributes */
readAttributes(cpool, input, howMuch);
status |= howMuch;
}
public void reserveSmallConstants(GrowableConstantPool gcp) {
for (int i=0; i < fields.length; i++)
fields[i].reserveSmallConstants(gcp);
for (int i=0; i < methods.length; i++)
methods[i].reserveSmallConstants(gcp);
}
public void prepareWriting(GrowableConstantPool gcp) {
gcp.putClassName(name);
gcp.putClassName(superclass.getName());
for (int i=0; i < interfaces.length; i++)
gcp.putClassName(interfaces[i].getName());
for (int i=0; i < fields.length; i++)
fields[i].prepareWriting(gcp);
for (int i=0; i < methods.length; i++)
methods[i].prepareWriting(gcp);
if (sourceFile != null) {
gcp.putUTF8("SourceFile");
gcp.putUTF8(sourceFile);
}
if (outerClasses != null || innerClasses != null
|| extraClasses != null) {
gcp.putUTF8("InnerClasses");
int outerCount = outerClasses != null ? outerClasses.length : 0;
for (int i=outerCount; i-- > 0;) {
gcp.putClassName(outerClasses[i].inner);
if (outerClasses[i].outer != null)
gcp.putClassName(outerClasses[i].outer);
if (outerClasses[i].name != null)
gcp.putUTF8(outerClasses[i].name);
}
int innerCount = innerClasses != null ? innerClasses.length : 0;
for (int i=0; i< innerCount; i++) {
gcp.putClassName(innerClasses[i].inner);
if (innerClasses[i].outer != null)
gcp.putClassName(innerClasses[i].outer);
if (innerClasses[i].name != null)
gcp.putUTF8(innerClasses[i].name);
}
int extraCount = extraClasses != null ? extraClasses.length : 0;
for (int i=0; i< extraCount; i++) {
gcp.putClassName(extraClasses[i].inner);
if (extraClasses[i].outer != null)
gcp.putClassName(extraClasses[i].outer);
if (extraClasses[i].name != null)
gcp.putUTF8(extraClasses[i].name);
}
}
if (deprecatedFlag)
gcp.putUTF8("Deprecated");
prepareAttributes(gcp);
}
protected int getKnownAttributeCount() {
int count = 0;
if (sourceFile != null)
count++;
if (innerClasses != null || outerClasses != null
|| extraClasses != null)
count++;
return count;
}
public void writeKnownAttributes(GrowableConstantPool gcp,
DataOutputStream output)
throws IOException {
if (sourceFile != null) {
output.writeShort(gcp.putUTF8("SourceFile"));
output.writeInt(2);
output.writeShort(gcp.putUTF8(sourceFile));
}
if (outerClasses != null || innerClasses != null
|| extraClasses != null) {
output.writeShort(gcp.putUTF8("InnerClasses"));
int outerCount = (outerClasses != null) ? outerClasses.length : 0;
int innerCount = (innerClasses != null) ? innerClasses.length : 0;
int extraCount = (extraClasses != null) ? extraClasses.length : 0;
int count = outerCount + innerCount + extraCount;
output.writeInt(2 + count * 8);
output.writeShort(count);
for (int i=outerCount; i-- > 0; ) {
output.writeShort(gcp.putClassName(outerClasses[i].inner));
output.writeShort(outerClasses[i].outer != null ?
gcp.putClassName(outerClasses[i].outer) : 0);
output.writeShort(outerClasses[i].name != null ?
gcp.putUTF8(outerClasses[i].name) : 0);
output.writeShort(outerClasses[i].modifiers);
}
for (int i=0; i< innerCount; i++) {
output.writeShort(gcp.putClassName(innerClasses[i].inner));
output.writeShort(innerClasses[i].outer != null ?
gcp.putClassName(innerClasses[i].outer) : 0);
output.writeShort(innerClasses[i].name != null ?
gcp.putUTF8(innerClasses[i].name) : 0);
output.writeShort(innerClasses[i].modifiers);
}
for (int i=0; i< extraCount; i++) {
output.writeShort(gcp.putClassName(extraClasses[i].inner));
output.writeShort(extraClasses[i].outer != null ?
gcp.putClassName(extraClasses[i].outer) : 0);
output.writeShort(extraClasses[i].name != null ?
gcp.putUTF8(extraClasses[i].name) : 0);
output.writeShort(extraClasses[i].modifiers);
}
}
if (deprecatedFlag) {
output.writeShort(gcp.putUTF8("Deprecated"));
output.writeInt(0);
}
}
public void write(DataOutputStream out) throws IOException {
GrowableConstantPool gcp = new GrowableConstantPool();
reserveSmallConstants(gcp);
prepareWriting(gcp);
out.writeInt(0xcafebabe);
out.writeShort(3);
out.writeShort(45);
gcp.write(out);
out.writeShort(modifiers);
out.writeShort(gcp.putClassName(name));
out.writeShort(gcp.putClassName(superclass.getName()));
out.writeShort(interfaces.length);
for (int i=0; i < interfaces.length; i++)
out.writeShort(gcp.putClassName(interfaces[i].getName()));
out.writeShort(fields.length);
for (int i=0; i < fields.length; i++)
fields[i].write(gcp, out);
out.writeShort(methods.length);
for (int i=0; i < methods.length; i++)
methods[i].write(gcp, out);
writeAttributes(gcp, out);
}
public void loadInfoReflection(Class clazz, int howMuch)
throws SecurityException {
if ((howMuch & HIERARCHY) != 0) {
modifiers = clazz.getModifiers();
if (clazz.getSuperclass() == null)
superclass = clazz == Object.class ? null : javaLangObject;
else
superclass = ClassInfo.forName
(clazz.getSuperclass().getName());
Class[] ifaces = clazz.getInterfaces();
interfaces = new ClassInfo[ifaces.length];
for (int i=0; i<ifaces.length; i++)
interfaces[i] = ClassInfo.forName(ifaces[i].getName());
status |= HIERARCHY;
}
if ((howMuch & FIELDS) != 0 && fields == null) {
Field[] fs;
try {
fs = clazz.getDeclaredFields();
} catch (SecurityException ex) {
fs = clazz.getFields();
GlobalOptions.err.println
("Could only get public fields of class "
+ name + ".");
}
fields = new FieldInfo[fs.length];
for (int i = fs.length; --i >= 0; ) {
String type = TypeSignature.getSignature(fs[i].getType());
fields[i] = new FieldInfo
(this, fs[i].getName(), type, fs[i].getModifiers());
}
}
if ((howMuch & METHODS) != 0 && methods == null) {
Constructor[] cs;
Method[] ms;
try {
cs = clazz.getDeclaredConstructors();
ms = clazz.getDeclaredMethods();
} catch (SecurityException ex) {
cs = clazz.getConstructors();
ms = clazz.getMethods();
GlobalOptions.err.println
("Could only get public methods of class "
+ name + ".");
}
methods = new MethodInfo[cs.length + ms.length];
for (int i = cs.length; --i >= 0; ) {
String type = TypeSignature.getSignature
(cs[i].getParameterTypes(), void.class);
methods[i] = new MethodInfo
(this, "<init>", type, cs[i].getModifiers());
}
for (int i = ms.length; --i >= 0; ) {
String type = TypeSignature.getSignature
(ms[i].getParameterTypes(), ms[i].getReturnType());
methods[cs.length+i] = new MethodInfo
(this, ms[i].getName(), type, ms[i].getModifiers());
}
}
if ((howMuch & INNERCLASSES) != 0 && innerClasses == null) {
Class[] is;
try {
is = clazz.getDeclaredClasses();
} catch (SecurityException ex) {
is = clazz.getClasses();
GlobalOptions.err.println
("Could only get public inner classes of class "
+ name + ".");
}
if (is.length > 0) {
innerClasses = new InnerClassInfo[is.length];
for (int i = is.length; --i >= 0; ) {
String inner = is[i].getName();
int dollar = inner.lastIndexOf('$');
String name = inner.substring(dollar+1);
innerClasses[i] = new InnerClassInfo
(inner, getName(), name, is[i].getModifiers());
}
}
}
if ((howMuch & OUTERCLASSES) != 0 && outerClasses == null) {
int count = 0;
Class declarer = clazz.getDeclaringClass();
while (declarer != null) {
count++;
declarer = declarer.getDeclaringClass();
}
if (count > 0) {
outerClasses = new InnerClassInfo[count];
Class current = clazz;
for (int i = 0; i < count; i++) {
declarer = current.getDeclaringClass();
String name = current.getName();
int dollar = name.lastIndexOf('$');
outerClasses[i] = new InnerClassInfo
(name, declarer.getName(),
name.substring(dollar+1), current.getModifiers());
current = declarer;
}
}
}
status |= howMuch;
}
public void loadInfo(int howMuch) {
if ((status & howMuch) == howMuch)
return;
if (modified) {
System.err.println("Allocating info 0x"
+ Integer.toHexString(howMuch)
+ " (status 0x" + Integer.toHexString(status)
+ ") in class " + this);
Thread.dumpStack();
return;
}
try {
DataInputStream input = new DataInputStream
(new BufferedInputStream
(classpath.getFile(name.replace('.', '/') + ".class")));
read(input, howMuch);
} catch (IOException ex) {
String message = ex.getMessage();
if ((howMuch & ~(FIELDS|METHODS|HIERARCHY
|INNERCLASSES|OUTERCLASSES)) != 0) {
throw new NoClassDefFoundError(name);
}
// Try getting the info through the reflection interface
// instead.
Class clazz = null;
try {
clazz = Class.forName(name);
} catch (ClassNotFoundException ex2) {
} catch (NoClassDefFoundError ex2) {
}
try {
if (clazz != null) {
loadInfoReflection(clazz, howMuch);
return;
}
} catch (SecurityException ex2) {
GlobalOptions.err.println
(ex2+" while collecting info about class " + name + ".");
}
// Give a warning and ``guess'' the hierarchie, methods etc.
GlobalOptions.err.println
("Can't read class " + name + ", types may be incorrect. ("
+ ex.getClass().getName()
+ (message != null ? ": " + message : "") + ")");
ex.printStackTrace(GlobalOptions.err);
if ((howMuch & HIERARCHY) != 0) {
modifiers = Modifier.PUBLIC;
if (name.equals("java.lang.Object"))
superclass = null;
else
superclass = javaLangObject;
interfaces = new ClassInfo[0];
}
if ((howMuch & METHODS) != 0)
methods = new MethodInfo[0];
if ((howMuch & FIELDS) != 0)
fields = new FieldInfo[0];
status |= howMuch;
}
}
/**
* This is the counter part to loadInfo. It will drop all info specified
* in howMuch and clean up the memory.
* @param howMuch tells how much info we should drop
*/
public void dropInfo(int howMuch) {
if ((status & howMuch) == 0)
return;
if (modified) {
System.err.println("Dropping info 0x"
+ Integer.toHexString(howMuch)
+ " (status 0x" + Integer.toHexString(status)
+ ") in class " + this);
Thread.dumpStack();
return;
}
howMuch &= status;
if ((howMuch & FIELDS) != 0) {
fields = null;
} else if ((status & FIELDS) != 0
&& (howMuch & (KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) {
for (int i=0; i < fields.length; i++)
fields[i].dropInfo(howMuch);
}
if ((howMuch & METHODS) != 0) {
methods = null;
} else if ((status & METHODS) != 0
&& (howMuch & (KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) {
for (int i=0; i < methods.length; i++)
methods[i].dropInfo(howMuch);
}
if ((howMuch & KNOWNATTRIBS) != 0)
sourceFile = null;
if ((howMuch & OUTERCLASSES) != 0)
outerClasses = null;
if ((howMuch & INNERCLASSES) != 0) {
innerClasses = null;
extraClasses = null;
}
super.dropInfo(howMuch);
status &= ~howMuch;
}
public String getName() {
return name;
}
public String getJavaName() {
/* Don't load attributes for class names not containing a
* dollar sign.
*/
if (name.indexOf('$') == -1)
return getName();
if (getOuterClasses() != null) {
int last = outerClasses.length-1;
StringBuffer sb =
new StringBuffer(outerClasses[last].outer != null
? outerClasses[last].outer : "METHOD");
for (int i=last; i >= 0; i--)
sb.append(".").append(outerClasses[i].name != null
? outerClasses[i].name : "ANONYMOUS");
return sb.toString();
}
return getName();
}
public ClassInfo getSuperclass() {
if ((status & HIERARCHY) == 0)
loadInfo(HIERARCHY);
return superclass;
}
public ClassInfo[] getInterfaces() {
if ((status & HIERARCHY) == 0)
loadInfo(HIERARCHY);
return interfaces;
}
public int getModifiers() {
if ((status & HIERARCHY) == 0)
loadInfo(HIERARCHY);
return modifiers;
}
public boolean isInterface() {
return Modifier.isInterface(getModifiers());
}
public boolean isDeprecated() {
return deprecatedFlag;
}
public FieldInfo findField(String name, String typeSig) {
if ((status & FIELDS) == 0)
loadInfo(FIELDS);
for (int i=0; i< fields.length; i++)
if (fields[i].getName().equals(name)
&& fields[i].getType().equals(typeSig))
return fields[i];
return null;
}
public MethodInfo findMethod(String name, String typeSig) {
if ((status & METHODS) == 0)
loadInfo(METHODS);
for (int i=0; i< methods.length; i++)
if (methods[i].getName().equals(name)
&& methods[i].getType().equals(typeSig))
return methods[i];
return null;
}
public MethodInfo[] getMethods() {
if ((status & METHODS) == 0)
loadInfo(METHODS);
return methods;
}
public FieldInfo[] getFields() {
if ((status & FIELDS) == 0)
loadInfo(FIELDS);
return fields;
}
public InnerClassInfo[] getOuterClasses() {
if ((status & OUTERCLASSES) == 0)
loadInfo(OUTERCLASSES);
return outerClasses;
}
public InnerClassInfo[] getInnerClasses() {
if ((status & INNERCLASSES) == 0)
loadInfo(INNERCLASSES);
return innerClasses;
}
public InnerClassInfo[] getExtraClasses() {
if ((status & INNERCLASSES) == 0)
loadInfo(INNERCLASSES);
return extraClasses;
}
public String getSourceFile() {
return sourceFile;
}
public void setName(String newName) {
name = newName;
modified = true;
}
public void setSuperclass(ClassInfo newSuper) {
superclass = newSuper;
modified = true;
}
public void setInterfaces(ClassInfo[] newIfaces) {
interfaces = newIfaces;
modified = true;
}
public void setModifiers(int newModifiers) {
modifiers = newModifiers;
modified = true;
}
public void setDeprecated(boolean flag) {
deprecatedFlag = flag;
}
public void setMethods(MethodInfo[] mi) {
methods = mi;
modified = true;
}
public void setFields(FieldInfo[] fi) {
fields = fi;
modified = true;
}
public void setOuterClasses(InnerClassInfo[] oc) {
outerClasses = oc;
modified = true;
}
public void setInnerClasses(InnerClassInfo[] ic) {
innerClasses = ic;
modified = true;
}
public void setExtraClasses(InnerClassInfo[] ec) {
extraClasses = ec;
modified = true;
}
public void setSourceFile(String newSource) {
sourceFile = newSource;
modified = true;
}
public boolean superClassOf(ClassInfo son) {
while (son != this && son != null) {
son = son.getSuperclass();
}
return son == this;
}
public boolean implementedBy(ClassInfo clazz) {
while (clazz != this && clazz != null) {
ClassInfo[] ifaces = clazz.getInterfaces();
for (int i=0; i< ifaces.length; i++) {
if (implementedBy(ifaces[i]))
return true;
}
clazz = clazz.getSuperclass();
}
return clazz == this;
}
public String toString() {
return name;
}
}

@ -17,23 +17,12 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.bytecode; package jode.bytecode;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.NoSuchElementException;
///#def COLLECTIONS java.util
import java.util.Iterator;
///#enddef
///#def COLLECTIONEXTRA java.lang
import java.lang.UnsupportedOperationException;
///#enddef
/** /**
* This class represent the constant pool. Normally you wont need to * This class represent the constant pool.
* touch this class, as ClassInfo already does all the hard work. You
* will only need it if you want to add your own custom attributes
* that use the constant pool.
* *
* @author Jochen Hoenicke * @author Jochen Hoenicke
*/ */
@ -117,12 +106,16 @@ public class ConstantPool {
} }
public String getUTF8(int i) throws ClassFormatException { public String getUTF8(int i) throws ClassFormatException {
if (i == 0)
return null;
if (tags[i] != UTF8) if (tags[i] != UTF8)
throw new ClassFormatException("Tag mismatch"); throw new ClassFormatException("Tag mismatch");
return (String)constants[i]; return (String)constants[i];
} }
public Reference getRef(int i) throws ClassFormatException { public Reference getRef(int i) throws ClassFormatException {
if (i == 0)
return null;
if (tags[i] != FIELDREF if (tags[i] != FIELDREF
&& tags[i] != METHODREF && tags[i] != INTERFACEMETHODREF) && tags[i] != METHODREF && tags[i] != INTERFACEMETHODREF)
throw new ClassFormatException("Tag mismatch"); throw new ClassFormatException("Tag mismatch");
@ -151,21 +144,20 @@ public class ConstantPool {
if (i == 0) if (i == 0)
throw new ClassFormatException("null constant"); throw new ClassFormatException("null constant");
switch (tags[i]) { switch (tags[i]) {
case INTEGER: case ConstantPool.INTEGER:
case FLOAT: case ConstantPool.FLOAT:
case LONG: case ConstantPool.LONG:
case DOUBLE: case ConstantPool.DOUBLE:
return constants[i]; return constants[i];
case CLASS: case ConstantPool.STRING:
return Reference.getReference(getClassType(i),
"class", "Ljava/lang/Class;");
case STRING:
return getUTF8(indices1[i]); return getUTF8(indices1[i]);
} }
throw new ClassFormatException("Tag mismatch: "+tags[i]); throw new ClassFormatException("Tag mismatch: "+tags[i]);
} }
public String getClassType(int i) throws ClassFormatException { public String getClassType(int i) throws ClassFormatException {
if (i == 0)
return null;
if (tags[i] != CLASS) if (tags[i] != CLASS)
throw new ClassFormatException("Tag mismatch"); throw new ClassFormatException("Tag mismatch");
String clName = getUTF8(indices1[i]); String clName = getUTF8(indices1[i]);
@ -177,59 +169,22 @@ public class ConstantPool {
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
throw new ClassFormatException(ex.getMessage()); throw new ClassFormatException(ex.getMessage());
} }
return clName; return clName;
} }
public String getClassName(int i) throws ClassFormatException { public String getClassName(int i) throws ClassFormatException {
if (i == 0)
return null;
if (tags[i] != CLASS) if (tags[i] != CLASS)
throw new ClassFormatException("Tag mismatch"); throw new ClassFormatException("Tag mismatch");
if (constants[i] == null) { String clName = getUTF8(indices1[i]);
String clName = getUTF8(indices1[i]); try {
try { TypeSignature.checkTypeSig("L"+clName+";");
TypeSignature.checkTypeSig("L"+clName+";"); } catch (IllegalArgumentException ex) {
} catch (IllegalArgumentException ex) { throw new ClassFormatException(ex.getMessage());
throw new ClassFormatException(ex.getMessage());
}
constants[i] = clName.replace('/','.').intern();
} }
return (String) constants[i]; return clName.replace('/','.').intern();
}
/**
* Iterates through all class entries in the class pool and returns
* their (dot seperated) class name.
*/
public Iterator iterateClassNames() {
return new Iterator()
{
int entry = 1;
public boolean hasNext() {
try {
while (entry < count
&& (tags[entry] != CLASS
|| getUTF8(indices1[entry])
.charAt(0) == '['))
entry++;
} catch (ClassFormatException ex) {
throw new InternalError(ex.getMessage());
}
return entry < count;
}
public Object next() {
if (!hasNext())
throw new NoSuchElementException();
try {
return getClassName(entry++);
} catch (ClassFormatException ex) {
throw new InternalError(ex.getMessage());
}
}
public void remove() {
throw new UnsupportedOperationException();
}
};
} }
public String toString(int i) { public String toString(int i) {
@ -270,7 +225,7 @@ public class ConstantPool {
} }
public String toString() { public String toString() {
StringBuffer result = new StringBuffer("ConstantPool[ null"); StringBuffer result = new StringBuffer("[ null");
for (int i=1; i< count; i++) { for (int i=1; i< count; i++) {
result.append(", ").append(i).append(" = ").append(toString(i)); result.append(", ").append(i).append(" = ").append(toString(i));
} }

@ -0,0 +1,206 @@
/* FieldInfo Copyright (C) 1998-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
public class FieldInfo extends BinaryInfo {
ClassInfo clazzInfo;
int modifier;
String name;
String typeSig;
Object constant;
boolean syntheticFlag;
boolean deprecatedFlag;
public FieldInfo(ClassInfo ci) {
this.clazzInfo = ci;
}
public FieldInfo(ClassInfo ci, String name, String typeSig, int modifier) {
this.clazzInfo = ci;
this.name = name;
this.typeSig = typeSig;
this.modifier = modifier;
}
protected void readAttribute(String name, int length,
ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
if (name.equals("ConstantValue")) {
if ((howMuch & KNOWNATTRIBS) != 0) {
if (length != 2)
throw new ClassFormatException("ConstantValue attribute"
+ " has wrong length");
int index = input.readUnsignedShort();
constant = cp.getConstant(index);
} else
input.readFully(new byte[length]);
} else if (name.equals("Synthetic")) {
syntheticFlag = true;
if (length != 0)
throw new ClassFormatException
("Synthetic attribute has wrong length");
} else if (name.equals("Deprecated")) {
deprecatedFlag = true;
if (length != 0)
throw new ClassFormatException
("Deprecated attribute has wrong length");
} else
super.readAttribute(name, length, cp, input, howMuch);
}
public void read(ConstantPool constantPool,
DataInputStream input, int howMuch) throws IOException {
modifier = input.readUnsignedShort();
name = constantPool.getUTF8(input.readUnsignedShort());
typeSig = constantPool.getUTF8(input.readUnsignedShort());
readAttributes(constantPool, input, howMuch);
}
public void reserveSmallConstants(GrowableConstantPool gcp) {
}
public void prepareWriting(GrowableConstantPool gcp) {
gcp.putUTF8(name);
gcp.putUTF8(typeSig);
if (constant != null) {
gcp.putUTF8("ConstantValue");
if (typeSig.charAt(0) == 'J' || typeSig.charAt(0) == 'D')
gcp.putLongConstant(constant);
else
gcp.putConstant(constant);
}
if (syntheticFlag)
gcp.putUTF8("Synthetic");
if (deprecatedFlag)
gcp.putUTF8("Deprecated");
prepareAttributes(gcp);
}
protected int getKnownAttributeCount() {
int count = 0;
if (constant != null)
count++;
if (syntheticFlag)
count++;
if (deprecatedFlag)
count++;
return count;
}
public void writeKnownAttributes(GrowableConstantPool gcp,
DataOutputStream output)
throws IOException {
if (constant != null) {
output.writeShort(gcp.putUTF8("ConstantValue"));
output.writeInt(2);
int index;
if (typeSig.charAt(0) == 'J'
|| typeSig.charAt(0) == 'D')
index = gcp.putLongConstant(constant);
else
index = gcp.putConstant(constant);
output.writeShort(index);
}
if (syntheticFlag) {
output.writeShort(gcp.putUTF8("Synthetic"));
output.writeInt(0);
}
if (deprecatedFlag) {
output.writeShort(gcp.putUTF8("Deprecated"));
output.writeInt(0);
}
}
public void write(GrowableConstantPool constantPool,
DataOutputStream output) throws IOException {
output.writeShort(modifier);
output.writeShort(constantPool.putUTF8(name));
output.writeShort(constantPool.putUTF8(typeSig));
writeAttributes(constantPool, output);
}
public void dropInfo(int howMuch) {
if ((howMuch & KNOWNATTRIBS) != 0)
constant = null;
super.dropInfo(howMuch);
}
public String getName() {
return name;
}
public String getType() {
return typeSig;
}
public int getModifiers() {
return modifier;
}
public boolean isSynthetic() {
return syntheticFlag;
}
public boolean isDeprecated() {
return deprecatedFlag;
}
public Object getConstant() {
clazzInfo.loadInfo(KNOWNATTRIBS);
return constant;
}
public void setName(String newName) {
name = newName;
}
public void setType(String newType) {
typeSig = newType;
}
public void setModifiers(int newModifier) {
modifier = newModifier;
}
public void setSynthetic(boolean flag) {
syntheticFlag = flag;
}
public void setDeprecated(boolean flag) {
deprecatedFlag = flag;
}
public void setConstant(Object newConstant) {
constant = newConstant;
}
public String toString() {
return "Field "+Modifier.toString(modifier)+" "+
typeSig+" "+name;
}
}

@ -17,16 +17,13 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.bytecode; package jode.bytecode;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Hashtable; import java.util.Hashtable;
/** /**
* This class represent a constant pool, where new constants can be * This class represent a constant pool, where new constants can be added to.
* added to. Normally you wont need to touch this class, as ClassInfo
* already does all the hard work. You will only need it if you want
* to add your own custom attributes that use the constant pool.
* *
* @author Jochen Hoenicke * @author Jochen Hoenicke
*/ */
@ -62,9 +59,6 @@ public class GrowableConstantPool extends ConstantPool {
} }
} }
/**
* Create a new growable constant pool
*/
public GrowableConstantPool () { public GrowableConstantPool () {
count = 1; count = 1;
tags = new int[128]; tags = new int[128];
@ -74,13 +68,11 @@ public class GrowableConstantPool extends ConstantPool {
written = false; written = false;
} }
private final void grow(int wantedSize) { public final void grow(int wantedSize) {
if (written) if (written)
throw new IllegalStateException("adding to written ConstantPool"); throw new IllegalStateException("adding to written ConstantPool");
if (wantedSize > 65535)
throw new IllegalArgumentException("Too many constants added");
if (tags.length < wantedSize) { if (tags.length < wantedSize) {
int newSize = Math.min(65535, Math.max(tags.length*2, wantedSize)); int newSize = Math.max(tags.length*2, wantedSize);
int[] tmpints = new int[newSize]; int[] tmpints = new int[newSize];
System.arraycopy(tags, 0, tmpints, 0, count); System.arraycopy(tags, 0, tmpints, 0, count);
tags = tmpints; tags = tmpints;
@ -125,7 +117,7 @@ public class GrowableConstantPool extends ConstantPool {
return newIndex; return newIndex;
} }
private int putIndexed(int tag, Object obj1, int index1, int index2) { int putIndexed(int tag, Object obj1, int index1, int index2) {
Key key = new Key(tag, obj1, index2); Key key = new Key(tag, obj1, index2);
Integer indexObj = (Integer) entryToIndex.get(key); Integer indexObj = (Integer) entryToIndex.get(key);
if (indexObj != null) { if (indexObj != null) {
@ -143,45 +135,16 @@ public class GrowableConstantPool extends ConstantPool {
return count++; return count++;
} }
/**
* Adds a new UTF8 entry to the constant pool and returns the index.
* If it already exists it will reuse the previous entry.
* @param utf the UTF8 string.
* @return the index of the pool entry.
* @exception IllegalStateException if the pool was already written,
* but the entry was not added before.
*/
public final int putUTF8(String utf) { public final int putUTF8(String utf) {
return putConstant(UTF8, utf); return putConstant(UTF8, utf);
} }
/**
* Adds a new class name entry to the constant pool and returns
* the index. If it already exists it will reuse the previous
* entry.
* @param name the dot separated full qualified class name.
* @return the index of the pool entry.
* @exception IllegalArgumentException if the class name is illegal.
* @exception IllegalStateException if the pool was already written,
* but the entry was not added before.
*/
public int putClassName(String name) { public int putClassName(String name) {
name = name.replace('.','/'); name = name.replace('.','/');
TypeSignature.checkTypeSig("L"+name+";"); TypeSignature.checkTypeSig("L"+name+";");
return putIndexed(CLASS, name, putUTF8(name), 0); return putIndexed(CLASS, name, putUTF8(name), 0);
} }
/**
* Adds a new class entry to the constant pool and returns
* the index. If it already exists it will reuse the previous
* entry. This is the same as putClassName, except for the format
* of the parameter and that it can also handle arrays.
* @param name the type signature of the class to add.
* @return the index of the pool entry.
* @exception IllegalArgumentException if the class name is illegal.
* @exception IllegalStateException if the pool was already written,
* but the entry was not added before.
*/
public int putClassType(String name) { public int putClassType(String name) {
TypeSignature.checkTypeSig(name); TypeSignature.checkTypeSig(name);
if (name.charAt(0) == 'L') if (name.charAt(0) == 'L')
@ -191,19 +154,6 @@ public class GrowableConstantPool extends ConstantPool {
return putIndexed(CLASS, name, putUTF8(name), 0); return putIndexed(CLASS, name, putUTF8(name), 0);
} }
/**
* Adds a new field/method or interface reference to the constant
* pool and returns the index. If it already exists it will reuse
* the previous entry.
* @param tag the tag of the reference, one of FIELDREF, METHODREF
* or INTERFACEMETHODREF.
* @param ref the reference.
* @return the index of the pool entry.
* @exception IllegalArgumentException if the reference type or
* class name is illegal.
* @exception IllegalStateException if the pool was already written,
* but the entry was not added before.
*/
public int putRef(int tag, Reference ref) { public int putRef(int tag, Reference ref) {
String className = ref.getClazz(); String className = ref.getClazz();
String typeSig = ref.getType(); String typeSig = ref.getType();
@ -222,14 +172,10 @@ public class GrowableConstantPool extends ConstantPool {
} }
/** /**
* Puts a "one slot" constant into this constant pool. If it * Puts a constant into this constant pool
* already exists it will reuse the previous entry. * @param c the constant, must be of type
* @param c the constant; must be of type * Integer, Long, Float, Double or String
* Integer, Float or String * @return the index into the pool of this constant.
* @return the index of the pool entry.
* @exception IllegalArgumentException if the constant is of wrong type.
* @exception IllegalStateException if the pool was already written,
* but the entry was not added before.
*/ */
public int putConstant(Object c) { public int putConstant(Object c) {
if (c instanceof String) { if (c instanceof String) {
@ -248,13 +194,10 @@ public class GrowableConstantPool extends ConstantPool {
} }
/** /**
* Puts a "double slot" constant into this constant pool. If it * Puts a constant into this constant pool
* already exists it will reuse the previous entry. * @param c the constant, must be of type
* @param c the constant; must be of type Long or Double * Integer, Long, Float, Double or String
* @return the index of the pool entry. * @return the index into the pool of this constant.
* @exception IllegalArgumentException if the constant is of wrong type.
* @exception IllegalStateException if the pool was already written,
* but the entry was not added before.
*/ */
public int putLongConstant(Object c) { public int putLongConstant(Object c) {
int tag; int tag;
@ -269,11 +212,9 @@ public class GrowableConstantPool extends ConstantPool {
} }
/** /**
* Reserves an entry in this constant pool for a constant (for ldc). * Reserve an entry in this constant pool for a constant (for ldc).
* The constant must still be put into the pool, before the pool may
* be written.
* @param c the constant, must be of type * @param c the constant, must be of type
* Integer, Float or String * Integer, Long, Float, Double or String
* @return the reserved index into the pool of this constant. * @return the reserved index into the pool of this constant.
*/ */
public int reserveConstant(Object c) { public int reserveConstant(Object c) {
@ -285,11 +226,20 @@ public class GrowableConstantPool extends ConstantPool {
} }
/** /**
* Writes the constant pool to the stream. This is normally called * Reserve an entry in this constant pool for a constant (for ldc).
* by {@link ClassInfo#write}. * @param c the constant, must be of type
* @param stream the stream to write to. * Integer, Long, Float, Double or String
* @exception IOException if it occured while writing. * @return the reserved index into the pool of this constant.
*/ */
public int reserveLongConstant(Object c) {
return putLongConstant(c);
}
public int copyConstant(ConstantPool cp, int index)
throws ClassFormatException {
return putConstant(cp.getConstant(index));
}
public void write(DataOutputStream stream) public void write(DataOutputStream stream)
throws IOException { throws IOException {
written = true; written = true;

@ -0,0 +1,29 @@
/* Handler Copyright (C) 1999-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
/**
* A simple class containing the info about an exception handler
*/
public class Handler {
public Instruction start, end, catcher;
public String type;
}

@ -1,4 +1,4 @@
/* TypeInstruction Copyright (C) 1999-2002 Jochen Hoenicke. /* InnerClassInfo Copyright (C) 1999-2002 Jochen Hoenicke.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@ -17,34 +17,26 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.bytecode; package jode.bytecode;
/** /**
* This class represents an instruction in the byte code. * A simple class containing the info about an inner class.
*
*/ */
class TypeInstruction extends Instruction { public class InnerClassInfo {
/** public String inner, outer;
* The typesignature of the class/array. public String name;
*/ public int modifiers;
private String typeSig;
public TypeInstruction(int opcode, String typeSig) {
super(opcode);
this.typeSig = typeSig;
}
public final String getClazzType() public InnerClassInfo(String inner, String outer, String name, int modif) {
{ this.inner = inner;
return typeSig; this.outer = outer;
} this.name = name;
this.modifiers = modif;
public final void setClazzType(String type)
{
typeSig = type;
} }
public String toString() { public String toString() {
return super.toString()+' '+typeSig; return "InnerClassInfo["+inner+","+outer+","+name+","
+java.lang.reflect.Modifier.toString(modifiers)+"]";
} }
} }

@ -0,0 +1,830 @@
/* Instruction Copyright (C) 1999-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
/**
* This class represents an instruction in the byte code.
*
*/
public final class Instruction implements Opcodes{
/**
* The opcode of the instruction. We map some opcodes, e.g.
* <pre>
* iload_[0-3] -> iload, ldc_w -> ldc, wide iinc -> iinc.
* </pre>
*/
// a byte would be enough, but then we would need an unsigned convert.
private int opcode;
/**
* If this opcode uses a local this gives the slot. For multianewarray
* this gives the dimension.
*/
private int shortData;
/**
* The address of this opcode.
*/
private int addr;
/**
* Optional object data for this opcode. There are four different
* usages of this field:
* <dl>
* <dt>opc_ldc / opc_ldc2_w</dt>
* <dd>The constant of type Integer/Long/Float/Double/String. </dd>
* <dt>opc_invokexxx / opc_xxxfield / opc_xxxstatic</dt>
* <dd>The field/method Reference</dd>
* <dt>opc_new / opc_checkcast / opc_instanceof / opc_multianewarray</dt>
* <dd>The typesignature of the class/array</dd>
* <dt>opc_lookupswitch</dt>
* <dd>The array of values of type int[]</dd>
* </dl>
*/
private Object objData;
/**
* The successors of this opcodes, where flow may lead to
* (except that nextByAddr is implicit if !alwaysJump). The
* value null means no successor, if there is one succesor, this
* is of type Instruction, otherwise, this is an array of Instruction.
*/
private Object succs;
/**
* The predecessors of this opcode, orthogonal to the succs array.
* This must be null or a non empty array.
*/
private Instruction[] preds;
/**
* The next instruction in code order.
*/
Instruction nextByAddr;
/**
* The previous instruction in code order, useful when changing
* the order.
*/
Instruction prevByAddr;
/**
* You can use this field to add some info to each instruction.
* After using, you must set it to null again.
* @XXX Do we really need this. Every field here can quickly take
* half a megabyte!
*/
private Object tmpInfo;
public Instruction(int opcode) {
this.opcode = opcode;
}
/**
* Returns the opcode of the instruction. We map some opcodes:
* <pre>
* [iflda]load_x -&gt; [iflda]load
* [iflda]store_x -&gt; [iflda]store
* [ifa]const_xx, ldc_w -&gt; ldc
* [dl]const_xx -&gt; ldc2_w
* wide opcode -&gt; opcode
* tableswitch -&gt; lookupswitch
* [a]newarray -&gt; multianewarray
* </pre>
*/
public final int getOpcode() {
return opcode;
}
/**
* Returns the address of this opcode. As long as you don't remove
* or insert instructions, you can be sure, that the addresses of the
* opcodes are unique, and that
* <pre>
* instr.getAddr() + instr.getLength() == instr.getNextByAddr().getAddr()
* <pre>
*
* If you insert/remove Instructions, you should be aware that the
* above property is not guaranteed anymore.
*/
public final int getAddr() {
return addr;
}
public final int getNextAddr() {
return nextByAddr.addr;
}
/**
* Returns the length of this opcode. See getAddr() for some
* notes. Note that the length doesn't necessarily reflect the
* real length, when this bytecode is written again, since the
* length of an ldc instruction depends on the number of entries
* in constant pool, and the order they are allocated.
*/
public final int getLength() {
return getNextAddr() - addr;
}
final void setAddr(int addr) {
this.addr = addr;
}
public final boolean hasLocalSlot() {
return opcode == opc_iinc || opcode == opc_ret
|| opcode >= opc_iload && opcode <= opc_aload
|| opcode >= opc_istore && opcode <= opc_astore;
}
public final int getLocalSlot()
/*{ require { hasLocalSlot()
:: "Instruction has no slot" } }*/
{
return shortData;
}
public final void setLocalSlot(int slot)
/*{ require { hasLocalSlot()
:: "Instruction has no slot" } }*/
{
shortData = slot;
}
/**
* Optional integer data for this opcode. There are various uses
* for this:
* <dl>
* <dt>opc_iinc</dt>
* <dd>The value by which the constant is increased/decreased. (short)</dd>
* <dt>opc_multianewarray</dt>
* <dd>The number of dimensions (1..255)</dd>
* </dl>
*/
public final int getIncrement()
/*{ require { opcode == opc_iinc || opcode == opc_multianewarray
|| opcode == opc_tableswitch
:: "Instruction has no int data" } }*/
{
/* shortData already used for local slot */
return ((Short) objData).shortValue();
}
/**
* Optional integer data for this opcode. There are various uses
* for this:
* <dl>
* <dt>opc_iinc</dt>
* <dd>The value by which the constant is increased/decreased. (short)</dd>
* <dt>opc_multianewarray</dt>
* <dd>The number of dimensions (1..255)</dd>
* </dl>
*/
public final void setIncrement(int incr)
/*{ require { opcode == opc_iinc || opcode == opc_multianewarray
:: "Instruction has no int data" } }*/
{
/* shortData already used for local slot */
objData = new Short((short) incr);
}
/**
*
*/
public final int getDimensions()
/*{ require { opcode == opc_multianewarray
:: "Instruction has no dimensions" } }*/
{
return shortData;
}
/**
*
*/
public final void setDimensions(int dims)
/*{ require { opcode == opc_multianewarray
:: "Instruction has no dimensions" } }*/
{
shortData = dims;
}
public final Object getConstant()
/*{ require { opcode == opc_ldc || opcode == opc_ldc2_w
:: "Instruction has no constant" } }*/
{
return objData;
}
public final void setConstant(Object constant)
/*{ require { opcode == opc_ldc || opcode == opc_ldc2_w
:: "Instruction has no constant" } }*/
{
objData = constant;
}
public final Reference getReference()
/*{ require { opcode >= opc_getstatic && opcode <= opc_invokeinterface
:: "Instruction has no reference" } }*/
{
return (Reference) objData;
}
public final void setReference(Reference ref)
/*{ require { opcode >= opc_getstatic && opcode <= opc_invokeinterface
:: "Instruction has no reference" } }*/
{
objData = ref;
}
public final String getClazzType()
/*{ require { opcode == opc_new
|| opcode == opc_checkcast
|| opcode == opc_instanceof
|| opcode == opc_multianewarray
:: "Instruction has no typesig" } }*/
{
return (String) objData;
}
public final void setClazzType(String type)
/*{ require { opcode == opc_new
|| opcode == opc_checkcast
|| opcode == opc_instanceof
|| opcode == opc_multianewarray
:: "Instruction has no typesig" } }*/
{
objData = type;
}
public final int[] getValues()
/*{ require { opcode == opc_lookupswitch
:: "Instruction has no values" } }*/
{
return (int[]) objData;
}
public final void setValues(int[] values)
/*{ require { opcode == opc_lookupswitch
:: "Instruction has no values" } }*/
{
objData = values;
}
public final boolean doesAlwaysJump() {
switch (opcode) {
case opc_ret:
case opc_goto:
case opc_jsr:
case opc_tableswitch:
case opc_lookupswitch:
case opc_ireturn:
case opc_lreturn:
case opc_freturn:
case opc_dreturn:
case opc_areturn:
case opc_return:
case opc_athrow:
return true;
default:
return false;
}
}
public final Instruction[] getPreds() {
return preds;
}
/**
* Returns true if this opcode has successors, other than the implicit
* getNextByAddr().
*/
public boolean hasSuccs() {
return succs != null;
}
/**
* Returns the successors of this opcodes, where flow may lead to
* (except that nextByAddr is implicit if !alwaysJump). The
* value null means that there is no successor.
*/
public final Instruction[] getSuccs() {
if (succs instanceof Instruction)
return new Instruction[] { (Instruction) succs };
return (Instruction[]) succs;
}
/**
* Returns the single successor of this opcodes. This gives the
* target of a goto, jsr, or if opcode.
* @return null if there is no successor, otherwise the successor.
* @exception ClassCastException if this has more than one succ.
*/
public final Instruction getSingleSucc() {
return (Instruction) succs;
}
public final Instruction getPrevByAddr() {
if (prevByAddr.opcode == opc_impdep1)
return null;
return prevByAddr;
}
public final Instruction getNextByAddr() {
if (nextByAddr.opcode == opc_impdep1)
return null;
return nextByAddr;
}
public final Object getTmpInfo() {
return tmpInfo;
}
public final void setTmpInfo(Object info) {
tmpInfo = info;
}
// INTERNAL FUNCTIONS TO KEEP PREDS AND SUCCS CONSISTENT
final void removeSuccs() {
if (succs == null)
return;
if (succs instanceof Instruction[]) {
Instruction[] ss = (Instruction[]) succs;
for (int i = 0; i < ss.length; i++)
if (ss[i] != null)
ss[i].removePredecessor(this);
} else
((Instruction) succs).removePredecessor(this);
succs = null;
}
/**
* @param to may be null
*/
private final void promoteSuccs(Instruction from, Instruction to) {
if (succs == from)
succs = to;
else if (succs instanceof Instruction[]) {
Instruction[] ss = (Instruction[]) succs;
for (int i = 0; i < ss.length; i++)
if (ss[i] == from)
ss[i] = to;
}
}
/**
* @exception ClassCastException if newSuccs is neither an Instruction
* nor an array of instructions.
*/
public final void setSuccs(Object newSuccs) {
if (succs == newSuccs)
return;
removeSuccs();
if (newSuccs == null)
return;
if (newSuccs instanceof Instruction[]) {
Instruction[] ns = (Instruction[]) newSuccs;
switch (ns.length) {
case 0:
break;
case 1:
succs = ns[0];
ns[0].addPredecessor(this);
break;
default:
succs = ns;
for (int i = 0; i < ns.length; i++)
ns[i].addPredecessor(this);
break;
}
} else {
succs = newSuccs;
((Instruction) newSuccs).addPredecessor(this);
}
}
void addPredecessor(Instruction pred) {
if (preds == null) {
preds = new Instruction[] { pred };
return;
}
int predsLength = preds.length;
Instruction[] newPreds = new Instruction[predsLength+1];
System.arraycopy(preds, 0, newPreds, 0, predsLength);
newPreds[predsLength] = pred;
preds = newPreds;
}
void removePredecessor(Instruction pred) {
/* Hopefully it doesn't matter if this is slow */
int predLength = preds.length;
if (predLength == 1) {
if (preds[0] != pred)
throw new jode.AssertError
("removing not existing predecessor");
preds = null;
} else {
Instruction[] newPreds = new Instruction[predLength-1];
int j;
for (j = 0; preds[j] != pred; j++)
newPreds[j] = preds[j];
System.arraycopy(preds, j+1, newPreds, j, predLength - j - 1);
preds = newPreds;
}
}
// ADDING, REMOVING AND REPLACING INSTRUCTIONS
/**
* Replaces the opcode of this instruction. You should only use the
* mapped opcodes:
* <pre>
* [iflda]load_x -&gt; [iflda]load
* [iflda]store_x -&gt; [iflda]store
* [ifa]const_xx, ldc_w -&gt; ldc
* [dl]const_xx -&gt; ldc2_w
* wide opcode -&gt; opcode
* tableswitch -&gt; lookupswitch
* [a]newarray -&gt; multianewarray
* </pre>
*/
public final void replaceInstruction(Instruction newInstr,
BytecodeInfo codeinfo) {
/* remove predecessors of successors */
removeSuccs();
newInstr.addr = addr;
nextByAddr.prevByAddr = newInstr;
newInstr.nextByAddr = nextByAddr;
prevByAddr.nextByAddr = newInstr;
newInstr.prevByAddr = prevByAddr;
prevByAddr = null;
nextByAddr = null;
/* promote the successors of the predecessors to newInstr */
if (preds != null) {
for (int j=0; j < preds.length; j++)
preds[j].promoteSuccs(this, newInstr);
newInstr.preds = preds;
preds = null;
}
/* adjust exception handlers */
Handler[] handlers = codeinfo.getExceptionHandlers();
for (int i=0; i< handlers.length; i++) {
if (handlers[i].start == this)
handlers[i].start = newInstr;
if (handlers[i].end == this)
handlers[i].end = newInstr;
if (handlers[i].catcher == this)
handlers[i].catcher = newInstr;
}
/* adjust local variable table and line number table */
LocalVariableInfo[] lvt = codeinfo.getLocalVariableTable();
if (lvt != null) {
for (int i=0; i< lvt.length; i++) {
if (lvt[i].start == this)
lvt[i].start = newInstr;
if (lvt[i].end == this)
lvt[i].end = newInstr;
}
}
LineNumber[] lnt = codeinfo.getLineNumberTable();
if (lnt != null) {
for (int i=0; i< lnt.length; i++) {
if (lnt[i].start == this)
lnt[i].start = newInstr;
}
}
}
void appendInstruction(Instruction newInstr, BytecodeInfo codeinfo) {
newInstr.addr = nextByAddr.addr;
newInstr.nextByAddr = nextByAddr;
nextByAddr.prevByAddr = newInstr;
newInstr.prevByAddr = this;
nextByAddr = newInstr;
/* adjust exception handlers end */
Handler[] handlers = codeinfo.getExceptionHandlers();
if (handlers != null) {
for (int i=0; i< handlers.length; i++) {
if (handlers[i].end == this)
handlers[i].end = newInstr;
}
}
}
/**
* Removes this instruction (as if it would be replaced by a nop).
*/
void removeInstruction(BytecodeInfo codeinfo) {
/* remove from chained list and adjust addr / length */
prevByAddr.nextByAddr = nextByAddr;
nextByAddr.prevByAddr = prevByAddr;
/* remove predecessors of successors */
removeSuccs();
/* promote the predecessors to next instruction */
if (preds != null) {
for (int j=0; j < preds.length; j++)
preds[j].promoteSuccs(this, nextByAddr);
if (nextByAddr.preds == null)
nextByAddr.preds = preds;
else {
Instruction[] newPreds = new Instruction
[nextByAddr.preds.length + preds.length];
System.arraycopy(nextByAddr.preds, 0, newPreds, 0,
nextByAddr.preds.length);
System.arraycopy(preds, 0, newPreds, nextByAddr.preds.length,
preds.length);
nextByAddr.preds = newPreds;
}
preds = null;
}
/* adjust exception handlers */
Handler[] handlers = codeinfo.getExceptionHandlers();
for (int i=0; i< handlers.length; i++) {
if (handlers[i].start == this && handlers[i].end == this) {
/* Remove the handler.
* This is very seldom, so we can make it slow */
Handler[] newHandlers = new Handler[handlers.length - 1];
System.arraycopy(handlers, 0, newHandlers, 0, i);
System.arraycopy(handlers, i+1, newHandlers, i,
handlers.length - (i+1));
handlers = newHandlers;
codeinfo.setExceptionHandlers(newHandlers);
i--;
} else {
if (handlers[i].start == this)
handlers[i].start = nextByAddr;
if (handlers[i].end == this)
handlers[i].end = prevByAddr;
if (handlers[i].catcher == this)
handlers[i].catcher = nextByAddr;
}
}
/* adjust local variable table and line number table */
LocalVariableInfo[] lvt = codeinfo.getLocalVariableTable();
if (lvt != null) {
for (int i=0; i< lvt.length; i++) {
if (lvt[i].start == this && lvt[i].end == this) {
/* Remove the local variable info.
* This is very seldom, so we can make it slow
*/
LocalVariableInfo[] newLVT =
new LocalVariableInfo[lvt.length - 1];
System.arraycopy(lvt, 0, newLVT, 0, i);
System.arraycopy(lvt, i+1, newLVT, i,
newLVT.length - i);
lvt = newLVT;
codeinfo.setLocalVariableTable(newLVT);
i--;
} else {
if (lvt[i].start == this)
lvt[i].start = nextByAddr;
if (lvt[i].end == this)
lvt[i].end = prevByAddr;
}
}
}
LineNumber[] lnt = codeinfo.getLineNumberTable();
if (lnt != null) {
for (int i=0; i< lnt.length; i++) {
if (lnt[i].start == this) {
if (nextByAddr.opcode == opc_impdep1
|| (i+1 < lnt.length
&& lnt[i+1].start == nextByAddr)) {
/* Remove the line number.
* This is very seldom, so we can make it slow */
LineNumber[] newLNT =
new LineNumber[lnt.length - 1];
System.arraycopy(lnt, 0, newLNT, 0, i);
System.arraycopy(lnt, i+1, newLNT, i,
newLNT.length - i);
lnt = newLNT;
codeinfo.setLineNumberTable(newLNT);
i--;
} else
lnt[i].start = nextByAddr;
}
}
}
prevByAddr = null;
nextByAddr = null;
}
public int compareTo(Instruction instr) {
if (addr != instr.addr)
return addr - instr.addr;
if (this == instr)
return 0;
do {
instr = instr.nextByAddr;
if (instr.addr > addr)
return -1;
} while (instr != this);
return 1;
}
/**
* This returns the number of stack entries this instruction
* pushes and pops from the stack. The result fills the given
* array.
*
* @param poppush an array of two ints. The first element will
* get the number of pops, the second the number of pushes.
*/
public void getStackPopPush(int[] poppush)
/*{ require { poppush != null && poppush.length == 2
:: "poppush must be an array of two ints" } } */
{
byte delta = (byte) stackDelta.charAt(opcode);
if (delta < 0x40) {
poppush[0] = delta & 7;
poppush[1] = delta >> 3;
} else {
switch (opcode) {
case opc_invokevirtual:
case opc_invokespecial:
case opc_invokestatic:
case opc_invokeinterface: {
Reference ref = getReference();
String typeSig = ref.getType();
poppush[0] = opcode != opc_invokestatic ? 1 : 0;
poppush[0] += TypeSignature.getArgumentSize(typeSig);
poppush[1] = TypeSignature.getReturnSize(typeSig);
break;
}
case opc_putfield:
case opc_putstatic: {
Reference ref = getReference();
poppush[1] = 0;
poppush[0] = TypeSignature.getTypeSize(ref.getType());
if (opcode == opc_putfield)
poppush[0]++;
break;
}
case opc_getstatic:
case opc_getfield: {
Reference ref = getReference();
poppush[1] = TypeSignature.getTypeSize(ref.getType());
poppush[0] = opcode == opc_getfield ? 1 : 0;
break;
}
case opc_multianewarray: {
poppush[1] = 1;
poppush[0] = getDimensions();
break;
}
default:
throw new jode.AssertError("Unknown Opcode: "+opcode);
}
}
}
public Instruction findMatchingPop() {
int poppush[] = new int[2];
getStackPopPush(poppush);
int count = poppush[1];
Instruction instr = this;
while (true) {
if (instr.succs != null || instr.doesAlwaysJump())
return null;
instr = instr.nextByAddr;
if (instr.preds != null)
return null;
instr.getStackPopPush(poppush);
if (count == poppush[0])
return instr;
count += poppush[1] - poppush[0];
}
}
public Instruction findMatchingPush() {
int count = 0;
Instruction instr = this;
int poppush[] = new int[2];
while (true) {
if (instr.preds != null)
return null;
instr = instr.prevByAddr;
if (instr == null || instr.succs != null || instr.doesAlwaysJump())
return null;
instr.getStackPopPush(poppush);
if (count < poppush[1]) {
return count == 0 ? instr : null;
}
count += poppush[0] - poppush[1];
}
}
public String getDescription() {
StringBuffer result = new StringBuffer(String.valueOf(addr))
.append('_').append(Integer.toHexString(hashCode()))
.append(": ").append(opcodeString[opcode]);
if (opcode != opc_lookupswitch) {
if (hasLocalSlot())
result.append(' ').append(getLocalSlot());
if (succs != null)
result.append(' ').append(((Instruction) succs).addr);
if (objData != null)
result.append(' ').append(objData);
if (opcode == opc_multianewarray)
result.append(' ').append(getDimensions());
} else {
int[] values = getValues();
Instruction[] succs = getSuccs();
for (int i=0; i < values.length; i++) {
result.append(' ').append(values[i]).append("->")
.append(((Instruction) succs[i]).addr);
}
result.append(' ').append("default: ")
.append(((Instruction) succs[values.length]).addr);
}
return result.toString();
}
public String toString() {
return "" + addr + "_" + Integer.toHexString(hashCode());
}
private final static String stackDelta =
"\000\010\010\010\010\010\010\010\010\020\020\010\010\010\020\020\010\010\010\010\020\010\020\010\020\010\010\010\010\010\020\020\020\020\010\010\010\010\020\020\020\020\010\010\010\010\012\022\012\022\012\012\012\012\001\002\001\002\001\001\001\001\001\002\002\002\002\001\001\001\001\002\002\002\002\001\001\001\001\003\004\003\004\003\003\003\003\001\002\021\032\043\042\053\064\022\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\011\022\011\022\012\023\012\023\012\023\012\024\012\024\012\024\000\021\011\021\012\012\022\011\021\021\012\022\012\011\011\011\014\012\012\014\014\001\001\001\001\001\001\002\002\002\002\002\002\002\002\000\010\000\001\001\001\002\001\002\001\000\100\100\100\100\100\100\100\100\177\010\011\011\011\001\011\011\001\001\177\100\001\001\000\010";
/* stackDelta contains \100 if stack count of opcode is variable
* \177 if opcode is illegal, or 8*stack_push + stack_pop otherwise
* The above values are extracted from following list with:
* perl -ne'/"(.*)"/ and print $1'
*
* "\000" // nop
* "\010\010\010\010\010\010\010\010" // aconst_null, iconst_m?[0-5]
* "\020\020\010\010\010\020\020" // [lfd]const_[0-2]
* "\010\010\010\010\020" // sipush bipush ldcx
* "\010\020\010\020\010" // [ilfda]load
* "\010\010\010\010"
* "\020\020\020\020"
* "\010\010\010\010"
* "\020\020\020\020"
* "\010\010\010\010"
* "\012\022\012\022\012\012\012\012" // [ilfdabcs]aload
* "\001\002\001\002\001" // [ilfda]store
* "\001\001\001\001"
* "\002\002\002\002"
* "\001\001\001\001"
* "\002\002\002\002"
* "\001\001\001\001"
* "\003\004\003\004\003\003\003\003" // [ilfdabcs]astore
* "\001\002" // pop
* "\021\032\043\042\053\064" // dup2?(_x[12])?
* "\022" // swap
* "\012\024\012\024" // [ilfd]add
* "\012\024\012\024" // [ilfd]sub
* "\012\024\012\024" // [ilfd]mul
* "\012\024\012\024" // [ilfd]div
* "\012\024\012\024" // [ilfd]rem
* "\011\022\011\022" // [ilfd]neg
* "\012\023\012\023\012\023" // [il]u?sh[lr]
* "\012\024\012\024\012\024" // [il](and|or|xor)
* "\000" // opc_iinc
* "\021\011\021" // i2[lfd]
* "\012\012\022" // l2[ifd]
* "\011\021\021" // f2[ild]
* "\012\022\012" // d2[ilf]
* "\011\011\011" // i2[bcs]
* "\014\012\012\014\014" // [lfd]cmp.?
* "\001\001\001\001\001\001" // if..
* "\002\002\002\002\002\002" // if_icmp..
* "\002\002" // if_acmp..
* "\000\010\000\001\001" // goto,jsr,ret, .*switch
* "\001\002\001\002\001\000" // [ilfda]?return
* "\100\100\100\100" // (get/put)(static|field)
* "\100\100\100\100" // invoke.*
* "\177\010\011\011\011" // 186 - 190
* "\001\011\011\001\001" // 191 - 195
* "\177\100\001\001" // 196 - 199
* "\000\010" // goto_w, jsr_w
*/
}

@ -0,0 +1,29 @@
/* LineNumber Copyright (C) 1999-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
/**
* A simple class containing the info of the LineNumberTable
*/
public class LineNumber {
public Instruction start;
public int linenr;
}

@ -0,0 +1,30 @@
/* LocalVariableInfo Copyright (C) 1999-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
/**
* A simple class containing the info of the LocalVariableTable
*/
public class LocalVariableInfo {
public Instruction start, end;
public String name, type;
public int slot;
}

@ -0,0 +1,45 @@
## Input file for automake to generate the Makefile.in used by configure
JAR = @JAR@
JAVAC = @JAVAC@
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@
FULL_CLASSPATH := $(shell $(SUBSTCP) $(top_srcdir):$(top_builddir):$(CLASSPATH):$(CLASSLIB))
MY_JAVA_FILES = \
BinaryInfo.java \
BytecodeInfo.java \
ClassFormatException.java \
ClassInfo.java \
ConstantPool.java \
FieldInfo.java \
GrowableConstantPool.java \
Handler.java \
InnerClassInfo.java \
Instruction.java \
LineNumber.java \
LocalVariableInfo.java \
MethodInfo.java \
Opcodes.java \
Reference.java \
SearchPath.java \
TypeSignature.java
noinst_DATA = $(MY_JAVA_FILES:.java=.class)
EXTRA_DIST = $(MY_JAVA_FILES)
@QUOTE@-include Makefile.dep
%.class: %.java
$(JAVAC) -classpath $(FULL_CLASSPATH) -d $(top_builddir) $<
Makefile.dep: $(MY_JAVA_FILES:.java=.class)
$(JAVADEP) $^
clean-local:
@rm -f *.class
@rm -f *.dep

@ -0,0 +1,241 @@
/* MethodInfo Copyright (C) 1998-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
public class MethodInfo extends BinaryInfo {
ClassInfo clazzInfo;
int modifier;
String name;
String typeSig;
BytecodeInfo bytecode;
String[] exceptions;
boolean syntheticFlag;
boolean deprecatedFlag;
public MethodInfo(ClassInfo ci) {
clazzInfo = ci;
}
public MethodInfo(ClassInfo ci,
String name, String typeSig, int modifier) {
this.clazzInfo = ci;
this.name = name;
this.typeSig = typeSig;
this.modifier = modifier;
}
protected void readAttribute(String name, int length, ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
if (name.equals("Code")) {
if ((howMuch & KNOWNATTRIBS) != 0) {
bytecode = new BytecodeInfo(this);
bytecode.read(cp, input);
} else
input.readFully(new byte[length]);
} else if (name.equals("Exceptions")) {
int count = input.readUnsignedShort();
exceptions = new String[count];
for (int i=0; i< count; i++)
exceptions[i] = cp.getClassName(input.readUnsignedShort());
if (length != 2 * (count + 1))
throw new ClassFormatException
("Exceptions attribute has wrong length");
} else if (name.equals("Synthetic")) {
syntheticFlag = true;
if (length != 0)
throw new ClassFormatException
("Synthetic attribute has wrong length");
} else if (name.equals("Deprecated")) {
deprecatedFlag = true;
if (length != 0)
throw new ClassFormatException
("Deprecated attribute has wrong length");
} else
super.readAttribute(name, length, cp, input, howMuch);
}
public void read(ConstantPool constantPool,
DataInputStream input, int howMuch) throws IOException {
modifier = input.readUnsignedShort();
name = constantPool.getUTF8(input.readUnsignedShort());
typeSig = constantPool.getUTF8(input.readUnsignedShort());
readAttributes(constantPool, input, howMuch);
}
public void reserveSmallConstants(GrowableConstantPool gcp) {
if (bytecode != null)
bytecode.reserveSmallConstants(gcp);
}
public void prepareWriting(GrowableConstantPool gcp) {
gcp.putUTF8(name);
gcp.putUTF8(typeSig);
if (bytecode != null) {
gcp.putUTF8("Code");
bytecode.prepareWriting(gcp);
}
if (exceptions != null) {
gcp.putUTF8("Exceptions");
for (int i=0; i< exceptions.length; i++)
gcp.putClassName(exceptions[i]);
}
if (syntheticFlag)
gcp.putUTF8("Synthetic");
if (deprecatedFlag)
gcp.putUTF8("Deprecated");
prepareAttributes(gcp);
}
protected int getKnownAttributeCount() {
int count = 0;
if (bytecode != null)
count++;
if (exceptions != null)
count++;
if (syntheticFlag)
count++;
if (deprecatedFlag)
count++;
return count;
}
public void writeKnownAttributes(GrowableConstantPool gcp,
DataOutputStream output)
throws IOException {
if (bytecode != null) {
output.writeShort(gcp.putUTF8("Code"));
output.writeInt(bytecode.getSize());
bytecode.write(gcp, output);
}
if (exceptions != null) {
int count = exceptions.length;
output.writeShort(gcp.putUTF8("Exceptions"));
output.writeInt(2 + count * 2);
output.writeShort(count);
for (int i=0; i< count; i++)
output.writeShort(gcp.putClassName(exceptions[i]));
}
if (syntheticFlag) {
output.writeShort(gcp.putUTF8("Synthetic"));
output.writeInt(0);
}
if (deprecatedFlag) {
output.writeShort(gcp.putUTF8("Deprecated"));
output.writeInt(0);
}
}
public void write(GrowableConstantPool constantPool,
DataOutputStream output) throws IOException {
output.writeShort(modifier);
output.writeShort(constantPool.putUTF8(name));
output.writeShort(constantPool.putUTF8(typeSig));
writeAttributes(constantPool, output);
}
public void dropInfo(int howMuch) {
if ((howMuch & KNOWNATTRIBS) != 0) {
bytecode = null;
exceptions = null;
}
if (bytecode != null)
bytecode.dropInfo(howMuch);
super.dropInfo(howMuch);
}
public ClassInfo getClazzInfo() {
return clazzInfo;
}
public String getName() {
return name;
}
public String getType() {
return typeSig;
}
public int getModifiers() {
return modifier;
}
public boolean isStatic() {
return Modifier.isStatic(modifier);
}
public boolean isSynthetic() {
return syntheticFlag;
}
public boolean isDeprecated() {
return deprecatedFlag;
}
public BytecodeInfo getBytecode() {
return bytecode;
}
public String[] getExceptions() {
return exceptions;
}
public void setName(String newName) {
name = newName;
}
public void setType(String newType) {
typeSig = newType;
}
public void setModifiers(int newModifier) {
modifier = newModifier;
}
public void setSynthetic(boolean flag) {
syntheticFlag = flag;
}
public void setDeprecated(boolean flag) {
deprecatedFlag = flag;
}
public void setBytecode(BytecodeInfo newBytecode) {
clazzInfo.loadInfo(KNOWNATTRIBS);
bytecode = newBytecode;
}
public void setExceptions(String[] newExceptions) {
clazzInfo.loadInfo(KNOWNATTRIBS);
exceptions = newExceptions;
}
public String toString() {
return "Method "+Modifier.toString(modifier)+" "+
typeSig + " " + clazzInfo.getName() + "."+ name;
}
}

@ -17,7 +17,7 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.bytecode; package jode.bytecode;
/** /**
* This is an interface containing the constants for the byte code opcodes. * This is an interface containing the constants for the byte code opcodes.
@ -267,14 +267,4 @@ public interface Opcodes {
public final static String newArrayTypes = "ZCFDBSIJ"; public final static String newArrayTypes = "ZCFDBSIJ";
public final static Object[] constants = {
null,
new Integer(-1), new Integer(0), new Integer(1),
new Integer(2), new Integer(3), new Integer(4), new Integer(5),
new Long(0), new Long(1),
new Float(0), new Float(1), new Float(2),
new Double(0), new Double(1)
};
} }

@ -17,15 +17,12 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.bytecode; package jode.bytecode;
import net.sf.jode.util.UnifyHash; import jode.util.UnifyHash;
///#def COLLECTIONS java.util import @COLLECTIONS@.Iterator;
import java.util.Iterator;
///#enddef
/** /**
* This class represents a field or method reference. It consists of * This class represents a field or method reference.
* the class name the method/field name and the type signature.
*/ */
public class Reference { public class Reference {
/** /**
@ -70,10 +67,6 @@ public class Reference {
} }
public String toString() { public String toString() {
String classStr = clazz; return clazz + " " + name + " " + type;
if (clazz.startsWith("L"))
classStr = clazz.substring(1, clazz.length() - 1)
.replace('/', '.');
return classStr + "." + name + " " + type;
} }
} }

@ -0,0 +1,581 @@
/* SearchPath Copyright (C) 1998-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import jode.GlobalOptions;
/**
* This class represents a path of multiple directories and/or zip files,
* where we can search for file names.
*
* @author Jochen Hoenicke
*/
public class SearchPath {
/**
* We need a different pathSeparatorChar, since ':' (used for most
* UNIX System) is used a protocol separator in URLs.
*
* We currently allow both pathSeparatorChar and
* altPathSeparatorChar and decide if it is a protocol separator
* by context.
*/
public static final char altPathSeparatorChar = ',';
URL[] bases;
byte[][] urlzips;
File[] dirs;
ZipFile[] zips;
String[] zipDirs;
Hashtable[] zipEntries;
private static void addEntry(Hashtable entries, String name) {
String dir = "";
int pathsep = name.lastIndexOf("/");
if (pathsep != -1) {
dir = name.substring(0, pathsep);
name = name.substring(pathsep+1);
}
Vector dirContent = (Vector) entries.get(dir);
if (dirContent == null) {
dirContent = new Vector();
entries.put(dir, dirContent);
if (dir != "")
addEntry(entries, dir);
}
dirContent.addElement(name);
}
private void fillZipEntries(int nr) {
Enumeration zipEnum = zips[nr].entries();
zipEntries[nr] = new Hashtable();
while (zipEnum.hasMoreElements()) {
ZipEntry ze = (ZipEntry) zipEnum.nextElement();
String name = ze.getName();
// if (name.charAt(0) == '/')
// name = name.substring(1);
if (zipDirs[nr] != null) {
if (!name.startsWith(zipDirs[nr]))
continue;
name = name.substring(zipDirs[nr].length());
}
if (!ze.isDirectory() && name.endsWith(".class"))
addEntry(zipEntries[nr], name);
}
}
private void readURLZip(int nr, URLConnection conn) {
int length = conn.getContentLength();
if (length <= 0)
// Give a approximation if length is unknown
length = 10240;
else
// Increase the length by one, so we hopefully don't need
// to grow the array later (we need a little overshot to
// know when the end is reached).
length++;
urlzips[nr] = new byte[length];
try {
InputStream is = conn.getInputStream();
int pos = 0;
for (;;) {
// This is ugly, is.available() may return zero even
// if there are more bytes.
int avail = Math.max(is.available(), 1);
if (pos + is.available() > urlzips[nr].length) {
// grow the byte array.
byte[] newarr = new byte
[Math.max(2*urlzips[nr].length, pos + is.available())];
System.arraycopy(urlzips[nr], 0, newarr, 0, pos);
urlzips[nr] = newarr;
}
int count = is.read(urlzips[nr], pos, urlzips[nr].length-pos);
if (count == -1)
break;
pos += count;
}
if (pos < urlzips[nr].length) {
// shrink the byte array again.
byte[] newarr = new byte[pos];
System.arraycopy(urlzips[nr], 0, newarr, 0, pos);
urlzips[nr] = newarr;
}
} catch (IOException ex) {
GlobalOptions.err.println("IOException while reading "
+"remote zip file "+bases[nr]);
// disable entry
bases[nr] = null;
urlzips[nr] = null;
return;
}
try {
// fill entries into hash table
ZipInputStream zis = new ZipInputStream
(new ByteArrayInputStream(urlzips[nr]));
zipEntries[nr] = new Hashtable();
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null) {
String name = ze.getName();
// if (name.charAt(0) == '/')
// name = name.substring(1);
if (zipDirs[nr] != null) {
if (!name.startsWith(zipDirs[nr]))
continue;
name = name.substring(zipDirs[nr].length());
}
if (!ze.isDirectory() && name.endsWith(".class"))
addEntry(zipEntries[nr], name);
zis.closeEntry();
}
zis.close();
} catch (IOException ex) {
GlobalOptions.err.println("Remote zip file "+bases[nr]
+" is corrupted.");
// disable entry
bases[nr] = null;
urlzips[nr] = null;
zipEntries[nr] = null;
return;
}
}
/**
* Creates a new search path for the given path.
* @param path The path where we should search for files. They
* should be separated by the system dependent pathSeparator. The
* entries may also be zip or jar files.
*/
public SearchPath(String path) {
// Calculate a good approximation (rounded upwards) of the tokens
// in this path.
int length = 1;
for (int index=path.indexOf(File.pathSeparatorChar);
index != -1; length++)
index = path.indexOf(File.pathSeparatorChar, index+1);
if (File.pathSeparatorChar != altPathSeparatorChar) {
for (int index=path.indexOf(altPathSeparatorChar);
index != -1; length++)
index = path.indexOf(altPathSeparatorChar, index+1);
}
bases = new URL[length];
urlzips = new byte[length][];
dirs = new File[length];
zips = new ZipFile[length];
zipEntries = new Hashtable[length];
zipDirs = new String[length];
int i = 0;
for (int ptr=0; ptr < path.length(); ptr++, i++) {
int next = ptr;
while (next < path.length()
&& path.charAt(next) != File.pathSeparatorChar
&& path.charAt(next) != altPathSeparatorChar)
next++;
int index = ptr;
colon_separator:
while (next > ptr
&& next < path.length()
&& path.charAt(next) == ':') {
// Check if this is a URL instead of a pathSeparator
// Since this is a while loop it allows nested urls like
// jar:ftp://ftp.foo.org/pub/foo.jar!/
while (index < next) {
char c = path.charAt(index);
// According to RFC 1738 letters, digits, '+', '-'
// and '.' are allowed SCHEMA characters. We
// disallow '.' because it is a good marker that
// the user has specified a filename instead of a
// URL.
if ((c < 'A' || c > 'Z')
&& (c < 'a' || c > 'z')
&& (c < '0' || c > '9')
&& "+-".indexOf(c) == -1) {
break colon_separator;
}
index++;
}
next++;
index++;
while (next < path.length()
&& path.charAt(next) != File.pathSeparatorChar
&& path.charAt(next) != altPathSeparatorChar)
next++;
}
String token = path.substring(ptr, next);
ptr = next;
boolean mustBeJar = false;
// We handle jar URL's ourself.
if (token.startsWith("jar:")) {
index = 0;
do {
index = token.indexOf('!', index);
} while (index != -1 && index != token.length()-1
&& token.charAt(index+1) != '/');
if (index == -1 || index == token.length()-1) {
GlobalOptions.err.println("Warning: Illegal jar url "
+ token + ".");
continue;
}
zipDirs[i] = token.substring(index+2);
if (!zipDirs[i].endsWith("/"))
zipDirs[i] = zipDirs[i] + "/";
token = token.substring(4, index);
mustBeJar = true;
}
index = token.indexOf(':');
if (index != -1 && index < token.length()-2
&& token.charAt(index+1) == '/'
&& token.charAt(index+2) == '/') {
// This looks like an URL.
try {
bases[i] = new URL(token);
try {
URLConnection connection = bases[i].openConnection();
if (mustBeJar
|| token.endsWith(".zip") || token.endsWith(".jar")
|| connection.getContentType().endsWith("/zip")) {
// This is a zip file. Read it into memory.
readURLZip(i, connection);
}
} catch (IOException ex) {
// ignore
} catch (SecurityException ex) {
GlobalOptions.err.println
("Warning: Security exception while accessing "
+ bases[i] + ".");
}
} catch (MalformedURLException ex) {
/* disable entry */
bases[i] = null;
dirs[i] = null;
}
} else {
try {
dirs[i] = new File(token);
if (mustBeJar || !dirs[i].isDirectory()) {
try {
zips[i] = new ZipFile(dirs[i]);
} catch (java.io.IOException ex) {
/* disable this entry */
dirs[i] = null;
}
}
} catch (SecurityException ex) {
/* disable this entry */
GlobalOptions.err.println
("Warning: SecurityException while accessing "
+ token + ".");
dirs[i] = null;
}
}
}
}
public boolean exists(String filename) {
String localFileName =
(java.io.File.separatorChar != '/')
? filename.replace('/', java.io.File.separatorChar)
: filename;
for (int i=0; i<dirs.length; i++) {
if (zipEntries[i] != null) {
if (zipEntries[i].get(filename) != null)
return true;
String dir = "";
String name = filename;
int index = filename.lastIndexOf('/');
if (index >= 0) {
dir = filename.substring(0, index);
name = filename.substring(index+1);
}
Vector directory = (Vector)zipEntries[i].get(dir);
if (directory != null && directory.contains(name))
return true;
continue;
}
if (bases[i] != null) {
try {
URL url = new URL(bases[i], filename);
URLConnection conn = url.openConnection();
conn.connect();
conn.getInputStream().close();
return true;
} catch (IOException ex) {
/* ignore */
}
continue;
}
if (dirs[i] == null)
continue;
if (zips[i] != null) {
String fullname = zipDirs[i] != null
? zipDirs[i] + filename : filename;
ZipEntry ze = zips[i].getEntry(fullname);
if (ze != null)
return true;
} else {
try {
File f = new File(dirs[i], localFileName);
if (f.exists())
return true;
} catch (SecurityException ex) {
/* ignore and take next element */
}
}
}
return false;
}
/**
* Searches for a file in the search path.
* @param filename the filename. The path components should be separated
* by <code>/</code>.
* @return An InputStream for the file.
*/
public InputStream getFile(String filename) throws IOException {
String localFileName =
(java.io.File.separatorChar != '/')
? filename.replace('/', java.io.File.separatorChar)
: filename;
for (int i=0; i<dirs.length; i++) {
if (urlzips[i] != null) {
ZipInputStream zis = new ZipInputStream
(new ByteArrayInputStream(urlzips[i]));
ZipEntry ze;
String fullname = zipDirs[i] != null
? zipDirs[i] + filename : filename;
while ((ze = zis.getNextEntry()) != null) {
if (ze.getName().equals(fullname)) {
///#ifdef JDK11
/// // The skip method in jdk1.1.7 ZipInputStream
/// // is buggy. We return a wrapper that fixes
/// // this.
/// return new FilterInputStream(zis) {
/// private byte[] tmpbuf = new byte[512];
/// public long skip(long n) throws IOException {
/// long skipped = 0;
/// while (n > 0) {
/// int count = read(tmpbuf, 0,
/// (int)Math.min(n, 512L));
/// if (count == -1)
/// return skipped;
/// skipped += count;
/// n -= count;
/// }
/// return skipped;
/// }
/// };
///#else
return zis;
///#endif
}
zis.closeEntry();
}
continue;
}
if (bases[i] != null) {
try {
URL url = new URL(bases[i], filename);
URLConnection conn = url.openConnection();
conn.setAllowUserInteraction(true);
return conn.getInputStream();
} catch (SecurityException ex) {
GlobalOptions.err.println("Warning: SecurityException"
+ " while accessing "
+ bases[i] + filename);
ex.printStackTrace(GlobalOptions.err);
/* ignore and take next element */
} catch (FileNotFoundException ex) {
/* ignore and take next element */
}
continue;
}
if (dirs[i] == null)
continue;
if (zips[i] != null) {
String fullname = zipDirs[i] != null
? zipDirs[i] + filename : filename;
ZipEntry ze = zips[i].getEntry(fullname);
if (ze != null)
return zips[i].getInputStream(ze);
} else {
try {
File f = new File(dirs[i], localFileName);
if (f.exists())
return new FileInputStream(f);
} catch (SecurityException ex) {
GlobalOptions.err.println("Warning: SecurityException"
+ " while accessing "
+ dirs[i] + localFileName);
/* ignore and take next element */
}
}
}
throw new FileNotFoundException(filename);
}
/**
* Searches for a filename in the search path and tells if it is a
* directory.
* @param filename the filename. The path components should be separated
* by <code>/</code>.
* @return true, if filename exists and is a directory, false otherwise.
*/
public boolean isDirectory(String filename) {
String localFileName =
(java.io.File.separatorChar != '/')
? filename.replace('/', java.io.File.separatorChar)
: filename;
for (int i=0; i<dirs.length; i++) {
if (dirs[i] == null)
continue;
if (zips[i] != null && zipEntries[i] == null)
fillZipEntries(i);
if (zipEntries[i] != null) {
if (zipEntries[i].containsKey(filename))
return true;
} else {
try {
File f = new File(dirs[i], localFileName);
if (f.exists())
return f.isDirectory();
} catch (SecurityException ex) {
GlobalOptions.err.println("Warning: SecurityException"
+ " while accessing "
+ dirs[i] + localFileName);
}
}
}
return false;
}
/**
* Searches for all files in the given directory.
* @param dirName the directory name. The path components should
* be separated by <code>/</code>.
* @return An enumeration with all files/directories in the given
* directory. */
public Enumeration listFiles(final String dirName) {
return new Enumeration() {
int pathNr;
Enumeration zipEnum;
int fileNr;
String localDirName =
(java.io.File.separatorChar != '/')
? dirName.replace('/', java.io.File.separatorChar)
: dirName;
File currentDir;
String[] files;
public String findNextFile() {
while (true) {
if (zipEnum != null) {
while (zipEnum.hasMoreElements()) {
return (String) zipEnum.nextElement();
}
zipEnum = null;
}
if (files != null) {
while (fileNr < files.length) {
String name = files[fileNr++];
if (name.endsWith(".class")) {
return name;
} else if (name.indexOf(".") == -1) {
/* ignore directories containing a dot.
* they can't be a package directory.
*/
File f = new File(currentDir, name);
if (f.exists() && f.isDirectory())
return name;
}
}
files = null;
}
if (pathNr == dirs.length)
return null;
if (zips[pathNr] != null && zipEntries[pathNr] == null)
fillZipEntries(pathNr);
if (zipEntries[pathNr] != null) {
Vector entries =
(Vector) zipEntries[pathNr].get(dirName);
if (entries != null)
zipEnum = entries.elements();
} else if (dirs[pathNr] != null) {
try {
File f = new File(dirs[pathNr], localDirName);
if (f.exists() && f.isDirectory()) {
currentDir = f;
files = f.list();
fileNr = 0;
}
} catch (SecurityException ex) {
GlobalOptions.err.println
("Warning: SecurityException"
+ " while accessing "
+ dirs[pathNr] + localDirName);
/* ignore and take next element */
}
}
pathNr++;
}
}
String nextName;
public boolean hasMoreElements() {
return (nextName != null
|| (nextName = findNextFile()) != null);
}
public Object nextElement() {
if (nextName == null)
return findNextFile();
else {
String result = nextName;
nextName = null;
return result;
}
}
};
}
}

@ -17,51 +17,11 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.bytecode; package jode.bytecode;
import jode.AssertError;
/** /**
* This class contains some static methods to handle type signatures. <br> * This class contains some static methods to handle type signatures.
*
* A type signature is a compact textual representation of a java
* types. It is described in the Java Virtual Machine Specification.
* Primitive types have a one letter type signature. Type signature
* of classes contains the class name. Type signatures for arrays and
* methods are recursively build from the type signatures of their
* elements. <br> Since java 5 there is a new class of type
* signatures supporting generics. These can be accessed with the
* getSignature methods of ClassInfo, MethodInfo and FieldInfo.
*
* Here are a few examples:
* <table><tr><th>type signature</th><th>Java type</th></tr>
* <tr><td><code>Z</code></td><td><code>boolean</code></td></tr>
* <tr><td><code>B</code></td><td><code>byte</code></td></tr>
* <tr><td><code>S</code></td><td><code>short</code></td></tr>
* <tr><td><code>C</code></td><td><code>char</code></td></tr>
* <tr><td><code>I</code></td><td><code>int</code></td></tr>
* <tr><td><code>F</code></td><td><code>float</code></td></tr>
* <tr><td><code>J</code></td><td><code>long</code></td></tr>
* <tr><td><code>D</code></td><td><code>double</code></td></tr>
* <tr><td><code>Ljava/lang/Object;</code></td>
* <td><code>java.lang.Object</code></td></tr>
* <tr><td><code>[[I</code></td><td><code>int[][]</code></td></tr>
* <tr><td><code>(Ljava/lang/Object;I)V</code></td>
* <td>method with argument types <code>Object</code> and
* <code>int</code> and <code>void</code> return type.</td></tr>
* <tr><td><code>()I</code></td>
* <td> method without arguments
* and <code>int</code> return type.</td></tr>
* <tr><td colspan="2"><code>&lt;E:Ljava/lang/Object;&gt;Ljava/lang/Object;Ljava/util/Collection&lt;TE;&gt;;</code></td>
* </tr><tr><td></td>
* <td> generic class over &lt;E extends Object&gt; extending
* Object and implementing Collections&lt;E&gt;</td></tr>
* <tr><td colspan="2"><code>&lt;T:Ljava/lang/Object;&gt;([TT;)[TT;</code></td>
* </tr><tr><td></td>
* <td> generic method over &lt;T extends Object&gt; taking an
* array of T as parameters and returning an array of T.</td></tr>
* </table>
*
*
* @author Jochen Hoenicke
*/ */
public class TypeSignature { public class TypeSignature {
/** /**
@ -90,7 +50,7 @@ public class TypeSignature {
else if (javaType == Void.TYPE) else if (javaType == Void.TYPE)
return sb.append('V'); return sb.append('V');
else else
throw new InternalError("Unknown primitive type: "+javaType); throw new AssertError("Unknown primitive type: "+javaType);
} else if (javaType.isArray()) { } else if (javaType.isArray()) {
return appendSignature(sb.append('['), return appendSignature(sb.append('['),
javaType.getComponentType()); javaType.getComponentType());
@ -101,20 +61,22 @@ public class TypeSignature {
} }
/** /**
* Generates the type signature of the given Class. * Generate the signature for the given Class.
* @param clazz a java.lang.Class, this may also be a primitive or * @param clazz a java.lang.Class, this may also be a primitive or
* array type. * array type.
* @return the type signature. * @return the type signature (see section 4.3.2 Field Descriptors
* of the JVM specification)
*/ */
public static String getSignature(Class clazz) { public static String getSignature(Class clazz) {
return appendSignature(new StringBuffer(), clazz).toString(); return appendSignature(new StringBuffer(), clazz).toString();
} }
/** /**
* Generates a method signature. * Generate a method signature.
* @param paramT the java.lang.Class of the parameter types of the method. * @param paramT the java.lang.Class of the parameter types of the method.
* @param returnT the java.lang.Class of the return type of the method. * @param returnT the java.lang.Class of the return type of the method.
* @return the method type signature * @return the method signature (see section 4.3.3 Method Descriptors
* of the JVM specification)
*/ */
public static String getSignature(Class paramT[], Class returnT) { public static String getSignature(Class paramT[], Class returnT) {
StringBuffer sig = new StringBuffer("("); StringBuffer sig = new StringBuffer("(");
@ -124,8 +86,8 @@ public class TypeSignature {
} }
/** /**
* Generates a Class object for a type signature. This is the * Generate a Class for a type signature. This is the pendant to
* inverse function of getSignature. * getSignature.
* @param typeSig a single type signature * @param typeSig a single type signature
* @return the Class object representing that type. * @return the Class object representing that type.
*/ */
@ -162,57 +124,33 @@ public class TypeSignature {
} }
/** /**
* Check if the given type is a two slot type. The only two slot * Check if the given type is a two slot type. */
* types are long and double.
*/
private static boolean usingTwoSlots(char type) { private static boolean usingTwoSlots(char type) {
return "JD".indexOf(type) >= 0; return "JD".indexOf(type) >= 0;
} }
/** /**
* Returns the number of words, an object of the given simple type * Returns the number of words, an object of the given simple type
* signature takes. For long and double this is two, for all other * signature takes.
* types it is one.
*/ */
public static int getTypeSize(String typeSig) { public static int getTypeSize(String typeSig) {
return usingTwoSlots(typeSig.charAt(0)) ? 2 : 1; return usingTwoSlots(typeSig.charAt(0)) ? 2 : 1;
} }
/**
* Gets the element type of an array.
* @param typeSig type signature of the array.
* @return type signature for the element type.
* @exception IllegalArgumentException if typeSig is not an array
* type signature.
*/
public static String getElementType(String typeSig) { public static String getElementType(String typeSig) {
if (typeSig.charAt(0) != '[') if (typeSig.charAt(0) != '[')
throw new IllegalArgumentException(); throw new IllegalArgumentException();
return typeSig.substring(1); return typeSig.substring(1);
} }
/** public static ClassInfo getClassInfo(String typeSig) {
* Gets the ClassInfo for a class type.
* @param classpath the classpath in which the ClassInfo is searched.
* @param typeSig type signature of the class.
* @return the ClassInfo object for the class.
* @exception IllegalArgumentException if typeSig is not an class
* type signature.
*/
public static ClassInfo getClassInfo(ClassPath classpath, String typeSig) {
if (typeSig.charAt(0) != 'L') if (typeSig.charAt(0) != 'L')
throw new IllegalArgumentException(); throw new IllegalArgumentException();
return classpath.getClassInfo return ClassInfo.forName
(typeSig.substring(1, typeSig.length()-1).replace('/', '.')); (typeSig.substring(1, typeSig.length()-1).replace('/', '.'));
} }
/** public static int skipType(String methodTypeSig, int position) {
* Skips the next entry of a method type signature
* @param methodTypeSig type signature of the method.
* @param position the index to the last entry.
* @return the index to the next entry.
*/
static int skipType(String methodTypeSig, int position) {
char c = methodTypeSig.charAt(position++); char c = methodTypeSig.charAt(position++);
while (c == '[') while (c == '[')
c = methodTypeSig.charAt(position++); c = methodTypeSig.charAt(position++);
@ -222,13 +160,10 @@ public class TypeSignature {
} }
/** /**
* Gets the number of words the parameters for the given method * Returns the number of words, the arguments for the given method
* type signature takes. This is the sum of getTypeSize() for * type signature takes.
* each parameter type.
* @param methodTypeSig the method type signature.
* @return the number of words the parameters take.
*/ */
public static int getParameterSize(String methodTypeSig) { public static int getArgumentSize(String methodTypeSig) {
int nargs = 0; int nargs = 0;
int i = 1; int i = 1;
for (;;) { for (;;) {
@ -244,11 +179,8 @@ public class TypeSignature {
} }
/** /**
* Gets the size of the return type of the given method in words. * Returns the number of words, an object of the given simple type
* This is zero for void return type, two for double or long return * signature takes.
* type and one otherwise.
* @param methodTypeSig the method type signature.
* @return the size of the return type in words.
*/ */
public static int getReturnSize(String methodTypeSig) { public static int getReturnSize(String methodTypeSig) {
int length = methodTypeSig.length(); int length = methodTypeSig.length();
@ -263,9 +195,8 @@ public class TypeSignature {
} }
/** /**
* Gets the parameter type signatures of the given method signature. * Returns the number of words, an object of the given simple type
* @param methodTypeSig the method type signature. * signature takes.
* @return an array containing all parameter types in correct order.
*/ */
public static String[] getParameterTypes(String methodTypeSig) { public static String[] getParameterTypes(String methodTypeSig) {
int pos = 1; int pos = 1;
@ -285,49 +216,15 @@ public class TypeSignature {
} }
/** /**
* Gets the return type for a method signature * Returns the number of words, an object of the given simple type
* @param methodTypeSig the method signature. * signature takes.
* @return the return type for a method signature, `V' for void methods.
*/ */
public static String getReturnType(String methodTypeSig) { public static String getReturnType(String methodTypeSig) {
return methodTypeSig.substring(methodTypeSig.lastIndexOf(')')+1); return methodTypeSig.substring(methodTypeSig.lastIndexOf(')')+1);
} }
/** /**
* Gets the default value an object of the given type has. It is * Check if there is a valid class name starting at index
* null for objects and arrays, Integer(0) for boolean and short
* integer types or Long(0L), Double(0.0), Float(0.0F) for long,
* double and float. This seems strange, but this way the type
* returned is the same as for FieldInfo.getConstant().
*
* @param typeSig the type signature.
* @return the default value.
* @exception IllegalArgumentException if this is a method type signature.
*/
public static Object getDefaultValue(String typeSig) {
switch(typeSig.charAt(0)) {
case 'Z':
case 'B':
case 'S':
case 'C':
case 'I':
return new Integer(0);
case 'J':
return new Long(0L);
case 'D':
return new Double(0.0);
case 'F':
return new Float(0.0F);
case 'L':
case '[':
return null;
default:
throw new IllegalArgumentException(typeSig);
}
}
/**
* Checks if there is a valid class name starting at index
* in string typesig and ending with a semicolon. * in string typesig and ending with a semicolon.
* @return the index at which the class name ends. * @return the index at which the class name ends.
* @exception IllegalArgumentException if there was an illegal character. * @exception IllegalArgumentException if there was an illegal character.
@ -347,7 +244,7 @@ public class TypeSignature {
} }
/** /**
* Checks if there is a valid simple type signature starting at index * Check if there is a valid simple type signature starting at index
* in string typesig. * in string typesig.
* @return the index at which the type signature ends. * @return the index at which the type signature ends.
* @exception IllegalArgumentException if there was an illegal character. * @exception IllegalArgumentException if there was an illegal character.
@ -366,14 +263,6 @@ public class TypeSignature {
return index; return index;
} }
/**
* Checks whether a given type signature is a valid (not method)
* type signature. Throws an exception otherwise.
* @param typesig the type signature.
* @exception NullPointerException if typeSig is null.
* @exception IllegalArgumentException if typeSig is not a valid
* type signature or if it's a method type signature.
*/
public static void checkTypeSig(String typesig) public static void checkTypeSig(String typesig)
throws IllegalArgumentException throws IllegalArgumentException
{ {
@ -387,14 +276,6 @@ public class TypeSignature {
} }
} }
/**
* Checks whether a given type signature is a valid method
* type signature. Throws an exception otherwise.
* @param typesig the type signature.
* @exception NullPointerException if typeSig is null.
* @exception IllegalArgumentException if typeSig is not a valid
* method type signature.
*/
public static void checkMethodTypeSig(String typesig) public static void checkMethodTypeSig(String typesig)
throws IllegalArgumentException throws IllegalArgumentException
{ {

@ -0,0 +1,2 @@
Makefile
Makefile.in

@ -17,7 +17,7 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.decompiler; package jode.decompiler;
public interface Analyzer { public interface Analyzer {

@ -17,7 +17,7 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.decompiler; package jode.decompiler;
import java.awt.Color; import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;

@ -17,29 +17,29 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.decompiler; package jode.decompiler;
import net.sf.jode.GlobalOptions; import jode.GlobalOptions;
import net.sf.jode.type.MethodType; import jode.type.MethodType;
import net.sf.jode.type.Type; import jode.type.Type;
import net.sf.jode.bytecode.ClassFormatException; import jode.bytecode.ClassInfo;
import net.sf.jode.bytecode.ClassInfo; import jode.bytecode.FieldInfo;
import net.sf.jode.bytecode.ClassPath; import jode.bytecode.MethodInfo;
import net.sf.jode.bytecode.FieldInfo; import jode.bytecode.InnerClassInfo;
import net.sf.jode.bytecode.MethodInfo; import jode.bytecode.ConstantPool;
import net.sf.jode.expr.Expression; import jode.expr.Expression;
import net.sf.jode.expr.ThisOperator; import jode.expr.ThisOperator;
import net.sf.jode.flow.TransformConstructors; import jode.flow.TransformConstructors;
import net.sf.jode.flow.StructuredBlock; import jode.flow.StructuredBlock;
import net.sf.jode.util.SimpleSet; import jode.util.SimpleSet;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.NoSuchElementException;
import java.util.Vector; import java.util.Vector;
import java.util.Enumeration;
import java.io.IOException; import java.io.IOException;
///#def COLLECTIONS java.util import @COLLECTIONS@.Collection;
import java.util.Collection; import @COLLECTIONS@.Set;
import java.util.Set;
///#enddef
public class ClassAnalyzer public class ClassAnalyzer
implements Scope, Declarable, ClassDeclarer implements Scope, Declarable, ClassDeclarer
@ -77,78 +77,57 @@ public class ClassAnalyzer
MethodAnalyzer staticConstructor; MethodAnalyzer staticConstructor;
MethodAnalyzer[] constructors; MethodAnalyzer[] constructors;
/**
* The outer values for method scoped classes.
*/
OuterValues outerValues; OuterValues outerValues;
/**
* The outer instance for non-static class scope classes.
*/
Expression outerInstance;
public ClassAnalyzer(ClassDeclarer parent, public ClassAnalyzer(ClassDeclarer parent,
ClassInfo clazz, ImportHandler imports, ClassInfo clazz, ImportHandler imports,
Expression[] outerValues) Expression[] outerValues)
throws ClassFormatException, IOException
{ {
clazz.load(ClassInfo.ALL); clazz.loadInfo(clazz.MOSTINFO);
ClassInfo superClass = clazz.getSuperclass();
String myPackage = clazz.getName().substring
(clazz.getName().lastIndexOf('.') + 1);
while (superClass != null) {
int howMuch = (superClass.getName().startsWith(myPackage)
&& (superClass.getName().lastIndexOf('.')
< myPackage.length()))
? ClassInfo.DECLARATIONS : ClassInfo.PUBLICDECLARATIONS;
try {
superClass.load(howMuch);
} catch (IOException ex) {
GlobalOptions.err.println
("Warning: Can't get "
+ (howMuch == ClassInfo.PUBLICDECLARATIONS
? "public" : "all")
+ " information of " + superClass
+" to detect name conflicts.");
GlobalOptions.err.println(ex.toString());
superClass.guess(howMuch);
}
superClass = superClass.getSuperclass();
}
this.parent = parent; this.parent = parent;
this.clazz = clazz; this.clazz = clazz;
this.imports = imports; this.imports = imports;
modifiers = clazz.getModifiers();
name = clazz.getClassName();
/* Check if this is a normal non-static inner class and set
* outerInstance.
*/
if ((Options.options & Options.OPTION_INNER) != 0
&& parent instanceof ClassAnalyzer && !isStatic())
outerInstance = new ThisOperator(((ClassAnalyzer) parent).clazz);
if (outerValues != null) if (outerValues != null)
this.outerValues = new OuterValues(this, outerValues); this.outerValues = new OuterValues(this, outerValues);
modifiers = clazz.getModifiers();
if (parent != null) {
InnerClassInfo[] outerInfos = clazz.getOuterClasses();
if (outerInfos[0].outer == null || outerInfos[0].name == null) {
if (parent instanceof ClassAnalyzer)
throw new jode.AssertError
("ClassInfo Attributes are inconsistent: "
+ clazz.getName());
} else {
if (!(parent instanceof ClassAnalyzer)
|| !(((ClassAnalyzer) parent).clazz.getName()
.equals(outerInfos[0].outer))
|| outerInfos[0].name == null)
throw new jode.AssertError
("ClassInfo Attributes are inconsistent: "
+ clazz.getName());
}
name = outerInfos[0].name;
modifiers = outerInfos[0].modifiers;
} else {
name = clazz.getName();
int dot = name.lastIndexOf('.');
if (dot >= 0)
name = name.substring(dot+1);
}
} }
public ClassAnalyzer(ClassDeclarer parent, public ClassAnalyzer(ClassDeclarer parent,
ClassInfo clazz, ImportHandler imports) ClassInfo clazz, ImportHandler imports)
throws ClassFormatException, IOException
{ {
this(parent, clazz, imports, null); this(parent, clazz, imports, null);
} }
public ClassAnalyzer(ClassInfo clazz, ImportHandler imports) public ClassAnalyzer(ClassInfo clazz, ImportHandler imports)
throws ClassFormatException, IOException
{ {
this(null, clazz, imports); this(null, clazz, imports);
} }
public ClassPath getClassPath() {
return clazz.getClassPath();
}
public final boolean isStatic() { public final boolean isStatic() {
return Modifier.isStatic(modifiers); return Modifier.isStatic(modifiers);
} }
@ -208,10 +187,6 @@ public class ClassAnalyzer
return outerValues; return outerValues;
} }
public Expression getOuterInstance() {
return outerInstance;
}
public void addBlockInitializer(int index, StructuredBlock initializer) { public void addBlockInitializer(int index, StructuredBlock initializer) {
if (blockInitializers[index] == null) if (blockInitializers[index] == null)
blockInitializers[index] = initializer; blockInitializers[index] = initializer;
@ -222,7 +197,7 @@ public class ClassAnalyzer
public void initialize() { public void initialize() {
FieldInfo[] finfos = clazz.getFields(); FieldInfo[] finfos = clazz.getFields();
MethodInfo[] minfos = clazz.getMethods(); MethodInfo[] minfos = clazz.getMethods();
ClassInfo[] innerInfos = clazz.getClasses(); InnerClassInfo[] innerInfos = clazz.getInnerClasses();
if (finfos == null) { if (finfos == null) {
/* This means that the class could not be loaded. /* This means that the class could not be loaded.
@ -234,21 +209,18 @@ public class ClassAnalyzer
if ((Options.options & Options.OPTION_INNER) != 0 if ((Options.options & Options.OPTION_INNER) != 0
&& innerInfos != null) { && innerInfos != null) {
/* Create inner classes */ /* Create inner classes */
Expression[] outerThis = new Expression[] {
new ThisOperator(clazz)
};
int innerCount = innerInfos.length; int innerCount = innerInfos.length;
inners = new ClassAnalyzer[innerCount]; inners = new ClassAnalyzer[innerCount];
for (int i=0; i < innerCount; i++) { for (int i=0; i < innerCount; i++) {
try { ClassInfo ci = ClassInfo.forName(innerInfos[i].inner);
inners[i] = new ClassAnalyzer inners[i] = new ClassAnalyzer
(this, innerInfos[i], imports, null); (this, ci, imports,
} catch (ClassFormatException ex) { Modifier.isStatic(innerInfos[i].modifiers)
GlobalOptions.err.println("Inner class "+innerInfos[i] ? null : outerThis);
+" malformed!");
ex.printStackTrace(GlobalOptions.err);
} catch (IOException ex) {
GlobalOptions.err.println("Can't read inner class "
+innerInfos[i]+".");
ex.printStackTrace(GlobalOptions.err);
}
} }
} else } else
inners = new ClassAnalyzer[0]; inners = new ClassAnalyzer[0];
@ -289,8 +261,6 @@ public class ClassAnalyzer
// initialize the inner classes. // initialize the inner classes.
for (int j=0; j < inners.length; j++) { for (int j=0; j < inners.length; j++) {
if (inners[j] == null)
continue;
inners[j].initialize(); inners[j].initialize();
innerComplexity += inners[j].getComplexity(); innerComplexity += inners[j].getComplexity();
} }
@ -329,20 +299,21 @@ public class ClassAnalyzer
// First analyze constructors and synthetic fields: // First analyze constructors and synthetic fields:
constrAna = null; constrAna = null;
if (constructors.length > 0) { if (constructors.length > 0) {
for (int j=0; j< constructors.length; j++) { for (int j=0; j< constructors.length; j++)
if (pl != null) { {
double constrCompl = constructors[j].getComplexity() if (pl != null) {
* subScale; double constrCompl = constructors[j].getComplexity()
if (constrCompl > STEP_COMPLEXITY) * subScale;
constructors[j].analyze(pl, done, constrCompl); if (constrCompl > STEP_COMPLEXITY)
else { constructors[j].analyze(pl, done, constrCompl);
pl.updateProgress(done, name); else {
constructors[j].analyze(null, 0.0, 0.0); pl.updateProgress(done, name);
} constructors[j].analyze(null, 0.0, 0.0);
}
done += constrCompl; done += constrCompl;
} else } else
constructors[j].analyze(null, 0.0, 0.0); constructors[j].analyze(null, 0.0, 0.0);
} }
constrAna = new TransformConstructors(this, false, constructors); constrAna = new TransformConstructors(this, false, constructors);
constrAna.removeSynthInitializers(); constrAna.removeSynthInitializers();
} }
@ -400,8 +371,6 @@ public class ClassAnalyzer
// Now analyze the inner classes. // Now analyze the inner classes.
for (int j=0; j < inners.length; j++) { for (int j=0; j < inners.length; j++) {
if (inners[j] == null)
continue;
if (pl != null) { if (pl != null) {
double innerCompl = inners[j].getComplexity() * subScale; double innerCompl = inners[j].getComplexity() * subScale;
if (innerCompl > STEP_COMPLEXITY) { if (innerCompl > STEP_COMPLEXITY) {
@ -428,7 +397,6 @@ public class ClassAnalyzer
} }
public void makeDeclaration(Set done) { public void makeDeclaration(Set done) {
// First prepare constructors:
if (constrAna != null) if (constrAna != null)
constrAna.transform(); constrAna.transform();
if (staticConstructor != null) { if (staticConstructor != null) {
@ -446,8 +414,7 @@ public class ClassAnalyzer
for (int j=0; j < fields.length; j++) for (int j=0; j < fields.length; j++)
fields[j].makeDeclaration(done); fields[j].makeDeclaration(done);
for (int j=0; j < inners.length; j++) for (int j=0; j < inners.length; j++)
if (inners[j] != null) inners[j].makeDeclaration(done);
inners[j].makeDeclaration(done);
for (int j=0; j < methods.length; j++) for (int j=0; j < methods.length; j++)
methods[j].makeDeclaration(done); methods[j].makeDeclaration(done);
} }
@ -468,7 +435,7 @@ public class ClassAnalyzer
return; return;
} }
writer.startOp(TabbedPrintWriter.NO_PAREN, 0); writer.startOp(writer.NO_PAREN, 0);
/* Clear the SUPER bit, which is also used as SYNCHRONIZED bit. */ /* Clear the SUPER bit, which is also used as SYNCHRONIZED bit. */
int modifiedModifiers = modifiers & ~(Modifier.SYNCHRONIZED int modifiedModifiers = modifiers & ~(Modifier.SYNCHRONIZED
| STRICTFP); | STRICTFP);
@ -497,7 +464,7 @@ public class ClassAnalyzer
writer.print(name); writer.print(name);
ClassInfo superClazz = clazz.getSuperclass(); ClassInfo superClazz = clazz.getSuperclass();
if (superClazz != null && if (superClazz != null &&
superClazz.getName() != "java.lang.Object") { superClazz != ClassInfo.javaLangObject) {
writer.breakOp(); writer.breakOp();
writer.print(" extends " + (writer.getClassString writer.print(" extends " + (writer.getClassString
(superClazz, Scope.CLASSNAME))); (superClazz, Scope.CLASSNAME)));
@ -506,7 +473,7 @@ public class ClassAnalyzer
if (interfaces.length > 0) { if (interfaces.length > 0) {
writer.breakOp(); writer.breakOp();
writer.print(clazz.isInterface() ? " extends " : " implements "); writer.print(clazz.isInterface() ? " extends " : " implements ");
writer.startOp(TabbedPrintWriter.EXPL_PAREN, 1); writer.startOp(writer.EXPL_PAREN, 1);
for (int i=0; i < interfaces.length; i++) { for (int i=0; i < interfaces.length; i++) {
if (i > 0) { if (i > 0) {
writer.print(", "); writer.print(", ");
@ -536,7 +503,6 @@ public class ClassAnalyzer
ProgressListener pl, double done, double scale) ProgressListener pl, double done, double scale)
throws IOException throws IOException
{ {
double subScale = scale / getComplexity(); double subScale = scale / getComplexity();
writer.pushScope(this); writer.pushScope(this);
boolean needFieldNewLine = false; boolean needFieldNewLine = false;
@ -581,11 +547,6 @@ public class ClassAnalyzer
if (needNewLine) if (needNewLine)
writer.println(); writer.println();
if (inners[i] == null) {
writer.println("COULDN'T READ INNER CLASS!");
continue;
}
if ((Options.options & Options.OPTION_IMMEDIATE) != 0) { if ((Options.options & Options.OPTION_IMMEDIATE) != 0) {
// We now do the analyzation we skipped before. // We now do the analyzation we skipped before.
inners[i].analyze(null, 0.0, 0.0); inners[i].analyze(null, 0.0, 0.0);
@ -630,7 +591,7 @@ public class ClassAnalyzer
needNewLine = true; needNewLine = true;
} }
writer.popScope(); writer.popScope();
clazz.drop(ClassInfo.DECLARATIONS); clazz.dropInfo(clazz.KNOWNATTRIBS | clazz.UNKNOWNATTRIBS);
} }
public void dumpSource(TabbedPrintWriter writer) public void dumpSource(TabbedPrintWriter writer)
@ -668,7 +629,6 @@ public class ClassAnalyzer
dumpSource(writer, pl, 0.8, 0.2); dumpSource(writer, pl, 0.8, 0.2);
if (pl != null) if (pl != null)
pl.updateProgress(1.0, name); pl.updateProgress(1.0, name);
writer.flush();
} }
public boolean isScopeOf(Object obj, int scopeType) { public boolean isScopeOf(Object obj, int scopeType) {
@ -704,15 +664,10 @@ public class ClassAnalyzer
} }
} }
if (usageType == CLASSNAME || usageType == AMBIGUOUSNAME) { if (usageType == CLASSNAME || usageType == AMBIGUOUSNAME) {
try { InnerClassInfo[] iinfos = info.getInnerClasses();
info.load(ClassInfo.DECLARATIONS);
} catch (IOException ex) {
info.guess(ClassInfo.DECLARATIONS);
}
ClassInfo[] iinfos = info.getClasses();
if (iinfos != null) { if (iinfos != null) {
for (int i=0; i < iinfos.length; i++) { for (int i=0; i < iinfos.length; i++) {
if (iinfos[i].getClassName().equals(name)) if (iinfos[i].name.equals(name))
return true; return true;
} }
} }
@ -757,7 +712,7 @@ public class ClassAnalyzer
/** require name != null; **/ /** require name != null; **/
int innerCount = inners.length; int innerCount = inners.length;
for (int i=0; i < innerCount; i++) { for (int i=0; i < innerCount; i++) {
if (inners[i] != null && inners[i].name.equals(name)) if (inners[i].name.equals(name))
return inners[i]; return inners[i];
} }
return null; return null;

@ -17,8 +17,8 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.decompiler; package jode.decompiler;
import net.sf.jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
/** /**
* This is the interface for objects, that a method can declare * This is the interface for objects, that a method can declare

@ -0,0 +1,116 @@
/* DeadCodeAnalysis Copyright (C) 1999-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.decompiler;
import jode.bytecode.BytecodeInfo;
import jode.bytecode.Instruction;
import jode.bytecode.Handler;
import @COLLECTIONS@.Iterator;
public class DeadCodeAnalysis {
private final static String REACHABLE = "R";
private final static String REACHCHANGED = "C";
private static void propagateReachability(BytecodeInfo code) {
boolean changed;
do {
changed = false;
for (Iterator iter = code.getInstructions().iterator();
iter.hasNext(); ) {
Instruction instr = (Instruction) iter.next();
if (instr.getTmpInfo() == REACHCHANGED) {
changed = true;
instr.setTmpInfo(REACHABLE);
Instruction[] succs = instr.getSuccs();
if (succs != null)
for (int i=0; i< succs.length; i++)
if (succs[i].getTmpInfo() == null)
succs[i].setTmpInfo(REACHCHANGED);
if (!instr.doesAlwaysJump()
&& instr.getNextByAddr() != null)
if (instr.getNextByAddr().getTmpInfo() == null)
instr.getNextByAddr().setTmpInfo(REACHCHANGED);
/*XXX code after jsr reachable iff ret is reachable...*/
if (instr.getOpcode() == Opcodes.opc_jsr)
if (instr.getNextByAddr().getTmpInfo() == null)
instr.getNextByAddr().setTmpInfo(REACHCHANGED);
}
}
} while (changed);
}
public static void removeDeadCode(BytecodeInfo code) {
((Instruction) code.getInstructions().get(0)).setTmpInfo(REACHCHANGED);
propagateReachability(code);
Handler[] handlers = code.getExceptionHandlers();
boolean changed;
do {
changed = false;
for (int i=0; i < handlers.length; i++) {
if (handlers[i].catcher.getTmpInfo() == null) {
/* check if the try block is somewhere reachable
* and mark the catcher as reachable then.
*/
for (Instruction instr = handlers[i].start;
instr != null; instr = instr.getNextByAddr()) {
if (instr.getTmpInfo() != null) {
handlers[i].catcher.setTmpInfo(REACHCHANGED);
propagateReachability(code);
changed = true;
break;
}
if (instr == handlers[i].end)
break;
}
}
}
} while (changed);
for (int i=0; i< handlers.length; i++) {
/* A handler is not reachable iff the catcher is not reachable */
if (handlers[i].catcher.getTmpInfo() == null) {
/* This is very seldom, so we can make it slow */
Handler[] newHandlers = new Handler[handlers.length - 1];
System.arraycopy(handlers, 0, newHandlers, 0, i);
System.arraycopy(handlers, i+1, newHandlers, i,
handlers.length - (i+1));
handlers = newHandlers;
code.setExceptionHandlers(newHandlers);
i--;
} else {
/* This works! */
while (handlers[i].start.getTmpInfo() == null)
handlers[i].start = handlers[i].start.getNextByAddr();
while (handlers[i].end.getTmpInfo() == null)
handlers[i].end = handlers[i].end.getPrevByAddr();
}
}
/* Now remove the dead code and clean up tmpInfo */
for (Iterator i = code.getInstructions().iterator(); i.hasNext(); ) {
Instruction instr = (Instruction) i.next();
if (instr.getTmpInfo() != null)
instr.setTmpInfo(null);
else
i.remove();
}
}
}

@ -17,7 +17,7 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.decompiler; package jode.decompiler;
/** /**
* This is the interface for objects, that a method can declare * This is the interface for objects, that a method can declare

@ -17,13 +17,14 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.decompiler; package jode.decompiler;
import net.sf.jode.GlobalOptions; import jode.GlobalOptions;
import net.sf.jode.bytecode.ClassPath; import jode.bytecode.SearchPath;
import net.sf.jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import java.io.File; import java.io.File;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.Writer; import java.io.Writer;
import java.io.BufferedWriter;
/** /**
* This is the interface that other java classes may use to decompile * This is the interface that other java classes may use to decompile
@ -37,15 +38,10 @@ import java.io.Writer;
* @version 1.0 * @version 1.0
*/ */
public class Decompiler { public class Decompiler {
private ClassPath classPath = null; private SearchPath searchPath = null;
private int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT; private int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT;
private int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT; private int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT;
private int tabWidth = 8;
private int indentSize = 4;
private int outputStyle = TabbedPrintWriter.BRACE_AT_EOL;
private int lineWidth = 79;
/** /**
* We need a different pathSeparatorChar, since ':' (used for most * We need a different pathSeparatorChar, since ':' (used for most
* UNIX System) is used a protocol separator in URLs. * UNIX System) is used a protocol separator in URLs.
@ -55,12 +51,10 @@ public class Decompiler {
* by context. * by context.
*/ */
public static final char altPathSeparatorChar public static final char altPathSeparatorChar
= ClassPath.altPathSeparatorChar; = SearchPath.altPathSeparatorChar;
/** /**
* Create a new decompiler. Normally you need only one, but you * Create a new decompiler.
* can have more around, with different options and different
* class paths.
*/ */
public Decompiler() { public Decompiler() {
} }
@ -73,7 +67,7 @@ public class Decompiler {
* @see #setClassPath(String[]) * @see #setClassPath(String[])
*/ */
public void setClassPath(String classpath) { public void setClassPath(String classpath) {
this.classPath = new ClassPath(classpath); searchPath = new SearchPath(classpath);
} }
/** /**
@ -86,19 +80,10 @@ public class Decompiler {
* @see #setClassPath(String) * @see #setClassPath(String)
*/ */
public void setClassPath(String[] classpath) { public void setClassPath(String[] classpath) {
this.classPath = new ClassPath(classpath); StringBuffer sb = new StringBuffer(classpath[0]);
} for (int i = 1; i < classpath.length; i++)
sb.append(altPathSeparatorChar).append(classpath[i]);
/** searchPath = new SearchPath(sb.toString());
* Set the class path. Should be called once before decompile is
* called, otherwise the system class path is used.
* @param classpath a classpath object.
* @exception NullPointerException if classpath is null.
* @exception IndexOutOfBoundsException if classpath array is empty.
* @see #setClassPath(String)
*/
public void setClassPath(ClassPath classpath) {
this.classPath = classpath;
} }
private static final String[] optionStrings = { private static final String[] optionStrings = {
@ -114,32 +99,16 @@ public class Decompiler {
*/ */
public void setOption(String option, String value) { public void setOption(String option, String value) {
if (option.equals("style")) { if (option.equals("style")) {
if (value.equals("gnu")) { if (value.equals("gnu"))
outputStyle = TabbedPrintWriter.GNU_SPACING Options.outputStyle = Options.GNU_STYLE;
| TabbedPrintWriter.INDENT_BRACES; else if (value.equals("sun"))
indentSize = 2; Options.outputStyle = Options.SUN_STYLE;
} else if (value.equals("sun")) { else if (value.equals("pascal"))
outputStyle = TabbedPrintWriter.BRACE_AT_EOL; Options.outputStyle = Options.PASCAL_STYLE;
indentSize = 4; else
} else if (value.equals("pascal")) {
outputStyle = 0;
indentSize = 4;
} else
throw new IllegalArgumentException("Invalid style "+value); throw new IllegalArgumentException("Invalid style "+value);
return; return;
} }
if (option.equals("tabwidth")) {
tabWidth = Integer.parseInt(value);
return;
}
if (option.equals("indent")) {
indentSize = Integer.parseInt(value);
return;
}
if (option.equals("linewidth")) {
lineWidth = Integer.parseInt(value);
return;
}
if (option.equals("import")) { if (option.equals("import")) {
int comma = value.indexOf(','); int comma = value.indexOf(',');
int packLimit = Integer.parseInt(value.substring(0, comma)); int packLimit = Integer.parseInt(value.substring(0, comma));
@ -159,10 +128,6 @@ public class Decompiler {
GlobalOptions.verboseLevel = Integer.parseInt(value); GlobalOptions.verboseLevel = Integer.parseInt(value);
return; return;
} }
if (option.equals("debug")) {
GlobalOptions.setDebugging(value);
return;
}
for (int i=0; i < optionStrings.length; i++) { for (int i=0; i < optionStrings.length; i++) {
if (option.equals(optionStrings[i])) { if (option.equals(optionStrings[i])) {
if (value.equals("0") if (value.equals("0")
@ -202,29 +167,27 @@ public class Decompiler {
* @param progress A progress listener (see below). Null if you * @param progress A progress listener (see below). Null if you
* don't need information about progress. * don't need information about progress.
* @exception IllegalArgumentException if className isn't correct. * @exception IllegalArgumentException if className isn't correct.
* @exception jode.jvm.VerifyException The code
* isn't valid or a dependent class, needed for type
* guessing, couldn't be found.
* @exception IOException if writer throws an exception. * @exception IOException if writer throws an exception.
* @exception RuntimeException If jode has a bug ;-) * @exception RuntimeException If jode has a bug ;-)
*/ */
public void decompile(String className, Writer writer, public void decompile(String className, Writer writer,
ProgressListener progress) ProgressListener progress)
throws java.io.IOException { throws java.io.IOException {
if (classPath == null) { if (searchPath == null) {
String cp = System.getProperty("java.class.path"); String classPath = System.getProperty("java.class.path")
String bootcp = System.getProperty("sun.boot.class.path"); .replace(File.pathSeparatorChar, altPathSeparatorChar);
if (bootcp != null) searchPath = new SearchPath(classPath);
cp = bootcp + altPathSeparatorChar + cp;
cp = cp.replace(File.pathSeparatorChar, altPathSeparatorChar);
classPath = new ClassPath(cp);
} }
ClassInfo clazz = classPath.getClassInfo(className); ClassInfo.setClassPath(searchPath);
ImportHandler imports = new ImportHandler(classPath, ClassInfo clazz = ClassInfo.forName(className);
importPackageLimit, ImportHandler imports = new ImportHandler(importPackageLimit,
importClassLimit); importClassLimit);
TabbedPrintWriter tabbedWriter = TabbedPrintWriter tabbedWriter =
new TabbedPrintWriter(writer, imports, false, new TabbedPrintWriter(writer, imports, false);
outputStyle, indentSize,
tabWidth, lineWidth);
ClassAnalyzer clazzAna = new ClassAnalyzer(null, clazz, imports); ClassAnalyzer clazzAna = new ClassAnalyzer(null, clazz, imports);
clazzAna.dumpJavaFile(tabbedWriter, progress); clazzAna.dumpJavaFile(tabbedWriter, progress);
writer.flush(); writer.flush();

@ -17,19 +17,19 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.decompiler; package jode.decompiler;
import net.sf.jode.type.Type; import jode.type.Type;
import net.sf.jode.bytecode.FieldInfo; import jode.bytecode.FieldInfo;
import net.sf.jode.expr.Expression; import jode.expr.Expression;
import net.sf.jode.expr.ConstOperator; import jode.expr.ThisOperator;
import net.sf.jode.expr.OuterLocalOperator; import jode.expr.LocalLoadOperator;
import jode.expr.ConstOperator;
import jode.expr.OuterLocalOperator;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.io.IOException; import java.io.IOException;
///#def COLLECTIONS java.util import @COLLECTIONS@.Set;
import java.util.Set;
///#enddef
public class FieldAnalyzer implements Analyzer { public class FieldAnalyzer implements Analyzer {
ClassAnalyzer clazz; ClassAnalyzer clazz;
@ -49,7 +49,7 @@ public class FieldAnalyzer implements Analyzer {
imports = i; imports = i;
modifiers = fd.getModifiers(); modifiers = fd.getModifiers();
type = Type.tType(cla.getClassPath(), fd.getType()); type = Type.tType(fd.getType());
fieldName = fd.getName(); fieldName = fd.getName();
constant = null; constant = null;
this.isSynthetic = fd.isSynthetic(); this.isSynthetic = fd.isSynthetic();
@ -176,7 +176,7 @@ public class FieldAnalyzer implements Analyzer {
| Modifier.STATIC | Modifier.STATIC
| Modifier.FINAL); | Modifier.FINAL);
*/ */
writer.startOp(TabbedPrintWriter.NO_PAREN, 0); writer.startOp(writer.NO_PAREN, 0);
String modif = Modifier.toString(modifiedModifiers); String modif = Modifier.toString(modifiedModifiers);
if (modif.length() > 0) if (modif.length() > 0)
writer.print(modif+" "); writer.print(modif+" ");
@ -186,8 +186,8 @@ public class FieldAnalyzer implements Analyzer {
if (constant != null) { if (constant != null) {
writer.breakOp(); writer.breakOp();
writer.print(" = "); writer.print(" = ");
constant.dumpExpression(TabbedPrintWriter.IMPL_PAREN, writer); constant.dumpExpression(writer.IMPL_PAREN, writer);
} }
writer.endOp(); writer.endOp();
writer.println(";"); writer.println(";");
} }

@ -17,24 +17,21 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.decompiler; package jode.decompiler;
import net.sf.jode.GlobalOptions; import jode.GlobalOptions;
import net.sf.jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import net.sf.jode.bytecode.ClassPath; import jode.bytecode.InnerClassInfo;
import net.sf.jode.type.Type; import jode.type.Type;
import net.sf.jode.type.ArrayType; import jode.type.ArrayType;
import net.sf.jode.type.ClassInfoType; import jode.type.ClassInterfacesType;
import net.sf.jode.type.ClassType; import jode.type.NullType;
import net.sf.jode.type.NullType;
import @COLLECTIONS@.SortedMap;
///#def COLLECTIONS java.util import @COLLECTIONS@.TreeMap;
import java.util.SortedMap; import @COLLECTIONS@.List;
import java.util.TreeMap; import @COLLECTIONS@.LinkedList;
import java.util.List; import @COLLECTIONS@.Comparator;
import java.util.LinkedList; import @COLLECTIONS@.Iterator;
import java.util.Comparator;
import java.util.Iterator;
///#enddef
import java.io.IOException; import java.io.IOException;
import java.util.Hashtable; import java.util.Hashtable;
@ -54,7 +51,6 @@ public class ImportHandler {
/* Classes that doesn't need to be qualified. */ /* Classes that doesn't need to be qualified. */
Hashtable cachedClassNames = null; Hashtable cachedClassNames = null;
ClassAnalyzer main; ClassAnalyzer main;
ClassPath classPath;
String className; String className;
String pkg; String pkg;
@ -79,13 +75,11 @@ public class ImportHandler {
} }
}; };
public ImportHandler(ClassPath classPath) { public ImportHandler() {
this(classPath, DEFAULT_PACKAGE_LIMIT, DEFAULT_CLASS_LIMIT); this(DEFAULT_PACKAGE_LIMIT, DEFAULT_CLASS_LIMIT);
} }
public ImportHandler(ClassPath classPath, public ImportHandler(int packageLimit, int classLimit) {
int packageLimit, int classLimit) {
this.classPath = classPath;
importPackageLimit = packageLimit; importPackageLimit = packageLimit;
importClassLimit = classLimit; importClassLimit = classLimit;
} }
@ -116,12 +110,12 @@ public class ImportHandler {
if (pkg.length() != 0) { if (pkg.length() != 0) {
/* Does this conflict with a class in this package? */ /* Does this conflict with a class in this package? */
if (classPath.existsClass(pkg+name)) if (ClassInfo.exists(pkg+name))
return true; return true;
} else { } else {
/* Does this conflict with a class in this unnamed /* Does this conflict with a class in this unnamed
* package? */ * package? */
if (classPath.existsClass(name.substring(1))) if (ClassInfo.exists(name.substring(1)))
return true; return true;
} }
@ -133,7 +127,7 @@ public class ImportHandler {
importName = importName.substring importName = importName.substring
(0, importName.length()-2); (0, importName.length()-2);
if (!importName.equals(pkgName)) { if (!importName.equals(pkgName)) {
if (classPath.existsClass(importName+name)) if (ClassInfo.exists(importName+name))
return true; return true;
} }
} else { } else {
@ -208,6 +202,7 @@ public class ImportHandler {
} }
public void dumpHeader(TabbedPrintWriter writer) public void dumpHeader(TabbedPrintWriter writer)
throws java.io.IOException
{ {
writer.println("/* "+ className writer.println("/* "+ className
+ " - Decompiled by JODE"); + " - Decompiled by JODE");
@ -257,34 +252,20 @@ public class ImportHandler {
*/ */
public void useClass(ClassInfo clazz) { public void useClass(ClassInfo clazz) {
for (;;) { for (;;) {
try { /* First handle inner classes: For class scoped classes
/* First handle inner classes: For class scoped classes * import outer class instead; for method scoped classes
* import outer class instead; for method scoped classes * we don't import anything.
* we don't import anything. */
*/ InnerClassInfo[] outerInfo = clazz.getOuterClasses();
clazz.load(ClassInfo.OUTERCLASS); if (outerInfo == null)
} catch (IOException ex) {
/* If we can't load outer class information, assume
* the clazz is not method or class scoped in this
* class. There is a big error otherwise anyways.
*/
break; break;
}
if (clazz.isMethodScoped()) if (outerInfo[0].name == null || outerInfo[0].outer == null)
return; return;
ClassInfo outer = clazz.getOuterClass(); clazz = ClassInfo.forName(outerInfo[0].outer);
if (outer == null)
break;
clazz = outer;
} }
useClass(clazz.getName()); String name = clazz.getName();
}
/* Marks the clazz as used, so that it will be imported if used often
* enough.
*/
public void useClass(String name) {
Integer i = (Integer) imports.get(name); Integer i = (Integer) imports.get(name);
if (i == null) { if (i == null) {
@ -318,10 +299,8 @@ public class ImportHandler {
public final void useType(Type type) { public final void useType(Type type) {
if (type instanceof ArrayType) if (type instanceof ArrayType)
useType(((ArrayType) type).getElementType()); useType(((ArrayType) type).getElementType());
else if (type instanceof ClassInfoType) else if (type instanceof ClassInterfacesType)
useClass(((ClassInfoType) type).getClassInfo()); useClass(((ClassInterfacesType) type).getClassInfo());
else if (type instanceof ClassType)
useClass(((ClassType) type).getClassName());
} }
/** /**
@ -339,24 +318,7 @@ public class ImportHandler {
* @return a legal string representation of clazz. * @return a legal string representation of clazz.
*/ */
public String getClassString(ClassInfo clazz) { public String getClassString(ClassInfo clazz) {
return getClassString(clazz.getName()); String name = clazz.getName();
}
/**
* Check if clazz is imported and maybe remove package delimiter from
* full qualified class name.
* <p>
* Known Bug 1: If this is called before the imports are cleaned up,
* (that is only for debugging messages), the result is unpredictable.
* <p>
* Known Bug 2: It is not checked if the class name conflicts with
* a local variable, field or method name. This is very unlikely
* since the java standard has different naming convention for those
* names. (But maybe an intelligent obfuscator may use this fact.)
* This can only happen with static fields or static methods.
* @return a legal string representation of clazz.
*/
public String getClassString(String name) {
if (cachedClassNames == null) if (cachedClassNames == null)
/* We are not yet clean, return the full name */ /* We are not yet clean, return the full name */
return name; return name;
@ -371,6 +333,7 @@ public class ImportHandler {
String pkgName = name.substring(0, pkgdelim); String pkgName = name.substring(0, pkgdelim);
Integer i;
if (pkgName.equals(pkg) if (pkgName.equals(pkg)
|| (imports.get(pkgName+".*") != null || (imports.get(pkgName+".*") != null
&& !conflictsImport(name))) { && !conflictsImport(name))) {
@ -386,10 +349,8 @@ public class ImportHandler {
public String getTypeString(Type type) { public String getTypeString(Type type) {
if (type instanceof ArrayType) if (type instanceof ArrayType)
return getTypeString(((ArrayType) type).getElementType()) + "[]"; return getTypeString(((ArrayType) type).getElementType()) + "[]";
else if (type instanceof ClassInfoType) else if (type instanceof ClassInterfacesType)
return getClassString(((ClassInfoType) type).getClassInfo()); return getClassString(((ClassInterfacesType) type).getClassInfo());
else if (type instanceof ClassType)
return getClassString(((ClassType) type).getClassName());
else if (type instanceof NullType) else if (type instanceof NullType)
return "Object"; return "Object";
else else
@ -401,3 +362,5 @@ public class ImportHandler {
return 1; return 1;
} }
} }

@ -17,13 +17,13 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.decompiler; package jode.decompiler;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Vector; import java.util.Vector;
import net.sf.jode.GlobalOptions; import jode.GlobalOptions;
import net.sf.jode.type.Type; import jode.type.Type;
import net.sf.jode.expr.Expression; import jode.expr.Expression;
import net.sf.jode.expr.LocalVarOperator; import jode.expr.LocalVarOperator;
/** /**
* The LocalInfo represents a local variable of a method. * The LocalInfo represents a local variable of a method.
@ -141,7 +141,7 @@ public class LocalInfo implements Declarable {
shadow.name = name; shadow.name = name;
if (constExpr != null) { if (constExpr != null) {
if (shadow.constExpr != null) if (shadow.constExpr != null)
throw new InternalError throw new jode.AssertError
("local has multiple constExpr"); ("local has multiple constExpr");
shadow.constExpr = constExpr; shadow.constExpr = constExpr;
} }
@ -250,8 +250,7 @@ public class LocalInfo implements Declarable {
return shadow.getName(); return shadow.getName();
} }
if (name == null) { if (name == null) {
return "local_" + (slot >= 0 ? slot + "_" : "") return "local_" + slot + "_" + Integer.toHexString(hashCode());
+ Integer.toHexString(hashCode());
} }
return name; return name;
} }
@ -315,9 +314,7 @@ public class LocalInfo implements Declarable {
&& otherType != Type.tError && li.type != Type.tError) { && otherType != Type.tError && li.type != Type.tError) {
GlobalOptions.err.println("Type error in local " + getName()+": " GlobalOptions.err.println("Type error in local " + getName()+": "
+ li.type + " and " + otherType); + li.type + " and " + otherType);
if ((GlobalOptions.debuggingFlags Thread.dumpStack();
& GlobalOptions.DEBUG_TYPES) != 0)
Thread.dumpStack();
} }
else if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0) else if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0)
GlobalOptions.err.println(getName()+" setType, new: "+newType GlobalOptions.err.println(getName()+" setType, new: "+newType
@ -364,15 +361,13 @@ public class LocalInfo implements Declarable {
} }
public boolean isConstant() { public boolean isConstant() {
LocalInfo li = getLocalInfo(); /* Checking if a local can be declared final is tricky,
Enumeration enumeration = li.operators.elements(); * since it can also be the case if it is written in
int writes = 0; * the "then" and "else" part of an if statement.
while (enumeration.hasMoreElements()) { *
if (((LocalVarOperator) enumeration.nextElement()).isWrite()) * We return true now, otherwise some code would not be
writes++; * decompilable.
} */
if (writes > 1)
return false;
return true; return true;
} }

@ -0,0 +1,45 @@
/* LocalVarEntry Copyright (C) 1999-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.decompiler;
import jode.type.Type;
public class LocalVarEntry {
String name;
Type type;
int startAddr;
int endAddr;
LocalVarEntry next;
public LocalVarEntry(int s, int e, String n, Type t) {
startAddr = s;
endAddr = e;
name = n;
type = t;
next = null;
}
public String getName() {
return name;
}
public Type getType() {
return type;
}
}

@ -0,0 +1,79 @@
/* LocalVariableRangeList Copyright (C) 1998-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.decompiler;
import jode.GlobalOptions;
import jode.type.Type;
public class LocalVariableRangeList {
LocalVarEntry list = null;
LocalVariableRangeList() {
}
private void add(LocalVarEntry li) {
LocalVarEntry prev = null;
LocalVarEntry next = list;
while (next != null && next.endAddr < li.startAddr) {
prev = next;
next = next.next;
}
/* prev.endAddr < li.startAddr <= next.endAddr
*/
if (next != null && li.endAddr >= next.startAddr) {
if (next.type.equals(li.type)
&& next.name.equals(li.name)) {
/* Same type, same name and overlapping range.
* This is the same local: extend next to the common
* range and don't add li.
*/
next.startAddr = Math.min(next.startAddr, li.startAddr);
next.endAddr = Math.max(next.endAddr, li.endAddr);
return;
}
GlobalOptions.err.println("warning: non disjoint locals");
}
li.next = next;
if (prev == null)
list = li;
else
prev.next = li;
}
private LocalVarEntry find(int addr) {
LocalVarEntry li = list;
while (li != null && li.endAddr < addr)
li = li.next;
if (li == null || li.startAddr > addr) {
return null;
}
return li;
}
public void addLocal(int startAddr, int endAddr,
String name, Type type) {
LocalVarEntry li = new LocalVarEntry(startAddr,endAddr,name,type);
add (li);
}
public LocalVarEntry getInfo(int addr) {
return find(addr);
}
}

@ -1,4 +1,4 @@
/* ConstantInstruction Copyright (C) 1999-2002 Jochen Hoenicke. /* LocalVariableTable Copyright (C) 1998-2002 Jochen Hoenicke.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@ -17,37 +17,27 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.bytecode; package jode.decompiler;
import net.sf.jode.util.StringQuoter; import jode.type.Type;
import jode.bytecode.LocalVariableInfo;
/** public class LocalVariableTable {
* This class represents an instruction in the byte code. LocalVariableRangeList[] locals;
*
*/
class ConstantInstruction extends Instruction {
/**
* The typesignature of the class/array.
*/
private Object constant;
ConstantInstruction(int opcode, Object constant) { public LocalVariableTable(int maxLocals, LocalVariableInfo[] lvt) {
super(opcode); locals = new LocalVariableRangeList[maxLocals];
this.constant = constant; for (int i=0; i < maxLocals; i++)
} locals[i] = new LocalVariableRangeList();
public final Object getConstant() for (int i=0; i<lvt.length; i++)
{ locals[lvt[i].slot].addLocal(lvt[i].start.getAddr(),
return constant; lvt[i].end.getAddr(),
lvt[i].name, Type.tType(lvt[i].type));
} }
public final void setConstant(Object constant) public LocalVarEntry getLocal(int slot, int addr)
throws ArrayIndexOutOfBoundsException
{ {
this.constant = constant; return locals[slot].getInfo(addr);
}
public String toString() {
return super.toString() + ' ' +
(constant instanceof String
? StringQuoter.quote((String) constant) : constant);
} }
} }

@ -17,28 +17,29 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.decompiler; package jode.decompiler;
import net.sf.jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import net.sf.jode.bytecode.ClassPath; import jode.GlobalOptions;
import net.sf.jode.bytecode.ClassFormatException;
import net.sf.jode.GlobalOptions;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Vector;
import gnu.getopt.LongOpt; import gnu.getopt.LongOpt;
import gnu.getopt.Getopt; import gnu.getopt.Getopt;
public class Main extends Options { public class Main extends Options {
private static int successCount = 0;
private static Vector failedClasses;
private static final int OPTION_START=0x10000; private static final int OPTION_START=0x10000;
private static final int OPTION_END =0x20000; private static final int OPTION_END =0x20000;
@ -46,14 +47,12 @@ public class Main extends Options {
new LongOpt("cp", LongOpt.REQUIRED_ARGUMENT, null, 'c'), new LongOpt("cp", LongOpt.REQUIRED_ARGUMENT, null, 'c'),
new LongOpt("classpath", LongOpt.REQUIRED_ARGUMENT, null, 'c'), new LongOpt("classpath", LongOpt.REQUIRED_ARGUMENT, null, 'c'),
new LongOpt("dest", LongOpt.REQUIRED_ARGUMENT, null, 'd'), new LongOpt("dest", LongOpt.REQUIRED_ARGUMENT, null, 'd'),
new LongOpt("keep-going", LongOpt.NO_ARGUMENT, null, 'k'),
new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'), new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'),
new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'V'), new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'V'),
new LongOpt("verbose", LongOpt.OPTIONAL_ARGUMENT, null, 'v'), new LongOpt("verbose", LongOpt.OPTIONAL_ARGUMENT, null, 'v'),
new LongOpt("debug", LongOpt.OPTIONAL_ARGUMENT, null, 'D'), new LongOpt("debug", LongOpt.OPTIONAL_ARGUMENT, null, 'D'),
new LongOpt("import", LongOpt.REQUIRED_ARGUMENT, null, 'i'), new LongOpt("import", LongOpt.REQUIRED_ARGUMENT, null, 'i'),
new LongOpt("style", LongOpt.REQUIRED_ARGUMENT, null, 's'), new LongOpt("style", LongOpt.REQUIRED_ARGUMENT, null, 's'),
new LongOpt("chars-per-line", LongOpt.REQUIRED_ARGUMENT, null, 'l'),
new LongOpt("lvt", LongOpt.OPTIONAL_ARGUMENT, null, new LongOpt("lvt", LongOpt.OPTIONAL_ARGUMENT, null,
OPTION_START+0), OPTION_START+0),
new LongOpt("inner", LongOpt.OPTIONAL_ARGUMENT, null, new LongOpt("inner", LongOpt.OPTIONAL_ARGUMENT, null,
@ -79,8 +78,8 @@ public class Main extends Options {
public static void usage() { public static void usage() {
PrintWriter err = GlobalOptions.err; PrintWriter err = GlobalOptions.err;
err.println("Version: " + GlobalOptions.version); err.println("Version: " + GlobalOptions.version);
err.println("Usage: java net.sf.jode.decompiler.Main [OPTION]* {CLASS|JAR}*"); err.println("Usage: java jode.decompiler.Main [OPTION]* {CLASS|JAR}*");
err.println("Give a fully qualified CLASS name, e.g. net.sf.jode.decompiler.Main, if you want to"); err.println("Give a fully qualified CLASS name, e.g. jode.decompiler.Main, if you want to");
err.println("decompile a single class, or a JAR file containing many classes."); err.println("decompile a single class, or a JAR file containing many classes.");
err.println("OPTION is any of these:"); err.println("OPTION is any of these:");
err.println(" -h, --help "+ err.println(" -h, --help "+
@ -95,10 +94,8 @@ public class Main extends Options {
"The directories should be separated by ','."); "The directories should be separated by ','.");
err.println(" -d, --dest <dir> "+ err.println(" -d, --dest <dir> "+
"write decompiled files to disk into directory destdir."); "write decompiled files to disk into directory destdir.");
err.println(" -s, --style {sun|gnu|pascal|python} "+ err.println(" -s, --style {sun|gnu} "+
"specify indentation style"); "specify indentation style");
err.println(" -l, --chars-per-line <number> "+
"specify line length");
err.println(" -i, --import <pkglimit>,<clslimit>"); err.println(" -i, --import <pkglimit>,<clslimit>");
err.println(" "+ err.println(" "+
"import classes used more than clslimit times"); "import classes used more than clslimit times");
@ -106,10 +103,32 @@ public class Main extends Options {
"and packages with more then pkglimit used classes."); "and packages with more then pkglimit used classes.");
err.println(" "+ err.println(" "+
"Limit 0 means never import. Default is 0,1."); "Limit 0 means never import. Default is 0,1.");
err.println(" -k, --keep-going "+ err.println(" -D, --debug=... "+
"After an error continue to decompile the other classes."); "use --debug=help for more information.");
err.println(" "+
"after an error decompiling one of them."); err.println("NOTE: The following options can be turned on or off with `yes' or `no'.");
err.println("The options tagged with (default) are normally on. Omitting the yes/no");
err.println("argument will toggle the option, e.g. --verify is equivalent to --verify=no.");
err.println(" --inner "+
"decompile inner classes (default).");
err.println(" --anonymous "+
"decompile anonymous classes (default).");
err.println(" --contrafo "+
"transform constructors of inner classes (default).");
err.println(" --lvt "+
"use the local variable table (default).");
err.println(" --pretty "+
"use `pretty' names for local variables (default).");
err.println(" --push "+
"allow PUSH instructions in output.");
err.println(" --decrypt "+
"decrypt encrypted strings (default).");
err.println(" --onetime "+
"remove locals, that are used only one time.");
err.println(" --immediate "+
"output source immediately (may produce buggy code).");
err.println(" --verify "+
"verify code before decompiling it (default).");
} }
public static boolean handleOption(int option, int longind, String arg) { public static boolean handleOption(int option, int longind, String arg) {
@ -121,29 +140,29 @@ public class Main extends Options {
options &= ~(1 << option); options &= ~(1 << option);
else { else {
GlobalOptions.err.println GlobalOptions.err.println
("net.sf.jode.decompiler.Main: option --"+longOptions[longind].getName() ("jode.decompiler.Main: option --"
+" takes one of `yes', `no', `on', `off' as parameter"); + longOptions[longind].getName()
+ " takes one of `yes', `no', `on', `off' as parameter");
return false; return false;
} }
return true; return true;
} }
public static boolean decompileClass public static void decompileClass(String className,
(String className, ClassPath classPath, ZipOutputStream destZip, String destDir,
String classPathStr, TabbedPrintWriter writer,
ZipOutputStream destZip, String destDir, ImportHandler imports) {
TabbedPrintWriter writer, ImportHandler imports) {
try { try {
ClassInfo clazz; ClassInfo clazz;
try { try {
clazz = classPath.getClassInfo(className); clazz = ClassInfo.forName(className);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
GlobalOptions.err.println GlobalOptions.err.println
("`"+className+"' is not a class name"); ("`"+className+"' is not a class name");
return false; return;
} }
if (skipClass(clazz)) if (skipClass(clazz))
return true; return;
String filename = String filename =
className.replace('.', File.separatorChar)+".java"; className.replace('.', File.separatorChar)+".java";
@ -169,54 +188,36 @@ public class Main extends Options {
clazzAna.dumpJavaFile(writer); clazzAna.dumpJavaFile(writer);
if (destZip != null) { if (destZip != null) {
writer.flush(); writer.flush();
destZip.closeEntry(); destZip.closeEntry();
} else if (destDir != null) } else if (destDir != null)
writer.close(); writer.close();
/* Now is a good time to clean up */ /* Now is a good time to clean up */
System.gc(); System.gc();
return true; successCount++;
} catch (FileNotFoundException ex) {
GlobalOptions.err.println
("Can't read "+ex.getMessage()+".");
GlobalOptions.err.println
("Check the class path ("+classPathStr+
") and check that you use the java class name.");
return false;
} catch (ClassFormatException ex) {
GlobalOptions.err.println
("Error while reading "+className+".");
ex.printStackTrace(GlobalOptions.err);
return false;
} catch (IOException ex) { } catch (IOException ex) {
failedClasses.addElement(className);
GlobalOptions.err.println GlobalOptions.err.println
("Can't write source of "+className+"."); ("Can't write source of "+className+".");
GlobalOptions.err.println("Check the permissions."); GlobalOptions.err.println("Check the permissions.");
ex.printStackTrace(GlobalOptions.err); ex.printStackTrace(GlobalOptions.err);
return false; } catch (Throwable t) {
} catch(RuntimeException ex) { failedClasses.addElement(className);
GlobalOptions.err.println
("Error whilst decompiling " + className + ".");
ex.printStackTrace(GlobalOptions.err);
return false;
} catch(InternalError ex) {
/* InternalError should not normally be
* caught, however we catch it here because
* some parts of JODE
* (e.g. TransformExceptionHandlers.analyze())
* throw it.
* TODO: Replace InternalError with something else in
* places they can actually be thrown
*/
GlobalOptions.err.println GlobalOptions.err.println
("Internal error whilst decompiling " + className + "."); ("Failed to decompile "+className+".");
ex.printStackTrace(GlobalOptions.err); t.printStackTrace(GlobalOptions.err);
return false;
} }
} }
public static void main(String[] params) throws Throwable{ public static void main(String[] params) {
decompile(params); try {
decompile(params);
} catch (ExceptionInInitializerError ex) {
ex.getException().printStackTrace();
} catch (Throwable ex) {
ex.printStackTrace();
}
printSummary();
/* When AWT applications are compiled with insufficient /* When AWT applications are compiled with insufficient
* classpath the type guessing by reflection code can * classpath the type guessing by reflection code can
* generate an awt thread that will prevent normal * generate an awt thread that will prevent normal
@ -225,35 +226,44 @@ public class Main extends Options {
System.exit(0); System.exit(0);
} }
private static void printSummary() {
GlobalOptions.err.println();
if (failedClasses.size() > 0) {
GlobalOptions.err.println("Failed to decompile these classes:");
Enumeration enumeration = failedClasses.elements();
while (enumeration.hasMoreElements()) {
GlobalOptions.err.println("\t" + enumeration.nextElement());
}
GlobalOptions.err.println("Failed to decompile " + failedClasses.size() + " classes.");
}
GlobalOptions.err.println("Decompiled " + successCount + " classes.");
}
public static void decompile(String[] params) { public static void decompile(String[] params) {
if (params.length == 0) { if (params.length == 0) {
usage(); usage();
return; return;
} }
ClassPath classPath; failedClasses = new Vector();
String classPathStr = System.getProperty("java.class.path")
.replace(File.pathSeparatorChar, ClassPath.altPathSeparatorChar);
String classPath = System.getProperty("java.class.path")
.replace(File.pathSeparatorChar, Decompiler.altPathSeparatorChar);
String bootClassPath = System.getProperty("sun.boot.class.path"); String bootClassPath = System.getProperty("sun.boot.class.path");
if (bootClassPath != null) if (bootClassPath != null)
classPathStr = classPathStr + ClassPath.altPathSeparatorChar classPath += Decompiler.altPathSeparatorChar
+ bootClassPath.replace(File.pathSeparatorChar, + bootClassPath.replace(File.pathSeparatorChar,
ClassPath.altPathSeparatorChar); Decompiler.altPathSeparatorChar);
String destDir = null; String destDir = null;
int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT; int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT;
int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT;; int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT;;
int outputStyle = TabbedPrintWriter.BRACE_AT_EOL;
int indentSize = 4;
int outputLineLength = 79;
boolean keepGoing = false;
GlobalOptions.err.println(GlobalOptions.copyright); GlobalOptions.err.println(GlobalOptions.copyright);
boolean errorInParams = false; boolean errorInParams = false;
Getopt g = new Getopt("net.sf.jode.decompiler.Main", params, "hVvkc:d:D:i:s:l:", Getopt g = new Getopt("jode.decompiler.Main", params, "hVvc:d:D:i:s:",
longOptions, true); longOptions, true);
for (int opt = g.getopt(); opt != -1; opt = g.getopt()) { for (int opt = g.getopt(); opt != -1; opt = g.getopt()) {
switch(opt) { switch(opt) {
@ -267,14 +277,11 @@ public class Main extends Options {
GlobalOptions.err.println(GlobalOptions.version); GlobalOptions.err.println(GlobalOptions.version);
break; break;
case 'c': case 'c':
classPathStr = g.getOptarg(); classPath = g.getOptarg();
break; break;
case 'd': case 'd':
destDir = g.getOptarg(); destDir = g.getOptarg();
break; break;
case 'k':
keepGoing = true;
break;
case 'v': { case 'v': {
String arg = g.getOptarg(); String arg = g.getOptarg();
if (arg == null) if (arg == null)
@ -284,7 +291,7 @@ public class Main extends Options {
GlobalOptions.verboseLevel = Integer.parseInt(arg); GlobalOptions.verboseLevel = Integer.parseInt(arg);
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
GlobalOptions.err.println GlobalOptions.err.println
("net.sf.jode.decompiler.Main: Argument `" ("jode.decompiler.Main: Argument `"
+arg+"' to --verbose must be numeric:"); +arg+"' to --verbose must be numeric:");
errorInParams = true; errorInParams = true;
} }
@ -300,38 +307,19 @@ public class Main extends Options {
} }
case 's': { case 's': {
String arg = g.getOptarg(); String arg = g.getOptarg();
if (arg.equals("gnu")) { if ("sun".startsWith(arg))
outputStyle = TabbedPrintWriter.GNU_SPACING outputStyle = SUN_STYLE;
| TabbedPrintWriter.INDENT_BRACES; else if ("gnu".startsWith(arg))
indentSize = 2; outputStyle = GNU_STYLE;
} else if (arg.equals("sun")) { else if ("pascal".startsWith(arg))
outputStyle = TabbedPrintWriter.BRACE_AT_EOL; outputStyle = Options.PASCAL_STYLE;
indentSize = 4; else {
} else if (arg.equals("pascal")) {
outputStyle = 0;
indentSize = 4;
} else if (arg.equals("python") || arg.equals("codd")) {
outputStyle = TabbedPrintWriter.BRACE_AT_EOL|TabbedPrintWriter.CODD_FORMATTING;
indentSize = 4;
} else {
GlobalOptions.err.println GlobalOptions.err.println
("net.sf.jode.decompiler.Main: Unknown style `"+arg+"'."); ("jode.decompiler.Main: Unknown style `"+arg+"'.");
errorInParams = true; errorInParams = true;
} }
break; break;
} }
case 'l': {
String arg = g.getOptarg();
try {
outputLineLength = Integer.parseInt(arg.trim());
}
catch (RuntimeException rte) {
GlobalOptions.err.println(
"net.sf.jode.decompiler.Main: Invalid Linelength " + arg);
errorInParams = true;
}
break;
}
case 'i': { case 'i': {
String arg = g.getOptarg(); String arg = g.getOptarg();
int comma = arg.indexOf(','); int comma = arg.indexOf(',');
@ -352,7 +340,7 @@ public class Main extends Options {
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
GlobalOptions.err.println GlobalOptions.err.println
("net.sf.jode.decompiler.Main: Invalid argument for -i option."); ("jode.decompiler.Main: Invalid argument for -i option.");
errorInParams = true; errorInParams = true;
} }
break; break;
@ -369,16 +357,14 @@ public class Main extends Options {
} }
if (errorInParams) if (errorInParams)
return; return;
classPath = new ClassPath(classPathStr); ClassInfo.setClassPath(classPath);
ImportHandler imports = new ImportHandler(classPath, ImportHandler imports = new ImportHandler(importPackageLimit,
importPackageLimit,
importClassLimit); importClassLimit);
ZipOutputStream destZip = null; ZipOutputStream destZip = null;
TabbedPrintWriter writer = null; TabbedPrintWriter writer = null;
if (destDir == null) if (destDir == null)
writer = new TabbedPrintWriter(System.out, imports, true, writer = new TabbedPrintWriter(System.out, imports);
outputStyle, indentSize, 0, outputLineLength);
else if (destDir.toLowerCase().endsWith(".zip") else if (destDir.toLowerCase().endsWith(".zip")
|| destDir.toLowerCase().endsWith(".jar")) { || destDir.toLowerCase().endsWith(".jar")) {
try { try {
@ -389,8 +375,7 @@ public class Main extends Options {
return; return;
} }
writer = new TabbedPrintWriter(new BufferedOutputStream(destZip), writer = new TabbedPrintWriter(new BufferedOutputStream(destZip),
imports, false, imports, false);
outputStyle, indentSize, 0, outputLineLength);
} }
for (int i= g.getOptind(); i< params.length; i++) { for (int i= g.getOptind(); i< params.length; i++) {
try { try {
@ -399,8 +384,9 @@ public class Main extends Options {
/* The user obviously wants to decompile a jar/zip file. /* The user obviously wants to decompile a jar/zip file.
* Lets do him a pleasure and allow this. * Lets do him a pleasure and allow this.
*/ */
ClassPath zipClassPath ClassInfo.setClassPath(params[i]
= new ClassPath(params[i], classPath); + Decompiler.altPathSeparatorChar
+ classPath);
Enumeration enumeration = new ZipFile(params[i]).entries(); Enumeration enumeration = new ZipFile(params[i]).entries();
while (enumeration.hasMoreElements()) { while (enumeration.hasMoreElements()) {
String entry String entry
@ -408,22 +394,14 @@ public class Main extends Options {
if (entry.endsWith(".class")) { if (entry.endsWith(".class")) {
entry = entry.substring(0, entry.length() - 6) entry = entry.substring(0, entry.length() - 6)
.replace('/', '.'); .replace('/', '.');
if (!decompileClass(entry, zipClassPath, decompileClass(entry, destZip, destDir,
classPathStr, writer, imports);
destZip, destDir,
writer, imports)
&& !keepGoing)
break;
} }
} }
} else { ClassInfo.setClassPath(classPath);
if (!decompileClass(params[i], classPath, } else
classPathStr, decompileClass(params[i], destZip, destDir,
destZip, destDir, writer, imports);
writer, imports)
&& !keepGoing)
break;
}
} catch (IOException ex) { } catch (IOException ex) {
GlobalOptions.err.println GlobalOptions.err.println
("Can't read zip file " + params[i] + "."); ("Can't read zip file " + params[i] + ".");

@ -0,0 +1,51 @@
## Input file for automake to generate the Makefile.in used by configure
JAR = @JAR@
JAVAC = @JAVAC@
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@
FULL_CLASSPATH := $(shell $(SUBSTCP) $(top_srcdir):$(top_builddir):$(CLASSPATH):$(CLASSLIB))
MY_JAVA_FILES = \
Analyzer.java \
Applet.java \
ClassAnalyzer.java \
ClassDeclarer.java \
DeadCodeAnalysis.java \
Declarable.java \
Decompiler.java \
FieldAnalyzer.java \
ImportHandler.java \
LocalInfo.java \
LocalVarEntry.java \
LocalVariableRangeList.java \
LocalVariableTable.java \
Main.java \
MethodAnalyzer.java \
Opcodes.java \
Options.java \
OuterValueListener.java \
OuterValues.java \
ProgressListener.java \
Scope.java \
TabbedPrintWriter.java \
Window.java
noinst_DATA = $(MY_JAVA_FILES:.java=.class)
EXTRA_DIST = $(MY_JAVA_FILES)
@QUOTE@-include Makefile.dep
%.class: %.java
$(JAVAC) -classpath $(FULL_CLASSPATH) -d $(top_builddir) $<
Makefile.dep: $(MY_JAVA_FILES:.java=.class)
$(JAVADEP) $^
clean-local:
@rm -f *.class
@rm -f *.dep

@ -17,40 +17,47 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.decompiler; package jode.decompiler;
import net.sf.jode.GlobalOptions; import jode.AssertError;
import net.sf.jode.bytecode.BasicBlocks; import jode.GlobalOptions;
import net.sf.jode.bytecode.Block; import jode.bytecode.BytecodeInfo;
import net.sf.jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import net.sf.jode.bytecode.Handler; import jode.bytecode.MethodInfo;
import net.sf.jode.bytecode.Instruction; import jode.bytecode.Handler;
import net.sf.jode.bytecode.LocalVariableInfo; import jode.bytecode.Instruction;
import net.sf.jode.bytecode.MethodInfo; import jode.bytecode.LocalVariableInfo;
import net.sf.jode.jvm.SyntheticAnalyzer; import jode.jvm.SyntheticAnalyzer;
import net.sf.jode.type.*; import jode.decompiler.Options;
import net.sf.jode.expr.Expression; import jode.type.*;
import net.sf.jode.expr.CheckNullOperator; import jode.expr.Expression;
import net.sf.jode.expr.ThisOperator; import jode.expr.ConstOperator;
import net.sf.jode.expr.LocalLoadOperator; import jode.expr.CheckNullOperator;
import net.sf.jode.expr.OuterLocalOperator; import jode.expr.ThisOperator;
import net.sf.jode.expr.InvokeOperator; import jode.expr.LocalLoadOperator;
import net.sf.jode.flow.StructuredBlock; import jode.expr.OuterLocalOperator;
import net.sf.jode.flow.FlowBlock; import jode.expr.InvokeOperator;
import net.sf.jode.flow.TransformExceptionHandlers; import jode.flow.StructuredBlock;
import net.sf.jode.jvm.CodeVerifier; import jode.flow.FlowBlock;
import net.sf.jode.jvm.VerifyException; import jode.flow.TransformExceptionHandlers;
import jode.flow.Jump;
import jode.jvm.CodeVerifier;
import jode.jvm.VerifyException;
import jode.util.SimpleMap;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.BitSet;
import java.util.Stack;
import java.util.Vector; import java.util.Vector;
import java.util.Enumeration; import java.util.Enumeration;
import java.io.DataInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
///#def COLLECTIONS java.util import @COLLECTIONS@.Map;
import java.util.Collection; import @COLLECTIONS@.Collection;
import java.util.ArrayList; import @COLLECTIONS@.ArrayList;
import java.util.Iterator; import @COLLECTIONS@.Iterator;
import java.util.Set; import @COLLECTIONS@.Set;
///#enddef
/** /**
* A method analyzer is the main class for analyzation of methods. * A method analyzer is the main class for analyzation of methods.
@ -92,10 +99,10 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
*/ */
MethodInfo minfo; MethodInfo minfo;
/** /**
* This is the basic blocks structure, or null if this method has * This is the bytecode info structure, or null if this method has
* no code (abstract or native). * no code (abstract or native).
*/ */
BasicBlocks bb; BytecodeInfo code;
/** /**
* The method name. * The method name.
@ -138,6 +145,11 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* the implicit <i>this</i> parameter for nonstatic methods. * the implicit <i>this</i> parameter for nonstatic methods.
*/ */
LocalInfo[] param; LocalInfo[] param;
/**
* The local variable table containing info about names and types of
* locals.
*/
LocalVariableTable lvt;
/** /**
* If this method is the special constructor, that is generated * If this method is the special constructor, that is generated
@ -191,13 +203,13 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
this.imports = imports; this.imports = imports;
this.minfo = minfo; this.minfo = minfo;
this.methodName = minfo.getName(); this.methodName = minfo.getName();
this.methodType = Type.tMethod(cla.getClassPath(), minfo.getType()); this.methodType = Type.tMethod(minfo.getType());
this.isConstructor = this.isConstructor =
methodName.equals("<init>") || methodName.equals("<clinit>"); methodName.equals("<init>") || methodName.equals("<clinit>");
if (minfo.getBasicBlocks() != null) if (minfo.getBytecode() != null) {
bb = minfo.getBasicBlocks(); code = minfo.getBytecode();
}
String[] excattr = minfo.getExceptions(); String[] excattr = minfo.getExceptions();
if (excattr == null) { if (excattr == null) {
exceptions = new Type[0]; exceptions = new Type[0];
@ -205,11 +217,10 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
int excCount = excattr.length; int excCount = excattr.length;
this.exceptions = new Type[excCount]; this.exceptions = new Type[excCount];
for (int i=0; i< excCount; i++) for (int i=0; i< excCount; i++)
exceptions[i] = Type.tClass(classAnalyzer.getClassPath(), exceptions[i] = Type.tClass(excattr[i]);
excattr[i]);
} }
if (minfo.isSynthetic() || methodName.indexOf('$') != -1) if (minfo.isSynthetic() || methodName.indexOf('$') != -1)
synth = new SyntheticAnalyzer(cla.getClazz(), minfo, true); synth = new SyntheticAnalyzer(minfo, true);
} }
/** /**
@ -240,8 +251,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* @return the bytecode info for this method, or null if it is * @return the bytecode info for this method, or null if it is
* abstract or native. * abstract or native.
*/ */
public final BasicBlocks getBasicBlocks() { public final BytecodeInfo getBytecodeInfo() {
return bb; return code;
} }
/** /**
@ -274,7 +285,12 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
*/ */
public void insertStructuredBlock(StructuredBlock insertBlock) { public void insertStructuredBlock(StructuredBlock insertBlock) {
if (methodHeader != null) { if (methodHeader != null) {
methodHeader.prependBlock(insertBlock); insertBlock.setJump(new Jump(FlowBlock.NEXT_BY_ADDR));
FlowBlock insertFlowBlock = new FlowBlock(this, 0);
insertFlowBlock.appendBlock(insertBlock, 0);
insertFlowBlock.setNextByAddr(methodHeader);
insertFlowBlock.doT2(methodHeader);
methodHeader = insertFlowBlock;
} else { } else {
throw new IllegalStateException(); throw new IllegalStateException();
} }
@ -408,15 +424,18 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
/** /**
* Create a local info for a local variable located at an * Create a local info for a local variable located at an
* instruction with the given address. * instruction with the given address.
* @param lvi the local variable info of the bytecode package. * @param addr the address of the instruction using this local.
* the address of the next instruction for stores.
* @param slot the slot, the local variable uses.
* @return a new local info representing that local. * @return a new local info representing that local.
*/ */
public LocalInfo getLocalInfo(LocalVariableInfo lvi) { public LocalInfo getLocalInfo(int addr, int slot) {
LocalInfo li = new LocalInfo(this, lvi.getSlot()); LocalInfo li = new LocalInfo(this, slot);
if ((Options.options & Options.OPTION_LVT) != 0 if (lvt != null) {
&& lvi.getName() != null) LocalVarEntry entry = lvt.getLocal(slot, addr);
li.addHint(lvi.getName(), Type.tType(classAnalyzer.getClassPath(), if (entry != null)
lvi.getType())); li.addHint(entry.getName(), entry.getType());
}
allLocals.addElement(li); allLocals.addElement(li);
return li; return li;
} }
@ -426,15 +445,10 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* been initialized. This is used for a nice progress bar. * been initialized. This is used for a nice progress bar.
*/ */
public double getComplexity() { public double getComplexity() {
if (bb == null) if (code == null)
return 0.0; return 0.0;
else { else
int count = 0; return code.getInstructions().size();
Block[] blocks = bb.getBlocks();
for (int i=0; i < blocks.length; i++)
count += blocks[i].getInstructions().length;
return count;
}
} }
/** /**
@ -444,88 +458,124 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
private void analyzeCode(ProgressListener pl, double done, double scale) private void analyzeCode(ProgressListener pl, double done, double scale)
{ {
int instrsPerStep = Integer.MAX_VALUE; int instrsPerStep = Integer.MAX_VALUE;
double instrScale = (scale * 0.9) / getComplexity();
if (GlobalOptions.verboseLevel > 0) if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.print(methodName+": "); GlobalOptions.err.print(methodName+": ");
if (pl != null) if (pl != null) {
instrsPerStep = (int) (STEP_COMPLEXITY / instrScale); instrsPerStep = (int) ((code.getInstructions().size()
* STEP_COMPLEXITY) / (scale * 0.9));
}
Block[] blocks = bb.getBlocks(); /* The adjacent analyzation relies on this */
FlowBlock[] flows = new FlowBlock[blocks.length]; DeadCodeAnalysis.removeDeadCode(code);
Handler[] handlers = code.getExceptionHandlers();
int returnCount;
TransformExceptionHandlers excHandlers; TransformExceptionHandlers excHandlers;
{ {
for (int i=0; i < blocks.length; i++) /* First create a FlowBlock for every block that has a
flows[i] = new FlowBlock(this, i, i > 0 ? flows[i-1]: null); * predecessor other than the previous instruction.
*/
for (Iterator i = code.getInstructions().iterator();
i.hasNext(); ) {
Instruction instr = (Instruction) i.next();
if (instr.getPrevByAddr() == null
|| instr.getPrevByAddr().doesAlwaysJump()
|| instr.getPreds() != null)
instr.setTmpInfo(new FlowBlock(this, instr.getAddr()));
}
for (int i=0; i < handlers.length; i++) {
Instruction instr = handlers[i].start;
if (instr.getTmpInfo() == null)
instr.setTmpInfo(new FlowBlock(this, instr.getAddr()));
/* end doesn't have a predecessor, but we must prevent
* it from being merged with the previous instructions.
*/
instr = handlers[i].end.getNextByAddr();
if (instr.getTmpInfo() == null)
instr.setTmpInfo(new FlowBlock(this, instr.getAddr()));
instr = handlers[i].catcher;
if (instr.getTmpInfo() == null)
instr.setTmpInfo(new FlowBlock(this, instr.getAddr()));
}
/* We transform every basic block into a FlowBlock /* While we read the opcodes into FlowBlocks
* we try to combine sequential blocks, as soon as we
* find two sequential instructions in a row, where the
* second has no predecessors.
*/ */
int mark = 1000;
int count = 0; int count = 0;
for (int i=0; i < blocks.length; i++) { FlowBlock lastBlock = null;
int mark = 100; boolean lastSequential = false;
Instruction[] instrs = blocks[i].getInstructions(); for (Iterator i = code.getInstructions().iterator();
for (int j=0; j < instrs.length; j++) { i.hasNext(); ) {
if (GlobalOptions.verboseLevel > 0 && j > mark) { Instruction instr = (Instruction) i.next();
GlobalOptions.err.print('.');
mark += 100; jode.flow.StructuredBlock block
} = Opcodes.readOpcode(instr, this);
if (++count >= instrsPerStep) {
done += count * instrScale; if (GlobalOptions.verboseLevel > 0 && instr.getAddr() > mark) {
pl.updateProgress(done, methodName); GlobalOptions.err.print('.');
count = 0; mark += 1000;
} }
Opcodes.addOpcode(flows[i], instrs[j], this); if (++count >= instrsPerStep) {
} done += count * scale / code.getInstructions().size();
Block[] succs = blocks[i].getSuccs(); pl.updateProgress(done, methodName);
FlowBlock[] flowSuccs; count = 0;
int lastOpcode = instrs.length > 0
? instrs[instrs.length-1].getOpcode() : Opcodes.opc_nop;
if (lastOpcode >= Opcodes.opc_ireturn
&& lastOpcode <= Opcodes.opc_areturn) {
flowSuccs = new FlowBlock[] { FlowBlock.END_OF_METHOD };
} else {
flowSuccs = new FlowBlock[succs.length];
for (int j=0; j< succs.length; j++) {
if (succs[j] == null)
flowSuccs[j] = FlowBlock.END_OF_METHOD;
else
flowSuccs[j] = flows[succs[j].getBlockNr()];
}
} }
flows[i].setSuccessors(flowSuccs);
}
done += count * instrScale;
Block startBlock = bb.getStartBlock();
if (startBlock == null)
methodHeader = new FlowBlock(this, 0, null);
else
methodHeader = flows[startBlock.getBlockNr()];
methodHeader.addStartPred();
Handler[] handlers = bb.getExceptionHandlers(); if (lastSequential && instr.getTmpInfo() == null
excHandlers = new TransformExceptionHandlers(flows); /* Only merge with previous block, if this is sequential,
* too.
* Why? appendBlock only handles sequential blocks.
*/
&& !instr.doesAlwaysJump() && instr.getSuccs() == null) {
lastBlock.appendBlock(block, instr.getLength());
} else {
if (instr.getTmpInfo() == null)
instr.setTmpInfo(new FlowBlock(this, instr.getAddr()));
FlowBlock flowBlock = (FlowBlock) instr.getTmpInfo();
flowBlock.appendBlock(block, instr.getLength());
if (lastBlock != null)
lastBlock.setNextByAddr(flowBlock);
instr.setTmpInfo(lastBlock = flowBlock);
lastSequential = !instr.doesAlwaysJump()
&& instr.getSuccs() == null;
}
}
methodHeader = (FlowBlock)
((Instruction) code.getInstructions().get(0)).getTmpInfo();
excHandlers = new TransformExceptionHandlers();
for (int i=0; i<handlers.length; i++) { for (int i=0; i<handlers.length; i++) {
Type type = null; Type type = null;
int start = handlers[i].getStart().getBlockNr(); FlowBlock start
int end = handlers[i].getEnd().getBlockNr(); = (FlowBlock) handlers[i].start.getTmpInfo();
FlowBlock handler = flows[handlers[i].getCatcher().getBlockNr()]; int endAddr = handlers[i].end.getNextByAddr().getAddr();
if (handlers[i].getType() != null) FlowBlock handler
type = Type.tClass(classAnalyzer.getClassPath(), = (FlowBlock) handlers[i].catcher.getTmpInfo();
handlers[i].getType()); if (handlers[i].type != null)
for (int j = start; j <= end; j++) { type = Type.tClass(handlers[i].type);
if (flows[j] != handler)
flows[j].addExceptionHandler(type, handler); excHandlers.addHandler(start, endAddr, handler, type);
}
} }
} }
for (Iterator i = code.getInstructions().iterator(); i.hasNext(); ) {
Instruction instr = (Instruction) i.next();
instr.setTmpInfo(null);
}
if (GlobalOptions.verboseLevel > 0) if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.print('-'); GlobalOptions.err.print('-');
//excHandlers.analyze(); excHandlers.analyze();
methodHeader.analyze(); methodHeader.analyze();
methodHeader.removeStartPred();
if ((Options.options & Options.OPTION_PUSH) == 0 if ((Options.options & Options.OPTION_PUSH) == 0
&& methodHeader.mapStackToLocal()) && methodHeader.mapStackToLocal())
@ -548,21 +598,28 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* code of this method, but not the method scoped classes. * code of this method, but not the method scoped classes.
*/ */
public void analyze(ProgressListener pl, double done, double scale) public void analyze(ProgressListener pl, double done, double scale)
throws ClassFormatError throws ClassFormatError
{ {
if (pl != null) if (pl != null)
pl.updateProgress(done, methodName); pl.updateProgress(done, methodName);
if (bb != null) { if (code != null) {
if ((Options.options & Options.OPTION_VERIFY) != 0) { if ((Options.options & Options.OPTION_VERIFY) != 0) {
CodeVerifier verifier CodeVerifier verifier
= new CodeVerifier(getClazz(), minfo, bb); = new CodeVerifier(getClazz(), minfo, code);
try { try {
verifier.verify(); verifier.verify();
} catch (VerifyException ex) { } catch (VerifyException ex) {
ex.printStackTrace(GlobalOptions.err); ex.printStackTrace(GlobalOptions.err);
throw new InternalError("Verification error"); throw new jode.AssertError("Verification error");
} }
} }
if ((Options.options & Options.OPTION_LVT) != 0) {
LocalVariableInfo[] localvars = code.getLocalVariableTable();
if (localvars != null)
lvt = new LocalVariableTable(code.getMaxLocals(),
localvars);
}
} }
Type[] paramTypes = getType().getParameterTypes(); Type[] paramTypes = getType().getParameterTypes();
@ -573,22 +630,17 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
int slot = 0; int slot = 0;
if (!isStatic()) { if (!isStatic()) {
ClassInfo classInfo = classAnalyzer.getClazz(); ClassInfo classInfo = classAnalyzer.getClazz();
param[offset] = getLocalInfo(bb != null LocalInfo thisLocal = getLocalInfo(0, slot++);
? bb.getParamInfo(slot) thisLocal.setExpression(new ThisOperator(classInfo, true));
: LocalVariableInfo.getInfo(slot)); param[offset++] = thisLocal;
param[offset].setExpression(new ThisOperator(classInfo, true));
slot++;
offset++;
} }
for (int i=0; i< paramTypes.length; i++) { for (int i=0; i< paramTypes.length; i++) {
param[offset] = getLocalInfo(bb != null param[offset] = getLocalInfo(0, slot);
? bb.getParamInfo(slot)
: LocalVariableInfo.getInfo(slot));
param[offset].setType(paramTypes[i]); param[offset].setType(paramTypes[i]);
slot += paramTypes[i].stackSize(); slot += paramTypes[i].stackSize();
offset++; offset++;
} }
for (int i= 0; i< exceptions.length; i++) for (int i= 0; i< exceptions.length; i++)
imports.useType(exceptions[i]); imports.useType(exceptions[i]);
@ -596,7 +648,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
if (!isConstructor) if (!isConstructor)
imports.useType(methodType.getReturnType()); imports.useType(methodType.getReturnType());
if (bb != null) if (code != null)
analyzeCode(pl, done, scale); analyzeCode(pl, done, scale);
} }
@ -607,6 +659,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
public void analyzeInnerClasses() public void analyzeInnerClasses()
throws ClassFormatError throws ClassFormatError
{ {
int serialnr = 0;
Enumeration elts = anonConstructors.elements(); Enumeration elts = anonConstructors.elements();
while (elts.hasMoreElements()) { while (elts.hasMoreElements()) {
InvokeOperator cop = (InvokeOperator) elts.nextElement(); InvokeOperator cop = (InvokeOperator) elts.nextElement();
@ -645,7 +698,6 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
if (!li.isShadow()) if (!li.isShadow())
imports.useType(li.getType()); imports.useType(li.getType());
} }
for (int i=0; i < param.length; i++) { for (int i=0; i < param.length; i++) {
param[i].guessName(); param[i].guessName();
Iterator doneIter = done.iterator(); Iterator doneIter = done.iterator();
@ -660,7 +712,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
done.add(param[i]); done.add(param[i]);
} }
if (bb != null) { if (code != null) {
methodHeader.makeDeclaration(done); methodHeader.makeDeclaration(done);
methodHeader.simplify(); methodHeader.simplify();
} }
@ -676,16 +728,12 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* @return true, iff it shouldn't be written to the source code. * @return true, iff it shouldn't be written to the source code.
*/ */
public boolean skipWriting() { public boolean skipWriting() {
if (isSynthetic()
&& (minfo.getModifiers() & 0x0040 /*ACC_BRIDGE*/) != 0)
return true;
if (synth != null) { if (synth != null) {
// We don't need this class anymore (hopefully?) // We don't need this class anymore (hopefully?)
if (synth.getKind() == SyntheticAnalyzer.GETCLASS) if (synth.getKind() == synth.GETCLASS)
return true; return true;
if (synth.getKind() >= SyntheticAnalyzer.ACCESSGETFIELD if (synth.getKind() >= synth.ACCESSGETFIELD
&& synth.getKind() <= SyntheticAnalyzer.ACCESSDUPPUTSTATIC && synth.getKind() <= synth.ACCESSDUPPUTSTATIC
&& (Options.options & Options.OPTION_INNER) != 0 && (Options.options & Options.OPTION_INNER) != 0
&& (Options.options & Options.OPTION_ANON) != 0) && (Options.options & Options.OPTION_ANON) != 0)
return true; return true;
@ -698,7 +746,6 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
boolean declareAsConstructor = isConstructor; boolean declareAsConstructor = isConstructor;
int skipParams = 0; int skipParams = 0;
int modifiedModifiers = minfo.getModifiers();
if (isConstructor() && !isStatic() if (isConstructor() && !isStatic()
&& classAnalyzer.outerValues != null) && classAnalyzer.outerValues != null)
skipParams = classAnalyzer.outerValues.getCount(); skipParams = classAnalyzer.outerValues.getCount();
@ -708,8 +755,6 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
declareAsConstructor = true; declareAsConstructor = true;
skipParams = hasJikesOuterValue skipParams = hasJikesOuterValue
&& classAnalyzer.outerValues.getCount() > 0 ? 1 : 0; && classAnalyzer.outerValues.getCount() > 0 ? 1 : 0;
// get the modifiers of the real constructor
modifiedModifiers = jikesConstructor.minfo.getModifiers();
} }
if (isJikesBlockInitializer) if (isJikesBlockInitializer)
@ -718,7 +763,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
/* The default constructor must be empty /* The default constructor must be empty
* and mustn't throw exceptions */ * and mustn't throw exceptions */
if (getMethodHeader() == null if (getMethodHeader() == null
|| !(getMethodHeader().getBlock() instanceof net.sf.jode.flow.EmptyBlock) || !(getMethodHeader().getBlock() instanceof jode.flow.EmptyBlock)
|| !getMethodHeader().hasNoJumps() || !getMethodHeader().hasNoJumps()
|| exceptions.length > 0) || exceptions.length > 0)
return false; return false;
@ -729,11 +774,11 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* But this rule doesn't necessarily apply for anonymous * But this rule doesn't necessarily apply for anonymous
* classes... * classes...
*/ */
&& ((modifiedModifiers && ((minfo.getModifiers()
& (Modifier.PROTECTED | Modifier.PUBLIC | Modifier.PRIVATE & (Modifier.PROTECTED | Modifier.PUBLIC | Modifier.PRIVATE
| Modifier.SYNCHRONIZED | Modifier.STATIC | Modifier.SYNCHRONIZED | Modifier.STATIC
| Modifier.ABSTRACT | Modifier.NATIVE)) | Modifier.ABSTRACT | Modifier.NATIVE))
== (classAnalyzer.getModifiers() == (getClassAnalyzer().getModifiers()
& (Modifier.PROTECTED | Modifier.PUBLIC)) & (Modifier.PROTECTED | Modifier.PUBLIC))
|| classAnalyzer.getName() == null) || classAnalyzer.getName() == null)
&& classAnalyzer.constructors.length == 1) { && classAnalyzer.constructors.length == 1) {
@ -764,12 +809,9 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
int modifiedModifiers = minfo.getModifiers(); int modifiedModifiers = minfo.getModifiers();
if (isConstructor() && !isStatic() if (isConstructor() && !isStatic()
&& (Options.options & Options.OPTION_CONTRAFO) != 0) { && (Options.options & Options.OPTION_CONTRAFO) != 0
if (classAnalyzer.outerValues != null) && classAnalyzer.outerValues != null)
skipParams = classAnalyzer.outerValues.getCount(); skipParams = classAnalyzer.outerValues.getCount();
else if (classAnalyzer.getOuterInstance() != null)
skipParams = 1;
}
if (jikesConstructor != null) { if (jikesConstructor != null) {
// This is the real part of a jikes constructor // This is the real part of a jikes constructor
@ -807,26 +849,27 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
modifiedModifiers &= ~Modifier.ABSTRACT; modifiedModifiers &= ~Modifier.ABSTRACT;
/* Don't ask me why, but jikes declares the static constructor /* Don't ask me why, but jikes declares the static constructor
* as final. * as final. Another compiler or obfuscator seems to declare
* it as public. I remove every fancy modifier, now.
*/ */
if (isConstructor() && isStatic()) if (isConstructor() && isStatic())
modifiedModifiers &= ~(Modifier.FINAL | Modifier.PUBLIC modifiedModifiers &= ~(Modifier.FINAL | Modifier.PUBLIC
| Modifier.PROTECTED | Modifier.PRIVATE); | Modifier.PROTECTED | Modifier.PRIVATE);
modifiedModifiers &= ~STRICTFP; modifiedModifiers &= ~STRICTFP;
writer.startOp(TabbedPrintWriter.NO_PAREN, 0); writer.startOp(writer.NO_PAREN, 1);
writer.startOp(TabbedPrintWriter.NO_PAREN, 5); String delim = "";
String delim ="";
if (minfo.isSynthetic()) { if (minfo.isSynthetic()) {
writer.print("/*synthetic*/"); writer.print("/*synthetic*/");
delim = " "; delim = " ";
} }
String modif = Modifier.toString(modifiedModifiers); String modif = Modifier.toString(modifiedModifiers);
writer.print(delim + modif); if (modif.length() > 0) {
if (modif.length() > 0) writer.print(delim + modif);
delim = " "; delim = " ";
}
if (isStrictFP()) { if (isStrictFP()) {
/* The STRICTFP modifier is set. /* The STRICTFP modifier is set.
* We handle it, since java.lang.reflect.Modifier is too dumb. * We handle it, since java.lang.reflect.Modifier is too dumb.
@ -857,10 +900,11 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
writer.print(" " + methodName); writer.print(" " + methodName);
} }
writer.breakOp(); writer.breakOp();
writer.printOptionalSpace(); if ((Options.outputStyle & Options.GNU_SPACING) != 0)
writer.print(" ");
writer.print("("); writer.print("(");
writer.startOp(TabbedPrintWriter.EXPL_PAREN, 0); writer.startOp(writer.EXPL_PAREN, 0);
int offset = skipParams + (isStatic() ? 0 : 1); int offset = skipParams + (isStatic() ? 0 : 1);
for (int i = offset; i < param.length; i++) { for (int i = offset; i < param.length; i++) {
if (i > offset) { if (i > offset) {
writer.print(", "); writer.print(", ");
@ -868,25 +912,25 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
} }
param[i].dumpDeclaration(writer); param[i].dumpDeclaration(writer);
} }
writer.endOp(); writer.endOp();
writer.print(")"); writer.print(")");
} }
writer.endOp();
if (exceptions.length > 0) { if (exceptions.length > 0) {
writer.breakOp(); writer.breakOp();
writer.print(" throws "); writer.print(" throws ");
writer.startOp(TabbedPrintWriter.NO_PAREN, 0); writer.startOp(writer.EXPL_PAREN, 2);
for (int i= 0; i< exceptions.length; i++) { for (int i= 0; i< exceptions.length; i++) {
if (i > 0) { if (i > 0) {
writer.print(", "); writer.print(",");
writer.breakOp(); writer.breakOp();
writer.print(" ");
} }
writer.printType(exceptions[i]); writer.printType(exceptions[i]);
} }
writer.endOp(); writer.endOp();
} }
writer.endOp(); writer.endOp();
if (bb != null) { if (code != null) {
writer.openBraceNoIndent(); writer.openBraceNoIndent();
writer.tab(); writer.tab();
methodHeader.dumpSource(writer); methodHeader.dumpSource(writer);
@ -982,7 +1026,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
} }
public void analyzeInvokeOperator(InvokeOperator cop) { public void analyzeInvokeOperator(InvokeOperator cop) {
ClassInfo clazz = cop.getClassInfo(); ClassInfo clazz = (ClassInfo) cop.getClassInfo();
ClassAnalyzer anonAnalyzer = getParent().getClassAnalyzer(clazz); ClassAnalyzer anonAnalyzer = getParent().getClassAnalyzer(clazz);
if (anonAnalyzer == null) { if (anonAnalyzer == null) {
@ -1022,14 +1066,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
outerValueArray = newOuter; outerValueArray = newOuter;
break; break;
} }
try { anonAnalyzer = new ClassAnalyzer(this, clazz, imports,
anonAnalyzer = new ClassAnalyzer(this, clazz, imports, outerValueArray);
outerValueArray);
} catch (IOException ex) {
GlobalOptions.err.println
("Error while reading anonymous class "+clazz+".");
return;
}
addClassAnalyzer(anonAnalyzer); addClassAnalyzer(anonAnalyzer);
anonAnalyzer.initialize(); anonAnalyzer.initialize();
anonAnalyzer.analyze(null, 0.0, 0.0); anonAnalyzer.analyze(null, 0.0, 0.0);

@ -17,18 +17,21 @@
* $Id$ * $Id$
*/ */
package net.sf.jode.decompiler; package jode.decompiler;
import net.sf.jode.type.Type; import jode.type.Type;
import net.sf.jode.type.IntegerType; import jode.type.IntegerType;
import net.sf.jode.expr.*; import jode.type.MethodType;
import net.sf.jode.flow.*; import jode.expr.*;
import net.sf.jode.bytecode.*; import jode.flow.*;
import jode.bytecode.*;
import java.io.*;
import java.util.Vector;
/** /**
* This is an abstract class which creates flow blocks for the * This is an abstract class which creates flow blocks for the
* opcodes in a byte stream. * opcodes in a byte stream.
*/ */
public abstract class Opcodes implements net.sf.jode.bytecode.Opcodes { public abstract class Opcodes implements jode.bytecode.Opcodes {
private final static Type tIntHint private final static Type tIntHint
= new IntegerType(IntegerType.IT_I, = new IntegerType(IntegerType.IT_I,
@ -73,7 +76,7 @@ public abstract class Opcodes implements net.sf.jode.bytecode.Opcodes {
Instruction instr, Instruction instr,
Expression expr) Expression expr)
{ {
return new InstructionBlock(expr); return new InstructionBlock(expr, new Jump(FlowBlock.NEXT_BY_ADDR));
} }
private static StructuredBlock createSpecial(MethodAnalyzer ma, private static StructuredBlock createSpecial(MethodAnalyzer ma,
@ -81,27 +84,39 @@ public abstract class Opcodes implements net.sf.jode.bytecode.Opcodes {
int type, int type,
int stackcount, int param) int stackcount, int param)
{ {
return new SpecialBlock(type, stackcount, param); return new SpecialBlock(type, stackcount, param,
new Jump(FlowBlock.NEXT_BY_ADDR));
}
private static StructuredBlock createGoto(MethodAnalyzer ma,
Instruction instr)
{
return new EmptyBlock
(new Jump((FlowBlock)instr.getSingleSucc().getTmpInfo()));
} }
private static StructuredBlock createJsr(MethodAnalyzer ma, private static StructuredBlock createJsr(MethodAnalyzer ma,
Instruction instr) Instruction instr)
{ {
return new JsrBlock(); return new JsrBlock
(new Jump((FlowBlock)instr.getSingleSucc().getTmpInfo()),
new Jump(FlowBlock.NEXT_BY_ADDR));
} }
private static StructuredBlock createIfGoto(MethodAnalyzer ma, private static StructuredBlock createIfGoto(MethodAnalyzer ma,
Instruction instr, Instruction instr,
Expression expr) Expression expr)
{ {
return new ConditionalBlock(expr); return new ConditionalBlock
(expr, new Jump((FlowBlock)instr.getSingleSucc().getTmpInfo()),
new Jump(FlowBlock.NEXT_BY_ADDR));
} }
private static StructuredBlock createSwitch(MethodAnalyzer ma, private static StructuredBlock createSwitch(MethodAnalyzer ma,
Instruction instr, Instruction instr,
int[] cases) int[] cases, FlowBlock[] dests)
{ {
return new SwitchBlock(new NopOperator(Type.tUInt), cases); return new SwitchBlock(new NopOperator(Type.tUInt), cases, dests);
} }
private static StructuredBlock createBlock(MethodAnalyzer ma, private static StructuredBlock createBlock(MethodAnalyzer ma,
@ -119,138 +134,107 @@ public abstract class Opcodes implements net.sf.jode.bytecode.Opcodes {
} }
/** /**
* Converts an instruction to a StructuredBlock and appencs it to the * Read an opcode out of a data input stream containing the bytecode.
* flow block. * @param addr The current address.
* @param flow The flowblock to which we should add. * @param stream The stream containing the java byte code.
* @param instr The instruction to add.
* @param ma The Method Analyzer * @param ma The Method Analyzer
* (where further information can be get from). * (where further information can be get from).
* @return The FlowBlock representing this opcode * @return The FlowBlock representing this opcode
* or null if the stream is empty. * or null if the stream is empty.
* @exception IOException if an read error occured.
* @exception ClassFormatError if an invalid opcode is detected.
*/ */
public static void addOpcode(FlowBlock flow, Instruction instr, public static StructuredBlock readOpcode(Instruction instr,
MethodAnalyzer ma) MethodAnalyzer ma)
throws ClassFormatError
{ {
ClassPath cp = ma.getClassAnalyzer().getClassPath();
int opcode = instr.getOpcode(); int opcode = instr.getOpcode();
switch (opcode) { switch (opcode) {
case opc_nop: case opc_nop:
break; return createBlock(ma, instr, new EmptyBlock
(new Jump(FlowBlock.NEXT_BY_ADDR)));
case opc_ldc: case opc_ldc:
case opc_ldc2_w: case opc_ldc2_w:
{ return createNormal (ma, instr,
Expression expr; new ConstOperator(instr.getConstant()));
if (instr.getConstant() instanceof Reference) {
Reference ref = (Reference) instr.getConstant();
expr = new ClassFieldOperator
(Type.tType(cp, ref.getClazz()));
} else {
expr = new ConstOperator(instr.getConstant());
}
flow.appendBlock(createNormal(ma, instr, expr));
}
break;
case opc_iload: case opc_lload: case opc_iload: case opc_lload:
case opc_fload: case opc_dload: case opc_aload: { case opc_fload: case opc_dload: case opc_aload:
LocalInfo local = ma.getLocalInfo(instr.getLocalInfo()); return createNormal
flow.appendReadBlock (ma, instr, new LocalLoadOperator
(createNormal (types[LOCAL_TYPES][opcode-opc_iload], ma,
(ma, instr, new LocalLoadOperator ma.getLocalInfo(instr.getAddr(), instr.getLocalSlot())));
(types[LOCAL_TYPES][opcode-opc_iload], ma, local)), local);
break;
}
case opc_iaload: case opc_laload: case opc_iaload: case opc_laload:
case opc_faload: case opc_daload: case opc_aaload: case opc_faload: case opc_daload: case opc_aaload:
case opc_baload: case opc_caload: case opc_saload: case opc_baload: case opc_caload: case opc_saload:
flow.appendBlock return createNormal
(createNormal (ma, instr, new ArrayLoadOperator
(ma, instr, new ArrayLoadOperator (types[ARRAY_TYPES][opcode - opc_iaload]));
(types[ARRAY_TYPES][opcode - opc_iaload])));
break;
case opc_istore: case opc_lstore: case opc_istore: case opc_lstore:
case opc_fstore: case opc_dstore: case opc_astore: { case opc_fstore: case opc_dstore: case opc_astore:
LocalInfo local = ma.getLocalInfo(instr.getLocalInfo()); return createNormal
flow.appendWriteBlock (ma, instr, new StoreInstruction
(createNormal (new LocalStoreOperator
(ma, instr, new StoreInstruction (types[LOCAL_TYPES][opcode-opc_istore],
(new LocalStoreOperator ma.getLocalInfo(instr.getNextByAddr().getAddr(),
(types[LOCAL_TYPES][opcode-opc_istore], local))), local); instr.getLocalSlot()))));
break;
}
case opc_iastore: case opc_lastore: case opc_iastore: case opc_lastore:
case opc_fastore: case opc_dastore: case opc_aastore: case opc_fastore: case opc_dastore: case opc_aastore:
case opc_bastore: case opc_castore: case opc_sastore: case opc_bastore: case opc_castore: case opc_sastore:
flow.appendBlock return createNormal
(createNormal (ma, instr, new StoreInstruction
(ma, instr, new StoreInstruction (new ArrayStoreOperator
(new ArrayStoreOperator (types[ARRAY_TYPES][opcode - opc_iastore])));
(types[ARRAY_TYPES][opcode - opc_iastore]))));
break;
case opc_pop: case opc_pop2: case opc_pop: case opc_pop2:
flow.appendBlock return createSpecial
(createSpecial (ma, instr, SpecialBlock.POP, opcode - opc_pop + 1, 0);
(ma, instr, SpecialBlock.POP, opcode - opc_pop + 1, 0)); case opc_dup: case opc_dup_x1: case opc_dup_x2:
break;
case opc_dup: case opc_dup_x1: case opc_dup_x2:
case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: case opc_dup2: case opc_dup2_x1: case opc_dup2_x2:
flow.appendBlock return createSpecial
(createSpecial (ma, instr, SpecialBlock.DUP,
(ma, instr, SpecialBlock.DUP, (opcode - opc_dup)/3+1, (opcode - opc_dup)%3);
(opcode - opc_dup)/3+1, (opcode - opc_dup)%3));
break;
case opc_swap: case opc_swap:
flow.appendBlock return createSpecial(ma, instr, SpecialBlock.SWAP, 1, 0);
(createSpecial(ma, instr, SpecialBlock.SWAP, 1, 0));
break;
case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd:
case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub:
case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul: case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul:
case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv: case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv:
case opc_irem: case opc_lrem: case opc_frem: case opc_drem: case opc_irem: case opc_lrem: case opc_frem: case opc_drem:
flow.appendBlock return createNormal
(createNormal (ma, instr, new BinaryOperator
(ma, instr, new BinaryOperator (types[BIN_TYPES][(opcode - opc_iadd)%4],
(types[BIN_TYPES][(opcode - opc_iadd)%4], (opcode - opc_iadd)/4+Operator.ADD_OP));
(opcode - opc_iadd)/4+Operator.ADD_OP)));
break;
case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg:
flow.appendBlock return createNormal
(createNormal (ma, instr, new UnaryOperator
(ma, instr, new UnaryOperator (types[UNARY_TYPES][opcode - opc_ineg], Operator.NEG_OP));
(types[UNARY_TYPES][opcode - opc_ineg], Operator.NEG_OP)));
break;
case opc_ishl: case opc_lshl: case opc_ishl: case opc_lshl:
case opc_ishr: case opc_lshr: case opc_ishr: case opc_lshr:
case opc_iushr: case opc_lushr: case opc_iushr: case opc_lushr:
flow.appendBlock return createNormal
(createNormal (ma, instr, new ShiftOperator
(ma, instr, new ShiftOperator (types[UNARY_TYPES][(opcode - opc_ishl)%2],
(types[UNARY_TYPES][(opcode - opc_ishl)%2], (opcode - opc_ishl)/2 + Operator.SHIFT_OP));
(opcode - opc_ishl)/2 + Operator.SHIFT_OP)));
break;
case opc_iand: case opc_land: case opc_iand: case opc_land:
case opc_ior : case opc_lor : case opc_ior : case opc_lor :
case opc_ixor: case opc_lxor: case opc_ixor: case opc_lxor:
flow.appendBlock return createNormal
(createNormal (ma, instr, new BinaryOperator
(ma, instr, new BinaryOperator (types[ZBIN_TYPES][(opcode - opc_iand)%2],
(types[ZBIN_TYPES][(opcode - opc_iand)%2], (opcode - opc_iand)/2 + Operator.AND_OP));
(opcode - opc_iand)/2 + Operator.AND_OP)));
break;
case opc_iinc: { case opc_iinc: {
LocalInfo local = ma.getLocalInfo(instr.getLocalInfo());
int value = instr.getIncrement(); int value = instr.getIncrement();
int operation = Operator.ADD_OP; int operation = Operator.ADD_OP;
if (value < 0) { if (value < 0) {
value = -value; value = -value;
operation = Operator.SUB_OP; operation = Operator.SUB_OP;
} }
flow.appendReadBlock LocalInfo li
(createNormal = ma.getLocalInfo(instr.getAddr(), instr.getLocalSlot());
(ma, instr, new IIncOperator return createNormal
(new LocalStoreOperator(Type.tInt, local), (ma, instr, new IIncOperator
value, operation + Operator.OPASSIGN_OP)), local); (new LocalStoreOperator(Type.tInt, li),
break; value, operation + Operator.OPASSIGN_OP));
} }
case opc_i2l: case opc_i2f: case opc_i2d: case opc_i2l: case opc_i2f: case opc_i2d:
case opc_l2i: case opc_l2f: case opc_l2d: case opc_l2i: case opc_l2f: case opc_l2d:
@ -260,101 +244,87 @@ public abstract class Opcodes implements net.sf.jode.bytecode.Opcodes {
int to = (opcode-opc_i2l)%3; int to = (opcode-opc_i2l)%3;
if (to >= from) if (to >= from)
to++; to++;
flow.appendBlock return createNormal
(createNormal (ma, instr, new ConvertOperator(types[UNARY_TYPES][from],
(ma, instr, new ConvertOperator(types[UNARY_TYPES][from], types[UNARY_TYPES][to]));
types[UNARY_TYPES][to])));
break;
} }
case opc_i2b: case opc_i2c: case opc_i2s: case opc_i2b: case opc_i2c: case opc_i2s:
flow.appendBlock(createNormal return createNormal
(ma, instr, new ConvertOperator (ma, instr, new ConvertOperator
(types[UNARY_TYPES][0], types[I2BCS_TYPES][opcode-opc_i2b]))); (types[UNARY_TYPES][0], types[I2BCS_TYPES][opcode-opc_i2b]));
break;
case opc_lcmp: case opc_lcmp:
case opc_fcmpl: case opc_fcmpg: case opc_fcmpl: case opc_fcmpg:
case opc_dcmpl: case opc_dcmpg: case opc_dcmpl: case opc_dcmpg:
flow.appendBlock(createNormal return createNormal
(ma, instr, new CompareToIntOperator (ma, instr, new CompareToIntOperator
(types[BIN_TYPES][(opcode-(opc_lcmp-3))/2], (types[BIN_TYPES][(opcode-(opc_lcmp-3))/2],
(opcode == opc_fcmpg || opcode == opc_dcmpg)))); (opcode == opc_fcmpg || opcode == opc_dcmpg)));
break;
case opc_ifeq: case opc_ifne: case opc_ifeq: case opc_ifne:
flow.appendBlock(createIfGoto return createIfGoto
(ma, instr, (ma, instr,
new CompareUnaryOperator new CompareUnaryOperator
(Type.tBoolInt, opcode - (opc_ifeq-Operator.COMPARE_OP)))); (Type.tBoolInt, opcode - (opc_ifeq-Operator.COMPARE_OP)));
break;
case opc_iflt: case opc_ifge: case opc_ifgt: case opc_ifle: case opc_iflt: case opc_ifge: case opc_ifgt: case opc_ifle:
flow.appendBlock(createIfGoto return createIfGoto
(ma, instr, (ma, instr,
new CompareUnaryOperator new CompareUnaryOperator
(Type.tInt, opcode - (opc_ifeq-Operator.COMPARE_OP)))); (Type.tInt, opcode - (opc_ifeq-Operator.COMPARE_OP)));
break;
case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpeq: case opc_if_icmpne:
flow.appendBlock return createIfGoto
(createIfGoto (ma, instr,
(ma, instr, new CompareBinaryOperator
new CompareBinaryOperator (tBoolIntHint, opcode - (opc_if_icmpeq-Operator.COMPARE_OP)));
(tBoolIntHint,
opcode - (opc_if_icmpeq-Operator.COMPARE_OP))));
break;
case opc_if_icmplt: case opc_if_icmpge: case opc_if_icmplt: case opc_if_icmpge:
case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmpgt: case opc_if_icmple:
flow.appendBlock return createIfGoto
(createIfGoto (ma, instr,
(ma, instr, new CompareBinaryOperator
new CompareBinaryOperator (tIntHint, opcode - (opc_if_icmpeq-Operator.COMPARE_OP)));
(tIntHint, opcode - (opc_if_icmpeq-Operator.COMPARE_OP))));
break;
case opc_if_acmpeq: case opc_if_acmpne: case opc_if_acmpeq: case opc_if_acmpne:
flow.appendBlock return createIfGoto
(createIfGoto (ma, instr,
(ma, instr, new CompareBinaryOperator
new CompareBinaryOperator (Type.tUObject,
(Type.tUObject, opcode - (opc_if_acmpeq-Operator.COMPARE_OP)));
opcode - (opc_if_acmpeq-Operator.COMPARE_OP)))); case opc_goto:
break; return createGoto(ma, instr);
case opc_jsr: case opc_jsr:
flow.appendBlock(createJsr(ma, instr)); return createJsr(ma, instr);
break; case opc_ret:
case opc_ret: { return createRet
LocalInfo local = ma.getLocalInfo(instr.getLocalInfo()); (ma, instr,
flow.appendReadBlock(createRet(ma, instr, local), local); ma.getLocalInfo(instr.getAddr(), instr.getLocalSlot()));
break;
}
case opc_lookupswitch: { case opc_lookupswitch: {
int[] cases = instr.getValues(); int[] cases = instr.getValues();
flow.appendBlock(createSwitch(ma, instr, cases)); FlowBlock[] dests = new FlowBlock[instr.getSuccs().length];
break; for (int i=0; i < dests.length; i++)
dests[i] = (FlowBlock) instr.getSuccs()[i].getTmpInfo();
dests[cases.length] = (FlowBlock)
instr.getSuccs()[cases.length].getTmpInfo();
return createSwitch(ma, instr, cases, dests);
} }
case opc_ireturn: case opc_lreturn: case opc_ireturn: case opc_lreturn:
case opc_freturn: case opc_dreturn: case opc_areturn: { case opc_freturn: case opc_dreturn: case opc_areturn: {
Type retType = Type.tSubType(ma.getReturnType()); Type retType = Type.tSubType(ma.getReturnType());
flow.appendBlock return createBlock
(createBlock (ma, instr, new ReturnBlock(new NopOperator(retType)));
(ma, instr, new ReturnBlock(new NopOperator(retType))));
break;
} }
case opc_return: case opc_return:
throw new InternalError("opc_return no longer allowed"); return createBlock
(ma, instr, new EmptyBlock(new Jump(FlowBlock.END_OF_METHOD)));
case opc_getstatic: case opc_getstatic:
case opc_getfield: { case opc_getfield: {
Reference ref = instr.getReference(); Reference ref = instr.getReference();
flow.appendBlock(createNormal return createNormal
(ma, instr, new GetFieldOperator (ma, instr, new GetFieldOperator
(ma, opcode == opc_getstatic, ref))); (ma, opcode == opc_getstatic, ref));
break;
} }
case opc_putstatic: case opc_putstatic:
case opc_putfield: { case opc_putfield: {
Reference ref = instr.getReference(); Reference ref = instr.getReference();
flow.appendBlock return createNormal
(createNormal (ma, instr, new StoreInstruction
(ma, instr, new StoreInstruction (new PutFieldOperator(ma, opcode == opc_putstatic, ref)));
(new PutFieldOperator(ma, opcode == opc_putstatic, ref))));
break;
} }
case opc_invokevirtual: case opc_invokevirtual:
case opc_invokespecial: case opc_invokespecial:
@ -368,64 +338,51 @@ public abstract class Opcodes implements net.sf.jode.bytecode.Opcodes {
: InvokeOperator.VIRTUAL); : InvokeOperator.VIRTUAL);
StructuredBlock block = createNormal StructuredBlock block = createNormal
(ma, instr, new InvokeOperator(ma, flag, ref)); (ma, instr, new InvokeOperator(ma, flag, ref));
flow.appendBlock(block); return block;
break;
} }
case opc_new: { case opc_new: {
Type type = Type.tType(cp, instr.getClazzType()); Type type = Type.tType(instr.getClazzType());
ma.useType(type); ma.useType(type);
flow.appendBlock(createNormal(ma, instr, new NewOperator(type))); return createNormal(ma, instr, new NewOperator(type));
break;
} }
case opc_arraylength: case opc_arraylength:
flow.appendBlock(createNormal return createNormal
(ma, instr, new ArrayLengthOperator())); (ma, instr, new ArrayLengthOperator());
break;
case opc_athrow: case opc_athrow:
flow.appendBlock(createBlock return createBlock
(ma, instr, (ma, instr,
new ThrowBlock(new NopOperator(Type.tUObject)))); new ThrowBlock(new NopOperator(Type.tUObject)));
break;
case opc_checkcast: { case opc_checkcast: {
Type type = Type.tType(cp, instr.getClazzType()); Type type = Type.tType(instr.getClazzType());
ma.useType(type); ma.useType(type);
flow.appendBlock(createNormal return createNormal
(ma, instr, new CheckCastOperator(type))); (ma, instr, new CheckCastOperator(type));
break;
} }
case opc_instanceof: { case opc_instanceof: {
Type type = Type.tType(cp, instr.getClazzType()); Type type = Type.tType(instr.getClazzType());
ma.useType(type); ma.useType(type);
flow.appendBlock(createNormal return createNormal
(ma, instr, new InstanceOfOperator(type))); (ma, instr, new InstanceOfOperator(type));
break;
} }
case opc_monitorenter: case opc_monitorenter:
flow.appendBlock(createNormal(ma, instr, return createNormal(ma, instr,
new MonitorEnterOperator())); new MonitorEnterOperator());
break;
case opc_monitorexit: case opc_monitorexit:
flow.appendBlock(createNormal(ma, instr, return createNormal(ma, instr,
new MonitorExitOperator())); new MonitorExitOperator());
break;
case opc_multianewarray: { case opc_multianewarray: {
Type type = Type.tType(cp, instr.getClazzType()); Type type = Type.tType(instr.getClazzType());
ma.useType(type); ma.useType(type);
int dimension = instr.getDimensions(); int dimension = instr.getDimensions();
flow.appendBlock(createNormal return createNormal(ma, instr,
(ma, instr, new NewArrayOperator(type, dimension));
new NewArrayOperator(type, dimension))); }
break;
}
case opc_ifnull: case opc_ifnonnull: case opc_ifnull: case opc_ifnonnull:
flow.appendBlock(createIfGoto return createIfGoto
(ma, instr, new CompareUnaryOperator (ma, instr, new CompareUnaryOperator
(Type.tUObject, (Type.tUObject, opcode - (opc_ifnull-Operator.COMPARE_OP)));
opcode - (opc_ifnull-Operator.COMPARE_OP))));
break;
default: default:
throw new InternalError("Invalid opcode "+opcode); throw new jode.AssertError("Invalid opcode "+opcode);
} }
} }
} }

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

Loading…
Cancel
Save